pax_global_header00006660000000000000000000000064147747312400014523gustar00rootroot0000000000000052 comment=2b1a284f8453baa2bd193709b67e5183074c74ba dxvk-2.6.1/000077500000000000000000000000001477473124000125055ustar00rootroot00000000000000dxvk-2.6.1/.github/000077500000000000000000000000001477473124000140455ustar00rootroot00000000000000dxvk-2.6.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001477473124000162305ustar00rootroot00000000000000dxvk-2.6.1/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000015561477473124000207310ustar00rootroot00000000000000--- name: Bug report about: Report crashes, rendering issues etc. title: '' labels: '' assignees: '' --- Please describe your issue as accurately as possible. If you use Windows, please check the following page: https://github.com/doitsujin/dxvk/wiki/Windows ### Software information Name of the game, settings used etc. ### System information - GPU: - Driver: - Wine version: - DXVK version: ### Apitrace file(s) - Put a link here For instructions on how to use apitrace, see: https://github.com/doitsujin/dxvk/wiki/Using-Apitrace ### Log files Please attach Proton or Wine logs as a text file: - 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. - When using regular Wine, use `wine game.exe > game.log 2>&1` and attach the resulting `game.log` file.dxvk-2.6.1/.github/workflows/000077500000000000000000000000001477473124000161025ustar00rootroot00000000000000dxvk-2.6.1/.github/workflows/artifacts.yml000066400000000000000000000043651477473124000206150ustar00rootroot00000000000000name: Artifacts (Package) on: [push, pull_request, workflow_dispatch] jobs: artifacts-mingw-w64: runs-on: ubuntu-latest steps: - name: Checkout code id: checkout-code uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Setup problem matcher uses: Joshua-Ashton/gcc-problem-matcher@v3 - name: Build release id: build-release uses: Joshua-Ashton/arch-mingw-github-action@v8 with: command: | export VERSION_NAME="${GITHUB_REF##*/}-${GITHUB_SHA##*/}" ./package-release.sh ${VERSION_NAME} build --no-package echo "VERSION_NAME=${VERSION_NAME}" >> $GITHUB_ENV - name: Upload artifacts id: upload-artifacts uses: actions/upload-artifact@v4 with: name: dxvk-win-${{ env.VERSION_NAME }} path: build/dxvk-${{ env.VERSION_NAME }} if-no-files-found: error artifacts-steamrt-sniper: runs-on: ubuntu-latest container: registry.gitlab.steamos.cloud/steamrt/sniper/sdk:beta steps: - name: Checkout code id: checkout-code uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Setup problem matcher uses: Joshua-Ashton/gcc-problem-matcher@v3 - name: Build release id: build-release shell: bash run: | export VERSION_NAME="${GITHUB_REF##*/}-${GITHUB_SHA##*/}" ./package-native.sh ${VERSION_NAME} build echo "VERSION_NAME=${VERSION_NAME}" >> $GITHUB_ENV - name: Upload artifacts id: upload-artifacts uses: actions/upload-artifact@v4 with: name: dxvk-native-${{ env.VERSION_NAME }} path: build/dxvk-native-${{ env.VERSION_NAME }}.tar.gz if-no-files-found: error merge-artifacts: runs-on: ubuntu-latest needs: [artifacts-mingw-w64, artifacts-steamrt-sniper] steps: - name: Get version id: get-version shell: bash run: | echo "VERSION_NAME=${GITHUB_REF##*/}-${GITHUB_SHA##*/}" >> $GITHUB_ENV - name: Merge Artifacts uses: actions/upload-artifact/merge@v4 with: name: dxvk-${{ env.VERSION_NAME }} pattern: dxvk* delete-merged: true dxvk-2.6.1/.github/workflows/test-build-windows.yml000066400000000000000000000060551477473124000223770ustar00rootroot00000000000000name: Test Builds on Windows on: [push, pull_request, workflow_dispatch] jobs: build-set-windows: runs-on: windows-latest steps: - name: Checkout code id: checkout-code uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Setup glslangValidator shell: pwsh run: | Invoke-WebRequest -Uri "https://raw.githubusercontent.com/HansKristian-Work/vkd3d-proton-ci/main/glslangValidator.exe" -OutFile "glslangValidator.exe" Write-Output "$pwd" | Out-File -FilePath "${Env:GITHUB_PATH}" -Append - name: Setup Meson shell: pwsh run: pip install meson - name: Find Visual Studio shell: pwsh run: | $installationPath = Get-VSSetupInstance ` | Select-VSSetupInstance -Require Microsoft.VisualStudio.Workload.NativeDesktop -Latest ` | Select-Object -ExpandProperty InstallationPath Write-Output "VSDEVCMD=${installationPath}\Common7\Tools\VsDevCmd.bat" ` | Out-File -FilePath "${Env:GITHUB_ENV}" -Append - name: Download D3D8 SDK Headers shell: pwsh run: | Invoke-WebRequest -URI https://raw.githubusercontent.com/NovaRain/DXSDK_Collection/master/DXSDK_Aug2007/Include/d3d8.h -OutFile include/d3d8.h Invoke-WebRequest -URI https://raw.githubusercontent.com/NovaRain/DXSDK_Collection/master/DXSDK_Aug2007/Include/d3d8types.h -OutFile include/d3d8types.h Invoke-WebRequest -URI https://raw.githubusercontent.com/NovaRain/DXSDK_Collection/master/DXSDK_Aug2007/Include/d3d8caps.h -OutFile include/d3d8caps.h - name: Get version id: get-version shell: bash run: | echo "VERSION_NAME=${GITHUB_REF##*/}-${GITHUB_SHA##*/}" >> $GITHUB_ENV - name: Build MSVC x86 shell: pwsh run: | & "${Env:COMSPEC}" /s /c "`"${Env:VSDEVCMD}`" -arch=x86 -host_arch=x64 -no_logo && set" ` | % { , ($_ -Split '=', 2) } ` | % { [System.Environment]::SetEnvironmentVariable($_[0], $_[1]) } meson --buildtype release --backend vs2022 build-msvc-x86 msbuild -m build-msvc-x86/dxvk.sln - name: Build MSVC x64 shell: pwsh run: | & "${Env:COMSPEC}" /s /c "`"${Env:VSDEVCMD}`" -arch=x64 -host_arch=x64 -no_logo && set" ` | % { , ($_ -Split '=', 2) } ` | % { [System.Environment]::SetEnvironmentVariable($_[0], $_[1]) } meson --buildtype release --backend vs2022 build-msvc-x64 msbuild -m build-msvc-x64/dxvk.sln - name: Prepare artifacts shell: pwsh run: | mkdir artifacts\x32 ls -Path build-msvc-x86\src -Include *.dll,*.pdb -Recurse | cp -Destination (Join-Path -Path (pwd) -ChildPath artifacts\x32) mkdir artifacts\x64 ls -Path build-msvc-x64\src -Include *.dll,*.pdb -Recurse | cp -Destination (Join-Path -Path (pwd) -ChildPath artifacts\x64) - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: dxvk-${{ env.VERSION_NAME }}-msvc-output path: artifacts\* dxvk-2.6.1/.gitmodules000066400000000000000000000007571477473124000146730ustar00rootroot00000000000000[submodule "include/native/directx"] path = include/native/directx url = https://github.com/Joshua-Ashton/mingw-directx-headers [submodule "include/vulkan"] path = include/vulkan url = https://github.com/KhronosGroup/Vulkan-Headers [submodule "include/spirv"] path = include/spirv url = https://github.com/KhronosGroup/SPIRV-Headers.git [submodule "subprojects/libdisplay-info"] path = subprojects/libdisplay-info url = https://github.com/doitsujin/libdisplay-info.git branch = windows dxvk-2.6.1/LICENSE000066400000000000000000000020621477473124000135120ustar00rootroot00000000000000 Copyright (c) 2017 Philip Rebohle Copyright (c) 2019 Joshua Ashton Copyright (c) 2019 Robin Kertels Copyright (c) 2023 Jeffrey Ellison zlib/libpng license This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: – The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. – Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. – This notice may not be removed or altered from any source distribution. dxvk-2.6.1/README.md000066400000000000000000000263261477473124000137750ustar00rootroot00000000000000# DXVK A Vulkan-based translation layer for Direct3D 8/9/10/11 which allows running 3D applications on Linux using Wine. For the current status of the project, please refer to the [project wiki](https://github.com/doitsujin/dxvk/wiki). The most recent development builds can be found [here](https://github.com/doitsujin/dxvk/actions/workflows/artifacts.yml?query=branch%3Amaster). Release builds can be found [here](https://github.com/doitsujin/dxvk/releases). ## How to use In 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. In a default Wine prefix that would be as follows: ``` export WINEPREFIX=/path/to/wineprefix cp x64/*.dll $WINEPREFIX/drive_c/windows/system32 cp x32/*.dll $WINEPREFIX/drive_c/windows/syswow64 winecfg ``` For a pure 32-bit Wine prefix (non default) the 32-bit DLLs instead go to the `system32` directory: ``` export WINEPREFIX=/path/to/wineprefix cp x32/*.dll $WINEPREFIX/drive_c/windows/system32 winecfg ``` Verify that your application uses DXVK instead of wined3d by enabling the HUD (see notes below). In order to remove DXVK from a prefix, remove the DLLs and DLL overrides, and run `wineboot -u` to restore the original DLL files. Tools such as Steam Play, Lutris, Bottles, Heroic Launcher, etc will automatically handle setup of dxvk on their own when enabled. #### DLL dependencies Listed below are the DLL requirements for using DXVK with any single API. - d3d8: `d3d8.dll` and `d3d9.dll` - d3d9: `d3d9.dll` - d3d10: `d3d10core.dll`, `d3d11.dll` and `dxgi.dll` - d3d11: `d3d11.dll` and `dxgi.dll` ### Notes on Vulkan drivers Before 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. ### Online multi-player games Manipulation 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.** ### HUD The `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: - `devinfo`: Displays the name of the GPU and the driver version. - `fps`: Shows the current frame rate. - `frametimes`: Shows a frame time graph. - `submissions`: Shows the number of command buffers submitted per frame. - `drawcalls`: Shows the number of draw calls and render passes per frame. - `pipelines`: Shows the total number of graphics and compute pipelines. - `descriptors`: Shows the number of descriptor pools and descriptor sets. - `memory`: Shows the amount of device memory allocated and used. - `allocations`: Shows detailed memory chunk suballocation info. - `gpuload`: Shows estimated GPU load. May be inaccurate. - `version`: Shows DXVK version. - `api`: Shows the D3D feature level used by the application. - `cs`: Shows worker thread statistics. - `compiler`: Shows shader compiler activity - `samplers`: Shows the current number of sampler pairs used *[D3D9 Only]* - `ffshaders`: Shows the current number of shaders generated from fixed function state *[D3D9 Only]* - `swvp`: Shows whether or not the device is running in software vertex processing mode *[D3D9 Only]* - `scale=x`: Scales the HUD by a factor of `x` (e.g. `1.5`) - `opacity=y`: Adjusts the HUD opacity by a factor of `y` (e.g. `0.5`, `1.0` being fully opaque). Additionally, `DXVK_HUD=1` has the same effect as `DXVK_HUD=devinfo,fps`, and `DXVK_HUD=full` enables all available HUD elements. ### Logs When 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. On Windows, log files will be created in the game's working directory by default, which is usually next to the game executable. ### Frame rate limit The `DXVK_FRAME_RATE` environment variable can be used to limit the frame rate. A value of `0` uncaps the frame rate, while any positive value will limit rendering to the given number of frames per second. Alternatively, the configuration file can be used. ### Device filter Some applications do not provide a method to select a different GPU. In that case, DXVK can be forced to use a given device: - `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. **Note:** If the device filter is configured incorrectly, it may filter out all devices and applications will be unable to create a D3D device. ### Debugging The following environment variables can be used for **debugging** purposes. - `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. - `DXVK_LOG_LEVEL=none|error|warn|info|debug` Controls message logging. - `DXVK_LOG_PATH=/some/directory` Changes path where log files are stored. Set to `none` to disable log file creation entirely, without disabling logging. - `DXVK_DEBUG=markers|validation` Enables use of the `VK_EXT_debug_utils` extension for translating performance event markers, or to enable Vulkan validation, respecticely. - `DXVK_CONFIG_FILE=/xxx/dxvk.conf` Sets path to the configuration file. - `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. ### Graphics Pipeline Library On 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. In 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`. This feature largely replaces the state cache. **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. ### State cache DXVK caches pipeline state by default, so that shaders can be recompiled ahead of time on subsequent runs of an application, even if the driver's own shader cache got invalidated in the meantime. This cache is enabled by default, and generally reduces stuttering. The following environment variables can be used to control the cache: - `DXVK_STATE_CACHE`: Controls the state cache. The following values are supported: - `disable`: Disables the cache entirely. - `reset`: Clears the cache file. - `DXVK_STATE_CACHE_PATH=/some/directory` Specifies a directory where to put the cache files. Defaults to the current working directory of the application. This feature is mostly only relevant on systems without support for `VK_EXT_graphics_pipeline_library` ## Build instructions In order to pull in all submodules that are needed for building, clone the repository using the following command: ``` git clone --recursive https://github.com/doitsujin/dxvk.git ``` ### Requirements: - [wine 7.1](https://www.winehq.org/) or newer - [Meson](https://mesonbuild.com/) build system (at least version 0.58) - [Mingw-w64](https://www.mingw-w64.org) compiler and headers (at least version 10.0) - [glslang](https://github.com/KhronosGroup/glslang) compiler ### Building DLLs #### The simple way Inside the DXVK directory, run: ``` ./package-release.sh master /your/target/directory --no-package ``` This 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. In 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: ``` # change to build.32 for 32-bit cd /your/target/directory/build.64 ninja install ``` #### Compiling manually ``` # 64-bit build. For 32-bit builds, replace # build-win64.txt with build-win32.txt meson setup --cross-file build-win64.txt --buildtype release --prefix /your/dxvk/directory build.w64 cd build.w64 ninja install ``` The D3D8, D3D9, D3D10, D3D11 and DXGI DLLs will be located in `/your/dxvk/directory/bin`. ### Build troubleshooting DXVK requires threading support from your mingw-w64 build environment. If you are missing this, you may see "error: ‘std::cv_status’ has not been declared" or similar threading related errors. On Debian and Ubuntu, this can be resolved by using the posix alternate, which supports threading. For example, choose the posix alternate from these commands: ``` update-alternatives --config x86_64-w64-mingw32-gcc update-alternatives --config x86_64-w64-mingw32-g++ update-alternatives --config i686-w64-mingw32-gcc update-alternatives --config i686-w64-mingw32-g++ ``` For non debian based distros, make sure that your mingw-w64-gcc cross compiler does have `--enable-threads=posix` enabled during configure. If your distro does ship its mingw-w64-gcc binary with `--enable-threads=win32` you might have to recompile locally or open a bug at your distro's bugtracker to ask for it. # DXVK Native DXVK Native is a version of DXVK which allows it to be used natively without Wine. This 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. [Release builds](https://github.com/doitsujin/dxvk/releases) are built using the Steam Runtime. ### How does it work? DXVK Native replaces certain Windows-isms with a platform and framework-agnostic replacement, for example, `HWND`s can become `SDL_Window*`s, etc. All it takes to do that is to add another WSI backend. **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`. DXVK Native comes with a slim set of Windows header definitions required for D3D9/11 and the MinGW headers for D3D9/11. In most cases, it will end up being plug and play with your renderer, but there may be certain teething issues such as: - `__uuidof(type)` is supported, but `__uuidof(variable)` is not supported. Use `__uuidof_var(variable)` instead. dxvk-2.6.1/RELEASE000066400000000000000000000000061477473124000135040ustar00rootroot000000000000002.6.1 dxvk-2.6.1/VP_DXVK_requirements.json000066400000000000000000000433031477473124000173670ustar00rootroot00000000000000{ "$schema": "https://schema.khronos.org/vulkan/profiles-0.8.1-224.json#", "capabilities": { "vulkan10requirements": { "features": { "VkPhysicalDeviceFeatures": { "robustBufferAccess": true } } }, "vulkan11requirements": { "features": { "VkPhysicalDeviceVulkan11Features": { "multiview": true } }, "properties": { "VkPhysicalDeviceVulkan11Properties": { "maxMultiviewViewCount": 6, "maxMultiviewInstanceIndex": 134217727 } } }, "vulkan12requirements": { "features": { "VkPhysicalDeviceVulkan12Features": { "uniformBufferStandardLayout": true, "subgroupBroadcastDynamicId": true, "imagelessFramebuffer": true, "separateDepthStencilLayouts": true, "hostQueryReset": true, "timelineSemaphore": true, "shaderSubgroupExtendedTypes": true } }, "properties": { "VkPhysicalDeviceVulkan12Properties": { "maxTimelineSemaphoreValueDifference": 2147483647 } } }, "vulkan13requirements": { "features": { "VkPhysicalDeviceVulkan12Features": { "vulkanMemoryModel": true, "vulkanMemoryModelDeviceScope": true, "bufferDeviceAddress": true }, "VkPhysicalDeviceVulkan13Features": { "robustImageAccess": true, "shaderTerminateInvocation": true, "shaderZeroInitializeWorkgroupMemory": true, "synchronization2": true, "shaderIntegerDotProduct": true, "maintenance4": true, "pipelineCreationCacheControl": true, "subgroupSizeControl": true, "computeFullSubgroups": true, "shaderDemoteToHelperInvocation": true, "inlineUniformBlock": true, "dynamicRendering": true } }, "properties": { "VkPhysicalDeviceVulkan13Properties": { "maxBufferSize": 1073741824, "maxInlineUniformBlockSize": 256, "maxPerStageDescriptorInlineUniformBlocks": 4, "maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks": 4, "maxDescriptorSetInlineUniformBlocks": 4, "maxDescriptorSetUpdateAfterBindInlineUniformBlocks": 4, "maxInlineUniformTotalSize": 4 } } }, "dxvk_common_optional": { "extensions": { "VK_KHR_load_store_op_none": 1, "VK_KHR_maintenance5": 1, "VK_KHR_maintenance7": 1, "VK_KHR_present_id": 1, "VK_KHR_present_wait": 1, "VK_KHR_swapchain_mutable_format": 1, "VK_EXT_line_rasterization": 1, "VK_EXT_pageable_device_local_memory": 1, "VK_EXT_swapchain_maintenance1": 1 }, "features": { "VkPhysicalDeviceFeatures": { "wideLines": true }, "VkPhysicalDeviceMaintenance5FeaturesKHR": { "maintenance5": true }, "VkPhysicalDeviceMaintenance7FeaturesKHR": { "maintenance7": true }, "VkPhysicalDevicePresentIdFeaturesKHR": { "presentId": true }, "VkPhysicalDevicePresentWaitFeaturesKHR": { "presentWait": true }, "VkPhysicalDeviceLineRasterizationFeaturesEXT": { "rectangularLines": true, "smoothLines": true }, "VkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT": { "pageableDeviceLocalMemory": true }, "VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT": { "swapchainMaintenance1": true } } }, "d3d9_baseline": { "extensions": { "VK_EXT_robustness2": 1 }, "features": { "VkPhysicalDeviceFeatures": { "geometryShader": true, "imageCubeArray": true, "depthClamp": true, "depthBiasClamp": true, "fillModeNonSolid": true, "sampleRateShading": true, "shaderClipDistance": true, "shaderCullDistance": true, "textureCompressionBC": true, "occlusionQueryPrecise": true, "independentBlend": true, "fullDrawIndexUint32": true, "shaderImageGatherExtended": true }, "VkPhysicalDeviceVulkan12Features": { "samplerMirrorClampToEdge": true }, "VkPhysicalDeviceRobustness2FeaturesEXT": { "nullDescriptor": true, "robustBufferAccess2": true } } }, "d3d9_optional": { "extensions": { "VK_EXT_memory_priority": 1, "VK_EXT_vertex_attribute_divisor": 1, "VK_EXT_depth_bias_control": 1, "VK_EXT_depth_clip_enable": 1, "VK_EXT_custom_border_color": 1, "VK_EXT_attachment_feedback_loop_layout": 1, "VK_EXT_non_seamless_cube_map": 1 }, "features": { "VkPhysicalDeviceFeatures": { "depthBounds": true, "vertexPipelineStoresAndAtomics": true, "pipelineStatisticsQuery": true, "samplerAnisotropy": true }, "VkPhysicalDeviceMemoryPriorityFeaturesEXT": { "memoryPriority": true }, "VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT": { "vertexAttributeInstanceRateDivisor": true, "vertexAttributeInstanceRateZeroDivisor": true }, "VkPhysicalDeviceDepthBiasControlFeaturesEXT": { "depthBiasControl": true, "leastRepresentableValueForceUnormRepresentation": true, "floatRepresentation": true, "depthBiasExact": true }, "VkPhysicalDeviceDepthClipEnableFeaturesEXT": { "depthClipEnable": true }, "VkPhysicalDeviceCustomBorderColorFeaturesEXT": { "customBorderColors": true, "customBorderColorWithoutFormat": true }, "VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT": { "attachmentFeedbackLoopLayout": true }, "VkPhysicalDeviceNonSeamlessCubeMapFeaturesEXT": { "nonSeamlessCubeMap": true } } }, "d3d11_baseline": { "extensions": { "VK_EXT_robustness2": 1, "VK_EXT_transform_feedback": 1 }, "features": { "VkPhysicalDeviceFeatures": { "depthBiasClamp": true, "depthClamp": true, "dualSrcBlend": true, "fillModeNonSolid": true, "fullDrawIndexUint32": true, "geometryShader": true, "imageCubeArray": true, "independentBlend": true, "multiViewport": true, "occlusionQueryPrecise": true, "sampleRateShading": true, "shaderClipDistance": true, "shaderCullDistance": true, "shaderImageGatherExtended": true, "textureCompressionBC": true }, "VkPhysicalDeviceVulkan11Features": { "shaderDrawParameters": true }, "VkPhysicalDeviceVulkan12Features": { "samplerMirrorClampToEdge": true }, "VkPhysicalDeviceRobustness2FeaturesEXT": { "nullDescriptor": true, "robustBufferAccess2": true }, "VkPhysicalDeviceTransformFeedbackFeaturesEXT": { "transformFeedback": true, "geometryStreams": true } } }, "d3d11_baseline_optional":{ "extensions": { "VK_EXT_memory_priority": 1, "VK_EXT_multi_draw": 1, "VK_EXT_vertex_attribute_divisor": 1, "VK_EXT_custom_border_color": 1, "VK_EXT_depth_bias_control": 1, "VK_EXT_depth_clip_enable": 1, "VK_EXT_swapchain_colorspace": 1, "VK_EXT_hdr_metadata": 1 }, "features": { "VkPhysicalDeviceFeatures": { "depthBounds": true, "pipelineStatisticsQuery": true, "logicOp": true, "samplerAnisotropy": true }, "VkPhysicalDeviceMemoryPriorityFeaturesEXT": { "memoryPriority": true }, "VkPhysicalDeviceMultiDrawFeaturesEXT": { "multiDraw": true }, "VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT": { "vertexAttributeInstanceRateDivisor": true, "vertexAttributeInstanceRateZeroDivisor": true }, "VkPhysicalDeviceCustomBorderColorFeaturesEXT": { "customBorderColors": true, "customBorderColorWithoutFormat": true }, "VkPhysicalDeviceDepthBiasControlFeaturesEXT": { "depthBiasControl": true, "leastRepresentableValueForceUnormRepresentation": true, "depthBiasExact": true }, "VkPhysicalDeviceDepthClipEnableFeaturesEXT": { "depthClipEnable": true } } }, "d3d11_level11_0": { "features": { "VkPhysicalDeviceFeatures": { "drawIndirectFirstInstance": true, "fragmentStoresAndAtomics": true, "multiDrawIndirect": true, "tessellationShader": true } } }, "d3d11_level11_0_optional": { "features": { "VkPhysicalDeviceFeatures": { "shaderFloat64": true, "shaderInt64": true } } }, "d3d11_level11_1": { "features": { "VkPhysicalDeviceFeatures": { "logicOp": true, "vertexPipelineStoresAndAtomics": true } } }, "d3d11_level12_0": { "features": { "VkPhysicalDeviceFeatures": { "shaderResourceResidency": true, "shaderResourceMinLod": true, "sparseBinding": true, "sparseResidencyBuffer": true, "sparseResidencyAliased": true, "sparseResidencyImage2D": true }, "VkPhysicalDeviceVulkan12Features": { "samplerFilterMinmax": true } }, "properties": { "VkPhysicalDeviceProperties": { "sparseProperties": { "residencyStandard2DBlockShape": true, "residencyAlignedMipSize": false, "residencyNonResidentStrict": true } } } } }, "profiles": { "VP_DXVK_d3d9_baseline": { "version": 1, "api-version": "1.3.204", "label": "DXVK D3D9 Baseline profile", "description": "DXVK for D3D9 minimum requirements", "capabilities": [ "vulkan10requirements", "vulkan11requirements", "vulkan12requirements", "vulkan13requirements", "d3d9_baseline" ] }, "VP_DXVK_d3d9_optimal": { "version": 1, "api-version": "1.3.224", "label": "DXVK D3D9 Optimal profile", "description": "DXVK for D3D9 including optional capabilities", "capabilities": [ "vulkan10requirements", "vulkan11requirements", "vulkan12requirements", "vulkan13requirements", "dxvk_common_optional", "d3d9_baseline", "d3d9_optional" ] }, "VP_DXVK_d3d10_level_10_1_baseline": { "version": 1, "api-version": "1.3.204", "label": "DXVK D3D10 Level 10.1 Baseline profile", "description": "DXVK for D3D10 Feature Level 10.1 minimum requirements", "capabilities": [ "vulkan10requirements", "vulkan11requirements", "vulkan12requirements", "vulkan13requirements", "d3d11_baseline" ] }, "VP_DXVK_d3d11_level_11_0_baseline": { "version": 1, "api-version": "1.3.204", "label": "DXVK D3D11 Level 11.0 Baseline profile", "description": "DXVK for D3D11 Feature Level 11.0 minimum requirements", "capabilities": [ "vulkan10requirements", "vulkan11requirements", "vulkan12requirements", "vulkan13requirements", "d3d11_baseline", "d3d11_level11_0" ] }, "VP_DXVK_d3d11_level_11_1_baseline": { "version": 1, "api-version": "1.3.204", "label": "DXVK D3D11 Level 11.1 Baseline profile", "description": "DXVK for D3D11 Feature Level 11.1 minimum requirements", "capabilities": [ "vulkan10requirements", "vulkan11requirements", "vulkan12requirements", "vulkan13requirements", "d3d11_baseline", "d3d11_level11_0", "d3d11_level11_1" ] }, "VP_DXVK_d3d11_level_11_1_optimal": { "version": 1, "api-version": "1.3.204", "label": "DXVK D3D11 Level 11.1 Optimal profile", "description": "DXVK for D3D11 Feature Level 11.1 including optional capabilities", "capabilities": [ "vulkan10requirements", "vulkan11requirements", "vulkan12requirements", "vulkan13requirements", "dxvk_common_optional", "d3d11_baseline", "d3d11_baseline_optional", "d3d11_level11_0", "d3d11_level11_0_optional", "d3d11_level11_1" ] }, "VP_DXVK_d3d11_level_12_0_optimal": { "version": 1, "api-version": "1.3.204", "label": "DXVK D3D11 Level 12.0 Optimal profile", "description": "DXVK for D3D11 Feature Level 12.0 including optional capabilities", "capabilities": [ "vulkan10requirements", "vulkan11requirements", "vulkan12requirements", "vulkan13requirements", "dxvk_common_optional", "d3d11_baseline", "d3d11_baseline_optional", "d3d11_level11_0", "d3d11_level11_0_optional", "d3d11_level11_1", "d3d11_level12_0" ] } }, "contributors": { "Philip Rebohle": { "company": "Valve" }, "Joshua Ashton": { "company": "Valve" }, "Pierre-Loup A. Griffais": { "company": "Valve" }, "Georg Lehmann": { "company": "DXVK" }, "Christophe Riccio": { "company": "LunarG" } }, "history": [ { "revision": 4, "date": "2022-12-18", "author": "Joshua Ashton", "comment": "Add VK_EXT_swapchain_colorspace and VK_EXT_hdr_metadata to d3d11_baseline_optional" }, { "revision": 3, "date": "2022-10-13", "author": "Christophe Riccio", "comment": "Factorize history and contributors sections using schema 0.8.1" }, { "revision": 2, "date": "2022-08-30", "author": "Philip Rebohle", "comment": "Add VP_DXVK_d3d11_level_12_0_optimal profile" }, { "revision": 1, "date": "2022-08-22", "author": "Christophe Riccio", "comment": "Initial revision" } ] } dxvk-2.6.1/build-win32.txt000066400000000000000000000004371477473124000153110ustar00rootroot00000000000000[binaries] c = 'i686-w64-mingw32-gcc' cpp = 'i686-w64-mingw32-g++' ar = 'i686-w64-mingw32-ar' strip = 'i686-w64-mingw32-strip' windres = 'i686-w64-mingw32-windres' [properties] needs_exe_wrapper = true [host_machine] system = 'windows' cpu_family = 'x86' cpu = 'x86' endian = 'little' dxvk-2.6.1/build-win64.txt000066400000000000000000000004571477473124000153200ustar00rootroot00000000000000[binaries] c = 'x86_64-w64-mingw32-gcc' cpp = 'x86_64-w64-mingw32-g++' ar = 'x86_64-w64-mingw32-ar' strip = 'x86_64-w64-mingw32-strip' windres = 'x86_64-w64-mingw32-windres' [properties] needs_exe_wrapper = true [host_machine] system = 'windows' cpu_family = 'x86_64' cpu = 'x86_64' endian = 'little' dxvk-2.6.1/buildenv.h.in000066400000000000000000000002261477473124000150730ustar00rootroot00000000000000#pragma once #define DXVK_TARGET "@BUILD_TARGET@" #define DXVK_COMPILER "@BUILD_COMPILER@" #define DXVK_COMPILER_VERSION "@BUILD_COMPILER_VERSION@" dxvk-2.6.1/dxvk.conf000066400000000000000000000610651477473124000143400ustar00rootroot00000000000000# Device filter. Only exposes devices whose Vulkan device name contains # the given string. May be useful to force an application to run on a # specific GPU, but not applications launched by that application. # dxvk.deviceFilter = "" # Expose the HDR10 ColorSpace (DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) # to the application by default. # This shows to the game that the global Windows 'HDR Mode' is enabled. # Many (broken) games will need this to be set to consider exposing HDR output # as determine it based on the DXGIOutput's current ColorSpace instead of # using CheckColorSpaceSupport. # This defaults to the value of the DXVK_HDR environment variable. # # Supported values: True, False # dxgi.enableHDR = True # Expose support for dcomp swap chains with a dummy window. # # This is not a valid implementation of DirectComposition swapchains, # however some games may rely on this functionality to be present while # others may require swap chain creation to fail. # # Supported values: True, False # dxgi.enableDummyCompositionSwapchain = False # Allows the Vulkan driver to opt-in to exclusive full-screen mode on # Windows. Certain features, such as variable refresh rate or HDR, will # not work without this setting, however enabling it will break certain # games that use additional GDI windows, and it will also break alt+tab. # # This setting has no effect on non-Windows platforms. # # Supported values: True, False # dxvk.allowFse = False # Enables Unreal Engine 4 HDR workarounds for games that do not follow # the standard -Win64-Shipping.exe naming scheme. May be needed to avoid # crashes in D3D11 games on HDR-enabled systems due to statically linked # AMDAGS. # # Supported values: True, False # dxgi.enableUe4Workarounds = False # Create the VkSurface on the first call to IDXGISwapChain::Present, # rather than when creating the swap chain. Some games that start # rendering with a different graphics API may require this option, # or otherwise the window may stay black. # # Supported values: True, False # dxgi.deferSurfaceCreation = False # d3d9.deferSurfaceCreation = False # Enforce a stricter maximum frame latency. Overrides the application # setting specified by calling IDXGIDevice::SetMaximumFrameLatency. # Setting this to 0 will have no effect. # # Supported values : 0 - 16 # dxgi.maxFrameLatency = 0 # d3d9.maxFrameLatency = 0 # Enables frame rate limiter. The main purpose of this is to work around # bugs in games that have physics or other simulation tied to their frame # rate, but do not provide their own limiter. # # Supported values # -1: Always disables the limiter # 0: Default behaviour. Limits the frame rate to the selected display # refresh rate when vertical synchronization is enabled if the # actual display mode does not match the game's one. # n: Limit to n frames per second. # dxgi.maxFrameRate = 0 # d3d9.maxFrameRate = 0 # Controls latency sleep and Nvidia Reflex support. # # Supported values: # - Auto: By default, DXVK only supports latency sleep in D3D11 games that # use Reflex if the graphics driver supports VK_NV_low_latency2, # and if dxvk-nvapi is enabled in Proton. # - True: Enables built-in latency reduction based on internal timings. # This assumes that input sampling for any given frame happens after # the D3D9 or DXGI Present call returns; games that render and present # asynchronously will not behave as intended. # Similarly, this will not have any effect in games with built-in frame # rate limiters, or if an external limiter (such as MangoHud) is used. # In some games, enabling this may reduce performance or lead to less # consistent frame pacing. # The implementation will either use VK_NV_low_latency2 if supported # by the driver, or a custom algorithm. # - False: Disable Reflex support as well as built-in latency reduction. # dxvk.latencySleep = Auto # Tolerance for the latency sleep heuristic, in microseconds. Higher values # increase latency, but may lead to better frame pacing in some cases. Does # not have any effect if NV_low_latency2 is used. # # Supported values: Any non-negative number # dxvk.latencyTolerance = 1000 # Disables the use of VK_NV_low_latency2. This will make Reflex unavailable # in games, and if dxvk.latencySleep is set to True, a custom algorithm will # be used for latency control. By default, the extension will not be used in # 32-bit applications due to driver issues. # # Supported values: Auto, True, False # dxvk.disableNvLowLatency2 = Auto # Override PCI vendor and device IDs reported to the application. Can # cause the app to adjust behaviour depending on the selected values. # # Supported values: Any four-digit hex number. # dxgi.customDeviceId = 0000 # dxgi.customVendorId = 0000 # d3d9.customDeviceId = 0000 # d3d9.customVendorId = 0000 # Override the reported device description # # Supported values: Any string. # dxgi.customDeviceDesc = "" # d3d9.customDeviceDesc = "" # Report Nvidia GPUs as AMD GPUs. Unless NVAPI support is explicitly # enabled through Proton, this is done by default in order to work # around crashes or low performance with Nvidia-speciic code paths # in games, especially Unreal Engine. # # Supported values: Auto, True, False # dxgi.hideNvidiaGpu = Auto # Report Nvidia GPUs running on NVK as AMD GPUs. # # Supported values: Auto, True, False # dxgi.hideNvkGpu = Auto # Report AMD GPUs as Nvidia GPUs. This is only done for games that are # known to have issues with AMDAGS or other AMD-specific code paths. # # Supported values: Auto, True, False # dxgi.hideAmdGpu = Auto # Report Intel GPUs as AMD GPUs. This is only done for games that are # known to have issues with Intel-specific libraries such as XESS. # # Supported values: Auto, True, False # dxgi.hideIntelGpu = Auto # Override maximum amount of device memory and shared system memory # reported to the application. This may fix texture streaming issues # in games that do not support cards with large amounts of VRAM. # This is not a hard cap and applications can choose to ignore it. # # Supported values: Any number in Megabytes. # dxgi.maxDeviceMemory = 0 # dxgi.maxSharedMemory = 0 # Overrides synchronization interval (Vsync) for presentation. # Setting this to 0 disables vertical synchronization entirely. # A positive value 'n' will enable Vsync and repeat the same # image n times, and a negative value will have no effect. # # Supported values: Any non-negative number # dxgi.syncInterval = -1 # d3d9.presentInterval = -1 # Controls tearing behaviour with regards to in-game Vsync settings. # # True enables the mailbox present mode in case regular Vsync is disabled. # This eliminates tearing, but may be unsupported on some systems. # # False enables the relaxed fifo present mode in case regular Vsync is enabled. # This should result in tearing but reduce stutter if FPS are too low, # but may be unsupported on some systems. # # Please do not report issues with this option. # # Supported values: Auto, True, False # dxvk.tearFree = Auto # Controls tiler optimizations. Enabling these will alter the behaviour of # submission heuristics and enables some non-default behaviour in DXVK. # This option is only intended to be changed for performance testing and # debugging purposes. # # Supported values: Auto, True, False # dxvk.tilerMode = Auto # Override the maximum feature level that a D3D11 device can be created # with. Setting this to a higher value may allow some applications to run # that would otherwise fail to create a D3D11 device. # # Supported values: 9_1, 9_2, 9_3, 10_0, 10_1, 11_0, 11_1, 12_0, 12_1 # d3d11.maxFeatureLevel = 12_1 # Overrides the maximum allowed tessellation factor. This can be used to # improve performance in titles which overuse tessellation. # # Supported values: Any number between 8 and 64 # d3d11.maxTessFactor = 0 # Enables relaxed pipeline barriers around UAV writes. # # Ignores write-after-write hazards in compute shaders, and all UAV # hazards in graphics shaders. This may improve performance in some # games, but may also introduce rendering issues. Please don't report # bugs with the option enabled. # # Supported values: True, False # d3d11.relaxedBarriers = False # Enables relaxed UAV pipeline barriers in graphics shaders only. # # Similar to the relaxedBarriers option, except it does not apply to # compute UAVs. Please do not report bugs with this option enabled. # # Supported values: True, False # d3d11.relaxedGraphicsBarriers = False # Overrides anisotropic filtering for all samplers. Set this to a positive # value to enable AF for all samplers in the game, or to 0 in order to # disable AF entirely. Negative values will have no effect. # # Supported values: Any number between 0 and 16 # d3d11.samplerAnisotropy = -1 # d3d9.samplerAnisotropy = -1 # Changes the mipmap LOD bias for all samplers. The given number will be # added to the LOD bias provided by the application, rather than replacing # it entirely. Positive values will reduce texture detail, while negative # values may increase sharpness at the cost of shimmer. # # Supported values: Any number between -2.0 and 1.0 # d3d11.samplerLodBias = 0.0 # d3d9.samplerLodBias = 0.0 # Clamps any negative LOD bias to 0. Applies after samplerLodBias has been # applied. May help with games that use a high negative LOD bias by default. # # Supported values: True, False # d3d11.clampNegativeLodBias = False # d3d9.clampNegativeLodBias = False # Declares vertex positions as invariant in order to solve # potential Z-fighting issues at a small performance cost. # # Supported values: True, False # d3d11.invariantPosition = True # d3d9.invariantPosition = True # Forces per-sample rate shading when MSAA is enabled, rather than per-pixel # shading. May improve visual clarity at a significant performance cost, but # may also introduce visual issues in some games. # # Supported values: True, False # d3d11.forceSampleRateShading = False # d3d9.forceSampleRateShading = False # Forces the sample count of all textures to 1, and performs # the needed fixups in resolve operations and shaders. # # Supported values: True, False # d3d11.disableMsaa = False # Clears workgroup memory in compute shaders to zero. Some games don't do # this and rely on undefined behaviour. Enabling may reduce performance. # # Supported values: True, False # d3d11.zeroWorkgroupMemory = False # Forces insertion of memory barriers after writes to group-shared memory in # compute shaders. This is only intended to be used as a workaround for games # that don't properly synchronize access to groupshard variables, and may have # a negative performance impact as it prevents compiler optimizations. # # Supported values: True, False # d3d11.forceVolatileTgsmAccess = False # Forces insertion of full memory and control barriers after accessing any # read-write UAV inside compute shaders. This is only intended to be used as # a workaround for games that do not synchronize access to coherent UAVs, # and will likely have a negative performance impact. # # Supported values: True, False # d3d11.forceComputeUavBarriers = False # Clears mapped memory to zero when suballocated memory is freed. This will # drastically increase CPU overhead and should only be used as a last resort # if a game does not properly initialize mapped buffers on its own. # # Supported values: True, False # dxvk.zeroMappedMemory = False # Allocates dynamic resources with the given set of bind flags in # cached system memory rather than uncached memory or host-visible # VRAM, in order to allow fast readback from the CPU. This is only # useful for buggy applications, and may reduce GPU-bound performance. # # Supported values: Any combination of the following: # - v: Vertex buffers # - i: Index buffers # - c: Constant buffers # - r: Shader resources # - a: All dynamic resources # d3d11.cachedDynamicResources = "" # Force-enables the D3D11 context lock via the ID3D10Multithread # interface. This may be useful to debug race conditions. # # Supported values: True, False # d3d11.enableContextLock = False # Exposes or hides support for driver command lists # # Some games use the feature flag to decide whether to use deferred # contexts or not. We enable this by default, but in some situations # this can lead to issues if games detect an AMD GPU where command # lists are not natively supported on Windows. # # Supported values: True, False # d3d11.exposeDriverCommandLists = True # Reproducible Command Stream # # Ensure that for the same D3D commands the output VK commands # don't change between runs. Useful for comparative benchmarking, # can negatively affect performance and can break some games # that don't use queries correctly. # # Supported values: # - True/False # d3d11.reproducibleCommandStream = False # d3d9.reproducibleCommandStream = False # Sets number of pipeline compiler threads. # # If the graphics pipeline library feature is enabled, the given # number of threads will be used for shader compilation. Some of # these threads will be reserved for high-priority work. # # Supported values: # - 0 to use all available CPU cores # - any positive number to enforce the thread count # dxvk.numCompilerThreads = 0 # Toggles raw SSBO usage. # # Uses storage buffers to implement raw and structured buffer # views. Enabled by default on hardware which has a storage # buffer offset alignment requirement of 4 Bytes (e.g. AMD). # Enabling this may improve performance, but is not safe on # hardware with higher alignment requirements. # # Supported values: # - Auto: Don't change the default # - True, False: Always enable / disable # dxvk.useRawSsbo = Auto # Controls graphics pipeline library behaviour # # Can be used to change VK_EXT_graphics_pipeline_library usage for # debugging purpose. Doing so will likely result in increased stutter # or degraded performance. # # Supported values: # - Auto: Enable if supported, and compile optimized pipelines in the background # - True: Enable if supported, but do not compile optimized pipelines # - False: Always disable the feature # dxvk.enableGraphicsPipelineLibrary = Auto # Controls pipeline lifetime tracking # # If enabled, pipeline libraries will be freed aggressively in order # save memory and address space. Has no effect if graphics pipeline # libraries are not supported or disabled. # # Supported values: # - Auto: Enable tracking for 32-bit applications only # - True: Always enable tracking # - False: Always disable tracking # dxvk.trackPipelineLifetime = Auto # Controls memory defragmentation # # By default, DXVK will try to defragment video memory if there is a # significant amount of memory wasted, or if the allocation budget of # the application is exceeded. This option is provided solely for # debug purposes. # # Supported values: # - True: Enable defragmentation # - Auto: Enable defragmentation, except on blocked drivers # - False: Disable defragmentation # dxvk.enableMemoryDefrag = Auto # Sets enabled HUD elements # # Behaves like the DXVK_HUD environment variable if the # environment variable is not set, otherwise it will be # ignored. The syntax is identical. # dxvk.hud = # Reported shader model # # The shader model to state that we support in the device # capabilities that the application queries. Note that # the value will be limited to 1 for D3D8 applications. # # Supported values: # - 0: Fixed-function only # - 1: Shader Model 1 # - 2: Shader Model 2 # - 3: Shader Model 3 # d3d9.shaderModel = 3 # DPI Awareness # # Decides whether we should call SetProcessDPIAware on device # creation. Helps avoid upscaling blur in modern Windows on # Hi-DPI screens/devices. # # Supported values: # - True, False: Always enable / disable # d3d9.dpiAware = True # Strict Constant Copies # # Decides whether we should always copy defined constants to # the UBO when relative addressing is used, or only when the # relative addressing starts a defined constant. # # Supported values: # - True, False: Always enable / disable # d3d9.strictConstantCopies = False # Strict Pow # # Decides whether we have an opSelect for handling pow(0,0) = 0 # otherwise it becomes undefined. # # Supported values: # - True, False: Always enable / disable # d3d9.strictPow = True # Lenient Clear # # Decides whether or not we fastpath clear anyway if we are close enough to # clearing a full render target. # # Supported values: # - True, False: Always enable / disable # d3d9.lenientClear = False # Max available memory # # Changes the max initial value used in tracking and GetAvailableTextureMem # Value in Megabytes # # Supported values: # - Max Available Memory: Any int32_t # - Memory Tracking Testing: True, False # d3d9.maxAvailableMemory = 4096 # d3d9.memoryTrackTest = False # Force enable/disable floating point quirk emulation # # Force toggle anything * 0 emulation # Setting it to True will use a faster but less accurate approach that works for most games. # Supported values: # - True: Use a faster but less accurate approach. Good enough for most games # - False: Disable float emulation completely # - Strict: Use a slower but more correct approach. Necessary for some games # - Auto: DXVK will pick automatically # d3d9.floatEmulation = Auto # Overrides the application's MSAA level on the swapchain # # Supported values: -1 (application) and 0 to 16 (user override) # d3d9.forceSwapchainMSAA = -1 # Device Local Constant Buffers # # Enables using device local, host accessible memory for constant buffers in D3D9. # This tends to actually be slower for some reason on AMD, # and the exact same performance on NVIDIA. # # Supported values: # - True/False # d3d9.deviceLocalConstantBuffers = False # Support DF formats # # Support the vendor extension DF floating point depth formats on AMD and Intel. # Note that this config is ignored and disabled by default on Nvidia, or when # spoofing a Nvidia GPU, as it does not support these formats natively. # # Supported values: # - True/False # d3d9.supportDFFormats = True # Use D32f for D24 # # Useful for reproducing AMD issues on other hw. # # Supported values: # - True/False # d3d9.useD32forD24 = False # Support X4R4G4B4 # # Support the X4R4G4B4 format. # The Sims 2 is a very broken game. # # Supported values: # - True/False # d3d9.supportX4R4G4B4 = True # Support D16_LOCKABLE # # Support the D16_LOCKABLE format. # Always enabled on AMD, or when spoofing an AMD GPU # via customVendorId, disabled by default on Nvidia and Intel. # # Supported values: # - True/False # d3d9.supportD16Lockable = False # Disable A8 as a Render Target # # Disable support for A8 format render targets # Once again, The Sims 2 is a very broken game. # # Supported values: # - True/False # d3d9.disableA8RT = False # Support for VCache Query # # Support for the vcache query # Not very important as a user config. # Used internally. # # Supported values: # - True/False # Defaults to True if vendorId == 0x10de # d3d9.supportVCache = True # Force Sampler Type Spec Constants # # Useful if games use the wrong image and sampler # type combo like Halo: CE or Spellforce. # Can fix rendering in older, broken games in some instances. # # Supported values: # - True/False # d3d9.forceSamplerTypeSpecConstants = False # Force Aspect Ratio # # Only exposes modes with a given aspect ratio. # Useful for titles that break if they see ultra-wide. # # Supported values: # - Any ratio, ie. "16:9", "4:3" # d3d9.forceAspectRatio = "" # Enumerate by Displays # # Whether we should enumerate D3D9 adapters by display (windows behaviour) # or by physical adapter. # May be useful in PRIME setups. # # Supported values: # - True/False # d3d9.enumerateByDisplays = True # Cached Dynamic Buffers # # Allocates dynamic resources in D3DPOOL_DEFAULT in # cached system memory rather than uncached memory or host-visible # VRAM, in order to allow fast readback from the CPU. This is only # useful for buggy applications, and may reduce GPU-bound performance. # # Supported values: # - True/False # d3d9.cachedDynamicBuffers = False # Seamless Cubes # # Don't use non seamless cube maps even if they are supported. # Non seamless cubes are correct d3d9 behavior, but can produce worse looking edges. # # Supported values: # - True/False # d3d9.seamlessCubes = False # Debug Utils # # Enables debug utils as this is off by default, this enables user annotations like BeginEvent()/EndEvent(). # Alternatively could be enabled with DXVK_DEBUG=markers environment variable. # # Supported values: # - True/False # dxvk.enableDebugUtils = False # Memory limit for locked D3D9 textures # # How much virtual memory will be used for textures (in MB). # 0 to disable the limit. # THIS DOES NOT IMPACT ACTUAL MEMORY CONSUMPTION OR TEXTURE QUALITY. # DO NOT CHANGE THIS UNLESS YOU HAVE A VERY GOOD REASON. # d3d9.textureMemory = 100 # Hide integrated graphics from applications # # Only has an effect when dedicated GPUs are present on the system. It is # not recommended to use this option at all unless absolutely necessary for # a game to work; prefer using DXVK_FILTER_DEVICE_NAME whenever possible. # # Supported values: # - True/False # dxvk.hideIntegratedGraphics = False # Trigger DEVICELOST when losing focus # # D3D9 requires the application to call Device::Reset after # it loses focus in fullscreen. # Some games rely on observing a D3DERR_DEVICELOST or D3DERR_NOTRESET. # Others don't handle it correctly. # # Supported values: # - True/False # d3d9.deviceLossOnFocusLoss = False # Reject Device::Reset if any losable resource is still alive # # D3D9 rejects Device::Reset if there's still any alive resources of specific types. # (State blocks, additional swapchains, D3DPOOL_DEFAULT resources) # Some games leak resources leading to a hang. # # Supported values: # - True/False # d3d9.countLosableResources = True # Add an extra frame buffer when necessary # # Some games create a swapchain with only 1 buffer and still expect GetFrontBufferData() to correctly return back the data of the last present. # To make that work correctly, we add a second buffer and copy to it every single frame. # This is unnecessary for all but a single modded game (Silent Hill 2 Enhanced Edition), that's why it's a config option. # d3d9.extraFrontbuffer = False # Dref scaling for DXS0/FVF # # Some early D3D8 games expect Dref (depth texcoord Z) to be on the range of # [0..2^bitDepth - 1]. This option allows DXSO and fixed vertex function to # scale it back down to [0..1]. # # Supported values: Any number representing bitDepth (typically 24). # d3d8.drefScaling = 0 # Shadow perspective divide # # Older applications designed for Nvidia hardware (or ported from XBox) # expect shadow map texture coordinates to be perspective divided, even # though D3DTTFF_PROJECTED is never set for any texture coordinates. # Older Nvidia cards (GeForce 3, GeForce 4 series) performed this # projection directly in hardware. # # This option forces the D3DTTFF_PROJECTED flag for the necessary stages # when a depth texture is bound to slot 0, in order to emulate older # Nvidia hardware behavior. # # Supported values: # - True/False # d3d8.shadowPerspectiveDivide = False # Force vertex shader declaration # # Some games rely on undefined behavior by using undeclared vertex shader inputs. # The simplest way to fix them is to modify their vertex shader decl. # # This option takes a comma-separated list of colon-separated number pairs, where # the first number is a D3DVSDE_REGISTER value, the second is a D3DVSDT_TYPE value. # # Supported values: # - e.g. "0:2,3:2,7:1" for float3 position : v0, float3 normal : v3, float2 uv : v7. # d3d8.forceVsDecl = "" # Draw call batching # # Specialized drawcall batcher, typically for games that draw a lot of similar # geometry in separate drawcalls (sometimes even one triangle at a time). # # May hurt performance or introduce graphical artifacts outside of # specific games that are known to benefit from it. # # Supported values: # - True/False # d3d8.batching = False # P8 texture support workaround # # Early Nvidia GPUs, such as the GeForce 4 generation cards, included and exposed # P8 texture support. However, it was no longer advertised with cards in the FX series # and above. ATI/AMD drivers and hardware were most likely in a similar situation. # # This option will ensure all P8 textures are placed in D3DPOOL_SCRATCH, so that # their creation is guaranteed to succeed even if the format is unsupported. # Can help older titles that don't properly handle the lack of P8 support. # # Supported values: # - True/False # d3d8.placeP8InScratch = False # Legacy discard buffer behavior # # Older applications may rely on D3DLOCK_DISCARD being ignored for everything # except D3DUSAGE_DYNAMIC + D3DUSAGE_WRITEONLY buffers, however this approach # incurs a performance penalty. # # Supported values: # - True/False # d3d8.forceLegacyDiscard = False dxvk-2.6.1/include/000077500000000000000000000000001477473124000141305ustar00rootroot00000000000000dxvk-2.6.1/include/native/000077500000000000000000000000001477473124000154165ustar00rootroot00000000000000dxvk-2.6.1/include/native/directx/000077500000000000000000000000001477473124000170605ustar00rootroot00000000000000dxvk-2.6.1/include/native/meson.build000066400000000000000000000005621477473124000175630ustar00rootroot00000000000000install_subdir( 'directx', install_dir: get_option('includedir') / 'dxvk', strip_directory: true, exclude_files: '.git' ) install_subdir( 'windows', install_dir: get_option('includedir') / 'dxvk', strip_directory: true, ) install_headers( 'wsi/native_wsi.h', 'wsi/native_sdl3.h', 'wsi/native_sdl2.h', 'wsi/native_glfw.h', subdir: 'dxvk/wsi', ) dxvk-2.6.1/include/native/windows/000077500000000000000000000000001477473124000171105ustar00rootroot00000000000000dxvk-2.6.1/include/native/windows/oaidl.h000066400000000000000000000000341477473124000203460ustar00rootroot00000000000000#pragma once // Don't care.dxvk-2.6.1/include/native/windows/objbase.h000066400000000000000000000000341477473124000206630ustar00rootroot00000000000000#pragma once // Don't care.dxvk-2.6.1/include/native/windows/ocidl.h000066400000000000000000000000341477473124000203500ustar00rootroot00000000000000#pragma once // Don't care.dxvk-2.6.1/include/native/windows/ole2.h000066400000000000000000000000341477473124000201170ustar00rootroot00000000000000#pragma once // Don't care.dxvk-2.6.1/include/native/windows/poppack.h000066400000000000000000000004351477473124000207200ustar00rootroot00000000000000/** * This file has no copyright assigned and is placed in the Public Domain. * This file is part of the mingw-w64 runtime package. * No warranty is given; refer to the file DISCLAIMER.PD within this package. */ #if !(defined(lint) || defined(RC_INVOKED)) #pragma pack(pop) #endif dxvk-2.6.1/include/native/windows/pshpack4.h000066400000000000000000000004401477473124000207740ustar00rootroot00000000000000/** * This file has no copyright assigned and is placed in the Public Domain. * This file is part of the mingw-w64 runtime package. * No warranty is given; refer to the file DISCLAIMER.PD within this package. */ #if !(defined(lint) || defined(RC_INVOKED)) #pragma pack(push,4) #endif dxvk-2.6.1/include/native/windows/rpc.h000066400000000000000000000000341477473124000200420ustar00rootroot00000000000000#pragma once // Don't care.dxvk-2.6.1/include/native/windows/rpcndr.h000066400000000000000000000000341477473124000205460ustar00rootroot00000000000000#pragma once // Don't care.dxvk-2.6.1/include/native/windows/unknwn.h000066400000000000000000000023561477473124000206070ustar00rootroot00000000000000#pragma once #include "windows_base.h" typedef interface IUnknown IUnknown; DEFINE_GUID(IID_IUnknown, 0x00000000,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46) #ifdef __cplusplus struct IUnknown { public: virtual HRESULT QueryInterface(REFIID riid, void** ppvObject) = 0; template HRESULT STDMETHODCALLTYPE QueryInterface(Q **pp) { return QueryInterface(__uuidof(Q), (void **)pp); } virtual ULONG AddRef() = 0; virtual ULONG Release() = 0; }; #else typedef struct IUnknownVtbl { BEGIN_INTERFACE HRESULT (STDMETHODCALLTYPE *QueryInterface)( IUnknown *This, REFIID riid, void **ppvObject ); ULONG (STDMETHODCALLTYPE *AddRef)(IUnknown *This); ULONG (STDMETHODCALLTYPE *Release)(IUnknown *This); END_INTERFACE } IUnknownVtbl; interface IUnknown { CONST_VTBL struct IUnknownVtbl *lpVtbl; }; #define IUnknown_AddRef(This) ((This)->lpVtbl->AddRef(This)) #define IUnknown_Release(This) ((This)->lpVtbl->Release(This)) #endif // __cplusplus DECLARE_UUIDOF_HELPER(IUnknown, 0x00000000,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46) #define IID_PPV_ARGS(ppType) __uuidof(decltype(**(ppType))), [](auto** pp) { (void)static_cast(*pp); return reinterpret_cast(pp); }(ppType) dxvk-2.6.1/include/native/windows/windows.h000066400000000000000000000000731477473124000207530ustar00rootroot00000000000000#pragma once #include "windows_base.h" #include "unknwn.h"dxvk-2.6.1/include/native/windows/windows_base.h000066400000000000000000000260371477473124000217550ustar00rootroot00000000000000#pragma once #ifdef __cplusplus #include #include #else #include #include #include #endif // __cplusplus // GCC complains about the COM interfaces // not having virtual destructors // and class conversion for C...DESC helper types #if defined(__GNUC__) && defined(__cplusplus) #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" #pragma GCC diagnostic ignored "-Wclass-conversion" #endif // __GNUC__ && __cplusplus typedef int32_t INT; typedef uint32_t UINT; typedef int32_t LONG; typedef uint32_t ULONG; typedef int32_t *LPLONG; typedef int32_t HRESULT; typedef wchar_t WCHAR; typedef WCHAR *NWPSTR, *LPWSTR, *PWSTR; typedef unsigned char UCHAR, *PUCHAR; typedef char CHAR; typedef const CHAR *LPCSTR, *PCSTR; typedef INT BOOL; typedef BOOL WINBOOL; typedef uint16_t UINT16; typedef uint32_t UINT32; typedef uint64_t UINT64; typedef void VOID; typedef void* PVOID; typedef void* LPVOID; typedef const void* LPCVOID; typedef size_t SIZE_T; typedef int8_t INT8; typedef uint8_t UINT8; typedef uint8_t BYTE; typedef int16_t SHORT; typedef uint16_t USHORT; typedef int64_t LONGLONG; typedef int64_t INT64; typedef uint64_t ULONGLONG; typedef uint64_t UINT64; typedef intptr_t LONG_PTR; typedef uintptr_t ULONG_PTR; typedef float FLOAT; #ifndef GUID_DEFINED #define GUID_DEFINED typedef struct GUID { uint32_t Data1; uint16_t Data2; uint16_t Data3; uint8_t Data4[8]; } GUID; #endif // GUID_DEFINED typedef GUID UUID; typedef GUID IID; #ifdef __cplusplus #define REFIID const IID& #define REFGUID const GUID& #define REFCLSID const GUID& #else #define REFIID const IID* #define REFGUID const GUID* #define REFCLSID const GUID* const #endif // __cplusplus #ifdef __cplusplus template constexpr GUID __uuidof_helper(); #define __uuidof(T) __uuidof_helper() #define __uuidof_var(T) __uuidof_helper() inline bool operator==(const GUID& a, const GUID& b) { return std::memcmp(&a, &b, sizeof(GUID)) == 0; } inline bool operator!=(const GUID& a, const GUID& b) { return std::memcmp(&a, &b, sizeof(GUID)) != 0; } #endif // __cplusplus typedef uint32_t DWORD; typedef uint16_t WORD; typedef DWORD *LPDWORD; typedef void* HANDLE; typedef HANDLE HMONITOR; typedef HANDLE HDC; typedef HANDLE HMODULE; typedef HANDLE HINSTANCE; typedef HANDLE HWND; typedef HANDLE HKEY; typedef HANDLE *LPHANDLE; typedef DWORD COLORREF; #if INTPTR_MAX == INT64_MAX typedef int64_t INT_PTR; typedef uint64_t UINT_PTR; #else typedef int32_t INT_PTR; typedef uint32_t UINT_PTR; #endif typedef INT_PTR* PINT_PTR; typedef UINT_PTR* PUINT_PTR; #ifdef STRICT #define DECLARE_HANDLE(a) typedef struct a##__ { int unused; } *a #else /*STRICT*/ #define DECLARE_HANDLE(a) typedef HANDLE a #endif /*STRICT*/ typedef char* LPSTR; typedef wchar_t* LPWSTR; typedef const char* LPCSTR; typedef const wchar_t* LPCWSTR; typedef struct LUID { DWORD LowPart; LONG HighPart; } LUID; typedef struct POINT { LONG x; LONG y; } POINT; typedef POINT* LPPOINT; typedef struct RECT { LONG left; LONG top; LONG right; LONG bottom; } RECT,*PRECT,*NPRECT,*LPRECT; typedef struct SIZE { LONG cx; LONG cy; } SIZE,*PSIZE,*LPSIZE; typedef union { struct { DWORD LowPart; LONG HighPart; }; struct { DWORD LowPart; LONG HighPart; } u; LONGLONG QuadPart; } LARGE_INTEGER; typedef struct MEMORYSTATUS { DWORD dwLength; SIZE_T dwTotalPhys; } MEMORYSTATUS; typedef struct SECURITY_ATTRIBUTES { DWORD nLength; void* lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES; typedef struct PALETTEENTRY { BYTE peRed; BYTE peGreen; BYTE peBlue; BYTE peFlags; } PALETTEENTRY, *PPALETTEENTRY, *LPPALETTEENTRY; typedef struct RGNDATAHEADER { DWORD dwSize; DWORD iType; DWORD nCount; DWORD nRgnSize; RECT rcBound; } RGNDATAHEADER; typedef struct RGNDATA { RGNDATAHEADER rdh; char Buffer[1]; } RGNDATA,*PRGNDATA,*NPRGNDATA,*LPRGNDATA; // Ignore these. #define STDMETHODCALLTYPE #define __stdcall #define CONST const #define CONST_VTBL const #define TRUE 1 #define FALSE 0 #define WAIT_TIMEOUT 0x00000102 #define WAIT_FAILED 0xffffffff #define WAIT_OBJECT_0 0 #define WAIT_ABANDONED 0x00000080 #define interface struct #define MIDL_INTERFACE(x) struct #ifdef __cplusplus #define DEFINE_GUID(iid, a, b, c, d, e, f, g, h, i, j, k) \ constexpr GUID iid = {a,b,c,{d,e,f,g,h,i,j,k}}; #define DECLARE_UUIDOF_HELPER(type, a, b, c, d, e, f, g, h, i, j, k) \ extern "C++" { template <> constexpr GUID __uuidof_helper() { return GUID{a,b,c,{d,e,f,g,h,i,j,k}}; } } \ extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } #else #define DEFINE_GUID(iid, a, b, c, d, e, f, g, h, i, j, k) \ static const GUID iid = {a,b,c,{d,e,f,g,h,i,j,k}}; #define DECLARE_UUIDOF_HELPER(type, a, b, c, d, e, f, g, h, i, j, k) #endif // __cplusplus #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) #define S_OK 0 #define S_FALSE 1 #define E_INVALIDARG ((HRESULT)0x80070057) #define E_FAIL ((HRESULT)0x80004005) #define E_NOINTERFACE ((HRESULT)0x80004002) #define E_NOTIMPL ((HRESULT)0x80004001) #define E_OUTOFMEMORY ((HRESULT)0x8007000E) #define E_POINTER ((HRESULT)0x80004003) #define DXGI_STATUS_OCCLUDED ((HRESULT)0x087a0001) #define DXGI_STATUS_CLIPPED ((HRESULT)0x087a0002) #define DXGI_STATUS_NO_REDIRECTION ((HRESULT)0x087a0004) #define DXGI_STATUS_NO_DESKTOP_ACCESS ((HRESULT)0x087a0005) #define DXGI_STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE ((HRESULT)0x087a0006) #define DXGI_STATUS_MODE_CHANGED ((HRESULT)0x087a0007) #define DXGI_STATUS_MODE_CHANGE_IN_PROGRESS ((HRESULT)0x087a0008) #define DXGI_STATUS_UNOCCLUDED ((HRESULT)0x087a0009) #define DXGI_STATUS_DDA_WAS_STILL_DRAWING ((HRESULT)0x087a000a) #define DXGI_STATUS_PRESENT_REQUIRED ((HRESULT)0x087a002f) #define DXGI_ERROR_INVALID_CALL ((HRESULT)0x887A0001) #define DXGI_ERROR_NOT_FOUND ((HRESULT)0x887A0002) #define DXGI_ERROR_MORE_DATA ((HRESULT)0x887A0003) #define DXGI_ERROR_UNSUPPORTED ((HRESULT)0x887A0004) #define DXGI_ERROR_DEVICE_REMOVED ((HRESULT)0x887A0005) #define DXGI_ERROR_DEVICE_HUNG ((HRESULT)0x887A0006) #define DXGI_ERROR_DEVICE_RESET ((HRESULT)0x887A0007) #define DXGI_ERROR_WAS_STILL_DRAWING ((HRESULT)0x887A000A) #define DXGI_ERROR_FRAME_STATISTICS_DISJOINT ((HRESULT)0x887A000B) #define DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE ((HRESULT)0x887A000C) #define DXGI_ERROR_DRIVER_INTERNAL_ERROR ((HRESULT)0x887A0020) #define DXGI_ERROR_NONEXCLUSIVE ((HRESULT)0x887A0021) #define DXGI_ERROR_NOT_CURRENTLY_AVAILABLE ((HRESULT)0x887A0022) #define DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED ((HRESULT)0x887A0023) #define DXGI_ERROR_REMOTE_OUTOFMEMORY ((HRESULT)0x887A0024) #define DXGI_ERROR_ACCESS_LOST ((HRESULT)0x887A0026) #define DXGI_ERROR_WAIT_TIMEOUT ((HRESULT)0x887A0027) #define DXGI_ERROR_SESSION_DISCONNECTED ((HRESULT)0x887A0028) #define DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE ((HRESULT)0x887A0029) #define DXGI_ERROR_CANNOT_PROTECT_CONTENT ((HRESULT)0x887A002A) #define DXGI_ERROR_ACCESS_DENIED ((HRESULT)0x887A002B) #define DXGI_ERROR_NAME_ALREADY_EXISTS ((HRESULT)0x887A002C) #define DXGI_ERROR_SDK_COMPONENT_MISSING ((HRESULT)0x887A002D) #define D3D11_ERROR_DEFERRED_CONTEXT_MAP_WITHOUT_INITIAL_DISCARD ((HRESULT)0x887C0004) #define WINAPI #define WINUSERAPI #define RGB(r,g,b) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16))) #define MAKE_HRESULT(sev,fac,code) \ ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) ) #ifdef __cplusplus #define STDMETHOD(name) virtual HRESULT name #define STDMETHOD_(type, name) virtual type name #else #define STDMETHOD(name) HRESULT (STDMETHODCALLTYPE *name) #define STDMETHOD_(type, name) type (STDMETHODCALLTYPE *name) #endif // __cplusplus #define THIS_ #define THIS #define __C89_NAMELESSSTRUCTNAME #define __C89_NAMELESSUNIONNAME #define __C89_NAMELESSUNIONNAME1 #define __C89_NAMELESSUNIONNAME2 #define __C89_NAMELESSUNIONNAME3 #define __C89_NAMELESSUNIONNAME4 #define __C89_NAMELESSUNIONNAME5 #define __C89_NAMELESSUNIONNAME6 #define __C89_NAMELESSUNIONNAME7 #define __C89_NAMELESSUNIONNAME8 #define __C89_NAMELESS #define DUMMYUNIONNAME #define DUMMYSTRUCTNAME #define DUMMYUNIONNAME1 #define DUMMYUNIONNAME2 #define DUMMYUNIONNAME3 #define DUMMYUNIONNAME4 #define DUMMYUNIONNAME5 #define DUMMYUNIONNAME6 #define DUMMYUNIONNAME7 #define DUMMYUNIONNAME8 #define DUMMYUNIONNAME9 #ifdef __cplusplus #define DECLARE_INTERFACE(x) struct x #define DECLARE_INTERFACE_(x, y) struct x : public y #else #ifdef CONST_VTABLE #define DECLARE_INTERFACE(x) \ typedef interface x { \ const struct x##Vtbl *lpVtbl; \ } x; \ typedef const struct x##Vtbl x##Vtbl; \ const struct x##Vtbl #else #define DECLARE_INTERFACE(x) \ typedef interface x { \ struct x##Vtbl *lpVtbl; \ } x; \ typedef struct x##Vtbl x##Vtbl; \ struct x##Vtbl #endif // CONST_VTABLE #define DECLARE_INTERFACE_(x, y) DECLARE_INTERFACE(x) #endif // __cplusplus #define BEGIN_INTERFACE #define END_INTERFACE #ifdef __cplusplus #define PURE = 0 #else #define PURE #endif // __cplusplus #define DECLSPEC_SELECTANY #define __MSABI_LONG(x) x #define ENUM_CURRENT_SETTINGS ((DWORD)-1) #define ENUM_REGISTRY_SETTINGS ((DWORD)-2) #define INVALID_HANDLE_VALUE ((HANDLE)-1) #define DUPLICATE_CLOSE_SOURCE ((DWORD)0x1) #define DUPLICATE_SAME_ACCESS ((DWORD)0x2) #define FAILED(hr) ((HRESULT)(hr) < 0) #define SUCCEEDED(hr) ((HRESULT)(hr) >= 0) #define RtlZeroMemory(Destination,Length) memset((Destination),0,(Length)) #define ZeroMemory RtlZeroMemory #ifndef DEFINE_ENUM_FLAG_OPERATORS #ifdef __cplusplus # define DEFINE_ENUM_FLAG_OPERATORS(type) \ extern "C++" \ { \ inline type operator &(type x, type y) { return (type)((int)x & (int)y); } \ inline type operator &=(type &x, type y) { return (type &)((int &)x &= (int)y); } \ inline type operator ~(type x) { return (type)~(int)x; } \ inline type operator |(type x, type y) { return (type)((int)x | (int)y); } \ inline type operator |=(type &x, type y) { return (type &)((int &)x |= (int)y); } \ inline type operator ^(type x, type y) { return (type)((int)x ^ (int)y); } \ inline type operator ^=(type &x, type y) { return (type &)((int &)x ^= (int)y); } \ } #else # define DEFINE_ENUM_FLAG_OPERATORS(type) #endif #endif /* DEFINE_ENUM_FLAG_OPERATORS */ dxvk-2.6.1/include/native/wsi/000077500000000000000000000000001477473124000162205ustar00rootroot00000000000000dxvk-2.6.1/include/native/wsi/native_glfw.h000066400000000000000000000011471477473124000207010ustar00rootroot00000000000000#include #include namespace dxvk::wsi { inline GLFWwindow* fromHwnd(HWND hWindow) { return reinterpret_cast(hWindow); } inline HWND toHwnd(GLFWwindow* pWindow) { return reinterpret_cast(pWindow); } // Offset so null HMONITORs go to -1 inline int32_t fromHmonitor(HMONITOR hMonitor) { return static_cast(reinterpret_cast(hMonitor)) - 1; } // Offset so -1 display id goes to 0 == NULL inline HMONITOR toHmonitor(int32_t displayId) { return reinterpret_cast(static_cast(displayId + 1)); } }dxvk-2.6.1/include/native/wsi/native_sdl2.h000066400000000000000000000011411477473124000206000ustar00rootroot00000000000000#include #include namespace dxvk::wsi { inline SDL_Window* fromHwnd(HWND hWindow) { return reinterpret_cast(hWindow); } inline HWND toHwnd(SDL_Window* pWindow) { return reinterpret_cast(pWindow); } // Offset so null HMONITORs go to -1 inline int32_t fromHmonitor(HMONITOR hMonitor) { return static_cast(reinterpret_cast(hMonitor)) - 1; } // Offset so -1 display id goes to 0 == NULL inline HMONITOR toHmonitor(int32_t displayId) { return reinterpret_cast(static_cast(displayId + 1)); } } dxvk-2.6.1/include/native/wsi/native_sdl3.h000066400000000000000000000011241477473124000206020ustar00rootroot00000000000000#include #include namespace dxvk::wsi { inline SDL_Window* fromHwnd(HWND hWindow) { return reinterpret_cast(hWindow); } inline HWND toHwnd(SDL_Window* pWindow) { return reinterpret_cast(pWindow); } // Offset so null HMONITORs go to -1 inline SDL_DisplayID fromHmonitor(HMONITOR hMonitor) { return SDL_DisplayID(reinterpret_cast(hMonitor)); } // Offset so -1 display id goes to 0 == NULL inline HMONITOR toHmonitor(SDL_DisplayID display) { return reinterpret_cast(uintptr_t(display)); } } dxvk-2.6.1/include/native/wsi/native_wsi.h000066400000000000000000000004041477473124000205370ustar00rootroot00000000000000#pragma once #ifdef DXVK_WSI_WIN32 #error You shouldnt be using this code path. #elif DXVK_WSI_SDL3 #include "wsi/native_sdl3.h" #elif DXVK_WSI_SDL2 #include "wsi/native_sdl2.h" #elif DXVK_WSI_GLFW #include "wsi/native_glfw.h" #else #error Unknown wsi! #endifdxvk-2.6.1/include/openvr/000077500000000000000000000000001477473124000154415ustar00rootroot00000000000000dxvk-2.6.1/include/openvr/LICENSE000066400000000000000000000027171477473124000164550ustar00rootroot00000000000000Copyright (c) 2015, Valve Corporation All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.dxvk-2.6.1/include/openvr/openvr.hpp000066400000000000000000006200211477473124000174640ustar00rootroot00000000000000#pragma once // openvr.h //========= Copyright Valve Corporation ============// // Dynamically generated file. Do not modify this file directly. #ifndef _OPENVR_API #define _OPENVR_API #include // vrtypes.h #ifndef _INCLUDE_VRTYPES_H #define _INCLUDE_VRTYPES_H // Forward declarations to avoid requiring vulkan.h struct VkDevice_T; struct VkPhysicalDevice_T; struct VkInstance_T; struct VkQueue_T; // Forward declarations to avoid requiring d3d12.h struct ID3D12Resource; struct ID3D12CommandQueue; namespace vr { #pragma pack( push, 8 ) typedef void* glSharedTextureHandle_t; typedef int32_t glInt_t; typedef uint32_t glUInt_t; // right-handed system // +y is up // +x is to the right // -z is forward // Distance unit is meters struct HmdMatrix34_t { float m[3][4]; }; struct HmdMatrix44_t { float m[4][4]; }; struct HmdVector3_t { float v[3]; }; struct HmdVector4_t { float v[4]; }; struct HmdVector3d_t { double v[3]; }; struct HmdVector2_t { float v[2]; }; struct HmdQuaternion_t { double w, x, y, z; }; struct HmdQuaternionf_t { float w, x, y, z; }; struct HmdColor_t { float r, g, b, a; }; struct HmdQuad_t { HmdVector3_t vCorners[ 4 ]; }; struct HmdRect2_t { HmdVector2_t vTopLeft; HmdVector2_t vBottomRight; }; /** Used to return the post-distortion UVs for each color channel. * UVs range from 0 to 1 with 0,0 in the upper left corner of the * source render target. The 0,0 to 1,1 range covers a single eye. */ struct DistortionCoordinates_t { float rfRed[2]; float rfGreen[2]; float rfBlue[2]; }; enum EVREye { Eye_Left = 0, Eye_Right = 1 }; enum ETextureType { TextureType_DirectX = 0, // Handle is an ID3D11Texture TextureType_OpenGL = 1, // Handle is an OpenGL texture name or an OpenGL render buffer name, depending on submit flags TextureType_Vulkan = 2, // Handle is a pointer to a VRVulkanTextureData_t structure TextureType_IOSurface = 3, // Handle is a macOS cross-process-sharable IOSurfaceRef TextureType_DirectX12 = 4, // Handle is a pointer to a D3D12TextureData_t structure TextureType_DXGISharedHandle = 5, // Handle is a HANDLE DXGI share handle, only supported for Overlay render targets. // this texture is used directly by our renderer, so only perform atomic (copyresource or resolve) on it }; enum EColorSpace { ColorSpace_Auto = 0, // Assumes 'gamma' for 8-bit per component formats, otherwise 'linear'. This mirrors the DXGI formats which have _SRGB variants. ColorSpace_Gamma = 1, // Texture data can be displayed directly on the display without any conversion (a.k.a. display native format). ColorSpace_Linear = 2, // Same as gamma but has been converted to a linear representation using DXGI's sRGB conversion algorithm. }; struct Texture_t { void* handle; // See ETextureType definition above ETextureType eType; EColorSpace eColorSpace; }; // Handle to a shared texture (HANDLE on Windows obtained using OpenSharedResource). typedef uint64_t SharedTextureHandle_t; #define INVALID_SHARED_TEXTURE_HANDLE ((vr::SharedTextureHandle_t)0) enum ETrackingResult { TrackingResult_Uninitialized = 1, TrackingResult_Calibrating_InProgress = 100, TrackingResult_Calibrating_OutOfRange = 101, TrackingResult_Running_OK = 200, TrackingResult_Running_OutOfRange = 201, }; typedef uint32_t DriverId_t; static const uint32_t k_nDriverNone = 0xFFFFFFFF; static const uint32_t k_unMaxDriverDebugResponseSize = 32768; /** Used to pass device IDs to API calls */ typedef uint32_t TrackedDeviceIndex_t; static const uint32_t k_unTrackedDeviceIndex_Hmd = 0; static const uint32_t k_unMaxTrackedDeviceCount = 64; static const uint32_t k_unTrackedDeviceIndexOther = 0xFFFFFFFE; static const uint32_t k_unTrackedDeviceIndexInvalid = 0xFFFFFFFF; /** Describes what kind of object is being tracked at a given ID */ enum ETrackedDeviceClass { TrackedDeviceClass_Invalid = 0, // the ID was not valid. TrackedDeviceClass_HMD = 1, // Head-Mounted Displays TrackedDeviceClass_Controller = 2, // Tracked controllers TrackedDeviceClass_GenericTracker = 3, // Generic trackers, similar to controllers TrackedDeviceClass_TrackingReference = 4, // Camera and base stations that serve as tracking reference points TrackedDeviceClass_DisplayRedirect = 5, // Accessories that aren't necessarily tracked themselves, but may redirect video output from other tracked devices }; /** Describes what specific role associated with a tracked device */ enum ETrackedControllerRole { TrackedControllerRole_Invalid = 0, // Invalid value for controller type TrackedControllerRole_LeftHand = 1, // Tracked device associated with the left hand TrackedControllerRole_RightHand = 2, // Tracked device associated with the right hand TrackedControllerRole_OptOut = 3, // Tracked device is opting out of left/right hand selection TrackedControllerRole_Max = 4 }; /** describes a single pose for a tracked object */ struct TrackedDevicePose_t { HmdMatrix34_t mDeviceToAbsoluteTracking; HmdVector3_t vVelocity; // velocity in tracker space in m/s HmdVector3_t vAngularVelocity; // angular velocity in radians/s (?) ETrackingResult eTrackingResult; bool bPoseIsValid; // This indicates that there is a device connected for this spot in the pose array. // It could go from true to false if the user unplugs the device. bool bDeviceIsConnected; }; /** Identifies which style of tracking origin the application wants to use * for the poses it is requesting */ enum ETrackingUniverseOrigin { TrackingUniverseSeated = 0, // Poses are provided relative to the seated zero pose TrackingUniverseStanding = 1, // Poses are provided relative to the safe bounds configured by the user TrackingUniverseRawAndUncalibrated = 2, // 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. }; typedef uint64_t WebConsoleHandle_t; #define INVALID_WEB_CONSOLE_HANDLE ((vr::WebConsoleHandle_t)0) // Refers to a single container of properties typedef uint64_t PropertyContainerHandle_t; typedef uint32_t PropertyTypeTag_t; static const PropertyContainerHandle_t k_ulInvalidPropertyContainer = 0; static const PropertyTypeTag_t k_unInvalidPropertyTag = 0; typedef PropertyContainerHandle_t DriverHandle_t; static const PropertyContainerHandle_t k_ulInvalidDriverHandle = 0; // Use these tags to set/get common types as struct properties static const PropertyTypeTag_t k_unFloatPropertyTag = 1; static const PropertyTypeTag_t k_unInt32PropertyTag = 2; static const PropertyTypeTag_t k_unUint64PropertyTag = 3; static const PropertyTypeTag_t k_unBoolPropertyTag = 4; static const PropertyTypeTag_t k_unStringPropertyTag = 5; static const PropertyTypeTag_t k_unHmdMatrix34PropertyTag = 20; static const PropertyTypeTag_t k_unHmdMatrix44PropertyTag = 21; static const PropertyTypeTag_t k_unHmdVector3PropertyTag = 22; static const PropertyTypeTag_t k_unHmdVector4PropertyTag = 23; static const PropertyTypeTag_t k_unHiddenAreaPropertyTag = 30; static const PropertyTypeTag_t k_unPathHandleInfoTag = 31; static const PropertyTypeTag_t k_unActionPropertyTag = 32; static const PropertyTypeTag_t k_unInputValuePropertyTag = 33; static const PropertyTypeTag_t k_unWildcardPropertyTag = 34; static const PropertyTypeTag_t k_unHapticVibrationPropertyTag = 35; static const PropertyTypeTag_t k_unSkeletonPropertyTag = 36; static const PropertyTypeTag_t k_unOpenVRInternalReserved_Start = 1000; static const PropertyTypeTag_t k_unOpenVRInternalReserved_End = 10000; /** Each entry in this enum represents a property that can be retrieved about a * tracked device. Many fields are only valid for one ETrackedDeviceClass. */ enum ETrackedDeviceProperty { Prop_Invalid = 0, // general properties that apply to all device classes Prop_TrackingSystemName_String = 1000, Prop_ModelNumber_String = 1001, Prop_SerialNumber_String = 1002, Prop_RenderModelName_String = 1003, Prop_WillDriftInYaw_Bool = 1004, Prop_ManufacturerName_String = 1005, Prop_TrackingFirmwareVersion_String = 1006, Prop_HardwareRevision_String = 1007, Prop_AllWirelessDongleDescriptions_String = 1008, Prop_ConnectedWirelessDongle_String = 1009, Prop_DeviceIsWireless_Bool = 1010, Prop_DeviceIsCharging_Bool = 1011, Prop_DeviceBatteryPercentage_Float = 1012, // 0 is empty, 1 is full Prop_StatusDisplayTransform_Matrix34 = 1013, Prop_Firmware_UpdateAvailable_Bool = 1014, Prop_Firmware_ManualUpdate_Bool = 1015, Prop_Firmware_ManualUpdateURL_String = 1016, Prop_HardwareRevision_Uint64 = 1017, Prop_FirmwareVersion_Uint64 = 1018, Prop_FPGAVersion_Uint64 = 1019, Prop_VRCVersion_Uint64 = 1020, Prop_RadioVersion_Uint64 = 1021, Prop_DongleVersion_Uint64 = 1022, Prop_BlockServerShutdown_Bool = 1023, Prop_CanUnifyCoordinateSystemWithHmd_Bool = 1024, Prop_ContainsProximitySensor_Bool = 1025, Prop_DeviceProvidesBatteryStatus_Bool = 1026, Prop_DeviceCanPowerOff_Bool = 1027, Prop_Firmware_ProgrammingTarget_String = 1028, Prop_DeviceClass_Int32 = 1029, Prop_HasCamera_Bool = 1030, Prop_DriverVersion_String = 1031, Prop_Firmware_ForceUpdateRequired_Bool = 1032, Prop_ViveSystemButtonFixRequired_Bool = 1033, Prop_ParentDriver_Uint64 = 1034, Prop_ResourceRoot_String = 1035, Prop_RegisteredDeviceType_String = 1036, Prop_InputProfilePath_String = 1037, // input profile to use for this device in the input system. Will default to tracking system name if this isn't provided Prop_NeverTracked_Bool = 1038, // Used for devices that will never have a valid pose by design Prop_NumCameras_Int32 = 1039, Prop_CameraFrameLayout_Int32 = 1040, // EVRTrackedCameraFrameLayout value // Properties that are unique to TrackedDeviceClass_HMD Prop_ReportsTimeSinceVSync_Bool = 2000, Prop_SecondsFromVsyncToPhotons_Float = 2001, Prop_DisplayFrequency_Float = 2002, Prop_UserIpdMeters_Float = 2003, Prop_CurrentUniverseId_Uint64 = 2004, Prop_PreviousUniverseId_Uint64 = 2005, Prop_DisplayFirmwareVersion_Uint64 = 2006, Prop_IsOnDesktop_Bool = 2007, Prop_DisplayMCType_Int32 = 2008, Prop_DisplayMCOffset_Float = 2009, Prop_DisplayMCScale_Float = 2010, Prop_EdidVendorID_Int32 = 2011, Prop_DisplayMCImageLeft_String = 2012, Prop_DisplayMCImageRight_String = 2013, Prop_DisplayGCBlackClamp_Float = 2014, Prop_EdidProductID_Int32 = 2015, Prop_CameraToHeadTransform_Matrix34 = 2016, Prop_DisplayGCType_Int32 = 2017, Prop_DisplayGCOffset_Float = 2018, Prop_DisplayGCScale_Float = 2019, Prop_DisplayGCPrescale_Float = 2020, Prop_DisplayGCImage_String = 2021, Prop_LensCenterLeftU_Float = 2022, Prop_LensCenterLeftV_Float = 2023, Prop_LensCenterRightU_Float = 2024, Prop_LensCenterRightV_Float = 2025, Prop_UserHeadToEyeDepthMeters_Float = 2026, Prop_CameraFirmwareVersion_Uint64 = 2027, Prop_CameraFirmwareDescription_String = 2028, Prop_DisplayFPGAVersion_Uint64 = 2029, Prop_DisplayBootloaderVersion_Uint64 = 2030, Prop_DisplayHardwareVersion_Uint64 = 2031, Prop_AudioFirmwareVersion_Uint64 = 2032, Prop_CameraCompatibilityMode_Int32 = 2033, Prop_ScreenshotHorizontalFieldOfViewDegrees_Float = 2034, Prop_ScreenshotVerticalFieldOfViewDegrees_Float = 2035, Prop_DisplaySuppressed_Bool = 2036, Prop_DisplayAllowNightMode_Bool = 2037, Prop_DisplayMCImageWidth_Int32 = 2038, Prop_DisplayMCImageHeight_Int32 = 2039, Prop_DisplayMCImageNumChannels_Int32 = 2040, Prop_DisplayMCImageData_Binary = 2041, Prop_SecondsFromPhotonsToVblank_Float = 2042, Prop_DriverDirectModeSendsVsyncEvents_Bool = 2043, Prop_DisplayDebugMode_Bool = 2044, Prop_GraphicsAdapterLuid_Uint64 = 2045, Prop_DriverProvidedChaperonePath_String = 2048, Prop_ExpectedTrackingReferenceCount_Int32 = 2049, // expected number of sensors or basestations to reserve UI space for Prop_ExpectedControllerCount_Int32 = 2050, // expected number of tracked controllers to reserve UI space for Prop_NamedIconPathControllerLeftDeviceOff_String = 2051, // placeholder icon for "left" controller if not yet detected/loaded Prop_NamedIconPathControllerRightDeviceOff_String = 2052, // placeholder icon for "right" controller if not yet detected/loaded Prop_NamedIconPathTrackingReferenceDeviceOff_String = 2053, // placeholder icon for sensor/base if not yet detected/loaded Prop_DoNotApplyPrediction_Bool = 2054, Prop_CameraToHeadTransforms_Matrix34_Array = 2055, Prop_DistortionMeshResolution_Int32 = 2056, // custom resolution of compositor calls to IVRSystem::ComputeDistortion Prop_DriverIsDrawingControllers_Bool = 2057, Prop_DriverRequestsApplicationPause_Bool = 2058, Prop_DriverRequestsReducedRendering_Bool = 2059, Prop_MinimumIpdStepMeters_Float = 2060, Prop_AudioBridgeFirmwareVersion_Uint64 = 2061, Prop_ImageBridgeFirmwareVersion_Uint64 = 2062, Prop_ImuToHeadTransform_Matrix34 = 2063, Prop_ImuFactoryGyroBias_Vector3 = 2064, Prop_ImuFactoryGyroScale_Vector3 = 2065, Prop_ImuFactoryAccelerometerBias_Vector3 = 2066, Prop_ImuFactoryAccelerometerScale_Vector3 = 2067, // Properties that are unique to TrackedDeviceClass_Controller Prop_AttachedDeviceId_String = 3000, Prop_SupportedButtons_Uint64 = 3001, Prop_Axis0Type_Int32 = 3002, // Return value is of type EVRControllerAxisType Prop_Axis1Type_Int32 = 3003, // Return value is of type EVRControllerAxisType Prop_Axis2Type_Int32 = 3004, // Return value is of type EVRControllerAxisType Prop_Axis3Type_Int32 = 3005, // Return value is of type EVRControllerAxisType Prop_Axis4Type_Int32 = 3006, // Return value is of type EVRControllerAxisType Prop_ControllerRoleHint_Int32 = 3007, // Return value is of type ETrackedControllerRole // Properties that are unique to TrackedDeviceClass_TrackingReference Prop_FieldOfViewLeftDegrees_Float = 4000, Prop_FieldOfViewRightDegrees_Float = 4001, Prop_FieldOfViewTopDegrees_Float = 4002, Prop_FieldOfViewBottomDegrees_Float = 4003, Prop_TrackingRangeMinimumMeters_Float = 4004, Prop_TrackingRangeMaximumMeters_Float = 4005, Prop_ModeLabel_String = 4006, // Properties that are used for user interface like icons names Prop_IconPathName_String = 5000, // DEPRECATED. Value not referenced. Now expected to be part of icon path properties. Prop_NamedIconPathDeviceOff_String = 5001, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others Prop_NamedIconPathDeviceSearching_String = 5002, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others Prop_NamedIconPathDeviceSearchingAlert_String = 5003, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others Prop_NamedIconPathDeviceReady_String = 5004, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others Prop_NamedIconPathDeviceReadyAlert_String = 5005, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others Prop_NamedIconPathDeviceNotReady_String = 5006, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others Prop_NamedIconPathDeviceStandby_String = 5007, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others Prop_NamedIconPathDeviceAlertLow_String = 5008, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others // Properties that are used by helpers, but are opaque to applications Prop_DisplayHiddenArea_Binary_Start = 5100, Prop_DisplayHiddenArea_Binary_End = 5150, Prop_ParentContainer = 5151, // Properties that are unique to drivers Prop_UserConfigPath_String = 6000, Prop_InstallPath_String = 6001, Prop_HasDisplayComponent_Bool = 6002, Prop_HasControllerComponent_Bool = 6003, Prop_HasCameraComponent_Bool = 6004, Prop_HasDriverDirectModeComponent_Bool = 6005, Prop_HasVirtualDisplayComponent_Bool = 6006, // Properties that are set internally based on other information provided by drivers Prop_ControllerType_String = 7000, Prop_LegacyInputProfile_String = 7001, // Vendors are free to expose private debug data in this reserved region Prop_VendorSpecific_Reserved_Start = 10000, Prop_VendorSpecific_Reserved_End = 10999, Prop_TrackedDeviceProperty_Max = 1000000, }; /** No string property will ever be longer than this length */ static const uint32_t k_unMaxPropertyStringSize = 32 * 1024; /** Used to return errors that occur when reading properties. */ enum ETrackedPropertyError { TrackedProp_Success = 0, TrackedProp_WrongDataType = 1, TrackedProp_WrongDeviceClass = 2, TrackedProp_BufferTooSmall = 3, TrackedProp_UnknownProperty = 4, // Driver has not set the property (and may not ever). TrackedProp_InvalidDevice = 5, TrackedProp_CouldNotContactServer = 6, TrackedProp_ValueNotProvidedByDevice = 7, TrackedProp_StringExceedsMaximumLength = 8, TrackedProp_NotYetAvailable = 9, // The property value isn't known yet, but is expected soon. Call again later. TrackedProp_PermissionDenied = 10, TrackedProp_InvalidOperation = 11, TrackedProp_CannotWriteToWildcards = 12, }; /** Allows the application to control what part of the provided texture will be used in the * frame buffer. */ struct VRTextureBounds_t { float uMin, vMin; float uMax, vMax; }; /** Allows specifying pose used to render provided scene texture (if different from value returned by WaitGetPoses). */ struct VRTextureWithPose_t : public Texture_t { HmdMatrix34_t mDeviceToAbsoluteTracking; // Actual pose used to render scene textures. }; struct VRTextureDepthInfo_t { void* handle; // See ETextureType definition above HmdMatrix44_t mProjection; HmdVector2_t vRange; // 0..1 }; struct VRTextureWithDepth_t : public Texture_t { VRTextureDepthInfo_t depth; }; struct VRTextureWithPoseAndDepth_t : public VRTextureWithPose_t { VRTextureDepthInfo_t depth; }; /** Allows the application to control how scene textures are used by the compositor when calling Submit. */ enum EVRSubmitFlags { // Simple render path. App submits rendered left and right eye images with no lens distortion correction applied. Submit_Default = 0x00, // App submits final left and right eye images with lens distortion already applied (lens distortion makes the images appear // barrel distorted with chromatic aberration correction applied). The app would have used the data returned by // vr::IVRSystem::ComputeDistortion() to apply the correct distortion to the rendered images before calling Submit(). Submit_LensDistortionAlreadyApplied = 0x01, // If the texture pointer passed in is actually a renderbuffer (e.g. for MSAA in OpenGL) then set this flag. Submit_GlRenderBuffer = 0x02, // Do not use Submit_Reserved = 0x04, // Set to indicate that pTexture is a pointer to a VRTextureWithPose_t. // This flag can be combined with Submit_TextureWithDepth to pass a VRTextureWithPoseAndDepth_t. Submit_TextureWithPose = 0x08, // Set to indicate that pTexture is a pointer to a VRTextureWithDepth_t. // This flag can be combined with Submit_TextureWithPose to pass a VRTextureWithPoseAndDepth_t. Submit_TextureWithDepth = 0x10, }; /** Data required for passing Vulkan textures to IVRCompositor::Submit. * Be sure to call OpenVR_Shutdown before destroying these resources. */ struct VRVulkanTextureData_t { uint64_t m_nImage; // VkImage VkDevice_T *m_pDevice; VkPhysicalDevice_T *m_pPhysicalDevice; VkInstance_T *m_pInstance; VkQueue_T *m_pQueue; uint32_t m_nQueueFamilyIndex; uint32_t m_nWidth, m_nHeight, m_nFormat, m_nSampleCount; }; /** Data required for passing D3D12 textures to IVRCompositor::Submit. * Be sure to call OpenVR_Shutdown before destroying these resources. */ struct D3D12TextureData_t { ID3D12Resource *m_pResource; ID3D12CommandQueue *m_pCommandQueue; uint32_t m_nNodeMask; }; /** Status of the overall system or tracked objects */ enum EVRState { VRState_Undefined = -1, VRState_Off = 0, VRState_Searching = 1, VRState_Searching_Alert = 2, VRState_Ready = 3, VRState_Ready_Alert = 4, VRState_NotReady = 5, VRState_Standby = 6, VRState_Ready_Alert_Low = 7, }; /** The types of events that could be posted (and what the parameters mean for each event type) */ enum EVREventType { VREvent_None = 0, VREvent_TrackedDeviceActivated = 100, VREvent_TrackedDeviceDeactivated = 101, VREvent_TrackedDeviceUpdated = 102, VREvent_TrackedDeviceUserInteractionStarted = 103, VREvent_TrackedDeviceUserInteractionEnded = 104, VREvent_IpdChanged = 105, VREvent_EnterStandbyMode = 106, VREvent_LeaveStandbyMode = 107, VREvent_TrackedDeviceRoleChanged = 108, VREvent_WatchdogWakeUpRequested = 109, VREvent_LensDistortionChanged = 110, VREvent_PropertyChanged = 111, VREvent_WirelessDisconnect = 112, VREvent_WirelessReconnect = 113, VREvent_ButtonPress = 200, // data is controller VREvent_ButtonUnpress = 201, // data is controller VREvent_ButtonTouch = 202, // data is controller VREvent_ButtonUntouch = 203, // data is controller VREvent_DualAnalog_Press = 250, // data is dualAnalog VREvent_DualAnalog_Unpress = 251, // data is dualAnalog VREvent_DualAnalog_Touch = 252, // data is dualAnalog VREvent_DualAnalog_Untouch = 253, // data is dualAnalog VREvent_DualAnalog_Move = 254, // data is dualAnalog VREvent_DualAnalog_ModeSwitch1 = 255, // data is dualAnalog VREvent_DualAnalog_ModeSwitch2 = 256, // data is dualAnalog VREvent_DualAnalog_Cancel = 257, // data is dualAnalog VREvent_MouseMove = 300, // data is mouse VREvent_MouseButtonDown = 301, // data is mouse VREvent_MouseButtonUp = 302, // data is mouse VREvent_FocusEnter = 303, // data is overlay VREvent_FocusLeave = 304, // data is overlay VREvent_Scroll = 305, // data is mouse VREvent_TouchPadMove = 306, // data is mouse VREvent_OverlayFocusChanged = 307, // data is overlay, global event VREvent_InputFocusCaptured = 400, // data is process DEPRECATED VREvent_InputFocusReleased = 401, // data is process DEPRECATED VREvent_SceneFocusLost = 402, // data is process VREvent_SceneFocusGained = 403, // data is process VREvent_SceneApplicationChanged = 404, // data is process - The App actually drawing the scene changed (usually to or from the compositor) VREvent_SceneFocusChanged = 405, // data is process - New app got access to draw the scene VREvent_InputFocusChanged = 406, // data is process VREvent_SceneApplicationSecondaryRenderingStarted = 407, // data is process VREvent_SceneApplicationUsingWrongGraphicsAdapter = 408, // data is process VREvent_ActionBindingReloaded = 409, // data is process - The App that action binds reloaded for VREvent_HideRenderModels = 410, // Sent to the scene application to request hiding render models temporarily VREvent_ShowRenderModels = 411, // Sent to the scene application to request restoring render model visibility VREvent_ConsoleOpened = 420, VREvent_ConsoleClosed = 421, VREvent_OverlayShown = 500, VREvent_OverlayHidden = 501, VREvent_DashboardActivated = 502, VREvent_DashboardDeactivated = 503, VREvent_DashboardThumbSelected = 504, // Sent to the overlay manager - data is overlay VREvent_DashboardRequested = 505, // Sent to the overlay manager - data is overlay VREvent_ResetDashboard = 506, // Send to the overlay manager VREvent_RenderToast = 507, // Send to the dashboard to render a toast - data is the notification ID VREvent_ImageLoaded = 508, // Sent to overlays when a SetOverlayRaw or SetOverlayFromFile call finishes loading VREvent_ShowKeyboard = 509, // Sent to keyboard renderer in the dashboard to invoke it VREvent_HideKeyboard = 510, // Sent to keyboard renderer in the dashboard to hide it VREvent_OverlayGamepadFocusGained = 511, // Sent to an overlay when IVROverlay::SetFocusOverlay is called on it VREvent_OverlayGamepadFocusLost = 512, // Send to an overlay when it previously had focus and IVROverlay::SetFocusOverlay is called on something else VREvent_OverlaySharedTextureChanged = 513, //VREvent_DashboardGuideButtonDown = 514, // These are no longer sent //VREvent_DashboardGuideButtonUp = 515, VREvent_ScreenshotTriggered = 516, // Screenshot button combo was pressed, Dashboard should request a screenshot VREvent_ImageFailed = 517, // Sent to overlays when a SetOverlayRaw or SetOverlayfromFail fails to load VREvent_DashboardOverlayCreated = 518, VREvent_SwitchGamepadFocus = 519, // Screenshot API VREvent_RequestScreenshot = 520, // Sent by vrclient application to compositor to take a screenshot VREvent_ScreenshotTaken = 521, // Sent by compositor to the application that the screenshot has been taken VREvent_ScreenshotFailed = 522, // Sent by compositor to the application that the screenshot failed to be taken VREvent_SubmitScreenshotToDashboard = 523, // Sent by compositor to the dashboard that a completed screenshot was submitted VREvent_ScreenshotProgressToDashboard = 524, // Sent by compositor to the dashboard that a completed screenshot was submitted VREvent_PrimaryDashboardDeviceChanged = 525, VREvent_RoomViewShown = 526, // Sent by compositor whenever room-view is enabled VREvent_RoomViewHidden = 527, // Sent by compositor whenever room-view is disabled VREvent_Notification_Shown = 600, VREvent_Notification_Hidden = 601, VREvent_Notification_BeginInteraction = 602, VREvent_Notification_Destroyed = 603, VREvent_Quit = 700, // data is process VREvent_ProcessQuit = 701, // data is process VREvent_QuitAborted_UserPrompt = 702, // data is process VREvent_QuitAcknowledged = 703, // data is process VREvent_DriverRequestedQuit = 704, // The driver has requested that SteamVR shut down VREvent_ChaperoneDataHasChanged = 800, VREvent_ChaperoneUniverseHasChanged = 801, VREvent_ChaperoneTempDataHasChanged = 802, VREvent_ChaperoneSettingsHaveChanged = 803, VREvent_SeatedZeroPoseReset = 804, VREvent_AudioSettingsHaveChanged = 820, VREvent_BackgroundSettingHasChanged = 850, VREvent_CameraSettingsHaveChanged = 851, VREvent_ReprojectionSettingHasChanged = 852, VREvent_ModelSkinSettingsHaveChanged = 853, VREvent_EnvironmentSettingsHaveChanged = 854, VREvent_PowerSettingsHaveChanged = 855, VREvent_EnableHomeAppSettingsHaveChanged = 856, VREvent_SteamVRSectionSettingChanged = 857, VREvent_LighthouseSectionSettingChanged = 858, VREvent_NullSectionSettingChanged = 859, VREvent_UserInterfaceSectionSettingChanged = 860, VREvent_NotificationsSectionSettingChanged = 861, VREvent_KeyboardSectionSettingChanged = 862, VREvent_PerfSectionSettingChanged = 863, VREvent_DashboardSectionSettingChanged = 864, VREvent_WebInterfaceSectionSettingChanged = 865, VREvent_StatusUpdate = 900, VREvent_WebInterface_InstallDriverCompleted = 950, VREvent_MCImageUpdated = 1000, VREvent_FirmwareUpdateStarted = 1100, VREvent_FirmwareUpdateFinished = 1101, VREvent_KeyboardClosed = 1200, VREvent_KeyboardCharInput = 1201, VREvent_KeyboardDone = 1202, // Sent when DONE button clicked on keyboard VREvent_ApplicationTransitionStarted = 1300, VREvent_ApplicationTransitionAborted = 1301, VREvent_ApplicationTransitionNewAppStarted = 1302, VREvent_ApplicationListUpdated = 1303, VREvent_ApplicationMimeTypeLoad = 1304, VREvent_ApplicationTransitionNewAppLaunchComplete = 1305, VREvent_ProcessConnected = 1306, VREvent_ProcessDisconnected = 1307, VREvent_Compositor_MirrorWindowShown = 1400, VREvent_Compositor_MirrorWindowHidden = 1401, VREvent_Compositor_ChaperoneBoundsShown = 1410, VREvent_Compositor_ChaperoneBoundsHidden = 1411, VREvent_TrackedCamera_StartVideoStream = 1500, VREvent_TrackedCamera_StopVideoStream = 1501, VREvent_TrackedCamera_PauseVideoStream = 1502, VREvent_TrackedCamera_ResumeVideoStream = 1503, VREvent_TrackedCamera_EditingSurface = 1550, VREvent_PerformanceTest_EnableCapture = 1600, VREvent_PerformanceTest_DisableCapture = 1601, VREvent_PerformanceTest_FidelityLevel = 1602, VREvent_MessageOverlay_Closed = 1650, VREvent_MessageOverlayCloseRequested = 1651, VREvent_Input_HapticVibration = 1700, // data is hapticVibration VREvent_Input_BindingLoadFailed = 1701, // data is process VREvent_Input_BindingLoadSuccessful = 1702, // data is process // Vendors are free to expose private events in this reserved region VREvent_VendorSpecific_Reserved_Start = 10000, VREvent_VendorSpecific_Reserved_End = 19999, }; /** Level of Hmd activity */ // UserInteraction_Timeout means the device is in the process of timing out. // InUse = ( k_EDeviceActivityLevel_UserInteraction || k_EDeviceActivityLevel_UserInteraction_Timeout ) // VREvent_TrackedDeviceUserInteractionStarted fires when the devices transitions from Standby -> UserInteraction or Idle -> UserInteraction. // VREvent_TrackedDeviceUserInteractionEnded fires when the devices transitions from UserInteraction_Timeout -> Idle enum EDeviceActivityLevel { k_EDeviceActivityLevel_Unknown = -1, k_EDeviceActivityLevel_Idle = 0, // No activity for the last 10 seconds k_EDeviceActivityLevel_UserInteraction = 1, // Activity (movement or prox sensor) is happening now k_EDeviceActivityLevel_UserInteraction_Timeout = 2, // No activity for the last 0.5 seconds k_EDeviceActivityLevel_Standby = 3, // Idle for at least 5 seconds (configurable in Settings -> Power Management) }; /** VR controller button and axis IDs */ enum EVRButtonId { k_EButton_System = 0, k_EButton_ApplicationMenu = 1, k_EButton_Grip = 2, k_EButton_DPad_Left = 3, k_EButton_DPad_Up = 4, k_EButton_DPad_Right = 5, k_EButton_DPad_Down = 6, k_EButton_A = 7, k_EButton_ProximitySensor = 31, k_EButton_Axis0 = 32, k_EButton_Axis1 = 33, k_EButton_Axis2 = 34, k_EButton_Axis3 = 35, k_EButton_Axis4 = 36, // aliases for well known controllers k_EButton_SteamVR_Touchpad = k_EButton_Axis0, k_EButton_SteamVR_Trigger = k_EButton_Axis1, k_EButton_Dashboard_Back = k_EButton_Grip, k_EButton_Max = 64 }; inline uint64_t ButtonMaskFromId( EVRButtonId id ) { return 1ull << id; } /** used for controller button events */ struct VREvent_Controller_t { uint32_t button; // EVRButtonId enum }; /** used for simulated mouse events in overlay space */ enum EVRMouseButton { VRMouseButton_Left = 0x0001, VRMouseButton_Right = 0x0002, VRMouseButton_Middle = 0x0004, }; /** used for simulated mouse events in overlay space */ struct VREvent_Mouse_t { float x, y; // co-ords are in GL space, bottom left of the texture is 0,0 uint32_t button; // EVRMouseButton enum }; /** used for simulated mouse wheel scroll in overlay space */ struct VREvent_Scroll_t { float xdelta, ydelta; // movement in fraction of the pad traversed since last delta, 1.0 for a full swipe uint32_t repeatCount; }; /** when in mouse input mode you can receive data from the touchpad, these events are only sent if the users finger is on the touchpad (or just released from it). These events are sent to overlays with the VROverlayFlags_SendVRTouchpadEvents flag set. **/ struct VREvent_TouchPadMove_t { // true if the users finger is detected on the touch pad bool bFingerDown; // How long the finger has been down in seconds float flSecondsFingerDown; // These values indicate the starting finger position (so you can do some basic swipe stuff) float fValueXFirst; float fValueYFirst; // This is the raw sampled coordinate without deadzoning float fValueXRaw; float fValueYRaw; }; /** notification related events. Details will still change at this point */ struct VREvent_Notification_t { uint64_t ulUserValue; uint32_t notificationId; }; /** Used for events about processes */ struct VREvent_Process_t { uint32_t pid; uint32_t oldPid; bool bForced; }; /** Used for a few events about overlays */ struct VREvent_Overlay_t { uint64_t overlayHandle; uint64_t devicePath; }; /** Used for a few events about overlays */ struct VREvent_Status_t { uint32_t statusState; // EVRState enum }; /** Used for keyboard events **/ struct VREvent_Keyboard_t { char cNewInput[8]; // Up to 11 bytes of new input uint64_t uUserValue; // Possible flags about the new input }; struct VREvent_Ipd_t { float ipdMeters; }; struct VREvent_Chaperone_t { uint64_t m_nPreviousUniverse; uint64_t m_nCurrentUniverse; }; /** Not actually used for any events */ struct VREvent_Reserved_t { uint64_t reserved0; uint64_t reserved1; uint64_t reserved2; uint64_t reserved3; }; struct VREvent_PerformanceTest_t { uint32_t m_nFidelityLevel; }; struct VREvent_SeatedZeroPoseReset_t { bool bResetBySystemMenu; }; struct VREvent_Screenshot_t { uint32_t handle; uint32_t type; }; struct VREvent_ScreenshotProgress_t { float progress; }; struct VREvent_ApplicationLaunch_t { uint32_t pid; uint32_t unArgsHandle; }; struct VREvent_EditingCameraSurface_t { uint64_t overlayHandle; uint32_t nVisualMode; }; struct VREvent_MessageOverlay_t { uint32_t unVRMessageOverlayResponse; // vr::VRMessageOverlayResponse enum }; struct VREvent_Property_t { PropertyContainerHandle_t container; ETrackedDeviceProperty prop; }; enum EDualAnalogWhich { k_EDualAnalog_Left = 0, k_EDualAnalog_Right = 1, }; struct VREvent_DualAnalog_t { float x, y; // coordinates are -1..1 analog values float transformedX, transformedY; // transformed by the center and radius numbers provided by the overlay EDualAnalogWhich which; }; struct VREvent_HapticVibration_t { uint64_t containerHandle; // property container handle of the device with the haptic component uint64_t componentHandle; // Which haptic component needs to vibrate float fDurationSeconds; float fFrequency; float fAmplitude; }; struct VREvent_WebConsole_t { WebConsoleHandle_t webConsoleHandle; }; struct VREvent_InputBindingLoad_t { vr::PropertyContainerHandle_t ulAppContainer; uint64_t pathMessage; uint64_t pathUrl; }; /** NOTE!!! If you change this you MUST manually update openvr_interop.cs.py */ typedef union { VREvent_Reserved_t reserved; VREvent_Controller_t controller; VREvent_Mouse_t mouse; VREvent_Scroll_t scroll; VREvent_Process_t process; VREvent_Notification_t notification; VREvent_Overlay_t overlay; VREvent_Status_t status; VREvent_Keyboard_t keyboard; VREvent_Ipd_t ipd; VREvent_Chaperone_t chaperone; VREvent_PerformanceTest_t performanceTest; VREvent_TouchPadMove_t touchPadMove; VREvent_SeatedZeroPoseReset_t seatedZeroPoseReset; VREvent_Screenshot_t screenshot; VREvent_ScreenshotProgress_t screenshotProgress; VREvent_ApplicationLaunch_t applicationLaunch; VREvent_EditingCameraSurface_t cameraSurface; VREvent_MessageOverlay_t messageOverlay; VREvent_Property_t property; VREvent_DualAnalog_t dualAnalog; VREvent_HapticVibration_t hapticVibration; VREvent_WebConsole_t webConsole; VREvent_InputBindingLoad_t inputBinding; } VREvent_Data_t; #if defined(__linux__) || defined(__APPLE__) // This structure was originally defined mis-packed on Linux, preserved for // compatibility. #pragma pack( push, 4 ) #endif /** An event posted by the server to all running applications */ struct VREvent_t { uint32_t eventType; // EVREventType enum TrackedDeviceIndex_t trackedDeviceIndex; float eventAgeSeconds; // event data must be the end of the struct as its size is variable VREvent_Data_t data; }; #if defined(__linux__) || defined(__APPLE__) #pragma pack( pop ) #endif enum EVRInputError { VRInputError_None = 0, VRInputError_NameNotFound = 1, VRInputError_WrongType = 2, VRInputError_InvalidHandle = 3, VRInputError_InvalidParam = 4, VRInputError_NoSteam = 5, VRInputError_MaxCapacityReached = 6, VRInputError_IPCError = 7, VRInputError_NoActiveActionSet = 8, VRInputError_InvalidDevice = 9, VRInputError_InvalidSkeleton = 10, VRInputError_InvalidBoneCount = 11, VRInputError_InvalidCompressedData = 12, VRInputError_NoData = 13, VRInputError_BufferTooSmall = 14, VRInputError_MismatchedActionManifest = 15, }; /** The mesh to draw into the stencil (or depth) buffer to perform * early stencil (or depth) kills of pixels that will never appear on the HMD. * This mesh draws on all the pixels that will be hidden after distortion. * * If the HMD does not provide a visible area mesh pVertexData will be * NULL and unTriangleCount will be 0. */ struct HiddenAreaMesh_t { const HmdVector2_t *pVertexData; uint32_t unTriangleCount; }; enum EHiddenAreaMeshType { k_eHiddenAreaMesh_Standard = 0, k_eHiddenAreaMesh_Inverse = 1, k_eHiddenAreaMesh_LineLoop = 2, k_eHiddenAreaMesh_Max = 3, }; /** Identifies what kind of axis is on the controller at index n. Read this type * with pVRSystem->Get( nControllerDeviceIndex, Prop_Axis0Type_Int32 + n ); */ enum EVRControllerAxisType { k_eControllerAxis_None = 0, k_eControllerAxis_TrackPad = 1, k_eControllerAxis_Joystick = 2, k_eControllerAxis_Trigger = 3, // Analog trigger data is in the X axis }; /** contains information about one axis on the controller */ struct VRControllerAxis_t { float 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. float y; // Ranges from -1.0 to 1.0 for joysticks and track pads. Is always 0.0 for triggers. }; /** the number of axes in the controller state */ static const uint32_t k_unControllerStateAxisCount = 5; #if defined(__linux__) || defined(__APPLE__) // This structure was originally defined mis-packed on Linux, preserved for // compatibility. #pragma pack( push, 4 ) #endif /** Holds all the state of a controller at one moment in time. */ struct VRControllerState001_t { // If packet num matches that on your prior call, then the controller state hasn't been changed since // your last call and there is no need to process it uint32_t unPacketNum; // bit flags for each of the buttons. Use ButtonMaskFromId to turn an ID into a mask uint64_t ulButtonPressed; uint64_t ulButtonTouched; // Axis data for the controller's analog inputs VRControllerAxis_t rAxis[ k_unControllerStateAxisCount ]; }; #if defined(__linux__) || defined(__APPLE__) #pragma pack( pop ) #endif typedef VRControllerState001_t VRControllerState_t; /** determines how to provide output to the application of various event processing functions. */ enum EVRControllerEventOutputType { ControllerEventOutput_OSEvents = 0, ControllerEventOutput_VREvents = 1, }; /** Collision Bounds Style */ enum ECollisionBoundsStyle { COLLISION_BOUNDS_STYLE_BEGINNER = 0, COLLISION_BOUNDS_STYLE_INTERMEDIATE, COLLISION_BOUNDS_STYLE_SQUARES, COLLISION_BOUNDS_STYLE_ADVANCED, COLLISION_BOUNDS_STYLE_NONE, COLLISION_BOUNDS_STYLE_COUNT }; /** Allows the application to customize how the overlay appears in the compositor */ struct Compositor_OverlaySettings { uint32_t size; // sizeof(Compositor_OverlaySettings) bool curved, antialias; float scale, distance, alpha; float uOffset, vOffset, uScale, vScale; float gridDivs, gridWidth, gridScale; HmdMatrix44_t transform; }; /** used to refer to a single VR overlay */ typedef uint64_t VROverlayHandle_t; static const VROverlayHandle_t k_ulOverlayHandleInvalid = 0; /** Errors that can occur around VR overlays */ enum EVROverlayError { VROverlayError_None = 0, VROverlayError_UnknownOverlay = 10, VROverlayError_InvalidHandle = 11, VROverlayError_PermissionDenied = 12, VROverlayError_OverlayLimitExceeded = 13, // No more overlays could be created because the maximum number already exist VROverlayError_WrongVisibilityType = 14, VROverlayError_KeyTooLong = 15, VROverlayError_NameTooLong = 16, VROverlayError_KeyInUse = 17, VROverlayError_WrongTransformType = 18, VROverlayError_InvalidTrackedDevice = 19, VROverlayError_InvalidParameter = 20, VROverlayError_ThumbnailCantBeDestroyed = 21, VROverlayError_ArrayTooSmall = 22, VROverlayError_RequestFailed = 23, VROverlayError_InvalidTexture = 24, VROverlayError_UnableToLoadFile = 25, VROverlayError_KeyboardAlreadyInUse = 26, VROverlayError_NoNeighbor = 27, VROverlayError_TooManyMaskPrimitives = 29, VROverlayError_BadMaskPrimitive = 30, VROverlayError_TextureAlreadyLocked = 31, VROverlayError_TextureLockCapacityReached = 32, VROverlayError_TextureNotLocked = 33, }; /** enum values to pass in to VR_Init to identify whether the application will * draw a 3D scene. */ enum EVRApplicationType { VRApplication_Other = 0, // Some other kind of application that isn't covered by the other entries VRApplication_Scene = 1, // Application will submit 3D frames VRApplication_Overlay = 2, // Application only interacts with overlays VRApplication_Background = 3, // Application should not start SteamVR if it's not already running, and should not // keep it running if everything else quits. VRApplication_Utility = 4, // Init should not try to load any drivers. The application needs access to utility // interfaces (like IVRSettings and IVRApplications) but not hardware. VRApplication_VRMonitor = 5, // Reserved for vrmonitor VRApplication_SteamWatchdog = 6,// Reserved for Steam VRApplication_Bootstrapper = 7, // Start up SteamVR VRApplication_Max }; /** error codes for firmware */ enum EVRFirmwareError { VRFirmwareError_None = 0, VRFirmwareError_Success = 1, VRFirmwareError_Fail = 2, }; /** error codes for notifications */ enum EVRNotificationError { VRNotificationError_OK = 0, VRNotificationError_InvalidNotificationId = 100, VRNotificationError_NotificationQueueFull = 101, VRNotificationError_InvalidOverlayHandle = 102, VRNotificationError_SystemWithUserValueAlreadyExists = 103, }; /** Holds the transform for a single bone */ struct VRBoneTransform_t { HmdVector4_t position; HmdQuaternionf_t orientation; }; /** error codes returned by Vr_Init */ // Please add adequate error description to https://developer.valvesoftware.com/w/index.php?title=Category:SteamVRHelp enum EVRInitError { VRInitError_None = 0, VRInitError_Unknown = 1, VRInitError_Init_InstallationNotFound = 100, VRInitError_Init_InstallationCorrupt = 101, VRInitError_Init_VRClientDLLNotFound = 102, VRInitError_Init_FileNotFound = 103, VRInitError_Init_FactoryNotFound = 104, VRInitError_Init_InterfaceNotFound = 105, VRInitError_Init_InvalidInterface = 106, VRInitError_Init_UserConfigDirectoryInvalid = 107, VRInitError_Init_HmdNotFound = 108, VRInitError_Init_NotInitialized = 109, VRInitError_Init_PathRegistryNotFound = 110, VRInitError_Init_NoConfigPath = 111, VRInitError_Init_NoLogPath = 112, VRInitError_Init_PathRegistryNotWritable = 113, VRInitError_Init_AppInfoInitFailed = 114, VRInitError_Init_Retry = 115, // Used internally to cause retries to vrserver VRInitError_Init_InitCanceledByUser = 116, // The calling application should silently exit. The user canceled app startup VRInitError_Init_AnotherAppLaunching = 117, VRInitError_Init_SettingsInitFailed = 118, VRInitError_Init_ShuttingDown = 119, VRInitError_Init_TooManyObjects = 120, VRInitError_Init_NoServerForBackgroundApp = 121, VRInitError_Init_NotSupportedWithCompositor = 122, VRInitError_Init_NotAvailableToUtilityApps = 123, VRInitError_Init_Internal = 124, VRInitError_Init_HmdDriverIdIsNone = 125, VRInitError_Init_HmdNotFoundPresenceFailed = 126, VRInitError_Init_VRMonitorNotFound = 127, VRInitError_Init_VRMonitorStartupFailed = 128, VRInitError_Init_LowPowerWatchdogNotSupported = 129, VRInitError_Init_InvalidApplicationType = 130, VRInitError_Init_NotAvailableToWatchdogApps = 131, VRInitError_Init_WatchdogDisabledInSettings = 132, VRInitError_Init_VRDashboardNotFound = 133, VRInitError_Init_VRDashboardStartupFailed = 134, VRInitError_Init_VRHomeNotFound = 135, VRInitError_Init_VRHomeStartupFailed = 136, VRInitError_Init_RebootingBusy = 137, VRInitError_Init_FirmwareUpdateBusy = 138, VRInitError_Init_FirmwareRecoveryBusy = 139, VRInitError_Init_USBServiceBusy = 140, VRInitError_Init_VRWebHelperStartupFailed = 141, VRInitError_Driver_Failed = 200, VRInitError_Driver_Unknown = 201, VRInitError_Driver_HmdUnknown = 202, VRInitError_Driver_NotLoaded = 203, VRInitError_Driver_RuntimeOutOfDate = 204, VRInitError_Driver_HmdInUse = 205, VRInitError_Driver_NotCalibrated = 206, VRInitError_Driver_CalibrationInvalid = 207, VRInitError_Driver_HmdDisplayNotFound = 208, VRInitError_Driver_TrackedDeviceInterfaceUnknown = 209, // VRInitError_Driver_HmdDisplayNotFoundAfterFix = 210, // not needed: here for historic reasons VRInitError_Driver_HmdDriverIdOutOfBounds = 211, VRInitError_Driver_HmdDisplayMirrored = 212, VRInitError_IPC_ServerInitFailed = 300, VRInitError_IPC_ConnectFailed = 301, VRInitError_IPC_SharedStateInitFailed = 302, VRInitError_IPC_CompositorInitFailed = 303, VRInitError_IPC_MutexInitFailed = 304, VRInitError_IPC_Failed = 305, VRInitError_IPC_CompositorConnectFailed = 306, VRInitError_IPC_CompositorInvalidConnectResponse = 307, VRInitError_IPC_ConnectFailedAfterMultipleAttempts = 308, VRInitError_Compositor_Failed = 400, VRInitError_Compositor_D3D11HardwareRequired = 401, VRInitError_Compositor_FirmwareRequiresUpdate = 402, VRInitError_Compositor_OverlayInitFailed = 403, VRInitError_Compositor_ScreenshotsInitFailed = 404, VRInitError_Compositor_UnableToCreateDevice = 405, VRInitError_VendorSpecific_UnableToConnectToOculusRuntime = 1000, VRInitError_VendorSpecific_WindowsNotInDevMode = 1001, VRInitError_VendorSpecific_HmdFound_CantOpenDevice = 1101, VRInitError_VendorSpecific_HmdFound_UnableToRequestConfigStart = 1102, VRInitError_VendorSpecific_HmdFound_NoStoredConfig = 1103, VRInitError_VendorSpecific_HmdFound_ConfigTooBig = 1104, VRInitError_VendorSpecific_HmdFound_ConfigTooSmall = 1105, VRInitError_VendorSpecific_HmdFound_UnableToInitZLib = 1106, VRInitError_VendorSpecific_HmdFound_CantReadFirmwareVersion = 1107, VRInitError_VendorSpecific_HmdFound_UnableToSendUserDataStart = 1108, VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataStart = 1109, VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataNext = 1110, VRInitError_VendorSpecific_HmdFound_UserDataAddressRange = 1111, VRInitError_VendorSpecific_HmdFound_UserDataError = 1112, VRInitError_VendorSpecific_HmdFound_ConfigFailedSanityCheck = 1113, VRInitError_Steam_SteamInstallationNotFound = 2000, }; enum EVRScreenshotType { VRScreenshotType_None = 0, VRScreenshotType_Mono = 1, // left eye only VRScreenshotType_Stereo = 2, VRScreenshotType_Cubemap = 3, VRScreenshotType_MonoPanorama = 4, VRScreenshotType_StereoPanorama = 5 }; enum EVRScreenshotPropertyFilenames { VRScreenshotPropertyFilenames_Preview = 0, VRScreenshotPropertyFilenames_VR = 1, }; enum EVRTrackedCameraError { VRTrackedCameraError_None = 0, VRTrackedCameraError_OperationFailed = 100, VRTrackedCameraError_InvalidHandle = 101, VRTrackedCameraError_InvalidFrameHeaderVersion = 102, VRTrackedCameraError_OutOfHandles = 103, VRTrackedCameraError_IPCFailure = 104, VRTrackedCameraError_NotSupportedForThisDevice = 105, VRTrackedCameraError_SharedMemoryFailure = 106, VRTrackedCameraError_FrameBufferingFailure = 107, VRTrackedCameraError_StreamSetupFailure = 108, VRTrackedCameraError_InvalidGLTextureId = 109, VRTrackedCameraError_InvalidSharedTextureHandle = 110, VRTrackedCameraError_FailedToGetGLTextureId = 111, VRTrackedCameraError_SharedTextureFailure = 112, VRTrackedCameraError_NoFrameAvailable = 113, VRTrackedCameraError_InvalidArgument = 114, VRTrackedCameraError_InvalidFrameBufferSize = 115, }; enum EVRTrackedCameraFrameLayout { EVRTrackedCameraFrameLayout_Mono = 0x0001, EVRTrackedCameraFrameLayout_Stereo = 0x0002, EVRTrackedCameraFrameLayout_VerticalLayout = 0x0010, // Stereo frames are Top/Bottom (left/right) EVRTrackedCameraFrameLayout_HorizontalLayout = 0x0020, // Stereo frames are Left/Right }; enum EVRTrackedCameraFrameType { VRTrackedCameraFrameType_Distorted = 0, // This is the camera video frame size in pixels, still distorted. VRTrackedCameraFrameType_Undistorted, // In pixels, an undistorted inscribed rectangle region without invalid regions. This size is subject to changes shortly. VRTrackedCameraFrameType_MaximumUndistorted, // In pixels, maximum undistorted with invalid regions. Non zero alpha component identifies valid regions. MAX_CAMERA_FRAME_TYPES }; typedef uint64_t TrackedCameraHandle_t; #define INVALID_TRACKED_CAMERA_HANDLE ((vr::TrackedCameraHandle_t)0) struct CameraVideoStreamFrameHeader_t { EVRTrackedCameraFrameType eFrameType; uint32_t nWidth; uint32_t nHeight; uint32_t nBytesPerPixel; uint32_t nFrameSequence; TrackedDevicePose_t standingTrackedDevicePose; }; // Screenshot types typedef uint32_t ScreenshotHandle_t; static const uint32_t k_unScreenshotHandleInvalid = 0; /** Frame timing data provided by direct mode drivers. */ struct DriverDirectMode_FrameTiming { uint32_t m_nSize; // Set to sizeof( DriverDirectMode_FrameTiming ) uint32_t m_nNumFramePresents; // number of times frame was presented uint32_t m_nNumMisPresented; // number of times frame was presented on a vsync other than it was originally predicted to uint32_t m_nNumDroppedFrames; // number of additional times previous frame was scanned out (i.e. compositor missed vsync) uint32_t m_nReprojectionFlags; }; enum EVSync { VSync_None, VSync_WaitRender, // block following render work until vsync VSync_NoWaitRender, // do not block following render work (allow to get started early) }; /** raw IMU data provided by IVRIOBuffer from paths to tracked devices with IMUs */ enum Imu_OffScaleFlags { OffScale_AccelX = 0x01, OffScale_AccelY = 0x02, OffScale_AccelZ = 0x04, OffScale_GyroX = 0x08, OffScale_GyroY = 0x10, OffScale_GyroZ = 0x20, }; struct ImuSample_t { double fSampleTime; HmdVector3d_t vAccel; HmdVector3d_t vGyro; uint32_t unOffScaleFlags; }; #pragma pack( pop ) // figure out how to import from the VR API dll #if defined(_WIN32) #ifdef VR_API_EXPORT #define VR_INTERFACE extern "C" __declspec( dllexport ) #else #define VR_INTERFACE extern "C" __declspec( dllimport ) #endif #elif defined(__GNUC__) || defined(COMPILER_GCC) || defined(__APPLE__) #ifdef VR_API_EXPORT #define VR_INTERFACE extern "C" __attribute__((visibility("default"))) #else #define VR_INTERFACE extern "C" #endif #else #error "Unsupported Platform." #endif #if defined( _WIN32 ) #define VR_CALLTYPE __cdecl #else #define VR_CALLTYPE #endif } // namespace vr #endif // _INCLUDE_VRTYPES_H // vrannotation.h #ifdef API_GEN # define VR_CLANG_ATTR(ATTR) __attribute__((annotate( ATTR ))) #else # define VR_CLANG_ATTR(ATTR) #endif #define VR_METHOD_DESC(DESC) VR_CLANG_ATTR( "desc:" #DESC ";" ) #define VR_IGNOREATTR() VR_CLANG_ATTR( "ignore" ) #define VR_OUT_STRUCT() VR_CLANG_ATTR( "out_struct: ;" ) #define VR_OUT_STRING() VR_CLANG_ATTR( "out_string: ;" ) #define VR_OUT_ARRAY_CALL(COUNTER,FUNCTION,PARAMS) VR_CLANG_ATTR( "out_array_call:" #COUNTER "," #FUNCTION "," #PARAMS ";" ) #define VR_OUT_ARRAY_COUNT(COUNTER) VR_CLANG_ATTR( "out_array_count:" #COUNTER ";" ) #define VR_ARRAY_COUNT(COUNTER) VR_CLANG_ATTR( "array_count:" #COUNTER ";" ) #define VR_ARRAY_COUNT_D(COUNTER, DESC) VR_CLANG_ATTR( "array_count:" #COUNTER ";desc:" #DESC ) #define VR_BUFFER_COUNT(COUNTER) VR_CLANG_ATTR( "buffer_count:" #COUNTER ";" ) #define VR_OUT_BUFFER_COUNT(COUNTER) VR_CLANG_ATTR( "out_buffer_count:" #COUNTER ";" ) #define VR_OUT_STRING_COUNT(COUNTER) VR_CLANG_ATTR( "out_string_count:" #COUNTER ";" ) // ivrsystem.h namespace vr { class IVRSystem { public: // ------------------------------------ // Display Methods // ------------------------------------ /** Suggested size for the intermediate render target that the distortion pulls from. */ virtual void GetRecommendedRenderTargetSize( uint32_t *pnWidth, uint32_t *pnHeight ) = 0; /** The projection matrix for the specified eye */ virtual HmdMatrix44_t GetProjectionMatrix( EVREye eEye, float fNearZ, float fFarZ ) = 0; /** The components necessary to build your own projection matrix in case your * application is doing something fancy like infinite Z */ virtual void GetProjectionRaw( EVREye eEye, float *pfLeft, float *pfRight, float *pfTop, float *pfBottom ) = 0; /** Gets the result of the distortion function for the specified eye and input UVs. UVs go from 0,0 in * the upper left of that eye's viewport and 1,1 in the lower right of that eye's viewport. * Returns true for success. Otherwise, returns false, and distortion coordinates are not suitable. */ virtual bool ComputeDistortion( EVREye eEye, float fU, float fV, DistortionCoordinates_t *pDistortionCoordinates ) = 0; /** Returns the transform from eye space to the head space. Eye space is the per-eye flavor of head * space that provides stereo disparity. Instead of Model * View * Projection the sequence is Model * View * Eye^-1 * Projection. * Normally View and Eye^-1 will be multiplied together and treated as View in your application. */ virtual HmdMatrix34_t GetEyeToHeadTransform( EVREye eEye ) = 0; /** Returns the number of elapsed seconds since the last recorded vsync event. This * will come from a vsync timer event in the timer if possible or from the application-reported * time if that is not available. If no vsync times are available the function will * return zero for vsync time and frame counter and return false from the method. */ virtual bool GetTimeSinceLastVsync( float *pfSecondsSinceLastVsync, uint64_t *pulFrameCounter ) = 0; /** [D3D9 Only] * Returns the adapter index that the user should pass into CreateDevice to set up D3D9 in such * a way that it can go full screen exclusive on the HMD. Returns -1 if there was an error. */ virtual int32_t GetD3D9AdapterIndex() = 0; /** [D3D10/11 Only] * Returns the adapter index that the user should pass into EnumAdapters to create the device * and swap chain in DX10 and DX11. If an error occurs the index will be set to -1. */ virtual void GetDXGIOutputInfo( int32_t *pnAdapterIndex ) = 0; /** * Returns platform- and texture-type specific adapter identification so that applications and the * compositor are creating textures and swap chains on the same GPU. If an error occurs the device * will be set to 0. * pInstance is an optional parameter that is required only when textureType is TextureType_Vulkan. * [D3D10/11/12 Only (D3D9 Not Supported)] * Returns the adapter LUID that identifies the GPU attached to the HMD. The user should * enumerate all adapters using IDXGIFactory::EnumAdapters and IDXGIAdapter::GetDesc to find * the adapter with the matching LUID, or use IDXGIFactory4::EnumAdapterByLuid. * The discovered IDXGIAdapter should be used to create the device and swap chain. * [Vulkan Only] * Returns the VkPhysicalDevice that should be used by the application. * pInstance must be the instance the application will use to query for the VkPhysicalDevice. The application * must create the VkInstance with extensions returned by IVRCompositor::GetVulkanInstanceExtensionsRequired enabled. * [macOS Only] * For TextureType_IOSurface returns the id that should be used by the application. * On 10.13+ for TextureType_OpenGL returns the 'registryId' of the renderer which should be used * by the application. See Apple Technical Q&A QA1168 for information on enumerating GL Renderers, and the * new kCGLRPRegistryIDLow and kCGLRPRegistryIDHigh CGLRendererProperty values in the 10.13 SDK. * Pre 10.13 for TextureType_OpenGL returns 0, as there is no dependable way to correlate the HMDs MTLDevice * with a GL Renderer. */ virtual void GetOutputDevice( uint64_t *pnDevice, ETextureType textureType, VkInstance_T *pInstance = nullptr ) = 0; // ------------------------------------ // Display Mode methods // ------------------------------------ /** Use to determine if the headset display is part of the desktop (i.e. extended) or hidden (i.e. direct mode). */ virtual bool IsDisplayOnDesktop() = 0; /** Set the display visibility (true = extended, false = direct mode). Return value of true indicates that the change was successful. */ virtual bool SetDisplayVisibility( bool bIsVisibleOnDesktop ) = 0; // ------------------------------------ // Tracking Methods // ------------------------------------ /** The pose that the tracker thinks that the HMD will be in at the specified number of seconds into the * future. Pass 0 to get the state at the instant the method is called. Most of the time the application should * calculate the time until the photons will be emitted from the display and pass that time into the method. * * This is roughly analogous to the inverse of the view matrix in most applications, though * many games will need to do some additional rotation or translation on top of the rotation * and translation provided by the head pose. * * For devices where bPoseIsValid is true the application can use the pose to position the device * in question. The provided array can be any size up to k_unMaxTrackedDeviceCount. * * Seated experiences should call this method with TrackingUniverseSeated and receive poses relative * to the seated zero pose. Standing experiences should call this method with TrackingUniverseStanding * and receive poses relative to the Chaperone Play Area. TrackingUniverseRawAndUncalibrated should * probably not be used unless the application is the Chaperone calibration tool itself, but will provide * poses relative to the hardware-specific coordinate system in the driver. */ virtual void GetDeviceToAbsoluteTrackingPose( ETrackingUniverseOrigin eOrigin, float fPredictedSecondsToPhotonsFromNow, VR_ARRAY_COUNT(unTrackedDevicePoseArrayCount) TrackedDevicePose_t *pTrackedDevicePoseArray, uint32_t unTrackedDevicePoseArrayCount ) = 0; /** Sets the zero pose for the seated tracker coordinate system to the current position and yaw of the HMD. After * ResetSeatedZeroPose all GetDeviceToAbsoluteTrackingPose calls that pass TrackingUniverseSeated as the origin * will be relative to this new zero pose. The new zero coordinate system will not change the fact that the Y axis * is up in the real world, so the next pose returned from GetDeviceToAbsoluteTrackingPose after a call to * ResetSeatedZeroPose may not be exactly an identity matrix. * * NOTE: This function overrides the user's previously saved seated zero pose and should only be called as the result of a user action. * Users are also able to set their seated zero pose via the OpenVR Dashboard. **/ virtual void ResetSeatedZeroPose() = 0; /** Returns the transform from the seated zero pose to the standing absolute tracking system. This allows * applications to represent the seated origin to used or transform object positions from one coordinate * system to the other. * * The seated origin may or may not be inside the Play Area or Collision Bounds returned by IVRChaperone. Its position * depends on what the user has set from the Dashboard settings and previous calls to ResetSeatedZeroPose. */ virtual HmdMatrix34_t GetSeatedZeroPoseToStandingAbsoluteTrackingPose() = 0; /** Returns the transform from the tracking origin to the standing absolute tracking system. This allows * applications to convert from raw tracking space to the calibrated standing coordinate system. */ virtual HmdMatrix34_t GetRawZeroPoseToStandingAbsoluteTrackingPose() = 0; /** Get a sorted array of device indices of a given class of tracked devices (e.g. controllers). Devices are sorted right to left * relative to the specified tracked device (default: hmd -- pass in -1 for absolute tracking space). Returns the number of devices * in the list, or the size of the array needed if not large enough. */ virtual uint32_t GetSortedTrackedDeviceIndicesOfClass( ETrackedDeviceClass eTrackedDeviceClass, VR_ARRAY_COUNT(unTrackedDeviceIndexArrayCount) vr::TrackedDeviceIndex_t *punTrackedDeviceIndexArray, uint32_t unTrackedDeviceIndexArrayCount, vr::TrackedDeviceIndex_t unRelativeToTrackedDeviceIndex = k_unTrackedDeviceIndex_Hmd ) = 0; /** Returns the level of activity on the device. */ virtual EDeviceActivityLevel GetTrackedDeviceActivityLevel( vr::TrackedDeviceIndex_t unDeviceId ) = 0; /** Convenience utility to apply the specified transform to the specified pose. * This properly transforms all pose components, including velocity and angular velocity */ virtual void ApplyTransform( TrackedDevicePose_t *pOutputPose, const TrackedDevicePose_t *pTrackedDevicePose, const HmdMatrix34_t *pTransform ) = 0; /** Returns the device index associated with a specific role, for example the left hand or the right hand. */ virtual vr::TrackedDeviceIndex_t GetTrackedDeviceIndexForControllerRole( vr::ETrackedControllerRole unDeviceType ) = 0; /** Returns the controller type associated with a device index. */ virtual vr::ETrackedControllerRole GetControllerRoleForTrackedDeviceIndex( vr::TrackedDeviceIndex_t unDeviceIndex ) = 0; // ------------------------------------ // Property methods // ------------------------------------ /** Returns the device class of a tracked device. If there has not been a device connected in this slot * since the application started this function will return TrackedDevice_Invalid. For previous detected * devices the function will return the previously observed device class. * * To determine which devices exist on the system, just loop from 0 to k_unMaxTrackedDeviceCount and check * the device class. Every device with something other than TrackedDevice_Invalid is associated with an * actual tracked device. */ virtual ETrackedDeviceClass GetTrackedDeviceClass( vr::TrackedDeviceIndex_t unDeviceIndex ) = 0; /** Returns true if there is a device connected in this slot. */ virtual bool IsTrackedDeviceConnected( vr::TrackedDeviceIndex_t unDeviceIndex ) = 0; /** Returns a bool property. If the device index is not valid or the property is not a bool type this function will return false. */ virtual bool GetBoolTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0; /** Returns a float property. If the device index is not valid or the property is not a float type this function will return 0. */ virtual float GetFloatTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0; /** Returns an int property. If the device index is not valid or the property is not a int type this function will return 0. */ virtual int32_t GetInt32TrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0; /** Returns a uint64 property. If the device index is not valid or the property is not a uint64 type this function will return 0. */ virtual uint64_t GetUint64TrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0; /** Returns a matrix property. If the device index is not valid or the property is not a matrix type, this function will return identity. */ virtual HmdMatrix34_t GetMatrix34TrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0; /** 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, * this function will return 0. Otherwise it returns the number of bytes necessary to hold the array of properties. If unBufferSize is * greater than the returned size and pBuffer is non-NULL, pBuffer is filled with the contents of array of properties. */ virtual uint32_t GetArrayTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, PropertyTypeTag_t propType, void *pBuffer, uint32_t unBufferSize, ETrackedPropertyError *pError = 0L ) = 0; /** Returns a string property. If the device index is not valid or the property is not a string type this function will * return 0. Otherwise it returns the length of the number of bytes necessary to hold this string including the trailing * null. Strings will always fit in buffers of k_unMaxPropertyStringSize characters. */ virtual uint32_t GetStringTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, ETrackedPropertyError *pError = 0L ) = 0; /** returns a string that corresponds with the specified property error. The string will be the name * of the error enum value for all valid error codes */ virtual const char *GetPropErrorNameFromEnum( ETrackedPropertyError error ) = 0; // ------------------------------------ // Event methods // ------------------------------------ /** Returns true and fills the event with the next event on the queue if there is one. If there are no events * this method returns false. uncbVREvent should be the size in bytes of the VREvent_t struct */ virtual bool PollNextEvent( VREvent_t *pEvent, uint32_t uncbVREvent ) = 0; /** Returns true and fills the event with the next event on the queue if there is one. If there are no events * this method returns false. Fills in the pose of the associated tracked device in the provided pose struct. * This pose will always be older than the call to this function and should not be used to render the device. uncbVREvent should be the size in bytes of the VREvent_t struct */ virtual bool PollNextEventWithPose( ETrackingUniverseOrigin eOrigin, VREvent_t *pEvent, uint32_t uncbVREvent, vr::TrackedDevicePose_t *pTrackedDevicePose ) = 0; /** returns the name of an EVREvent enum value */ virtual const char *GetEventTypeNameFromEnum( EVREventType eType ) = 0; // ------------------------------------ // Rendering helper methods // ------------------------------------ /** 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 * 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. * This mesh is meant to be rendered into the stencil buffer (or into the depth buffer setting nearz) before rendering each eye's view. * This will improve performance by letting the GPU early-reject pixels the user will never see before running the pixel shader. * NOTE: Render this mesh with backface culling disabled since the winding order of the vertices can be different per-HMD or per-eye. * 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. * 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. */ virtual HiddenAreaMesh_t GetHiddenAreaMesh( EVREye eEye, EHiddenAreaMeshType type = k_eHiddenAreaMesh_Standard ) = 0; // ------------------------------------ // Controller methods // ------------------------------------ /** Fills the supplied struct with the current state of the controller. Returns false if the controller index * is invalid. */ virtual bool GetControllerState( vr::TrackedDeviceIndex_t unControllerDeviceIndex, vr::VRControllerState_t *pControllerState, uint32_t unControllerStateSize ) = 0; /** fills the supplied struct with the current state of the controller and the provided pose with the pose of * the controller when the controller state was updated most recently. Use this form if you need a precise controller * pose as input to your application when the user presses or releases a button. */ virtual bool GetControllerStateWithPose( ETrackingUniverseOrigin eOrigin, vr::TrackedDeviceIndex_t unControllerDeviceIndex, vr::VRControllerState_t *pControllerState, uint32_t unControllerStateSize, TrackedDevicePose_t *pTrackedDevicePose ) = 0; /** Trigger a single haptic pulse on a controller. After this call the application may not trigger another haptic pulse on this controller * and axis combination for 5ms. */ virtual void TriggerHapticPulse( vr::TrackedDeviceIndex_t unControllerDeviceIndex, uint32_t unAxisId, unsigned short usDurationMicroSec ) = 0; /** returns the name of an EVRButtonId enum value */ virtual const char *GetButtonIdNameFromEnum( EVRButtonId eButtonId ) = 0; /** returns the name of an EVRControllerAxisType enum value */ virtual const char *GetControllerAxisTypeNameFromEnum( EVRControllerAxisType eAxisType ) = 0; /** Returns true if this application is receiving input from the system. This would return false if * system-related functionality is consuming the input stream. */ virtual bool IsInputAvailable() = 0; /** Returns true SteamVR is drawing controllers on top of the application. Applications should consider * not drawing anything attached to the user's hands in this case. */ virtual bool IsSteamVRDrawingControllers() = 0; /** Returns true if the user has put SteamVR into a mode that is distracting them from the application. * For applications where this is appropriate, the application should pause ongoing activity. */ virtual bool ShouldApplicationPause() = 0; /** Returns true if SteamVR is doing significant rendering work and the game should do what it can to reduce * its own workload. One common way to do this is to reduce the size of the render target provided for each eye. */ virtual bool ShouldApplicationReduceRenderingWork() = 0; // ------------------------------------ // Debug Methods // ------------------------------------ /** Sends a request to the driver for the specified device and returns the response. The maximum response size is 32k, * but this method can be called with a smaller buffer. If the response exceeds the size of the buffer, it is truncated. * The size of the response including its terminating null is returned. */ virtual uint32_t DriverDebugRequest( vr::TrackedDeviceIndex_t unDeviceIndex, const char *pchRequest, VR_OUT_STRING() char *pchResponseBuffer, uint32_t unResponseBufferSize ) = 0; // ------------------------------------ // Firmware methods // ------------------------------------ /** Performs the actual firmware update if applicable. * The following events will be sent, if VRFirmwareError_None was returned: VREvent_FirmwareUpdateStarted, VREvent_FirmwareUpdateFinished * Use the properties Prop_Firmware_UpdateAvailable_Bool, Prop_Firmware_ManualUpdate_Bool, and Prop_Firmware_ManualUpdateURL_String * to figure our whether a firmware update is available, and to figure out whether its a manual update * Prop_Firmware_ManualUpdateURL_String should point to an URL describing the manual update process */ virtual vr::EVRFirmwareError PerformFirmwareUpdate( vr::TrackedDeviceIndex_t unDeviceIndex ) = 0; // ------------------------------------ // Application life cycle methods // ------------------------------------ /** Call this to acknowledge to the system that VREvent_Quit has been received and that the process is exiting. * This extends the timeout until the process is killed. */ virtual void AcknowledgeQuit_Exiting() = 0; /** Call this to tell the system that the user is being prompted to save data. This * halts the timeout and dismisses the dashboard (if it was up). Applications should be sure to actually * prompt the user to save and then exit afterward, otherwise the user will be left in a confusing state. */ virtual void AcknowledgeQuit_UserPrompt() = 0; }; static const char * const IVRSystem_Version = "IVRSystem_019"; } // ivrapplications.h namespace vr { /** Used for all errors reported by the IVRApplications interface */ enum EVRApplicationError { VRApplicationError_None = 0, VRApplicationError_AppKeyAlreadyExists = 100, // Only one application can use any given key VRApplicationError_NoManifest = 101, // the running application does not have a manifest VRApplicationError_NoApplication = 102, // No application is running VRApplicationError_InvalidIndex = 103, VRApplicationError_UnknownApplication = 104, // the application could not be found VRApplicationError_IPCFailed = 105, // An IPC failure caused the request to fail VRApplicationError_ApplicationAlreadyRunning = 106, VRApplicationError_InvalidManifest = 107, VRApplicationError_InvalidApplication = 108, VRApplicationError_LaunchFailed = 109, // the process didn't start VRApplicationError_ApplicationAlreadyStarting = 110, // the system was already starting the same application VRApplicationError_LaunchInProgress = 111, // The system was already starting a different application VRApplicationError_OldApplicationQuitting = 112, VRApplicationError_TransitionAborted = 113, VRApplicationError_IsTemplate = 114, // error when you try to call LaunchApplication() on a template type app (use LaunchTemplateApplication) VRApplicationError_SteamVRIsExiting = 115, VRApplicationError_BufferTooSmall = 200, // The provided buffer was too small to fit the requested data VRApplicationError_PropertyNotSet = 201, // The requested property was not set VRApplicationError_UnknownProperty = 202, VRApplicationError_InvalidParameter = 203, }; /** The maximum length of an application key */ static const uint32_t k_unMaxApplicationKeyLength = 128; /** these are the properties available on applications. */ enum EVRApplicationProperty { VRApplicationProperty_Name_String = 0, VRApplicationProperty_LaunchType_String = 11, VRApplicationProperty_WorkingDirectory_String = 12, VRApplicationProperty_BinaryPath_String = 13, VRApplicationProperty_Arguments_String = 14, VRApplicationProperty_URL_String = 15, VRApplicationProperty_Description_String = 50, VRApplicationProperty_NewsURL_String = 51, VRApplicationProperty_ImagePath_String = 52, VRApplicationProperty_Source_String = 53, VRApplicationProperty_ActionManifestURL_String = 54, VRApplicationProperty_IsDashboardOverlay_Bool = 60, VRApplicationProperty_IsTemplate_Bool = 61, VRApplicationProperty_IsInstanced_Bool = 62, VRApplicationProperty_IsInternal_Bool = 63, VRApplicationProperty_WantsCompositorPauseInStandby_Bool = 64, VRApplicationProperty_LastLaunchTime_Uint64 = 70, }; /** These are states the scene application startup process will go through. */ enum EVRApplicationTransitionState { VRApplicationTransition_None = 0, VRApplicationTransition_OldAppQuitSent = 10, VRApplicationTransition_WaitingForExternalLaunch = 11, VRApplicationTransition_NewAppLaunched = 20, }; struct AppOverrideKeys_t { const char *pchKey; const char *pchValue; }; /** Currently recognized mime types */ static const char * const k_pch_MimeType_HomeApp = "vr/home"; static const char * const k_pch_MimeType_GameTheater = "vr/game_theater"; class IVRApplications { public: // --------------- Application management --------------- // /** Adds an application manifest to the list to load when building the list of installed applications. * Temporary manifests are not automatically loaded */ virtual EVRApplicationError AddApplicationManifest( const char *pchApplicationManifestFullPath, bool bTemporary = false ) = 0; /** Removes an application manifest from the list to load when building the list of installed applications. */ virtual EVRApplicationError RemoveApplicationManifest( const char *pchApplicationManifestFullPath ) = 0; /** Returns true if an application is installed */ virtual bool IsApplicationInstalled( const char *pchAppKey ) = 0; /** Returns the number of applications available in the list */ virtual uint32_t GetApplicationCount() = 0; /** Returns the key of the specified application. The index is at least 0 and is less than the return * value of GetApplicationCount(). The buffer should be at least k_unMaxApplicationKeyLength in order to * fit the key. */ virtual EVRApplicationError GetApplicationKeyByIndex( uint32_t unApplicationIndex, VR_OUT_STRING() char *pchAppKeyBuffer, uint32_t unAppKeyBufferLen ) = 0; /** Returns the key of the application for the specified Process Id. The buffer should be at least * k_unMaxApplicationKeyLength in order to fit the key. */ virtual EVRApplicationError GetApplicationKeyByProcessId( uint32_t unProcessId, VR_OUT_STRING() char *pchAppKeyBuffer, uint32_t unAppKeyBufferLen ) = 0; /** Launches the application. The existing scene application will exit and then the new application will start. * This call is not valid for dashboard overlay applications. */ virtual EVRApplicationError LaunchApplication( const char *pchAppKey ) = 0; /** Launches an instance of an application of type template, with its app key being pchNewAppKey (which must be unique) and optionally override sections * from the manifest file via AppOverrideKeys_t */ virtual EVRApplicationError LaunchTemplateApplication( const char *pchTemplateAppKey, const char *pchNewAppKey, VR_ARRAY_COUNT( unKeys ) const AppOverrideKeys_t *pKeys, uint32_t unKeys ) = 0; /** 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 */ virtual vr::EVRApplicationError LaunchApplicationFromMimeType( const char *pchMimeType, const char *pchArgs ) = 0; /** Launches the dashboard overlay application if it is not already running. This call is only valid for * dashboard overlay applications. */ virtual EVRApplicationError LaunchDashboardOverlay( const char *pchAppKey ) = 0; /** Cancel a pending launch for an application */ virtual bool CancelApplicationLaunch( const char *pchAppKey ) = 0; /** Identifies a running application. OpenVR can't always tell which process started in response * to a URL. This function allows a URL handler (or the process itself) to identify the app key * for the now running application. Passing a process ID of 0 identifies the calling process. * The application must be one that's known to the system via a call to AddApplicationManifest. */ virtual EVRApplicationError IdentifyApplication( uint32_t unProcessId, const char *pchAppKey ) = 0; /** Returns the process ID for an application. Return 0 if the application was not found or is not running. */ virtual uint32_t GetApplicationProcessId( const char *pchAppKey ) = 0; /** Returns a string for an applications error */ virtual const char *GetApplicationsErrorNameFromEnum( EVRApplicationError error ) = 0; // --------------- Application properties --------------- // /** Returns a value for an application property. The required buffer size to fit this value will be returned. */ virtual uint32_t GetApplicationPropertyString( const char *pchAppKey, EVRApplicationProperty eProperty, VR_OUT_STRING() char *pchPropertyValueBuffer, uint32_t unPropertyValueBufferLen, EVRApplicationError *peError = nullptr ) = 0; /** Returns a bool value for an application property. Returns false in all error cases. */ virtual bool GetApplicationPropertyBool( const char *pchAppKey, EVRApplicationProperty eProperty, EVRApplicationError *peError = nullptr ) = 0; /** Returns a uint64 value for an application property. Returns 0 in all error cases. */ virtual uint64_t GetApplicationPropertyUint64( const char *pchAppKey, EVRApplicationProperty eProperty, EVRApplicationError *peError = nullptr ) = 0; /** Sets the application auto-launch flag. This is only valid for applications which return true for VRApplicationProperty_IsDashboardOverlay_Bool. */ virtual EVRApplicationError SetApplicationAutoLaunch( const char *pchAppKey, bool bAutoLaunch ) = 0; /** Gets the application auto-launch flag. This is only valid for applications which return true for VRApplicationProperty_IsDashboardOverlay_Bool. */ virtual bool GetApplicationAutoLaunch( const char *pchAppKey ) = 0; /** Adds this mime-type to the list of supported mime types for this application*/ virtual EVRApplicationError SetDefaultApplicationForMimeType( const char *pchAppKey, const char *pchMimeType ) = 0; /** return the app key that will open this mime type */ virtual bool GetDefaultApplicationForMimeType( const char *pchMimeType, VR_OUT_STRING() char *pchAppKeyBuffer, uint32_t unAppKeyBufferLen ) = 0; /** Get the list of supported mime types for this application, comma-delimited */ virtual bool GetApplicationSupportedMimeTypes( const char *pchAppKey, VR_OUT_STRING() char *pchMimeTypesBuffer, uint32_t unMimeTypesBuffer ) = 0; /** 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 */ virtual uint32_t GetApplicationsThatSupportMimeType( const char *pchMimeType, VR_OUT_STRING() char *pchAppKeysThatSupportBuffer, uint32_t unAppKeysThatSupportBuffer ) = 0; /** Get the args list from an app launch that had the process already running, you call this when you get a VREvent_ApplicationMimeTypeLoad */ virtual uint32_t GetApplicationLaunchArguments( uint32_t unHandle, VR_OUT_STRING() char *pchArgs, uint32_t unArgs ) = 0; // --------------- Transition methods --------------- // /** Returns the app key for the application that is starting up */ virtual EVRApplicationError GetStartingApplication( VR_OUT_STRING() char *pchAppKeyBuffer, uint32_t unAppKeyBufferLen ) = 0; /** Returns the application transition state */ virtual EVRApplicationTransitionState GetTransitionState() = 0; /** Returns errors that would prevent the specified application from launching immediately. Calling this function will * cause the current scene application to quit, so only call it when you are actually about to launch something else. * What the caller should do about these failures depends on the failure: * VRApplicationError_OldApplicationQuitting - An existing application has been told to quit. Wait for a VREvent_ProcessQuit * and try again. * VRApplicationError_ApplicationAlreadyStarting - This application is already starting. This is a permanent failure. * VRApplicationError_LaunchInProgress - A different application is already starting. This is a permanent failure. * VRApplicationError_None - Go ahead and launch. Everything is clear. */ virtual EVRApplicationError PerformApplicationPrelaunchCheck( const char *pchAppKey ) = 0; /** Returns a string for an application transition state */ virtual const char *GetApplicationsTransitionStateNameFromEnum( EVRApplicationTransitionState state ) = 0; /** Returns true if the outgoing scene app has requested a save prompt before exiting */ virtual bool IsQuitUserPromptRequested() = 0; /** Starts a subprocess within the calling application. This * suppresses all application transition UI and automatically identifies the new executable * as part of the same application. On success the calling process should exit immediately. * If working directory is NULL or "" the directory portion of the binary path will be * the working directory. */ virtual EVRApplicationError LaunchInternalProcess( const char *pchBinaryPath, const char *pchArguments, const char *pchWorkingDirectory ) = 0; /** Returns the current scene process ID according to the application system. A scene process will get scene * focus once it starts rendering, but it will appear here once it calls VR_Init with the Scene application * type. */ virtual uint32_t GetCurrentSceneProcessId() = 0; }; static const char * const IVRApplications_Version = "IVRApplications_006"; } // namespace vr // ivrsettings.h namespace vr { enum EVRSettingsError { VRSettingsError_None = 0, VRSettingsError_IPCFailed = 1, VRSettingsError_WriteFailed = 2, VRSettingsError_ReadFailed = 3, VRSettingsError_JsonParseFailed = 4, VRSettingsError_UnsetSettingHasNoDefault = 5, // This will be returned if the setting does not appear in the appropriate default file and has not been set }; // The maximum length of a settings key static const uint32_t k_unMaxSettingsKeyLength = 128; class IVRSettings { public: virtual const char *GetSettingsErrorNameFromEnum( EVRSettingsError eError ) = 0; // Returns true if file sync occurred (force or settings dirty) virtual bool Sync( bool bForce = false, EVRSettingsError *peError = nullptr ) = 0; virtual void SetBool( const char *pchSection, const char *pchSettingsKey, bool bValue, EVRSettingsError *peError = nullptr ) = 0; virtual void SetInt32( const char *pchSection, const char *pchSettingsKey, int32_t nValue, EVRSettingsError *peError = nullptr ) = 0; virtual void SetFloat( const char *pchSection, const char *pchSettingsKey, float flValue, EVRSettingsError *peError = nullptr ) = 0; virtual void SetString( const char *pchSection, const char *pchSettingsKey, const char *pchValue, EVRSettingsError *peError = nullptr ) = 0; // Users of the system need to provide a proper default in default.vrsettings in the resources/settings/ directory // of either the runtime or the driver_xxx directory. Otherwise the default will be false, 0, 0.0 or "" virtual bool GetBool( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr ) = 0; virtual int32_t GetInt32( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr ) = 0; virtual float GetFloat( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr ) = 0; virtual void GetString( const char *pchSection, const char *pchSettingsKey, VR_OUT_STRING() char *pchValue, uint32_t unValueLen, EVRSettingsError *peError = nullptr ) = 0; virtual void RemoveSection( const char *pchSection, EVRSettingsError *peError = nullptr ) = 0; virtual void RemoveKeyInSection( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr ) = 0; }; //----------------------------------------------------------------------------- static const char * const IVRSettings_Version = "IVRSettings_002"; //----------------------------------------------------------------------------- // steamvr keys static const char * const k_pch_SteamVR_Section = "steamvr"; static const char * const k_pch_SteamVR_RequireHmd_String = "requireHmd"; static const char * const k_pch_SteamVR_ForcedDriverKey_String = "forcedDriver"; static const char * const k_pch_SteamVR_ForcedHmdKey_String = "forcedHmd"; static const char * const k_pch_SteamVR_DisplayDebug_Bool = "displayDebug"; static const char * const k_pch_SteamVR_DebugProcessPipe_String = "debugProcessPipe"; static const char * const k_pch_SteamVR_DisplayDebugX_Int32 = "displayDebugX"; static const char * const k_pch_SteamVR_DisplayDebugY_Int32 = "displayDebugY"; static const char * const k_pch_SteamVR_SendSystemButtonToAllApps_Bool= "sendSystemButtonToAllApps"; static const char * const k_pch_SteamVR_LogLevel_Int32 = "loglevel"; static const char * const k_pch_SteamVR_IPD_Float = "ipd"; static const char * const k_pch_SteamVR_Background_String = "background"; static const char * const k_pch_SteamVR_BackgroundUseDomeProjection_Bool = "backgroundUseDomeProjection"; static const char * const k_pch_SteamVR_BackgroundCameraHeight_Float = "backgroundCameraHeight"; static const char * const k_pch_SteamVR_BackgroundDomeRadius_Float = "backgroundDomeRadius"; static const char * const k_pch_SteamVR_GridColor_String = "gridColor"; static const char * const k_pch_SteamVR_PlayAreaColor_String = "playAreaColor"; static const char * const k_pch_SteamVR_ShowStage_Bool = "showStage"; static const char * const k_pch_SteamVR_ActivateMultipleDrivers_Bool = "activateMultipleDrivers"; static const char * const k_pch_SteamVR_DirectMode_Bool = "directMode"; static const char * const k_pch_SteamVR_DirectModeEdidVid_Int32 = "directModeEdidVid"; static const char * const k_pch_SteamVR_DirectModeEdidPid_Int32 = "directModeEdidPid"; static const char * const k_pch_SteamVR_UsingSpeakers_Bool = "usingSpeakers"; static const char * const k_pch_SteamVR_SpeakersForwardYawOffsetDegrees_Float = "speakersForwardYawOffsetDegrees"; static const char * const k_pch_SteamVR_BaseStationPowerManagement_Bool = "basestationPowerManagement"; static const char * const k_pch_SteamVR_NeverKillProcesses_Bool = "neverKillProcesses"; static const char * const k_pch_SteamVR_SupersampleScale_Float = "supersampleScale"; static const char * const k_pch_SteamVR_AllowAsyncReprojection_Bool = "allowAsyncReprojection"; static const char * const k_pch_SteamVR_AllowReprojection_Bool = "allowInterleavedReprojection"; static const char * const k_pch_SteamVR_ForceReprojection_Bool = "forceReprojection"; static const char * const k_pch_SteamVR_ForceFadeOnBadTracking_Bool = "forceFadeOnBadTracking"; static const char * const k_pch_SteamVR_DefaultMirrorView_Int32 = "defaultMirrorView"; static const char * const k_pch_SteamVR_ShowMirrorView_Bool = "showMirrorView"; static const char * const k_pch_SteamVR_MirrorViewGeometry_String = "mirrorViewGeometry"; static const char * const k_pch_SteamVR_StartMonitorFromAppLaunch = "startMonitorFromAppLaunch"; static const char * const k_pch_SteamVR_StartCompositorFromAppLaunch_Bool = "startCompositorFromAppLaunch"; static const char * const k_pch_SteamVR_StartDashboardFromAppLaunch_Bool = "startDashboardFromAppLaunch"; static const char * const k_pch_SteamVR_StartOverlayAppsFromDashboard_Bool = "startOverlayAppsFromDashboard"; static const char * const k_pch_SteamVR_EnableHomeApp = "enableHomeApp"; static const char * const k_pch_SteamVR_CycleBackgroundImageTimeSec_Int32 = "CycleBackgroundImageTimeSec"; static const char * const k_pch_SteamVR_RetailDemo_Bool = "retailDemo"; static const char * const k_pch_SteamVR_IpdOffset_Float = "ipdOffset"; static const char * const k_pch_SteamVR_AllowSupersampleFiltering_Bool = "allowSupersampleFiltering"; static const char * const k_pch_SteamVR_SupersampleManualOverride_Bool = "supersampleManualOverride"; static const char * const k_pch_SteamVR_EnableLinuxVulkanAsync_Bool = "enableLinuxVulkanAsync"; static const char * const k_pch_SteamVR_AllowDisplayLockedMode_Bool = "allowDisplayLockedMode"; static const char * const k_pch_SteamVR_HaveStartedTutorialForNativeChaperoneDriver_Bool = "haveStartedTutorialForNativeChaperoneDriver"; static const char * const k_pch_SteamVR_ForceWindows32bitVRMonitor = "forceWindows32BitVRMonitor"; static const char * const k_pch_SteamVR_DebugInput = "debugInput"; static const char * const k_pch_SteamVR_LegacyInputRebinding = "legacyInputRebinding"; static const char * const k_pch_SteamVR_DebugInputBinding = "debugInputBinding"; //----------------------------------------------------------------------------- // lighthouse keys static const char * const k_pch_Lighthouse_Section = "driver_lighthouse"; static const char * const k_pch_Lighthouse_DisableIMU_Bool = "disableimu"; static const char * const k_pch_Lighthouse_DisableIMUExceptHMD_Bool = "disableimuexcepthmd"; static const char * const k_pch_Lighthouse_UseDisambiguation_String = "usedisambiguation"; static const char * const k_pch_Lighthouse_DisambiguationDebug_Int32 = "disambiguationdebug"; static const char * const k_pch_Lighthouse_PrimaryBasestation_Int32 = "primarybasestation"; static const char * const k_pch_Lighthouse_DBHistory_Bool = "dbhistory"; static const char * const k_pch_Lighthouse_EnableBluetooth_Bool = "enableBluetooth"; static const char * const k_pch_Lighthouse_PowerManagedBaseStations_String = "PowerManagedBaseStations"; //----------------------------------------------------------------------------- // null keys static const char * const k_pch_Null_Section = "driver_null"; static const char * const k_pch_Null_SerialNumber_String = "serialNumber"; static const char * const k_pch_Null_ModelNumber_String = "modelNumber"; static const char * const k_pch_Null_WindowX_Int32 = "windowX"; static const char * const k_pch_Null_WindowY_Int32 = "windowY"; static const char * const k_pch_Null_WindowWidth_Int32 = "windowWidth"; static const char * const k_pch_Null_WindowHeight_Int32 = "windowHeight"; static const char * const k_pch_Null_RenderWidth_Int32 = "renderWidth"; static const char * const k_pch_Null_RenderHeight_Int32 = "renderHeight"; static const char * const k_pch_Null_SecondsFromVsyncToPhotons_Float = "secondsFromVsyncToPhotons"; static const char * const k_pch_Null_DisplayFrequency_Float = "displayFrequency"; //----------------------------------------------------------------------------- // user interface keys static const char * const k_pch_UserInterface_Section = "userinterface"; static const char * const k_pch_UserInterface_StatusAlwaysOnTop_Bool = "StatusAlwaysOnTop"; static const char * const k_pch_UserInterface_MinimizeToTray_Bool = "MinimizeToTray"; static const char * const k_pch_UserInterface_Screenshots_Bool = "screenshots"; static const char * const k_pch_UserInterface_ScreenshotType_Int = "screenshotType"; //----------------------------------------------------------------------------- // notification keys static const char * const k_pch_Notifications_Section = "notifications"; static const char * const k_pch_Notifications_DoNotDisturb_Bool = "DoNotDisturb"; //----------------------------------------------------------------------------- // keyboard keys static const char * const k_pch_Keyboard_Section = "keyboard"; static const char * const k_pch_Keyboard_TutorialCompletions = "TutorialCompletions"; static const char * const k_pch_Keyboard_ScaleX = "ScaleX"; static const char * const k_pch_Keyboard_ScaleY = "ScaleY"; static const char * const k_pch_Keyboard_OffsetLeftX = "OffsetLeftX"; static const char * const k_pch_Keyboard_OffsetRightX = "OffsetRightX"; static const char * const k_pch_Keyboard_OffsetY = "OffsetY"; static const char * const k_pch_Keyboard_Smoothing = "Smoothing"; //----------------------------------------------------------------------------- // perf keys static const char * const k_pch_Perf_Section = "perfcheck"; static const char * const k_pch_Perf_HeuristicActive_Bool = "heuristicActive"; static const char * const k_pch_Perf_NotifyInHMD_Bool = "warnInHMD"; static const char * const k_pch_Perf_NotifyOnlyOnce_Bool = "warnOnlyOnce"; static const char * const k_pch_Perf_AllowTimingStore_Bool = "allowTimingStore"; static const char * const k_pch_Perf_SaveTimingsOnExit_Bool = "saveTimingsOnExit"; static const char * const k_pch_Perf_TestData_Float = "perfTestData"; static const char * const k_pch_Perf_LinuxGPUProfiling_Bool = "linuxGPUProfiling"; //----------------------------------------------------------------------------- // collision bounds keys static const char * const k_pch_CollisionBounds_Section = "collisionBounds"; static const char * const k_pch_CollisionBounds_Style_Int32 = "CollisionBoundsStyle"; static const char * const k_pch_CollisionBounds_GroundPerimeterOn_Bool = "CollisionBoundsGroundPerimeterOn"; static const char * const k_pch_CollisionBounds_CenterMarkerOn_Bool = "CollisionBoundsCenterMarkerOn"; static const char * const k_pch_CollisionBounds_PlaySpaceOn_Bool = "CollisionBoundsPlaySpaceOn"; static const char * const k_pch_CollisionBounds_FadeDistance_Float = "CollisionBoundsFadeDistance"; static const char * const k_pch_CollisionBounds_ColorGammaR_Int32 = "CollisionBoundsColorGammaR"; static const char * const k_pch_CollisionBounds_ColorGammaG_Int32 = "CollisionBoundsColorGammaG"; static const char * const k_pch_CollisionBounds_ColorGammaB_Int32 = "CollisionBoundsColorGammaB"; static const char * const k_pch_CollisionBounds_ColorGammaA_Int32 = "CollisionBoundsColorGammaA"; //----------------------------------------------------------------------------- // camera keys static const char * const k_pch_Camera_Section = "camera"; static const char * const k_pch_Camera_EnableCamera_Bool = "enableCamera"; static const char * const k_pch_Camera_EnableCameraInDashboard_Bool = "enableCameraInDashboard"; static const char * const k_pch_Camera_EnableCameraForCollisionBounds_Bool = "enableCameraForCollisionBounds"; static const char * const k_pch_Camera_EnableCameraForRoomView_Bool = "enableCameraForRoomView"; static const char * const k_pch_Camera_BoundsColorGammaR_Int32 = "cameraBoundsColorGammaR"; static const char * const k_pch_Camera_BoundsColorGammaG_Int32 = "cameraBoundsColorGammaG"; static const char * const k_pch_Camera_BoundsColorGammaB_Int32 = "cameraBoundsColorGammaB"; static const char * const k_pch_Camera_BoundsColorGammaA_Int32 = "cameraBoundsColorGammaA"; static const char * const k_pch_Camera_BoundsStrength_Int32 = "cameraBoundsStrength"; static const char * const k_pch_Camera_RoomViewMode_Int32 = "cameraRoomViewMode"; //----------------------------------------------------------------------------- // audio keys static const char * const k_pch_audio_Section = "audio"; static const char * const k_pch_audio_OnPlaybackDevice_String = "onPlaybackDevice"; static const char * const k_pch_audio_OnRecordDevice_String = "onRecordDevice"; static const char * const k_pch_audio_OnPlaybackMirrorDevice_String = "onPlaybackMirrorDevice"; static const char * const k_pch_audio_OffPlaybackDevice_String = "offPlaybackDevice"; static const char * const k_pch_audio_OffRecordDevice_String = "offRecordDevice"; static const char * const k_pch_audio_VIVEHDMIGain = "viveHDMIGain"; //----------------------------------------------------------------------------- // power management keys static const char * const k_pch_Power_Section = "power"; static const char * const k_pch_Power_PowerOffOnExit_Bool = "powerOffOnExit"; static const char * const k_pch_Power_TurnOffScreensTimeout_Float = "turnOffScreensTimeout"; static const char * const k_pch_Power_TurnOffControllersTimeout_Float = "turnOffControllersTimeout"; static const char * const k_pch_Power_ReturnToWatchdogTimeout_Float = "returnToWatchdogTimeout"; static const char * const k_pch_Power_AutoLaunchSteamVROnButtonPress = "autoLaunchSteamVROnButtonPress"; static const char * const k_pch_Power_PauseCompositorOnStandby_Bool = "pauseCompositorOnStandby"; //----------------------------------------------------------------------------- // dashboard keys static const char * const k_pch_Dashboard_Section = "dashboard"; static const char * const k_pch_Dashboard_EnableDashboard_Bool = "enableDashboard"; static const char * const k_pch_Dashboard_ArcadeMode_Bool = "arcadeMode"; static const char * const k_pch_Dashboard_EnableWebUI = "webUI"; static const char * const k_pch_Dashboard_EnableWebUIDevTools = "webUIDevTools"; //----------------------------------------------------------------------------- // model skin keys static const char * const k_pch_modelskin_Section = "modelskins"; //----------------------------------------------------------------------------- // driver keys - These could be checked in any driver_ section static const char * const k_pch_Driver_Enable_Bool = "enable"; //----------------------------------------------------------------------------- // web interface keys static const char* const k_pch_WebInterface_Section = "WebInterface"; static const char* const k_pch_WebInterface_WebPort_String = "WebPort"; //----------------------------------------------------------------------------- // tracking overrides - keys are device paths, values are the device paths their // tracking/pose information overrides static const char* const k_pch_TrackingOverride_Section = "TrackingOverrides"; //----------------------------------------------------------------------------- // per-app keys - the section name for these is the app key itself. Some of these are prefixed by the controller type static const char* const k_pch_App_BindingAutosaveURLSuffix_String = "AutosaveURL"; static const char* const k_pch_App_BindingCurrentURLSuffix_String = "CurrentURL"; static const char* const k_pch_App_NeedToUpdateAutosaveSuffix_Bool = "NeedToUpdateAutosave"; static const char* const k_pch_App_ActionManifestURL_String = "ActionManifestURL"; } // namespace vr // ivrchaperone.h namespace vr { #pragma pack( push, 8 ) enum ChaperoneCalibrationState { // OK! ChaperoneCalibrationState_OK = 1, // Chaperone is fully calibrated and working correctly // Warnings ChaperoneCalibrationState_Warning = 100, ChaperoneCalibrationState_Warning_BaseStationMayHaveMoved = 101, // A base station thinks that it might have moved ChaperoneCalibrationState_Warning_BaseStationRemoved = 102, // There are less base stations than when calibrated ChaperoneCalibrationState_Warning_SeatedBoundsInvalid = 103, // Seated bounds haven't been calibrated for the current tracking center // Errors ChaperoneCalibrationState_Error = 200, // The UniverseID is invalid ChaperoneCalibrationState_Error_BaseStationUninitialized = 201, // Tracking center hasn't be calibrated for at least one of the base stations ChaperoneCalibrationState_Error_BaseStationConflict = 202, // Tracking center is calibrated, but base stations disagree on the tracking space ChaperoneCalibrationState_Error_PlayAreaInvalid = 203, // Play Area hasn't been calibrated for the current tracking center ChaperoneCalibrationState_Error_CollisionBoundsInvalid = 204, // Collision Bounds haven't been calibrated for the current tracking center }; /** HIGH LEVEL TRACKING SPACE ASSUMPTIONS: * 0,0,0 is the preferred standing area center. * 0Y is the floor height. * -Z is the preferred forward facing direction. */ class IVRChaperone { public: /** Get the current state of Chaperone calibration. This state can change at any time during a session due to physical base station changes. **/ virtual ChaperoneCalibrationState GetCalibrationState() = 0; /** Returns the width and depth of the Play Area (formerly named Soft Bounds) in X and Z. * Tracking space center (0,0,0) is the center of the Play Area. **/ virtual bool GetPlayAreaSize( float *pSizeX, float *pSizeZ ) = 0; /** Returns the 4 corner positions of the Play Area (formerly named Soft Bounds). * Corners are in counter-clockwise order. * Standing center (0,0,0) is the center of the Play Area. * It's a rectangle. * 2 sides are parallel to the X axis and 2 sides are parallel to the Z axis. * Height of every corner is 0Y (on the floor). **/ virtual bool GetPlayAreaRect( HmdQuad_t *rect ) = 0; /** Reload Chaperone data from the .vrchap file on disk. */ virtual void ReloadInfo( void ) = 0; /** Optionally give the chaperone system a hit about the color and brightness in the scene **/ virtual void SetSceneColor( HmdColor_t color ) = 0; /** Get the current chaperone bounds draw color and brightness **/ virtual void GetBoundsColor( HmdColor_t *pOutputColorArray, int nNumOutputColors, float flCollisionBoundsFadeDistance, HmdColor_t *pOutputCameraColor ) = 0; /** Determine whether the bounds are showing right now **/ virtual bool AreBoundsVisible() = 0; /** Force the bounds to show, mostly for utilities **/ virtual void ForceBoundsVisible( bool bForce ) = 0; }; static const char * const IVRChaperone_Version = "IVRChaperone_003"; #pragma pack( pop ) } // ivrchaperonesetup.h namespace vr { enum EChaperoneConfigFile { EChaperoneConfigFile_Live = 1, // The live chaperone config, used by most applications and games EChaperoneConfigFile_Temp = 2, // The temporary chaperone config, used to live-preview collision bounds in room setup }; enum EChaperoneImportFlags { EChaperoneImport_BoundsOnly = 0x0001, }; /** Manages the working copy of the chaperone info. By default this will be the same as the * live copy. Any changes made with this interface will stay in the working copy until * CommitWorkingCopy() is called, at which point the working copy and the live copy will be * the same again. */ class IVRChaperoneSetup { public: /** Saves the current working copy to disk */ virtual bool CommitWorkingCopy( EChaperoneConfigFile configFile ) = 0; /** Reverts the working copy to match the live chaperone calibration. * To modify existing data this MUST be do WHILE getting a non-error ChaperoneCalibrationStatus. * Only after this should you do gets and sets on the existing data. */ virtual void RevertWorkingCopy() = 0; /** Returns the width and depth of the Play Area (formerly named Soft Bounds) in X and Z from the working copy. * Tracking space center (0,0,0) is the center of the Play Area. */ virtual bool GetWorkingPlayAreaSize( float *pSizeX, float *pSizeZ ) = 0; /** Returns the 4 corner positions of the Play Area (formerly named Soft Bounds) from the working copy. * Corners are in clockwise order. * Tracking space center (0,0,0) is the center of the Play Area. * It's a rectangle. * 2 sides are parallel to the X axis and 2 sides are parallel to the Z axis. * Height of every corner is 0Y (on the floor). **/ virtual bool GetWorkingPlayAreaRect( HmdQuad_t *rect ) = 0; /** Returns the number of Quads if the buffer points to null. Otherwise it returns Quads * into the buffer up to the max specified from the working copy. */ virtual bool GetWorkingCollisionBoundsInfo( VR_OUT_ARRAY_COUNT(punQuadsCount) HmdQuad_t *pQuadsBuffer, uint32_t* punQuadsCount ) = 0; /** Returns the number of Quads if the buffer points to null. Otherwise it returns Quads * into the buffer up to the max specified. */ virtual bool GetLiveCollisionBoundsInfo( VR_OUT_ARRAY_COUNT(punQuadsCount) HmdQuad_t *pQuadsBuffer, uint32_t* punQuadsCount ) = 0; /** Returns the preferred seated position from the working copy. */ virtual bool GetWorkingSeatedZeroPoseToRawTrackingPose( HmdMatrix34_t *pmatSeatedZeroPoseToRawTrackingPose ) = 0; /** Returns the standing origin from the working copy. */ virtual bool GetWorkingStandingZeroPoseToRawTrackingPose( HmdMatrix34_t *pmatStandingZeroPoseToRawTrackingPose ) = 0; /** Sets the Play Area in the working copy. */ virtual void SetWorkingPlayAreaSize( float sizeX, float sizeZ ) = 0; /** Sets the Collision Bounds in the working copy. */ virtual void SetWorkingCollisionBoundsInfo( VR_ARRAY_COUNT(unQuadsCount) HmdQuad_t *pQuadsBuffer, uint32_t unQuadsCount ) = 0; /** Sets the preferred seated position in the working copy. */ virtual void SetWorkingSeatedZeroPoseToRawTrackingPose( const HmdMatrix34_t *pMatSeatedZeroPoseToRawTrackingPose ) = 0; /** Sets the preferred standing position in the working copy. */ virtual void SetWorkingStandingZeroPoseToRawTrackingPose( const HmdMatrix34_t *pMatStandingZeroPoseToRawTrackingPose ) = 0; /** Tear everything down and reload it from the file on disk */ virtual void ReloadFromDisk( EChaperoneConfigFile configFile ) = 0; /** Returns the preferred seated position. */ virtual bool GetLiveSeatedZeroPoseToRawTrackingPose( HmdMatrix34_t *pmatSeatedZeroPoseToRawTrackingPose ) = 0; virtual void SetWorkingCollisionBoundsTagsInfo( VR_ARRAY_COUNT(unTagCount) uint8_t *pTagsBuffer, uint32_t unTagCount ) = 0; virtual bool GetLiveCollisionBoundsTagsInfo( VR_OUT_ARRAY_COUNT(punTagCount) uint8_t *pTagsBuffer, uint32_t *punTagCount ) = 0; virtual bool SetWorkingPhysicalBoundsInfo( VR_ARRAY_COUNT(unQuadsCount) HmdQuad_t *pQuadsBuffer, uint32_t unQuadsCount ) = 0; virtual bool GetLivePhysicalBoundsInfo( VR_OUT_ARRAY_COUNT(punQuadsCount) HmdQuad_t *pQuadsBuffer, uint32_t* punQuadsCount ) = 0; virtual bool ExportLiveToBuffer( VR_OUT_STRING() char *pBuffer, uint32_t *pnBufferLength ) = 0; virtual bool ImportFromBufferToWorking( const char *pBuffer, uint32_t nImportFlags ) = 0; }; static const char * const IVRChaperoneSetup_Version = "IVRChaperoneSetup_005"; } // ivrcompositor.h namespace vr { #pragma pack( push, 8 ) /** Errors that can occur with the VR compositor */ enum EVRCompositorError { VRCompositorError_None = 0, VRCompositorError_RequestFailed = 1, VRCompositorError_IncompatibleVersion = 100, VRCompositorError_DoNotHaveFocus = 101, VRCompositorError_InvalidTexture = 102, VRCompositorError_IsNotSceneApplication = 103, VRCompositorError_TextureIsOnWrongDevice = 104, VRCompositorError_TextureUsesUnsupportedFormat = 105, VRCompositorError_SharedTexturesNotSupported = 106, VRCompositorError_IndexOutOfRange = 107, VRCompositorError_AlreadySubmitted = 108, VRCompositorError_InvalidBounds = 109, }; /** Timing mode passed to SetExplicitTimingMode(); see that function for documentation */ enum EVRCompositorTimingMode { VRCompositorTimingMode_Implicit = 0, VRCompositorTimingMode_Explicit_RuntimePerformsPostPresentHandoff = 1, VRCompositorTimingMode_Explicit_ApplicationPerformsPostPresentHandoff = 2, }; const uint32_t VRCompositor_ReprojectionReason_Cpu = 0x01; const uint32_t VRCompositor_ReprojectionReason_Gpu = 0x02; const uint32_t VRCompositor_ReprojectionAsync = 0x04; // This flag indicates the async reprojection mode is active, // but does not indicate if reprojection actually happened or not. // Use the ReprojectionReason flags above to check if reprojection // was actually applied (i.e. scene texture was reused). // NumFramePresents > 1 also indicates the scene texture was reused, // and also the number of times that it was presented in total. /** Provides a single frame's timing information to the app */ struct Compositor_FrameTiming { uint32_t m_nSize; // Set to sizeof( Compositor_FrameTiming ) uint32_t m_nFrameIndex; uint32_t m_nNumFramePresents; // number of times this frame was presented uint32_t m_nNumMisPresented; // number of times this frame was presented on a vsync other than it was originally predicted to uint32_t m_nNumDroppedFrames; // number of additional times previous frame was scanned out uint32_t m_nReprojectionFlags; /** Absolute time reference for comparing frames. This aligns with the vsync that running start is relative to. */ double m_flSystemTimeInSeconds; /** These times may include work from other processes due to OS scheduling. * The fewer packets of work these are broken up into, the less likely this will happen. * GPU work can be broken up by calling Flush. This can sometimes be useful to get the GPU started * processing that work earlier in the frame. */ float m_flPreSubmitGpuMs; // time spent rendering the scene (gpu work submitted between WaitGetPoses and second Submit) float m_flPostSubmitGpuMs; // additional time spent rendering by application (e.g. companion window) float m_flTotalRenderGpuMs; // time between work submitted immediately after present (ideally vsync) until the end of compositor submitted work float m_flCompositorRenderGpuMs; // time spend performing distortion correction, rendering chaperone, overlays, etc. float m_flCompositorRenderCpuMs; // time spent on cpu submitting the above work for this frame float m_flCompositorIdleCpuMs; // time spent waiting for running start (application could have used this much more time) /** Miscellaneous measured intervals. */ float m_flClientFrameIntervalMs; // time between calls to WaitGetPoses float m_flPresentCallCpuMs; // time blocked on call to present (usually 0.0, but can go long) float m_flWaitForPresentCpuMs; // time spent spin-waiting for frame index to change (not near-zero indicates wait object failure) float m_flSubmitFrameMs; // time spent in IVRCompositor::Submit (not near-zero indicates driver issue) /** The following are all relative to this frame's SystemTimeInSeconds */ float m_flWaitGetPosesCalledMs; float m_flNewPosesReadyMs; float m_flNewFrameReadyMs; // second call to IVRCompositor::Submit float m_flCompositorUpdateStartMs; float m_flCompositorUpdateEndMs; float m_flCompositorRenderStartMs; vr::TrackedDevicePose_t m_HmdPose; // pose used by app to render this frame }; /** Cumulative stats for current application. These are not cleared until a new app connects, * but they do stop accumulating once the associated app disconnects. */ struct Compositor_CumulativeStats { uint32_t m_nPid; // Process id associated with these stats (may no longer be running). uint32_t m_nNumFramePresents; // total number of times we called present (includes reprojected frames) uint32_t m_nNumDroppedFrames; // total number of times an old frame was re-scanned out (without reprojection) uint32_t m_nNumReprojectedFrames; // total number of times a frame was scanned out a second time (with reprojection) /** Values recorded at startup before application has fully faded in the first time. */ uint32_t m_nNumFramePresentsOnStartup; uint32_t m_nNumDroppedFramesOnStartup; uint32_t m_nNumReprojectedFramesOnStartup; /** Applications may explicitly fade to the compositor. This is usually to handle level transitions, and loading often causes * system wide hitches. The following stats are collected during this period. Does not include values recorded during startup. */ uint32_t m_nNumLoading; uint32_t m_nNumFramePresentsLoading; uint32_t m_nNumDroppedFramesLoading; uint32_t m_nNumReprojectedFramesLoading; /** 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 * fading back to the compositor. The following stats are a result of this, and are a subset of those recorded above. * Does not include values recorded during start up or loading. */ uint32_t m_nNumTimedOut; uint32_t m_nNumFramePresentsTimedOut; uint32_t m_nNumDroppedFramesTimedOut; uint32_t m_nNumReprojectedFramesTimedOut; }; #pragma pack( pop ) /** Allows the application to interact with the compositor */ class IVRCompositor { public: /** Sets tracking space returned by WaitGetPoses */ virtual void SetTrackingSpace( ETrackingUniverseOrigin eOrigin ) = 0; /** Gets current tracking space returned by WaitGetPoses */ virtual ETrackingUniverseOrigin GetTrackingSpace() = 0; /** Scene applications should call this function to get poses to render with (and optionally poses predicted an additional frame out to use for gameplay). * 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 * start rendering. * * Return codes: * - IsNotSceneApplication (make sure to call VR_Init with VRApplicaiton_Scene) * - DoNotHaveFocus (some other app has taken focus - this will throttle the call to 10hz to reduce the impact on that app) */ virtual EVRCompositorError WaitGetPoses( VR_ARRAY_COUNT(unRenderPoseArrayCount) TrackedDevicePose_t* pRenderPoseArray, uint32_t unRenderPoseArrayCount, VR_ARRAY_COUNT(unGamePoseArrayCount) TrackedDevicePose_t* pGamePoseArray, uint32_t unGamePoseArrayCount ) = 0; /** Get the last set of poses returned by WaitGetPoses. */ virtual EVRCompositorError GetLastPoses( VR_ARRAY_COUNT( unRenderPoseArrayCount ) TrackedDevicePose_t* pRenderPoseArray, uint32_t unRenderPoseArrayCount, VR_ARRAY_COUNT( unGamePoseArrayCount ) TrackedDevicePose_t* pGamePoseArray, uint32_t unGamePoseArrayCount ) = 0; /** Interface for accessing last set of poses returned by WaitGetPoses one at a time. * Returns VRCompositorError_IndexOutOfRange if unDeviceIndex not less than k_unMaxTrackedDeviceCount otherwise VRCompositorError_None. * It is okay to pass NULL for either pose if you only want one of the values. */ virtual EVRCompositorError GetLastPoseForTrackedDeviceIndex( TrackedDeviceIndex_t unDeviceIndex, TrackedDevicePose_t *pOutputPose, TrackedDevicePose_t *pOutputGamePose ) = 0; /** 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 * 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. * * OpenGL dirty state: * glBindTexture * * Return codes: * - IsNotSceneApplication (make sure to call VR_Init with VRApplicaiton_Scene) * - DoNotHaveFocus (some other app has taken focus) * - TextureIsOnWrongDevice (application did not use proper AdapterIndex - see IVRSystem.GetDXGIOutputInfo) * - SharedTexturesNotSupported (application needs to call CreateDXGIFactory1 or later before creating DX device) * - TextureUsesUnsupportedFormat (scene textures must be compatible with DXGI sharing rules - e.g. uncompressed, no mips, etc.) * - InvalidTexture (usually means bad arguments passed in) * - AlreadySubmitted (app has submitted two left textures or two right textures in a single frame - i.e. before calling WaitGetPoses again) */ virtual EVRCompositorError Submit( EVREye eEye, const Texture_t *pTexture, const VRTextureBounds_t* pBounds = 0, EVRSubmitFlags nSubmitFlags = Submit_Default ) = 0; /** Clears the frame that was sent with the last call to Submit. This will cause the * compositor to show the grid until Submit is called again. */ virtual void ClearLastSubmittedFrame() = 0; /** Call immediately after presenting your app's window (i.e. companion window) to unblock the compositor. * This is an optional call, which only needs to be used if you can't instead call WaitGetPoses immediately after Present. * 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 * introduce a deadlock of some sort. This function tells the compositor that you have finished all rendering after having Submitted buffers for both * eyes, and it is free to start its rendering work. This should only be called from the same thread you are rendering on. */ virtual void PostPresentHandoff() = 0; /** Returns true if timing data is filled it. Sets oldest timing info if nFramesAgo is larger than the stored history. * Be sure to set timing.size = sizeof(Compositor_FrameTiming) on struct passed in before calling this function. */ virtual bool GetFrameTiming( Compositor_FrameTiming *pTiming, uint32_t unFramesAgo = 0 ) = 0; /** 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. * 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. */ virtual uint32_t GetFrameTimings( Compositor_FrameTiming *pTiming, uint32_t nFrames ) = 0; /** Returns the time in seconds left in the current (as identified by FrameTiming's frameIndex) frame. * Due to "running start", this value may roll over to the next frame before ever reaching 0.0. */ virtual float GetFrameTimeRemaining() = 0; /** Fills out stats accumulated for the last connected application. Pass in sizeof( Compositor_CumulativeStats ) as second parameter. */ virtual void GetCumulativeStats( Compositor_CumulativeStats *pStats, uint32_t nStatsSizeInBytes ) = 0; /** Fades the view on the HMD to the specified color. The fade will take fSeconds, and the color values are between * 0.0 and 1.0. This color is faded on top of the scene based on the alpha parameter. Removing the fade color instantly * would be FadeToColor( 0.0, 0.0, 0.0, 0.0, 0.0 ). Values are in un-premultiplied alpha space. */ virtual void FadeToColor( float fSeconds, float fRed, float fGreen, float fBlue, float fAlpha, bool bBackground = false ) = 0; /** Get current fade color value. */ virtual HmdColor_t GetCurrentFadeColor( bool bBackground = false ) = 0; /** Fading the Grid in or out in fSeconds */ virtual void FadeGrid( float fSeconds, bool bFadeIn ) = 0; /** Get current alpha value of grid. */ virtual float GetCurrentGridAlpha() = 0; /** Override the skybox used in the compositor (e.g. for during level loads when the app can't feed scene images fast enough) * Order is Front, Back, Left, Right, Top, Bottom. If only a single texture is passed, it is assumed in lat-long format. * If two are passed, it is assumed a lat-long stereo pair. */ virtual EVRCompositorError SetSkyboxOverride( VR_ARRAY_COUNT( unTextureCount ) const Texture_t *pTextures, uint32_t unTextureCount ) = 0; /** Resets compositor skybox back to defaults. */ virtual void ClearSkyboxOverride() = 0; /** Brings the compositor window to the front. This is useful for covering any other window that may be on the HMD * and is obscuring the compositor window. */ virtual void CompositorBringToFront() = 0; /** Pushes the compositor window to the back. This is useful for allowing other applications to draw directly to the HMD. */ virtual void CompositorGoToBack() = 0; /** Tells the compositor process to clean up and exit. You do not need to call this function at shutdown. Under normal * circumstances the compositor will manage its own life cycle based on what applications are running. */ virtual void CompositorQuit() = 0; /** Return whether the compositor is fullscreen */ virtual bool IsFullscreen() = 0; /** Returns the process ID of the process that is currently rendering the scene */ virtual uint32_t GetCurrentSceneFocusProcess() = 0; /** Returns the process ID of the process that rendered the last frame (or 0 if the compositor itself rendered the frame.) * Returns 0 when fading out from an app and the app's process Id when fading into an app. */ virtual uint32_t GetLastFrameRenderer() = 0; /** Returns true if the current process has the scene focus */ virtual bool CanRenderScene() = 0; /** Creates a window on the primary monitor to display what is being shown in the headset. */ virtual void ShowMirrorWindow() = 0; /** Closes the mirror window. */ virtual void HideMirrorWindow() = 0; /** Returns true if the mirror window is shown. */ virtual bool IsMirrorWindowVisible() = 0; /** Writes all images that the compositor knows about (including overlays) to a 'screenshots' folder in the SteamVR runtime root. */ virtual void CompositorDumpImages() = 0; /** Let an app know it should be rendering with low resources. */ virtual bool ShouldAppRenderWithLowResources() = 0; /** Override interleaved reprojection logic to force on. */ virtual void ForceInterleavedReprojectionOn( bool bOverride ) = 0; /** Force reconnecting to the compositor process. */ virtual void ForceReconnectProcess() = 0; /** Temporarily suspends rendering (useful for finer control over scene transitions). */ virtual void SuspendRendering( bool bSuspend ) = 0; /** Opens a shared D3D11 texture with the undistorted composited image for each eye. Use ReleaseMirrorTextureD3D11 when finished * instead of calling Release on the resource itself. */ virtual vr::EVRCompositorError GetMirrorTextureD3D11( vr::EVREye eEye, void *pD3D11DeviceOrResource, void **ppD3D11ShaderResourceView ) = 0; virtual void ReleaseMirrorTextureD3D11( void *pD3D11ShaderResourceView ) = 0; /** Access to mirror textures from OpenGL. */ virtual vr::EVRCompositorError GetMirrorTextureGL( vr::EVREye eEye, vr::glUInt_t *pglTextureId, vr::glSharedTextureHandle_t *pglSharedTextureHandle ) = 0; virtual bool ReleaseSharedGLTexture( vr::glUInt_t glTextureId, vr::glSharedTextureHandle_t glSharedTextureHandle ) = 0; virtual void LockGLSharedTextureForAccess( vr::glSharedTextureHandle_t glSharedTextureHandle ) = 0; virtual void UnlockGLSharedTextureForAccess( vr::glSharedTextureHandle_t glSharedTextureHandle ) = 0; /** [Vulkan Only] * return 0. Otherwise it returns the length of the number of bytes necessary to hold this string including the trailing * null. The string will be a space separated list of-required instance extensions to enable in VkCreateInstance */ virtual uint32_t GetVulkanInstanceExtensionsRequired( VR_OUT_STRING() char *pchValue, uint32_t unBufferSize ) = 0; /** [Vulkan only] * return 0. Otherwise it returns the length of the number of bytes necessary to hold this string including the trailing * null. The string will be a space separated list of required device extensions to enable in VkCreateDevice */ virtual uint32_t GetVulkanDeviceExtensionsRequired( VkPhysicalDevice_T *pPhysicalDevice, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize ) = 0; /** [ Vulkan/D3D12 Only ] * There are two purposes for SetExplicitTimingMode: * 1. To get a more accurate GPU timestamp for when the frame begins in Vulkan/D3D12 applications. * 2. (Optional) To avoid having WaitGetPoses access the Vulkan queue so that the queue can be accessed from * another thread while WaitGetPoses is executing. * * More accurate GPU timestamp for the start of the frame is achieved by the application calling * SubmitExplicitTimingData immediately before its first submission to the Vulkan/D3D12 queue. * This is more accurate because normally this GPU timestamp is recorded during WaitGetPoses. In D3D11, * WaitGetPoses queues a GPU timestamp write, but it does not actually get submitted to the GPU until the * application flushes. By using SubmitExplicitTimingData, the timestamp is recorded at the same place for * Vulkan/D3D12 as it is for D3D11, resulting in a more accurate GPU time measurement for the frame. * * Avoiding WaitGetPoses accessing the Vulkan queue can be achieved using SetExplicitTimingMode as well. If this is desired, * the application should set the timing mode to Explicit_ApplicationPerformsPostPresentHandoff and *MUST* call PostPresentHandoff * itself. If these conditions are met, then WaitGetPoses is guaranteed not to access the queue. Note that PostPresentHandoff * and SubmitExplicitTimingData will access the queue, so only WaitGetPoses becomes safe for accessing the queue from another * thread. */ virtual void SetExplicitTimingMode( EVRCompositorTimingMode eTimingMode ) = 0; /** [ Vulkan/D3D12 Only ] * Submit explicit timing data. When SetExplicitTimingMode is true, this must be called immediately before * the application's first vkQueueSubmit (Vulkan) or ID3D12CommandQueue::ExecuteCommandLists (D3D12) of each frame. * This function will insert a GPU timestamp write just before the application starts its rendering. This function * will perform a vkQueueSubmit on Vulkan so must not be done simultaneously with VkQueue operations on another thread. * Returns VRCompositorError_RequestFailed if SetExplicitTimingMode is not enabled. */ virtual EVRCompositorError SubmitExplicitTimingData() = 0; }; static const char * const IVRCompositor_Version = "IVRCompositor_022"; } // namespace vr // ivrnotifications.h namespace vr { #pragma pack( push, 8 ) // Used for passing graphic data struct NotificationBitmap_t { NotificationBitmap_t() : m_pImageData( nullptr ) , m_nWidth( 0 ) , m_nHeight( 0 ) , m_nBytesPerPixel( 0 ) { }; void *m_pImageData; int32_t m_nWidth; int32_t m_nHeight; int32_t m_nBytesPerPixel; }; /** Be aware that the notification type is used as 'priority' to pick the next notification */ enum EVRNotificationType { /** Transient notifications are automatically hidden after a period of time set by the user. * They are used for things like information and chat messages that do not require user interaction. */ EVRNotificationType_Transient = 0, /** Persistent notifications are shown to the user until they are hidden by calling RemoveNotification(). * They are used for things like phone calls and alarms that require user interaction. */ EVRNotificationType_Persistent = 1, /** System notifications are shown no matter what. It is expected, that the ulUserValue is used as ID. * If there is already a system notification in the queue with that ID it is not accepted into the queue * to prevent spamming with system notification */ EVRNotificationType_Transient_SystemWithUserValue = 2, }; enum EVRNotificationStyle { /** Creates a notification with minimal external styling. */ EVRNotificationStyle_None = 0, /** Used for notifications about overlay-level status. In Steam this is used for events like downloads completing. */ EVRNotificationStyle_Application = 100, /** Used for notifications about contacts that are unknown or not available. In Steam this is used for friend invitations and offline friends. */ EVRNotificationStyle_Contact_Disabled = 200, /** 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. */ EVRNotificationStyle_Contact_Enabled = 201, /** 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. */ EVRNotificationStyle_Contact_Active = 202, }; static const uint32_t k_unNotificationTextMaxSize = 256; typedef uint32_t VRNotificationId; #pragma pack( pop ) /** Allows notification sources to interact with the VR system This current interface is not yet implemented. Do not use yet. */ class IVRNotifications { public: /** Create a notification and enqueue it to be shown to the user. * An overlay handle is required to create a notification, as otherwise it would be impossible for a user to act on it. * To create a two-line notification, use a line break ('\n') to split the text into two lines. * The pImage argument may be NULL, in which case the specified overlay's icon will be used instead. */ virtual EVRNotificationError CreateNotification( VROverlayHandle_t ulOverlayHandle, uint64_t ulUserValue, EVRNotificationType type, const char *pchText, EVRNotificationStyle style, const NotificationBitmap_t *pImage, /* out */ VRNotificationId *pNotificationId ) = 0; /** Destroy a notification, hiding it first if it currently shown to the user. */ virtual EVRNotificationError RemoveNotification( VRNotificationId notificationId ) = 0; }; static const char * const IVRNotifications_Version = "IVRNotifications_002"; } // namespace vr // ivroverlay.h namespace vr { /** The maximum length of an overlay key in bytes, counting the terminating null character. */ static const uint32_t k_unVROverlayMaxKeyLength = 128; /** The maximum length of an overlay name in bytes, counting the terminating null character. */ static const uint32_t k_unVROverlayMaxNameLength = 128; /** The maximum number of overlays that can exist in the system at one time. */ static const uint32_t k_unMaxOverlayCount = 64; /** The maximum number of overlay intersection mask primitives per overlay */ static const uint32_t k_unMaxOverlayIntersectionMaskPrimitivesCount = 32; /** Types of input supported by VR Overlays */ enum VROverlayInputMethod { VROverlayInputMethod_None = 0, // No input events will be generated automatically for this overlay VROverlayInputMethod_Mouse = 1, // Tracked controllers will get mouse events automatically VROverlayInputMethod_DualAnalog = 2, // Analog inputs from tracked controllers are turned into DualAnalog events }; /** Allows the caller to figure out which overlay transform getter to call. */ enum VROverlayTransformType { VROverlayTransform_Absolute = 0, VROverlayTransform_TrackedDeviceRelative = 1, VROverlayTransform_SystemOverlay = 2, VROverlayTransform_TrackedComponent = 3, }; /** Overlay control settings */ enum VROverlayFlags { VROverlayFlags_None = 0, // The following only take effect when rendered using the high quality render path (see SetHighQualityOverlay). VROverlayFlags_Curved = 1, VROverlayFlags_RGSS4X = 2, // Set this flag on a dashboard overlay to prevent a tab from showing up for that overlay VROverlayFlags_NoDashboardTab = 3, // Set this flag on a dashboard that is able to deal with gamepad focus events VROverlayFlags_AcceptsGamepadEvents = 4, // Indicates that the overlay should dim/brighten to show gamepad focus VROverlayFlags_ShowGamepadFocus = 5, // When in VROverlayInputMethod_Mouse you can optionally enable sending VRScroll_t VROverlayFlags_SendVRScrollEvents = 6, VROverlayFlags_SendVRTouchpadEvents = 7, // If set this will render a vertical scroll wheel on the primary controller, // only needed if not using VROverlayFlags_SendVRScrollEvents but you still want to represent a scroll wheel VROverlayFlags_ShowTouchPadScrollWheel = 8, // If this is set ownership and render access to the overlay are transferred // to the new scene process on a call to IVRApplications::LaunchInternalProcess VROverlayFlags_TransferOwnershipToInternalProcess = 9, // If set, renders 50% of the texture in each eye, side by side VROverlayFlags_SideBySide_Parallel = 10, // Texture is left/right VROverlayFlags_SideBySide_Crossed = 11, // Texture is crossed and right/left VROverlayFlags_Panorama = 12, // Texture is a panorama VROverlayFlags_StereoPanorama = 13, // Texture is a stereo panorama // If this is set on an overlay owned by the scene application that overlay // will be sorted with the "Other" overlays on top of all other scene overlays VROverlayFlags_SortWithNonSceneOverlays = 14, // If set, the overlay will be shown in the dashboard, otherwise it will be hidden. VROverlayFlags_VisibleInDashboard = 15, }; enum VRMessageOverlayResponse { VRMessageOverlayResponse_ButtonPress_0 = 0, VRMessageOverlayResponse_ButtonPress_1 = 1, VRMessageOverlayResponse_ButtonPress_2 = 2, VRMessageOverlayResponse_ButtonPress_3 = 3, VRMessageOverlayResponse_CouldntFindSystemOverlay = 4, VRMessageOverlayResponse_CouldntFindOrCreateClientOverlay= 5, VRMessageOverlayResponse_ApplicationQuit = 6 }; struct VROverlayIntersectionParams_t { HmdVector3_t vSource; HmdVector3_t vDirection; ETrackingUniverseOrigin eOrigin; }; struct VROverlayIntersectionResults_t { HmdVector3_t vPoint; HmdVector3_t vNormal; HmdVector2_t vUVs; float fDistance; }; // Input modes for the Big Picture gamepad text entry enum EGamepadTextInputMode { k_EGamepadTextInputModeNormal = 0, k_EGamepadTextInputModePassword = 1, k_EGamepadTextInputModeSubmit = 2, }; // Controls number of allowed lines for the Big Picture gamepad text entry enum EGamepadTextInputLineMode { k_EGamepadTextInputLineModeSingleLine = 0, k_EGamepadTextInputLineModeMultipleLines = 1 }; /** Directions for changing focus between overlays with the gamepad */ enum EOverlayDirection { OverlayDirection_Up = 0, OverlayDirection_Down = 1, OverlayDirection_Left = 2, OverlayDirection_Right = 3, OverlayDirection_Count = 4, }; enum EVROverlayIntersectionMaskPrimitiveType { OverlayIntersectionPrimitiveType_Rectangle, OverlayIntersectionPrimitiveType_Circle, }; struct IntersectionMaskRectangle_t { float m_flTopLeftX; float m_flTopLeftY; float m_flWidth; float m_flHeight; }; struct IntersectionMaskCircle_t { float m_flCenterX; float m_flCenterY; float m_flRadius; }; /** NOTE!!! If you change this you MUST manually update openvr_interop.cs.py and openvr_api_flat.h.py */ typedef union { IntersectionMaskRectangle_t m_Rectangle; IntersectionMaskCircle_t m_Circle; } VROverlayIntersectionMaskPrimitive_Data_t; struct VROverlayIntersectionMaskPrimitive_t { EVROverlayIntersectionMaskPrimitiveType m_nPrimitiveType; VROverlayIntersectionMaskPrimitive_Data_t m_Primitive; }; class IVROverlay { public: // --------------------------------------------- // Overlay management methods // --------------------------------------------- /** Finds an existing overlay with the specified key. */ virtual EVROverlayError FindOverlay( const char *pchOverlayKey, VROverlayHandle_t * pOverlayHandle ) = 0; /** Creates a new named overlay. All overlays start hidden and with default settings. */ virtual EVROverlayError CreateOverlay( const char *pchOverlayKey, const char *pchOverlayName, VROverlayHandle_t * pOverlayHandle ) = 0; /** Destroys the specified overlay. When an application calls VR_Shutdown all overlays created by that app are * automatically destroyed. */ virtual EVROverlayError DestroyOverlay( VROverlayHandle_t ulOverlayHandle ) = 0; /** Specify which overlay to use the high quality render path. This overlay will be composited in during the distortion pass which * results in it drawing on top of everything else, but also at a higher quality as it samples the source texture directly rather than * 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 * for overlays that are expected to take up most of the user's view (e.g. streaming video). * This mode does not support mouse input to your overlay. */ virtual EVROverlayError SetHighQualityOverlay( VROverlayHandle_t ulOverlayHandle ) = 0; /** Returns the overlay handle of the current overlay being rendered using the single high quality overlay render path. * Otherwise it will return k_ulOverlayHandleInvalid. */ virtual vr::VROverlayHandle_t GetHighQualityOverlay() = 0; /** Fills the provided buffer with the string key of the overlay. Returns the size of buffer required to store the key, including * the terminating null character. k_unVROverlayMaxKeyLength will be enough bytes to fit the string. */ virtual uint32_t GetOverlayKey( VROverlayHandle_t ulOverlayHandle, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, EVROverlayError *pError = 0L ) = 0; /** Fills the provided buffer with the friendly name of the overlay. Returns the size of buffer required to store the key, including * the terminating null character. k_unVROverlayMaxNameLength will be enough bytes to fit the string. */ virtual uint32_t GetOverlayName( VROverlayHandle_t ulOverlayHandle, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, EVROverlayError *pError = 0L ) = 0; /** set the name to use for this overlay */ virtual EVROverlayError SetOverlayName( VROverlayHandle_t ulOverlayHandle, const char *pchName ) = 0; /** 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 * will be set and VROverlayError_ArrayTooSmall is returned. */ virtual EVROverlayError GetOverlayImageData( VROverlayHandle_t ulOverlayHandle, void *pvBuffer, uint32_t unBufferSize, uint32_t *punWidth, uint32_t *punHeight ) = 0; /** returns a string that corresponds with the specified overlay error. The string will be the name * of the error enum value for all valid error codes */ virtual const char *GetOverlayErrorNameFromEnum( EVROverlayError error ) = 0; // --------------------------------------------- // Overlay rendering methods // --------------------------------------------- /** Sets the pid that is allowed to render to this overlay (the creator pid is always allow to render), * by default this is the pid of the process that made the overlay */ virtual EVROverlayError SetOverlayRenderingPid( VROverlayHandle_t ulOverlayHandle, uint32_t unPID ) = 0; /** Gets the pid that is allowed to render to this overlay */ virtual uint32_t GetOverlayRenderingPid( VROverlayHandle_t ulOverlayHandle ) = 0; /** Specify flag setting for a given overlay */ virtual EVROverlayError SetOverlayFlag( VROverlayHandle_t ulOverlayHandle, VROverlayFlags eOverlayFlag, bool bEnabled ) = 0; /** Sets flag setting for a given overlay */ virtual EVROverlayError GetOverlayFlag( VROverlayHandle_t ulOverlayHandle, VROverlayFlags eOverlayFlag, bool *pbEnabled ) = 0; /** Sets the color tint of the overlay quad. Use 0.0 to 1.0 per channel. */ virtual EVROverlayError SetOverlayColor( VROverlayHandle_t ulOverlayHandle, float fRed, float fGreen, float fBlue ) = 0; /** Gets the color tint of the overlay quad. */ virtual EVROverlayError GetOverlayColor( VROverlayHandle_t ulOverlayHandle, float *pfRed, float *pfGreen, float *pfBlue ) = 0; /** Sets the alpha of the overlay quad. Use 1.0 for 100 percent opacity to 0.0 for 0 percent opacity. */ virtual EVROverlayError SetOverlayAlpha( VROverlayHandle_t ulOverlayHandle, float fAlpha ) = 0; /** Gets the alpha of the overlay quad. By default overlays are rendering at 100 percent alpha (1.0). */ virtual EVROverlayError GetOverlayAlpha( VROverlayHandle_t ulOverlayHandle, float *pfAlpha ) = 0; /** Sets the aspect ratio of the texels in the overlay. 1.0 means the texels are square. 2.0 means the texels * are twice as wide as they are tall. Defaults to 1.0. */ virtual EVROverlayError SetOverlayTexelAspect( VROverlayHandle_t ulOverlayHandle, float fTexelAspect ) = 0; /** Gets the aspect ratio of the texels in the overlay. Defaults to 1.0 */ virtual EVROverlayError GetOverlayTexelAspect( VROverlayHandle_t ulOverlayHandle, float *pfTexelAspect ) = 0; /** Sets the rendering sort order for the overlay. Overlays are rendered this order: * Overlays owned by the scene application * Overlays owned by some other application * * Within a category overlays are rendered lowest sort order to highest sort order. Overlays with the same * sort order are rendered back to front base on distance from the HMD. * * Sort order defaults to 0. */ virtual EVROverlayError SetOverlaySortOrder( VROverlayHandle_t ulOverlayHandle, uint32_t unSortOrder ) = 0; /** Gets the sort order of the overlay. See SetOverlaySortOrder for how this works. */ virtual EVROverlayError GetOverlaySortOrder( VROverlayHandle_t ulOverlayHandle, uint32_t *punSortOrder ) = 0; /** Sets the width of the overlay quad in meters. By default overlays are rendered on a quad that is 1 meter across */ virtual EVROverlayError SetOverlayWidthInMeters( VROverlayHandle_t ulOverlayHandle, float fWidthInMeters ) = 0; /** Returns the width of the overlay quad in meters. By default overlays are rendered on a quad that is 1 meter across */ virtual EVROverlayError GetOverlayWidthInMeters( VROverlayHandle_t ulOverlayHandle, float *pfWidthInMeters ) = 0; /** For high-quality curved overlays only, sets the distance range in meters from the overlay used to automatically curve * the surface around the viewer. Min is distance is when the surface will be most curved. Max is when least curved. */ virtual EVROverlayError SetOverlayAutoCurveDistanceRangeInMeters( VROverlayHandle_t ulOverlayHandle, float fMinDistanceInMeters, float fMaxDistanceInMeters ) = 0; /** For high-quality curved overlays only, gets the distance range in meters from the overlay used to automatically curve * the surface around the viewer. Min is distance is when the surface will be most curved. Max is when least curved. */ virtual EVROverlayError GetOverlayAutoCurveDistanceRangeInMeters( VROverlayHandle_t ulOverlayHandle, float *pfMinDistanceInMeters, float *pfMaxDistanceInMeters ) = 0; /** Sets the colorspace the overlay texture's data is in. Defaults to 'auto'. * If the texture needs to be resolved, you should call SetOverlayTexture with the appropriate colorspace instead. */ virtual EVROverlayError SetOverlayTextureColorSpace( VROverlayHandle_t ulOverlayHandle, EColorSpace eTextureColorSpace ) = 0; /** Gets the overlay's current colorspace setting. */ virtual EVROverlayError GetOverlayTextureColorSpace( VROverlayHandle_t ulOverlayHandle, EColorSpace *peTextureColorSpace ) = 0; /** 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. */ virtual EVROverlayError SetOverlayTextureBounds( VROverlayHandle_t ulOverlayHandle, const VRTextureBounds_t *pOverlayTextureBounds ) = 0; /** 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. */ virtual EVROverlayError GetOverlayTextureBounds( VROverlayHandle_t ulOverlayHandle, VRTextureBounds_t *pOverlayTextureBounds ) = 0; /** Gets render model to draw behind this overlay */ virtual uint32_t GetOverlayRenderModel( vr::VROverlayHandle_t ulOverlayHandle, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, HmdColor_t *pColor, vr::EVROverlayError *pError ) = 0; /** Sets render model to draw behind this overlay and the vertex color to use, pass null for pColor to match the overlays vertex color. The model is scaled by the same amount as the overlay, with a default of 1m. */ virtual vr::EVROverlayError SetOverlayRenderModel( vr::VROverlayHandle_t ulOverlayHandle, const char *pchRenderModel, const HmdColor_t *pColor ) = 0; /** Returns the transform type of this overlay. */ virtual EVROverlayError GetOverlayTransformType( VROverlayHandle_t ulOverlayHandle, VROverlayTransformType *peTransformType ) = 0; /** Sets the transform to absolute tracking origin. */ virtual EVROverlayError SetOverlayTransformAbsolute( VROverlayHandle_t ulOverlayHandle, ETrackingUniverseOrigin eTrackingOrigin, const HmdMatrix34_t *pmatTrackingOriginToOverlayTransform ) = 0; /** Gets the transform if it is absolute. Returns an error if the transform is some other type. */ virtual EVROverlayError GetOverlayTransformAbsolute( VROverlayHandle_t ulOverlayHandle, ETrackingUniverseOrigin *peTrackingOrigin, HmdMatrix34_t *pmatTrackingOriginToOverlayTransform ) = 0; /** Sets the transform to relative to the transform of the specified tracked device. */ virtual EVROverlayError SetOverlayTransformTrackedDeviceRelative( VROverlayHandle_t ulOverlayHandle, TrackedDeviceIndex_t unTrackedDevice, const HmdMatrix34_t *pmatTrackedDeviceToOverlayTransform ) = 0; /** Gets the transform if it is relative to a tracked device. Returns an error if the transform is some other type. */ virtual EVROverlayError GetOverlayTransformTrackedDeviceRelative( VROverlayHandle_t ulOverlayHandle, TrackedDeviceIndex_t *punTrackedDevice, HmdMatrix34_t *pmatTrackedDeviceToOverlayTransform ) = 0; /** Sets the transform to draw the overlay on a rendermodel component mesh instead of a quad. This will only draw when the system is * drawing the device. Overlays with this transform type cannot receive mouse events. */ virtual EVROverlayError SetOverlayTransformTrackedDeviceComponent( VROverlayHandle_t ulOverlayHandle, TrackedDeviceIndex_t unDeviceIndex, const char *pchComponentName ) = 0; /** Gets the transform information when the overlay is rendering on a component. */ virtual EVROverlayError GetOverlayTransformTrackedDeviceComponent( VROverlayHandle_t ulOverlayHandle, TrackedDeviceIndex_t *punDeviceIndex, VR_OUT_STRING() char *pchComponentName, uint32_t unComponentNameSize ) = 0; /** Gets the transform if it is relative to another overlay. Returns an error if the transform is some other type. */ virtual vr::EVROverlayError GetOverlayTransformOverlayRelative( VROverlayHandle_t ulOverlayHandle, VROverlayHandle_t *ulOverlayHandleParent, HmdMatrix34_t *pmatParentOverlayToOverlayTransform ) = 0; /** Sets the transform to relative to the transform of the specified overlay. This overlays visibility will also track the parents visibility */ virtual vr::EVROverlayError SetOverlayTransformOverlayRelative( VROverlayHandle_t ulOverlayHandle, VROverlayHandle_t ulOverlayHandleParent, const HmdMatrix34_t *pmatParentOverlayToOverlayTransform ) = 0; /** Shows the VR overlay. For dashboard overlays, only the Dashboard Manager is allowed to call this. */ virtual EVROverlayError ShowOverlay( VROverlayHandle_t ulOverlayHandle ) = 0; /** Hides the VR overlay. For dashboard overlays, only the Dashboard Manager is allowed to call this. */ virtual EVROverlayError HideOverlay( VROverlayHandle_t ulOverlayHandle ) = 0; /** Returns true if the overlay is visible. */ virtual bool IsOverlayVisible( VROverlayHandle_t ulOverlayHandle ) = 0; /** 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 */ virtual EVROverlayError GetTransformForOverlayCoordinates( VROverlayHandle_t ulOverlayHandle, ETrackingUniverseOrigin eTrackingOrigin, HmdVector2_t coordinatesInOverlay, HmdMatrix34_t *pmatTransform ) = 0; // --------------------------------------------- // Overlay input methods // --------------------------------------------- /** Returns true and fills the event with the next event on the overlay's event queue, if there is one. * If there are no events this method returns false. uncbVREvent should be the size in bytes of the VREvent_t struct */ virtual bool PollNextOverlayEvent( VROverlayHandle_t ulOverlayHandle, VREvent_t *pEvent, uint32_t uncbVREvent ) = 0; /** Returns the current input settings for the specified overlay. */ virtual EVROverlayError GetOverlayInputMethod( VROverlayHandle_t ulOverlayHandle, VROverlayInputMethod *peInputMethod ) = 0; /** Sets the input settings for the specified overlay. */ virtual EVROverlayError SetOverlayInputMethod( VROverlayHandle_t ulOverlayHandle, VROverlayInputMethod eInputMethod ) = 0; /** Gets the mouse scaling factor that is used for mouse events. The actual texture may be a different size, but this is * typically the size of the underlying UI in pixels. */ virtual EVROverlayError GetOverlayMouseScale( VROverlayHandle_t ulOverlayHandle, HmdVector2_t *pvecMouseScale ) = 0; /** Sets the mouse scaling factor that is used for mouse events. The actual texture may be a different size, but this is * typically the size of the underlying UI in pixels (not in world space). */ virtual EVROverlayError SetOverlayMouseScale( VROverlayHandle_t ulOverlayHandle, const HmdVector2_t *pvecMouseScale ) = 0; /** Computes the overlay-space pixel coordinates of where the ray intersects the overlay with the * specified settings. Returns false if there is no intersection. */ virtual bool ComputeOverlayIntersection( VROverlayHandle_t ulOverlayHandle, const VROverlayIntersectionParams_t *pParams, VROverlayIntersectionResults_t *pResults ) = 0; /** Returns true if the specified overlay is the hover target. An overlay is the hover target when it is the last overlay "moused over" * by the virtual mouse pointer */ virtual bool IsHoverTargetOverlay( VROverlayHandle_t ulOverlayHandle ) = 0; /** Returns the current Gamepad focus overlay */ virtual vr::VROverlayHandle_t GetGamepadFocusOverlay() = 0; /** Sets the current Gamepad focus overlay */ virtual EVROverlayError SetGamepadFocusOverlay( VROverlayHandle_t ulNewFocusOverlay ) = 0; /** Sets an overlay's neighbor. This will also set the neighbor of the "to" overlay * to point back to the "from" overlay. If an overlay's neighbor is set to invalid both * ends will be cleared */ virtual EVROverlayError SetOverlayNeighbor( EOverlayDirection eDirection, VROverlayHandle_t ulFrom, VROverlayHandle_t ulTo ) = 0; /** Changes the Gamepad focus from one overlay to one of its neighbors. Returns VROverlayError_NoNeighbor if there is no * neighbor in that direction */ virtual EVROverlayError MoveGamepadFocusToNeighbor( EOverlayDirection eDirection, VROverlayHandle_t ulFrom ) = 0; /** Sets the analog input to Dual Analog coordinate scale for the specified overlay. */ virtual EVROverlayError SetOverlayDualAnalogTransform( VROverlayHandle_t ulOverlay, EDualAnalogWhich eWhich, const HmdVector2_t & vCenter, float fRadius ) = 0; /** Gets the analog input to Dual Analog coordinate scale for the specified overlay. */ virtual EVROverlayError GetOverlayDualAnalogTransform( VROverlayHandle_t ulOverlay, EDualAnalogWhich eWhich, HmdVector2_t *pvCenter, float *pfRadius ) = 0; // --------------------------------------------- // Overlay texture methods // --------------------------------------------- /** Texture to draw for the overlay. This function can only be called by the overlay's creator or renderer process (see SetOverlayRenderingPid) . * * OpenGL dirty state: * glBindTexture */ virtual EVROverlayError SetOverlayTexture( VROverlayHandle_t ulOverlayHandle, const Texture_t *pTexture ) = 0; /** Use this to tell the overlay system to release the texture set for this overlay. */ virtual EVROverlayError ClearOverlayTexture( VROverlayHandle_t ulOverlayHandle ) = 0; /** Separate interface for providing the data as a stream of bytes, but there is an upper bound on data * that can be sent. This function can only be called by the overlay's renderer process. */ virtual EVROverlayError SetOverlayRaw( VROverlayHandle_t ulOverlayHandle, void *pvBuffer, uint32_t unWidth, uint32_t unHeight, uint32_t unDepth ) = 0; /** Separate interface for providing the image through a filename: can be png or jpg, and should not be bigger than 1920x1080. * This function can only be called by the overlay's renderer process */ virtual EVROverlayError SetOverlayFromFile( VROverlayHandle_t ulOverlayHandle, const char *pchFilePath ) = 0; /** Get the native texture handle/device for an overlay you have created. * On windows this handle will be a ID3D11ShaderResourceView with a ID3D11Texture2D bound. * * The texture will always be sized to match the backing texture you supplied in SetOverlayTexture above. * * You MUST call ReleaseNativeOverlayHandle() with pNativeTextureHandle once you are done with this texture. * * pNativeTextureHandle is an OUTPUT, it will be a pointer to a ID3D11ShaderResourceView *. * pNativeTextureRef is an INPUT and should be a ID3D11Resource *. The device used by pNativeTextureRef will be used to bind pNativeTextureHandle. */ virtual 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; /** Release the pNativeTextureHandle provided from the GetOverlayTexture call, this allows the system to free the underlying GPU resources for this object, * so only do it once you stop rendering this texture. */ virtual EVROverlayError ReleaseNativeOverlayHandle( VROverlayHandle_t ulOverlayHandle, void *pNativeTextureHandle ) = 0; /** Get the size of the overlay texture */ virtual EVROverlayError GetOverlayTextureSize( VROverlayHandle_t ulOverlayHandle, uint32_t *pWidth, uint32_t *pHeight ) = 0; // ---------------------------------------------- // Dashboard Overlay Methods // ---------------------------------------------- /** Creates a dashboard overlay and returns its handle */ virtual EVROverlayError CreateDashboardOverlay( const char *pchOverlayKey, const char *pchOverlayFriendlyName, VROverlayHandle_t * pMainHandle, VROverlayHandle_t *pThumbnailHandle ) = 0; /** Returns true if the dashboard is visible */ virtual bool IsDashboardVisible() = 0; /** returns true if the dashboard is visible and the specified overlay is the active system Overlay */ virtual bool IsActiveDashboardOverlay( VROverlayHandle_t ulOverlayHandle ) = 0; /** Sets the dashboard overlay to only appear when the specified process ID has scene focus */ virtual EVROverlayError SetDashboardOverlaySceneProcess( VROverlayHandle_t ulOverlayHandle, uint32_t unProcessId ) = 0; /** Gets the process ID that this dashboard overlay requires to have scene focus */ virtual EVROverlayError GetDashboardOverlaySceneProcess( VROverlayHandle_t ulOverlayHandle, uint32_t *punProcessId ) = 0; /** Shows the dashboard. */ virtual void ShowDashboard( const char *pchOverlayToShow ) = 0; /** Returns the tracked device that has the laser pointer in the dashboard */ virtual vr::TrackedDeviceIndex_t GetPrimaryDashboardDevice() = 0; // --------------------------------------------- // Keyboard methods // --------------------------------------------- /** Show the virtual keyboard to accept input **/ virtual EVROverlayError ShowKeyboard( EGamepadTextInputMode eInputMode, EGamepadTextInputLineMode eLineInputMode, const char *pchDescription, uint32_t unCharMax, const char *pchExistingText, bool bUseMinimalMode, uint64_t uUserValue ) = 0; virtual EVROverlayError ShowKeyboardForOverlay( VROverlayHandle_t ulOverlayHandle, EGamepadTextInputMode eInputMode, EGamepadTextInputLineMode eLineInputMode, const char *pchDescription, uint32_t unCharMax, const char *pchExistingText, bool bUseMinimalMode, uint64_t uUserValue ) = 0; /** Get the text that was entered into the text input **/ virtual uint32_t GetKeyboardText( VR_OUT_STRING() char *pchText, uint32_t cchText ) = 0; /** Hide the virtual keyboard **/ virtual void HideKeyboard() = 0; /** Set the position of the keyboard in world space **/ virtual void SetKeyboardTransformAbsolute( ETrackingUniverseOrigin eTrackingOrigin, const HmdMatrix34_t *pmatTrackingOriginToKeyboardTransform ) = 0; /** 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 **/ virtual void SetKeyboardPositionForOverlay( VROverlayHandle_t ulOverlayHandle, HmdRect2_t avoidRect ) = 0; // --------------------------------------------- // Overlay input methods // --------------------------------------------- /** Sets a list of primitives to be used for controller ray intersection * typically the size of the underlying UI in pixels (not in world space). */ virtual EVROverlayError SetOverlayIntersectionMask( VROverlayHandle_t ulOverlayHandle, VROverlayIntersectionMaskPrimitive_t *pMaskPrimitives, uint32_t unNumMaskPrimitives, uint32_t unPrimitiveSize = sizeof( VROverlayIntersectionMaskPrimitive_t ) ) = 0; virtual EVROverlayError GetOverlayFlags( VROverlayHandle_t ulOverlayHandle, uint32_t *pFlags ) = 0; // --------------------------------------------- // Message box methods // --------------------------------------------- /** Show the message overlay. This will block and return you a result. **/ virtual VRMessageOverlayResponse ShowMessageOverlay( const char* pchText, const char* pchCaption, const char* pchButton0Text, const char* pchButton1Text = nullptr, const char* pchButton2Text = nullptr, const char* pchButton3Text = nullptr ) = 0; /** If the calling process owns the overlay and it's open, this will close it. **/ virtual void CloseMessageOverlay() = 0; }; static const char * const IVROverlay_Version = "IVROverlay_018"; } // namespace vr // ivrrendermodels.h namespace vr { static const char * const k_pch_Controller_Component_GDC2015 = "gdc2015"; // Canonical coordinate system of the gdc 2015 wired controller, provided for backwards compatibility static const char * const k_pch_Controller_Component_Base = "base"; // For controllers with an unambiguous 'base'. static const char * const k_pch_Controller_Component_Tip = "tip"; // For controllers with an unambiguous 'tip' (used for 'laser-pointing') static 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 static const char * const k_pch_Controller_Component_Status = "status"; // 1:1 aspect ratio status area, with canonical [0,1] uv mapping #pragma pack( push, 8 ) /** Errors that can occur with the VR compositor */ enum EVRRenderModelError { VRRenderModelError_None = 0, VRRenderModelError_Loading = 100, VRRenderModelError_NotSupported = 200, VRRenderModelError_InvalidArg = 300, VRRenderModelError_InvalidModel = 301, VRRenderModelError_NoShapes = 302, VRRenderModelError_MultipleShapes = 303, VRRenderModelError_TooManyVertices = 304, VRRenderModelError_MultipleTextures = 305, VRRenderModelError_BufferTooSmall = 306, VRRenderModelError_NotEnoughNormals = 307, VRRenderModelError_NotEnoughTexCoords = 308, VRRenderModelError_InvalidTexture = 400, }; typedef uint32_t VRComponentProperties; enum EVRComponentProperty { VRComponentProperty_IsStatic = (1 << 0), VRComponentProperty_IsVisible = (1 << 1), VRComponentProperty_IsTouched = (1 << 2), VRComponentProperty_IsPressed = (1 << 3), VRComponentProperty_IsScrolled = (1 << 4), }; /** Describes state information about a render-model component, including transforms and other dynamic properties */ struct RenderModel_ComponentState_t { HmdMatrix34_t mTrackingToComponentRenderModel; // Transform required when drawing the component render model HmdMatrix34_t mTrackingToComponentLocal; // Transform available for attaching to a local component coordinate system (-Z out from surface ) VRComponentProperties uProperties; }; /** A single vertex in a render model */ struct RenderModel_Vertex_t { HmdVector3_t vPosition; // position in meters in device space HmdVector3_t vNormal; float rfTextureCoord[2]; }; /** A texture map for use on a render model */ #if defined(__linux__) || defined(__APPLE__) // This structure was originally defined mis-packed on Linux, preserved for // compatibility. #pragma pack( push, 4 ) #endif struct RenderModel_TextureMap_t { uint16_t unWidth, unHeight; // width and height of the texture map in pixels const uint8_t *rubTextureMapData; // Map texture data. All textures are RGBA with 8 bits per channel per pixel. Data size is width * height * 4ub }; #if defined(__linux__) || defined(__APPLE__) #pragma pack( pop ) #endif /** Session unique texture identifier. Rendermodels which share the same texture will have the same id. IDs <0 denote the texture is not present */ typedef int32_t TextureID_t; const TextureID_t INVALID_TEXTURE_ID = -1; #if defined(__linux__) || defined(__APPLE__) // This structure was originally defined mis-packed on Linux, preserved for // compatibility. #pragma pack( push, 4 ) #endif struct RenderModel_t { const RenderModel_Vertex_t *rVertexData; // Vertex data for the mesh uint32_t unVertexCount; // Number of vertices in the vertex data const uint16_t *rIndexData; // Indices into the vertex data for each triangle uint32_t unTriangleCount; // Number of triangles in the mesh. Index count is 3 * TriangleCount TextureID_t diffuseTextureId; // Session unique texture identifier. Rendermodels which share the same texture will have the same id. <0 == texture not present }; #if defined(__linux__) || defined(__APPLE__) #pragma pack( pop ) #endif struct RenderModel_ControllerMode_State_t { bool bScrollWheelVisible; // is this controller currently set to be in a scroll wheel mode }; #pragma pack( pop ) class IVRRenderModels { public: /** Loads and returns a render model for use in the application. pchRenderModelName should be a render model name * from the Prop_RenderModelName_String property or an absolute path name to a render model on disk. * * The resulting render model is valid until VR_Shutdown() is called or until FreeRenderModel() is called. When the * application is finished with the render model it should call FreeRenderModel() to free the memory associated * with the model. * * The method returns VRRenderModelError_Loading while the render model is still being loaded. * The method returns VRRenderModelError_None once loaded successfully, otherwise will return an error. */ virtual EVRRenderModelError LoadRenderModel_Async( const char *pchRenderModelName, RenderModel_t **ppRenderModel ) = 0; /** Frees a previously returned render model * It is safe to call this on a null ptr. */ virtual void FreeRenderModel( RenderModel_t *pRenderModel ) = 0; /** Loads and returns a texture for use in the application. */ virtual EVRRenderModelError LoadTexture_Async( TextureID_t textureId, RenderModel_TextureMap_t **ppTexture ) = 0; /** Frees a previously returned texture * It is safe to call this on a null ptr. */ virtual void FreeTexture( RenderModel_TextureMap_t *pTexture ) = 0; /** Creates a D3D11 texture and loads data into it. */ virtual EVRRenderModelError LoadTextureD3D11_Async( TextureID_t textureId, void *pD3D11Device, void **ppD3D11Texture2D ) = 0; /** Helper function to copy the bits into an existing texture. */ virtual EVRRenderModelError LoadIntoTextureD3D11_Async( TextureID_t textureId, void *pDstTexture ) = 0; /** Use this to free textures created with LoadTextureD3D11_Async instead of calling Release on them. */ virtual void FreeTextureD3D11( void *pD3D11Texture2D ) = 0; /** Use this to get the names of available render models. Index does not correlate to a tracked device index, but * is only used for iterating over all available render models. If the index is out of range, this function will return 0. * Otherwise, it will return the size of the buffer required for the name. */ virtual uint32_t GetRenderModelName( uint32_t unRenderModelIndex, VR_OUT_STRING() char *pchRenderModelName, uint32_t unRenderModelNameLen ) = 0; /** Returns the number of available render models. */ virtual uint32_t GetRenderModelCount() = 0; /** Returns the number of components of the specified render model. * Components are useful when client application wish to draw, label, or otherwise interact with components of tracked objects. * Examples controller components: * renderable things such as triggers, buttons * non-renderable things which include coordinate systems such as 'tip', 'base', a neutral controller agnostic hand-pose * If all controller components are enumerated and rendered, it will be equivalent to drawing the traditional render model * Returns 0 if components not supported, >0 otherwise */ virtual uint32_t GetComponentCount( const char *pchRenderModelName ) = 0; /** Use this to get the names of available components. Index does not correlate to a tracked device index, but * is only used for iterating over all available components. If the index is out of range, this function will return 0. * Otherwise, it will return the size of the buffer required for the name. */ virtual uint32_t GetComponentName( const char *pchRenderModelName, uint32_t unComponentIndex, VR_OUT_STRING( ) char *pchComponentName, uint32_t unComponentNameLen ) = 0; /** Get the button mask for all buttons associated with this component * If no buttons (or axes) are associated with this component, return 0 * Note: multiple components may be associated with the same button. Ex: two grip buttons on a single controller. * Note: A single component may be associated with multiple buttons. Ex: A trackpad which also provides "D-pad" functionality */ virtual uint64_t GetComponentButtonMask( const char *pchRenderModelName, const char *pchComponentName ) = 0; /** Use this to get the render model name for the specified rendermode/component combination, to be passed to LoadRenderModel. * If the component name is out of range, this function will return 0. * Otherwise, it will return the size of the buffer required for the name. */ virtual uint32_t GetComponentRenderModelName( const char *pchRenderModelName, const char *pchComponentName, VR_OUT_STRING( ) char *pchComponentRenderModelName, uint32_t unComponentRenderModelNameLen ) = 0; /** Use this to query information about the component, as a function of the controller state. * * For dynamic controller components (ex: trigger) values will reflect component motions * For static components this will return a consistent value independent of the VRControllerState_t * * If the pchRenderModelName or pchComponentName is invalid, this will return false (and transforms will be set to identity). * Otherwise, return true * Note: For dynamic objects, visibility may be dynamic. (I.e., true/false will be returned based on controller state and controller mode state ) */ virtual bool GetComponentState( const char *pchRenderModelName, const char *pchComponentName, const vr::VRControllerState_t *pControllerState, const RenderModel_ControllerMode_State_t *pState, RenderModel_ComponentState_t *pComponentState ) = 0; /** Returns true if the render model has a component with the specified name */ virtual bool RenderModelHasComponent( const char *pchRenderModelName, const char *pchComponentName ) = 0; /** Returns the URL of the thumbnail image for this rendermodel */ virtual uint32_t GetRenderModelThumbnailURL( const char *pchRenderModelName, VR_OUT_STRING() char *pchThumbnailURL, uint32_t unThumbnailURLLen, vr::EVRRenderModelError *peError ) = 0; /** 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 * 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 * model. */ virtual uint32_t GetRenderModelOriginalPath( const char *pchRenderModelName, VR_OUT_STRING() char *pchOriginalPath, uint32_t unOriginalPathLen, vr::EVRRenderModelError *peError ) = 0; /** Returns a string for a render model error */ virtual const char *GetRenderModelErrorNameFromEnum( vr::EVRRenderModelError error ) = 0; }; static const char * const IVRRenderModels_Version = "IVRRenderModels_005"; } // ivrextendeddisplay.h namespace vr { /** NOTE: Use of this interface is not recommended in production applications. It will not work for displays which use * direct-to-display mode. Creating our own window is also incompatible with the VR compositor and is not available when the compositor is running. */ class IVRExtendedDisplay { public: /** Size and position that the window needs to be on the VR display. */ virtual void GetWindowBounds( int32_t *pnX, int32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight ) = 0; /** Gets the viewport in the frame buffer to draw the output of the distortion into */ virtual void GetEyeOutputViewport( EVREye eEye, uint32_t *pnX, uint32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight ) = 0; /** [D3D10/11 Only] * Returns the adapter index and output index that the user should pass into EnumAdapters and EnumOutputs * to create the device and swap chain in DX10 and DX11. If an error occurs both indices will be set to -1. */ virtual void GetDXGIOutputInfo( int32_t *pnAdapterIndex, int32_t *pnAdapterOutputIndex ) = 0; }; static const char * const IVRExtendedDisplay_Version = "IVRExtendedDisplay_001"; } // ivrtrackedcamera.h namespace vr { class IVRTrackedCamera { public: /** Returns a string for an error */ virtual const char *GetCameraErrorNameFromEnum( vr::EVRTrackedCameraError eCameraError ) = 0; /** For convenience, same as tracked property request Prop_HasCamera_Bool */ virtual vr::EVRTrackedCameraError HasCamera( vr::TrackedDeviceIndex_t nDeviceIndex, bool *pHasCamera ) = 0; /** Gets size of the image frame. */ virtual vr::EVRTrackedCameraError GetCameraFrameSize( vr::TrackedDeviceIndex_t nDeviceIndex, vr::EVRTrackedCameraFrameType eFrameType, uint32_t *pnWidth, uint32_t *pnHeight, uint32_t *pnFrameBufferSize ) = 0; virtual vr::EVRTrackedCameraError GetCameraIntrinsics( vr::TrackedDeviceIndex_t nDeviceIndex, vr::EVRTrackedCameraFrameType eFrameType, vr::HmdVector2_t *pFocalLength, vr::HmdVector2_t *pCenter ) = 0; virtual vr::EVRTrackedCameraError GetCameraProjection( vr::TrackedDeviceIndex_t nDeviceIndex, vr::EVRTrackedCameraFrameType eFrameType, float flZNear, float flZFar, vr::HmdMatrix44_t *pProjection ) = 0; /** 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. * 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. * The camera should be considered a global resource accessible for shared consumption but not exclusive to any caller. * The camera may go inactive due to lack of active consumers or headset idleness. */ virtual vr::EVRTrackedCameraError AcquireVideoStreamingService( vr::TrackedDeviceIndex_t nDeviceIndex, vr::TrackedCameraHandle_t *pHandle ) = 0; virtual vr::EVRTrackedCameraError ReleaseVideoStreamingService( vr::TrackedCameraHandle_t hTrackedCamera ) = 0; /** Copies the image frame into a caller's provided buffer. The image data is currently provided as RGBA data, 4 bytes per pixel. * A caller can provide null for the framebuffer or frameheader if not desired. Requesting the frame header first, followed by the frame buffer allows * the caller to determine if the frame as advanced per the frame header sequence. * If there is no frame available yet, due to initial camera spinup or re-activation, the error will be VRTrackedCameraError_NoFrameAvailable. * Ideally a caller should be polling at ~16ms intervals */ virtual vr::EVRTrackedCameraError GetVideoStreamFrameBuffer( vr::TrackedCameraHandle_t hTrackedCamera, vr::EVRTrackedCameraFrameType eFrameType, void *pFrameBuffer, uint32_t nFrameBufferSize, vr::CameraVideoStreamFrameHeader_t *pFrameHeader, uint32_t nFrameHeaderSize ) = 0; /** Gets size of the image frame. */ virtual vr::EVRTrackedCameraError GetVideoStreamTextureSize( vr::TrackedDeviceIndex_t nDeviceIndex, vr::EVRTrackedCameraFrameType eFrameType, vr::VRTextureBounds_t *pTextureBounds, uint32_t *pnWidth, uint32_t *pnHeight ) = 0; /** Access a shared D3D11 texture for the specified tracked camera stream. * 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. * Instead, use GetVideoStreamTextureSize() with VRTrackedCameraFrameType_Undistorted to determine the proper interior subregion bounds along with GetVideoStreamTextureD3D11() with * VRTrackedCameraFrameType_MaximumUndistorted to provide the texture. The VRTrackedCameraFrameType_MaximumUndistorted will yield an image where the invalid regions are decoded * 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 * guarantees a rectangle where all pixels are valid. */ virtual vr::EVRTrackedCameraError GetVideoStreamTextureD3D11( vr::TrackedCameraHandle_t hTrackedCamera, vr::EVRTrackedCameraFrameType eFrameType, void *pD3D11DeviceOrResource, void **ppD3D11ShaderResourceView, vr::CameraVideoStreamFrameHeader_t *pFrameHeader, uint32_t nFrameHeaderSize ) = 0; /** Access a shared GL texture for the specified tracked camera stream */ virtual vr::EVRTrackedCameraError GetVideoStreamTextureGL( vr::TrackedCameraHandle_t hTrackedCamera, vr::EVRTrackedCameraFrameType eFrameType, vr::glUInt_t *pglTextureId, vr::CameraVideoStreamFrameHeader_t *pFrameHeader, uint32_t nFrameHeaderSize ) = 0; virtual vr::EVRTrackedCameraError ReleaseVideoStreamTextureGL( vr::TrackedCameraHandle_t hTrackedCamera, vr::glUInt_t glTextureId ) = 0; }; static const char * const IVRTrackedCamera_Version = "IVRTrackedCamera_003"; } // namespace vr // ivrscreenshots.h namespace vr { /** Errors that can occur with the VR compositor */ enum EVRScreenshotError { VRScreenshotError_None = 0, VRScreenshotError_RequestFailed = 1, VRScreenshotError_IncompatibleVersion = 100, VRScreenshotError_NotFound = 101, VRScreenshotError_BufferTooSmall = 102, VRScreenshotError_ScreenshotAlreadyInProgress = 108, }; /** Allows the application to generate screenshots */ class IVRScreenshots { public: /** Request a screenshot of the requested type. * A request of the VRScreenshotType_Stereo type will always * work. Other types will depend on the underlying application * support. * The first file name is for the preview image and should be a * regular screenshot (ideally from the left eye). The second * is the VR screenshot in the correct format. They should be * in the same aspect ratio. Formats per type: * VRScreenshotType_Mono: the VR filename is ignored (can be * nullptr), this is a normal flat single shot. * VRScreenshotType_Stereo: The VR image should be a * side-by-side with the left eye image on the left. * VRScreenshotType_Cubemap: The VR image should be six square * images composited horizontally. * VRScreenshotType_StereoPanorama: above/below with left eye * panorama being the above image. Image is typically square * with the panorama being 2x horizontal. * * Note that the VR dashboard will call this function when * the user presses the screenshot binding (currently System * Button + Trigger). If Steam is running, the destination * file names will be in %TEMP% and will be copied into * Steam's screenshot library for the running application * once SubmitScreenshot() is called. * If Steam is not running, the paths will be in the user's * documents folder under Documents\SteamVR\Screenshots. * Other VR applications can call this to initiate a * screenshot outside of user control. * The destination file names do not need an extension, * will be replaced with the correct one for the format * which is currently .png. */ virtual vr::EVRScreenshotError RequestScreenshot( vr::ScreenshotHandle_t *pOutScreenshotHandle, vr::EVRScreenshotType type, const char *pchPreviewFilename, const char *pchVRFilename ) = 0; /** Called by the running VR application to indicate that it * wishes to be in charge of screenshots. If the * application does not call this, the Compositor will only * support VRScreenshotType_Stereo screenshots that will be * captured without notification to the running app. * Once hooked your application will receive a * VREvent_RequestScreenshot event when the user presses the * buttons to take a screenshot. */ virtual vr::EVRScreenshotError HookScreenshot( VR_ARRAY_COUNT( numTypes ) const vr::EVRScreenshotType *pSupportedTypes, int numTypes ) = 0; /** When your application receives a * VREvent_RequestScreenshot event, call these functions to get * the details of the screenshot request. */ virtual vr::EVRScreenshotType GetScreenshotPropertyType( vr::ScreenshotHandle_t screenshotHandle, vr::EVRScreenshotError *pError ) = 0; /** Get the filename for the preview or vr image (see * vr::EScreenshotPropertyFilenames). The return value is * the size of the string. */ virtual uint32_t GetScreenshotPropertyFilename( vr::ScreenshotHandle_t screenshotHandle, vr::EVRScreenshotPropertyFilenames filenameType, VR_OUT_STRING() char *pchFilename, uint32_t cchFilename, vr::EVRScreenshotError *pError ) = 0; /** Call this if the application is taking the screen shot * will take more than a few ms processing. This will result * in an overlay being presented that shows a completion * bar. */ virtual vr::EVRScreenshotError UpdateScreenshotProgress( vr::ScreenshotHandle_t screenshotHandle, float flProgress ) = 0; /** Tells the compositor to take an internal screenshot of * type VRScreenshotType_Stereo. It will take the current * submitted scene textures of the running application and * write them into the preview image and a side-by-side file * for the VR image. * This is similar to request screenshot, but doesn't ever * talk to the application, just takes the shot and submits. */ virtual vr::EVRScreenshotError TakeStereoScreenshot( vr::ScreenshotHandle_t *pOutScreenshotHandle, const char *pchPreviewFilename, const char *pchVRFilename ) = 0; /** Submit the completed screenshot. If Steam is running * this will call into the Steam client and upload the * screenshot to the screenshots section of the library for * the running application. If Steam is not running, this * function will display a notification to the user that the * screenshot was taken. The paths should be full paths with * extensions. * File paths should be absolute including extensions. * screenshotHandle can be k_unScreenshotHandleInvalid if this * was a new shot taking by the app to be saved and not * initiated by a user (achievement earned or something) */ virtual vr::EVRScreenshotError SubmitScreenshot( vr::ScreenshotHandle_t screenshotHandle, vr::EVRScreenshotType type, const char *pchSourcePreviewFilename, const char *pchSourceVRFilename ) = 0; }; static const char * const IVRScreenshots_Version = "IVRScreenshots_001"; } // namespace vr // ivrresources.h namespace vr { class IVRResources { public: // ------------------------------------ // Shared Resource Methods // ------------------------------------ /** Loads the specified resource into the provided buffer if large enough. * Returns the size in bytes of the buffer required to hold the specified resource. */ virtual uint32_t LoadSharedResource( const char *pchResourceName, char *pchBuffer, uint32_t unBufferLen ) = 0; /** Provides the full path to the specified resource. Resource names can include named directories for * drivers and other things, and this resolves all of those and returns the actual physical path. * pchResourceTypeDirectory is the subdirectory of resources to look in. */ virtual uint32_t GetResourceFullPath( const char *pchResourceName, const char *pchResourceTypeDirectory, VR_OUT_STRING() char *pchPathBuffer, uint32_t unBufferLen ) = 0; }; static const char * const IVRResources_Version = "IVRResources_001"; } // ivrdrivermanager.h namespace vr { class IVRDriverManager { public: virtual uint32_t GetDriverCount() const = 0; /** Returns the length of the number of bytes necessary to hold this string including the trailing null. */ virtual uint32_t GetDriverName( vr::DriverId_t nDriver, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize ) = 0; virtual DriverHandle_t GetDriverHandle( const char *pchDriverName ) = 0; }; static const char * const IVRDriverManager_Version = "IVRDriverManager_001"; } // namespace vr // ivrinput.h namespace vr { typedef uint64_t VRActionHandle_t; typedef uint64_t VRActionSetHandle_t; typedef uint64_t VRInputValueHandle_t; static const VRActionHandle_t k_ulInvalidActionHandle = 0; static const VRActionSetHandle_t k_ulInvalidActionSetHandle = 0; static const VRInputValueHandle_t k_ulInvalidInputValueHandle = 0; static const uint32_t k_unMaxActionNameLength = 64; static const uint32_t k_unMaxActionSetNameLength = 64; static const uint32_t k_unMaxActionOriginCount = 16; struct InputAnalogActionData_t { // Whether or not this action is currently available to be bound in the active action set bool bActive; // The origin that caused this action's current state VRInputValueHandle_t activeOrigin; // The current state of this action; will be delta updates for mouse actions float x, y, z; // Deltas since the previous call to UpdateActionState() float deltaX, deltaY, deltaZ; // Time relative to now when this event happened. Will be negative to indicate a past time. float fUpdateTime; }; struct InputDigitalActionData_t { // Whether or not this action is currently available to be bound in the active action set bool bActive; // The origin that caused this action's current state VRInputValueHandle_t activeOrigin; // The current state of this action; will be true if currently pressed bool bState; // This is true if the state has changed since the last frame bool bChanged; // Time relative to now when this event happened. Will be negative to indicate a past time. float fUpdateTime; }; struct InputPoseActionData_t { // Whether or not this action is currently available to be bound in the active action set bool bActive; // The origin that caused this action's current state VRInputValueHandle_t activeOrigin; // The current state of this action TrackedDevicePose_t pose; }; enum EVRSkeletalTransformSpace { VRSkeletalTransformSpace_Action = 0, VRSkeletalTransformSpace_Parent = 1, VRSkeletalTransformSpace_Additive = 2, }; struct InputSkeletonActionData_t { // Whether or not this action is currently available to be bound in the active action set bool bActive; // The origin that caused this action's current state VRInputValueHandle_t activeOrigin; }; enum EVRInputFilterCancelType { VRInputFilterCancel_Timers = 0, VRInputFilterCancel_Momentum = 1, }; struct InputOriginInfo_t { VRInputValueHandle_t devicePath; TrackedDeviceIndex_t trackedDeviceIndex; char rchRenderModelComponentName[128]; }; struct VRActiveActionSet_t { /** This is the handle of the action set to activate for this frame. */ VRActionSetHandle_t ulActionSet; /** This is the handle of a device path that this action set should be active for. To * activate for all devices, set this to k_ulInvalidInputValueHandle. */ VRInputValueHandle_t ulRestrictedToDevice; /** The action set to activate for all devices other than ulRestrictedDevice. If * ulRestrictedToDevice is set to k_ulInvalidInputValueHandle, this parameter is * ignored. */ VRActionSetHandle_t ulSecondaryActionSet; }; class IVRInput { public: // --------------- Handle management --------------- // /** Sets the path to the action manifest JSON file that is used by this application. If this information * was set on the Steam partner site, calls to this function are ignored. If the Steam partner site * setting and the path provided by this call are different, VRInputError_MismatchedActionManifest is returned. * This call must be made before the first call to UpdateActionState or IVRSystem::PollNextEvent. */ virtual EVRInputError SetActionManifestPath( const char *pchActionManifestPath ) = 0; /** Returns a handle for an action set. This handle is used for all performance-sensitive calls. */ virtual EVRInputError GetActionSetHandle( const char *pchActionSetName, VRActionSetHandle_t *pHandle ) = 0; /** Returns a handle for an action. This handle is used for all performance-sensitive calls. */ virtual EVRInputError GetActionHandle( const char *pchActionName, VRActionHandle_t *pHandle ) = 0; /** Returns a handle for any path in the input system. E.g. /user/hand/right */ virtual EVRInputError GetInputSourceHandle( const char *pchInputSourcePath, VRInputValueHandle_t *pHandle ) = 0; // --------------- Reading action state ------------------- // /** Reads the current state into all actions. After this call, the results of Get*Action calls * will be the same until the next call to UpdateActionState. */ virtual EVRInputError UpdateActionState( VR_ARRAY_COUNT( unSetCount ) VRActiveActionSet_t *pSets, uint32_t unSizeOfVRSelectedActionSet_t, uint32_t unSetCount ) = 0; /** Reads the state of a digital action given its handle. This will return VRInputError_WrongType if the type of * action is something other than digital */ virtual EVRInputError GetDigitalActionData( VRActionHandle_t action, InputDigitalActionData_t *pActionData, uint32_t unActionDataSize ) = 0; /** Reads the state of an analog action given its handle. This will return VRInputError_WrongType if the type of * action is something other than analog */ virtual EVRInputError GetAnalogActionData( VRActionHandle_t action, InputAnalogActionData_t *pActionData, uint32_t unActionDataSize ) = 0; /** Reads the state of a pose action given its handle. */ virtual EVRInputError GetPoseActionData( VRActionHandle_t action, ETrackingUniverseOrigin eOrigin, float fPredictedSecondsFromNow, InputPoseActionData_t *pActionData, uint32_t unActionDataSize ) = 0; /** Reads the state of a skeletal action given its handle. */ virtual 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; /** Reads the state of a skeletal action given its handle in a compressed form that is suitable for * sending over the network. The required buffer size will never exceed ( sizeof(VR_BoneTransform_t)*boneCount + 2). * Usually the size will be much smaller. */ virtual EVRInputError GetSkeletalActionDataCompressed( VRActionHandle_t action, EVRSkeletalTransformSpace eBoneParent, float fPredictedSecondsFromNow, VR_OUT_BUFFER_COUNT( unCompressedSize ) void *pvCompressedData, uint32_t unCompressedSize, uint32_t *punRequiredCompressedSize ) = 0; /** Turns a compressed buffer from GetSkeletalActionDataCompressed and turns it back into a bone transform array. */ virtual EVRInputError UncompressSkeletalActionData( void *pvCompressedBuffer, uint32_t unCompressedBufferSize, EVRSkeletalTransformSpace *peBoneParent, VR_ARRAY_COUNT( unTransformArrayCount ) VRBoneTransform_t *pTransformArray, uint32_t unTransformArrayCount ) = 0; // --------------- Haptics ------------------- // /** Triggers a haptic event as described by the specified action */ virtual EVRInputError TriggerHapticVibrationAction( VRActionHandle_t action, float fStartSecondsFromNow, float fDurationSeconds, float fFrequency, float fAmplitude ) = 0; // --------------- Action Origins ---------------- // /** Retrieve origin handles for an action */ virtual EVRInputError GetActionOrigins( VRActionSetHandle_t actionSetHandle, VRActionHandle_t digitalActionHandle, VR_ARRAY_COUNT( originOutCount ) VRInputValueHandle_t *originsOut, uint32_t originOutCount ) = 0; /** Retrieves the name of the origin in the current language */ virtual EVRInputError GetOriginLocalizedName( VRInputValueHandle_t origin, VR_OUT_STRING() char *pchNameArray, uint32_t unNameArraySize ) = 0; /** Retrieves useful information for the origin of this action */ virtual EVRInputError GetOriginTrackedDeviceInfo( VRInputValueHandle_t origin, InputOriginInfo_t *pOriginInfo, uint32_t unOriginInfoSize ) = 0; /** Shows the current binding for the action in-headset */ virtual EVRInputError ShowActionOrigins( VRActionSetHandle_t actionSetHandle, VRActionHandle_t ulActionHandle ) = 0; /** Shows the current binding all the actions in the specified action sets */ virtual EVRInputError ShowBindingsForActionSet( VR_ARRAY_COUNT( unSetCount ) VRActiveActionSet_t *pSets, uint32_t unSizeOfVRSelectedActionSet_t, uint32_t unSetCount, VRInputValueHandle_t originToHighlight ) = 0; }; static const char * const IVRInput_Version = "IVRInput_003"; } // namespace vr // ivriobuffer.h namespace vr { typedef uint64_t IOBufferHandle_t; static const uint64_t k_ulInvalidIOBufferHandle = 0; enum EIOBufferError { IOBuffer_Success = 0, IOBuffer_OperationFailed = 100, IOBuffer_InvalidHandle = 101, IOBuffer_InvalidArgument = 102, IOBuffer_PathExists = 103, IOBuffer_PathDoesNotExist = 104, IOBuffer_Permission = 105, }; enum EIOBufferMode { IOBufferMode_Read = 0x0001, IOBufferMode_Write = 0x0002, IOBufferMode_Create = 0x0200, }; // ---------------------------------------------------------------------------------------------- // Purpose: // ---------------------------------------------------------------------------------------------- class IVRIOBuffer { public: /** opens an existing or creates a new IOBuffer of unSize bytes */ virtual vr::EIOBufferError Open( const char *pchPath, vr::EIOBufferMode mode, uint32_t unElementSize, uint32_t unElements, vr::IOBufferHandle_t *pulBuffer ) = 0; /** closes a previously opened or created buffer */ virtual vr::EIOBufferError Close( vr::IOBufferHandle_t ulBuffer ) = 0; /** reads up to unBytes from buffer into *pDst, returning number of bytes read in *punRead */ virtual vr::EIOBufferError Read( vr::IOBufferHandle_t ulBuffer, void *pDst, uint32_t unBytes, uint32_t *punRead ) = 0; /** writes unBytes of data from *pSrc into a buffer. */ virtual vr::EIOBufferError Write( vr::IOBufferHandle_t ulBuffer, void *pSrc, uint32_t unBytes ) = 0; /** retrieves the property container of an buffer. */ virtual vr::PropertyContainerHandle_t PropertyContainer( vr::IOBufferHandle_t ulBuffer ) = 0; }; static const char *IVRIOBuffer_Version = "IVRIOBuffer_001"; } // End #endif // _OPENVR_API namespace vr { /** Finds the active installation of the VR API and initializes it. The provided path must be absolute * or relative to the current working directory. These are the local install versions of the equivalent * functions in steamvr.h and will work without a local Steam install. * * This path is to the "root" of the VR API install. That's the directory with * the "drivers" directory and a platform (i.e. "win32") directory in it, not the directory with the DLL itself. * * pStartupInfo is reserved for future use. */ inline IVRSystem *VR_Init( EVRInitError *peError, EVRApplicationType eApplicationType, const char *pStartupInfo = nullptr ); /** unloads vrclient.dll. Any interface pointers from the interface are * invalid after this point */ inline void VR_Shutdown(); /** Returns true if there is an HMD attached. This check is as lightweight as possible and * can be called outside of VR_Init/VR_Shutdown. It should be used when an application wants * to know if initializing VR is a possibility but isn't ready to take that step yet. */ VR_INTERFACE bool VR_CALLTYPE VR_IsHmdPresent(); /** Returns true if the OpenVR runtime is installed. */ VR_INTERFACE bool VR_CALLTYPE VR_IsRuntimeInstalled(); /** Returns where the OpenVR runtime is installed. */ VR_INTERFACE const char *VR_CALLTYPE VR_RuntimePath(); /** Returns the name of the enum value for an EVRInitError. This function may be called outside of VR_Init()/VR_Shutdown(). */ VR_INTERFACE const char *VR_CALLTYPE VR_GetVRInitErrorAsSymbol( EVRInitError error ); /** Returns an English string for an EVRInitError. Applications should call VR_GetVRInitErrorAsSymbol instead and * use that as a key to look up their own localized error message. This function may be called outside of VR_Init()/VR_Shutdown(). */ VR_INTERFACE const char *VR_CALLTYPE VR_GetVRInitErrorAsEnglishDescription( EVRInitError error ); /** Returns the interface of the specified version. This method must be called after VR_Init. The * pointer returned is valid until VR_Shutdown is called. */ VR_INTERFACE void *VR_CALLTYPE VR_GetGenericInterface( const char *pchInterfaceVersion, EVRInitError *peError ); /** Returns whether the interface of the specified version exists. */ VR_INTERFACE bool VR_CALLTYPE VR_IsInterfaceVersionValid( const char *pchInterfaceVersion ); /** Returns a token that represents whether the VR interface handles need to be reloaded */ VR_INTERFACE uint32_t VR_CALLTYPE VR_GetInitToken(); // These typedefs allow old enum names from SDK 0.9.11 to be used in applications. // They will go away in the future. typedef EVRInitError HmdError; typedef EVREye Hmd_Eye; typedef EColorSpace ColorSpace; typedef ETrackingResult HmdTrackingResult; typedef ETrackedDeviceClass TrackedDeviceClass; typedef ETrackingUniverseOrigin TrackingUniverseOrigin; typedef ETrackedDeviceProperty TrackedDeviceProperty; typedef ETrackedPropertyError TrackedPropertyError; typedef EVRSubmitFlags VRSubmitFlags_t; typedef EVRState VRState_t; typedef ECollisionBoundsStyle CollisionBoundsStyle_t; typedef EVROverlayError VROverlayError; typedef EVRFirmwareError VRFirmwareError; typedef EVRCompositorError VRCompositorError; typedef EVRScreenshotError VRScreenshotsError; inline uint32_t &VRToken() { static uint32_t token; return token; } class COpenVRContext { public: COpenVRContext() { Clear(); } void Clear(); inline void CheckClear() { if ( VRToken() != VR_GetInitToken() ) { Clear(); VRToken() = VR_GetInitToken(); } } IVRSystem *VRSystem() { CheckClear(); if ( m_pVRSystem == nullptr ) { EVRInitError eError; m_pVRSystem = ( IVRSystem * )VR_GetGenericInterface( IVRSystem_Version, &eError ); } return m_pVRSystem; } IVRChaperone *VRChaperone() { CheckClear(); if ( m_pVRChaperone == nullptr ) { EVRInitError eError; m_pVRChaperone = ( IVRChaperone * )VR_GetGenericInterface( IVRChaperone_Version, &eError ); } return m_pVRChaperone; } IVRChaperoneSetup *VRChaperoneSetup() { CheckClear(); if ( m_pVRChaperoneSetup == nullptr ) { EVRInitError eError; m_pVRChaperoneSetup = ( IVRChaperoneSetup * )VR_GetGenericInterface( IVRChaperoneSetup_Version, &eError ); } return m_pVRChaperoneSetup; } IVRCompositor *VRCompositor() { CheckClear(); if ( m_pVRCompositor == nullptr ) { EVRInitError eError; m_pVRCompositor = ( IVRCompositor * )VR_GetGenericInterface( IVRCompositor_Version, &eError ); } return m_pVRCompositor; } IVROverlay *VROverlay() { CheckClear(); if ( m_pVROverlay == nullptr ) { EVRInitError eError; m_pVROverlay = ( IVROverlay * )VR_GetGenericInterface( IVROverlay_Version, &eError ); } return m_pVROverlay; } IVRResources *VRResources() { CheckClear(); if ( m_pVRResources == nullptr ) { EVRInitError eError; m_pVRResources = (IVRResources *)VR_GetGenericInterface( IVRResources_Version, &eError ); } return m_pVRResources; } IVRScreenshots *VRScreenshots() { CheckClear(); if ( m_pVRScreenshots == nullptr ) { EVRInitError eError; m_pVRScreenshots = ( IVRScreenshots * )VR_GetGenericInterface( IVRScreenshots_Version, &eError ); } return m_pVRScreenshots; } IVRRenderModels *VRRenderModels() { CheckClear(); if ( m_pVRRenderModels == nullptr ) { EVRInitError eError; m_pVRRenderModels = ( IVRRenderModels * )VR_GetGenericInterface( IVRRenderModels_Version, &eError ); } return m_pVRRenderModels; } IVRExtendedDisplay *VRExtendedDisplay() { CheckClear(); if ( m_pVRExtendedDisplay == nullptr ) { EVRInitError eError; m_pVRExtendedDisplay = ( IVRExtendedDisplay * )VR_GetGenericInterface( IVRExtendedDisplay_Version, &eError ); } return m_pVRExtendedDisplay; } IVRSettings *VRSettings() { CheckClear(); if ( m_pVRSettings == nullptr ) { EVRInitError eError; m_pVRSettings = ( IVRSettings * )VR_GetGenericInterface( IVRSettings_Version, &eError ); } return m_pVRSettings; } IVRApplications *VRApplications() { CheckClear(); if ( m_pVRApplications == nullptr ) { EVRInitError eError; m_pVRApplications = ( IVRApplications * )VR_GetGenericInterface( IVRApplications_Version, &eError ); } return m_pVRApplications; } IVRTrackedCamera *VRTrackedCamera() { CheckClear(); if ( m_pVRTrackedCamera == nullptr ) { EVRInitError eError; m_pVRTrackedCamera = ( IVRTrackedCamera * )VR_GetGenericInterface( IVRTrackedCamera_Version, &eError ); } return m_pVRTrackedCamera; } IVRDriverManager *VRDriverManager() { CheckClear(); if ( !m_pVRDriverManager ) { EVRInitError eError; m_pVRDriverManager = ( IVRDriverManager * )VR_GetGenericInterface( IVRDriverManager_Version, &eError ); } return m_pVRDriverManager; } IVRInput *VRInput() { CheckClear(); if ( !m_pVRInput ) { EVRInitError eError; m_pVRInput = (IVRInput *)VR_GetGenericInterface( IVRInput_Version, &eError ); } return m_pVRInput; } IVRIOBuffer *VRIOBuffer() { if ( !m_pVRIOBuffer ) { EVRInitError eError; m_pVRIOBuffer = ( IVRIOBuffer * )VR_GetGenericInterface( IVRIOBuffer_Version, &eError ); } return m_pVRIOBuffer; } private: IVRSystem *m_pVRSystem; IVRChaperone *m_pVRChaperone; IVRChaperoneSetup *m_pVRChaperoneSetup; IVRCompositor *m_pVRCompositor; IVROverlay *m_pVROverlay; IVRResources *m_pVRResources; IVRRenderModels *m_pVRRenderModels; IVRExtendedDisplay *m_pVRExtendedDisplay; IVRSettings *m_pVRSettings; IVRApplications *m_pVRApplications; IVRTrackedCamera *m_pVRTrackedCamera; IVRScreenshots *m_pVRScreenshots; IVRDriverManager *m_pVRDriverManager; IVRInput *m_pVRInput; IVRIOBuffer *m_pVRIOBuffer; }; inline COpenVRContext &OpenVRInternal_ModuleContext() { static void *ctx[ sizeof( COpenVRContext ) / sizeof( void * ) ]; return *( COpenVRContext * )ctx; // bypass zero-init constructor } inline IVRSystem *VR_CALLTYPE VRSystem() { return OpenVRInternal_ModuleContext().VRSystem(); } inline IVRChaperone *VR_CALLTYPE VRChaperone() { return OpenVRInternal_ModuleContext().VRChaperone(); } inline IVRChaperoneSetup *VR_CALLTYPE VRChaperoneSetup() { return OpenVRInternal_ModuleContext().VRChaperoneSetup(); } inline IVRCompositor *VR_CALLTYPE VRCompositor() { return OpenVRInternal_ModuleContext().VRCompositor(); } inline IVROverlay *VR_CALLTYPE VROverlay() { return OpenVRInternal_ModuleContext().VROverlay(); } inline IVRScreenshots *VR_CALLTYPE VRScreenshots() { return OpenVRInternal_ModuleContext().VRScreenshots(); } inline IVRRenderModels *VR_CALLTYPE VRRenderModels() { return OpenVRInternal_ModuleContext().VRRenderModels(); } inline IVRApplications *VR_CALLTYPE VRApplications() { return OpenVRInternal_ModuleContext().VRApplications(); } inline IVRSettings *VR_CALLTYPE VRSettings() { return OpenVRInternal_ModuleContext().VRSettings(); } inline IVRResources *VR_CALLTYPE VRResources() { return OpenVRInternal_ModuleContext().VRResources(); } inline IVRExtendedDisplay *VR_CALLTYPE VRExtendedDisplay() { return OpenVRInternal_ModuleContext().VRExtendedDisplay(); } inline IVRTrackedCamera *VR_CALLTYPE VRTrackedCamera() { return OpenVRInternal_ModuleContext().VRTrackedCamera(); } inline IVRDriverManager *VR_CALLTYPE VRDriverManager() { return OpenVRInternal_ModuleContext().VRDriverManager(); } inline IVRInput *VR_CALLTYPE VRInput() { return OpenVRInternal_ModuleContext().VRInput(); } inline IVRIOBuffer *VR_CALLTYPE VRIOBuffer() { return OpenVRInternal_ModuleContext().VRIOBuffer(); } inline void COpenVRContext::Clear() { m_pVRSystem = nullptr; m_pVRChaperone = nullptr; m_pVRChaperoneSetup = nullptr; m_pVRCompositor = nullptr; m_pVROverlay = nullptr; m_pVRRenderModels = nullptr; m_pVRExtendedDisplay = nullptr; m_pVRSettings = nullptr; m_pVRApplications = nullptr; m_pVRTrackedCamera = nullptr; m_pVRResources = nullptr; m_pVRScreenshots = nullptr; m_pVRDriverManager = nullptr; m_pVRInput = nullptr; m_pVRIOBuffer = nullptr; } VR_INTERFACE uint32_t VR_CALLTYPE VR_InitInternal2( EVRInitError *peError, EVRApplicationType eApplicationType, const char *pStartupInfo ); VR_INTERFACE void VR_CALLTYPE VR_ShutdownInternal(); /** Finds the active installation of vrclient.dll and initializes it */ inline IVRSystem *VR_Init( EVRInitError *peError, EVRApplicationType eApplicationType, const char *pStartupInfo ) { IVRSystem *pVRSystem = nullptr; EVRInitError eError; VRToken() = VR_InitInternal2( &eError, eApplicationType, pStartupInfo ); COpenVRContext &ctx = OpenVRInternal_ModuleContext(); ctx.Clear(); if ( eError == VRInitError_None ) { if ( VR_IsInterfaceVersionValid( IVRSystem_Version ) ) { pVRSystem = VRSystem(); } else { VR_ShutdownInternal(); eError = VRInitError_Init_InterfaceNotFound; } } if ( peError ) *peError = eError; return pVRSystem; } /** unloads vrclient.dll. Any interface pointers from the interface are * invalid after this point */ inline void VR_Shutdown() { VR_ShutdownInternal(); } }dxvk-2.6.1/include/spirv/000077500000000000000000000000001477473124000152735ustar00rootroot00000000000000dxvk-2.6.1/include/vulkan/000077500000000000000000000000001477473124000154305ustar00rootroot00000000000000dxvk-2.6.1/meson.build000066400000000000000000000132661477473124000146570ustar00rootroot00000000000000project('dxvk', ['c', 'cpp'], version : '2.6.1', meson_version : '>= 0.58', default_options : [ 'cpp_std=c++17', 'b_vscrt=static_from_buildtype', 'warning_level=2' ]) pkg = import('pkgconfig') cpu_family = target_machine.cpu_family() platform = target_machine.system() fs = import('fs') cpp = meson.get_compiler('cpp') cc = meson.get_compiler('c') dxvk_is_msvc = cpp.get_argument_syntax() == 'msvc' compiler_args = [ '-msse', '-msse2', '-msse3', '-mfpmath=sse', '-Wimplicit-fallthrough', # gcc '-Wno-missing-field-initializers', '-Wno-unused-parameter', '-Wno-misleading-indentation', '-Wno-cast-function-type', # Needed for GetProcAddress. # clang '-Wno-unused-private-field', '-Wno-microsoft-exception-spec', '-Wno-extern-c-compat', '-Wno-unused-const-variable', '-Wno-missing-braces', ] link_args = [] if get_option('build_id') link_args += [ '-Wl,--build-id', ] endif dxvk_include_dirs = ['./include'] if fs.is_dir('./include/vulkan/include') dxvk_include_dirs += ['./include/vulkan/include'] elif not cpp.check_header('vulkan/vulkan.h') error('Missing Vulkan-Headers') endif if fs.is_dir('./include/spirv/include') dxvk_include_dirs += ['./include/spirv/include'] elif not cpp.check_header('spirv/unified1/spirv.hpp') error('Missing SPIRV-Headers') endif dep_displayinfo = dependency( 'libdisplay-info', version: ['>= 0.0.0', '< 0.2.0'], fallback: ['libdisplay-info', 'di_dep'], default_options: ['default_library=static'], ) if platform == 'windows' dxvk_so_version = {'name_prefix': ''} compiler_args += [ '-DNOMINMAX', '-D_WIN32_WINNT=0xa00', ] if not dxvk_is_msvc link_args += [ '-static', '-static-libgcc', '-static-libstdc++', # We need to set the section alignment for debug symbols to # work properly as well as avoiding a memcpy from the Wine loader. '-Wl,--file-alignment=4096', ] # Wine's built-in back traces only work with dwarf4 symbols if get_option('debug') compiler_args += [ '-gdwarf-4', ] endif if cpu_family == 'x86' # Enable stdcall fixup on 32-bit link_args += [ '-Wl,--enable-stdcall-fixup', '-Wl,--kill-at', ] # Fix stack alignment issues with mingw on 32-bit compiler_args += [ '-mpreferred-stack-boundary=2' ] endif else # setup file alignment + enable PDB output for MSVC builds # PDBs are useful for Windows consumers of DXVK compiler_args += [ '/Z7' ] link_args += [ '/FILEALIGN:4096', '/DEBUG:FULL' ] endif lib_d3d9 = cpp.find_library('d3d9') lib_d3d11 = cpp.find_library('d3d11') lib_dxgi = cpp.find_library('dxgi') if dxvk_is_msvc res_ext = '.res' wrc = find_program('rc') wrc_generator = generator(wrc, output : [ '@BASENAME@' + res_ext ], arguments : [ '/fo', '@OUTPUT@', '@INPUT@' ], ) else res_ext = '.o' wrc = find_program('windres') wrc_generator = generator(wrc, output : [ '@BASENAME@' + res_ext ], arguments : [ '-i', '@INPUT@', '-o', '@OUTPUT@' ], ) endif dxvk_name_prefix = '' compiler_args += ['-DDXVK_WSI_WIN32'] else dxvk_abi_version = '0' dxvk_version_raw = meson.project_version().strip('v').split('.') dxvk_version = [ dxvk_version_raw[0] ] foreach i : [ 1, 2 ] padded = dxvk_version_raw[i] if padded.to_int() < 10 padded = '0' + padded endif dxvk_version += [ padded ] endforeach dxvk_so_version = {'version': dxvk_abi_version + '.' + dxvk_version[0] + dxvk_version[1] + dxvk_version[2]} wrc = find_program('touch') wrc_generator = generator(wrc, output : [ '@BASENAME@_ignored.h' ], arguments : [ '@OUTPUT@' ] ) dxvk_include_dirs += [ './include/native', './include/native/windows', './include/native/directx' ] lib_sdl3 = dependency('SDL3', required: false) lib_sdl2 = dependency('SDL2', required: false) lib_glfw = dependency('glfw', required: false) if lib_sdl3.found() compiler_args += ['-DDXVK_WSI_SDL3'] endif if lib_sdl2.found() compiler_args += ['-DDXVK_WSI_SDL2'] endif if lib_glfw.found() compiler_args += ['-DDXVK_WSI_GLFW'] endif if (not lib_sdl3.found() and not lib_sdl2.found() and not lib_glfw.found()) error('SDL3, SDL2, or GLFW are required to build dxvk-native') endif dxvk_name_prefix = 'dxvk_' dxvk_pkg_prefix = 'dxvk-' link_args += [ '-static-libgcc', '-static-libstdc++', ] endif dxvk_include_path = include_directories(dxvk_include_dirs) add_project_arguments(cpp.get_supported_arguments(compiler_args), language: 'cpp') add_project_arguments(cc.get_supported_arguments(compiler_args), language: 'c') add_project_link_arguments(cpp.get_supported_link_arguments(link_args), language: 'cpp') add_project_link_arguments(cc.get_supported_link_arguments(link_args), language: 'c') exe_ext = '' def_spec_ext = '.def' glsl_compiler = find_program('glslang', 'glslangValidator') glsl_args = [ '--quiet', '--target-env', 'vulkan1.3', '--vn', '@BASENAME@', '--depfile', '@DEPFILE@', '@INPUT@', '-o', '@OUTPUT@', ] glsl_generator = generator( glsl_compiler, output : [ '@BASENAME@.h' ], depfile : '@BASENAME@.h.d', arguments : glsl_args, ) dxvk_version = vcs_tag( command: ['git', 'describe', '--dirty=+'], input: 'version.h.in', output: 'version.h', ) conf_data = configuration_data() conf_data.set('BUILD_COMPILER', cpp.get_id()) conf_data.set('BUILD_COMPILER_VERSION', cpp.version()) conf_data.set('BUILD_TARGET', cpu_family) dxvk_buildenv = configure_file( configuration : conf_data, input: 'buildenv.h.in', output: 'buildenv.h', ) if platform != 'windows' subdir('include/native') endif subdir('src') dxvk-2.6.1/meson_options.txt000066400000000000000000000011121477473124000161350ustar00rootroot00000000000000option('enable_dxgi', type : 'boolean', value : true, description: 'Build DXGI') option('enable_d3d8', type : 'boolean', value : true, description: 'Build D3D8') option('enable_d3d9', type : 'boolean', value : true, description: 'Build D3D9') option('enable_d3d10', type : 'boolean', value : true, description: 'Build D3D10') option('enable_d3d11', type : 'boolean', value : true, description: 'Build D3D11') option('build_id', type : 'boolean', value : false) option('dxvk_native_wsi', type : 'string', value : 'sdl2', description: 'WSI system to use if building natively.')dxvk-2.6.1/package-native.sh000077500000000000000000000035611477473124000157300ustar00rootroot00000000000000#!/usr/bin/env bash set -e shopt -s extglob if [ -z "$1" ] || [ -z "$2" ]; then echo "Usage: $0 version destdir [--no-package] [--dev-build]" exit 1 fi DXVK_VERSION="$1" DXVK_SRC_DIR=$(readlink -f "$0") DXVK_SRC_DIR=$(dirname "$DXVK_SRC_DIR") DXVK_BUILD_DIR=$(realpath "$2")"/dxvk-native-$DXVK_VERSION" DXVK_ARCHIVE_PATH=$(realpath "$2")"/dxvk-native-$DXVK_VERSION.tar.gz" if [ -e "$DXVK_BUILD_DIR" ]; then echo "Build directory $DXVK_BUILD_DIR already exists" exit 1 fi shift 2 opt_nopackage=0 opt_devbuild=0 opt_buildid=false opt_64_only=0 opt_32_only=0 CC=${CC:="gcc"} CXX=${CXX:="g++"} while [ $# -gt 0 ]; do case "$1" in "--no-package") opt_nopackage=1 ;; "--dev-build") opt_nopackage=1 opt_devbuild=1 ;; "--build-id") opt_buildid=true ;; "--64-only") opt_64_only=1 ;; "--32-only") opt_32_only=1 ;; *) echo "Unrecognized option: $1" >&2 exit 1 esac shift done function build_arch { cd "$DXVK_SRC_DIR" opt_strip= if [ $opt_devbuild -eq 0 ]; then opt_strip=--strip fi CC="$CC -m$1" CXX="$CXX -m$1" meson setup \ --buildtype "release" \ --prefix "$DXVK_BUILD_DIR/usr" \ $opt_strip \ --bindir "$2" \ --libdir "$2" \ -Dbuild_id=$opt_buildid \ --force-fallback-for=libdisplay-info \ "$DXVK_BUILD_DIR/build.$1" cd "$DXVK_BUILD_DIR/build.$1" ninja install if [ $opt_devbuild -eq 0 ]; then rm -r "$DXVK_BUILD_DIR/build.$1" fi } function package { cd "$DXVK_BUILD_DIR" tar -czf "$DXVK_ARCHIVE_PATH" "usr" cd ".." rm -R "dxvk-native-$DXVK_VERSION" } if [ $opt_32_only -eq 0 ]; then build_arch 64 lib fi if [ $opt_64_only -eq 0 ]; then build_arch 32 lib32 fi if [ $opt_nopackage -eq 0 ]; then package fi dxvk-2.6.1/package-release.sh000077500000000000000000000041341477473124000160570ustar00rootroot00000000000000#!/usr/bin/env bash set -e shopt -s extglob if [ -z "$1" ] || [ -z "$2" ]; then echo "Usage: $0 version destdir [--no-package] [--dev-build]" exit 1 fi DXVK_VERSION="$1" DXVK_SRC_DIR=$(readlink -f "$0") DXVK_SRC_DIR=$(dirname "$DXVK_SRC_DIR") DXVK_BUILD_DIR=$(realpath "$2")"/dxvk-$DXVK_VERSION" DXVK_ARCHIVE_PATH=$(realpath "$2")"/dxvk-$DXVK_VERSION.tar.gz" if [ -e "$DXVK_BUILD_DIR" ]; then echo "Build directory $DXVK_BUILD_DIR already exists" exit 1 fi shift 2 opt_nopackage=0 opt_devbuild=0 opt_buildid=false opt_64_only=0 opt_32_only=0 crossfile="build-win" while [ $# -gt 0 ]; do case "$1" in "--no-package") opt_nopackage=1 ;; "--dev-build") opt_nopackage=1 opt_devbuild=1 ;; "--build-id") opt_buildid=true ;; "--64-only") opt_64_only=1 ;; "--32-only") opt_32_only=1 ;; *) echo "Unrecognized option: $1" >&2 exit 1 esac shift done function build_arch { export WINEARCH="win$1" export WINEPREFIX="$DXVK_BUILD_DIR/wine.$1" cd "$DXVK_SRC_DIR" opt_strip= if [ $opt_devbuild -eq 0 ]; then opt_strip=--strip fi meson setup --cross-file "$DXVK_SRC_DIR/$crossfile$1.txt" \ --buildtype "release" \ --prefix "$DXVK_BUILD_DIR" \ $opt_strip \ --bindir "x$1" \ --libdir "x$1" \ -Db_ndebug=if-release \ -Dbuild_id=$opt_buildid \ "$DXVK_BUILD_DIR/build.$1" cd "$DXVK_BUILD_DIR/build.$1" ninja install if [ $opt_devbuild -eq 0 ]; then # get rid of some useless .a files rm "$DXVK_BUILD_DIR/x$1/"*.!(dll) rm -R "$DXVK_BUILD_DIR/build.$1" fi } function package { cd "$DXVK_BUILD_DIR/.." tar -czf "$DXVK_ARCHIVE_PATH" "dxvk-$DXVK_VERSION" rm -R "dxvk-$DXVK_VERSION" } if [ $opt_32_only -eq 0 ]; then build_arch 64 fi if [ $opt_64_only -eq 0 ]; then build_arch 32 fi if [ $opt_nopackage -eq 0 ]; then package fi dxvk-2.6.1/src/000077500000000000000000000000001477473124000132745ustar00rootroot00000000000000dxvk-2.6.1/src/d3d10/000077500000000000000000000000001477473124000141075ustar00rootroot00000000000000dxvk-2.6.1/src/d3d10/d3d10_blend.cpp000066400000000000000000000052211477473124000165720ustar00rootroot00000000000000#include "d3d10_blend.h" #include "../d3d11/d3d11_blend.h" #include "../d3d11/d3d11_device.h" namespace dxvk { HRESULT STDMETHODCALLTYPE D3D10BlendState::QueryInterface( REFIID riid, void** ppvObject) { return m_d3d11->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE D3D10BlendState::AddRef() { return m_d3d11->AddRef(); } ULONG STDMETHODCALLTYPE D3D10BlendState::Release() { return m_d3d11->Release(); } void STDMETHODCALLTYPE D3D10BlendState::GetDevice( ID3D10Device** ppDevice) { GetD3D10Device(m_d3d11, ppDevice); } HRESULT STDMETHODCALLTYPE D3D10BlendState::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_d3d11->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10BlendState::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_d3d11->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10BlendState::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_d3d11->SetPrivateDataInterface(guid, pData); } void STDMETHODCALLTYPE D3D10BlendState::GetDesc( D3D10_BLEND_DESC* pDesc) { D3D11_BLEND_DESC d3d11Desc; m_d3d11->GetDesc(&d3d11Desc); pDesc->AlphaToCoverageEnable = d3d11Desc.AlphaToCoverageEnable; pDesc->SrcBlend = D3D10_BLEND (d3d11Desc.RenderTarget[0].SrcBlend); pDesc->DestBlend = D3D10_BLEND (d3d11Desc.RenderTarget[0].DestBlend); pDesc->BlendOp = D3D10_BLEND_OP(d3d11Desc.RenderTarget[0].BlendOp); pDesc->SrcBlendAlpha = D3D10_BLEND (d3d11Desc.RenderTarget[0].SrcBlendAlpha); pDesc->DestBlendAlpha = D3D10_BLEND (d3d11Desc.RenderTarget[0].DestBlendAlpha); pDesc->BlendOpAlpha = D3D10_BLEND_OP(d3d11Desc.RenderTarget[0].BlendOpAlpha); for (uint32_t i = 0; i < 8; i++) { uint32_t srcId = d3d11Desc.IndependentBlendEnable ? i : 0; pDesc->BlendEnable[i] = d3d11Desc.RenderTarget[srcId].BlendEnable; pDesc->RenderTargetWriteMask[i] = d3d11Desc.RenderTarget[srcId].RenderTargetWriteMask; } } void STDMETHODCALLTYPE D3D10BlendState::GetDesc1( D3D10_BLEND_DESC1* pDesc) { static_assert(sizeof(D3D10_BLEND_DESC1) == sizeof(D3D11_BLEND_DESC)); m_d3d11->GetDesc(reinterpret_cast(pDesc)); } }dxvk-2.6.1/src/d3d10/d3d10_blend.h000066400000000000000000000025211477473124000162370ustar00rootroot00000000000000#pragma once #include "d3d10_util.h" namespace dxvk { class D3D11BlendState; class D3D11Device; class D3D10BlendState : public ID3D10BlendState1 { public: D3D10BlendState(D3D11BlendState* pParent) : m_d3d11(pParent) { } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); void STDMETHODCALLTYPE GetDevice( ID3D10Device** ppDevice); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData); void STDMETHODCALLTYPE GetDesc( D3D10_BLEND_DESC* pDesc); void STDMETHODCALLTYPE GetDesc1( D3D10_BLEND_DESC1* pDesc); D3D11BlendState* GetD3D11Iface() { return m_d3d11; } private: D3D11BlendState* m_d3d11; }; }dxvk-2.6.1/src/d3d10/d3d10_buffer.cpp000066400000000000000000000056311477473124000167640ustar00rootroot00000000000000#include "d3d10_buffer.h" #include "../d3d11/d3d11_buffer.h" #include "../d3d11/d3d11_device.h" #include "../d3d11/d3d11_context.h" namespace dxvk { HRESULT STDMETHODCALLTYPE D3D10Buffer::QueryInterface( REFIID riid, void** ppvObject) { return m_d3d11->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE D3D10Buffer::AddRef() { return m_d3d11->AddRef(); } ULONG STDMETHODCALLTYPE D3D10Buffer::Release() { return m_d3d11->Release(); } void STDMETHODCALLTYPE D3D10Buffer::GetDevice( ID3D10Device** ppDevice) { GetD3D10Device(m_d3d11, ppDevice); } HRESULT STDMETHODCALLTYPE D3D10Buffer::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_d3d11->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10Buffer::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_d3d11->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10Buffer::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_d3d11->SetPrivateDataInterface(guid, pData); } void STDMETHODCALLTYPE D3D10Buffer::GetType( D3D10_RESOURCE_DIMENSION* rType) { *rType = D3D10_RESOURCE_DIMENSION_BUFFER; } void STDMETHODCALLTYPE D3D10Buffer::SetEvictionPriority( UINT EvictionPriority) { m_d3d11->SetEvictionPriority(EvictionPriority); } UINT STDMETHODCALLTYPE D3D10Buffer::GetEvictionPriority() { return m_d3d11->GetEvictionPriority(); } HRESULT STDMETHODCALLTYPE D3D10Buffer::Map( D3D10_MAP MapType, UINT MapFlags, void** ppData) { Com ctx; GetD3D11Context(m_d3d11, &ctx); D3D11_MAPPED_SUBRESOURCE sr; HRESULT hr = ctx->Map(m_d3d11, 0, D3D11_MAP(MapType), MapFlags, &sr); if (FAILED(hr)) return hr; if (ppData != nullptr) { *ppData = sr.pData; return S_OK; } return S_FALSE; } void STDMETHODCALLTYPE D3D10Buffer::Unmap() { Com ctx; GetD3D11Context(m_d3d11, &ctx); ctx->Unmap(m_d3d11, 0); } void STDMETHODCALLTYPE D3D10Buffer::GetDesc( D3D10_BUFFER_DESC* pDesc) { D3D11_BUFFER_DESC d3d11Desc; m_d3d11->GetDesc(&d3d11Desc); pDesc->ByteWidth = d3d11Desc.ByteWidth; pDesc->Usage = D3D10_USAGE(d3d11Desc.Usage); pDesc->BindFlags = d3d11Desc.BindFlags; pDesc->CPUAccessFlags = d3d11Desc.CPUAccessFlags; pDesc->MiscFlags = ConvertD3D11ResourceFlags(d3d11Desc.MiscFlags); } } dxvk-2.6.1/src/d3d10/d3d10_buffer.h000066400000000000000000000032701477473124000164260ustar00rootroot00000000000000#pragma once #include "d3d10_util.h" namespace dxvk { class D3D11Buffer; class D3D11Device; class D3D10Device; class D3D10Buffer : public ID3D10Buffer { public: D3D10Buffer(D3D11Buffer* pParent) : m_d3d11(pParent) { } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); void STDMETHODCALLTYPE GetDevice( ID3D10Device** ppDevice); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData); void STDMETHODCALLTYPE GetType( D3D10_RESOURCE_DIMENSION* rType); void STDMETHODCALLTYPE SetEvictionPriority( UINT EvictionPriority); UINT STDMETHODCALLTYPE GetEvictionPriority(); HRESULT STDMETHODCALLTYPE Map( D3D10_MAP MapType, UINT MapFlags, void** ppData); void STDMETHODCALLTYPE Unmap(); void STDMETHODCALLTYPE GetDesc( D3D10_BUFFER_DESC* pDesc); D3D11Buffer* GetD3D11Iface() { return m_d3d11; } private: D3D11Buffer* m_d3d11; }; }dxvk-2.6.1/src/d3d10/d3d10_core.cpp000066400000000000000000000040551477473124000164420ustar00rootroot00000000000000#include #include #include "../dxgi/dxgi_interfaces.h" extern "C" { using namespace dxvk; HRESULT __stdcall D3D11CoreCreateDevice( IDXGIFactory* pFactory, IDXGIAdapter* pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, const D3D_FEATURE_LEVEL* pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, ID3D11Device** ppDevice, D3D_FEATURE_LEVEL* pFeatureLevel); DLLEXPORT HRESULT __stdcall D3D10CoreCreateDevice( IDXGIFactory* pFactory, IDXGIAdapter* pAdapter, UINT Flags, D3D_FEATURE_LEVEL FeatureLevel, ID3D10Device** ppDevice) { InitReturnPtr(ppDevice); Com d3d11Device; HRESULT hr = pAdapter->CheckInterfaceSupport( __uuidof(ID3D10Device), nullptr); if (FAILED(hr)) return hr; hr = D3D11CoreCreateDevice(pFactory, pAdapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, Flags, &FeatureLevel, 1, D3D11_SDK_VERSION, &d3d11Device, nullptr); if (FAILED(hr)) return hr; Com multithread; d3d11Device->QueryInterface(__uuidof(ID3D10Multithread), reinterpret_cast(&multithread)); multithread->SetMultithreadProtected(!(Flags & D3D10_CREATE_DEVICE_SINGLETHREADED)); Com dxvkDevice; d3d11Device->QueryInterface(__uuidof(IDXGIDXVKDevice), reinterpret_cast(&dxvkDevice)); dxvkDevice->SetAPIVersion(10); if (FAILED(d3d11Device->QueryInterface( __uuidof(ID3D10Device), reinterpret_cast(ppDevice)))) return E_FAIL; return S_OK; } UINT64 STDMETHODCALLTYPE D3D10CoreGetVersion() { // Match the Windows 10 return value, but we // don't know the exact function signature return 0xa000100041770ull; } HRESULT STDMETHODCALLTYPE D3D10CoreRegisterLayers() { return E_NOTIMPL; } } dxvk-2.6.1/src/d3d10/d3d10_depth_stencil.cpp000066400000000000000000000033031477473124000203320ustar00rootroot00000000000000#include "d3d10_depth_stencil.h" #include "../d3d11/d3d11_depth_stencil.h" #include "../d3d11/d3d11_device.h" namespace dxvk { HRESULT STDMETHODCALLTYPE D3D10DepthStencilState::QueryInterface( REFIID riid, void** ppvObject) { return m_d3d11->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE D3D10DepthStencilState::AddRef() { return m_d3d11->AddRef(); } ULONG STDMETHODCALLTYPE D3D10DepthStencilState::Release() { return m_d3d11->Release(); } void STDMETHODCALLTYPE D3D10DepthStencilState::GetDevice( ID3D10Device** ppDevice) { GetD3D10Device(m_d3d11, ppDevice); } HRESULT STDMETHODCALLTYPE D3D10DepthStencilState::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_d3d11->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10DepthStencilState::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_d3d11->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10DepthStencilState::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_d3d11->SetPrivateDataInterface(guid, pData); } void STDMETHODCALLTYPE D3D10DepthStencilState::GetDesc( D3D10_DEPTH_STENCIL_DESC* pDesc) { static_assert(sizeof(D3D10_DEPTH_STENCIL_DESC) == sizeof(D3D11_DEPTH_STENCIL_DESC)); m_d3d11->GetDesc(reinterpret_cast(pDesc)); } }dxvk-2.6.1/src/d3d10/d3d10_depth_stencil.h000066400000000000000000000024551477473124000200060ustar00rootroot00000000000000#pragma once #include "d3d10_util.h" namespace dxvk { class D3D11DepthStencilState; class D3D11Device; class D3D10DepthStencilState : public ID3D10DepthStencilState { public: D3D10DepthStencilState(D3D11DepthStencilState* pParent) : m_d3d11(pParent) { } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); void STDMETHODCALLTYPE GetDevice( ID3D10Device** ppDevice); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData); void STDMETHODCALLTYPE GetDesc( D3D10_DEPTH_STENCIL_DESC* pDesc); D3D11DepthStencilState* GetD3D11Iface() { return m_d3d11; } private: D3D11DepthStencilState* m_d3d11; }; }dxvk-2.6.1/src/d3d10/d3d10_device.cpp000066400000000000000000001575341477473124000167640ustar00rootroot00000000000000#include "d3d10_device.h" #include "../d3d11/d3d11_device.h" #include "../d3d11/d3d11_context_imm.h" namespace dxvk { D3D10Device::D3D10Device( D3D11Device* pDevice, D3D11ImmediateContext* pContext) : m_device(pDevice), m_context(pContext) { } D3D10Device::~D3D10Device() { } HRESULT STDMETHODCALLTYPE D3D10Device::QueryInterface( REFIID riid, void** ppvObject) { return m_device->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE D3D10Device::AddRef() { return m_device->AddRef(); } ULONG STDMETHODCALLTYPE D3D10Device::Release() { return m_device->Release(); } HRESULT STDMETHODCALLTYPE D3D10Device::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_device->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10Device::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_device->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10Device::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_device->SetPrivateDataInterface(guid, pData); } HRESULT STDMETHODCALLTYPE D3D10Device::GetDeviceRemovedReason() { return m_device->GetDeviceRemovedReason(); } HRESULT STDMETHODCALLTYPE D3D10Device::SetExceptionMode( UINT RaiseFlags) { return m_device->SetExceptionMode(RaiseFlags); } UINT STDMETHODCALLTYPE D3D10Device::GetExceptionMode() { return m_device->GetExceptionMode(); } D3D10_FEATURE_LEVEL1 STDMETHODCALLTYPE D3D10Device::GetFeatureLevel() { return D3D10_FEATURE_LEVEL1(m_device->GetFeatureLevel()); } void STDMETHODCALLTYPE D3D10Device::ClearState() { m_context->ClearState(); } void STDMETHODCALLTYPE D3D10Device::Flush() { m_context->Flush(); } HRESULT STDMETHODCALLTYPE D3D10Device::CreateBuffer( const D3D10_BUFFER_DESC* pDesc, const D3D10_SUBRESOURCE_DATA* pInitialData, ID3D10Buffer** ppBuffer) { InitReturnPtr(ppBuffer); if (pDesc == nullptr) return E_INVALIDARG; D3D11_BUFFER_DESC d3d11Desc; d3d11Desc.ByteWidth = pDesc->ByteWidth; d3d11Desc.Usage = D3D11_USAGE(pDesc->Usage); d3d11Desc.BindFlags = pDesc->BindFlags; d3d11Desc.CPUAccessFlags = pDesc->CPUAccessFlags; d3d11Desc.MiscFlags = ConvertD3D10ResourceFlags(pDesc->MiscFlags); d3d11Desc.StructureByteStride = 0; ID3D11Buffer* d3d11Buffer = nullptr; HRESULT hr = m_device->CreateBuffer(&d3d11Desc, reinterpret_cast(pInitialData), ppBuffer != nullptr ? &d3d11Buffer : nullptr); if (hr != S_OK) return hr; *ppBuffer = static_cast(d3d11Buffer)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateTexture1D( const D3D10_TEXTURE1D_DESC* pDesc, const D3D10_SUBRESOURCE_DATA* pInitialData, ID3D10Texture1D** ppTexture1D) { InitReturnPtr(ppTexture1D); if (pDesc == nullptr) return E_INVALIDARG; D3D11_TEXTURE1D_DESC d3d11Desc; d3d11Desc.Width = pDesc->Width; d3d11Desc.MipLevels = pDesc->MipLevels; d3d11Desc.ArraySize = pDesc->ArraySize; d3d11Desc.Format = pDesc->Format; d3d11Desc.Usage = D3D11_USAGE(pDesc->Usage); d3d11Desc.BindFlags = pDesc->BindFlags; d3d11Desc.CPUAccessFlags = pDesc->CPUAccessFlags; d3d11Desc.MiscFlags = ConvertD3D10ResourceFlags(pDesc->MiscFlags); ID3D11Texture1D* d3d11Texture1D = nullptr; HRESULT hr = m_device->CreateTexture1D(&d3d11Desc, reinterpret_cast(pInitialData), ppTexture1D != nullptr ? &d3d11Texture1D : nullptr); if (hr != S_OK) return hr; *ppTexture1D = static_cast(d3d11Texture1D)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateTexture2D( const D3D10_TEXTURE2D_DESC* pDesc, const D3D10_SUBRESOURCE_DATA* pInitialData, ID3D10Texture2D** ppTexture2D) { InitReturnPtr(ppTexture2D); if (pDesc == nullptr) return E_INVALIDARG; D3D11_TEXTURE2D_DESC d3d11Desc; d3d11Desc.Width = pDesc->Width; d3d11Desc.Height = pDesc->Height; d3d11Desc.MipLevels = pDesc->MipLevels; d3d11Desc.ArraySize = pDesc->ArraySize; d3d11Desc.Format = pDesc->Format; d3d11Desc.SampleDesc = pDesc->SampleDesc; d3d11Desc.Usage = D3D11_USAGE(pDesc->Usage); d3d11Desc.BindFlags = pDesc->BindFlags; d3d11Desc.CPUAccessFlags = pDesc->CPUAccessFlags; d3d11Desc.MiscFlags = ConvertD3D10ResourceFlags(pDesc->MiscFlags); ID3D11Texture2D* d3d11Texture2D = nullptr; HRESULT hr = m_device->CreateTexture2D(&d3d11Desc, reinterpret_cast(pInitialData), ppTexture2D != nullptr ? &d3d11Texture2D : nullptr); if (hr != S_OK) return hr; *ppTexture2D = static_cast(d3d11Texture2D)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateTexture3D( const D3D10_TEXTURE3D_DESC* pDesc, const D3D10_SUBRESOURCE_DATA* pInitialData, ID3D10Texture3D** ppTexture3D) { InitReturnPtr(ppTexture3D); if (pDesc == nullptr) return E_INVALIDARG; D3D11_TEXTURE3D_DESC d3d11Desc; d3d11Desc.Width = pDesc->Width; d3d11Desc.Height = pDesc->Height; d3d11Desc.Depth = pDesc->Depth; d3d11Desc.MipLevels = pDesc->MipLevels; d3d11Desc.Format = pDesc->Format; d3d11Desc.Usage = D3D11_USAGE(pDesc->Usage); d3d11Desc.BindFlags = pDesc->BindFlags; d3d11Desc.CPUAccessFlags = pDesc->CPUAccessFlags; d3d11Desc.MiscFlags = ConvertD3D10ResourceFlags(pDesc->MiscFlags); ID3D11Texture3D* d3d11Texture3D = nullptr; HRESULT hr = m_device->CreateTexture3D(&d3d11Desc, reinterpret_cast(pInitialData), ppTexture3D != nullptr ? &d3d11Texture3D : nullptr); if (hr != S_OK) return hr; *ppTexture3D = static_cast(d3d11Texture3D)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateShaderResourceView( ID3D10Resource* pResource, const D3D10_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D10ShaderResourceView** ppSRView) { InitReturnPtr(ppSRView); if (!pResource) return E_INVALIDARG; Com d3d11Resource; GetD3D11Resource(pResource, &d3d11Resource); ID3D11ShaderResourceView* d3d11Srv = nullptr; HRESULT hr = m_device->CreateShaderResourceView(d3d11Resource.ptr(), reinterpret_cast(pDesc), ppSRView ? &d3d11Srv : nullptr); if (hr != S_OK) return hr; *ppSRView = static_cast(d3d11Srv)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateShaderResourceView1( ID3D10Resource* pResource, const D3D10_SHADER_RESOURCE_VIEW_DESC1* pDesc, ID3D10ShaderResourceView1** ppSRView) { InitReturnPtr(ppSRView); if (!pResource) return E_INVALIDARG; Com d3d11Resource; GetD3D11Resource(pResource, &d3d11Resource); ID3D11ShaderResourceView* d3d11View = nullptr; HRESULT hr = m_device->CreateShaderResourceView(d3d11Resource.ptr(), reinterpret_cast(pDesc), ppSRView ? &d3d11View : nullptr); if (hr != S_OK) return hr; *ppSRView = static_cast(d3d11View)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateRenderTargetView( ID3D10Resource* pResource, const D3D10_RENDER_TARGET_VIEW_DESC* pDesc, ID3D10RenderTargetView** ppRTView) { InitReturnPtr(ppRTView); if (!pResource) return E_INVALIDARG; Com d3d11Resource; GetD3D11Resource(pResource, &d3d11Resource); ID3D11RenderTargetView* d3d11View = nullptr; HRESULT hr = m_device->CreateRenderTargetView(d3d11Resource.ptr(), reinterpret_cast(pDesc), ppRTView ? &d3d11View : nullptr); if (hr != S_OK) return hr; *ppRTView = static_cast(d3d11View)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateDepthStencilView( ID3D10Resource* pResource, const D3D10_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D10DepthStencilView** ppDepthStencilView) { InitReturnPtr(ppDepthStencilView); if (!pResource) return E_INVALIDARG; Com d3d11Resource; GetD3D11Resource(pResource, &d3d11Resource); // D3D10 doesn't have the Flags member, so we have // to convert the structure. pDesc can be nullptr. D3D11_DEPTH_STENCIL_VIEW_DESC d3d11Desc; if (pDesc != nullptr) { d3d11Desc.ViewDimension = D3D11_DSV_DIMENSION(pDesc->ViewDimension); d3d11Desc.Format = pDesc->Format; d3d11Desc.Flags = 0; switch (pDesc->ViewDimension) { case D3D10_DSV_DIMENSION_UNKNOWN: break; case D3D10_DSV_DIMENSION_TEXTURE1D: d3d11Desc.Texture1D.MipSlice = pDesc->Texture1D.MipSlice; break; case D3D10_DSV_DIMENSION_TEXTURE1DARRAY: d3d11Desc.Texture1DArray.MipSlice = pDesc->Texture1DArray.MipSlice; d3d11Desc.Texture1DArray.FirstArraySlice = pDesc->Texture1DArray.FirstArraySlice; d3d11Desc.Texture1DArray.ArraySize = pDesc->Texture1DArray.ArraySize; break; case D3D10_DSV_DIMENSION_TEXTURE2D: d3d11Desc.Texture2D.MipSlice = pDesc->Texture2D.MipSlice; break; case D3D10_DSV_DIMENSION_TEXTURE2DARRAY: d3d11Desc.Texture2DArray.MipSlice = pDesc->Texture2DArray.MipSlice; d3d11Desc.Texture2DArray.FirstArraySlice = pDesc->Texture2DArray.FirstArraySlice; d3d11Desc.Texture2DArray.ArraySize = pDesc->Texture2DArray.ArraySize; break; case D3D10_DSV_DIMENSION_TEXTURE2DMS: break; case D3D10_DSV_DIMENSION_TEXTURE2DMSARRAY: d3d11Desc.Texture2DMSArray.FirstArraySlice = pDesc->Texture2DMSArray.FirstArraySlice; d3d11Desc.Texture2DMSArray.ArraySize = pDesc->Texture2DMSArray.ArraySize; break; } } ID3D11DepthStencilView* d3d11View = nullptr; HRESULT hr = m_device->CreateDepthStencilView( d3d11Resource.ptr(), pDesc ? &d3d11Desc : nullptr, ppDepthStencilView ? &d3d11View : nullptr); if (hr != S_OK) return hr; *ppDepthStencilView = static_cast(d3d11View)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateInputLayout( const D3D10_INPUT_ELEMENT_DESC* pInputElementDescs, UINT NumElements, const void* pShaderBytecodeWithInputSignature, SIZE_T BytecodeLength, ID3D10InputLayout** ppInputLayout) { InitReturnPtr(ppInputLayout); static_assert(sizeof(D3D10_INPUT_ELEMENT_DESC) == sizeof(D3D11_INPUT_ELEMENT_DESC)); ID3D11InputLayout* d3d11InputLayout = nullptr; HRESULT hr = m_device->CreateInputLayout( reinterpret_cast(pInputElementDescs), NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout ? &d3d11InputLayout : nullptr); if (hr != S_OK) return hr; *ppInputLayout = static_cast(d3d11InputLayout)->GetD3D10Iface(); return hr; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateVertexShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D10VertexShader** ppVertexShader) { InitReturnPtr(ppVertexShader); ID3D11VertexShader* d3d11Shader = nullptr; HRESULT hr = m_device->CreateVertexShader( pShaderBytecode, BytecodeLength, nullptr, ppVertexShader ? &d3d11Shader : nullptr); if (hr != S_OK) return hr; *ppVertexShader = static_cast(d3d11Shader)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateGeometryShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D10GeometryShader** ppGeometryShader) { InitReturnPtr(ppGeometryShader); ID3D11GeometryShader* d3d11Shader = nullptr; HRESULT hr = m_device->CreateGeometryShader( pShaderBytecode, BytecodeLength, nullptr, ppGeometryShader ? &d3d11Shader : nullptr); if (hr != S_OK) return hr; *ppGeometryShader = static_cast(d3d11Shader)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateGeometryShaderWithStreamOutput( const void* pShaderBytecode, SIZE_T BytecodeLength, const D3D10_SO_DECLARATION_ENTRY* pSODeclaration, UINT NumEntries, UINT OutputStreamStride, ID3D10GeometryShader** ppGeometryShader) { InitReturnPtr(ppGeometryShader); std::vector d3d11Entries(NumEntries); for (uint32_t i = 0; i < NumEntries; i++) { d3d11Entries[i].Stream = 0; d3d11Entries[i].SemanticName = pSODeclaration[i].SemanticName; d3d11Entries[i].SemanticIndex = pSODeclaration[i].SemanticIndex; d3d11Entries[i].StartComponent = pSODeclaration[i].StartComponent; d3d11Entries[i].ComponentCount = pSODeclaration[i].ComponentCount; d3d11Entries[i].OutputSlot = pSODeclaration[i].OutputSlot; } ID3D11GeometryShader* d3d11Shader = nullptr; HRESULT hr = m_device->CreateGeometryShaderWithStreamOutput( pShaderBytecode, BytecodeLength, d3d11Entries.data(), d3d11Entries.size(), &OutputStreamStride, 1, D3D11_SO_NO_RASTERIZED_STREAM, nullptr, ppGeometryShader ? &d3d11Shader : nullptr); if (hr != S_OK) return hr; *ppGeometryShader = static_cast(d3d11Shader)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreatePixelShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D10PixelShader** ppPixelShader) { InitReturnPtr(ppPixelShader); ID3D11PixelShader* d3d11Shader = nullptr; HRESULT hr = m_device->CreatePixelShader( pShaderBytecode, BytecodeLength, nullptr, ppPixelShader ? &d3d11Shader : nullptr); if (hr != S_OK) return hr; *ppPixelShader = static_cast(d3d11Shader)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateBlendState( const D3D10_BLEND_DESC* pBlendStateDesc, ID3D10BlendState** ppBlendState) { InitReturnPtr(ppBlendState); D3D11_BLEND_DESC d3d11Desc; if (pBlendStateDesc != nullptr) { d3d11Desc.AlphaToCoverageEnable = pBlendStateDesc->AlphaToCoverageEnable; d3d11Desc.IndependentBlendEnable = TRUE; for (uint32_t i = 0; i < 8; i++) { d3d11Desc.RenderTarget[i].BlendEnable = pBlendStateDesc->BlendEnable[i]; d3d11Desc.RenderTarget[i].SrcBlend = D3D11_BLEND (pBlendStateDesc->SrcBlend); d3d11Desc.RenderTarget[i].DestBlend = D3D11_BLEND (pBlendStateDesc->DestBlend); d3d11Desc.RenderTarget[i].BlendOp = D3D11_BLEND_OP(pBlendStateDesc->BlendOp); d3d11Desc.RenderTarget[i].SrcBlendAlpha = D3D11_BLEND (pBlendStateDesc->SrcBlendAlpha); d3d11Desc.RenderTarget[i].DestBlendAlpha = D3D11_BLEND (pBlendStateDesc->DestBlendAlpha); d3d11Desc.RenderTarget[i].BlendOpAlpha = D3D11_BLEND_OP(pBlendStateDesc->BlendOpAlpha); d3d11Desc.RenderTarget[i].RenderTargetWriteMask = pBlendStateDesc->RenderTargetWriteMask[i]; } } ID3D11BlendState* d3d11BlendState = nullptr; HRESULT hr = m_device->CreateBlendState(&d3d11Desc, ppBlendState ? &d3d11BlendState : nullptr); if (hr != S_OK) return hr; *ppBlendState = static_cast(d3d11BlendState)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateBlendState1( const D3D10_BLEND_DESC1* pBlendStateDesc, ID3D10BlendState1** ppBlendState) { InitReturnPtr(ppBlendState); ID3D11BlendState* d3d11BlendState = nullptr; HRESULT hr = m_device->CreateBlendState( reinterpret_cast(pBlendStateDesc), ppBlendState ? &d3d11BlendState : nullptr); if (hr != S_OK) return hr; *ppBlendState = static_cast(d3d11BlendState)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateDepthStencilState( const D3D10_DEPTH_STENCIL_DESC* pDepthStencilDesc, ID3D10DepthStencilState** ppDepthStencilState) { InitReturnPtr(ppDepthStencilState); ID3D11DepthStencilState* d3d11DepthStencilState = nullptr; HRESULT hr = m_device->CreateDepthStencilState( reinterpret_cast(pDepthStencilDesc), ppDepthStencilState ? &d3d11DepthStencilState : nullptr); if (hr != S_OK) return hr; *ppDepthStencilState = static_cast(d3d11DepthStencilState)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateRasterizerState( const D3D10_RASTERIZER_DESC* pRasterizerDesc, ID3D10RasterizerState** ppRasterizerState) { InitReturnPtr(ppRasterizerState); ID3D11RasterizerState* d3d11RasterizerState = nullptr; HRESULT hr = m_device->CreateRasterizerState( reinterpret_cast(pRasterizerDesc), ppRasterizerState ? &d3d11RasterizerState : nullptr); if (hr != S_OK) return hr; *ppRasterizerState = static_cast(d3d11RasterizerState)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateSamplerState( const D3D10_SAMPLER_DESC* pSamplerDesc, ID3D10SamplerState** ppSamplerState) { InitReturnPtr(ppSamplerState); if (pSamplerDesc == nullptr) return E_INVALIDARG; D3D11_SAMPLER_DESC d3d11Desc; d3d11Desc.Filter = D3D11_FILTER(pSamplerDesc->Filter); d3d11Desc.AddressU = D3D11_TEXTURE_ADDRESS_MODE(pSamplerDesc->AddressU); d3d11Desc.AddressV = D3D11_TEXTURE_ADDRESS_MODE(pSamplerDesc->AddressV); d3d11Desc.AddressW = D3D11_TEXTURE_ADDRESS_MODE(pSamplerDesc->AddressW); d3d11Desc.MipLODBias = pSamplerDesc->MipLODBias; d3d11Desc.MaxAnisotropy = pSamplerDesc->MaxAnisotropy; d3d11Desc.ComparisonFunc = D3D11_COMPARISON_FUNC(pSamplerDesc->ComparisonFunc); d3d11Desc.MinLOD = pSamplerDesc->MinLOD; d3d11Desc.MaxLOD = pSamplerDesc->MaxLOD; for (uint32_t i = 0; i < 4; i++) d3d11Desc.BorderColor[i] = pSamplerDesc->BorderColor[i]; ID3D11SamplerState* d3d11SamplerState = nullptr; HRESULT hr = m_device->CreateSamplerState(&d3d11Desc, ppSamplerState ? &d3d11SamplerState : nullptr); if (hr != S_OK) return hr; *ppSamplerState = static_cast(d3d11SamplerState)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateQuery( const D3D10_QUERY_DESC* pQueryDesc, ID3D10Query** ppQuery) { InitReturnPtr(ppQuery); if (pQueryDesc == nullptr) return E_INVALIDARG; D3D11_QUERY_DESC d3d11Desc; d3d11Desc.Query = D3D11_QUERY(pQueryDesc->Query); d3d11Desc.MiscFlags = pQueryDesc->MiscFlags; ID3D11Query* d3d11Query = nullptr; HRESULT hr = m_device->CreateQuery(&d3d11Desc, ppQuery ? &d3d11Query : nullptr); if (hr != S_OK) return hr; *ppQuery = static_cast(d3d11Query)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreatePredicate( const D3D10_QUERY_DESC* pPredicateDesc, ID3D10Predicate** ppPredicate) { InitReturnPtr(ppPredicate); D3D11_QUERY_DESC d3d11Desc; d3d11Desc.Query = D3D11_QUERY(pPredicateDesc->Query); d3d11Desc.MiscFlags = pPredicateDesc->MiscFlags; ID3D11Predicate* d3d11Predicate = nullptr; HRESULT hr = m_device->CreatePredicate(&d3d11Desc, ppPredicate ? &d3d11Predicate : nullptr); if (hr != S_OK) return hr; *ppPredicate = D3D11Query::FromPredicate(d3d11Predicate)->GetD3D10Iface(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D10Device::CreateCounter( const D3D10_COUNTER_DESC* pCounterDesc, ID3D10Counter** ppCounter) { Logger::err("D3D10Device::CreateCounter: Not implemented"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D10Device::CheckFormatSupport( DXGI_FORMAT Format, UINT* pFormatSupport) { return m_device->CheckFormatSupport(Format, pFormatSupport); } HRESULT STDMETHODCALLTYPE D3D10Device::CheckMultisampleQualityLevels( DXGI_FORMAT Format, UINT SampleCount, UINT* pNumQualityLevels) { return m_device->CheckMultisampleQualityLevels( Format, SampleCount, pNumQualityLevels); } void STDMETHODCALLTYPE D3D10Device::CheckCounterInfo( D3D10_COUNTER_INFO* pCounterInfo) { Logger::err("D3D10Device::CheckCounterInfo: Not implemented"); } HRESULT STDMETHODCALLTYPE D3D10Device::CheckCounter( const D3D10_COUNTER_DESC* pDesc, D3D10_COUNTER_TYPE* pType, UINT* pActiveCounters, char* name, UINT* pNameLength, char* units, UINT* pUnitsLength, char* description, UINT* pDescriptionLength) { Logger::err("D3D10Device::CheckCounter: Not implemented"); return E_NOTIMPL; } UINT STDMETHODCALLTYPE D3D10Device::GetCreationFlags() { return m_device->GetCreationFlags(); } HRESULT STDMETHODCALLTYPE D3D10Device::OpenSharedResource( HANDLE hResource, REFIID ReturnedInterface, void** ppResource) { return m_device->OpenSharedResource(hResource, ReturnedInterface, ppResource); } void STDMETHODCALLTYPE D3D10Device::ClearRenderTargetView( ID3D10RenderTargetView* pRenderTargetView, const FLOAT ColorRGBA[4]) { D3D10RenderTargetView* d3d10View = static_cast(pRenderTargetView); D3D11RenderTargetView* d3d11View = d3d10View ? d3d10View->GetD3D11Iface() : nullptr; m_context->ClearRenderTargetView(d3d11View, ColorRGBA); } void STDMETHODCALLTYPE D3D10Device::ClearDepthStencilView( ID3D10DepthStencilView* pDepthStencilView, UINT ClearFlags, FLOAT Depth, UINT8 Stencil) { D3D10DepthStencilView* d3d10View = static_cast(pDepthStencilView); D3D11DepthStencilView* d3d11View = d3d10View ? d3d10View->GetD3D11Iface() : nullptr; m_context->ClearDepthStencilView(d3d11View, ClearFlags, Depth, Stencil); } void STDMETHODCALLTYPE D3D10Device::SetPredication( ID3D10Predicate* pPredicate, BOOL PredicateValue) { D3D10Query* d3d10Predicate = static_cast(pPredicate); D3D11Query* d3d11Predicate = d3d10Predicate ? d3d10Predicate->GetD3D11Iface() : nullptr; m_context->SetPredication(D3D11Query::AsPredicate(d3d11Predicate), PredicateValue); } void STDMETHODCALLTYPE D3D10Device::GetPredication( ID3D10Predicate** ppPredicate, BOOL* pPredicateValue) { ID3D11Predicate* d3d11Predicate = nullptr; m_context->GetPredication( ppPredicate ? &d3d11Predicate : nullptr, pPredicateValue); if (ppPredicate) *ppPredicate = d3d11Predicate ? D3D11Query::FromPredicate(d3d11Predicate)->GetD3D10Iface() : nullptr; } void STDMETHODCALLTYPE D3D10Device::CopySubresourceRegion( ID3D10Resource* pDstResource, UINT DstSubresource, UINT DstX, UINT DstY, UINT DstZ, ID3D10Resource* pSrcResource, UINT SrcSubresource, const D3D10_BOX* pSrcBox) { if (!pDstResource || !pSrcResource) return; Com d3d11DstResource; Com d3d11SrcResource; GetD3D11Resource(pDstResource, &d3d11DstResource); GetD3D11Resource(pSrcResource, &d3d11SrcResource); m_context->CopySubresourceRegion( d3d11DstResource.ptr(), DstSubresource, DstX, DstY, DstZ, d3d11SrcResource.ptr(), SrcSubresource, reinterpret_cast(pSrcBox)); } void STDMETHODCALLTYPE D3D10Device::CopyResource( ID3D10Resource* pDstResource, ID3D10Resource* pSrcResource) { if (!pDstResource || !pSrcResource) return; Com d3d11DstResource; Com d3d11SrcResource; GetD3D11Resource(pDstResource, &d3d11DstResource); GetD3D11Resource(pSrcResource, &d3d11SrcResource); m_context->CopyResource( d3d11DstResource.ptr(), d3d11SrcResource.ptr()); } void STDMETHODCALLTYPE D3D10Device::UpdateSubresource( ID3D10Resource* pDstResource, UINT DstSubresource, const D3D10_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch) { if (!pDstResource) return; Com d3d11DstResource; GetD3D11Resource(pDstResource, &d3d11DstResource); m_context->UpdateSubresource( d3d11DstResource.ptr(), DstSubresource, reinterpret_cast(pDstBox), pSrcData, SrcRowPitch, SrcDepthPitch); } void STDMETHODCALLTYPE D3D10Device::GenerateMips( ID3D10ShaderResourceView* pShaderResourceView) { D3D10ShaderResourceView* d3d10View = static_cast(pShaderResourceView); D3D11ShaderResourceView* d3d11View = d3d10View ? d3d10View->GetD3D11Iface() : nullptr; m_context->GenerateMips(d3d11View); } void STDMETHODCALLTYPE D3D10Device::ResolveSubresource( ID3D10Resource* pDstResource, UINT DstSubresource, ID3D10Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format) { if (!pDstResource || !pSrcResource) return; Com d3d11DstResource; Com d3d11SrcResource; GetD3D11Resource(pDstResource, &d3d11DstResource); GetD3D11Resource(pSrcResource, &d3d11SrcResource); m_context->ResolveSubresource( d3d11DstResource.ptr(), DstSubresource, d3d11SrcResource.ptr(), SrcSubresource, Format); } void STDMETHODCALLTYPE D3D10Device::Draw( UINT VertexCount, UINT StartVertexLocation) { m_context->Draw(VertexCount, StartVertexLocation); } void STDMETHODCALLTYPE D3D10Device::DrawIndexed( UINT IndexCount, UINT StartIndexLocation, INT BaseVertexLocation) { m_context->DrawIndexed(IndexCount, StartIndexLocation, BaseVertexLocation); } void STDMETHODCALLTYPE D3D10Device::DrawInstanced( UINT VertexCountPerInstance, UINT InstanceCount, UINT StartVertexLocation, UINT StartInstanceLocation) { m_context->DrawInstanced( VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); } void STDMETHODCALLTYPE D3D10Device::DrawIndexedInstanced( UINT IndexCountPerInstance, UINT InstanceCount, UINT StartIndexLocation, INT BaseVertexLocation, UINT StartInstanceLocation) { m_context->DrawIndexedInstanced( IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation); } void STDMETHODCALLTYPE D3D10Device::DrawAuto() { m_context->DrawAuto(); } void STDMETHODCALLTYPE D3D10Device::IASetInputLayout( ID3D10InputLayout* pInputLayout) { D3D10InputLayout* d3d10InputLayout = static_cast(pInputLayout); D3D11InputLayout* d3d11InputLayout = d3d10InputLayout ? d3d10InputLayout->GetD3D11Iface() : nullptr; m_context->IASetInputLayout(d3d11InputLayout); } void STDMETHODCALLTYPE D3D10Device::IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY Topology) { m_context->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY(Topology)); } void STDMETHODCALLTYPE D3D10Device::IASetVertexBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer* const* ppVertexBuffers, const UINT* pStrides, const UINT* pOffsets) { ID3D11Buffer* d3d11Buffers[D3D10_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT]; if (NumBuffers > D3D10_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT) return; for (uint32_t i = 0; i < NumBuffers; i++) { d3d11Buffers[i] = ppVertexBuffers[i] ? static_cast(ppVertexBuffers[i])->GetD3D11Iface() : nullptr; } m_context->IASetVertexBuffers( StartSlot, NumBuffers, d3d11Buffers, pStrides, pOffsets); } void STDMETHODCALLTYPE D3D10Device::IASetIndexBuffer( ID3D10Buffer* pIndexBuffer, DXGI_FORMAT Format, UINT Offset) { D3D10Buffer* d3d10Buffer = static_cast(pIndexBuffer); D3D11Buffer* d3d11Buffer = d3d10Buffer ? d3d10Buffer->GetD3D11Iface() : nullptr; m_context->IASetIndexBuffer(d3d11Buffer, Format, Offset); } void STDMETHODCALLTYPE D3D10Device::IAGetInputLayout( ID3D10InputLayout** ppInputLayout) { ID3D11InputLayout* d3d11InputLayout = nullptr; m_context->IAGetInputLayout(&d3d11InputLayout); *ppInputLayout = d3d11InputLayout ? static_cast(d3d11InputLayout)->GetD3D10Iface() : nullptr; } void STDMETHODCALLTYPE D3D10Device::IAGetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY* pTopology) { D3D11_PRIMITIVE_TOPOLOGY d3d11Topology; m_context->IAGetPrimitiveTopology(&d3d11Topology); *pTopology = d3d11Topology <= 32 /* begin patch list */ ? D3D10_PRIMITIVE_TOPOLOGY(d3d11Topology) : D3D10_PRIMITIVE_TOPOLOGY_UNDEFINED; } void STDMETHODCALLTYPE D3D10Device::IAGetVertexBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer** ppVertexBuffers, UINT* pStrides, UINT* pOffsets) { ID3D11Buffer* d3d11Buffers[D3D10_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT]; m_context->IAGetVertexBuffers( StartSlot, NumBuffers, ppVertexBuffers ? d3d11Buffers : nullptr, pStrides, pOffsets); if (ppVertexBuffers) { for (uint32_t i = 0; i < NumBuffers; i++) { ppVertexBuffers[i] = d3d11Buffers[i] ? static_cast(d3d11Buffers[i])->GetD3D10Iface() : nullptr; } } } void STDMETHODCALLTYPE D3D10Device::IAGetIndexBuffer( ID3D10Buffer** pIndexBuffer, DXGI_FORMAT* Format, UINT* Offset) { ID3D11Buffer* d3d11Buffer = nullptr; m_context->IAGetIndexBuffer( pIndexBuffer ? &d3d11Buffer : nullptr, Format, Offset); if (pIndexBuffer) *pIndexBuffer = d3d11Buffer ? static_cast(d3d11Buffer)->GetD3D10Iface() : nullptr; } void STDMETHODCALLTYPE D3D10Device::VSSetShader( ID3D10VertexShader* pVertexShader) { D3D10VertexShader* d3d10Shader = static_cast(pVertexShader); D3D11VertexShader* d3d11Shader = d3d10Shader ? d3d10Shader->GetD3D11Iface() : nullptr; m_context->VSSetShader(d3d11Shader, nullptr, 0); } void STDMETHODCALLTYPE D3D10Device::VSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer* const* ppConstantBuffers) { ID3D11Buffer* d3d11Buffers[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT]; if (NumBuffers > D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT) return; for (uint32_t i = 0; i < NumBuffers; i++) { d3d11Buffers[i] = ppConstantBuffers && ppConstantBuffers[i] ? static_cast(ppConstantBuffers[i])->GetD3D11Iface() : nullptr; } m_context->VSSetConstantBuffers(StartSlot, NumBuffers, d3d11Buffers); } void STDMETHODCALLTYPE D3D10Device::VSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D10ShaderResourceView* const* ppShaderResourceViews) { ID3D11ShaderResourceView* d3d11Views[D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT]; if (NumViews > D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT) return; for (uint32_t i = 0; i < NumViews; i++) { d3d11Views[i] = ppShaderResourceViews && ppShaderResourceViews[i] ? static_cast(ppShaderResourceViews[i])->GetD3D11Iface() : nullptr; } m_context->VSSetShaderResources(StartSlot, NumViews, d3d11Views); } void STDMETHODCALLTYPE D3D10Device::VSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D10SamplerState* const* ppSamplers) { ID3D11SamplerState* d3d11Samplers[D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT]; if (NumSamplers > D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT) return; for (uint32_t i = 0; i < NumSamplers; i++) { d3d11Samplers[i] = ppSamplers && ppSamplers[i] ? static_cast(ppSamplers[i])->GetD3D11Iface() : nullptr; } m_context->VSSetSamplers(StartSlot, NumSamplers, d3d11Samplers); } void STDMETHODCALLTYPE D3D10Device::VSGetShader( ID3D10VertexShader** ppVertexShader) { ID3D11VertexShader* d3d11Shader = nullptr; m_context->VSGetShader(&d3d11Shader, nullptr, nullptr); *ppVertexShader = d3d11Shader ? static_cast(d3d11Shader)->GetD3D10Iface() : nullptr; } void STDMETHODCALLTYPE D3D10Device::VSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer** ppConstantBuffers) { ID3D11Buffer* d3d11Buffers[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT]; m_context->VSGetConstantBuffers(StartSlot, NumBuffers, d3d11Buffers); for (uint32_t i = 0; i < NumBuffers; i++) { ppConstantBuffers[i] = d3d11Buffers[i] ? static_cast(d3d11Buffers[i])->GetD3D10Iface() : nullptr; } } void STDMETHODCALLTYPE D3D10Device::VSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D10ShaderResourceView** ppShaderResourceViews) { ID3D11ShaderResourceView* d3d11Views[D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT]; m_context->VSGetShaderResources(StartSlot, NumViews, d3d11Views); for (uint32_t i = 0; i < NumViews; i++) { ppShaderResourceViews[i] = d3d11Views[i] ? static_cast(d3d11Views[i])->GetD3D10Iface() : nullptr; } } void STDMETHODCALLTYPE D3D10Device::VSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D10SamplerState** ppSamplers) { ID3D11SamplerState* d3d11Samplers[D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT]; m_context->VSGetSamplers(StartSlot, NumSamplers, d3d11Samplers); for (uint32_t i = 0; i < NumSamplers; i++) { ppSamplers[i] = d3d11Samplers[i] ? static_cast(d3d11Samplers[i])->GetD3D10Iface() : nullptr; } } void STDMETHODCALLTYPE D3D10Device::GSSetShader( ID3D10GeometryShader* pShader) { D3D10GeometryShader* d3d10Shader = static_cast(pShader); D3D11GeometryShader* d3d11Shader = d3d10Shader ? d3d10Shader->GetD3D11Iface() : nullptr; m_context->GSSetShader(d3d11Shader, nullptr, 0); } void STDMETHODCALLTYPE D3D10Device::GSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer* const* ppConstantBuffers) { ID3D11Buffer* d3d11Buffers[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT]; if (NumBuffers > D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT) return; for (uint32_t i = 0; i < NumBuffers; i++) { d3d11Buffers[i] = ppConstantBuffers && ppConstantBuffers[i] ? static_cast(ppConstantBuffers[i])->GetD3D11Iface() : nullptr; } m_context->GSSetConstantBuffers(StartSlot, NumBuffers, d3d11Buffers); } void STDMETHODCALLTYPE D3D10Device::GSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D10ShaderResourceView* const* ppShaderResourceViews) { ID3D11ShaderResourceView* d3d11Views[D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT]; if (NumViews > D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT) return; for (uint32_t i = 0; i < NumViews; i++) { d3d11Views[i] = ppShaderResourceViews && ppShaderResourceViews[i] ? static_cast(ppShaderResourceViews[i])->GetD3D11Iface() : nullptr; } m_context->GSSetShaderResources(StartSlot, NumViews, d3d11Views); } void STDMETHODCALLTYPE D3D10Device::GSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D10SamplerState* const* ppSamplers) { ID3D11SamplerState* d3d11Samplers[D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT]; if (NumSamplers > D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT) return; for (uint32_t i = 0; i < NumSamplers; i++) { d3d11Samplers[i] = ppSamplers && ppSamplers[i] ? static_cast(ppSamplers[i])->GetD3D11Iface() : nullptr; } m_context->GSSetSamplers(StartSlot, NumSamplers, d3d11Samplers); } void STDMETHODCALLTYPE D3D10Device::GSGetShader( ID3D10GeometryShader** ppGeometryShader) { ID3D11GeometryShader* d3d11Shader = nullptr; m_context->GSGetShader(&d3d11Shader, nullptr, nullptr); *ppGeometryShader = d3d11Shader ? static_cast(d3d11Shader)->GetD3D10Iface() : nullptr; } void STDMETHODCALLTYPE D3D10Device::GSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer** ppConstantBuffers) { ID3D11Buffer* d3d11Buffers[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT]; m_context->GSGetConstantBuffers(StartSlot, NumBuffers, d3d11Buffers); for (uint32_t i = 0; i < NumBuffers; i++) { ppConstantBuffers[i] = d3d11Buffers[i] ? static_cast(d3d11Buffers[i])->GetD3D10Iface() : nullptr; } } void STDMETHODCALLTYPE D3D10Device::GSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D10ShaderResourceView** ppShaderResourceViews) { ID3D11ShaderResourceView* d3d11Views[D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT]; m_context->GSGetShaderResources(StartSlot, NumViews, d3d11Views); for (uint32_t i = 0; i < NumViews; i++) { ppShaderResourceViews[i] = d3d11Views[i] ? static_cast(d3d11Views[i])->GetD3D10Iface() : nullptr; } } void STDMETHODCALLTYPE D3D10Device::GSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D10SamplerState** ppSamplers) { ID3D11SamplerState* d3d11Samplers[D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT]; m_context->GSGetSamplers(StartSlot, NumSamplers, d3d11Samplers); for (uint32_t i = 0; i < NumSamplers; i++) { ppSamplers[i] = d3d11Samplers[i] ? static_cast(d3d11Samplers[i])->GetD3D10Iface() : nullptr; } } void STDMETHODCALLTYPE D3D10Device::PSSetShader( ID3D10PixelShader* pPixelShader) { D3D10PixelShader* d3d10Shader = static_cast(pPixelShader); D3D11PixelShader* d3d11Shader = d3d10Shader ? d3d10Shader->GetD3D11Iface() : nullptr; m_context->PSSetShader(d3d11Shader, nullptr, 0); } void STDMETHODCALLTYPE D3D10Device::PSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer* const* ppConstantBuffers) { ID3D11Buffer* d3d11Buffers[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT]; if (NumBuffers > D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT) return; for (uint32_t i = 0; i < NumBuffers; i++) { d3d11Buffers[i] = ppConstantBuffers && ppConstantBuffers[i] ? static_cast(ppConstantBuffers[i])->GetD3D11Iface() : nullptr; } m_context->PSSetConstantBuffers(StartSlot, NumBuffers, d3d11Buffers); } void STDMETHODCALLTYPE D3D10Device::PSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D10ShaderResourceView* const* ppShaderResourceViews) { ID3D11ShaderResourceView* d3d11Views[D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT]; if (NumViews > D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT) return; for (uint32_t i = 0; i < NumViews; i++) { d3d11Views[i] = ppShaderResourceViews && ppShaderResourceViews[i] ? static_cast(ppShaderResourceViews[i])->GetD3D11Iface() : nullptr; } m_context->PSSetShaderResources(StartSlot, NumViews, d3d11Views); } void STDMETHODCALLTYPE D3D10Device::PSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D10SamplerState* const* ppSamplers) { ID3D11SamplerState* d3d11Samplers[D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT]; if (NumSamplers > D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT) return; for (uint32_t i = 0; i < NumSamplers; i++) { d3d11Samplers[i] = ppSamplers && ppSamplers[i] ? static_cast(ppSamplers[i])->GetD3D11Iface() : nullptr; } m_context->PSSetSamplers(StartSlot, NumSamplers, d3d11Samplers); } void STDMETHODCALLTYPE D3D10Device::PSGetShader( ID3D10PixelShader** ppPixelShader) { ID3D11PixelShader* d3d11Shader = nullptr; m_context->PSGetShader(&d3d11Shader, nullptr, nullptr); *ppPixelShader = d3d11Shader ? static_cast(d3d11Shader)->GetD3D10Iface() : nullptr; } void STDMETHODCALLTYPE D3D10Device::PSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer** ppConstantBuffers) { ID3D11Buffer* d3d11Buffers[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT]; m_context->PSGetConstantBuffers(StartSlot, NumBuffers, d3d11Buffers); for (uint32_t i = 0; i < NumBuffers; i++) { ppConstantBuffers[i] = d3d11Buffers[i] ? static_cast(d3d11Buffers[i])->GetD3D10Iface() : nullptr; } } void STDMETHODCALLTYPE D3D10Device::PSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D10ShaderResourceView** ppShaderResourceViews) { ID3D11ShaderResourceView* d3d11Views[D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT]; m_context->PSGetShaderResources(StartSlot, NumViews, d3d11Views); for (uint32_t i = 0; i < NumViews; i++) { ppShaderResourceViews[i] = d3d11Views[i] ? static_cast(d3d11Views[i])->GetD3D10Iface() : nullptr; } } void STDMETHODCALLTYPE D3D10Device::PSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D10SamplerState** ppSamplers) { ID3D11SamplerState* d3d11Samplers[D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT]; m_context->PSGetSamplers(StartSlot, NumSamplers, d3d11Samplers); for (uint32_t i = 0; i < NumSamplers; i++) { ppSamplers[i] = d3d11Samplers[i] ? static_cast(d3d11Samplers[i])->GetD3D10Iface() : nullptr; } } void STDMETHODCALLTYPE D3D10Device::OMSetRenderTargets( UINT NumViews, ID3D10RenderTargetView* const* ppRenderTargetViews, ID3D10DepthStencilView* pDepthStencilView) { ID3D11RenderTargetView* d3d11Rtv[D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT]; if (NumViews > D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT) return; for (uint32_t i = 0; i < NumViews; i++) { d3d11Rtv[i] = ppRenderTargetViews && ppRenderTargetViews[i] ? static_cast(ppRenderTargetViews[i])->GetD3D11Iface() : nullptr; } D3D10DepthStencilView* d3d10Dsv = static_cast(pDepthStencilView); D3D11DepthStencilView* d3d11Dsv = d3d10Dsv ? d3d10Dsv->GetD3D11Iface() : nullptr; m_context->OMSetRenderTargets(NumViews, d3d11Rtv, d3d11Dsv); } void STDMETHODCALLTYPE D3D10Device::OMSetBlendState( ID3D10BlendState* pBlendState, const FLOAT BlendFactor[4], UINT SampleMask) { D3D10BlendState* d3d10BlendState = static_cast(pBlendState); D3D11BlendState* d3d11BlendState = d3d10BlendState ? d3d10BlendState->GetD3D11Iface() : nullptr; m_context->OMSetBlendState(d3d11BlendState, BlendFactor, SampleMask); } void STDMETHODCALLTYPE D3D10Device::OMSetDepthStencilState( ID3D10DepthStencilState* pDepthStencilState, UINT StencilRef) { D3D10DepthStencilState* d3d10DepthStencilState = static_cast(pDepthStencilState); D3D11DepthStencilState* d3d11DepthStencilState = d3d10DepthStencilState ? d3d10DepthStencilState->GetD3D11Iface() : nullptr; m_context->OMSetDepthStencilState(d3d11DepthStencilState, StencilRef); } void STDMETHODCALLTYPE D3D10Device::OMGetRenderTargets( UINT NumViews, ID3D10RenderTargetView** ppRenderTargetViews, ID3D10DepthStencilView** ppDepthStencilView) { ID3D11RenderTargetView* d3d11Rtv[D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT]; ID3D11DepthStencilView* d3d11Dsv = nullptr; m_context->OMGetRenderTargets(NumViews, ppRenderTargetViews ? d3d11Rtv : nullptr, ppDepthStencilView ? &d3d11Dsv : nullptr); if (ppRenderTargetViews != nullptr) { for (uint32_t i = 0; i < NumViews; i++) { ppRenderTargetViews[i] = d3d11Rtv[i] ? static_cast(d3d11Rtv[i])->GetD3D10Iface() : nullptr; } } if (ppDepthStencilView) *ppDepthStencilView = d3d11Dsv ? static_cast(d3d11Dsv)->GetD3D10Iface() : nullptr; } void STDMETHODCALLTYPE D3D10Device::OMGetBlendState( ID3D10BlendState** ppBlendState, FLOAT BlendFactor[4], UINT* pSampleMask) { ID3D11BlendState* d3d11BlendState = nullptr; m_context->OMGetBlendState( ppBlendState ? &d3d11BlendState : nullptr, BlendFactor, pSampleMask); if (ppBlendState != nullptr) *ppBlendState = d3d11BlendState ? static_cast(d3d11BlendState)->GetD3D10Iface() : nullptr; } void STDMETHODCALLTYPE D3D10Device::OMGetDepthStencilState( ID3D10DepthStencilState** ppDepthStencilState, UINT* pStencilRef) { ID3D11DepthStencilState* d3d11DepthStencilState = nullptr; m_context->OMGetDepthStencilState( ppDepthStencilState ? &d3d11DepthStencilState : nullptr, pStencilRef); if (ppDepthStencilState != nullptr) *ppDepthStencilState = d3d11DepthStencilState ? static_cast(d3d11DepthStencilState)->GetD3D10Iface() : nullptr; } void STDMETHODCALLTYPE D3D10Device::RSSetState( ID3D10RasterizerState* pRasterizerState) { D3D10RasterizerState* d3d10RasterizerState = static_cast(pRasterizerState); D3D11RasterizerState* d3d11RasterizerState = d3d10RasterizerState ? d3d10RasterizerState->GetD3D11Iface() : nullptr; m_context->RSSetState(d3d11RasterizerState); } void STDMETHODCALLTYPE D3D10Device::RSSetViewports( UINT NumViewports, const D3D10_VIEWPORT* pViewports) { D3D11_VIEWPORT vp[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; if (NumViewports > D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE) return; for (uint32_t i = 0; i < NumViewports; i++) { vp[i].TopLeftX = float(pViewports[i].TopLeftX); vp[i].TopLeftY = float(pViewports[i].TopLeftY); vp[i].Width = float(pViewports[i].Width); vp[i].Height = float(pViewports[i].Height); vp[i].MinDepth = pViewports[i].MinDepth; vp[i].MaxDepth = pViewports[i].MaxDepth; } m_context->RSSetViewports(NumViewports, vp); } void STDMETHODCALLTYPE D3D10Device::RSSetScissorRects( UINT NumRects, const D3D10_RECT* pRects) { m_context->RSSetScissorRects(NumRects, pRects); } void STDMETHODCALLTYPE D3D10Device::RSGetState( ID3D10RasterizerState** ppRasterizerState) { ID3D11RasterizerState* d3d11RasterizerState = nullptr; m_context->RSGetState(&d3d11RasterizerState); *ppRasterizerState = d3d11RasterizerState ? static_cast(d3d11RasterizerState)->GetD3D10Iface() : nullptr; } void STDMETHODCALLTYPE D3D10Device::RSGetViewports( UINT* NumViewports, D3D10_VIEWPORT* pViewports) { D3D11_VIEWPORT vp[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; m_context->RSGetViewports(NumViewports, pViewports != nullptr ? vp : nullptr); if (pViewports != nullptr) { for (uint32_t i = 0; i < *NumViewports; i++) { pViewports[i].TopLeftX = int32_t(vp[i].TopLeftX); pViewports[i].TopLeftY = int32_t(vp[i].TopLeftY); pViewports[i].Width = uint32_t(vp[i].Width); pViewports[i].Height = uint32_t(vp[i].Height); pViewports[i].MinDepth = vp[i].MinDepth; pViewports[i].MaxDepth = vp[i].MaxDepth; } } } void STDMETHODCALLTYPE D3D10Device::RSGetScissorRects( UINT* NumRects, D3D10_RECT* pRects) { m_context->RSGetScissorRects(NumRects, pRects); } void STDMETHODCALLTYPE D3D10Device::SOSetTargets( UINT NumBuffers, ID3D10Buffer* const* ppSOTargets, const UINT* pOffsets) { ID3D11Buffer* d3d11Buffers[D3D10_SO_BUFFER_SLOT_COUNT]; if (NumBuffers > D3D10_SO_BUFFER_SLOT_COUNT) return; for (uint32_t i = 0; i < NumBuffers; i++) { d3d11Buffers[i] = ppSOTargets && ppSOTargets[i] ? static_cast(ppSOTargets[i])->GetD3D11Iface() : nullptr; } m_context->SOSetTargets(NumBuffers, d3d11Buffers, pOffsets); } void STDMETHODCALLTYPE D3D10Device::SOGetTargets( UINT NumBuffers, ID3D10Buffer** ppSOTargets, UINT* pOffsets) { ID3D11Buffer* d3d11Buffers[D3D10_SO_BUFFER_SLOT_COUNT]; m_context->SOGetTargetsWithOffsets(NumBuffers, ppSOTargets ? d3d11Buffers : nullptr, pOffsets); if (ppSOTargets != nullptr) { for (uint32_t i = 0; i < NumBuffers; i++) { ppSOTargets[i] = d3d11Buffers[i] ? static_cast(d3d11Buffers[i])->GetD3D10Iface() : nullptr; } } } void STDMETHODCALLTYPE D3D10Device::SetTextFilterSize( UINT Width, UINT Height) { // D3D10 doesn't seem to actually store or do anything with these values, // as when calling GetTextFilterSize, it just makes the values 0. } void STDMETHODCALLTYPE D3D10Device::GetTextFilterSize( UINT* pWidth, UINT* pHeight) { if (pWidth) *pWidth = 0; if (pHeight) *pHeight = 0; } } dxvk-2.6.1/src/d3d10/d3d10_device.h000066400000000000000000000474311477473124000164230ustar00rootroot00000000000000#pragma once #include "d3d10_multithread.h" namespace dxvk { class D3D11Device; class D3D11ImmediateContext; class D3D10Device final : public ID3D10Device1 { public: D3D10Device( D3D11Device* pDevice, D3D11ImmediateContext* pContext); ~D3D10Device(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData); HRESULT STDMETHODCALLTYPE GetDeviceRemovedReason(); HRESULT STDMETHODCALLTYPE SetExceptionMode( UINT RaiseFlags); UINT STDMETHODCALLTYPE GetExceptionMode(); D3D10_FEATURE_LEVEL1 STDMETHODCALLTYPE GetFeatureLevel(); void STDMETHODCALLTYPE ClearState(); void STDMETHODCALLTYPE Flush(); HRESULT STDMETHODCALLTYPE CreateBuffer( const D3D10_BUFFER_DESC* pDesc, const D3D10_SUBRESOURCE_DATA* pInitialData, ID3D10Buffer** ppBuffer); HRESULT STDMETHODCALLTYPE CreateTexture1D( const D3D10_TEXTURE1D_DESC* pDesc, const D3D10_SUBRESOURCE_DATA* pInitialData, ID3D10Texture1D** ppTexture1D); HRESULT STDMETHODCALLTYPE CreateTexture2D( const D3D10_TEXTURE2D_DESC* pDesc, const D3D10_SUBRESOURCE_DATA* pInitialData, ID3D10Texture2D** ppTexture2D); HRESULT STDMETHODCALLTYPE CreateTexture3D( const D3D10_TEXTURE3D_DESC* pDesc, const D3D10_SUBRESOURCE_DATA* pInitialData, ID3D10Texture3D** ppTexture3D); HRESULT STDMETHODCALLTYPE CreateShaderResourceView( ID3D10Resource* pResource, const D3D10_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D10ShaderResourceView** ppSRView); HRESULT STDMETHODCALLTYPE CreateShaderResourceView1( ID3D10Resource* pResource, const D3D10_SHADER_RESOURCE_VIEW_DESC1* pDesc, ID3D10ShaderResourceView1** ppSRView); HRESULT STDMETHODCALLTYPE CreateRenderTargetView( ID3D10Resource* pResource, const D3D10_RENDER_TARGET_VIEW_DESC* pDesc, ID3D10RenderTargetView** ppRTView); HRESULT STDMETHODCALLTYPE CreateDepthStencilView( ID3D10Resource* pResource, const D3D10_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D10DepthStencilView** ppDepthStencilView); HRESULT STDMETHODCALLTYPE CreateInputLayout( const D3D10_INPUT_ELEMENT_DESC* pInputElementDescs, UINT NumElements, const void* pShaderBytecodeWithInputSignature, SIZE_T BytecodeLength, ID3D10InputLayout** ppInputLayout); HRESULT STDMETHODCALLTYPE CreateVertexShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D10VertexShader** ppVertexShader); HRESULT STDMETHODCALLTYPE CreateGeometryShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D10GeometryShader** ppGeometryShader); HRESULT STDMETHODCALLTYPE CreateGeometryShaderWithStreamOutput( const void* pShaderBytecode, SIZE_T BytecodeLength, const D3D10_SO_DECLARATION_ENTRY* pSODeclaration, UINT NumEntries, UINT OutputStreamStride, ID3D10GeometryShader** ppGeometryShader); HRESULT STDMETHODCALLTYPE CreatePixelShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D10PixelShader** ppPixelShader); HRESULT STDMETHODCALLTYPE CreateBlendState( const D3D10_BLEND_DESC* pBlendStateDesc, ID3D10BlendState** ppBlendState); HRESULT STDMETHODCALLTYPE CreateBlendState1( const D3D10_BLEND_DESC1* pBlendStateDesc, ID3D10BlendState1** ppBlendState); HRESULT STDMETHODCALLTYPE CreateDepthStencilState( const D3D10_DEPTH_STENCIL_DESC* pDepthStencilDesc, ID3D10DepthStencilState** ppDepthStencilState); HRESULT STDMETHODCALLTYPE CreateRasterizerState( const D3D10_RASTERIZER_DESC* pRasterizerDesc, ID3D10RasterizerState** ppRasterizerState); HRESULT STDMETHODCALLTYPE CreateSamplerState( const D3D10_SAMPLER_DESC* pSamplerDesc, ID3D10SamplerState** ppSamplerState); HRESULT STDMETHODCALLTYPE CreateQuery( const D3D10_QUERY_DESC* pQueryDesc, ID3D10Query** ppQuery); HRESULT STDMETHODCALLTYPE CreatePredicate( const D3D10_QUERY_DESC* pPredicateDesc, ID3D10Predicate** ppPredicate); HRESULT STDMETHODCALLTYPE CreateCounter( const D3D10_COUNTER_DESC* pCounterDesc, ID3D10Counter** ppCounter); HRESULT STDMETHODCALLTYPE CheckFormatSupport( DXGI_FORMAT Format, UINT* pFormatSupport); HRESULT STDMETHODCALLTYPE CheckMultisampleQualityLevels( DXGI_FORMAT Format, UINT SampleCount, UINT* pNumQualityLevels); void STDMETHODCALLTYPE CheckCounterInfo( D3D10_COUNTER_INFO* pCounterInfo); HRESULT STDMETHODCALLTYPE CheckCounter( const D3D10_COUNTER_DESC* pDesc, D3D10_COUNTER_TYPE* pType, UINT* pActiveCounters, char* name, UINT* pNameLength, char* units, UINT* pUnitsLength, char* description, UINT* pDescriptionLength); UINT STDMETHODCALLTYPE GetCreationFlags(); HRESULT STDMETHODCALLTYPE OpenSharedResource( HANDLE hResource, REFIID ReturnedInterface, void** ppResource); void STDMETHODCALLTYPE ClearRenderTargetView( ID3D10RenderTargetView* pRenderTargetView, const FLOAT ColorRGBA[4]); void STDMETHODCALLTYPE ClearDepthStencilView( ID3D10DepthStencilView* pDepthStencilView, UINT ClearFlags, FLOAT Depth, UINT8 Stencil); void STDMETHODCALLTYPE SetPredication( ID3D10Predicate* pPredicate, BOOL PredicateValue); void STDMETHODCALLTYPE GetPredication( ID3D10Predicate** ppPredicate, BOOL* pPredicateValue); void STDMETHODCALLTYPE CopySubresourceRegion( ID3D10Resource* pDstResource, UINT DstSubresource, UINT DstX, UINT DstY, UINT DstZ, ID3D10Resource* pSrcResource, UINT SrcSubresource, const D3D10_BOX* pSrcBox); void STDMETHODCALLTYPE CopyResource( ID3D10Resource* pDstResource, ID3D10Resource* pSrcResource); void STDMETHODCALLTYPE UpdateSubresource( ID3D10Resource* pDstResource, UINT DstSubresource, const D3D10_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch); void STDMETHODCALLTYPE GenerateMips( ID3D10ShaderResourceView* pShaderResourceView); void STDMETHODCALLTYPE ResolveSubresource( ID3D10Resource* pDstResource, UINT DstSubresource, ID3D10Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format); void STDMETHODCALLTYPE Draw( UINT VertexCount, UINT StartVertexLocation); void STDMETHODCALLTYPE DrawIndexed( UINT IndexCount, UINT StartIndexLocation, INT BaseVertexLocation); void STDMETHODCALLTYPE DrawInstanced( UINT VertexCountPerInstance, UINT InstanceCount, UINT StartVertexLocation, UINT StartInstanceLocation); void STDMETHODCALLTYPE DrawIndexedInstanced( UINT IndexCountPerInstance, UINT InstanceCount, UINT StartIndexLocation, INT BaseVertexLocation, UINT StartInstanceLocation); void STDMETHODCALLTYPE DrawAuto(); void STDMETHODCALLTYPE IASetInputLayout( ID3D10InputLayout* pInputLayout); void STDMETHODCALLTYPE IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY Topology); void STDMETHODCALLTYPE IASetVertexBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer* const* ppVertexBuffers, const UINT* pStrides, const UINT* pOffsets); void STDMETHODCALLTYPE IASetIndexBuffer( ID3D10Buffer* pIndexBuffer, DXGI_FORMAT Format, UINT Offset); void STDMETHODCALLTYPE IAGetInputLayout( ID3D10InputLayout** ppInputLayout); void STDMETHODCALLTYPE IAGetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY* pTopology); void STDMETHODCALLTYPE IAGetVertexBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer** ppVertexBuffers, UINT* pStrides, UINT* pOffsets); void STDMETHODCALLTYPE IAGetIndexBuffer( ID3D10Buffer** pIndexBuffer, DXGI_FORMAT* Format, UINT* Offset); void STDMETHODCALLTYPE VSSetShader( ID3D10VertexShader* pVertexShader); void STDMETHODCALLTYPE VSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer* const* ppConstantBuffers); void STDMETHODCALLTYPE VSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D10ShaderResourceView* const* ppShaderResourceViews); void STDMETHODCALLTYPE VSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D10SamplerState* const* ppSamplers); void STDMETHODCALLTYPE VSGetShader( ID3D10VertexShader** ppVertexShader); void STDMETHODCALLTYPE VSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer** ppConstantBuffers); void STDMETHODCALLTYPE VSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D10ShaderResourceView** ppShaderResourceViews); void STDMETHODCALLTYPE VSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D10SamplerState** ppSamplers); void STDMETHODCALLTYPE GSSetShader( ID3D10GeometryShader* pShader); void STDMETHODCALLTYPE GSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer* const* ppConstantBuffers); void STDMETHODCALLTYPE GSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D10ShaderResourceView* const* ppShaderResourceViews); void STDMETHODCALLTYPE GSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D10SamplerState* const* ppSamplers); void STDMETHODCALLTYPE GSGetShader( ID3D10GeometryShader** ppGeometryShader); void STDMETHODCALLTYPE GSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer** ppConstantBuffers); void STDMETHODCALLTYPE GSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D10ShaderResourceView** ppShaderResourceViews); void STDMETHODCALLTYPE GSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D10SamplerState** ppSamplers); void STDMETHODCALLTYPE PSSetShader( ID3D10PixelShader* pPixelShader); void STDMETHODCALLTYPE PSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer* const* ppConstantBuffers); void STDMETHODCALLTYPE PSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D10ShaderResourceView* const* ppShaderResourceViews); void STDMETHODCALLTYPE PSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D10SamplerState* const* ppSamplers); void STDMETHODCALLTYPE PSGetShader( ID3D10PixelShader** ppPixelShader); void STDMETHODCALLTYPE PSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer** ppConstantBuffers); void STDMETHODCALLTYPE PSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D10ShaderResourceView** ppShaderResourceViews); void STDMETHODCALLTYPE PSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D10SamplerState** ppSamplers); void STDMETHODCALLTYPE OMSetRenderTargets( UINT NumViews, ID3D10RenderTargetView* const* ppRenderTargetViews, ID3D10DepthStencilView* pDepthStencilView); void STDMETHODCALLTYPE OMSetBlendState( ID3D10BlendState* pBlendState, const FLOAT BlendFactor[4], UINT SampleMask); void STDMETHODCALLTYPE OMSetDepthStencilState( ID3D10DepthStencilState* pDepthStencilState, UINT StencilRef); void STDMETHODCALLTYPE OMGetRenderTargets( UINT NumViews, ID3D10RenderTargetView** ppRenderTargetViews, ID3D10DepthStencilView** ppDepthStencilView); void STDMETHODCALLTYPE OMGetBlendState( ID3D10BlendState** ppBlendState, FLOAT BlendFactor[4], UINT* pSampleMask); void STDMETHODCALLTYPE OMGetDepthStencilState( ID3D10DepthStencilState** ppDepthStencilState, UINT* pStencilRef); void STDMETHODCALLTYPE RSSetState( ID3D10RasterizerState* pRasterizerState); void STDMETHODCALLTYPE RSSetViewports( UINT NumViewports, const D3D10_VIEWPORT* pViewports); void STDMETHODCALLTYPE RSSetScissorRects( UINT NumRects, const D3D10_RECT* pRects); void STDMETHODCALLTYPE RSGetState( ID3D10RasterizerState** ppRasterizerState); void STDMETHODCALLTYPE RSGetViewports( UINT* NumViewports, D3D10_VIEWPORT* pViewports); void STDMETHODCALLTYPE RSGetScissorRects( UINT* NumRects, D3D10_RECT* pRects); void STDMETHODCALLTYPE SOSetTargets( UINT NumBuffers, ID3D10Buffer* const* ppSOTargets, const UINT* pOffsets); void STDMETHODCALLTYPE SOGetTargets( UINT NumBuffers, ID3D10Buffer** ppSOTargets, UINT* pOffsets); void STDMETHODCALLTYPE SetTextFilterSize( UINT Width, UINT Height); void STDMETHODCALLTYPE GetTextFilterSize( UINT* pWidth, UINT* pHeight); private: D3D11Device* m_device; D3D11ImmediateContext* m_context; }; }dxvk-2.6.1/src/d3d10/d3d10_include.h000066400000000000000000000002551477473124000166000ustar00rootroot00000000000000#pragma once #include "../dxgi/dxgi_include.h" #include "../util/sync/sync_spinlock.h" #include "../util/sync/sync_recursive.h" #include #include dxvk-2.6.1/src/d3d10/d3d10_input_layout.cpp000066400000000000000000000026071477473124000202470ustar00rootroot00000000000000#include "d3d10_input_layout.h" #include "../d3d11/d3d11_device.h" #include "../d3d11/d3d11_input_layout.h" namespace dxvk { HRESULT STDMETHODCALLTYPE D3D10InputLayout::QueryInterface( REFIID riid, void** ppvObject) { return m_d3d11->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE D3D10InputLayout::AddRef() { return m_d3d11->AddRef(); } ULONG STDMETHODCALLTYPE D3D10InputLayout::Release() { return m_d3d11->Release(); } void STDMETHODCALLTYPE D3D10InputLayout::GetDevice( ID3D10Device** ppDevice) { GetD3D10Device(m_d3d11, ppDevice); } HRESULT STDMETHODCALLTYPE D3D10InputLayout::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_d3d11->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10InputLayout::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_d3d11->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10InputLayout::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_d3d11->SetPrivateDataInterface(guid, pData); } }dxvk-2.6.1/src/d3d10/d3d10_input_layout.h000066400000000000000000000022541477473124000177120ustar00rootroot00000000000000#pragma once #include "d3d10_util.h" namespace dxvk { class D3D11Device; class D3D11InputLayout; class D3D10InputLayout : public ID3D10InputLayout { public: D3D10InputLayout(D3D11InputLayout* pParent) : m_d3d11(pParent) { } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); void STDMETHODCALLTYPE GetDevice( ID3D10Device** ppDevice); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData); D3D11InputLayout* GetD3D11Iface() { return m_d3d11; } private: D3D11InputLayout* m_d3d11; }; }dxvk-2.6.1/src/d3d10/d3d10_multithread.cpp000066400000000000000000000025151477473124000200330ustar00rootroot00000000000000#include #include "d3d10_device.h" namespace dxvk { D3D10Multithread::D3D10Multithread( IUnknown* pParent, BOOL Protected, BOOL Force) : m_parent (pParent), m_protected (Protected || Force), m_enabled (Protected), m_forced (Force) { } D3D10Multithread::~D3D10Multithread() { } ULONG STDMETHODCALLTYPE D3D10Multithread::AddRef() { return m_parent->AddRef(); } ULONG STDMETHODCALLTYPE D3D10Multithread::Release() { return m_parent->Release(); } HRESULT STDMETHODCALLTYPE D3D10Multithread::QueryInterface( REFIID riid, void** ppvObject) { return m_parent->QueryInterface(riid, ppvObject); } void STDMETHODCALLTYPE D3D10Multithread::Enter() { if (m_protected) m_mutex.lock(); } void STDMETHODCALLTYPE D3D10Multithread::Leave() { if (m_protected) m_mutex.unlock(); } BOOL STDMETHODCALLTYPE D3D10Multithread::SetMultithreadProtected( BOOL bMTProtect) { BOOL result = m_enabled; m_enabled = bMTProtect; if (!m_forced) m_protected = m_enabled; return result; } BOOL STDMETHODCALLTYPE D3D10Multithread::GetMultithreadProtected() { return m_enabled; } } dxvk-2.6.1/src/d3d10/d3d10_multithread.h000066400000000000000000000043241477473124000175000ustar00rootroot00000000000000#pragma once #include "d3d10_include.h" namespace dxvk { /** * \brief Device lock * * Lightweight RAII wrapper that implements * a subset of the functionality provided by * \c std::unique_lock, with the goal of being * cheaper to construct and destroy. */ class D3D10DeviceLock { public: D3D10DeviceLock() : m_mutex(nullptr) { } D3D10DeviceLock(sync::RecursiveSpinlock& mutex) : m_mutex(&mutex) { mutex.lock(); } D3D10DeviceLock(D3D10DeviceLock&& other) : m_mutex(other.m_mutex) { other.m_mutex = nullptr; } D3D10DeviceLock& operator = (D3D10DeviceLock&& other) { if (m_mutex) m_mutex->unlock(); m_mutex = other.m_mutex; other.m_mutex = nullptr; return *this; } ~D3D10DeviceLock() { if (unlikely(m_mutex != nullptr)) m_mutex->unlock(); } private: sync::RecursiveSpinlock* m_mutex; }; /** * \brief D3D10 device and D3D11 context lock * * Can be queried from the D3D10 device or from * any D3D11 context in order to make individual * calls thread-safe. Provides methods to lock * the device or context explicitly. */ class D3D10Multithread : public ID3D10Multithread { public: D3D10Multithread( IUnknown* pParent, BOOL Protected, BOOL Force); ~D3D10Multithread(); ULONG STDMETHODCALLTYPE AddRef() final; ULONG STDMETHODCALLTYPE Release() final; HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) final; void STDMETHODCALLTYPE Enter() final; void STDMETHODCALLTYPE Leave() final; BOOL STDMETHODCALLTYPE SetMultithreadProtected( BOOL bMTProtect) final; BOOL STDMETHODCALLTYPE GetMultithreadProtected() final; D3D10DeviceLock AcquireLock() { return unlikely(m_protected) ? D3D10DeviceLock(m_mutex) : D3D10DeviceLock(); } private: IUnknown* m_parent; BOOL m_protected; BOOL m_enabled; BOOL m_forced; sync::RecursiveSpinlock m_mutex; }; }dxvk-2.6.1/src/d3d10/d3d10_query.cpp000066400000000000000000000045561477473124000166650ustar00rootroot00000000000000#include "d3d10_query.h" #include "d3d10_device.h" #include "../d3d11/d3d11_device.h" #include "../d3d11/d3d11_context.h" #include "../d3d11/d3d11_query.h" namespace dxvk { HRESULT STDMETHODCALLTYPE D3D10Query::QueryInterface( REFIID riid, void** ppvObject) { return m_d3d11->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE D3D10Query::AddRef() { return m_d3d11->AddRef(); } ULONG STDMETHODCALLTYPE D3D10Query::Release() { return m_d3d11->Release(); } void STDMETHODCALLTYPE D3D10Query::GetDevice( ID3D10Device** ppDevice) { GetD3D10Device(m_d3d11, ppDevice); } HRESULT STDMETHODCALLTYPE D3D10Query::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_d3d11->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10Query::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_d3d11->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10Query::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_d3d11->SetPrivateDataInterface(guid, pData); } void STDMETHODCALLTYPE D3D10Query::Begin() { Com ctx; GetD3D11Context(m_d3d11, &ctx); ctx->Begin(m_d3d11); } void STDMETHODCALLTYPE D3D10Query::End() { Com ctx; GetD3D11Context(m_d3d11, &ctx); ctx->End(m_d3d11); } HRESULT STDMETHODCALLTYPE D3D10Query::GetData( void* pData, UINT DataSize, UINT GetDataFlags) { Com ctx; GetD3D11Context(m_d3d11, &ctx); return ctx->GetData(m_d3d11, pData, DataSize, GetDataFlags); } UINT STDMETHODCALLTYPE D3D10Query::GetDataSize() { return m_d3d11->GetDataSize(); } void STDMETHODCALLTYPE D3D10Query::GetDesc( D3D10_QUERY_DESC* pDesc) { D3D11_QUERY_DESC d3d11Desc; m_d3d11->GetDesc(&d3d11Desc); pDesc->Query = D3D10_QUERY(d3d11Desc.Query); pDesc->MiscFlags = d3d11Desc.MiscFlags; } }dxvk-2.6.1/src/d3d10/d3d10_query.h000066400000000000000000000030401477473124000163150ustar00rootroot00000000000000#pragma once #include "d3d10_util.h" namespace dxvk { class D3D10Device; class D3D11Device; class D3D11Query; class D3D10Query : public ID3D10Predicate { public: D3D10Query(D3D11Query* pParent) : m_d3d11(pParent) { } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); void STDMETHODCALLTYPE GetDevice( ID3D10Device** ppDevice); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData); void STDMETHODCALLTYPE Begin(); void STDMETHODCALLTYPE End(); HRESULT STDMETHODCALLTYPE GetData( void* pData, UINT DataSize, UINT GetDataFlags); UINT STDMETHODCALLTYPE GetDataSize(); void STDMETHODCALLTYPE GetDesc( D3D10_QUERY_DESC* pDesc); D3D11Query* GetD3D11Iface() { return m_d3d11; } private: D3D11Query* m_d3d11; }; }dxvk-2.6.1/src/d3d10/d3d10_rasterizer.cpp000066400000000000000000000032441477473124000177030ustar00rootroot00000000000000#include "d3d10_rasterizer.h" #include "../d3d11/d3d11_device.h" #include "../d3d11/d3d11_rasterizer.h" namespace dxvk { HRESULT STDMETHODCALLTYPE D3D10RasterizerState::QueryInterface( REFIID riid, void** ppvObject) { return m_d3d11->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE D3D10RasterizerState::AddRef() { return m_d3d11->AddRef(); } ULONG STDMETHODCALLTYPE D3D10RasterizerState::Release() { return m_d3d11->Release(); } void STDMETHODCALLTYPE D3D10RasterizerState::GetDevice( ID3D10Device** ppDevice) { GetD3D10Device(m_d3d11, ppDevice); } HRESULT STDMETHODCALLTYPE D3D10RasterizerState::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_d3d11->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10RasterizerState::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_d3d11->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10RasterizerState::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_d3d11->SetPrivateDataInterface(guid, pData); } void STDMETHODCALLTYPE D3D10RasterizerState::GetDesc( D3D10_RASTERIZER_DESC* pDesc) { static_assert(sizeof(D3D10_RASTERIZER_DESC) == sizeof(D3D11_RASTERIZER_DESC)); m_d3d11->GetDesc(reinterpret_cast(pDesc)); } }dxvk-2.6.1/src/d3d10/d3d10_rasterizer.h000066400000000000000000000024371477473124000173530ustar00rootroot00000000000000#pragma once #include "d3d10_util.h" namespace dxvk { class D3D11RasterizerState; class D3D11Device; class D3D10RasterizerState : public ID3D10RasterizerState { public: D3D10RasterizerState(D3D11RasterizerState* pParent) : m_d3d11(pParent) { } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); void STDMETHODCALLTYPE GetDevice( ID3D10Device** ppDevice); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData); void STDMETHODCALLTYPE GetDesc( D3D10_RASTERIZER_DESC* pDesc); D3D11RasterizerState* GetD3D11Iface() { return m_d3d11; } private: D3D11RasterizerState* m_d3d11; }; }dxvk-2.6.1/src/d3d10/d3d10_sampler.cpp000066400000000000000000000043321477473124000171530ustar00rootroot00000000000000#include "d3d10_sampler.h" #include "../d3d11/d3d11_device.h" #include "../d3d11/d3d11_sampler.h" namespace dxvk { HRESULT STDMETHODCALLTYPE D3D10SamplerState::QueryInterface( REFIID riid, void** ppvObject) { return m_d3d11->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE D3D10SamplerState::AddRef() { return m_d3d11->AddRef(); } ULONG STDMETHODCALLTYPE D3D10SamplerState::Release() { return m_d3d11->Release(); } void STDMETHODCALLTYPE D3D10SamplerState::GetDevice( ID3D10Device** ppDevice) { GetD3D10Device(m_d3d11, ppDevice); } HRESULT STDMETHODCALLTYPE D3D10SamplerState::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_d3d11->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10SamplerState::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_d3d11->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10SamplerState::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_d3d11->SetPrivateDataInterface(guid, pData); } void STDMETHODCALLTYPE D3D10SamplerState::GetDesc( D3D10_SAMPLER_DESC* pDesc) { D3D11_SAMPLER_DESC d3d11Desc; m_d3d11->GetDesc(&d3d11Desc); pDesc->Filter = D3D10_FILTER(d3d11Desc.Filter); pDesc->AddressU = D3D10_TEXTURE_ADDRESS_MODE(d3d11Desc.AddressU); pDesc->AddressV = D3D10_TEXTURE_ADDRESS_MODE(d3d11Desc.AddressV); pDesc->AddressW = D3D10_TEXTURE_ADDRESS_MODE(d3d11Desc.AddressW); pDesc->MipLODBias = d3d11Desc.MipLODBias; pDesc->MaxAnisotropy = d3d11Desc.MaxAnisotropy; pDesc->ComparisonFunc = D3D10_COMPARISON_FUNC(d3d11Desc.ComparisonFunc); pDesc->MinLOD = d3d11Desc.MinLOD; pDesc->MaxLOD = d3d11Desc.MaxLOD; for (uint32_t i = 0; i < 4; i++) pDesc->BorderColor[i] = d3d11Desc.BorderColor[i]; } }dxvk-2.6.1/src/d3d10/d3d10_sampler.h000066400000000000000000000024121477473124000166150ustar00rootroot00000000000000#pragma once #include "d3d10_util.h" namespace dxvk { class D3D11Device; class D3D11SamplerState; class D3D10SamplerState : public ID3D10SamplerState { public: D3D10SamplerState(D3D11SamplerState* pParent) : m_d3d11(pParent) { } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); void STDMETHODCALLTYPE GetDevice( ID3D10Device** ppDevice); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData); void STDMETHODCALLTYPE GetDesc( D3D10_SAMPLER_DESC* pDesc); D3D11SamplerState* GetD3D11Iface() { return m_d3d11; } private: D3D11SamplerState* m_d3d11; }; }dxvk-2.6.1/src/d3d10/d3d10_shader.h000066400000000000000000000037651477473124000164340ustar00rootroot00000000000000#pragma once #include "d3d10_util.h" namespace dxvk { template class D3D11Shader; template class D3D10Shader : public D3D10Interface { using D3D11ShaderClass = D3D11Shader; public: D3D10Shader(D3D11Shader* pParent) : m_d3d11(pParent) { } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) { return m_d3d11->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE AddRef() { return m_d3d11->AddRef(); } ULONG STDMETHODCALLTYPE Release() { return m_d3d11->Release(); } void STDMETHODCALLTYPE GetDevice( ID3D10Device** ppDevice) { GetD3D10Device(m_d3d11, ppDevice); } HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_d3d11->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_d3d11->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_d3d11->SetPrivateDataInterface(guid, pData); } D3D11ShaderClass* GetD3D11Iface() { return m_d3d11; } private: D3D11ShaderClass* m_d3d11; }; using D3D10VertexShader = D3D10Shader; using D3D10GeometryShader = D3D10Shader; using D3D10PixelShader = D3D10Shader; }dxvk-2.6.1/src/d3d10/d3d10_texture.cpp000066400000000000000000000231761477473124000172170ustar00rootroot00000000000000#include "d3d10_texture.h" #include "d3d10_device.h" #include "../d3d11/d3d11_device.h" #include "../d3d11/d3d11_context.h" #include "../d3d11/d3d11_texture.h" namespace dxvk { HRESULT STDMETHODCALLTYPE D3D10Texture1D::QueryInterface( REFIID riid, void** ppvObject) { return m_d3d11->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE D3D10Texture1D::AddRef() { return m_d3d11->AddRef(); } ULONG STDMETHODCALLTYPE D3D10Texture1D::Release() { return m_d3d11->Release(); } void STDMETHODCALLTYPE D3D10Texture1D::GetDevice( ID3D10Device** ppDevice) { GetD3D10Device(m_d3d11, ppDevice); } HRESULT STDMETHODCALLTYPE D3D10Texture1D::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_d3d11->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10Texture1D::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_d3d11->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10Texture1D::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_d3d11->SetPrivateDataInterface(guid, pData); } void STDMETHODCALLTYPE D3D10Texture1D::GetType( D3D10_RESOURCE_DIMENSION* rType) { *rType = D3D10_RESOURCE_DIMENSION_TEXTURE1D; } void STDMETHODCALLTYPE D3D10Texture1D::SetEvictionPriority( UINT EvictionPriority) { m_d3d11->SetEvictionPriority(EvictionPriority); } UINT STDMETHODCALLTYPE D3D10Texture1D::GetEvictionPriority() { return m_d3d11->GetEvictionPriority(); } HRESULT STDMETHODCALLTYPE D3D10Texture1D::Map( UINT Subresource, D3D10_MAP MapType, UINT MapFlags, void** ppData) { Com ctx; GetD3D11Context(m_d3d11, &ctx); D3D11_MAPPED_SUBRESOURCE sr; HRESULT hr = ctx->Map(m_d3d11, Subresource, D3D11_MAP(MapType), MapFlags, &sr); if (FAILED(hr)) return hr; if (ppData != nullptr) { *ppData = sr.pData; return S_OK; } return S_FALSE; } void STDMETHODCALLTYPE D3D10Texture1D::Unmap( UINT Subresource) { Com ctx; GetD3D11Context(m_d3d11, &ctx); ctx->Unmap(m_d3d11, Subresource); } void STDMETHODCALLTYPE D3D10Texture1D::GetDesc( D3D10_TEXTURE1D_DESC* pDesc) { D3D11_TEXTURE1D_DESC d3d11Desc; m_d3d11->GetDesc(&d3d11Desc); pDesc->Width = d3d11Desc.Width; pDesc->MipLevels = d3d11Desc.MipLevels; pDesc->ArraySize = d3d11Desc.ArraySize; pDesc->Format = d3d11Desc.Format; pDesc->Usage = D3D10_USAGE(d3d11Desc.Usage); pDesc->BindFlags = d3d11Desc.BindFlags; pDesc->CPUAccessFlags = d3d11Desc.CPUAccessFlags; pDesc->MiscFlags = ConvertD3D11ResourceFlags(d3d11Desc.MiscFlags); } HRESULT STDMETHODCALLTYPE D3D10Texture2D::QueryInterface( REFIID riid, void** ppvObject) { return m_d3d11->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE D3D10Texture2D::AddRef() { return m_d3d11->AddRef(); } ULONG STDMETHODCALLTYPE D3D10Texture2D::Release() { return m_d3d11->Release(); } void STDMETHODCALLTYPE D3D10Texture2D::GetDevice( ID3D10Device** ppDevice) { GetD3D10Device(m_d3d11, ppDevice); } HRESULT STDMETHODCALLTYPE D3D10Texture2D::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_d3d11->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10Texture2D::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_d3d11->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10Texture2D::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_d3d11->SetPrivateDataInterface(guid, pData); } void STDMETHODCALLTYPE D3D10Texture2D::GetType( D3D10_RESOURCE_DIMENSION* rType) { *rType = D3D10_RESOURCE_DIMENSION_TEXTURE2D; } void STDMETHODCALLTYPE D3D10Texture2D::SetEvictionPriority( UINT EvictionPriority) { m_d3d11->SetEvictionPriority(EvictionPriority); } UINT STDMETHODCALLTYPE D3D10Texture2D::GetEvictionPriority() { return m_d3d11->GetEvictionPriority(); } HRESULT STDMETHODCALLTYPE D3D10Texture2D::Map( UINT Subresource, D3D10_MAP MapType, UINT MapFlags, D3D10_MAPPED_TEXTURE2D* pMappedTex2D) { Com ctx; GetD3D11Context(m_d3d11, &ctx); D3D11_MAPPED_SUBRESOURCE sr; HRESULT hr = ctx->Map(m_d3d11, Subresource, D3D11_MAP(MapType), MapFlags, &sr); if (FAILED(hr)) return hr; if (pMappedTex2D != nullptr) { pMappedTex2D->pData = sr.pData; pMappedTex2D->RowPitch = sr.RowPitch; return S_OK; } return S_FALSE; } void STDMETHODCALLTYPE D3D10Texture2D::Unmap( UINT Subresource) { Com ctx; GetD3D11Context(m_d3d11, &ctx); ctx->Unmap(m_d3d11, Subresource); } void STDMETHODCALLTYPE D3D10Texture2D::GetDesc( D3D10_TEXTURE2D_DESC* pDesc) { D3D11_TEXTURE2D_DESC d3d11Desc; m_d3d11->GetDesc(&d3d11Desc); pDesc->Width = d3d11Desc.Width; pDesc->Height = d3d11Desc.Height; pDesc->MipLevels = d3d11Desc.MipLevels; pDesc->ArraySize = d3d11Desc.ArraySize; pDesc->Format = d3d11Desc.Format; pDesc->SampleDesc = d3d11Desc.SampleDesc; pDesc->Usage = D3D10_USAGE(d3d11Desc.Usage); pDesc->BindFlags = d3d11Desc.BindFlags; pDesc->CPUAccessFlags = d3d11Desc.CPUAccessFlags; pDesc->MiscFlags = ConvertD3D11ResourceFlags(d3d11Desc.MiscFlags); } HRESULT STDMETHODCALLTYPE D3D10Texture3D::QueryInterface( REFIID riid, void** ppvObject) { return m_d3d11->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE D3D10Texture3D::AddRef() { return m_d3d11->AddRef(); } ULONG STDMETHODCALLTYPE D3D10Texture3D::Release() { return m_d3d11->Release(); } void STDMETHODCALLTYPE D3D10Texture3D::GetDevice( ID3D10Device** ppDevice) { GetD3D10Device(m_d3d11, ppDevice); } HRESULT STDMETHODCALLTYPE D3D10Texture3D::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_d3d11->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10Texture3D::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_d3d11->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10Texture3D::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_d3d11->SetPrivateDataInterface(guid, pData); } void STDMETHODCALLTYPE D3D10Texture3D::GetType( D3D10_RESOURCE_DIMENSION* rType) { *rType = D3D10_RESOURCE_DIMENSION_TEXTURE3D; } void STDMETHODCALLTYPE D3D10Texture3D::SetEvictionPriority( UINT EvictionPriority) { m_d3d11->SetEvictionPriority(EvictionPriority); } UINT STDMETHODCALLTYPE D3D10Texture3D::GetEvictionPriority() { return m_d3d11->GetEvictionPriority(); } HRESULT STDMETHODCALLTYPE D3D10Texture3D::Map( UINT Subresource, D3D10_MAP MapType, UINT MapFlags, D3D10_MAPPED_TEXTURE3D* pMappedTex3D) { Com ctx; GetD3D11Context(m_d3d11, &ctx); D3D11_MAPPED_SUBRESOURCE sr; HRESULT hr = ctx->Map(m_d3d11, Subresource, D3D11_MAP(MapType), MapFlags, &sr); if (FAILED(hr)) return hr; if (pMappedTex3D != nullptr) { pMappedTex3D->pData = sr.pData; pMappedTex3D->RowPitch = sr.RowPitch; pMappedTex3D->DepthPitch = sr.DepthPitch; return S_OK; } return S_FALSE; } void STDMETHODCALLTYPE D3D10Texture3D::Unmap( UINT Subresource) { Com ctx; GetD3D11Context(m_d3d11, &ctx); ctx->Unmap(m_d3d11, Subresource); } void STDMETHODCALLTYPE D3D10Texture3D::GetDesc( D3D10_TEXTURE3D_DESC* pDesc) { D3D11_TEXTURE3D_DESC d3d11Desc; m_d3d11->GetDesc(&d3d11Desc); pDesc->Width = d3d11Desc.Width; pDesc->Height = d3d11Desc.Height; pDesc->Depth = d3d11Desc.Depth; pDesc->MipLevels = d3d11Desc.MipLevels; pDesc->Format = d3d11Desc.Format; pDesc->Usage = D3D10_USAGE(d3d11Desc.Usage); pDesc->BindFlags = d3d11Desc.BindFlags; pDesc->CPUAccessFlags = d3d11Desc.CPUAccessFlags; pDesc->MiscFlags = ConvertD3D11ResourceFlags(d3d11Desc.MiscFlags); } }dxvk-2.6.1/src/d3d10/d3d10_texture.h000066400000000000000000000127331477473124000166610ustar00rootroot00000000000000#pragma once #include "d3d10_util.h" namespace dxvk { class D3D10Device; class D3D11Device; class D3D11Texture1D; class D3D11Texture2D; class D3D11Texture3D; /////////////////////////////////////////// // D 3 D 1 0 T E X T U R E 1 D class D3D10Texture1D : public ID3D10Texture1D { public: D3D10Texture1D(D3D11Texture1D* pParent) : m_d3d11(pParent) { } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); void STDMETHODCALLTYPE GetDevice( ID3D10Device** ppDevice); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData); void STDMETHODCALLTYPE GetType( D3D10_RESOURCE_DIMENSION* rType); void STDMETHODCALLTYPE SetEvictionPriority( UINT EvictionPriority); UINT STDMETHODCALLTYPE GetEvictionPriority(); HRESULT STDMETHODCALLTYPE Map( UINT Subresource, D3D10_MAP MapType, UINT MapFlags, void** ppData); void STDMETHODCALLTYPE Unmap( UINT Subresource); void STDMETHODCALLTYPE GetDesc( D3D10_TEXTURE1D_DESC* pDesc); D3D11Texture1D* GetD3D11Iface() { return m_d3d11; } private: D3D11Texture1D* m_d3d11; }; /////////////////////////////////////////// // D 3 D 1 0 T E X T U R E 2 D class D3D10Texture2D : public ID3D10Texture2D { public: D3D10Texture2D(D3D11Texture2D* pParent) : m_d3d11(pParent) { } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); void STDMETHODCALLTYPE GetDevice( ID3D10Device** ppDevice); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData); void STDMETHODCALLTYPE GetType( D3D10_RESOURCE_DIMENSION* rType); void STDMETHODCALLTYPE SetEvictionPriority( UINT EvictionPriority); UINT STDMETHODCALLTYPE GetEvictionPriority(); HRESULT STDMETHODCALLTYPE Map( UINT Subresource, D3D10_MAP MapType, UINT MapFlags, D3D10_MAPPED_TEXTURE2D* pMappedTex2D); void STDMETHODCALLTYPE Unmap( UINT Subresource); void STDMETHODCALLTYPE GetDesc( D3D10_TEXTURE2D_DESC* pDesc); D3D11Texture2D* GetD3D11Iface() { return m_d3d11; } private: D3D11Texture2D* m_d3d11; }; /////////////////////////////////////////// // D 3 D 1 0 T E X T U R E 3 D class D3D10Texture3D : public ID3D10Texture3D { public: D3D10Texture3D(D3D11Texture3D* pParent) : m_d3d11(pParent) { } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); void STDMETHODCALLTYPE GetDevice( ID3D10Device** ppDevice); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData); void STDMETHODCALLTYPE GetType( D3D10_RESOURCE_DIMENSION* rType); void STDMETHODCALLTYPE SetEvictionPriority( UINT EvictionPriority); UINT STDMETHODCALLTYPE GetEvictionPriority(); HRESULT STDMETHODCALLTYPE Map( UINT Subresource, D3D10_MAP MapType, UINT MapFlags, D3D10_MAPPED_TEXTURE3D* pMappedTex3D); void STDMETHODCALLTYPE Unmap( UINT Subresource); void STDMETHODCALLTYPE GetDesc( D3D10_TEXTURE3D_DESC* pDesc); D3D11Texture3D* GetD3D11Iface() { return m_d3d11; } private: D3D11Texture3D* m_d3d11; }; }dxvk-2.6.1/src/d3d10/d3d10_util.cpp000066400000000000000000000057151477473124000164730ustar00rootroot00000000000000#include "d3d10_util.h" #include "d3d10_device.h" #include "../d3d11/d3d11_device.h" namespace dxvk { UINT ConvertD3D10ResourceFlags(UINT MiscFlags) { UINT result = 0; if (MiscFlags & D3D10_RESOURCE_MISC_GENERATE_MIPS) result |= D3D11_RESOURCE_MISC_GENERATE_MIPS; if (MiscFlags & D3D10_RESOURCE_MISC_SHARED) result |= D3D11_RESOURCE_MISC_SHARED; if (MiscFlags & D3D10_RESOURCE_MISC_TEXTURECUBE) result |= D3D11_RESOURCE_MISC_TEXTURECUBE; if (MiscFlags & D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX) result |= D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; if (MiscFlags & D3D10_RESOURCE_MISC_GDI_COMPATIBLE) result |= D3D11_RESOURCE_MISC_GDI_COMPATIBLE; return result; } UINT ConvertD3D11ResourceFlags(UINT MiscFlags) { UINT result = 0; if (MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS) result |= D3D10_RESOURCE_MISC_GENERATE_MIPS; if (MiscFlags & D3D11_RESOURCE_MISC_SHARED) result |= D3D10_RESOURCE_MISC_SHARED; if (MiscFlags & D3D11_RESOURCE_MISC_TEXTURECUBE) result |= D3D10_RESOURCE_MISC_TEXTURECUBE; if (MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) result |= D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX; if (MiscFlags & D3D11_RESOURCE_MISC_GDI_COMPATIBLE) result |= D3D10_RESOURCE_MISC_GDI_COMPATIBLE; return result; } void GetD3D10ResourceFromView( ID3D11View* pSrcView, ID3D10Resource** ppDstResource) { Com d3d11Resource; pSrcView->GetResource(&d3d11Resource); GetD3D10Resource(d3d11Resource.ptr(), ppDstResource); } void GetD3D11ResourceFromView( ID3D10View* pSrcView, ID3D11Resource** ppDstResource) { Com d3d10Resource; pSrcView->GetResource(&d3d10Resource); GetD3D11Resource(d3d10Resource.ptr(), ppDstResource); } void GetD3D10Resource( ID3D11Resource* pSrcResource, ID3D10Resource** ppDstResource) { pSrcResource->QueryInterface( __uuidof(ID3D10Resource), reinterpret_cast(ppDstResource)); } void GetD3D11Resource( ID3D10Resource* pSrcResource, ID3D11Resource** ppDstResource) { pSrcResource->QueryInterface( __uuidof(ID3D11Resource), reinterpret_cast(ppDstResource)); } void GetD3D10Device( ID3D11DeviceChild* pObject, ID3D10Device** ppDevice) { ID3D11Device* d3d11Device = nullptr; pObject->GetDevice(&d3d11Device); *ppDevice = static_cast(d3d11Device)->GetD3D10Interface(); } void GetD3D11Device( ID3D11DeviceChild* pObject, ID3D11Device** ppDevice) { pObject->GetDevice(ppDevice); } void GetD3D11Context( ID3D11DeviceChild* pObject, ID3D11DeviceContext** ppContext) { Com d3d11Device; pObject->GetDevice(&d3d11Device); d3d11Device->GetImmediateContext(ppContext); } }dxvk-2.6.1/src/d3d10/d3d10_util.h000066400000000000000000000045651477473124000161420ustar00rootroot00000000000000#pragma once #include "d3d10_include.h" namespace dxvk { /** * \brief Converts misc. resource flags * * Converts the D3D11 misc. resource flags to * their D3D10 equivalents and vice versa. * \param [in] MiscFlags Original bit mask * \returns Converted bit mask */ UINT ConvertD3D10ResourceFlags(UINT MiscFlags); UINT ConvertD3D11ResourceFlags(UINT MiscFlags); /** * \brief Retrieves D3D10 resource from D3D11 view * * \param [in] pSrcView The D3D11 resource view * \param [out] ppDstResource The D3D10 resource */ void GetD3D10ResourceFromView( ID3D11View* pSrcView, ID3D10Resource** ppDstResource); /** * \brief Retrieves D3D11 resource from D3D10 view * * \param [in] pSrcView The D3D10 resource view * \param [out] ppDstResource The D3D11 resource */ void GetD3D11ResourceFromView( ID3D10View* pSrcView, ID3D11Resource** ppDstResource); /** * \brief Retrieves D3D10 resource from D3D11 resource * * \param [in] pSrcResource The D3D11 resource * \param [out] ppDstResource The D3D10 resource */ void GetD3D10Resource( ID3D11Resource* pSrcResource, ID3D10Resource** ppDstResource); /** * \brief Retrieves D3D11 resource from D3D10 resource * * \param [in] pSrcResource The D3D10 resource * \param [out] ppDstResource The D3D11 resource */ void GetD3D11Resource( ID3D10Resource* pSrcResource, ID3D11Resource** ppDstResource); /** * \brief Retrieves D3D10 device from D3D11 object * * \param [in] pObject The D3D11 device child * \param [out] ppDevice The D3D10 device pointer */ void GetD3D10Device( ID3D11DeviceChild* pObject, ID3D10Device** ppDevice); /** * \brief Retrieves D3D11 device from D3D11 object * * \param [in] pObject The D3D11 device child * \param [out] ppDevice The D3D11 device pointer */ void GetD3D11Device( ID3D11DeviceChild* pObject, ID3D11Device** ppDevice); /** * \brief Retrieves D3D11 context from D3D11 object * * \param [in] pObject The D3D11 device child * \param [out] ppContext The D3D11 immediate context */ void GetD3D11Context( ID3D11DeviceChild* pObject, ID3D11DeviceContext** ppContext); }dxvk-2.6.1/src/d3d10/d3d10_view_dsv.cpp000066400000000000000000000063311477473124000173370ustar00rootroot00000000000000#include "d3d10_view_dsv.h" #include "../d3d11/d3d11_device.h" #include "../d3d11/d3d11_view_dsv.h" namespace dxvk { HRESULT STDMETHODCALLTYPE D3D10DepthStencilView::QueryInterface( REFIID riid, void** ppvObject) { return m_d3d11->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE D3D10DepthStencilView::AddRef() { return m_d3d11->AddRef(); } ULONG STDMETHODCALLTYPE D3D10DepthStencilView::Release() { return m_d3d11->Release(); } void STDMETHODCALLTYPE D3D10DepthStencilView::GetDevice( ID3D10Device** ppDevice) { GetD3D10Device(m_d3d11, ppDevice); } HRESULT STDMETHODCALLTYPE D3D10DepthStencilView::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_d3d11->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10DepthStencilView::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_d3d11->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10DepthStencilView::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_d3d11->SetPrivateDataInterface(guid, pData); } void STDMETHODCALLTYPE D3D10DepthStencilView::GetResource( ID3D10Resource** ppResource) { GetD3D10ResourceFromView(m_d3d11, ppResource); } void STDMETHODCALLTYPE D3D10DepthStencilView::GetDesc( D3D10_DEPTH_STENCIL_VIEW_DESC* pDesc) { D3D11_DEPTH_STENCIL_VIEW_DESC d3d11Desc; m_d3d11->GetDesc(&d3d11Desc); pDesc->ViewDimension = D3D10_DSV_DIMENSION(d3d11Desc.ViewDimension); pDesc->Format = d3d11Desc.Format; switch (d3d11Desc.ViewDimension) { case D3D11_DSV_DIMENSION_UNKNOWN: break; case D3D11_DSV_DIMENSION_TEXTURE1D: pDesc->Texture1D.MipSlice = d3d11Desc.Texture1D.MipSlice; break; case D3D11_DSV_DIMENSION_TEXTURE1DARRAY: pDesc->Texture1DArray.MipSlice = d3d11Desc.Texture1DArray.MipSlice; pDesc->Texture1DArray.FirstArraySlice = d3d11Desc.Texture1DArray.FirstArraySlice; pDesc->Texture1DArray.ArraySize = d3d11Desc.Texture1DArray.ArraySize; break; case D3D11_DSV_DIMENSION_TEXTURE2D: pDesc->Texture2D.MipSlice = d3d11Desc.Texture2D.MipSlice; break; case D3D11_DSV_DIMENSION_TEXTURE2DARRAY: pDesc->Texture2DArray.MipSlice = d3d11Desc.Texture2DArray.MipSlice; pDesc->Texture2DArray.FirstArraySlice = d3d11Desc.Texture2DArray.FirstArraySlice; pDesc->Texture2DArray.ArraySize = d3d11Desc.Texture2DArray.ArraySize; break; case D3D11_DSV_DIMENSION_TEXTURE2DMS: break; case D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY: pDesc->Texture2DMSArray.FirstArraySlice = d3d11Desc.Texture2DMSArray.FirstArraySlice; pDesc->Texture2DMSArray.ArraySize = d3d11Desc.Texture2DMSArray.ArraySize; break; } } }dxvk-2.6.1/src/d3d10/d3d10_view_dsv.h000066400000000000000000000026071477473124000170060ustar00rootroot00000000000000#pragma once #include "d3d10_util.h" namespace dxvk { class D3D11Device; class D3D11DepthStencilView; class D3D10DepthStencilView : public ID3D10DepthStencilView { public: D3D10DepthStencilView(D3D11DepthStencilView* pParent) : m_d3d11(pParent) { } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); void STDMETHODCALLTYPE GetDevice( ID3D10Device** ppDevice); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData); void STDMETHODCALLTYPE GetResource( ID3D10Resource** ppResource); void STDMETHODCALLTYPE GetDesc( D3D10_DEPTH_STENCIL_VIEW_DESC* pDesc); D3D11DepthStencilView* GetD3D11Iface() { return m_d3d11; } private: D3D11DepthStencilView* m_d3d11; }; }dxvk-2.6.1/src/d3d10/d3d10_view_rtv.cpp000066400000000000000000000036061477473124000173600ustar00rootroot00000000000000#include "d3d10_view_rtv.h" #include "../d3d11/d3d11_device.h" #include "../d3d11/d3d11_view_rtv.h" namespace dxvk { HRESULT STDMETHODCALLTYPE D3D10RenderTargetView::QueryInterface( REFIID riid, void** ppvObject) { return m_d3d11->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE D3D10RenderTargetView::AddRef() { return m_d3d11->AddRef(); } ULONG STDMETHODCALLTYPE D3D10RenderTargetView::Release() { return m_d3d11->Release(); } void STDMETHODCALLTYPE D3D10RenderTargetView::GetDevice( ID3D10Device** ppDevice) { GetD3D10Device(m_d3d11, ppDevice); } HRESULT STDMETHODCALLTYPE D3D10RenderTargetView::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_d3d11->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10RenderTargetView::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_d3d11->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10RenderTargetView::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_d3d11->SetPrivateDataInterface(guid, pData); } void STDMETHODCALLTYPE D3D10RenderTargetView::GetResource( ID3D10Resource** ppResource) { GetD3D10ResourceFromView(m_d3d11, ppResource); } void STDMETHODCALLTYPE D3D10RenderTargetView::GetDesc( D3D10_RENDER_TARGET_VIEW_DESC* pDesc) { static_assert(sizeof(D3D10_RENDER_TARGET_VIEW_DESC) == sizeof(D3D11_RENDER_TARGET_VIEW_DESC)); m_d3d11->GetDesc(reinterpret_cast(pDesc)); } }dxvk-2.6.1/src/d3d10/d3d10_view_rtv.h000066400000000000000000000026071477473124000170250ustar00rootroot00000000000000#pragma once #include "d3d10_util.h" namespace dxvk { class D3D11Device; class D3D11RenderTargetView; class D3D10RenderTargetView : public ID3D10RenderTargetView { public: D3D10RenderTargetView(D3D11RenderTargetView* pParent) : m_d3d11(pParent) { } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); void STDMETHODCALLTYPE GetDevice( ID3D10Device** ppDevice); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData); void STDMETHODCALLTYPE GetResource( ID3D10Resource** ppResource); void STDMETHODCALLTYPE GetDesc( D3D10_RENDER_TARGET_VIEW_DESC* pDesc); D3D11RenderTargetView* GetD3D11Iface() { return m_d3d11; } private: D3D11RenderTargetView* m_d3d11; }; }dxvk-2.6.1/src/d3d10/d3d10_view_srv.cpp000066400000000000000000000047461477473124000173650ustar00rootroot00000000000000#include "d3d10_view_srv.h" #include "../d3d11/d3d11_device.h" #include "../d3d11/d3d11_view_srv.h" namespace dxvk { HRESULT STDMETHODCALLTYPE D3D10ShaderResourceView::QueryInterface( REFIID riid, void** ppvObject) { return m_d3d11->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE D3D10ShaderResourceView::AddRef() { return m_d3d11->AddRef(); } ULONG STDMETHODCALLTYPE D3D10ShaderResourceView::Release() { return m_d3d11->Release(); } void STDMETHODCALLTYPE D3D10ShaderResourceView::GetDevice( ID3D10Device** ppDevice) { GetD3D10Device(m_d3d11, ppDevice); } HRESULT STDMETHODCALLTYPE D3D10ShaderResourceView::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_d3d11->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10ShaderResourceView::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_d3d11->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D10ShaderResourceView::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_d3d11->SetPrivateDataInterface(guid, pData); } void STDMETHODCALLTYPE D3D10ShaderResourceView::GetResource( ID3D10Resource** ppResource) { GetD3D10ResourceFromView(m_d3d11, ppResource); } void STDMETHODCALLTYPE D3D10ShaderResourceView::GetDesc( D3D10_SHADER_RESOURCE_VIEW_DESC* pDesc) { static_assert(sizeof(D3D10_SHADER_RESOURCE_VIEW_DESC) == sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC)); m_d3d11->GetDesc(reinterpret_cast(pDesc)); if (pDesc->ViewDimension > D3D10_SRV_DIMENSION_TEXTURECUBE) pDesc->ViewDimension = D3D10_SRV_DIMENSION_UNKNOWN; } void STDMETHODCALLTYPE D3D10ShaderResourceView::GetDesc1( D3D10_SHADER_RESOURCE_VIEW_DESC1* pDesc) { static_assert(sizeof(D3D10_SHADER_RESOURCE_VIEW_DESC1) == sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC)); m_d3d11->GetDesc(reinterpret_cast(pDesc)); if (pDesc->ViewDimension > D3D10_1_SRV_DIMENSION_TEXTURECUBEARRAY) pDesc->ViewDimension = D3D10_1_SRV_DIMENSION_UNKNOWN; } }dxvk-2.6.1/src/d3d10/d3d10_view_srv.h000066400000000000000000000027641477473124000170300ustar00rootroot00000000000000#pragma once #include "d3d10_util.h" namespace dxvk { class D3D11Device; class D3D11ShaderResourceView; class D3D10ShaderResourceView : public ID3D10ShaderResourceView1 { public: D3D10ShaderResourceView(D3D11ShaderResourceView* pParent) : m_d3d11(pParent) { } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); void STDMETHODCALLTYPE GetDevice( ID3D10Device** ppDevice); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pData); void STDMETHODCALLTYPE GetResource( ID3D10Resource** ppResource); void STDMETHODCALLTYPE GetDesc( D3D10_SHADER_RESOURCE_VIEW_DESC* pDesc); void STDMETHODCALLTYPE GetDesc1( D3D10_SHADER_RESOURCE_VIEW_DESC1* pDesc); D3D11ShaderResourceView* GetD3D11Iface() { return m_d3d11; } private: D3D11ShaderResourceView* m_d3d11; }; }dxvk-2.6.1/src/d3d10/d3d10core.def000066400000000000000000000001561477473124000162550ustar00rootroot00000000000000LIBRARY D3D10CORE.DLL EXPORTS D3D10CoreCreateDevice D3D10CoreGetVersion D3D10CoreRegisterLayers dxvk-2.6.1/src/d3d10/d3d10core.sym000066400000000000000000000001611477473124000163230ustar00rootroot00000000000000{ global: D3D10CoreCreateDevice; D3D10CoreGetVersion; D3D10CoreRegisterLayers; local: *; }; dxvk-2.6.1/src/d3d10/meson.build000066400000000000000000000020241477473124000162470ustar00rootroot00000000000000d3d10_core_res = wrc_generator.process('version10_core.rc') d3d10_core_src = [ 'd3d10_core.cpp', ] d3d10_core_ld_args = [] d3d10_core_link_depends = [] if platform == 'windows' d3d10_d3d11_dep = lib_d3d11 else d3d10_core_ld_args += [ '-Wl,--version-script', join_paths(meson.current_source_dir(), 'd3d10core.sym') ] d3d10_core_link_depends += files('d3d10core.sym') d3d10_d3d11_dep = d3d11_dep endif d3d10_core_dll = shared_library(dxvk_name_prefix+'d3d10core', d3d10_core_src, d3d10_core_res, dependencies : [ d3d10_d3d11_dep ], include_directories : dxvk_include_path, install : true, vs_module_defs : 'd3d10core'+def_spec_ext, link_args : d3d10_core_ld_args, link_depends : [ d3d10_core_link_depends ], kwargs : dxvk_so_version, ) d3d10_core_dep = declare_dependency( link_with : [ d3d10_core_dll ], ) if platform != 'windows' pkg.generate(d3d10_core_dll, filebase: dxvk_pkg_prefix + 'd3d10core', subdirs: 'dxvk', ) endif dxvk-2.6.1/src/d3d10/version10_core.rc000066400000000000000000000015021477473124000172710ustar00rootroot00000000000000#include // DLL version information. VS_VERSION_INFO VERSIONINFO FILEVERSION 10,0,17763,1 PRODUCTVERSION 10,0,17763,1 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS 0 FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "080904b0" BEGIN VALUE "CompanyName", "DXVK" VALUE "FileDescription", "Direct3D 10 Runtime" VALUE "FileVersion", "10.0.17763.1 (WinBuild.160101.0800)" VALUE "InternalName", "D3D10Core.dll" VALUE "LegalCopyright", "zlib/libpng license" VALUE "OriginalFilename", "D3D10Core.dll" VALUE "ProductName", "DXVK" VALUE "ProductVersion", "10.0.17763.1" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0809, 1200 END END dxvk-2.6.1/src/d3d11/000077500000000000000000000000001477473124000141105ustar00rootroot00000000000000dxvk-2.6.1/src/d3d11/d3d11.def000066400000000000000000000002301477473124000153770ustar00rootroot00000000000000LIBRARY D3D11.DLL EXPORTS D3D11CoreCreateDevice @18 D3D11CreateDevice @22 D3D11CreateDeviceAndSwapChain @23 D3D11On12CreateDevice @24 dxvk-2.6.1/src/d3d11/d3d11.sym000066400000000000000000000002201477473124000154500ustar00rootroot00000000000000{ global: D3D11CoreCreateDevice; D3D11CreateDevice; D3D11CreateDeviceAndSwapChain; D3D11On12CreateDevice; local: *; }; dxvk-2.6.1/src/d3d11/d3d11_annotation.cpp000066400000000000000000000076521477473124000176740ustar00rootroot00000000000000#include "d3d11_annotation.h" #include "d3d11_context_def.h" #include "d3d11_context_imm.h" #include "d3d11_device.h" #include "../util/util_misc.h" #include "../util/util_win32_compat.h" namespace dxvk { template static void RegisterUserDefinedAnnotation(IDXVKUserDefinedAnnotation* annotation) { using RegistrationFunctionType = void(__stdcall *)(IDXVKUserDefinedAnnotation*); static const int16_t RegisterOrdinal = 28257; static const int16_t UnregisterOrdinal = 28258; HMODULE d3d9Module = ::LoadLibraryA("d3d9.dll"); if (!d3d9Module) { Logger::info("Unable to find d3d9, some annotations may be missed."); return; } const int16_t ordinal = Register ? RegisterOrdinal : UnregisterOrdinal; auto registrationFunction = reinterpret_cast(::GetProcAddress(d3d9Module, reinterpret_cast(static_cast(ordinal)))); if (!registrationFunction) { Logger::info("Unable to find DXVK_RegisterAnnotation, some annotations may be missed."); return; } registrationFunction(annotation); } template D3D11UserDefinedAnnotation::D3D11UserDefinedAnnotation( ContextType* container, const Rc& dxvkDevice) : m_container(container), m_annotationsEnabled(dxvkDevice->debugFlags().test(DxvkDebugFlag::Markers)) { if (!IsDeferred && m_annotationsEnabled) RegisterUserDefinedAnnotation(this); } template D3D11UserDefinedAnnotation::~D3D11UserDefinedAnnotation() { if (!IsDeferred && m_annotationsEnabled) RegisterUserDefinedAnnotation(this); } template ULONG STDMETHODCALLTYPE D3D11UserDefinedAnnotation::AddRef() { return m_container->AddRef(); } template ULONG STDMETHODCALLTYPE D3D11UserDefinedAnnotation::Release() { return m_container->Release(); } template HRESULT STDMETHODCALLTYPE D3D11UserDefinedAnnotation::QueryInterface( REFIID riid, void** ppvObject) { return m_container->QueryInterface(riid, ppvObject); } template INT STDMETHODCALLTYPE D3D11UserDefinedAnnotation::BeginEvent( D3DCOLOR Color, LPCWSTR Name) { if (!m_annotationsEnabled || !Name) return -1; D3D10DeviceLock lock = m_container->LockContext(); m_container->EmitCs([ cColor = Color, cLabel = dxvk::str::fromws(Name) ] (DxvkContext* ctx) { ctx->beginDebugLabel(vk::makeLabel(cColor, cLabel.c_str())); }); return m_eventDepth++; } template INT STDMETHODCALLTYPE D3D11UserDefinedAnnotation::EndEvent() { if (!m_annotationsEnabled) return -1; D3D10DeviceLock lock = m_container->LockContext(); if (!m_eventDepth) return 0; m_container->EmitCs([] (DxvkContext* ctx) { ctx->endDebugLabel(); }); return --m_eventDepth; } template void STDMETHODCALLTYPE D3D11UserDefinedAnnotation::SetMarker( D3DCOLOR Color, LPCWSTR Name) { if (!m_annotationsEnabled || !Name) return; D3D10DeviceLock lock = m_container->LockContext(); m_container->EmitCs([ cColor = Color, cLabel = dxvk::str::fromws(Name) ] (DxvkContext* ctx) { ctx->insertDebugLabel(vk::makeLabel(cColor, cLabel.c_str())); }); } template BOOL STDMETHODCALLTYPE D3D11UserDefinedAnnotation::GetStatus() { return m_annotationsEnabled; } template class D3D11UserDefinedAnnotation; template class D3D11UserDefinedAnnotation; } dxvk-2.6.1/src/d3d11/d3d11_annotation.h000066400000000000000000000027271477473124000173370ustar00rootroot00000000000000#pragma once #include #include "d3d11_include.h" #include "../dxvk/dxvk_annotation.h" #include "../dxvk/dxvk_device.h" namespace dxvk { class D3D11DeferredContext; class D3D11ImmediateContext; template class D3D11UserDefinedAnnotation final : public IDXVKUserDefinedAnnotation { constexpr static bool IsDeferred = std::is_same_v; public: D3D11UserDefinedAnnotation( ContextType* container, const Rc& dxvkDevice); ~D3D11UserDefinedAnnotation(); D3D11UserDefinedAnnotation (const D3D11UserDefinedAnnotation&) = delete; D3D11UserDefinedAnnotation& operator = (const D3D11UserDefinedAnnotation&) = delete; ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); INT STDMETHODCALLTYPE BeginEvent( D3DCOLOR Color, LPCWSTR Name); INT STDMETHODCALLTYPE EndEvent(); void STDMETHODCALLTYPE SetMarker( D3DCOLOR Color, LPCWSTR Name); BOOL STDMETHODCALLTYPE GetStatus(); private: ContextType* m_container = nullptr; int32_t m_eventDepth = 0u; bool m_annotationsEnabled = false; }; } dxvk-2.6.1/src/d3d11/d3d11_blend.cpp000066400000000000000000000273651477473124000166110ustar00rootroot00000000000000#include "d3d11_blend.h" #include "d3d11_device.h" namespace dxvk { D3D11BlendState::D3D11BlendState( D3D11Device* device, const D3D11_BLEND_DESC1& desc) : D3D11StateObject(device), m_desc(desc), m_d3d10(this) { // If Independent Blend is disabled, we must ignore the // blend modes for render target 1 to 7. In Vulkan, all // blend modes need to be identical in that case. for (uint32_t i = 0; i < m_blendModes.size(); i++) { m_blendModes[i] = DecodeBlendMode( desc.IndependentBlendEnable ? desc.RenderTarget[i] : desc.RenderTarget[0]); } // Multisample state is part of the blend state in D3D11 m_msState.setSampleMask(0u); // Set during bind m_msState.setAlphaToCoverage(desc.AlphaToCoverageEnable); // Vulkan only supports a global logic op for the blend // state, which might be problematic in some cases. if (desc.IndependentBlendEnable && desc.RenderTarget[0].LogicOpEnable) Logger::warn("D3D11: Per-target logic ops not supported"); m_loState.setLogicOp(desc.RenderTarget[0].LogicOpEnable, DecodeLogicOp(desc.RenderTarget[0].LogicOp)); } D3D11BlendState::~D3D11BlendState() { } HRESULT STDMETHODCALLTYPE D3D11BlendState::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11BlendState) || riid == __uuidof(ID3D11BlendState1)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(ID3D10BlendState) || riid == __uuidof(ID3D10BlendState1)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11BlendState), riid)) { Logger::warn("D3D11BlendState::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11BlendState::GetDesc(D3D11_BLEND_DESC* pDesc) { pDesc->AlphaToCoverageEnable = m_desc.AlphaToCoverageEnable; pDesc->IndependentBlendEnable = m_desc.IndependentBlendEnable; for (uint32_t i = 0; i < 8; i++) { pDesc->RenderTarget[i].BlendEnable = m_desc.RenderTarget[i].BlendEnable; pDesc->RenderTarget[i].SrcBlend = m_desc.RenderTarget[i].SrcBlend; pDesc->RenderTarget[i].DestBlend = m_desc.RenderTarget[i].DestBlend; pDesc->RenderTarget[i].BlendOp = m_desc.RenderTarget[i].BlendOp; pDesc->RenderTarget[i].SrcBlendAlpha = m_desc.RenderTarget[i].SrcBlendAlpha; pDesc->RenderTarget[i].DestBlendAlpha = m_desc.RenderTarget[i].DestBlendAlpha; pDesc->RenderTarget[i].BlendOpAlpha = m_desc.RenderTarget[i].BlendOpAlpha; pDesc->RenderTarget[i].RenderTargetWriteMask = m_desc.RenderTarget[i].RenderTargetWriteMask; } } void STDMETHODCALLTYPE D3D11BlendState::GetDesc1(D3D11_BLEND_DESC1* pDesc) { *pDesc = m_desc; } D3D11_BLEND_DESC1 D3D11BlendState::PromoteDesc(const D3D11_BLEND_DESC* pSrcDesc) { D3D11_BLEND_DESC1 dstDesc; dstDesc.AlphaToCoverageEnable = pSrcDesc->AlphaToCoverageEnable; dstDesc.IndependentBlendEnable = pSrcDesc->IndependentBlendEnable; for (uint32_t i = 0; i < 8; i++) { dstDesc.RenderTarget[i].BlendEnable = pSrcDesc->RenderTarget[i].BlendEnable; dstDesc.RenderTarget[i].LogicOpEnable = FALSE; dstDesc.RenderTarget[i].SrcBlend = pSrcDesc->RenderTarget[i].SrcBlend; dstDesc.RenderTarget[i].DestBlend = pSrcDesc->RenderTarget[i].DestBlend; dstDesc.RenderTarget[i].BlendOp = pSrcDesc->RenderTarget[i].BlendOp; dstDesc.RenderTarget[i].SrcBlendAlpha = pSrcDesc->RenderTarget[i].SrcBlendAlpha; dstDesc.RenderTarget[i].DestBlendAlpha = pSrcDesc->RenderTarget[i].DestBlendAlpha; dstDesc.RenderTarget[i].BlendOpAlpha = pSrcDesc->RenderTarget[i].BlendOpAlpha; dstDesc.RenderTarget[i].LogicOp = D3D11_LOGIC_OP_NOOP; dstDesc.RenderTarget[i].RenderTargetWriteMask = pSrcDesc->RenderTarget[i].RenderTargetWriteMask; } return dstDesc; } HRESULT D3D11BlendState::NormalizeDesc(D3D11_BLEND_DESC1* pDesc) { if (pDesc->AlphaToCoverageEnable) pDesc->AlphaToCoverageEnable = TRUE; if (pDesc->IndependentBlendEnable) pDesc->IndependentBlendEnable = TRUE; const uint32_t numRenderTargets = pDesc->IndependentBlendEnable ? 8 : 1; for (uint32_t i = 0; i < numRenderTargets; i++) { D3D11_RENDER_TARGET_BLEND_DESC1* rt = &pDesc->RenderTarget[i]; if (rt->BlendEnable) { rt->BlendEnable = TRUE; if (rt->LogicOpEnable) return E_INVALIDARG; if (!ValidateBlendOperations( rt->SrcBlend, rt->SrcBlendAlpha, rt->DestBlend, rt->DestBlendAlpha, rt->BlendOp, rt->BlendOpAlpha)) return E_INVALIDARG; } else { rt->SrcBlend = D3D11_BLEND_ONE; rt->DestBlend = D3D11_BLEND_ZERO; rt->BlendOp = D3D11_BLEND_OP_ADD; rt->SrcBlendAlpha = D3D11_BLEND_ONE; rt->DestBlendAlpha = D3D11_BLEND_ZERO; rt->BlendOpAlpha = D3D11_BLEND_OP_ADD; } if (rt->LogicOpEnable) { rt->LogicOpEnable = TRUE; // Blending must be disabled // if the logic op is enabled if (rt->BlendEnable || pDesc->IndependentBlendEnable || !ValidateLogicOp(rt->LogicOp)) return E_INVALIDARG; } else { rt->LogicOp = D3D11_LOGIC_OP_NOOP; } if (rt->RenderTargetWriteMask > D3D11_COLOR_WRITE_ENABLE_ALL) return E_INVALIDARG; } for (uint32_t i = numRenderTargets; i < 8; i++) { // Render targets blend operations are the same // across all render targets when blend is enabled // on rendertarget[0] with independent blend disabled pDesc->RenderTarget[i] = pDesc->RenderTarget[0]; } return S_OK; } DxvkBlendMode D3D11BlendState::DecodeBlendMode( const D3D11_RENDER_TARGET_BLEND_DESC1& BlendDesc) { DxvkBlendMode mode = { }; mode.setBlendEnable(BlendDesc.BlendEnable); mode.setColorOp(DecodeBlendFactor(BlendDesc.SrcBlend, false), DecodeBlendFactor(BlendDesc.DestBlend, false), DecodeBlendOp(BlendDesc.BlendOp)); mode.setAlphaOp(DecodeBlendFactor(BlendDesc.SrcBlendAlpha, true), DecodeBlendFactor(BlendDesc.DestBlendAlpha, true), DecodeBlendOp(BlendDesc.BlendOpAlpha)); mode.setWriteMask(BlendDesc.RenderTargetWriteMask); mode.normalize(); return mode; } VkBlendFactor D3D11BlendState::DecodeBlendFactor(D3D11_BLEND BlendFactor, bool IsAlpha) { switch (BlendFactor) { case D3D11_BLEND_ZERO: return VK_BLEND_FACTOR_ZERO; case D3D11_BLEND_ONE: return VK_BLEND_FACTOR_ONE; case D3D11_BLEND_SRC_COLOR: return VK_BLEND_FACTOR_SRC_COLOR; case D3D11_BLEND_INV_SRC_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; case D3D11_BLEND_SRC_ALPHA: return VK_BLEND_FACTOR_SRC_ALPHA; case D3D11_BLEND_INV_SRC_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; case D3D11_BLEND_DEST_ALPHA: return VK_BLEND_FACTOR_DST_ALPHA; case D3D11_BLEND_INV_DEST_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; case D3D11_BLEND_DEST_COLOR: return VK_BLEND_FACTOR_DST_COLOR; case D3D11_BLEND_INV_DEST_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; case D3D11_BLEND_SRC_ALPHA_SAT: return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; case D3D11_BLEND_BLEND_FACTOR: return IsAlpha ? VK_BLEND_FACTOR_CONSTANT_ALPHA : VK_BLEND_FACTOR_CONSTANT_COLOR; case D3D11_BLEND_INV_BLEND_FACTOR: return IsAlpha ? VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA : VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; case D3D11_BLEND_SRC1_COLOR: return VK_BLEND_FACTOR_SRC1_COLOR; case D3D11_BLEND_INV_SRC1_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR; case D3D11_BLEND_SRC1_ALPHA: return VK_BLEND_FACTOR_SRC1_ALPHA; case D3D11_BLEND_INV_SRC1_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA; default: return VK_BLEND_FACTOR_ZERO; } } VkBlendOp D3D11BlendState::DecodeBlendOp(D3D11_BLEND_OP BlendOp) { switch (BlendOp) { case D3D11_BLEND_OP_ADD: return VK_BLEND_OP_ADD; case D3D11_BLEND_OP_SUBTRACT: return VK_BLEND_OP_SUBTRACT; case D3D11_BLEND_OP_REV_SUBTRACT: return VK_BLEND_OP_REVERSE_SUBTRACT; case D3D11_BLEND_OP_MIN: return VK_BLEND_OP_MIN; case D3D11_BLEND_OP_MAX: return VK_BLEND_OP_MAX; default: return VK_BLEND_OP_ADD; } } VkLogicOp D3D11BlendState::DecodeLogicOp(D3D11_LOGIC_OP LogicOp) { switch (LogicOp) { case D3D11_LOGIC_OP_CLEAR: return VK_LOGIC_OP_CLEAR; case D3D11_LOGIC_OP_SET: return VK_LOGIC_OP_SET; case D3D11_LOGIC_OP_COPY: return VK_LOGIC_OP_COPY; case D3D11_LOGIC_OP_COPY_INVERTED: return VK_LOGIC_OP_COPY_INVERTED; case D3D11_LOGIC_OP_NOOP: return VK_LOGIC_OP_NO_OP; case D3D11_LOGIC_OP_INVERT: return VK_LOGIC_OP_INVERT; case D3D11_LOGIC_OP_AND: return VK_LOGIC_OP_AND; case D3D11_LOGIC_OP_NAND: return VK_LOGIC_OP_NAND; case D3D11_LOGIC_OP_OR: return VK_LOGIC_OP_OR; case D3D11_LOGIC_OP_NOR: return VK_LOGIC_OP_NOR; case D3D11_LOGIC_OP_XOR: return VK_LOGIC_OP_XOR; case D3D11_LOGIC_OP_EQUIV: return VK_LOGIC_OP_EQUIVALENT; case D3D11_LOGIC_OP_AND_REVERSE: return VK_LOGIC_OP_AND_REVERSE; case D3D11_LOGIC_OP_AND_INVERTED: return VK_LOGIC_OP_AND_INVERTED; case D3D11_LOGIC_OP_OR_REVERSE: return VK_LOGIC_OP_OR_REVERSE; case D3D11_LOGIC_OP_OR_INVERTED: return VK_LOGIC_OP_OR_INVERTED; default: return VK_LOGIC_OP_NO_OP; } } bool D3D11BlendState::ValidateBlendFactor(D3D11_BLEND Blend) { return Blend >= D3D11_BLEND_ZERO && Blend <= D3D11_BLEND_INV_SRC1_ALPHA; } bool D3D11BlendState::ValidateBlendFactorAlpha(D3D11_BLEND BlendAlpha) { return BlendAlpha >= D3D11_BLEND_ZERO && BlendAlpha <= D3D11_BLEND_INV_SRC1_ALPHA && BlendAlpha != D3D11_BLEND_SRC_COLOR && BlendAlpha != D3D11_BLEND_INV_SRC_COLOR && BlendAlpha != D3D11_BLEND_DEST_COLOR && BlendAlpha != D3D11_BLEND_INV_DEST_COLOR && BlendAlpha != D3D11_BLEND_SRC1_COLOR && BlendAlpha != D3D11_BLEND_INV_SRC1_COLOR; } bool D3D11BlendState::ValidateBlendOp(D3D11_BLEND_OP BlendOp) { return BlendOp >= D3D11_BLEND_OP_ADD && BlendOp <= D3D11_BLEND_OP_MAX; } bool D3D11BlendState::ValidateLogicOp(D3D11_LOGIC_OP LogicOp) { return LogicOp >= D3D11_LOGIC_OP_CLEAR && LogicOp <= D3D11_LOGIC_OP_OR_INVERTED; } bool D3D11BlendState::ValidateBlendOperations( D3D11_BLEND SrcBlend, D3D11_BLEND SrcBlendAlpha, D3D11_BLEND DestBlend, D3D11_BLEND DestBlendAlpha, D3D11_BLEND_OP BlendOp, D3D11_BLEND_OP BlendOpAlpha) { return ValidateBlendOp(BlendOp) && ValidateBlendOp(BlendOpAlpha) && ValidateBlendFactor(SrcBlend) && ValidateBlendFactor(DestBlend) && ValidateBlendFactorAlpha(SrcBlendAlpha) && ValidateBlendFactorAlpha(DestBlendAlpha); } } dxvk-2.6.1/src/d3d11/d3d11_blend.h000066400000000000000000000047561477473124000162550ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_device.h" #include "../d3d10/d3d10_blend.h" #include "d3d11_device_child.h" #include "d3d11_util.h" namespace dxvk { class D3D11Device; class D3D11BlendState : public D3D11StateObject { public: using DescType = D3D11_BLEND_DESC1; D3D11BlendState( D3D11Device* device, const D3D11_BLEND_DESC1& desc); ~D3D11BlendState(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) final; void STDMETHODCALLTYPE GetDesc( D3D11_BLEND_DESC* pDesc) final; void STDMETHODCALLTYPE GetDesc1( D3D11_BLEND_DESC1* pDesc) final; DxvkMultisampleState GetMsState(uint32_t SampleMask) const { DxvkMultisampleState msState = m_msState; msState.setSampleMask(SampleMask); return msState; } DxvkLogicOpState GetLoState() const { return m_loState; } std::array GetBlendState() const { return m_blendModes; } D3D10BlendState* GetD3D10Iface() { return &m_d3d10; } static D3D11_BLEND_DESC1 PromoteDesc( const D3D11_BLEND_DESC* pSrcDesc); static HRESULT NormalizeDesc( D3D11_BLEND_DESC1* pDesc); private: D3D11_BLEND_DESC1 m_desc; std::array m_blendModes = { }; DxvkMultisampleState m_msState = { }; DxvkLogicOpState m_loState = { }; D3D10BlendState m_d3d10; static DxvkBlendMode DecodeBlendMode( const D3D11_RENDER_TARGET_BLEND_DESC1& BlendDesc); static VkBlendFactor DecodeBlendFactor( D3D11_BLEND BlendFactor, bool IsAlpha); static VkBlendOp DecodeBlendOp( D3D11_BLEND_OP BlendOp); static VkLogicOp DecodeLogicOp( D3D11_LOGIC_OP LogicOp); static bool ValidateBlendFactor( D3D11_BLEND Blend); static bool ValidateBlendFactorAlpha( D3D11_BLEND BlendAlpha); static bool ValidateBlendOp( D3D11_BLEND_OP BlendOp); static bool ValidateLogicOp( D3D11_LOGIC_OP LogicOp); static bool ValidateBlendOperations( D3D11_BLEND SrcBlend, D3D11_BLEND SrcBlendAlpha, D3D11_BLEND SestBlend, D3D11_BLEND DestBlendAlpha, D3D11_BLEND_OP BlendOp, D3D11_BLEND_OP BlendOpAlpha); }; } dxvk-2.6.1/src/d3d11/d3d11_buffer.cpp000066400000000000000000000341701477473124000167660ustar00rootroot00000000000000#include "d3d11_buffer.h" #include "d3d11_context.h" #include "d3d11_context_imm.h" #include "d3d11_device.h" namespace dxvk { D3D11Buffer::D3D11Buffer( D3D11Device* pDevice, const D3D11_BUFFER_DESC* pDesc, const D3D11_ON_12_RESOURCE_INFO* p11on12Info) : D3D11DeviceChild(pDevice), m_desc (*pDesc), m_resource (this, pDevice), m_d3d10 (this) { DxvkBufferCreateInfo info; info.flags = 0; info.size = pDesc->ByteWidth; info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT; info.access = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; if (pDesc->BindFlags & D3D11_BIND_VERTEX_BUFFER) { info.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; info.stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; info.access |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; } if (pDesc->BindFlags & D3D11_BIND_INDEX_BUFFER) { info.usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; info.stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; info.access |= VK_ACCESS_INDEX_READ_BIT; } if (pDesc->BindFlags & D3D11_BIND_CONSTANT_BUFFER) { info.usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; info.stages |= m_parent->GetEnabledShaderStages(); info.access |= VK_ACCESS_UNIFORM_READ_BIT; } if (pDesc->BindFlags & D3D11_BIND_SHADER_RESOURCE) { info.usage |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; info.stages |= m_parent->GetEnabledShaderStages(); info.access |= VK_ACCESS_SHADER_READ_BIT; } if (pDesc->BindFlags & D3D11_BIND_STREAM_OUTPUT) { info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; info.stages |= VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT; info.access |= VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT; } if (pDesc->BindFlags & D3D11_BIND_UNORDERED_ACCESS) { info.usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; info.stages |= m_parent->GetEnabledShaderStages(); info.access |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } if (pDesc->MiscFlags & D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS) { info.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; info.stages |= VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; info.access |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT; } if (pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILED) { info.flags |= VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT; } // Set host read bit as necessary. We may internally read staging // buffer contents even if the buffer is not marked for reading. if (pDesc->CPUAccessFlags && pDesc->Usage != D3D11_USAGE_DYNAMIC) { info.stages |= VK_PIPELINE_STAGE_HOST_BIT; info.access |= VK_ACCESS_HOST_READ_BIT; if (pDesc->CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) info.access |= VK_ACCESS_HOST_WRITE_BIT; } // Always enable BDA usage if available so that CUDA interop can work if (m_parent->GetDXVKDevice()->features().vk12.bufferDeviceAddress) info.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; if (p11on12Info) { m_11on12 = *p11on12Info; DxvkBufferImportInfo importInfo; importInfo.buffer = VkBuffer(m_11on12.VulkanHandle); importInfo.offset = m_11on12.VulkanOffset; if (m_desc.CPUAccessFlags) m_11on12.Resource->Map(0, nullptr, &importInfo.mapPtr); m_buffer = m_parent->GetDXVKDevice()->importBuffer(info, importInfo, GetMemoryFlags()); m_cookie = m_buffer->cookie(); m_mapPtr = m_buffer->mapPtr(0); m_mapMode = DetermineMapMode(m_buffer->memFlags()); } else if (!(pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL)) { VkMemoryPropertyFlags memoryFlags = GetMemoryFlags(); m_mapMode = DetermineMapMode(memoryFlags); // Create the buffer and set the entire buffer slice as mapped, // so that we only have to update it when invalidating the buffer m_buffer = m_parent->GetDXVKDevice()->createBuffer(info, memoryFlags); m_cookie = m_buffer->cookie(); m_mapPtr = m_buffer->mapPtr(0); } else { m_sparseAllocator = m_parent->GetDXVKDevice()->createSparsePageAllocator(); m_sparseAllocator->setCapacity(info.size / SparseMemoryPageSize); m_cookie = 0u; m_mapPtr = nullptr; m_mapMode = D3D11_COMMON_BUFFER_MAP_MODE_NONE; } // For Stream Output buffers we need a counter if (pDesc->BindFlags & D3D11_BIND_STREAM_OUTPUT) m_soCounter = CreateSoCounterBuffer(); } D3D11Buffer::~D3D11Buffer() { if (m_desc.CPUAccessFlags && m_11on12.Resource != nullptr) m_11on12.Resource->Unmap(0, nullptr); } HRESULT STDMETHODCALLTYPE D3D11Buffer::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11Resource) || riid == __uuidof(ID3D11Buffer)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(ID3D10Resource) || riid == __uuidof(ID3D10Buffer)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (riid == __uuidof(IDXGIObject) || riid == __uuidof(IDXGIDeviceSubObject) || riid == __uuidof(IDXGIResource) || riid == __uuidof(IDXGIResource1)) { *ppvObject = ref(&m_resource); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11Buffer), riid)) { Logger::warn("D3D11Buffer::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } UINT STDMETHODCALLTYPE D3D11Buffer::GetEvictionPriority() { return DXGI_RESOURCE_PRIORITY_NORMAL; } void STDMETHODCALLTYPE D3D11Buffer::SetEvictionPriority(UINT EvictionPriority) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D11Buffer::SetEvictionPriority: Stub"); } void STDMETHODCALLTYPE D3D11Buffer::GetType(D3D11_RESOURCE_DIMENSION* pResourceDimension) { *pResourceDimension = D3D11_RESOURCE_DIMENSION_BUFFER; } void STDMETHODCALLTYPE D3D11Buffer::GetDesc(D3D11_BUFFER_DESC* pDesc) { *pDesc = m_desc; } bool D3D11Buffer::CheckViewCompatibility( UINT BindFlags, DXGI_FORMAT Format) const { // Check whether the given bind flags are supported if ((m_desc.BindFlags & BindFlags) != BindFlags) return false; // Structured buffer views use no format if (Format == DXGI_FORMAT_UNKNOWN) return (m_desc.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) != 0; // Check whether the given combination of buffer view // type and view format is supported by the device DXGI_VK_FORMAT_INFO viewFormat = m_parent->LookupFormat(Format, DXGI_VK_FORMAT_MODE_ANY); VkFormatFeatureFlags2 features = GetBufferFormatFeatures(BindFlags); return CheckFormatFeatureSupport(viewFormat.Format, features); } void D3D11Buffer::SetDebugName(const char* pName) { if (m_buffer) { m_parent->GetContext()->InjectCs(DxvkCsQueue::HighPriority, [ cBuffer = m_buffer, cName = std::string(pName ? pName : "") ] (DxvkContext* ctx) { ctx->setDebugName(cBuffer, cName.c_str()); }); } } HRESULT D3D11Buffer::NormalizeBufferProperties(D3D11_BUFFER_DESC* pDesc) { // Zero-sized buffers are illegal if (!pDesc->ByteWidth && !(pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL)) return E_INVALIDARG; // Constant buffer size must be a multiple of 16 if ((pDesc->BindFlags & D3D11_BIND_CONSTANT_BUFFER) && (pDesc->ByteWidth & 0xF)) return E_INVALIDARG; // Basic validation for structured buffers if ((pDesc->MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) && ((pDesc->MiscFlags & D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS) || (pDesc->StructureByteStride == 0) || (pDesc->StructureByteStride & 0x3))) return E_INVALIDARG; // Basic validation for raw buffers if ((pDesc->MiscFlags & D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS) && (!(pDesc->BindFlags & (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS)))) return E_INVALIDARG; // Mip generation obviously doesn't work for buffers if (pDesc->MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS) return E_INVALIDARG; // Basic validation for tiled buffers if (pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILED) { if ((pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL) || (pDesc->Usage != D3D11_USAGE_DEFAULT) || (pDesc->CPUAccessFlags)) return E_INVALIDARG; } // Basic validation for tile pools if (pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL) { if ((pDesc->MiscFlags & ~D3D11_RESOURCE_MISC_TILE_POOL) || (pDesc->ByteWidth % SparseMemoryPageSize) || (pDesc->Usage != D3D11_USAGE_DEFAULT) || (pDesc->BindFlags) || (pDesc->CPUAccessFlags)) return E_INVALIDARG; } if (!(pDesc->MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED)) pDesc->StructureByteStride = 0; return S_OK; } HRESULT D3D11Buffer::GetDescFromD3D12( ID3D12Resource* pResource, const D3D11_RESOURCE_FLAGS* pResourceFlags, D3D11_BUFFER_DESC* pBufferDesc) { D3D12_RESOURCE_DESC desc12 = pResource->GetDesc(); pBufferDesc->ByteWidth = desc12.Width; pBufferDesc->Usage = D3D11_USAGE_DEFAULT; pBufferDesc->BindFlags = D3D11_BIND_SHADER_RESOURCE; pBufferDesc->MiscFlags = 0; pBufferDesc->CPUAccessFlags = 0; pBufferDesc->StructureByteStride = 0; if (desc12.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) pBufferDesc->BindFlags |= D3D11_BIND_RENDER_TARGET; if (desc12.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) pBufferDesc->BindFlags |= D3D11_BIND_UNORDERED_ACCESS; if (pResourceFlags) { pBufferDesc->BindFlags = pResourceFlags->BindFlags; pBufferDesc->MiscFlags |= pResourceFlags->MiscFlags; pBufferDesc->CPUAccessFlags = pResourceFlags->CPUAccessFlags; pBufferDesc->StructureByteStride = pResourceFlags->StructureByteStride; } return S_OK; } BOOL D3D11Buffer::CheckFormatFeatureSupport( VkFormat Format, VkFormatFeatureFlags2 Features) const { DxvkFormatFeatures support = m_parent->GetDXVKDevice()->getFormatFeatures(Format); return (support.buffer & Features) == Features; } VkMemoryPropertyFlags D3D11Buffer::GetMemoryFlags() const { VkMemoryPropertyFlags memoryFlags = 0; if (m_desc.MiscFlags & (D3D11_RESOURCE_MISC_TILE_POOL | D3D11_RESOURCE_MISC_TILED)) return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; switch (m_desc.Usage) { case D3D11_USAGE_IMMUTABLE: memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; break; case D3D11_USAGE_DEFAULT: memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; if ((m_desc.BindFlags & D3D11_BIND_CONSTANT_BUFFER) || m_desc.CPUAccessFlags) { memoryFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; } if (m_desc.CPUAccessFlags & D3D11_CPU_ACCESS_READ) { memoryFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT; memoryFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; } break; case D3D11_USAGE_DYNAMIC: memoryFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; if (m_desc.BindFlags) memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; break; case D3D11_USAGE_STAGING: memoryFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; break; } bool useCached = (m_parent->GetOptions()->cachedDynamicResources == ~0u) || (m_parent->GetOptions()->cachedDynamicResources & m_desc.BindFlags); if ((memoryFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && useCached) { memoryFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; memoryFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; } return memoryFlags; } Rc D3D11Buffer::CreateSoCounterBuffer() { Rc device = m_parent->GetDXVKDevice(); DxvkBufferCreateInfo info; info.size = sizeof(D3D11SOCounter); info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT; info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT; info.access = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT; info.debugName = "SO counter"; return device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); } D3D11_COMMON_BUFFER_MAP_MODE D3D11Buffer::DetermineMapMode(VkMemoryPropertyFlags MemFlags) { return (MemFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ? D3D11_COMMON_BUFFER_MAP_MODE_DIRECT : D3D11_COMMON_BUFFER_MAP_MODE_NONE; } D3D11Buffer* GetCommonBuffer(ID3D11Resource* pResource) { D3D11_RESOURCE_DIMENSION dimension = D3D11_RESOURCE_DIMENSION_UNKNOWN; pResource->GetType(&dimension); return dimension == D3D11_RESOURCE_DIMENSION_BUFFER ? static_cast(pResource) : nullptr; } } dxvk-2.6.1/src/d3d11/d3d11_buffer.h000066400000000000000000000135371477473124000164370ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_cs.h" #include "../dxvk/dxvk_device.h" #include "../d3d10/d3d10_buffer.h" #include "d3d11_device_child.h" #include "d3d11_interfaces.h" #include "d3d11_on_12.h" #include "d3d11_resource.h" namespace dxvk { class D3D11Device; /** * \brief Buffer map mode */ enum D3D11_COMMON_BUFFER_MAP_MODE { D3D11_COMMON_BUFFER_MAP_MODE_NONE, D3D11_COMMON_BUFFER_MAP_MODE_DIRECT, }; /** * \brief Stream output buffer offset * * A byte offset into the buffer that * stores the byte offset where new * data will be written to. */ struct D3D11SOCounter { uint32_t byteOffset; }; class D3D11Buffer : public D3D11DeviceChild { static constexpr VkDeviceSize BufferSliceAlignment = 64; public: D3D11Buffer( D3D11Device* pDevice, const D3D11_BUFFER_DESC* pDesc, const D3D11_ON_12_RESOURCE_INFO* p11on12Info); ~D3D11Buffer(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) final; void STDMETHODCALLTYPE GetType( D3D11_RESOURCE_DIMENSION *pResourceDimension) final; UINT STDMETHODCALLTYPE GetEvictionPriority() final; void STDMETHODCALLTYPE SetEvictionPriority(UINT EvictionPriority) final; void STDMETHODCALLTYPE GetDesc( D3D11_BUFFER_DESC *pDesc) final; void STDMETHODCALLTYPE SetDebugName(const char* pName) final; bool CheckViewCompatibility( UINT BindFlags, DXGI_FORMAT Format) const; const D3D11_BUFFER_DESC* Desc() const { return &m_desc; } BOOL IsTilePool() const { return bool(m_desc.MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL); } D3D11_COMMON_BUFFER_MAP_MODE GetMapMode() const { return m_mapMode; } uint64_t GetCookie() const { return m_cookie; } Rc GetBuffer() const { return m_buffer; } Rc GetSparseAllocator() const { return m_sparseAllocator; } DxvkBufferSlice GetBufferSlice() const { return DxvkBufferSlice(m_buffer, 0, m_desc.ByteWidth); } DxvkBufferSlice GetBufferSlice(VkDeviceSize offset) const { VkDeviceSize size = m_desc.ByteWidth; offset = std::min(offset, size); return DxvkBufferSlice(m_buffer, offset, size - offset); } DxvkBufferSlice GetBufferSlice(VkDeviceSize offset, VkDeviceSize length) const { VkDeviceSize size = m_desc.ByteWidth; offset = std::min(offset, size); return DxvkBufferSlice(m_buffer, offset, std::min(length, size - offset)); } VkDeviceSize GetRemainingSize(VkDeviceSize offset) const { VkDeviceSize size = m_desc.ByteWidth; offset = std::min(offset, size); return size - offset; } DxvkBufferSlice GetSOCounter() { return m_soCounter != nullptr ? DxvkBufferSlice(m_soCounter) : DxvkBufferSlice(); } Rc AllocSlice(DxvkLocalAllocationCache* cache) { return m_buffer->allocateStorage(cache); } Rc DiscardSlice(DxvkLocalAllocationCache* cache) { auto allocation = m_buffer->allocateStorage(cache); m_mapPtr = allocation->mapPtr(); return allocation; } void* GetMapPtr() const { return m_mapPtr; } D3D10Buffer* GetD3D10Iface() { return &m_d3d10; } bool HasSequenceNumber() const { return m_mapMode != D3D11_COMMON_BUFFER_MAP_MODE_NONE && !(m_desc.MiscFlags & D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS) && !(m_desc.BindFlags); } void TrackSequenceNumber(uint64_t Seq) { m_seq = Seq; } uint64_t GetSequenceNumber() { return HasSequenceNumber() ? m_seq : DxvkCsThread::SynchronizeAll; } /** * \brief Retrieves D3D11on12 resource info * \returns 11on12 resource info */ D3D11_ON_12_RESOURCE_INFO Get11on12Info() const { return m_11on12; } /** * \brief Normalizes buffer description * * \param [in] pDesc Buffer description * \returns \c S_OK if the parameters are valid */ static HRESULT NormalizeBufferProperties( D3D11_BUFFER_DESC* pDesc); /** * \brief Initializes D3D11 buffer description from D3D12 * * \param [in] pResource D3D12 resource * \param [in] pResourceFlags D3D11 flag overrides * \param [out] pBufferDesc D3D11 buffer description * \returns \c S_OK if the parameters are valid */ static HRESULT GetDescFromD3D12( ID3D12Resource* pResource, const D3D11_RESOURCE_FLAGS* pResourceFlags, D3D11_BUFFER_DESC* pBufferDesc); private: D3D11_BUFFER_DESC m_desc; D3D11_ON_12_RESOURCE_INFO m_11on12; D3D11_COMMON_BUFFER_MAP_MODE m_mapMode; Rc m_buffer; uint64_t m_cookie = 0u; Rc m_soCounter; Rc m_sparseAllocator; uint64_t m_seq = 0ull; void* m_mapPtr = nullptr; D3D11DXGIResource m_resource; D3D10Buffer m_d3d10; BOOL CheckFormatFeatureSupport( VkFormat Format, VkFormatFeatureFlags2 Features) const; VkMemoryPropertyFlags GetMemoryFlags() const; Rc CreateSoCounterBuffer(); static D3D11_COMMON_BUFFER_MAP_MODE DetermineMapMode( VkMemoryPropertyFlags MemFlags); }; /** * \brief Retrieves buffer from resource pointer * * \param [in] pResource The resource to query * \returns Pointer to buffer, or \c nullptr */ D3D11Buffer* GetCommonBuffer( ID3D11Resource* pResource); } dxvk-2.6.1/src/d3d11/d3d11_class_linkage.cpp000066400000000000000000000032571477473124000203160ustar00rootroot00000000000000#include "d3d11_class_linkage.h" #include "d3d11_device.h" namespace dxvk { D3D11ClassLinkage::D3D11ClassLinkage( D3D11Device* pDevice) : D3D11DeviceChild(pDevice) { } D3D11ClassLinkage::~D3D11ClassLinkage() { } HRESULT STDMETHODCALLTYPE D3D11ClassLinkage::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11ClassLinkage)) { *ppvObject = ref(this); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11ClassLinkage), riid)) { Logger::warn("D3D11ClassLinkage::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE D3D11ClassLinkage::CreateClassInstance( LPCSTR pClassTypeName, UINT ConstantBufferOffset, UINT ConstantVectorOffset, UINT TextureOffset, UINT SamplerOffset, ID3D11ClassInstance **ppInstance) { InitReturnPtr(ppInstance); Logger::err("D3D11ClassLinkage::CreateClassInstance: Not implemented yet"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11ClassLinkage::GetClassInstance( LPCSTR pClassInstanceName, UINT InstanceIndex, ID3D11ClassInstance **ppInstance) { Logger::err("D3D11ClassLinkage::GetClassInstance: Not implemented yet"); return E_NOTIMPL; } } dxvk-2.6.1/src/d3d11/d3d11_class_linkage.h000066400000000000000000000017521477473124000177610ustar00rootroot00000000000000#pragma once #include "d3d11_device_child.h" namespace dxvk { class D3D11Device; // TODO implement properly class D3D11ClassLinkage : public D3D11DeviceChild { public: D3D11ClassLinkage( D3D11Device* pDevice); ~D3D11ClassLinkage(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) final; HRESULT STDMETHODCALLTYPE CreateClassInstance( LPCSTR pClassTypeName, UINT ConstantBufferOffset, UINT ConstantVectorOffset, UINT TextureOffset, UINT SamplerOffset, ID3D11ClassInstance **ppInstance); HRESULT STDMETHODCALLTYPE GetClassInstance( LPCSTR pClassInstanceName, UINT InstanceIndex, ID3D11ClassInstance **ppInstance); }; } dxvk-2.6.1/src/d3d11/d3d11_cmd.h000066400000000000000000000011731477473124000157220ustar00rootroot00000000000000#pragma once #include "d3d11_include.h" namespace dxvk { /** * \brief D3D11 command type * * Used to identify the type of command * data most recently added to a CS chunk. */ enum class D3D11CmdType : uint32_t { None, DrawIndirect, DrawIndirectIndexed, Draw, DrawIndexed, }; /** * \brief Indirect draw command data * * Stores the offset into the draw buffer for * the first draw, as well as the number of * draws to execute. */ struct D3D11CmdDrawIndirectData { uint32_t offset; uint32_t count; uint32_t stride; }; } dxvk-2.6.1/src/d3d11/d3d11_cmdlist.cpp000066400000000000000000000105301477473124000171460ustar00rootroot00000000000000#include "d3d11_cmdlist.h" #include "d3d11_device.h" #include "d3d11_buffer.h" #include "d3d11_texture.h" namespace dxvk { D3D11CommandList::D3D11CommandList( D3D11Device* pDevice, UINT ContextFlags) : D3D11DeviceChild(pDevice), m_contextFlags(ContextFlags) { } D3D11CommandList::~D3D11CommandList() { } HRESULT STDMETHODCALLTYPE D3D11CommandList::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11CommandList)) { *ppvObject = ref(this); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11CommandList), riid)) { Logger::warn("D3D11CommandList::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } UINT STDMETHODCALLTYPE D3D11CommandList::GetContextFlags() { return m_contextFlags; } void D3D11CommandList::AddQuery(D3D11Query* pQuery) { m_queries.emplace_back(pQuery); } uint64_t D3D11CommandList::AddChunk(DxvkCsChunkRef&& Chunk) { m_chunks.push_back(std::move(Chunk)); return m_chunks.size() - 1; } uint64_t D3D11CommandList::AddCommandList( D3D11CommandList* pCommandList) { // This will be the chunk ID of the first chunk // added, for the purpose of resource tracking. uint64_t baseChunkId = m_chunks.size(); for (const auto& chunk : pCommandList->m_chunks) m_chunks.push_back(chunk); for (const auto& query : pCommandList->m_queries) m_queries.push_back(query); for (const auto& resource : pCommandList->m_resources) { TrackedResource entry = resource; entry.chunkId += baseChunkId; m_resources.push_back(std::move(entry)); } // Return ID of the last chunk added. The command list // added can never be empty, so do not handle zero. return m_chunks.size() - 1; } void D3D11CommandList::EmitToCsThread( const D3D11ChunkDispatchProc& DispatchProc) { for (const auto& query : m_queries) query->DoDeferredEnd(); for (size_t i = 0, j = 0; i < m_chunks.size(); i++) { // If there are resources to track for the current chunk, // use a strong flush hint to dispatch GPU work quickly. GpuFlushType flushType = GpuFlushType::ImplicitWeakHint; if (j < m_resources.size() && m_resources[j].chunkId == i) flushType = GpuFlushType::ImplicitStrongHint; // Dispatch the chunk and capture its sequence number uint64_t seq = DispatchProc(DxvkCsChunkRef(m_chunks[i]), flushType); // Track resource sequence numbers for the added chunk while (j < m_resources.size() && m_resources[j].chunkId == i) TrackResourceSequenceNumber(m_resources[j++].ref, seq); } } void D3D11CommandList::TrackResourceUsage( ID3D11Resource* pResource, D3D11_RESOURCE_DIMENSION ResourceType, UINT Subresource, uint64_t ChunkId) { TrackedResource entry; entry.ref = D3D11ResourceRef(pResource, Subresource, ResourceType); entry.chunkId = ChunkId; m_resources.push_back(std::move(entry)); } void D3D11CommandList::TrackResourceSequenceNumber( const D3D11ResourceRef& Resource, uint64_t Seq) { ID3D11Resource* iface = Resource.Get(); switch (Resource.GetType()) { case D3D11_RESOURCE_DIMENSION_UNKNOWN: break; case D3D11_RESOURCE_DIMENSION_BUFFER: { auto impl = static_cast(iface); impl->TrackSequenceNumber(Seq); } break; case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { auto impl = static_cast(iface)->GetCommonTexture(); impl->TrackSequenceNumber(Resource.GetSubresource(), Seq); } break; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { auto impl = static_cast(iface)->GetCommonTexture(); impl->TrackSequenceNumber(Resource.GetSubresource(), Seq); } break; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: { auto impl = static_cast(iface)->GetCommonTexture(); impl->TrackSequenceNumber(Resource.GetSubresource(), Seq); } break; } } } dxvk-2.6.1/src/d3d11/d3d11_cmdlist.h000066400000000000000000000027311477473124000166170ustar00rootroot00000000000000#pragma once #include #include "d3d11_context.h" namespace dxvk { using D3D11ChunkDispatchProc = std::function; class D3D11CommandList : public D3D11DeviceChild { public: D3D11CommandList( D3D11Device* pDevice, UINT ContextFlags); ~D3D11CommandList(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) final; UINT STDMETHODCALLTYPE GetContextFlags() final; void AddQuery( D3D11Query* pQuery); uint64_t AddChunk( DxvkCsChunkRef&& Chunk); uint64_t AddCommandList( D3D11CommandList* pCommandList); void EmitToCsThread( const D3D11ChunkDispatchProc& DispatchProc); void TrackResourceUsage( ID3D11Resource* pResource, D3D11_RESOURCE_DIMENSION ResourceType, UINT Subresource, uint64_t ChunkId); private: struct TrackedResource { D3D11ResourceRef ref; uint64_t chunkId; }; UINT m_contextFlags; std::vector m_chunks; std::vector> m_queries; std::vector m_resources; void TrackResourceSequenceNumber( const D3D11ResourceRef& Resource, uint64_t Seq); }; } dxvk-2.6.1/src/d3d11/d3d11_context.cpp000066400000000000000000006234311477473124000172050ustar00rootroot00000000000000#include #include "d3d11_context.h" #include "d3d11_context_def.h" #include "d3d11_context_imm.h" namespace dxvk { template D3D11CommonContext::D3D11CommonContext( D3D11Device* pParent, const Rc& Device, UINT ContextFlags, DxvkCsChunkFlags CsFlags) : D3D11DeviceChild(pParent), m_contextExt(GetTypedContext()), m_annotation(GetTypedContext(), Device), m_device (Device), m_flags (ContextFlags), m_staging (Device, StagingBufferSize), m_csFlags (CsFlags), m_csChunk (AllocCsChunk()) { // Create local allocation cache with the same properties // that we will use for common dynamic buffer types uint32_t cachedDynamic = pParent->GetOptions()->cachedDynamicResources; VkMemoryPropertyFlags memoryFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; if (cachedDynamic & D3D11_BIND_CONSTANT_BUFFER) { memoryFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; cachedDynamic = 0; } VkBufferUsageFlags bufferUsage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; if (!(cachedDynamic & D3D11_BIND_SHADER_RESOURCE)) { bufferUsage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; } if (!(cachedDynamic & D3D11_BIND_VERTEX_BUFFER)) bufferUsage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; if (!(cachedDynamic & D3D11_BIND_INDEX_BUFFER)) bufferUsage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; m_allocationCache = m_device->createAllocationCache(bufferUsage, memoryFlags); } template D3D11CommonContext::~D3D11CommonContext() { } template HRESULT STDMETHODCALLTYPE D3D11CommonContext::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11DeviceContext) || riid == __uuidof(ID3D11DeviceContext1) || riid == __uuidof(ID3D11DeviceContext2) || riid == __uuidof(ID3D11DeviceContext3) || riid == __uuidof(ID3D11DeviceContext4)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(ID3D11VkExtContext) || riid == __uuidof(ID3D11VkExtContext1)) { *ppvObject = ref(&m_contextExt); return S_OK; } if (riid == __uuidof(ID3DUserDefinedAnnotation) || riid == __uuidof(IDXVKUserDefinedAnnotation)) { *ppvObject = ref(&m_annotation); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11DeviceContext), riid)) { Logger::warn("D3D11DeviceContext::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } template D3D11_DEVICE_CONTEXT_TYPE STDMETHODCALLTYPE D3D11CommonContext::GetType() { return IsDeferred ? D3D11_DEVICE_CONTEXT_DEFERRED : D3D11_DEVICE_CONTEXT_IMMEDIATE; } template UINT STDMETHODCALLTYPE D3D11CommonContext::GetContextFlags() { return m_flags; } template void STDMETHODCALLTYPE D3D11CommonContext::ClearState() { D3D10DeviceLock lock = LockContext(); ResetCommandListState(); ResetContextState(); } template void STDMETHODCALLTYPE D3D11CommonContext::DiscardResource(ID3D11Resource* pResource) { D3D10DeviceLock lock = LockContext(); if (!pResource) return; D3D11_RESOURCE_DIMENSION resType = D3D11_RESOURCE_DIMENSION_UNKNOWN; pResource->GetType(&resType); if (resType == D3D11_RESOURCE_DIMENSION_BUFFER) { DiscardBuffer(pResource); } else { auto texture = GetCommonTexture(pResource); auto image = texture->GetImage(); for (uint32_t i = 0; i < texture->CountSubresources(); i++) DiscardTexture(pResource, i); if (image) { EmitCs([cImage = std::move(image)] (DxvkContext* ctx) { ctx->discardImage(cImage); }); } } } template void STDMETHODCALLTYPE D3D11CommonContext::DiscardView(ID3D11View* pResourceView) { DiscardViewBase(pResourceView, nullptr, 0); } template void STDMETHODCALLTYPE D3D11CommonContext::DiscardView1( ID3D11View* pResourceView, const D3D11_RECT* pRects, UINT NumRects) { DiscardViewBase(pResourceView, pRects, NumRects); } template void STDMETHODCALLTYPE D3D11CommonContext::DiscardViewBase( ID3D11View* pResourceView, const D3D11_RECT* pRects, UINT NumRects) { D3D10DeviceLock lock = LockContext(); // We don't support discarding individual rectangles if (!pResourceView || (NumRects && pRects)) return; // ID3D11View has no methods to query the exact type of // the view, so we'll have to check each possible class auto dsv = dynamic_cast(pResourceView); auto rtv = dynamic_cast(pResourceView); auto uav = dynamic_cast(pResourceView); Rc view; if (dsv) view = dsv->GetImageView(); if (rtv) view = rtv->GetImageView(); if (uav) view = uav->GetImageView(); if (view == nullptr) return; // Get information about underlying resource Com resource; pResourceView->GetResource(&resource); uint32_t mipCount = GetCommonTexture(resource.ptr())->Desc()->MipLevels; // Discard mip levels one by one VkImageSubresourceRange sr = view->subresources(); for (uint32_t layer = 0; layer < sr.layerCount; layer++) { for (uint32_t mip = 0; mip < sr.levelCount; mip++) { DiscardTexture(resource.ptr(), D3D11CalcSubresource( sr.baseMipLevel + mip, sr.baseArrayLayer + layer, mipCount)); } } if (rtv || dsv) { EmitCs([cView = view] (DxvkContext* ctx) { ctx->clearRenderTarget(cView, 0, VkClearValue(), cView->info().aspects); }); } } template void STDMETHODCALLTYPE D3D11CommonContext::CopySubresourceRegion( ID3D11Resource* pDstResource, UINT DstSubresource, UINT DstX, UINT DstY, UINT DstZ, ID3D11Resource* pSrcResource, UINT SrcSubresource, const D3D11_BOX* pSrcBox) { CopySubresourceRegionBase( pDstResource, DstSubresource, DstX, DstY, DstZ, pSrcResource, SrcSubresource, pSrcBox, 0); } template void STDMETHODCALLTYPE D3D11CommonContext::CopySubresourceRegion1( ID3D11Resource* pDstResource, UINT DstSubresource, UINT DstX, UINT DstY, UINT DstZ, ID3D11Resource* pSrcResource, UINT SrcSubresource, const D3D11_BOX* pSrcBox, UINT CopyFlags) { CopySubresourceRegionBase( pDstResource, DstSubresource, DstX, DstY, DstZ, pSrcResource, SrcSubresource, pSrcBox, CopyFlags); } template void STDMETHODCALLTYPE D3D11CommonContext::CopySubresourceRegionBase( ID3D11Resource* pDstResource, UINT DstSubresource, UINT DstX, UINT DstY, UINT DstZ, ID3D11Resource* pSrcResource, UINT SrcSubresource, const D3D11_BOX* pSrcBox, UINT CopyFlags) { D3D10DeviceLock lock = LockContext(); if (!pDstResource || !pSrcResource) return; if (pSrcBox && (pSrcBox->left >= pSrcBox->right || pSrcBox->top >= pSrcBox->bottom || pSrcBox->front >= pSrcBox->back)) return; D3D11_RESOURCE_DIMENSION dstResourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; D3D11_RESOURCE_DIMENSION srcResourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; pDstResource->GetType(&dstResourceDim); pSrcResource->GetType(&srcResourceDim); if (dstResourceDim == D3D11_RESOURCE_DIMENSION_BUFFER && srcResourceDim == D3D11_RESOURCE_DIMENSION_BUFFER) { auto dstBuffer = static_cast(pDstResource); auto srcBuffer = static_cast(pSrcResource); VkDeviceSize dstOffset = DstX; VkDeviceSize srcOffset = 0; VkDeviceSize byteCount = -1; if (pSrcBox) { srcOffset = pSrcBox->left; byteCount = pSrcBox->right - pSrcBox->left; } CopyBuffer(dstBuffer, dstOffset, srcBuffer, srcOffset, byteCount); } else if (dstResourceDim != D3D11_RESOURCE_DIMENSION_BUFFER && srcResourceDim != D3D11_RESOURCE_DIMENSION_BUFFER) { auto dstTexture = GetCommonTexture(pDstResource); auto srcTexture = GetCommonTexture(pSrcResource); if (DstSubresource >= dstTexture->CountSubresources() || SrcSubresource >= srcTexture->CountSubresources()) return; auto dstFormatInfo = lookupFormatInfo(dstTexture->GetPackedFormat()); auto srcFormatInfo = lookupFormatInfo(srcTexture->GetPackedFormat()); auto dstLayers = vk::makeSubresourceLayers(dstTexture->GetSubresourceFromIndex(dstFormatInfo->aspectMask, DstSubresource)); auto srcLayers = vk::makeSubresourceLayers(srcTexture->GetSubresourceFromIndex(srcFormatInfo->aspectMask, SrcSubresource)); VkOffset3D srcOffset = { 0, 0, 0 }; VkOffset3D dstOffset = { int32_t(DstX), int32_t(DstY), int32_t(DstZ) }; VkExtent3D srcExtent = srcTexture->MipLevelExtent(srcLayers.mipLevel); if (pSrcBox) { srcOffset.x = pSrcBox->left; srcOffset.y = pSrcBox->top; srcOffset.z = pSrcBox->front; srcExtent.width = pSrcBox->right - pSrcBox->left; srcExtent.height = pSrcBox->bottom - pSrcBox->top; srcExtent.depth = pSrcBox->back - pSrcBox->front; } CopyImage( dstTexture, &dstLayers, dstOffset, srcTexture, &srcLayers, srcOffset, srcExtent); } } template void STDMETHODCALLTYPE D3D11CommonContext::CopyResource( ID3D11Resource* pDstResource, ID3D11Resource* pSrcResource) { D3D10DeviceLock lock = LockContext(); if (!pDstResource || !pSrcResource || (pDstResource == pSrcResource)) return; D3D11_RESOURCE_DIMENSION dstResourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; D3D11_RESOURCE_DIMENSION srcResourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; pDstResource->GetType(&dstResourceDim); pSrcResource->GetType(&srcResourceDim); if (dstResourceDim != srcResourceDim) return; if (dstResourceDim == D3D11_RESOURCE_DIMENSION_BUFFER) { auto dstBuffer = static_cast(pDstResource); auto srcBuffer = static_cast(pSrcResource); if (dstBuffer->Desc()->ByteWidth != srcBuffer->Desc()->ByteWidth) return; CopyBuffer(dstBuffer, 0, srcBuffer, 0, -1); } else { auto dstTexture = GetCommonTexture(pDstResource); auto srcTexture = GetCommonTexture(pSrcResource); auto dstDesc = dstTexture->Desc(); auto srcDesc = srcTexture->Desc(); // The subresource count must match as well if (dstDesc->ArraySize != srcDesc->ArraySize || dstDesc->MipLevels != srcDesc->MipLevels) return; auto dstFormatInfo = lookupFormatInfo(dstTexture->GetPackedFormat()); auto srcFormatInfo = lookupFormatInfo(srcTexture->GetPackedFormat()); for (uint32_t i = 0; i < dstDesc->MipLevels; i++) { VkImageSubresourceLayers dstLayers = { dstFormatInfo->aspectMask, i, 0, dstDesc->ArraySize }; VkImageSubresourceLayers srcLayers = { srcFormatInfo->aspectMask, i, 0, srcDesc->ArraySize }; CopyImage( dstTexture, &dstLayers, VkOffset3D(), srcTexture, &srcLayers, VkOffset3D(), srcTexture->MipLevelExtent(i)); } } } template void STDMETHODCALLTYPE D3D11CommonContext::CopyStructureCount( ID3D11Buffer* pDstBuffer, UINT DstAlignedByteOffset, ID3D11UnorderedAccessView* pSrcView) { D3D10DeviceLock lock = LockContext(); auto buf = static_cast(pDstBuffer); auto uav = static_cast(pSrcView); if (!buf || !uav) return; auto counterView = uav->GetCounterView(); if (counterView == nullptr) return; EmitCs([ cDstSlice = buf->GetBufferSlice(DstAlignedByteOffset), cSrcSlice = DxvkBufferSlice(counterView) ] (DxvkContext* ctx) { ctx->copyBuffer( cDstSlice.buffer(), cDstSlice.offset(), cSrcSlice.buffer(), cSrcSlice.offset(), sizeof(uint32_t)); }); if (buf->HasSequenceNumber()) GetTypedContext()->TrackBufferSequenceNumber(buf); } template void STDMETHODCALLTYPE D3D11CommonContext::ClearRenderTargetView( ID3D11RenderTargetView* pRenderTargetView, const FLOAT ColorRGBA[4]) { D3D10DeviceLock lock = LockContext(); auto rtv = static_cast(pRenderTargetView); if (!rtv) return; auto view = rtv->GetImageView(); auto color = ConvertColorValue(ColorRGBA, view->formatInfo()); EmitCs([ cClearValue = color, cImageView = std::move(view) ] (DxvkContext* ctx) { ctx->clearRenderTarget(cImageView, VK_IMAGE_ASPECT_COLOR_BIT, cClearValue, 0u); }); } template void STDMETHODCALLTYPE D3D11CommonContext::ClearUnorderedAccessViewUint( ID3D11UnorderedAccessView* pUnorderedAccessView, const UINT Values[4]) { D3D10DeviceLock lock = LockContext(); if (!pUnorderedAccessView) return; Com qiUav; if (FAILED(pUnorderedAccessView->QueryInterface(IID_PPV_ARGS(&qiUav)))) return; auto uav = static_cast(qiUav.ptr()); // Gather UAV format info. We'll use this to determine // whether we need to create a temporary view or not. D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc; uav->GetDesc(&uavDesc); VkFormat uavFormat = m_parent->LookupFormat(uavDesc.Format, DXGI_VK_FORMAT_MODE_ANY).Format; VkFormat rawFormat = m_parent->LookupFormat(uavDesc.Format, DXGI_VK_FORMAT_MODE_RAW).Format; if (uavDesc.Format == DXGI_FORMAT_A8_UNORM) rawFormat = uavFormat; if (uavFormat && !rawFormat) { Logger::err(str::format("D3D11: ClearUnorderedAccessViewUint: No raw format found for ", uavFormat)); return; } VkClearValue clearValue = { }; if (uavDesc.Format == DXGI_FORMAT_R11G11B10_FLOAT) { // R11G11B10 is a special case since there's no corresponding // integer format with the same bit layout. Use R32 instead. clearValue.color.uint32[0] = ((Values[0] & 0x7FF) << 0) | ((Values[1] & 0x7FF) << 11) | ((Values[2] & 0x3FF) << 22); clearValue.color.uint32[1] = 0; clearValue.color.uint32[2] = 0; clearValue.color.uint32[3] = 0; } else if (uavDesc.Format == DXGI_FORMAT_A8_UNORM) { // Use the unorm format itself to execute the clear, regardless // of whether we use A8 or emulate the format with R8. This is // necessary because we cannot create R8_UINT views for A8. float a = float(Values[3] & 0xff) / 255.0f; clearValue.color.float32[0] = a; clearValue.color.float32[1] = a; clearValue.color.float32[2] = a; clearValue.color.float32[3] = a; } else { clearValue.color.uint32[0] = Values[0]; clearValue.color.uint32[1] = Values[1]; clearValue.color.uint32[2] = Values[2]; clearValue.color.uint32[3] = Values[3]; } if (uav->GetResourceType() == D3D11_RESOURCE_DIMENSION_BUFFER) { // In case of raw and structured buffers as well as typed // buffers that can be used for atomic operations, we can // use the fast Vulkan buffer clear function. Rc bufferView = uav->GetBufferView(); if (bufferView->info().format == VK_FORMAT_R32_UINT || bufferView->info().format == VK_FORMAT_R32_SINT || bufferView->info().format == VK_FORMAT_R32_SFLOAT || bufferView->info().format == VK_FORMAT_B10G11R11_UFLOAT_PACK32) { EmitCs([ cClearValue = clearValue.color.uint32[0], cDstSlice = DxvkBufferSlice(bufferView) ] (DxvkContext* ctx) { ctx->clearBuffer( cDstSlice.buffer(), cDstSlice.offset(), cDstSlice.length(), cClearValue); }); } else { // Create a view with an integer format if necessary if (uavFormat != rawFormat) { DxvkBufferViewKey info = bufferView->info(); info.format = rawFormat; bufferView = bufferView->buffer()->createView(info); } EmitCs([ cClearValue = clearValue, cDstView = bufferView ] (DxvkContext* ctx) { ctx->clearBufferView( cDstView, 0, cDstView->elementCount(), cClearValue.color); }); } } else { Rc imageView = uav->GetImageView(); // If the clear value is zero, we can use the original view regardless of // the format since the bit pattern will not change in any supported format. bool isZeroClearValue = !(clearValue.color.uint32[0] | clearValue.color.uint32[1] | clearValue.color.uint32[2] | clearValue.color.uint32[3]); EmitCs([ cClearValue = clearValue, cDstView = imageView, cDstFormat = isZeroClearValue ? uavFormat : rawFormat ] (DxvkContext* ctx) { // Ensure that we can write to the image with the given format DxvkImageUsageInfo imageUsage = { }; imageUsage.usage = VK_IMAGE_USAGE_STORAGE_BIT; imageUsage.viewFormatCount = 1; imageUsage.viewFormats = &cDstFormat; ctx->ensureImageCompatibility(cDstView->image(), imageUsage); // If necessary, recreate the view Rc view = cDstView; if (view->info().format != cDstFormat) { DxvkImageViewKey key = cDstView->info(); key.format = cDstFormat; view = cDstView->image()->createView(key); } ctx->clearImageView(view, VkOffset3D { 0, 0, 0 }, cDstView->mipLevelExtent(0), VK_IMAGE_ASPECT_COLOR_BIT, cClearValue); }); } } template void STDMETHODCALLTYPE D3D11CommonContext::ClearUnorderedAccessViewFloat( ID3D11UnorderedAccessView* pUnorderedAccessView, const FLOAT Values[4]) { D3D10DeviceLock lock = LockContext(); auto uav = static_cast(pUnorderedAccessView); if (!uav) return; auto imgView = uav->GetImageView(); auto bufView = uav->GetBufferView(); const DxvkFormatInfo* info = nullptr; if (imgView != nullptr) info = imgView->formatInfo(); if (bufView != nullptr) info = bufView->formatInfo(); if (!info || info->flags.any(DxvkFormatFlag::SampledSInt, DxvkFormatFlag::SampledUInt)) return; VkClearValue clearValue; clearValue.color.float32[0] = Values[0]; clearValue.color.float32[1] = Values[1]; clearValue.color.float32[2] = Values[2]; clearValue.color.float32[3] = Values[3]; if (uav->GetResourceType() == D3D11_RESOURCE_DIMENSION_BUFFER) { EmitCs([ cClearValue = clearValue, cDstView = std::move(bufView) ] (DxvkContext* ctx) { ctx->clearBufferView( cDstView, 0, cDstView->elementCount(), cClearValue.color); }); } else { EmitCs([ cClearValue = clearValue, cDstView = std::move(imgView) ] (DxvkContext* ctx) { ctx->clearImageView(cDstView, VkOffset3D { 0, 0, 0 }, cDstView->mipLevelExtent(0), VK_IMAGE_ASPECT_COLOR_BIT, cClearValue); }); } } template void STDMETHODCALLTYPE D3D11CommonContext::ClearDepthStencilView( ID3D11DepthStencilView* pDepthStencilView, UINT ClearFlags, FLOAT Depth, UINT8 Stencil) { D3D10DeviceLock lock = LockContext(); auto dsv = static_cast(pDepthStencilView); if (!dsv) return; // Figure out which aspects to clear based on // the image view properties and clear flags. VkImageAspectFlags aspectMask = 0; if (ClearFlags & D3D11_CLEAR_DEPTH) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; if (ClearFlags & D3D11_CLEAR_STENCIL) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; aspectMask &= dsv->GetWritableAspectMask(); if (!aspectMask) return; VkClearValue clearValue; clearValue.depthStencil.depth = Depth; clearValue.depthStencil.stencil = Stencil; EmitCs([ cClearValue = clearValue, cAspectMask = aspectMask, cImageView = dsv->GetImageView() ] (DxvkContext* ctx) { ctx->clearRenderTarget(cImageView, cAspectMask, cClearValue, 0u); }); } template void STDMETHODCALLTYPE D3D11CommonContext::ClearView( ID3D11View* pView, const FLOAT Color[4], const D3D11_RECT* pRect, UINT NumRects) { D3D10DeviceLock lock = LockContext(); if (NumRects && !pRect) return; // ID3D11View has no methods to query the exact type of // the view, so we'll have to check each possible class auto dsv = dynamic_cast(pView); auto rtv = dynamic_cast(pView); auto uav = dynamic_cast(pView); auto vov = dynamic_cast(pView); // Retrieve underlying resource view Rc bufView; Rc imgView; if (dsv != nullptr) imgView = dsv->GetImageView(); if (rtv != nullptr) imgView = rtv->GetImageView(); if (uav != nullptr) { bufView = uav->GetBufferView(); imgView = uav->GetImageView(); } if (vov != nullptr) imgView = vov->GetView(); // 3D views are unsupported if (imgView != nullptr && imgView->info().viewType == VK_IMAGE_VIEW_TYPE_3D) return; // Query the view format. We'll have to convert // the clear color based on the format's data type. VkFormat format = VK_FORMAT_UNDEFINED; if (bufView != nullptr) format = bufView->info().format; if (imgView != nullptr) format = imgView->info().format; if (format == VK_FORMAT_UNDEFINED) return; // We'll need the format info to determine the buffer // element size, and we also need it for depth images. const DxvkFormatInfo* formatInfo = lookupFormatInfo(format); // Convert the clear color format. ClearView takes // the clear value for integer formats as a set of // integral floats, so we'll have to convert. VkClearValue clearValue = ConvertColorValue(Color, formatInfo); VkImageAspectFlags clearAspect = formatInfo->aspectMask & (VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT); // Clear all the rectangles that are specified for (uint32_t i = 0; i < NumRects || i < 1; i++) { if (NumRects) { if (pRect[i].left >= pRect[i].right || pRect[i].top >= pRect[i].bottom) continue; } if (bufView != nullptr) { VkDeviceSize offset = 0; VkDeviceSize length = bufView->info().size / formatInfo->elementSize; if (NumRects) { offset = pRect[i].left; length = pRect[i].right - pRect[i].left; } EmitCs([ cBufferView = bufView, cRangeOffset = offset, cRangeLength = length, cClearValue = clearValue ] (DxvkContext* ctx) { ctx->clearBufferView( cBufferView, cRangeOffset, cRangeLength, cClearValue.color); }); } if (imgView != nullptr) { VkOffset3D offset = { 0, 0, 0 }; VkExtent3D extent = imgView->mipLevelExtent(0); if (NumRects) { offset = { pRect[i].left, pRect[i].top, 0 }; extent = { uint32_t(pRect[i].right - pRect[i].left), uint32_t(pRect[i].bottom - pRect[i].top), 1 }; } EmitCs([ cImageView = imgView, cAreaOffset = offset, cAreaExtent = extent, cClearAspect = clearAspect, cClearValue = clearValue ] (DxvkContext* ctx) { const VkImageUsageFlags rtUsage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; bool isFullSize = cImageView->mipLevelExtent(0) == cAreaExtent; if ((cImageView->info().usage & rtUsage) && isFullSize) { ctx->clearRenderTarget(cImageView, cClearAspect, cClearValue, 0u); } else { ctx->clearImageView(cImageView, cAreaOffset, cAreaExtent, cClearAspect, cClearValue); } }); } } } template void STDMETHODCALLTYPE D3D11CommonContext::GenerateMips(ID3D11ShaderResourceView* pShaderResourceView) { D3D10DeviceLock lock = LockContext(); auto view = static_cast(pShaderResourceView); if (!view || view->GetResourceType() == D3D11_RESOURCE_DIMENSION_BUFFER) return; D3D11_COMMON_RESOURCE_DESC resourceDesc = view->GetResourceDesc(); if (!(resourceDesc.MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS)) return; EmitCs([cDstImageView = view->GetImageView()] (DxvkContext* ctx) { ctx->generateMipmaps(cDstImageView, VK_FILTER_LINEAR); }); } template void STDMETHODCALLTYPE D3D11CommonContext::ResolveSubresource( ID3D11Resource* pDstResource, UINT DstSubresource, ID3D11Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format) { D3D10DeviceLock lock = LockContext(); bool isSameSubresource = pDstResource == pSrcResource && DstSubresource == SrcSubresource; if (!pDstResource || !pSrcResource || isSameSubresource) return; D3D11_RESOURCE_DIMENSION dstResourceType; D3D11_RESOURCE_DIMENSION srcResourceType; pDstResource->GetType(&dstResourceType); pSrcResource->GetType(&srcResourceType); if (dstResourceType != D3D11_RESOURCE_DIMENSION_TEXTURE2D || srcResourceType != D3D11_RESOURCE_DIMENSION_TEXTURE2D) return; auto dstTexture = static_cast(pDstResource); auto srcTexture = static_cast(pSrcResource); D3D11_TEXTURE2D_DESC dstDesc; D3D11_TEXTURE2D_DESC srcDesc; dstTexture->GetDesc(&dstDesc); srcTexture->GetDesc(&srcDesc); if (dstDesc.SampleDesc.Count != 1) return; D3D11CommonTexture* dstTextureInfo = GetCommonTexture(pDstResource); D3D11CommonTexture* srcTextureInfo = GetCommonTexture(pSrcResource); const DXGI_VK_FORMAT_INFO dstFormatInfo = m_parent->LookupFormat(dstDesc.Format, DXGI_VK_FORMAT_MODE_ANY); const DXGI_VK_FORMAT_INFO srcFormatInfo = m_parent->LookupFormat(srcDesc.Format, DXGI_VK_FORMAT_MODE_ANY); auto dstVulkanFormatInfo = lookupFormatInfo(dstFormatInfo.Format); auto srcVulkanFormatInfo = lookupFormatInfo(srcFormatInfo.Format); if (DstSubresource >= dstTextureInfo->CountSubresources() || SrcSubresource >= srcTextureInfo->CountSubresources()) return; const VkImageSubresource dstSubresource = dstTextureInfo->GetSubresourceFromIndex( dstVulkanFormatInfo->aspectMask, DstSubresource); const VkImageSubresource srcSubresource = srcTextureInfo->GetSubresourceFromIndex( srcVulkanFormatInfo->aspectMask, SrcSubresource); const VkImageSubresourceLayers dstSubresourceLayers = { dstSubresource.aspectMask, dstSubresource.mipLevel, dstSubresource.arrayLayer, 1 }; const VkImageSubresourceLayers srcSubresourceLayers = { srcSubresource.aspectMask, srcSubresource.mipLevel, srcSubresource.arrayLayer, 1 }; if (srcDesc.SampleDesc.Count == 1 || m_parent->GetOptions()->disableMsaa) { EmitCs([ cDstImage = dstTextureInfo->GetImage(), cSrcImage = srcTextureInfo->GetImage(), cDstLayers = dstSubresourceLayers, cSrcLayers = srcSubresourceLayers ] (DxvkContext* ctx) { ctx->copyImage( cDstImage, cDstLayers, VkOffset3D { 0, 0, 0 }, cSrcImage, cSrcLayers, VkOffset3D { 0, 0, 0 }, cDstImage->mipLevelExtent(cDstLayers.mipLevel)); }); } else { VkFormat format = m_parent->LookupFormat( Format, DXGI_VK_FORMAT_MODE_ANY).Format; EmitCs([ cDstImage = dstTextureInfo->GetImage(), cSrcImage = srcTextureInfo->GetImage(), cDstSubres = dstSubresourceLayers, cSrcSubres = srcSubresourceLayers, cFormat = format ] (DxvkContext* ctx) { VkFormat format = cFormat ? cFormat : cSrcImage->info().format; VkImageResolve region; region.srcSubresource = cSrcSubres; region.srcOffset = VkOffset3D { 0, 0, 0 }; region.dstSubresource = cDstSubres; region.dstOffset = VkOffset3D { 0, 0, 0 }; region.extent = cDstImage->mipLevelExtent(cDstSubres.mipLevel); ctx->resolveImage(cDstImage, cSrcImage, region, format, getDefaultResolveMode(format), VK_RESOLVE_MODE_NONE); }); if constexpr (!IsDeferred) GetTypedContext()->m_hasPendingMsaaResolve = false; } if (dstTextureInfo->HasSequenceNumber()) GetTypedContext()->TrackTextureSequenceNumber(dstTextureInfo, DstSubresource); } template void STDMETHODCALLTYPE D3D11CommonContext::UpdateSubresource( ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch) { if (IsDeferred && unlikely(pDstBox != nullptr) && unlikely(!m_parent->GetOptions()->exposeDriverCommandLists)) { // If called from a deferred context and native command list support is not // exposed, we need to apply the destination box to the source pointer. This // only applies to UpdateSubresource, not to UpdateSubresource1. See MSDN: // https://msdn.microsoft.com/en-us/library/windows/desktop/ff476486(v=vs.85).aspx) size_t srcOffset = pDstBox->left; // For textures, the offset logic needs to take the format into account. // Ignore that multi-planar images exist, this is hairy enough already. D3D11CommonTexture* dstTexture = GetCommonTexture(pDstResource); if (dstTexture) { auto dstFormat = dstTexture->GetPackedFormat(); auto dstFormatInfo = lookupFormatInfo(dstFormat); size_t blockSize = dstFormatInfo->elementSize; VkOffset3D offset; offset.x = pDstBox->left / dstFormatInfo->blockSize.width; offset.y = pDstBox->top / dstFormatInfo->blockSize.height; offset.z = pDstBox->front / dstFormatInfo->blockSize.depth; srcOffset = offset.x * blockSize + offset.y * SrcRowPitch + offset.z * SrcDepthPitch; } pSrcData = reinterpret_cast(pSrcData) + srcOffset; } UpdateResource(pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch, 0); } template void STDMETHODCALLTYPE D3D11CommonContext::UpdateSubresource1( ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch, UINT CopyFlags) { UpdateResource(pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch, CopyFlags); } template void STDMETHODCALLTYPE D3D11CommonContext::DrawAuto() { D3D10DeviceLock lock = LockContext(); D3D11Buffer* buffer = m_state.ia.vertexBuffers[0].buffer.ptr(); if (!buffer) return; DxvkBufferSlice vtxBuf = buffer->GetBufferSlice(); DxvkBufferSlice ctrBuf = buffer->GetSOCounter(); if (!ctrBuf.defined()) return; if (unlikely(HasDirtyGraphicsBindings())) ApplyDirtyGraphicsBindings(); // We bind the SO counter as an indirect count buffer, // so reset any tracking we may have been doing here. m_state.id.reset(); EmitCs([=] (DxvkContext* ctx) mutable { ctx->bindDrawBuffers(DxvkBufferSlice(), Forwarder::move(ctrBuf)); ctx->drawIndirectXfb(0u, vtxBuf.buffer()->getXfbVertexStride(), vtxBuf.offset()); // Reset draw buffer right away so we don't // keep the SO counter alive indefinitely ctx->bindDrawBuffers(DxvkBufferSlice(), DxvkBufferSlice()); }); } template void STDMETHODCALLTYPE D3D11CommonContext::Draw( UINT VertexCount, UINT StartVertexLocation) { D3D10DeviceLock lock = LockContext(); if (unlikely(!VertexCount)) return; VkDrawIndirectCommand draw = { }; draw.vertexCount = VertexCount; draw.instanceCount = 1u; draw.firstVertex = StartVertexLocation; draw.firstInstance = 0u; BatchDraw(draw); } template void STDMETHODCALLTYPE D3D11CommonContext::DrawIndexed( UINT IndexCount, UINT StartIndexLocation, INT BaseVertexLocation) { D3D10DeviceLock lock = LockContext(); if (unlikely(!IndexCount)) return; VkDrawIndexedIndirectCommand draw = { }; draw.indexCount = IndexCount; draw.instanceCount = 1u; draw.firstIndex = StartIndexLocation; draw.vertexOffset = BaseVertexLocation; draw.firstInstance = 0u; BatchDrawIndexed(draw); } template void STDMETHODCALLTYPE D3D11CommonContext::DrawInstanced( UINT VertexCountPerInstance, UINT InstanceCount, UINT StartVertexLocation, UINT StartInstanceLocation) { D3D10DeviceLock lock = LockContext(); if (unlikely(!VertexCountPerInstance || !InstanceCount)) return; VkDrawIndirectCommand draw = { }; draw.vertexCount = VertexCountPerInstance; draw.instanceCount = InstanceCount; draw.firstVertex = StartVertexLocation; draw.firstInstance = StartInstanceLocation; BatchDraw(draw); } template void STDMETHODCALLTYPE D3D11CommonContext::DrawIndexedInstanced( UINT IndexCountPerInstance, UINT InstanceCount, UINT StartIndexLocation, INT BaseVertexLocation, UINT StartInstanceLocation) { D3D10DeviceLock lock = LockContext(); if (unlikely(!IndexCountPerInstance || !InstanceCount)) return; VkDrawIndexedIndirectCommand draw = { }; draw.indexCount = IndexCountPerInstance; draw.instanceCount = InstanceCount; draw.firstIndex = StartIndexLocation; draw.vertexOffset = BaseVertexLocation; draw.firstInstance = StartInstanceLocation; BatchDrawIndexed(draw); } template void STDMETHODCALLTYPE D3D11CommonContext::DrawIndexedInstancedIndirect( ID3D11Buffer* pBufferForArgs, UINT AlignedByteOffsetForArgs) { D3D10DeviceLock lock = LockContext(); SetDrawBuffers(pBufferForArgs, nullptr); if (!ValidateDrawBufferSize(pBufferForArgs, AlignedByteOffsetForArgs, sizeof(VkDrawIndexedIndirectCommand))) return; if (unlikely(HasDirtyGraphicsBindings())) ApplyDirtyGraphicsBindings(); // If possible, batch multiple indirect draw calls into one single multidraw call if (m_csDataType == D3D11CmdType::DrawIndirectIndexed) { auto cmdData = static_cast(m_csData->first()); auto stride = GetIndirectCommandStride(cmdData, AlignedByteOffsetForArgs, sizeof(VkDrawIndexedIndirectCommand)); if (stride) { cmdData->count += 1; cmdData->stride = stride; return; } } // Need to start a new draw sequence EmitCsCmd(D3D11CmdType::DrawIndirectIndexed, 1u, [] (DxvkContext* ctx, const D3D11CmdDrawIndirectData* data, size_t) { ctx->drawIndexedIndirect(data->offset, data->count, data->stride, true); }); auto cmdData = new (m_csData->first()) D3D11CmdDrawIndirectData(); cmdData->offset = AlignedByteOffsetForArgs; cmdData->count = 1; cmdData->stride = 0; } template void STDMETHODCALLTYPE D3D11CommonContext::DrawInstancedIndirect( ID3D11Buffer* pBufferForArgs, UINT AlignedByteOffsetForArgs) { D3D10DeviceLock lock = LockContext(); SetDrawBuffers(pBufferForArgs, nullptr); if (!ValidateDrawBufferSize(pBufferForArgs, AlignedByteOffsetForArgs, sizeof(VkDrawIndirectCommand))) return; if (unlikely(HasDirtyGraphicsBindings())) ApplyDirtyGraphicsBindings(); // If possible, batch multiple indirect draw calls into one single multidraw call if (m_csDataType == D3D11CmdType::DrawIndirect) { auto cmdData = static_cast(m_csData->first()); auto stride = GetIndirectCommandStride(cmdData, AlignedByteOffsetForArgs, sizeof(VkDrawIndirectCommand)); if (stride) { cmdData->count += 1; cmdData->stride = stride; return; } } // Need to start a new draw sequence EmitCsCmd(D3D11CmdType::DrawIndirect, 1u, [] (DxvkContext* ctx, const D3D11CmdDrawIndirectData* data, size_t) { ctx->drawIndirect(data->offset, data->count, data->stride, true); }); auto cmdData = new (m_csData->first()) D3D11CmdDrawIndirectData(); cmdData->offset = AlignedByteOffsetForArgs; cmdData->count = 1; cmdData->stride = 0; } template void STDMETHODCALLTYPE D3D11CommonContext::Dispatch( UINT ThreadGroupCountX, UINT ThreadGroupCountY, UINT ThreadGroupCountZ) { D3D10DeviceLock lock = LockContext(); if (unlikely(!ThreadGroupCountX || !ThreadGroupCountY || !ThreadGroupCountZ)) return; if (unlikely(HasDirtyComputeBindings())) ApplyDirtyComputeBindings(); EmitCs([=] (DxvkContext* ctx) { ctx->dispatch( ThreadGroupCountX, ThreadGroupCountY, ThreadGroupCountZ); }); } template void STDMETHODCALLTYPE D3D11CommonContext::DispatchIndirect( ID3D11Buffer* pBufferForArgs, UINT AlignedByteOffsetForArgs) { D3D10DeviceLock lock = LockContext(); SetDrawBuffers(pBufferForArgs, nullptr); if (!ValidateDrawBufferSize(pBufferForArgs, AlignedByteOffsetForArgs, sizeof(VkDispatchIndirectCommand))) return; if (unlikely(HasDirtyComputeBindings())) ApplyDirtyComputeBindings(); EmitCs([cOffset = AlignedByteOffsetForArgs] (DxvkContext* ctx) { ctx->dispatchIndirect(cOffset); }); } template void STDMETHODCALLTYPE D3D11CommonContext::IASetInputLayout(ID3D11InputLayout* pInputLayout) { D3D10DeviceLock lock = LockContext(); auto inputLayout = static_cast(pInputLayout); if (m_state.ia.inputLayout != inputLayout) { bool equal = false; // Some games (e.g. Grim Dawn) create lots and lots of // identical input layouts, so we'll only apply the state // if the input layouts has actually changed between calls. if (m_state.ia.inputLayout != nullptr && inputLayout != nullptr) equal = m_state.ia.inputLayout->Compare(inputLayout); m_state.ia.inputLayout = inputLayout; if (!equal) ApplyInputLayout(); } } template void STDMETHODCALLTYPE D3D11CommonContext::IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY Topology) { D3D10DeviceLock lock = LockContext(); if (m_state.ia.primitiveTopology != Topology) { m_state.ia.primitiveTopology = Topology; ApplyPrimitiveTopology(); } } template void STDMETHODCALLTYPE D3D11CommonContext::IASetVertexBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppVertexBuffers, const UINT* pStrides, const UINT* pOffsets) { D3D10DeviceLock lock = LockContext(); for (uint32_t i = 0; i < NumBuffers; i++) { auto newBuffer = static_cast(ppVertexBuffers[i]); if (m_state.ia.vertexBuffers[StartSlot + i].buffer != newBuffer) { m_state.ia.vertexBuffers[StartSlot + i].buffer = newBuffer; m_state.ia.vertexBuffers[StartSlot + i].offset = pOffsets[i]; m_state.ia.vertexBuffers[StartSlot + i].stride = pStrides[i]; BindVertexBuffer(StartSlot + i, newBuffer, pOffsets[i], pStrides[i]); } else if (m_state.ia.vertexBuffers[StartSlot + i].offset != pOffsets[i] || m_state.ia.vertexBuffers[StartSlot + i].stride != pStrides[i]) { m_state.ia.vertexBuffers[StartSlot + i].offset = pOffsets[i]; m_state.ia.vertexBuffers[StartSlot + i].stride = pStrides[i]; BindVertexBufferRange(StartSlot + i, newBuffer, pOffsets[i], pStrides[i]); } } m_state.ia.maxVbCount = std::clamp(StartSlot + NumBuffers, m_state.ia.maxVbCount, uint32_t(m_state.ia.vertexBuffers.size())); } template void STDMETHODCALLTYPE D3D11CommonContext::IASetIndexBuffer( ID3D11Buffer* pIndexBuffer, DXGI_FORMAT Format, UINT Offset) { D3D10DeviceLock lock = LockContext(); auto newBuffer = static_cast(pIndexBuffer); if (m_state.ia.indexBuffer.buffer != newBuffer) { m_state.ia.indexBuffer.buffer = newBuffer; m_state.ia.indexBuffer.offset = Offset; m_state.ia.indexBuffer.format = Format; BindIndexBuffer(newBuffer, Offset, Format); } else if (m_state.ia.indexBuffer.offset != Offset || m_state.ia.indexBuffer.format != Format) { m_state.ia.indexBuffer.offset = Offset; m_state.ia.indexBuffer.format = Format; BindIndexBufferRange(newBuffer, Offset, Format); } } template void STDMETHODCALLTYPE D3D11CommonContext::IAGetInputLayout(ID3D11InputLayout** ppInputLayout) { D3D10DeviceLock lock = LockContext(); *ppInputLayout = m_state.ia.inputLayout.ref(); } template void STDMETHODCALLTYPE D3D11CommonContext::IAGetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY* pTopology) { D3D10DeviceLock lock = LockContext(); *pTopology = m_state.ia.primitiveTopology; } template void STDMETHODCALLTYPE D3D11CommonContext::IAGetVertexBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppVertexBuffers, UINT* pStrides, UINT* pOffsets) { D3D10DeviceLock lock = LockContext(); for (uint32_t i = 0; i < NumBuffers; i++) { const bool inRange = StartSlot + i < m_state.ia.vertexBuffers.size(); if (ppVertexBuffers) { ppVertexBuffers[i] = inRange ? m_state.ia.vertexBuffers[StartSlot + i].buffer.ref() : nullptr; } if (pStrides) { pStrides[i] = inRange ? m_state.ia.vertexBuffers[StartSlot + i].stride : 0u; } if (pOffsets) { pOffsets[i] = inRange ? m_state.ia.vertexBuffers[StartSlot + i].offset : 0u; } } } template void STDMETHODCALLTYPE D3D11CommonContext::IAGetIndexBuffer( ID3D11Buffer** ppIndexBuffer, DXGI_FORMAT* pFormat, UINT* pOffset) { D3D10DeviceLock lock = LockContext(); if (ppIndexBuffer) *ppIndexBuffer = m_state.ia.indexBuffer.buffer.ref(); if (pFormat) *pFormat = m_state.ia.indexBuffer.format; if (pOffset) *pOffset = m_state.ia.indexBuffer.offset; } template void STDMETHODCALLTYPE D3D11CommonContext::VSSetShader( ID3D11VertexShader* pVertexShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { D3D10DeviceLock lock = LockContext(); auto shader = static_cast(pVertexShader); if (NumClassInstances) Logger::err("D3D11: Class instances not supported"); if (m_state.vs != shader) { m_state.vs = shader; BindShader(GetCommonShader(shader)); } } template void STDMETHODCALLTYPE D3D11CommonContext::VSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { D3D10DeviceLock lock = LockContext(); SetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers); } template void STDMETHODCALLTYPE D3D11CommonContext::VSSetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers, const UINT* pFirstConstant, const UINT* pNumConstants) { D3D10DeviceLock lock = LockContext(); SetConstantBuffers1( StartSlot, NumBuffers, ppConstantBuffers, pFirstConstant, pNumConstants); } template void STDMETHODCALLTYPE D3D11CommonContext::VSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { D3D10DeviceLock lock = LockContext(); SetShaderResources( StartSlot, NumViews, ppShaderResourceViews); } template void STDMETHODCALLTYPE D3D11CommonContext::VSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { D3D10DeviceLock lock = LockContext(); SetSamplers( StartSlot, NumSamplers, ppSamplers); } template void STDMETHODCALLTYPE D3D11CommonContext::VSGetShader( ID3D11VertexShader** ppVertexShader, ID3D11ClassInstance** ppClassInstances, UINT* pNumClassInstances) { D3D10DeviceLock lock = LockContext(); if (ppVertexShader) *ppVertexShader = m_state.vs.ref(); if (pNumClassInstances) *pNumClassInstances = 0; } template void STDMETHODCALLTYPE D3D11CommonContext::VSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers) { D3D10DeviceLock lock = LockContext(); GetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers, nullptr, nullptr); } template void STDMETHODCALLTYPE D3D11CommonContext::VSGetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers, UINT* pFirstConstant, UINT* pNumConstants) { D3D10DeviceLock lock = LockContext(); GetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers, pFirstConstant, pNumConstants); } template void STDMETHODCALLTYPE D3D11CommonContext::VSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView** ppShaderResourceViews) { D3D10DeviceLock lock = LockContext(); GetShaderResources( StartSlot, NumViews, ppShaderResourceViews); } template void STDMETHODCALLTYPE D3D11CommonContext::VSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState** ppSamplers) { D3D10DeviceLock lock = LockContext(); GetSamplers( StartSlot, NumSamplers, ppSamplers); } template void STDMETHODCALLTYPE D3D11CommonContext::HSSetShader( ID3D11HullShader* pHullShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { D3D10DeviceLock lock = LockContext(); auto shader = static_cast(pHullShader); if (NumClassInstances) Logger::err("D3D11: Class instances not supported"); if (m_state.hs != shader) { m_state.hs = shader; BindShader(GetCommonShader(shader)); } } template void STDMETHODCALLTYPE D3D11CommonContext::HSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { D3D10DeviceLock lock = LockContext(); SetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers); } template void STDMETHODCALLTYPE D3D11CommonContext::HSSetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers, const UINT* pFirstConstant, const UINT* pNumConstants) { D3D10DeviceLock lock = LockContext(); SetConstantBuffers1( StartSlot, NumBuffers, ppConstantBuffers, pFirstConstant, pNumConstants); } template void STDMETHODCALLTYPE D3D11CommonContext::HSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { D3D10DeviceLock lock = LockContext(); SetShaderResources( StartSlot, NumViews, ppShaderResourceViews); } template void STDMETHODCALLTYPE D3D11CommonContext::HSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { D3D10DeviceLock lock = LockContext(); SetSamplers( StartSlot, NumSamplers, ppSamplers); } template void STDMETHODCALLTYPE D3D11CommonContext::HSGetShader( ID3D11HullShader** ppHullShader, ID3D11ClassInstance** ppClassInstances, UINT* pNumClassInstances) { D3D10DeviceLock lock = LockContext(); if (ppHullShader) *ppHullShader = m_state.hs.ref(); if (pNumClassInstances) *pNumClassInstances = 0; } template void STDMETHODCALLTYPE D3D11CommonContext::HSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers) { D3D10DeviceLock lock = LockContext(); GetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers, nullptr, nullptr); } template void STDMETHODCALLTYPE D3D11CommonContext::HSGetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers, UINT* pFirstConstant, UINT* pNumConstants) { D3D10DeviceLock lock = LockContext(); GetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers, pFirstConstant, pNumConstants); } template void STDMETHODCALLTYPE D3D11CommonContext::HSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView** ppShaderResourceViews) { D3D10DeviceLock lock = LockContext(); GetShaderResources( StartSlot, NumViews, ppShaderResourceViews); } template void STDMETHODCALLTYPE D3D11CommonContext::HSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState** ppSamplers) { D3D10DeviceLock lock = LockContext(); GetSamplers( StartSlot, NumSamplers, ppSamplers); } template void STDMETHODCALLTYPE D3D11CommonContext::DSSetShader( ID3D11DomainShader* pDomainShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { D3D10DeviceLock lock = LockContext(); auto shader = static_cast(pDomainShader); if (NumClassInstances) Logger::err("D3D11: Class instances not supported"); if (m_state.ds != shader) { m_state.ds = shader; BindShader(GetCommonShader(shader)); } } template void STDMETHODCALLTYPE D3D11CommonContext::DSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { D3D10DeviceLock lock = LockContext(); SetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers); } template void STDMETHODCALLTYPE D3D11CommonContext::DSSetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers, const UINT* pFirstConstant, const UINT* pNumConstants) { D3D10DeviceLock lock = LockContext(); SetConstantBuffers1( StartSlot, NumBuffers, ppConstantBuffers, pFirstConstant, pNumConstants); } template void STDMETHODCALLTYPE D3D11CommonContext::DSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { D3D10DeviceLock lock = LockContext(); SetShaderResources( StartSlot, NumViews, ppShaderResourceViews); } template void STDMETHODCALLTYPE D3D11CommonContext::DSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { D3D10DeviceLock lock = LockContext(); SetSamplers( StartSlot, NumSamplers, ppSamplers); } template void STDMETHODCALLTYPE D3D11CommonContext::DSGetShader( ID3D11DomainShader** ppDomainShader, ID3D11ClassInstance** ppClassInstances, UINT* pNumClassInstances) { D3D10DeviceLock lock = LockContext(); if (ppDomainShader) *ppDomainShader = m_state.ds.ref(); if (pNumClassInstances) *pNumClassInstances = 0; } template void STDMETHODCALLTYPE D3D11CommonContext::DSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers) { D3D10DeviceLock lock = LockContext(); GetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers, nullptr, nullptr); } template void STDMETHODCALLTYPE D3D11CommonContext::DSGetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers, UINT* pFirstConstant, UINT* pNumConstants) { D3D10DeviceLock lock = LockContext(); GetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers, pFirstConstant, pNumConstants); } template void STDMETHODCALLTYPE D3D11CommonContext::DSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView** ppShaderResourceViews) { D3D10DeviceLock lock = LockContext(); GetShaderResources( StartSlot, NumViews, ppShaderResourceViews); } template void STDMETHODCALLTYPE D3D11CommonContext::DSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState** ppSamplers) { D3D10DeviceLock lock = LockContext(); GetSamplers( StartSlot, NumSamplers, ppSamplers); } template void STDMETHODCALLTYPE D3D11CommonContext::GSSetShader( ID3D11GeometryShader* pShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { D3D10DeviceLock lock = LockContext(); auto shader = static_cast(pShader); if (NumClassInstances) Logger::err("D3D11: Class instances not supported"); if (m_state.gs != shader) { m_state.gs = shader; BindShader(GetCommonShader(shader)); } } template void STDMETHODCALLTYPE D3D11CommonContext::GSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { D3D10DeviceLock lock = LockContext(); SetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers); } template void STDMETHODCALLTYPE D3D11CommonContext::GSSetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers, const UINT* pFirstConstant, const UINT* pNumConstants) { D3D10DeviceLock lock = LockContext(); SetConstantBuffers1( StartSlot, NumBuffers, ppConstantBuffers, pFirstConstant, pNumConstants); } template void STDMETHODCALLTYPE D3D11CommonContext::GSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { D3D10DeviceLock lock = LockContext(); SetShaderResources( StartSlot, NumViews, ppShaderResourceViews); } template void STDMETHODCALLTYPE D3D11CommonContext::GSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { D3D10DeviceLock lock = LockContext(); SetSamplers( StartSlot, NumSamplers, ppSamplers); } template void STDMETHODCALLTYPE D3D11CommonContext::GSGetShader( ID3D11GeometryShader** ppGeometryShader, ID3D11ClassInstance** ppClassInstances, UINT* pNumClassInstances) { D3D10DeviceLock lock = LockContext(); if (ppGeometryShader) *ppGeometryShader = m_state.gs.ref(); if (pNumClassInstances) *pNumClassInstances = 0; } template void STDMETHODCALLTYPE D3D11CommonContext::GSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers) { D3D10DeviceLock lock = LockContext(); GetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers, nullptr, nullptr); } template void STDMETHODCALLTYPE D3D11CommonContext::GSGetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers, UINT* pFirstConstant, UINT* pNumConstants) { D3D10DeviceLock lock = LockContext(); GetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers, pFirstConstant, pNumConstants); } template void STDMETHODCALLTYPE D3D11CommonContext::GSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView** ppShaderResourceViews) { D3D10DeviceLock lock = LockContext(); GetShaderResources( StartSlot, NumViews, ppShaderResourceViews); } template void STDMETHODCALLTYPE D3D11CommonContext::GSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState** ppSamplers) { D3D10DeviceLock lock = LockContext(); GetSamplers( StartSlot, NumSamplers, ppSamplers); } template void STDMETHODCALLTYPE D3D11CommonContext::PSSetShader( ID3D11PixelShader* pPixelShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { D3D10DeviceLock lock = LockContext(); auto shader = static_cast(pPixelShader); if (NumClassInstances) Logger::err("D3D11: Class instances not supported"); if (m_state.ps != shader) { m_state.ps = shader; BindShader(GetCommonShader(shader)); } } template void STDMETHODCALLTYPE D3D11CommonContext::PSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { D3D10DeviceLock lock = LockContext(); SetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers); } template void STDMETHODCALLTYPE D3D11CommonContext::PSSetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers, const UINT* pFirstConstant, const UINT* pNumConstants) { D3D10DeviceLock lock = LockContext(); SetConstantBuffers1( StartSlot, NumBuffers, ppConstantBuffers, pFirstConstant, pNumConstants); } template void STDMETHODCALLTYPE D3D11CommonContext::PSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { D3D10DeviceLock lock = LockContext(); SetShaderResources( StartSlot, NumViews, ppShaderResourceViews); } template void STDMETHODCALLTYPE D3D11CommonContext::PSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { D3D10DeviceLock lock = LockContext(); SetSamplers( StartSlot, NumSamplers, ppSamplers); } template void STDMETHODCALLTYPE D3D11CommonContext::PSGetShader( ID3D11PixelShader** ppPixelShader, ID3D11ClassInstance** ppClassInstances, UINT* pNumClassInstances) { D3D10DeviceLock lock = LockContext(); if (ppPixelShader) *ppPixelShader = m_state.ps.ref(); if (pNumClassInstances) *pNumClassInstances = 0; } template void STDMETHODCALLTYPE D3D11CommonContext::PSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers) { D3D10DeviceLock lock = LockContext(); GetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers, nullptr, nullptr); } template void STDMETHODCALLTYPE D3D11CommonContext::PSGetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers, UINT* pFirstConstant, UINT* pNumConstants) { D3D10DeviceLock lock = LockContext(); GetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers, pFirstConstant, pNumConstants); } template void STDMETHODCALLTYPE D3D11CommonContext::PSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView** ppShaderResourceViews) { D3D10DeviceLock lock = LockContext(); GetShaderResources( StartSlot, NumViews, ppShaderResourceViews); } template void STDMETHODCALLTYPE D3D11CommonContext::PSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState** ppSamplers) { D3D10DeviceLock lock = LockContext(); GetSamplers( StartSlot, NumSamplers, ppSamplers); } template void STDMETHODCALLTYPE D3D11CommonContext::CSSetShader( ID3D11ComputeShader* pComputeShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { D3D10DeviceLock lock = LockContext(); auto shader = static_cast(pComputeShader); if (NumClassInstances) Logger::err("D3D11: Class instances not supported"); if (m_state.cs != shader) { m_state.cs = shader; BindShader(GetCommonShader(shader)); } } template void STDMETHODCALLTYPE D3D11CommonContext::CSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { D3D10DeviceLock lock = LockContext(); SetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers); } template void STDMETHODCALLTYPE D3D11CommonContext::CSSetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers, const UINT* pFirstConstant, const UINT* pNumConstants) { D3D10DeviceLock lock = LockContext(); SetConstantBuffers1( StartSlot, NumBuffers, ppConstantBuffers, pFirstConstant, pNumConstants); } template void STDMETHODCALLTYPE D3D11CommonContext::CSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { D3D10DeviceLock lock = LockContext(); SetShaderResources( StartSlot, NumViews, ppShaderResourceViews); } template void STDMETHODCALLTYPE D3D11CommonContext::CSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { D3D10DeviceLock lock = LockContext(); SetSamplers( StartSlot, NumSamplers, ppSamplers); } template void STDMETHODCALLTYPE D3D11CommonContext::CSSetUnorderedAccessViews( UINT StartSlot, UINT NumUAVs, ID3D11UnorderedAccessView* const* ppUnorderedAccessViews, const UINT* pUAVInitialCounts) { D3D10DeviceLock lock = LockContext(); if (TestRtvUavHazards(0, nullptr, NumUAVs, ppUnorderedAccessViews)) return; // Unbind previously bound conflicting UAVs int32_t uavId = m_state.uav.mask.findNext(0); while (uavId >= 0) { if (uint32_t(uavId) < StartSlot || uint32_t(uavId) >= StartSlot + NumUAVs) { for (uint32_t i = 0; i < NumUAVs; i++) { auto uav = static_cast(ppUnorderedAccessViews[i]); if (CheckViewOverlap(uav, m_state.uav.views[uavId].ptr())) { m_state.uav.views[uavId] = nullptr; m_state.uav.mask.clr(uavId); if (!DirtyComputeUnorderedAccessView(uavId, true)) BindUnorderedAccessView(DxbcProgramType::ComputeShader, uavId, nullptr); } } uavId = m_state.uav.mask.findNext(uavId + 1); } else { uavId = m_state.uav.mask.findNext(StartSlot + NumUAVs); } } // Actually bind the given UAVs for (uint32_t i = 0; i < NumUAVs; i++) { auto uav = static_cast(ppUnorderedAccessViews[i]); auto ctr = pUAVInitialCounts ? pUAVInitialCounts[i] : ~0u; if (ctr != ~0u && uav && uav->HasCounter()) UpdateUnorderedAccessViewCounter(uav, ctr); if (m_state.uav.views[StartSlot + i] != uav) { m_state.uav.views[StartSlot + i] = uav; m_state.uav.mask.set(StartSlot + i, uav != nullptr); if (!DirtyComputeUnorderedAccessView(StartSlot + i, !uav)) BindUnorderedAccessView(DxbcProgramType::ComputeShader, StartSlot + i, uav); ResolveCsSrvHazards(uav); } } m_state.uav.maxCount = std::clamp(StartSlot + NumUAVs, m_state.uav.maxCount, uint32_t(m_state.uav.views.size())); } template void STDMETHODCALLTYPE D3D11CommonContext::CSGetShader( ID3D11ComputeShader** ppComputeShader, ID3D11ClassInstance** ppClassInstances, UINT* pNumClassInstances) { D3D10DeviceLock lock = LockContext(); if (ppComputeShader) *ppComputeShader = m_state.cs.ref(); if (pNumClassInstances) *pNumClassInstances = 0; } template void STDMETHODCALLTYPE D3D11CommonContext::CSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers) { D3D10DeviceLock lock = LockContext(); GetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers, nullptr, nullptr); } template void STDMETHODCALLTYPE D3D11CommonContext::CSGetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers, UINT* pFirstConstant, UINT* pNumConstants) { D3D10DeviceLock lock = LockContext(); GetConstantBuffers( StartSlot, NumBuffers, ppConstantBuffers, pFirstConstant, pNumConstants); } template void STDMETHODCALLTYPE D3D11CommonContext::CSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView** ppShaderResourceViews) { D3D10DeviceLock lock = LockContext(); GetShaderResources( StartSlot, NumViews, ppShaderResourceViews); } template void STDMETHODCALLTYPE D3D11CommonContext::CSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState** ppSamplers) { D3D10DeviceLock lock = LockContext(); GetSamplers( StartSlot, NumSamplers, ppSamplers); } template void STDMETHODCALLTYPE D3D11CommonContext::CSGetUnorderedAccessViews( UINT StartSlot, UINT NumUAVs, ID3D11UnorderedAccessView** ppUnorderedAccessViews) { D3D10DeviceLock lock = LockContext(); for (uint32_t i = 0; i < NumUAVs; i++) { ppUnorderedAccessViews[i] = StartSlot + i < m_state.uav.views.size() ? m_state.uav.views[StartSlot + i].ref() : nullptr; } } template void STDMETHODCALLTYPE D3D11CommonContext::OMSetRenderTargets( UINT NumViews, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView* pDepthStencilView) { D3D10DeviceLock lock = LockContext(); SetRenderTargetsAndUnorderedAccessViews( NumViews, ppRenderTargetViews, pDepthStencilView, NumViews, 0, nullptr, nullptr); } template void STDMETHODCALLTYPE D3D11CommonContext::OMSetRenderTargetsAndUnorderedAccessViews( UINT NumRTVs, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView* pDepthStencilView, UINT UAVStartSlot, UINT NumUAVs, ID3D11UnorderedAccessView* const* ppUnorderedAccessViews, const UINT* pUAVInitialCounts) { D3D10DeviceLock lock = LockContext(); SetRenderTargetsAndUnorderedAccessViews( NumRTVs, ppRenderTargetViews, pDepthStencilView, UAVStartSlot, NumUAVs, ppUnorderedAccessViews, pUAVInitialCounts); } template void STDMETHODCALLTYPE D3D11CommonContext::OMSetBlendState( ID3D11BlendState* pBlendState, const FLOAT BlendFactor[4], UINT SampleMask) { D3D10DeviceLock lock = LockContext(); auto blendState = static_cast(pBlendState); if (m_state.om.cbState != blendState || m_state.om.sampleMask != SampleMask) { m_state.om.cbState = blendState; m_state.om.sampleMask = SampleMask; ApplyBlendState(); } if (BlendFactor != nullptr) { for (uint32_t i = 0; i < 4; i++) m_state.om.blendFactor[i] = BlendFactor[i]; ApplyBlendFactor(); } } template void STDMETHODCALLTYPE D3D11CommonContext::OMSetDepthStencilState( ID3D11DepthStencilState* pDepthStencilState, UINT StencilRef) { D3D10DeviceLock lock = LockContext(); auto depthStencilState = static_cast(pDepthStencilState); if (m_state.om.dsState != depthStencilState) { m_state.om.dsState = depthStencilState; ApplyDepthStencilState(); } // The D3D11 runtime only appears to store the low 8 bits, // and some games rely on this behaviour. Do the same here. StencilRef &= 0xFF; if (m_state.om.stencilRef != StencilRef) { m_state.om.stencilRef = StencilRef; ApplyStencilRef(); } } template void STDMETHODCALLTYPE D3D11CommonContext::OMGetRenderTargets( UINT NumViews, ID3D11RenderTargetView** ppRenderTargetViews, ID3D11DepthStencilView** ppDepthStencilView) { OMGetRenderTargetsAndUnorderedAccessViews( NumViews, ppRenderTargetViews, ppDepthStencilView, NumViews, 0, nullptr); } template void STDMETHODCALLTYPE D3D11CommonContext::OMGetRenderTargetsAndUnorderedAccessViews( UINT NumRTVs, ID3D11RenderTargetView** ppRenderTargetViews, ID3D11DepthStencilView** ppDepthStencilView, UINT UAVStartSlot, UINT NumUAVs, ID3D11UnorderedAccessView** ppUnorderedAccessViews) { D3D10DeviceLock lock = LockContext(); if (ppRenderTargetViews) { for (UINT i = 0; i < NumRTVs; i++) { ppRenderTargetViews[i] = i < m_state.om.rtvs.size() ? m_state.om.rtvs[i].ref() : nullptr; } } if (ppDepthStencilView) *ppDepthStencilView = m_state.om.dsv.ref(); if (ppUnorderedAccessViews) { for (UINT i = 0; i < NumUAVs; i++) { ppUnorderedAccessViews[i] = UAVStartSlot + i < m_state.om.uavs.size() ? m_state.om.uavs[UAVStartSlot + i].ref() : nullptr; } } } template void STDMETHODCALLTYPE D3D11CommonContext::OMGetBlendState( ID3D11BlendState** ppBlendState, FLOAT BlendFactor[4], UINT* pSampleMask) { D3D10DeviceLock lock = LockContext(); if (ppBlendState) *ppBlendState = ref(m_state.om.cbState); if (BlendFactor) std::memcpy(BlendFactor, m_state.om.blendFactor, sizeof(FLOAT) * 4); if (pSampleMask) *pSampleMask = m_state.om.sampleMask; } template void STDMETHODCALLTYPE D3D11CommonContext::OMGetDepthStencilState( ID3D11DepthStencilState** ppDepthStencilState, UINT* pStencilRef) { D3D10DeviceLock lock = LockContext(); if (ppDepthStencilState) *ppDepthStencilState = ref(m_state.om.dsState); if (pStencilRef) *pStencilRef = m_state.om.stencilRef; } template void STDMETHODCALLTYPE D3D11CommonContext::RSSetState(ID3D11RasterizerState* pRasterizerState) { D3D10DeviceLock lock = LockContext(); auto currRasterizerState = m_state.rs.state; auto nextRasterizerState = static_cast(pRasterizerState); if (m_state.rs.state != nextRasterizerState) { m_state.rs.state = nextRasterizerState; ApplyRasterizerState(); // If necessary, update the rasterizer sample count push constant uint32_t currSampleCount = currRasterizerState != nullptr ? currRasterizerState->Desc()->ForcedSampleCount : 0; uint32_t nextSampleCount = nextRasterizerState != nullptr ? nextRasterizerState->Desc()->ForcedSampleCount : 0; if (currSampleCount != nextSampleCount) ApplyRasterizerSampleCount(); // In D3D11, the rasterizer state defines whether the scissor test is // enabled, so if that changes, we need to update scissor rects as well. bool currScissorEnable = currRasterizerState && currRasterizerState->Desc()->ScissorEnable; bool nextScissorEnable = nextRasterizerState && nextRasterizerState->Desc()->ScissorEnable; if (currScissorEnable != nextScissorEnable) ApplyViewportState(); } } template void STDMETHODCALLTYPE D3D11CommonContext::RSSetViewports( UINT NumViewports, const D3D11_VIEWPORT* pViewports) { D3D10DeviceLock lock = LockContext(); if (unlikely(NumViewports > m_state.rs.viewports.size())) return; for (uint32_t i = 0; i < NumViewports; i++) { const D3D11_VIEWPORT& vp = pViewports[i]; bool valid = vp.Width >= 0.0f && vp.Height >= 0.0f && vp.MinDepth >= 0.0f && vp.MaxDepth <= 1.0f && vp.MinDepth <= vp.MaxDepth; if (!valid) return; } bool dirty = m_state.rs.numViewports != NumViewports; m_state.rs.numViewports = NumViewports; for (uint32_t i = 0; i < NumViewports; i++) { const D3D11_VIEWPORT& vp = m_state.rs.viewports[i]; dirty |= vp.TopLeftX != pViewports[i].TopLeftX || vp.TopLeftY != pViewports[i].TopLeftY || vp.Width != pViewports[i].Width || vp.Height != pViewports[i].Height || vp.MinDepth != pViewports[i].MinDepth || vp.MaxDepth != pViewports[i].MaxDepth; m_state.rs.viewports[i] = pViewports[i]; } if (dirty) ApplyViewportState(); } template void STDMETHODCALLTYPE D3D11CommonContext::RSSetScissorRects( UINT NumRects, const D3D11_RECT* pRects) { D3D10DeviceLock lock = LockContext(); if (unlikely(NumRects > m_state.rs.scissors.size())) return; bool dirty = m_state.rs.numScissors != NumRects; m_state.rs.numScissors = NumRects; for (uint32_t i = 0; i < NumRects; i++) { if (pRects[i].bottom >= pRects[i].top && pRects[i].right >= pRects[i].left) { const D3D11_RECT& sr = m_state.rs.scissors[i]; dirty |= sr.top != pRects[i].top || sr.left != pRects[i].left || sr.bottom != pRects[i].bottom || sr.right != pRects[i].right; m_state.rs.scissors[i] = pRects[i]; } } if (dirty && m_state.rs.state && m_state.rs.state->Desc()->ScissorEnable) ApplyViewportState(); } template void STDMETHODCALLTYPE D3D11CommonContext::RSGetState(ID3D11RasterizerState** ppRasterizerState) { D3D10DeviceLock lock = LockContext(); if (ppRasterizerState) *ppRasterizerState = ref(m_state.rs.state); } template void STDMETHODCALLTYPE D3D11CommonContext::RSGetViewports( UINT* pNumViewports, D3D11_VIEWPORT* pViewports) { D3D10DeviceLock lock = LockContext(); uint32_t numWritten = m_state.rs.numViewports; if (pViewports) { numWritten = std::min(numWritten, *pNumViewports); for (uint32_t i = 0; i < *pNumViewports; i++) { if (i < m_state.rs.numViewports) { pViewports[i] = m_state.rs.viewports[i]; } else { pViewports[i].TopLeftX = 0.0f; pViewports[i].TopLeftY = 0.0f; pViewports[i].Width = 0.0f; pViewports[i].Height = 0.0f; pViewports[i].MinDepth = 0.0f; pViewports[i].MaxDepth = 0.0f; } } } *pNumViewports = numWritten; } template void STDMETHODCALLTYPE D3D11CommonContext::RSGetScissorRects( UINT* pNumRects, D3D11_RECT* pRects) { D3D10DeviceLock lock = LockContext(); uint32_t numWritten = m_state.rs.numScissors; if (pRects) { numWritten = std::min(numWritten, *pNumRects); for (uint32_t i = 0; i < *pNumRects; i++) { if (i < m_state.rs.numScissors) { pRects[i] = m_state.rs.scissors[i]; } else { pRects[i].left = 0; pRects[i].top = 0; pRects[i].right = 0; pRects[i].bottom = 0; } } } *pNumRects = m_state.rs.numScissors; } template void STDMETHODCALLTYPE D3D11CommonContext::SOSetTargets( UINT NumBuffers, ID3D11Buffer* const* ppSOTargets, const UINT* pOffsets) { D3D10DeviceLock lock = LockContext(); for (uint32_t i = 0; i < NumBuffers; i++) { D3D11Buffer* buffer = static_cast(ppSOTargets[i]); UINT offset = pOffsets != nullptr ? pOffsets[i] : 0; m_state.so.targets[i].buffer = buffer; m_state.so.targets[i].offset = offset; } for (uint32_t i = NumBuffers; i < D3D11_SO_BUFFER_SLOT_COUNT; i++) { m_state.so.targets[i].buffer = nullptr; m_state.so.targets[i].offset = 0; } for (uint32_t i = 0; i < D3D11_SO_BUFFER_SLOT_COUNT; i++) { BindXfbBuffer(i, m_state.so.targets[i].buffer.ptr(), m_state.so.targets[i].offset); } } template void STDMETHODCALLTYPE D3D11CommonContext::SOGetTargets( UINT NumBuffers, ID3D11Buffer** ppSOTargets) { D3D10DeviceLock lock = LockContext(); for (uint32_t i = 0; i < NumBuffers; i++) { ppSOTargets[i] = i < m_state.so.targets.size() ? m_state.so.targets[i].buffer.ref() : nullptr; } } template void STDMETHODCALLTYPE D3D11CommonContext::SOGetTargetsWithOffsets( UINT NumBuffers, ID3D11Buffer** ppSOTargets, UINT* pOffsets) { D3D10DeviceLock lock = LockContext(); for (uint32_t i = 0; i < NumBuffers; i++) { const bool inRange = i < m_state.so.targets.size(); if (ppSOTargets) { ppSOTargets[i] = inRange ? m_state.so.targets[i].buffer.ref() : nullptr; } if (pOffsets) { pOffsets[i] = inRange ? m_state.so.targets[i].offset : 0u; } } } template void STDMETHODCALLTYPE D3D11CommonContext::SetPredication( ID3D11Predicate* pPredicate, BOOL PredicateValue) { D3D10DeviceLock lock = LockContext(); auto predicate = D3D11Query::FromPredicate(pPredicate); m_state.pr.predicateObject = predicate; m_state.pr.predicateValue = PredicateValue; static bool s_errorShown = false; if (pPredicate && !std::exchange(s_errorShown, true)) Logger::err("D3D11DeviceContext::SetPredication: Stub"); } template void STDMETHODCALLTYPE D3D11CommonContext::GetPredication( ID3D11Predicate** ppPredicate, BOOL* pPredicateValue) { D3D10DeviceLock lock = LockContext(); if (ppPredicate) *ppPredicate = D3D11Query::AsPredicate(m_state.pr.predicateObject.ref()); if (pPredicateValue) *pPredicateValue = m_state.pr.predicateValue; } template void STDMETHODCALLTYPE D3D11CommonContext::SetResourceMinLOD( ID3D11Resource* pResource, FLOAT MinLOD) { bool s_errorShown = false; if (std::exchange(s_errorShown, true)) Logger::err("D3D11DeviceContext::SetResourceMinLOD: Not implemented"); } template FLOAT STDMETHODCALLTYPE D3D11CommonContext::GetResourceMinLOD(ID3D11Resource* pResource) { bool s_errorShown = false; if (std::exchange(s_errorShown, true)) Logger::err("D3D11DeviceContext::GetResourceMinLOD: Not implemented"); return 0.0f; } template void STDMETHODCALLTYPE D3D11CommonContext::CopyTiles( ID3D11Resource* pTiledResource, const D3D11_TILED_RESOURCE_COORDINATE* pTileRegionStartCoordinate, const D3D11_TILE_REGION_SIZE* pTileRegionSize, ID3D11Buffer* pBuffer, UINT64 BufferStartOffsetInBytes, UINT Flags) { D3D10DeviceLock lock = LockContext(); if (!pTiledResource || !pBuffer) return; auto buffer = static_cast(pBuffer); // Get buffer slice and just forward the call VkDeviceSize bufferSize = pTileRegionSize->NumTiles * SparseMemoryPageSize; if (buffer->Desc()->ByteWidth < BufferStartOffsetInBytes + bufferSize) return; DxvkBufferSlice slice = buffer->GetBufferSlice(BufferStartOffsetInBytes, bufferSize); CopyTiledResourceData(pTiledResource, pTileRegionStartCoordinate, pTileRegionSize, slice, Flags); if (buffer->HasSequenceNumber()) GetTypedContext()->TrackBufferSequenceNumber(buffer); } template HRESULT STDMETHODCALLTYPE D3D11CommonContext::CopyTileMappings( ID3D11Resource* pDestTiledResource, const D3D11_TILED_RESOURCE_COORDINATE* pDestRegionCoordinate, ID3D11Resource* pSourceTiledResource, const D3D11_TILED_RESOURCE_COORDINATE* pSourceRegionCoordinate, const D3D11_TILE_REGION_SIZE* pTileRegionSize, UINT Flags) { D3D10DeviceLock lock = LockContext(); if (!pDestTiledResource || !pSourceTiledResource) return E_INVALIDARG; if constexpr (!IsDeferred) GetTypedContext()->ConsiderFlush(GpuFlushType::ImplicitWeakHint); DxvkSparseBindInfo bindInfo; bindInfo.dstResource = GetPagedResource(pDestTiledResource); bindInfo.srcResource = GetPagedResource(pSourceTiledResource); auto dstPageTable = bindInfo.dstResource->getSparsePageTable(); auto srcPageTable = bindInfo.srcResource->getSparsePageTable(); if (!dstPageTable || !srcPageTable) return E_INVALIDARG; if (pDestRegionCoordinate->Subresource >= dstPageTable->getSubresourceCount() || pSourceRegionCoordinate->Subresource >= srcPageTable->getSubresourceCount()) return E_INVALIDARG; VkOffset3D dstRegionOffset = { int32_t(pDestRegionCoordinate->X), int32_t(pDestRegionCoordinate->Y), int32_t(pDestRegionCoordinate->Z) }; VkOffset3D srcRegionOffset = { int32_t(pSourceRegionCoordinate->X), int32_t(pSourceRegionCoordinate->Y), int32_t(pSourceRegionCoordinate->Z) }; VkExtent3D regionExtent = { uint32_t(pTileRegionSize->Width), uint32_t(pTileRegionSize->Height), uint32_t(pTileRegionSize->Depth) }; for (uint32_t i = 0; i < pTileRegionSize->NumTiles; i++) { // We don't know the current tile mappings of either resource since // this may be called on a deferred context and tile mappings are // updated on the CS thread, so just resolve the copy in the backend uint32_t dstTile = dstPageTable->computePageIndex( pDestRegionCoordinate->Subresource, dstRegionOffset, regionExtent, !pTileRegionSize->bUseBox, i); uint32_t srcTile = srcPageTable->computePageIndex( pSourceRegionCoordinate->Subresource, srcRegionOffset, regionExtent, !pTileRegionSize->bUseBox, i); if (dstTile >= dstPageTable->getPageCount() || srcTile >= srcPageTable->getPageCount()) return E_INVALIDARG; DxvkSparseBind bind; bind.mode = DxvkSparseBindMode::Copy; bind.dstPage = dstTile; bind.srcPage = srcTile; bindInfo.binds.push_back(bind); } DxvkSparseBindFlags flags = (Flags & D3D11_TILE_MAPPING_NO_OVERWRITE) ? DxvkSparseBindFlags(DxvkSparseBindFlag::SkipSynchronization) : DxvkSparseBindFlags(); EmitCs([ cBindInfo = std::move(bindInfo), cFlags = flags ] (DxvkContext* ctx) { ctx->updatePageTable(cBindInfo, cFlags); }); return S_OK; } template HRESULT STDMETHODCALLTYPE D3D11CommonContext::ResizeTilePool( ID3D11Buffer* pTilePool, UINT64 NewSizeInBytes) { D3D10DeviceLock lock = LockContext(); if (NewSizeInBytes % SparseMemoryPageSize) return E_INVALIDARG; auto buffer = static_cast(pTilePool); if (!buffer->IsTilePool()) return E_INVALIDARG; // Perform the resize operation. This is somewhat trivialized // since all lifetime tracking is done by the backend. EmitCs([ cAllocator = buffer->GetSparseAllocator(), cPageCount = NewSizeInBytes / SparseMemoryPageSize ] (DxvkContext* ctx) { cAllocator->setCapacity(cPageCount); }); return S_OK; } template void STDMETHODCALLTYPE D3D11CommonContext::TiledResourceBarrier( ID3D11DeviceChild* pTiledResourceOrViewAccessBeforeBarrier, ID3D11DeviceChild* pTiledResourceOrViewAccessAfterBarrier) { D3D10DeviceLock lock = LockContext(); DxvkGlobalPipelineBarrier srcBarrier = GetTiledResourceDependency(pTiledResourceOrViewAccessBeforeBarrier); DxvkGlobalPipelineBarrier dstBarrier = GetTiledResourceDependency(pTiledResourceOrViewAccessAfterBarrier); if (srcBarrier.stages && dstBarrier.stages) { EmitCs([ cSrcBarrier = srcBarrier, cDstBarrier = dstBarrier ] (DxvkContext* ctx) { ctx->emitGraphicsBarrier( cSrcBarrier.stages, cSrcBarrier.access, cDstBarrier.stages, cDstBarrier.access); }); } } template HRESULT STDMETHODCALLTYPE D3D11CommonContext::UpdateTileMappings( ID3D11Resource* pTiledResource, UINT NumRegions, const D3D11_TILED_RESOURCE_COORDINATE* pRegionCoordinates, const D3D11_TILE_REGION_SIZE* pRegionSizes, ID3D11Buffer* pTilePool, UINT NumRanges, const UINT* pRangeFlags, const UINT* pRangeTileOffsets, const UINT* pRangeTileCounts, UINT Flags) { D3D10DeviceLock lock = LockContext(); if (!pTiledResource || !NumRegions || !NumRanges) return E_INVALIDARG; if constexpr (!IsDeferred) GetTypedContext()->ConsiderFlush(GpuFlushType::ImplicitWeakHint); // Find sparse allocator if the tile pool is defined DxvkSparseBindInfo bindInfo; if (pTilePool) { auto tilePool = static_cast(pTilePool); bindInfo.srcAllocator = tilePool->GetSparseAllocator(); if (bindInfo.srcAllocator == nullptr) return E_INVALIDARG; } // Find resource and sparse page table for the given resource bindInfo.dstResource = GetPagedResource(pTiledResource); auto pageTable = bindInfo.dstResource->getSparsePageTable(); if (!pageTable) return E_INVALIDARG; // Lookup table in case the app tries to bind the same // page multiple times. We should resolve that here and // only consider the last bind to any given page. std::vector bindIndices(pageTable->getPageCount(), ~0u); // This function allows pretty much every parameter to be nullptr // in some way, so initialize some defaults as necessary D3D11_TILED_RESOURCE_COORDINATE regionCoord = { }; D3D11_TILE_REGION_SIZE regionSize = { }; if (!pRegionSizes) { regionSize.NumTiles = pRegionCoordinates ? 1 : pageTable->getPageCount(); } uint32_t rangeFlag = 0u; uint32_t rangeTileOffset = 0u; uint32_t rangeTileCount = ~0u; // For now, just generate a simple list of tile index to // page index mappings, and let the backend optimize later uint32_t regionIdx = 0u; uint32_t regionTile = 0u; uint32_t rangeIdx = 0u; uint32_t rangeTile = 0u; while (regionIdx < NumRegions && rangeIdx < NumRanges) { if (!regionTile) { if (pRegionCoordinates) regionCoord = pRegionCoordinates[regionIdx]; if (pRegionSizes) regionSize = pRegionSizes[regionIdx]; } if (!rangeTile) { if (pRangeFlags) rangeFlag = pRangeFlags[rangeIdx]; if (pRangeTileOffsets) rangeTileOffset = pRangeTileOffsets[rangeIdx]; if (pRangeTileCounts) rangeTileCount = pRangeTileCounts[rangeIdx]; } if (!(rangeFlag & D3D11_TILE_RANGE_SKIP)) { if (regionCoord.Subresource >= pageTable->getSubresourceCount()) return E_INVALIDARG; if (regionSize.bUseBox && regionSize.NumTiles != regionSize.Width * regionSize.Height * regionSize.Depth) return E_INVALIDARG; VkOffset3D regionOffset = { int32_t(regionCoord.X), int32_t(regionCoord.Y), int32_t(regionCoord.Z) }; VkExtent3D regionExtent = { uint32_t(regionSize.Width), uint32_t(regionSize.Height), uint32_t(regionSize.Depth) }; uint32_t resourceTile = pageTable->computePageIndex(regionCoord.Subresource, regionOffset, regionExtent, !regionSize.bUseBox, regionTile); // Fill in bind info for the current tile DxvkSparseBind bind = { }; bind.dstPage = resourceTile; if (rangeFlag & D3D11_TILE_RANGE_NULL) { bind.mode = DxvkSparseBindMode::Null; } else if (pTilePool) { bind.mode = DxvkSparseBindMode::Bind; bind.srcPage = rangeFlag & D3D11_TILE_RANGE_REUSE_SINGLE_TILE ? rangeTileOffset : rangeTileOffset + rangeTile; } else { return E_INVALIDARG; } // Add bind info to the bind list, overriding // any existing bind for the same resource page if (resourceTile < pageTable->getPageCount()) { if (bindIndices[resourceTile] < bindInfo.binds.size()) bindInfo.binds[bindIndices[resourceTile]] = bind; else bindInfo.binds.push_back(bind); } } if (++regionTile == regionSize.NumTiles) { regionTile = 0; regionIdx += 1; } if (++rangeTile == rangeTileCount) { rangeTile = 0; rangeIdx += 1; } } // Translate flags. The backend benefits from NO_OVERWRITE since // otherwise we have to serialize execution of the current command // buffer, the sparse binding operation, and subsequent commands. // With NO_OVERWRITE, we can execute it more or less asynchronously. DxvkSparseBindFlags flags = (Flags & D3D11_TILE_MAPPING_NO_OVERWRITE) ? DxvkSparseBindFlags(DxvkSparseBindFlag::SkipSynchronization) : DxvkSparseBindFlags(); EmitCs([ cBindInfo = std::move(bindInfo), cFlags = flags ] (DxvkContext* ctx) { ctx->updatePageTable(cBindInfo, cFlags); }); return S_OK; } template void STDMETHODCALLTYPE D3D11CommonContext::UpdateTiles( ID3D11Resource* pDestTiledResource, const D3D11_TILED_RESOURCE_COORDINATE* pDestTileRegionStartCoordinate, const D3D11_TILE_REGION_SIZE* pDestTileRegionSize, const void* pSourceTileData, UINT Flags) { D3D10DeviceLock lock = LockContext(); if (!pDestTiledResource || !pSourceTileData) return; // Allocate staging memory and copy source data into it, at a // 64k page granularity. It is not clear whether this behaviour // is correct in case we're writing to incmplete pages. VkDeviceSize bufferSize = pDestTileRegionSize->NumTiles * SparseMemoryPageSize; DxvkBufferSlice slice = AllocStagingBuffer(bufferSize); std::memcpy(slice.mapPtr(0), pSourceTileData, bufferSize); // Fix up flags. The runtime probably validates this in some // way but our internal function relies on correct flags anyway. Flags &= D3D11_TILE_MAPPING_NO_OVERWRITE; Flags |= D3D11_TILE_COPY_LINEAR_BUFFER_TO_SWIZZLED_TILED_RESOURCE; CopyTiledResourceData(pDestTiledResource, pDestTileRegionStartCoordinate, pDestTileRegionSize, slice, Flags); if constexpr (!IsDeferred) static_cast(this)->ThrottleAllocation(); } template BOOL STDMETHODCALLTYPE D3D11CommonContext::IsAnnotationEnabled() { return m_annotation.GetStatus(); } template void STDMETHODCALLTYPE D3D11CommonContext::SetMarkerInt( LPCWSTR pLabel, INT Data) { // Not implemented in the backend, ignore } template void STDMETHODCALLTYPE D3D11CommonContext::BeginEventInt( LPCWSTR pLabel, INT Data) { // Not implemented in the backend, ignore } template void STDMETHODCALLTYPE D3D11CommonContext::EndEvent() { // Not implemented in the backend, ignore } template void STDMETHODCALLTYPE D3D11CommonContext::GetHardwareProtectionState( BOOL* pHwProtectionEnable) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::err("D3D11DeviceContext::GetHardwareProtectionState: Not implemented"); if (pHwProtectionEnable) *pHwProtectionEnable = FALSE; } template void STDMETHODCALLTYPE D3D11CommonContext::SetHardwareProtectionState( BOOL HwProtectionEnable) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::err("D3D11DeviceContext::SetHardwareProtectionState: Not implemented"); } template void STDMETHODCALLTYPE D3D11CommonContext::TransitionSurfaceLayout( IDXGIVkInteropSurface* pSurface, const VkImageSubresourceRange* pSubresources, VkImageLayout OldLayout, VkImageLayout NewLayout) { D3D10DeviceLock lock = LockContext(); // Get the underlying D3D11 resource Com resource; pSurface->QueryInterface(__uuidof(ID3D11Resource), reinterpret_cast(&resource)); // Get the texture from that resource D3D11CommonTexture* texture = GetCommonTexture(resource.ptr()); EmitCs([ cImage = texture->GetImage(), cSubresources = *pSubresources, cOldLayout = OldLayout, cNewLayout = NewLayout ] (DxvkContext* ctx) { ctx->transformImage( cImage, cSubresources, cOldLayout, cNewLayout); }); } template DxvkCsChunkRef D3D11CommonContext::AllocCsChunk() { return m_parent->AllocCsChunk(m_csFlags); } template DxvkBufferSlice D3D11CommonContext::AllocStagingBuffer( VkDeviceSize Size) { return m_staging.alloc(Size); } template void D3D11CommonContext::ApplyDirtyConstantBuffers( DxbcProgramType Stage, const DxbcBindingMask& BoundMask, DxbcBindingMask& DirtyMask) { uint32_t bindMask = BoundMask.cbvMask & DirtyMask.cbvMask; if (!bindMask) return; // Need to clear dirty bits before binding const auto& state = m_state.cbv[Stage]; DirtyMask.cbvMask -= bindMask; for (uint32_t slot : bit::BitMask(bindMask)) { const auto& cbv = state.buffers[slot]; BindConstantBuffer(Stage, slot, cbv.buffer.ptr(), cbv.constantOffset, cbv.constantBound); } } template void D3D11CommonContext::ApplyDirtySamplers( DxbcProgramType Stage, const DxbcBindingMask& BoundMask, DxbcBindingMask& DirtyMask) { uint32_t bindMask = BoundMask.samplerMask & DirtyMask.samplerMask; if (!bindMask) return; // Need to clear dirty bits before binding const auto& state = m_state.samplers[Stage]; DirtyMask.samplerMask -= bindMask; for (uint32_t slot : bit::BitMask(bindMask)) BindSampler(Stage, slot, state.samplers[slot]); } template void D3D11CommonContext::ApplyDirtyShaderResources( DxbcProgramType Stage, const DxbcBindingMask& BoundMask, DxbcBindingMask& DirtyMask) { const auto& state = m_state.srv[Stage]; for (uint32_t i = 0; i < state.maxCount; i += 64u) { uint32_t maskIndex = i / 64u; uint64_t bindMask = BoundMask.srvMask[maskIndex] & DirtyMask.srvMask[maskIndex]; if (!bindMask) continue; // Need to clear dirty bits before binding DirtyMask.srvMask[maskIndex] -= bindMask; for (uint32_t slot : bit::BitMask(bindMask)) BindShaderResource(Stage, slot + i, state.views[slot + i].ptr()); } } template void D3D11CommonContext::ApplyDirtyUnorderedAccessViews( DxbcProgramType Stage, const DxbcBindingMask& BoundMask, DxbcBindingMask& DirtyMask) { uint64_t bindMask = BoundMask.uavMask & DirtyMask.uavMask; if (!bindMask) return; const auto& views = Stage == DxbcProgramType::ComputeShader ? m_state.uav.views : m_state.om.uavs; // Need to clear dirty bits before binding DirtyMask.uavMask -= bindMask; for (uint32_t slot : bit::BitMask(bindMask)) BindUnorderedAccessView(Stage, slot, views[slot].ptr()); } template void D3D11CommonContext::ApplyDirtyGraphicsBindings() { auto dirtyMask = m_state.lazy.shadersDirty & m_state.lazy.shadersUsed; dirtyMask.clr(DxbcProgramType::ComputeShader); if (unlikely(!(dirtyMask & m_state.lazy.graphicsUavShaders).isClear())) { DxbcProgramType stage = DxbcProgramType::PixelShader; auto& boundMask = m_state.lazy.bindingsUsed[stage]; auto& dirtyMask = m_state.lazy.bindingsDirty[stage]; ApplyDirtyUnorderedAccessViews(stage, boundMask, dirtyMask); } for (uint32_t stageIndex : bit::BitMask(uint32_t(dirtyMask.raw()))) { DxbcProgramType stage = DxbcProgramType(stageIndex); auto& boundMask = m_state.lazy.bindingsUsed[stage]; auto& dirtyMask = m_state.lazy.bindingsDirty[stage]; ApplyDirtySamplers(stage, boundMask, dirtyMask); ApplyDirtyConstantBuffers(stage, boundMask, dirtyMask); ApplyDirtyShaderResources(stage, boundMask, dirtyMask); m_state.lazy.shadersDirty.clr(stage); } } template void D3D11CommonContext::ApplyDirtyComputeBindings() { DxbcProgramType stage = DxbcProgramType::ComputeShader; auto& boundMask = m_state.lazy.bindingsUsed[stage]; auto& dirtyMask = m_state.lazy.bindingsDirty[stage]; ApplyDirtySamplers(stage, boundMask, dirtyMask); ApplyDirtyConstantBuffers(stage, boundMask, dirtyMask); ApplyDirtyShaderResources(stage, boundMask, dirtyMask); ApplyDirtyUnorderedAccessViews(stage, boundMask, dirtyMask); m_state.lazy.shadersDirty.clr(stage); } template void D3D11CommonContext::ApplyInputLayout() { if (likely(m_state.ia.inputLayout != nullptr)) { uint32_t attributeCount = m_state.ia.inputLayout->GetAttributeCount(); uint32_t bindingCount = m_state.ia.inputLayout->GetBindingCount(); EmitCsCmd(D3D11CmdType::None, attributeCount + bindingCount, [ cAttributeCount = attributeCount, cBindingCount = bindingCount ] (DxvkContext* ctx, const DxvkVertexInput* layout, size_t) { ctx->setInputLayout(cAttributeCount, &layout[0], cBindingCount, &layout[cAttributeCount]); }); for (uint32_t i = 0; i < attributeCount + bindingCount; i++) new (m_csData->at(i)) DxvkVertexInput(m_state.ia.inputLayout->GetInput(i)); } else { EmitCs([] (DxvkContext* ctx) { ctx->setInputLayout(0, nullptr, 0, nullptr); }); } } template void D3D11CommonContext::ApplyPrimitiveTopology() { D3D11_PRIMITIVE_TOPOLOGY topology = m_state.ia.primitiveTopology; DxvkInputAssemblyState iaState(VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, false); if (topology <= D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ) { static const std::array s_iaStates = {{ DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, false), // Regular topologies DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_POINT_LIST, false), DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_LINE_LIST, false), DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, true), DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, false), DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, true), // Gap. This includes triangle fan which isn't supported in D3D11 DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, false), DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, false), DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, false), DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, false), // Adjacency topologies DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY, false), DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY, true), DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY, false), DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY, true), }}; iaState = s_iaStates[uint32_t(topology)]; } else if (topology >= D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST && topology <= D3D11_PRIMITIVE_TOPOLOGY_32_CONTROL_POINT_PATCHLIST) { // The number of control points per patch can be inferred from the enum value in D3D11 iaState = DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, false); iaState.setPatchVertexCount(topology - D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + 1); } EmitCs([iaState] (DxvkContext* ctx) { ctx->setInputAssemblyState(iaState); }); } template void D3D11CommonContext::ApplyBlendState() { if (m_state.om.cbState != nullptr) { EmitCs([ cBlendState = m_state.om.cbState->GetBlendState(), cMsState = m_state.om.cbState->GetMsState(m_state.om.sampleMask), cLoState = m_state.om.cbState->GetLoState() ] (DxvkContext* ctx) { for (uint32_t i = 0; i < cBlendState.size(); i++) ctx->setBlendMode(i, cBlendState[i]); ctx->setMultisampleState(cMsState); ctx->setLogicOpState(cLoState); }); } else { EmitCs([ cSampleMask = m_state.om.sampleMask ] (DxvkContext* ctx) { DxvkBlendMode cbState = InitDefaultBlendState(); for (uint32_t i = 0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++) ctx->setBlendMode(i, cbState); ctx->setMultisampleState(InitDefaultMultisampleState(cSampleMask)); ctx->setLogicOpState(InitDefaultLogicOpState()); }); } } template void D3D11CommonContext::ApplyBlendFactor() { EmitCs([ cBlendConstants = DxvkBlendConstants { m_state.om.blendFactor[0], m_state.om.blendFactor[1], m_state.om.blendFactor[2], m_state.om.blendFactor[3] } ] (DxvkContext* ctx) { ctx->setBlendConstants(cBlendConstants); }); } template void D3D11CommonContext::ApplyDepthStencilState() { if (m_state.om.dsState != nullptr) { EmitCs([ cState = m_state.om.dsState->GetState() ] (DxvkContext* ctx) { ctx->setDepthStencilState(cState); }); } else { EmitCs([] (DxvkContext* ctx) { ctx->setDepthStencilState(InitDefaultDepthStencilState()); }); } } template void D3D11CommonContext::ApplyStencilRef() { EmitCs([ cStencilRef = m_state.om.stencilRef ] (DxvkContext* ctx) { ctx->setStencilReference(cStencilRef); }); } template void D3D11CommonContext::ApplyRasterizerState() { if (m_state.rs.state != nullptr) { EmitCs([ cState = m_state.rs.state->GetState(), cDepthBias = m_state.rs.state->GetDepthBias() ] (DxvkContext* ctx) { ctx->setRasterizerState(cState); if (cState.depthBias()) ctx->setDepthBias(cDepthBias); }); } else { EmitCs([] (DxvkContext* ctx) { ctx->setRasterizerState(InitDefaultRasterizerState()); }); } } template void D3D11CommonContext::ApplyRasterizerSampleCount() { DxbcPushConstants pc; pc.rasterizerSampleCount = m_state.om.sampleCount; if (unlikely(!m_state.om.sampleCount)) { pc.rasterizerSampleCount = m_state.rs.state ? m_state.rs.state->Desc()->ForcedSampleCount : 0; if (!pc.rasterizerSampleCount) pc.rasterizerSampleCount = 1; } EmitCs([ cPushConstants = pc ] (DxvkContext* ctx) { ctx->pushConstants(0, sizeof(cPushConstants), &cPushConstants); }); } template void D3D11CommonContext::ApplyViewportState() { uint32_t viewportCount = m_state.rs.numViewports; if (likely(viewportCount)) { EmitCsCmd(D3D11CmdType::None, viewportCount, [] (DxvkContext* ctx, const DxvkViewport* viewports, size_t count) { ctx->setViewports(count, viewports); }); // Vulkan does not provide an easy way to disable the scissor test, // Set scissor rects that are at least as large as the framebuffer. bool enableScissorTest = m_state.rs.state && m_state.rs.state->Desc()->ScissorEnable; // D3D11's coordinate system has its origin in the bottom left, // but the viewport coordinates are aligned to the top-left // corner so we can get away with flipping the viewport. for (uint32_t i = 0; i < viewportCount; i++) { const auto& vp = m_state.rs.viewports[i]; auto* dst = new (m_csData->at(i)) DxvkViewport(); dst->viewport.x = vp.TopLeftX; dst->viewport.y = vp.Height + vp.TopLeftY; dst->viewport.width = vp.Width; dst->viewport.height = -vp.Height; dst->viewport.minDepth = vp.MinDepth; dst->viewport.maxDepth = vp.MaxDepth; if (!enableScissorTest) { dst->scissor.offset = VkOffset2D { 0, 0 }; dst->scissor.extent = VkExtent2D { D3D11_VIEWPORT_BOUNDS_MAX, D3D11_VIEWPORT_BOUNDS_MAX }; } else if (i < m_state.rs.numScissors) { const auto& sr = m_state.rs.scissors[i]; dst->scissor.offset = VkOffset2D { sr.left, sr.top }; dst->scissor.extent = VkExtent2D { uint32_t(std::max(sr.left, sr.right) - sr.left), uint32_t(std::max(sr.top, sr.bottom) - sr.top) }; } } } else { // The backend can't handle a viewport count of zero, // so we should at least specify one empty viewport EmitCs([] (DxvkContext* ctx) { DxvkViewport viewport = { }; ctx->setViewports(1, &viewport); }); } } template void D3D11CommonContext::BatchDraw( const VkDrawIndirectCommand& draw) { if (unlikely(HasDirtyGraphicsBindings())) ApplyDirtyGraphicsBindings(); // Batch consecutive draws if there are no state changes if (m_csDataType == D3D11CmdType::Draw) { auto* drawInfo = m_csChunk->pushData(m_csData, 1u); if (likely(drawInfo)) { new (drawInfo) VkDrawIndirectCommand(draw); return; } } EmitCsCmd(D3D11CmdType::Draw, 1u, [] (DxvkContext* ctx, const VkDrawIndirectCommand* draws, size_t count) { ctx->draw(count, draws); }); new (m_csData->first()) VkDrawIndirectCommand(draw); } template void D3D11CommonContext::BatchDrawIndexed( const VkDrawIndexedIndirectCommand& draw) { if (unlikely(HasDirtyGraphicsBindings())) ApplyDirtyGraphicsBindings(); // Batch consecutive draws if there are no state changes if (m_csDataType == D3D11CmdType::DrawIndexed) { auto* drawInfo = m_csChunk->pushData(m_csData, 1u); if (likely(drawInfo)) { new (drawInfo) VkDrawIndexedIndirectCommand(draw); return; } } EmitCsCmd(D3D11CmdType::DrawIndexed, 1u, [] (DxvkContext* ctx, const VkDrawIndexedIndirectCommand* draws, size_t count) { ctx->drawIndexed(count, draws); }); new (m_csData->first()) VkDrawIndexedIndirectCommand(draw); } template template void D3D11CommonContext::BindShader( const D3D11CommonShader* pShaderModule) { uint64_t oldUavMask = m_state.lazy.bindingsUsed[ShaderStage].uavMask; if (pShaderModule) { auto buffer = pShaderModule->GetIcb(); auto shader = pShaderModule->GetShader(); if (unlikely(shader->needsLibraryCompile())) m_device->requestCompileShader(shader); // If this shader activates any bindings that have not yet been applied, // mark the shader stage as dirty so it gets applied on the next draw. // Don't apply it right away since any dirty bindings are likely redundant. m_state.lazy.shadersUsed.set(ShaderStage); m_state.lazy.bindingsUsed[ShaderStage] = pShaderModule->GetBindingMask(); if (!m_state.lazy.shadersDirty.test(ShaderStage) && (DebugLazyBinding != Tristate::False)) { if (!(m_state.lazy.bindingsDirty[ShaderStage] & m_state.lazy.bindingsUsed[ShaderStage]).empty()) m_state.lazy.shadersDirty.set(ShaderStage); } EmitCs([ cBuffer = std::move(buffer), cShader = std::move(shader) ] (DxvkContext* ctx) mutable { constexpr VkShaderStageFlagBits stage = GetShaderStage(ShaderStage); uint32_t slotId = computeConstantBufferBinding(ShaderStage, D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT); ctx->bindShader( Forwarder::move(cShader)); ctx->bindUniformBuffer(stage, slotId, Forwarder::move(cBuffer)); }); } else { // Mark shader stage as inactive and clean since we'll have no active // bindings. This works because if the app changes any binding at all // for this stage, it will get flagged as dirty, and if another shader // gets bound, it will check for any dirty bindings again. m_state.lazy.shadersUsed.clr(ShaderStage); m_state.lazy.shadersDirty.clr(ShaderStage); m_state.lazy.bindingsUsed[ShaderStage].reset(); EmitCs([] (DxvkContext* ctx) { constexpr VkShaderStageFlagBits stage = GetShaderStage(ShaderStage); uint32_t slotId = computeConstantBufferBinding(ShaderStage, D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT); ctx->bindShader(nullptr); ctx->bindUniformBuffer(stage, slotId, DxvkBufferSlice()); }); } // On graphics, UAVs are available to all stages, but we treat them as part // of the pixel shader binding set. Re-compute the active UAV mask. We don't // need to set the PS as active or dirty here though since the UAV update // code will mark all other stages that access UAVs as dirty, too. uint64_t newUavMask = m_state.lazy.bindingsUsed[ShaderStage].uavMask; if (ShaderStage != DxbcProgramType::ComputeShader && oldUavMask != newUavMask) { constexpr DxbcProgramType ps = DxbcProgramType::PixelShader; // Since dirty UAVs are only tracked on the PS mask, we need to mark the // stage as dirty if any of the used UAVs overlap with the dirty PS mask. if (m_state.lazy.bindingsDirty[ps].uavMask & newUavMask) m_state.lazy.shadersDirty.set(ShaderStage); // Accumulate graphics UAV mask and write it back to the pixel shader mask. m_state.lazy.graphicsUavShaders.clr(ShaderStage); for (uint32_t stageIndex : bit::BitMask(uint32_t(m_state.lazy.graphicsUavShaders.raw()))) newUavMask |= m_state.lazy.bindingsUsed[DxbcProgramType(stageIndex)].uavMask; m_state.lazy.bindingsUsed[ps].uavMask = newUavMask; // Update bit mask of shaders actively accessing graphics UAVs if (newUavMask) m_state.lazy.graphicsUavShaders.set(ShaderStage); } } static VkDepthBiasRepresentationEXT FormatToDepthBiasRepresentation(DXGI_FORMAT format) { switch (format) { default: case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: case DXGI_FORMAT_D32_FLOAT: return VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT; case DXGI_FORMAT_D24_UNORM_S8_UINT: case DXGI_FORMAT_D16_UNORM: return VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT; } } template void D3D11CommonContext::BindFramebuffer() { DxvkDepthBiasRepresentation depthBiasRepresentation = { VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT, m_device->features().extDepthBiasControl.depthBiasExact }; DxvkRenderTargets attachments; uint32_t sampleCount = 0; // D3D11 doesn't have the concept of a framebuffer object, // so we'll just create a new one every time the render // target bindings are updated. Set up the attachments. for (UINT i = 0; i < m_state.om.rtvs.size(); i++) { if (m_state.om.rtvs[i] != nullptr) { attachments.color[i] = { m_state.om.rtvs[i]->GetImageView(), m_state.om.rtvs[i]->GetRenderLayout() }; sampleCount = m_state.om.rtvs[i]->GetSampleCount(); } } if (m_state.om.dsv != nullptr) { attachments.depth = { m_state.om.dsv->GetImageView(), m_state.om.dsv->GetRenderLayout() }; sampleCount = m_state.om.dsv->GetSampleCount(); if (m_device->features().extDepthBiasControl.leastRepresentableValueForceUnormRepresentation) depthBiasRepresentation.depthBiasRepresentation = FormatToDepthBiasRepresentation(m_state.om.dsv->GetViewFormat()); } // Create and bind the framebuffer object to the context EmitCs([ cAttachments = std::move(attachments), cRepresentation = depthBiasRepresentation ] (DxvkContext* ctx) mutable { ctx->setDepthBiasRepresentation(cRepresentation); ctx->bindRenderTargets(Forwarder::move(cAttachments), 0u); }); // If necessary, update push constant for the sample count if (m_state.om.sampleCount != sampleCount) { m_state.om.sampleCount = sampleCount; ApplyRasterizerSampleCount(); } } template void D3D11CommonContext::BindDrawBuffers( D3D11Buffer* pBufferForArgs, D3D11Buffer* pBufferForCount) { EmitCs([ cArgBuffer = pBufferForArgs ? pBufferForArgs->GetBufferSlice() : DxvkBufferSlice(), cCntBuffer = pBufferForCount ? pBufferForCount->GetBufferSlice() : DxvkBufferSlice() ] (DxvkContext* ctx) mutable { ctx->bindDrawBuffers( Forwarder::move(cArgBuffer), Forwarder::move(cCntBuffer)); }); } template void D3D11CommonContext::BindVertexBuffer( UINT Slot, D3D11Buffer* pBuffer, UINT Offset, UINT Stride) { if (pBuffer) { EmitCs([ cSlotId = Slot, cBufferSlice = pBuffer->GetBufferSlice(Offset), cStride = Stride ] (DxvkContext* ctx) mutable { ctx->bindVertexBuffer(cSlotId, Forwarder::move(cBufferSlice), cStride); }); } else { EmitCs([ cSlotId = Slot ] (DxvkContext* ctx) { ctx->bindVertexBuffer(cSlotId, DxvkBufferSlice(), 0); }); } } template void D3D11CommonContext::BindVertexBufferRange( UINT Slot, D3D11Buffer* pBuffer, UINT Offset, UINT Stride) { if (pBuffer) { VkDeviceSize offset = Offset; VkDeviceSize length = pBuffer->GetRemainingSize(Offset); EmitCs([ cSlotId = Slot, cBufferOffset = offset, cBufferLength = length, cStride = Stride ] (DxvkContext* ctx) mutable { ctx->bindVertexBufferRange(cSlotId, cBufferOffset, cBufferLength, cStride); }); } } template void D3D11CommonContext::BindIndexBuffer( D3D11Buffer* pBuffer, UINT Offset, DXGI_FORMAT Format) { VkIndexType indexType = Format == DXGI_FORMAT_R16_UINT ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32; if (pBuffer) { EmitCs([ cBufferSlice = pBuffer->GetBufferSlice(Offset), cIndexType = indexType ] (DxvkContext* ctx) mutable { ctx->bindIndexBuffer( Forwarder::move(cBufferSlice), cIndexType); }); } else { EmitCs([ cIndexType = indexType ] (DxvkContext* ctx) { ctx->bindIndexBuffer(DxvkBufferSlice(), cIndexType); }); } } template void D3D11CommonContext::BindIndexBufferRange( D3D11Buffer* pBuffer, UINT Offset, DXGI_FORMAT Format) { if (pBuffer) { VkIndexType indexType = Format == DXGI_FORMAT_R16_UINT ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32; VkDeviceSize offset = Offset; VkDeviceSize length = pBuffer->GetRemainingSize(Offset); EmitCs([ cBufferOffset = offset, cBufferLength = length, cIndexType = indexType ] (DxvkContext* ctx) mutable { ctx->bindIndexBufferRange( cBufferOffset, cBufferLength, cIndexType); }); } } template void D3D11CommonContext::BindXfbBuffer( UINT Slot, D3D11Buffer* pBuffer, UINT Offset) { if (pBuffer) { EmitCs([ cSlotId = Slot, cOffset = Offset, cBufferSlice = pBuffer->GetBufferSlice(), cCounterSlice = pBuffer->GetSOCounter() ] (DxvkContext* ctx) mutable { if (cCounterSlice.defined() && cOffset != ~0u) { ctx->updateBuffer( cCounterSlice.buffer(), cCounterSlice.offset(), sizeof(cOffset), &cOffset); } ctx->bindXfbBuffer(cSlotId, Forwarder::move(cBufferSlice), Forwarder::move(cCounterSlice)); }); } else { EmitCs([ cSlotId = Slot ] (DxvkContext* ctx) { ctx->bindXfbBuffer(cSlotId, DxvkBufferSlice(), DxvkBufferSlice()); }); } } template void D3D11CommonContext::BindConstantBuffer( DxbcProgramType ShaderStage, UINT Slot, D3D11Buffer* pBuffer, UINT Offset, UINT Length) { uint32_t slotId = computeConstantBufferBinding(ShaderStage, Slot); if (pBuffer) { EmitCs([ cSlotId = slotId, cStage = GetShaderStage(ShaderStage), cBufferSlice = pBuffer->GetBufferSlice(16 * Offset, 16 * Length) ] (DxvkContext* ctx) mutable { ctx->bindUniformBuffer(cStage, cSlotId, Forwarder::move(cBufferSlice)); }); } else { EmitCs([ cSlotId = slotId, cStage = GetShaderStage(ShaderStage) ] (DxvkContext* ctx) { ctx->bindUniformBuffer(cStage, cSlotId, DxvkBufferSlice()); }); } } template void D3D11CommonContext::BindConstantBufferRange( DxbcProgramType ShaderStage, UINT Slot, UINT Offset, UINT Length) { uint32_t slotId = computeConstantBufferBinding(ShaderStage, Slot); EmitCs([ cSlotId = slotId, cStage = GetShaderStage(ShaderStage), cOffset = 16u * Offset, cLength = 16u * Length ] (DxvkContext* ctx) { ctx->bindUniformBufferRange(cStage, cSlotId, cOffset, cLength); }); } template void D3D11CommonContext::BindSampler( DxbcProgramType ShaderStage, UINT Slot, D3D11SamplerState* pSampler) { uint32_t slotId = computeSamplerBinding(ShaderStage, Slot); if (pSampler) { EmitCs([ cSlotId = slotId, cStage = GetShaderStage(ShaderStage), cSampler = pSampler->GetDXVKSampler() ] (DxvkContext* ctx) mutable { ctx->bindResourceSampler(cStage, cSlotId, Forwarder::move(cSampler)); }); } else { EmitCs([ cSlotId = slotId, cStage = GetShaderStage(ShaderStage) ] (DxvkContext* ctx) { ctx->bindResourceSampler(cStage, cSlotId, nullptr); }); } } template void D3D11CommonContext::BindShaderResource( DxbcProgramType ShaderStage, UINT Slot, D3D11ShaderResourceView* pResource) { uint32_t slotId = computeSrvBinding(ShaderStage, Slot); if (pResource) { if (pResource->GetViewInfo().Dimension != D3D11_RESOURCE_DIMENSION_BUFFER) { EmitCs([ cSlotId = slotId, cStage = GetShaderStage(ShaderStage), cView = pResource->GetImageView() ] (DxvkContext* ctx) mutable { ctx->bindResourceImageView(cStage, cSlotId, Forwarder::move(cView)); }); } else { EmitCs([ cSlotId = slotId, cStage = GetShaderStage(ShaderStage), cView = pResource->GetBufferView() ] (DxvkContext* ctx) mutable { ctx->bindResourceBufferView(cStage, cSlotId, Forwarder::move(cView)); }); } } else { EmitCs([ cSlotId = slotId, cStage = GetShaderStage(ShaderStage) ] (DxvkContext* ctx) { ctx->bindResourceImageView(cStage, cSlotId, nullptr); }); } } template void D3D11CommonContext::BindUnorderedAccessView( DxbcProgramType ShaderStage, UINT Slot, D3D11UnorderedAccessView* pUav) { uint32_t uavSlotId = computeUavBinding(ShaderStage, Slot); uint32_t ctrSlotId = computeUavCounterBinding(ShaderStage, Slot); VkShaderStageFlags stages = ShaderStage == DxbcProgramType::ComputeShader ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_ALL_GRAPHICS; if (pUav) { if (pUav->GetViewInfo().Dimension == D3D11_RESOURCE_DIMENSION_BUFFER) { EmitCs([ cUavSlotId = uavSlotId, cCtrSlotId = ctrSlotId, cStages = stages, cBufferView = pUav->GetBufferView(), cCounterView = pUav->GetCounterView() ] (DxvkContext* ctx) mutable { ctx->bindResourceBufferView(cStages, cUavSlotId, Forwarder::move(cBufferView)); ctx->bindResourceBufferView(cStages, cCtrSlotId, Forwarder::move(cCounterView)); }); } else { EmitCs([ cUavSlotId = uavSlotId, cCtrSlotId = ctrSlotId, cStages = stages, cImageView = pUav->GetImageView() ] (DxvkContext* ctx) mutable { ctx->bindResourceImageView(cStages, cUavSlotId, Forwarder::move(cImageView)); ctx->bindResourceBufferView(cStages, cCtrSlotId, nullptr); }); } } else { EmitCs([ cUavSlotId = uavSlotId, cCtrSlotId = ctrSlotId, cStages = stages ] (DxvkContext* ctx) { ctx->bindResourceImageView(cStages, cUavSlotId, nullptr); ctx->bindResourceBufferView(cStages, cCtrSlotId, nullptr); }); } } template VkClearValue D3D11CommonContext::ConvertColorValue( const FLOAT Color[4], const DxvkFormatInfo* pFormatInfo) { VkClearValue result; if (pFormatInfo->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { for (uint32_t i = 0; i < 4; i++) { if (pFormatInfo->flags.test(DxvkFormatFlag::SampledUInt)) result.color.uint32[i] = uint32_t(std::max(0.0f, Color[i])); else if (pFormatInfo->flags.test(DxvkFormatFlag::SampledSInt)) result.color.int32[i] = int32_t(Color[i]); else result.color.float32[i] = Color[i]; } } else { result.depthStencil.depth = Color[0]; result.depthStencil.stencil = 0; } return result; } template void D3D11CommonContext::CopyBuffer( D3D11Buffer* pDstBuffer, VkDeviceSize DstOffset, D3D11Buffer* pSrcBuffer, VkDeviceSize SrcOffset, VkDeviceSize ByteCount) { // Clamp copy region to prevent out-of-bounds access VkDeviceSize dstLength = pDstBuffer->Desc()->ByteWidth; VkDeviceSize srcLength = pSrcBuffer->Desc()->ByteWidth; if (SrcOffset >= srcLength || DstOffset >= dstLength || !ByteCount) return; ByteCount = std::min(dstLength - DstOffset, ByteCount); ByteCount = std::min(srcLength - SrcOffset, ByteCount); EmitCs([ cDstBuffer = pDstBuffer->GetBufferSlice(DstOffset, ByteCount), cSrcBuffer = pSrcBuffer->GetBufferSlice(SrcOffset, ByteCount) ] (DxvkContext* ctx) { if (cDstBuffer.buffer() != cSrcBuffer.buffer()) { ctx->copyBuffer( cDstBuffer.buffer(), cDstBuffer.offset(), cSrcBuffer.buffer(), cSrcBuffer.offset(), cSrcBuffer.length()); } else { ctx->copyBufferRegion( cDstBuffer.buffer(), cDstBuffer.offset(), cSrcBuffer.offset(), cSrcBuffer.length()); } }); if (pDstBuffer->HasSequenceNumber()) GetTypedContext()->TrackBufferSequenceNumber(pDstBuffer); if (pSrcBuffer->HasSequenceNumber()) GetTypedContext()->TrackBufferSequenceNumber(pSrcBuffer); } template void D3D11CommonContext::CopyImage( D3D11CommonTexture* pDstTexture, const VkImageSubresourceLayers* pDstLayers, VkOffset3D DstOffset, D3D11CommonTexture* pSrcTexture, const VkImageSubresourceLayers* pSrcLayers, VkOffset3D SrcOffset, VkExtent3D SrcExtent) { // Image formats must be size-compatible auto dstFormatInfo = lookupFormatInfo(pDstTexture->GetPackedFormat()); auto srcFormatInfo = lookupFormatInfo(pSrcTexture->GetPackedFormat()); if (dstFormatInfo->elementSize != srcFormatInfo->elementSize) return; // Sample counts must match if (pDstTexture->Desc()->SampleDesc.Count != pSrcTexture->Desc()->SampleDesc.Count) return; // Obviously, the copy region must not be empty VkExtent3D dstMipExtent = pDstTexture->MipLevelExtent(pDstLayers->mipLevel); VkExtent3D srcMipExtent = pSrcTexture->MipLevelExtent(pSrcLayers->mipLevel); if (uint32_t(DstOffset.x) >= dstMipExtent.width || uint32_t(DstOffset.y) >= dstMipExtent.height || uint32_t(DstOffset.z) >= dstMipExtent.depth) return; if (uint32_t(SrcOffset.x) >= srcMipExtent.width || uint32_t(SrcOffset.y) >= srcMipExtent.height || uint32_t(SrcOffset.z) >= srcMipExtent.depth) return; // Don't perform the copy if the offsets aren't block-aligned if (!util::isBlockAligned(SrcOffset, srcFormatInfo->blockSize) || !util::isBlockAligned(DstOffset, dstFormatInfo->blockSize)) return; // Clamp the image region in order to avoid out-of-bounds access VkExtent3D blockCount = util::computeBlockCount(SrcExtent, srcFormatInfo->blockSize); VkExtent3D dstBlockCount = util::computeMaxBlockCount(DstOffset, dstMipExtent, dstFormatInfo->blockSize); VkExtent3D srcBlockCount = util::computeMaxBlockCount(SrcOffset, srcMipExtent, srcFormatInfo->blockSize); blockCount = util::minExtent3D(blockCount, dstBlockCount); blockCount = util::minExtent3D(blockCount, srcBlockCount); SrcExtent = util::computeBlockExtent(blockCount, srcFormatInfo->blockSize); SrcExtent = util::snapExtent3D(SrcOffset, SrcExtent, srcMipExtent); if (!SrcExtent.width || !SrcExtent.height || !SrcExtent.depth) return; // While copying between 2D and 3D images is allowed in CopySubresourceRegion, // copying more than one slice at a time is not suppoted. Layer counts are 1. if ((pDstTexture->GetVkImageType() == VK_IMAGE_TYPE_3D) != (pSrcTexture->GetVkImageType() == VK_IMAGE_TYPE_3D)) SrcExtent.depth = 1; // Certain types of copies require us to pass the destination extent to // the backend. This may be different when copying between compressed // and uncompressed image formats. VkExtent3D dstExtent = util::computeBlockExtent(blockCount, dstFormatInfo->blockSize); dstExtent = util::snapExtent3D(DstOffset, dstExtent, dstMipExtent); // It is possible for any of the given images to be a staging image with // no actual image, so we need to account for all possibilities here. bool dstIsImage = pDstTexture->HasImage(); bool srcIsImage = pSrcTexture->HasImage(); if (dstIsImage && srcIsImage) { EmitCs([ cDstImage = pDstTexture->GetImage(), cSrcImage = pSrcTexture->GetImage(), cDstLayers = *pDstLayers, cSrcLayers = *pSrcLayers, cDstOffset = DstOffset, cSrcOffset = SrcOffset, cExtent = SrcExtent ] (DxvkContext* ctx) { // CopyResource can only copy between different images, and // CopySubresourceRegion can only copy data from one single // subresource at a time, so this check is safe. if (cDstImage != cSrcImage || cDstLayers != cSrcLayers) { ctx->copyImage( cDstImage, cDstLayers, cDstOffset, cSrcImage, cSrcLayers, cSrcOffset, cExtent); } else { ctx->copyImageRegion( cDstImage, cDstLayers, cDstOffset, cSrcOffset, cExtent); } }); } else { // Since each subresource uses a dedicated buffer, we are going // to need one call per subresource for staging resource copies for (uint32_t i = 0; i < pDstLayers->layerCount; i++) { uint32_t dstSubresource = D3D11CalcSubresource(pDstLayers->mipLevel, pDstLayers->baseArrayLayer + i, pDstTexture->Desc()->MipLevels); uint32_t srcSubresource = D3D11CalcSubresource(pSrcLayers->mipLevel, pSrcLayers->baseArrayLayer + i, pSrcTexture->Desc()->MipLevels); // For multi-plane image data stored in a buffer, the backend // assumes that the second plane immediately follows the first // plane in memory, which is only true if we copy the full image. uint32_t planeCount = 1; if (dstFormatInfo->flags.test(DxvkFormatFlag::MultiPlane)) { bool needsSeparateCopies = !dstIsImage && !srcIsImage; if (!dstIsImage) needsSeparateCopies |= pDstTexture->MipLevelExtent(pDstLayers->mipLevel) != SrcExtent; if (!srcIsImage) needsSeparateCopies |= pSrcTexture->MipLevelExtent(pSrcLayers->mipLevel) != SrcExtent; if (needsSeparateCopies) planeCount = vk::getPlaneCount(srcFormatInfo->aspectMask); } for (uint32_t j = 0; j < planeCount; j++) { VkImageAspectFlags dstAspectMask = dstFormatInfo->aspectMask; VkImageAspectFlags srcAspectMask = srcFormatInfo->aspectMask; if (planeCount > 1) { dstAspectMask = vk::getPlaneAspect(j); srcAspectMask = dstAspectMask; } if (dstIsImage) { VkImageSubresourceLayers dstLayer = { dstAspectMask, pDstLayers->mipLevel, pDstLayers->baseArrayLayer + i, 1 }; EmitCs([ cDstImage = pDstTexture->GetImage(), cDstLayers = dstLayer, cDstOffset = DstOffset, cDstExtent = dstExtent, cDstFormat = pDstTexture->GetPackedFormat(), cSrcBuffer = pSrcTexture->GetMappedBuffer(srcSubresource), cSrcLayout = pSrcTexture->GetSubresourceLayout(srcAspectMask, srcSubresource), cSrcOffset = pSrcTexture->ComputeMappedOffset(srcSubresource, j, SrcOffset), cSrcCoord = SrcOffset, cSrcExtent = srcMipExtent ] (DxvkContext* ctx) { ctx->copyBufferToImage(cDstImage, cDstLayers, cDstOffset, cDstExtent, cSrcBuffer, cSrcOffset, cSrcLayout.RowPitch, cSrcLayout.DepthPitch, cDstFormat); }); } else if (srcIsImage) { VkImageSubresourceLayers srcLayer = { srcAspectMask, pSrcLayers->mipLevel, pSrcLayers->baseArrayLayer + i, 1 }; EmitCs([ cSrcImage = pSrcTexture->GetImage(), cSrcFormat = pSrcTexture->GetPackedFormat(), cSrcLayers = srcLayer, cSrcOffset = SrcOffset, cSrcExtent = SrcExtent, cDstBuffer = pDstTexture->GetMappedBuffer(dstSubresource), cDstLayout = pDstTexture->GetSubresourceLayout(dstAspectMask, dstSubresource), cDstOffset = pDstTexture->ComputeMappedOffset(dstSubresource, j, DstOffset), cDstCoord = DstOffset, cDstExtent = dstMipExtent ] (DxvkContext* ctx) { ctx->copyImageToBuffer(cDstBuffer, cDstOffset, cDstLayout.RowPitch, cDstLayout.DepthPitch, cSrcFormat, cSrcImage, cSrcLayers, cSrcOffset, cSrcExtent); }); } else { // The backend is not aware of image metadata in this case, // so we need to handle image planes and block sizes here VkDeviceSize elementSize = dstFormatInfo->elementSize; VkExtent3D dstBlockSize = dstFormatInfo->blockSize; VkExtent3D srcBlockSize = srcFormatInfo->blockSize; VkExtent3D planeBlockSize = { 1u, 1u, 1u }; if (planeCount > 1) { auto plane = &dstFormatInfo->planes[j]; dstBlockSize.width *= plane->blockSize.width; dstBlockSize.height *= plane->blockSize.height; srcBlockSize.width *= plane->blockSize.width; srcBlockSize.height *= plane->blockSize.height; planeBlockSize.width = plane->blockSize.width; planeBlockSize.height = plane->blockSize.height; elementSize = plane->elementSize; } EmitCs([ cPixelSize = elementSize, cSrcBuffer = pSrcTexture->GetMappedBuffer(srcSubresource), cSrcStart = pSrcTexture->GetSubresourceLayout(srcAspectMask, srcSubresource).Offset, cSrcOffset = util::computeBlockOffset(SrcOffset, srcBlockSize), cSrcSize = util::computeBlockCount(srcMipExtent, srcBlockSize), cDstBuffer = pDstTexture->GetMappedBuffer(dstSubresource), cDstStart = pDstTexture->GetSubresourceLayout(dstAspectMask, dstSubresource).Offset, cDstOffset = util::computeBlockOffset(DstOffset, dstBlockSize), cDstSize = util::computeBlockCount(dstMipExtent, dstBlockSize), cExtent = util::computeBlockCount(blockCount, planeBlockSize) ] (DxvkContext* ctx) { ctx->copyPackedBufferImage( cDstBuffer, cDstStart, cDstOffset, cDstSize, cSrcBuffer, cSrcStart, cSrcOffset, cSrcSize, cExtent, cPixelSize); }); } } } } if (pDstTexture->HasSequenceNumber()) { for (uint32_t i = 0; i < pDstLayers->layerCount; i++) { GetTypedContext()->TrackTextureSequenceNumber(pDstTexture, D3D11CalcSubresource( pDstLayers->mipLevel, pDstLayers->baseArrayLayer + i, pDstTexture->Desc()->MipLevels)); } } if (pSrcTexture->HasSequenceNumber()) { for (uint32_t i = 0; i < pSrcLayers->layerCount; i++) { GetTypedContext()->TrackTextureSequenceNumber(pSrcTexture, D3D11CalcSubresource( pSrcLayers->mipLevel, pSrcLayers->baseArrayLayer + i, pSrcTexture->Desc()->MipLevels)); } } } template void D3D11CommonContext::CopyTiledResourceData( ID3D11Resource* pResource, const D3D11_TILED_RESOURCE_COORDINATE* pRegionCoordinate, const D3D11_TILE_REGION_SIZE* pRegionSize, DxvkBufferSlice BufferSlice, UINT Flags) { Rc resource = GetPagedResource(pResource); // Do some validation based on page table properties auto pageTable = resource->getSparsePageTable(); if (!pageTable) return; if (pRegionSize->bUseBox && pRegionSize->NumTiles != pRegionSize->Width * pRegionSize->Height * pRegionSize->Depth) return; if (pRegionSize->NumTiles > pageTable->getPageCount()) return; // Ignore call if buffer access would be out of bounds VkDeviceSize bufferSize = pRegionSize->NumTiles * SparseMemoryPageSize; if (BufferSlice.length() < bufferSize) return; // Compute list of tile indices to copy std::vector tiles(pRegionSize->NumTiles); for (uint32_t i = 0; i < pRegionSize->NumTiles; i++) { VkOffset3D regionOffset = { int32_t(pRegionCoordinate->X), int32_t(pRegionCoordinate->Y), int32_t(pRegionCoordinate->Z) }; VkExtent3D regionExtent = { uint32_t(pRegionSize->Width), uint32_t(pRegionSize->Height), uint32_t(pRegionSize->Depth) }; uint32_t tile = pageTable->computePageIndex( pRegionCoordinate->Subresource, regionOffset, regionExtent, !pRegionSize->bUseBox, i); // Check that the tile is valid and not part of the mip tail auto tileInfo = pageTable->getPageInfo(tile); if (tileInfo.type != DxvkSparsePageType::Buffer && tileInfo.type != DxvkSparsePageType::Image) return; tiles[i] = tile; } // If D3D12 is anything to go by, not passing this flag will trigger // the other code path, regardless of whether TO_LINEAR_BUFFER is set. if (Flags & D3D11_TILE_COPY_LINEAR_BUFFER_TO_SWIZZLED_TILED_RESOURCE) { EmitCs([ cResource = std::move(resource), cTiles = std::move(tiles), cBuffer = std::move(BufferSlice) ] (DxvkContext* ctx) { ctx->copySparsePagesFromBuffer( cResource, cTiles.size(), cTiles.data(), cBuffer.buffer(), cBuffer.offset()); }); } else { EmitCs([ cResource = std::move(resource), cTiles = std::move(tiles), cBuffer = std::move(BufferSlice) ] (DxvkContext* ctx) { ctx->copySparsePagesToBuffer( cBuffer.buffer(), cBuffer.offset(), cResource, cTiles.size(), cTiles.data()); }); } } template template bool D3D11CommonContext::DirtyBindingGeneric( DxbcProgramType ShaderStage, T BoundMask, T& DirtyMask, T DirtyBit, bool IsNull) { // Forward immediately if lazy binding is forced off if (DebugLazyBinding == Tristate::False) return false; if ((BoundMask & ~DirtyMask) & DirtyBit) { // If we're binding a non-null resource to an active slot that has not been // marked for lazy binding yet, forward the call immediately in order to // avoid tracking overhead. This is by far the most common case. if (likely(!IsNull && DebugLazyBinding != Tristate::True)) return false; // If we are binding a null resource to an active slot, the app will likely // either bind something else or bind a shader that does not use this slot. // In that case, avoid likely redundant CS traffic and apply the binding on // the next draw. m_state.lazy.shadersDirty.set(ShaderStage); } // Binding is either inactive or already dirty. In the inactive case, there // is no need to mark the shader stage as dirty since binding a shader that // activates the binding will implicitly do so. DirtyMask |= DirtyBit; return true; } template bool D3D11CommonContext::DirtyConstantBuffer( DxbcProgramType ShaderStage, uint32_t Slot, bool IsNull) { return DirtyBindingGeneric(ShaderStage, m_state.lazy.bindingsUsed[ShaderStage].cbvMask, m_state.lazy.bindingsDirty[ShaderStage].cbvMask, 1u << Slot, IsNull); } template bool D3D11CommonContext::DirtySampler( DxbcProgramType ShaderStage, uint32_t Slot, bool IsNull) { return DirtyBindingGeneric(ShaderStage, m_state.lazy.bindingsUsed[ShaderStage].samplerMask, m_state.lazy.bindingsDirty[ShaderStage].samplerMask, 1u << Slot, IsNull); } template bool D3D11CommonContext::DirtyShaderResource( DxbcProgramType ShaderStage, uint32_t Slot, bool IsNull) { uint32_t idx = Slot / 64u; return DirtyBindingGeneric(ShaderStage, m_state.lazy.bindingsUsed[ShaderStage].srvMask[idx], m_state.lazy.bindingsDirty[ShaderStage].srvMask[idx], uint64_t(1u) << Slot, IsNull); } template bool D3D11CommonContext::DirtyComputeUnorderedAccessView( uint32_t Slot, bool IsNull) { constexpr DxbcProgramType ShaderStage = DxbcProgramType::ComputeShader; return DirtyBindingGeneric(ShaderStage, m_state.lazy.bindingsUsed[ShaderStage].uavMask, m_state.lazy.bindingsDirty[ShaderStage].uavMask, uint64_t(1u) << Slot, IsNull); } template bool D3D11CommonContext::DirtyGraphicsUnorderedAccessView( uint32_t Slot) { constexpr DxbcProgramType ShaderStage = DxbcProgramType::PixelShader; if (DebugLazyBinding == Tristate::False) return false; // Use different logic here and always use lazy binding for graphics UAVs. // Since graphics UAVs are generally bound together with render targets, // looking at the active binding mask doesn't really help us here. uint64_t dirtyBit = uint64_t(1u) << Slot; if (m_state.lazy.bindingsUsed[ShaderStage].uavMask & dirtyBit) { // Need to mark all graphics stages that use UAVs as dirty here to // make sure that bindings actually get reapplied properly. There // may be no pixel shader bound in this case, even though we do // all the tracking on the pixel shader bit mask. m_state.lazy.shadersDirty.set(m_state.lazy.graphicsUavShaders); } m_state.lazy.bindingsDirty[ShaderStage].uavMask |= dirtyBit; return true; } template void D3D11CommonContext::DiscardBuffer( ID3D11Resource* pResource) { auto buffer = static_cast(pResource); if (buffer->GetMapMode() != D3D11_COMMON_BUFFER_MAP_MODE_NONE) { D3D11_MAPPED_SUBRESOURCE sr; Map(pResource, 0, D3D11_MAP_WRITE_DISCARD, 0, &sr); Unmap(pResource, 0); } } template void D3D11CommonContext::DiscardTexture( ID3D11Resource* pResource, UINT Subresource) { auto texture = GetCommonTexture(pResource); if (texture->GetMapMode() != D3D11_COMMON_TEXTURE_MAP_MODE_NONE) { D3D11_MAPPED_SUBRESOURCE sr; Map(pResource, Subresource, D3D11_MAP_WRITE_DISCARD, 0, &sr); Unmap(pResource, Subresource); } } template template void D3D11CommonContext::GetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers, UINT* pFirstConstant, UINT* pNumConstants) { const auto& bindings = m_state.cbv[ShaderStage]; for (uint32_t i = 0; i < NumBuffers; i++) { const bool inRange = StartSlot + i < bindings.buffers.size(); if (ppConstantBuffers) { ppConstantBuffers[i] = inRange ? bindings.buffers[StartSlot + i].buffer.ref() : nullptr; } if (pFirstConstant) { pFirstConstant[i] = inRange ? bindings.buffers[StartSlot + i].constantOffset : 0u; } if (pNumConstants) { pNumConstants[i] = inRange ? bindings.buffers[StartSlot + i].constantCount : 0u; } } } template template void D3D11CommonContext::GetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView** ppShaderResourceViews) { const auto& bindings = m_state.srv[ShaderStage]; for (uint32_t i = 0; i < NumViews; i++) { ppShaderResourceViews[i] = StartSlot + i < bindings.views.size() ? bindings.views[StartSlot + i].ref() : nullptr; } } template template void D3D11CommonContext::GetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState** ppSamplers) { const auto& bindings = m_state.samplers[ShaderStage]; for (uint32_t i = 0; i < NumSamplers; i++) { ppSamplers[i] = StartSlot + i < bindings.samplers.size() ? ref(bindings.samplers[StartSlot + i]) : nullptr; } } template DxvkGlobalPipelineBarrier D3D11CommonContext::GetTiledResourceDependency( ID3D11DeviceChild* pObject) { if (!pObject) { DxvkGlobalPipelineBarrier result; result.stages = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; result.access = VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT; return result; } else { Com resource; if (FAILED(pObject->QueryInterface(IID_PPV_ARGS(&resource)))) { Com view; if (FAILED(pObject->QueryInterface(IID_PPV_ARGS(&view)))) return DxvkGlobalPipelineBarrier(); view->GetResource(&resource); } D3D11CommonTexture* texture = GetCommonTexture(resource.ptr()); if (texture) { Rc image = texture->GetImage(); DxvkGlobalPipelineBarrier result; result.stages = image->info().stages; result.access = image->info().access; return result; } else { Rc buffer = static_cast(resource.ptr())->GetBuffer(); if (buffer == nullptr) return DxvkGlobalPipelineBarrier(); DxvkGlobalPipelineBarrier result; result.stages = buffer->info().stages; result.access = buffer->info().access; return result; } } } template D3D11MaxUsedBindings D3D11CommonContext::GetMaxUsedBindings() { D3D11MaxUsedBindings result; for (uint32_t i = 0; i < result.stages.size(); i++) { auto stage = DxbcProgramType(i); result.stages[i].cbvCount = m_state.cbv[stage].maxCount; result.stages[i].srvCount = m_state.srv[stage].maxCount; result.stages[i].uavCount = 0; result.stages[i].samplerCount = m_state.samplers[stage].maxCount; result.stages[i].reserved = 0; } result.stages[uint32_t(DxbcProgramType::PixelShader)].uavCount = m_state.om.maxUav; result.stages[uint32_t(DxbcProgramType::ComputeShader)].uavCount = m_state.uav.maxCount; result.vbCount = m_state.ia.maxVbCount; result.soCount = D3D11_SO_BUFFER_SLOT_COUNT; return result; } template bool D3D11CommonContext::HasDirtyComputeBindings() { return m_state.lazy.shadersDirty.test(DxbcProgramType::ComputeShader); } template bool D3D11CommonContext::HasDirtyGraphicsBindings() { return (m_state.lazy.shadersDirty & m_state.lazy.shadersUsed).any( DxbcProgramType::VertexShader, DxbcProgramType::GeometryShader, DxbcProgramType::HullShader, DxbcProgramType::DomainShader, DxbcProgramType::PixelShader); } template void D3D11CommonContext::ResetCommandListState() { EmitCs([ cUsedBindings = GetMaxUsedBindings() ] (DxvkContext* ctx) { // Reset render targets ctx->bindRenderTargets(DxvkRenderTargets(), 0u); // Reset vertex input state ctx->setInputLayout(0, nullptr, 0, nullptr); // Reset render states ctx->setInputAssemblyState(InitDefaultPrimitiveTopology()); ctx->setDepthStencilState(InitDefaultDepthStencilState()); ctx->setRasterizerState(InitDefaultRasterizerState()); ctx->setLogicOpState(InitDefaultLogicOpState()); ctx->setMultisampleState(InitDefaultMultisampleState(D3D11_DEFAULT_SAMPLE_MASK)); DxvkBlendMode cbState = InitDefaultBlendState(); for (uint32_t i = 0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++) ctx->setBlendMode(i, cbState); // Reset dynamic states ctx->setBlendConstants(DxvkBlendConstants { 1.0f, 1.0f, 1.0f, 1.0f }); ctx->setStencilReference(D3D11_DEFAULT_STENCIL_REFERENCE); // Reset viewports DxvkViewport viewport = { }; ctx->setViewports(1, &viewport); // Unbind indirect draw buffer ctx->bindDrawBuffers(DxvkBufferSlice(), DxvkBufferSlice()); // Unbind index and vertex buffers ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32); for (uint32_t i = 0; i < cUsedBindings.vbCount; i++) ctx->bindVertexBuffer(i, DxvkBufferSlice(), 0); // Unbind transform feedback buffers for (uint32_t i = 0; i < cUsedBindings.soCount; i++) ctx->bindXfbBuffer(i, DxvkBufferSlice(), DxvkBufferSlice()); // Unbind all shaders ctx->bindShader(nullptr); ctx->bindShader(nullptr); ctx->bindShader(nullptr); ctx->bindShader(nullptr); ctx->bindShader(nullptr); ctx->bindShader(nullptr); // Unbind per-shader stage resources for (uint32_t i = 0; i < 6; i++) { auto programType = DxbcProgramType(i); auto stage = GetShaderStage(programType); // Unbind constant buffers, including the shader's ICB auto cbSlotId = computeConstantBufferBinding(programType, 0); ctx->bindUniformBuffer(stage, cbSlotId + D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT, DxvkBufferSlice()); for (uint32_t j = 0; j < cUsedBindings.stages[i].cbvCount; j++) ctx->bindUniformBuffer(stage, cbSlotId + j, DxvkBufferSlice()); // Unbind shader resource views auto srvSlotId = computeSrvBinding(programType, 0); for (uint32_t j = 0; j < cUsedBindings.stages[i].srvCount; j++) ctx->bindResourceImageView(stage, srvSlotId + j, nullptr); // Unbind texture samplers auto samplerSlotId = computeSamplerBinding(programType, 0); for (uint32_t j = 0; j < cUsedBindings.stages[i].samplerCount; j++) ctx->bindResourceSampler(stage, samplerSlotId + j, nullptr); // Unbind UAVs for supported stages if (programType == DxbcProgramType::PixelShader || programType == DxbcProgramType::ComputeShader) { VkShaderStageFlags stages = programType == DxbcProgramType::PixelShader ? VK_SHADER_STAGE_ALL_GRAPHICS : VK_SHADER_STAGE_COMPUTE_BIT; auto uavSlotId = computeUavBinding(programType, 0); auto ctrSlotId = computeUavCounterBinding(programType, 0); for (uint32_t j = 0; j < cUsedBindings.stages[i].uavCount; j++) { ctx->bindResourceImageView(stages, uavSlotId, nullptr); ctx->bindResourceBufferView(stages, ctrSlotId, nullptr); } } } // Initialize push constants DxbcPushConstants pc; pc.rasterizerSampleCount = 1; ctx->pushConstants(0, sizeof(pc), &pc); }); } template void D3D11CommonContext::ResetContextState() { // Reset shaders m_state.vs = nullptr; m_state.hs = nullptr; m_state.ds = nullptr; m_state.gs = nullptr; m_state.ps = nullptr; m_state.cs = nullptr; // Reset render state m_state.id.reset(); m_state.ia.reset(); m_state.om.reset(); m_state.rs.reset(); m_state.so.reset(); m_state.pr.reset(); // Reset resource bindings m_state.cbv.reset(); m_state.srv.reset(); m_state.uav.reset(); m_state.samplers.reset(); // Reset dirty tracking m_state.lazy.reset(); } template void D3D11CommonContext::ResetDirtyTracking() { // Must only be called when all bindings are guaranteed to get applied // to the DXVK context before the next draw or dispatch command. m_state.lazy.bindingsDirty.reset(); m_state.lazy.shadersDirty = 0u; } template void D3D11CommonContext::ResetStagingBuffer() { m_staging.reset(); } template template void D3D11CommonContext::ResolveSrvHazards( T* pView) { auto& bindings = m_state.srv[ShaderStage]; int32_t srvId = bindings.hazardous.findNext(0); while (srvId >= 0) { auto srv = bindings.views[srvId].ptr(); if (likely(srv && srv->TestHazards())) { bool hazard = CheckViewOverlap(pView, srv); if (unlikely(hazard)) { bindings.views[srvId] = nullptr; bindings.hazardous.clr(srvId); if (!DirtyShaderResource(ShaderStage, srvId, true)) BindShaderResource(ShaderStage, srvId, nullptr); } } else { // Avoid further redundant iterations bindings.hazardous.clr(srvId); } srvId = bindings.hazardous.findNext(srvId + 1); } } template template void D3D11CommonContext::ResolveCsSrvHazards( T* pView) { if (!pView) return; ResolveSrvHazards(pView); } template template void D3D11CommonContext::ResolveOmSrvHazards( T* pView) { if (!pView) return; ResolveSrvHazards(pView); ResolveSrvHazards(pView); ResolveSrvHazards(pView); ResolveSrvHazards(pView); ResolveSrvHazards(pView); } template bool D3D11CommonContext::ResolveOmRtvHazards( D3D11UnorderedAccessView* pView) { if (!pView || !pView->HasBindFlag(D3D11_BIND_RENDER_TARGET)) return false; bool hazard = false; if (CheckViewOverlap(pView, m_state.om.dsv.ptr())) { m_state.om.dsv = nullptr; hazard = true; } for (uint32_t i = 0; i < m_state.om.maxRtv; i++) { if (CheckViewOverlap(pView, m_state.om.rtvs[i].ptr())) { m_state.om.rtvs[i] = nullptr; hazard = true; } } return hazard; } template void D3D11CommonContext::ResolveOmUavHazards( D3D11RenderTargetView* pView) { if (!pView || !pView->HasBindFlag(D3D11_BIND_UNORDERED_ACCESS)) return; for (uint32_t i = 0; i < m_state.om.maxUav; i++) { if (CheckViewOverlap(pView, m_state.om.uavs[i].ptr())) { m_state.om.uavs[i] = nullptr; if (!DirtyGraphicsUnorderedAccessView(i)) BindUnorderedAccessView(DxbcProgramType::PixelShader, i, nullptr); } } } template void D3D11CommonContext::RestoreCommandListState() { BindFramebuffer(); BindShader(GetCommonShader(m_state.vs.ptr())); BindShader(GetCommonShader(m_state.hs.ptr())); BindShader(GetCommonShader(m_state.ds.ptr())); BindShader(GetCommonShader(m_state.gs.ptr())); BindShader(GetCommonShader(m_state.ps.ptr())); BindShader(GetCommonShader(m_state.cs.ptr())); ApplyInputLayout(); ApplyPrimitiveTopology(); ApplyBlendState(); ApplyBlendFactor(); ApplyDepthStencilState(); ApplyStencilRef(); ApplyRasterizerState(); ApplyRasterizerSampleCount(); ApplyViewportState(); BindIndexBuffer( m_state.ia.indexBuffer.buffer.ptr(), m_state.ia.indexBuffer.offset, m_state.ia.indexBuffer.format); for (uint32_t i = 0; i < m_state.ia.maxVbCount; i++) { BindVertexBuffer(i, m_state.ia.vertexBuffers[i].buffer.ptr(), m_state.ia.vertexBuffers[i].offset, m_state.ia.vertexBuffers[i].stride); } for (uint32_t i = 0; i < m_state.so.targets.size(); i++) BindXfbBuffer(i, m_state.so.targets[i].buffer.ptr(), ~0u); // Reset dirty binding and shader masks before applying // bindings to avoid implicit null binding overrids. ResetDirtyTracking(); for (uint32_t i = 0; i < uint32_t(DxbcProgramType::Count); i++) { auto stage = DxbcProgramType(i); RestoreConstantBuffers(stage); RestoreShaderResources(stage); RestoreSamplers(stage); } RestoreUnorderedAccessViews(DxbcProgramType::PixelShader); RestoreUnorderedAccessViews(DxbcProgramType::ComputeShader); // Draw buffer bindings aren't persistent at the API level, and // we can't meaningfully track them. Just reset this state here // and reapply on the next indirect draw. SetDrawBuffers(nullptr, nullptr); } template void D3D11CommonContext::RestoreConstantBuffers( DxbcProgramType Stage) { const auto& bindings = m_state.cbv[Stage]; for (uint32_t i = 0; i < bindings.maxCount; i++) { BindConstantBuffer(Stage, i, bindings.buffers[i].buffer.ptr(), bindings.buffers[i].constantOffset, bindings.buffers[i].constantBound); } } template void D3D11CommonContext::RestoreSamplers( DxbcProgramType Stage) { const auto& bindings = m_state.samplers[Stage]; for (uint32_t i = 0; i < bindings.maxCount; i++) BindSampler(Stage, i, bindings.samplers[i]); } template void D3D11CommonContext::RestoreShaderResources( DxbcProgramType Stage) { const auto& bindings = m_state.srv[Stage]; for (uint32_t i = 0; i < bindings.maxCount; i++) BindShaderResource(Stage, i, bindings.views[i].ptr()); } template void D3D11CommonContext::RestoreUnorderedAccessViews( DxbcProgramType Stage) { const auto& views = Stage == DxbcProgramType::ComputeShader ? m_state.uav.views : m_state.om.uavs; uint32_t maxCount = Stage == DxbcProgramType::ComputeShader ? m_state.uav.maxCount : m_state.om.maxUav; for (uint32_t i = 0; i < maxCount; i++) BindUnorderedAccessView(Stage, i, views[i].ptr()); } template template void D3D11CommonContext::SetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { auto& bindings = m_state.cbv[ShaderStage]; for (uint32_t i = 0; i < NumBuffers; i++) { auto newBuffer = static_cast(ppConstantBuffers[i]); uint32_t constantCount = newBuffer ? std::min(newBuffer->Desc()->ByteWidth / 16, UINT(D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT)) : 0u; if (bindings.buffers[StartSlot + i].buffer != newBuffer || bindings.buffers[StartSlot + i].constantOffset != 0 || bindings.buffers[StartSlot + i].constantCount != constantCount) { bindings.buffers[StartSlot + i].buffer = newBuffer; bindings.buffers[StartSlot + i].constantOffset = 0; bindings.buffers[StartSlot + i].constantCount = constantCount; bindings.buffers[StartSlot + i].constantBound = constantCount; if (!DirtyConstantBuffer(ShaderStage, StartSlot + i, !newBuffer)) BindConstantBuffer(ShaderStage, StartSlot + i, newBuffer, 0, constantCount); } } bindings.maxCount = std::clamp(StartSlot + NumBuffers, bindings.maxCount, uint32_t(bindings.buffers.size())); } template template void D3D11CommonContext::SetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers, const UINT* pFirstConstant, const UINT* pNumConstants) { auto& bindings = m_state.cbv[ShaderStage]; for (uint32_t i = 0; i < NumBuffers; i++) { auto newBuffer = static_cast(ppConstantBuffers[i]); UINT constantOffset; UINT constantCount; UINT constantBound; if (likely(newBuffer != nullptr)) { UINT bufferConstantsCount = newBuffer->Desc()->ByteWidth / 16; constantBound = std::min(bufferConstantsCount, UINT(D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT)); if (likely(pFirstConstant && pNumConstants)) { constantOffset = pFirstConstant[i]; constantCount = pNumConstants [i]; if (unlikely(constantCount > D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT)) continue; constantBound = (constantOffset + constantCount > bufferConstantsCount) ? bufferConstantsCount - std::min(constantOffset, bufferConstantsCount) : constantCount; } else { constantOffset = 0; constantCount = constantBound; } } else { constantOffset = 0; constantCount = 0; constantBound = 0; } // Do a full rebind if either the buffer changes if (bindings.buffers[StartSlot + i].buffer != newBuffer) { bindings.buffers[StartSlot + i].buffer = newBuffer; bindings.buffers[StartSlot + i].constantOffset = constantOffset; bindings.buffers[StartSlot + i].constantCount = constantCount; bindings.buffers[StartSlot + i].constantBound = constantBound; if (!DirtyConstantBuffer(ShaderStage, StartSlot + i, !newBuffer)) BindConstantBuffer(ShaderStage, StartSlot + i, newBuffer, constantOffset, constantBound); } else if (bindings.buffers[StartSlot + i].constantOffset != constantOffset || bindings.buffers[StartSlot + i].constantCount != constantCount) { bindings.buffers[StartSlot + i].constantOffset = constantOffset; bindings.buffers[StartSlot + i].constantCount = constantCount; bindings.buffers[StartSlot + i].constantBound = constantBound; if (!DirtyConstantBuffer(ShaderStage, StartSlot + i, !newBuffer)) BindConstantBufferRange(ShaderStage, StartSlot + i, constantOffset, constantBound); } } bindings.maxCount = std::clamp(StartSlot + NumBuffers, bindings.maxCount, uint32_t(bindings.buffers.size())); } template template void D3D11CommonContext::SetShaderResources( UINT StartSlot, UINT NumResources, ID3D11ShaderResourceView* const* ppResources) { auto& bindings = m_state.srv[ShaderStage]; for (uint32_t i = 0; i < NumResources; i++) { auto resView = static_cast(ppResources[i]); if (bindings.views[StartSlot + i] != resView) { if (likely(resView != nullptr)) { if (unlikely(resView->TestHazards())) { if (TestSrvHazards(resView)) resView = nullptr; // Only set if necessary, but don't reset it on every // bind as this would be more expensive than a few // redundant checks in OMSetRenderTargets and friends. bindings.hazardous.set(StartSlot + i, resView); } } bindings.views[StartSlot + i] = resView; if (!DirtyShaderResource(ShaderStage, StartSlot + i, !resView)) BindShaderResource(ShaderStage, StartSlot + i, resView); } } bindings.maxCount = std::clamp(StartSlot + NumResources, bindings.maxCount, uint32_t(bindings.views.size())); } template template void D3D11CommonContext::SetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { auto& bindings = m_state.samplers[ShaderStage]; for (uint32_t i = 0; i < NumSamplers; i++) { auto sampler = static_cast(ppSamplers[i]); if (bindings.samplers[StartSlot + i] != sampler) { bindings.samplers[StartSlot + i] = sampler; if (!DirtySampler(ShaderStage, StartSlot + i, !sampler)) BindSampler(ShaderStage, StartSlot + i, sampler); } } bindings.maxCount = std::clamp(StartSlot + NumSamplers, bindings.maxCount, uint32_t(bindings.samplers.size())); } template void D3D11CommonContext::SetRenderTargetsAndUnorderedAccessViews( UINT NumRTVs, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView* pDepthStencilView, UINT UAVStartSlot, UINT NumUAVs, ID3D11UnorderedAccessView* const* ppUnorderedAccessViews, const UINT* pUAVInitialCounts) { if (TestRtvUavHazards(NumRTVs, ppRenderTargetViews, NumUAVs, ppUnorderedAccessViews)) return; bool needsUpdate = false; bool isMultisampled = false; if (likely(NumRTVs != D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL)) { // Native D3D11 does not change the render targets if // the parameters passed to this method are invalid. if (!ValidateRenderTargets(NumRTVs, ppRenderTargetViews, pDepthStencilView)) return; for (uint32_t i = 0; i < m_state.om.rtvs.size(); i++) { auto rtv = i < NumRTVs ? static_cast(ppRenderTargetViews[i]) : nullptr; if (m_state.om.rtvs[i] != rtv) { m_state.om.rtvs[i] = rtv; needsUpdate = true; ResolveOmSrvHazards(rtv); if (NumUAVs == D3D11_KEEP_UNORDERED_ACCESS_VIEWS) ResolveOmUavHazards(rtv); if (rtv && rtv->GetSampleCount() > 1u) isMultisampled = true; } } auto dsv = static_cast(pDepthStencilView); if (m_state.om.dsv != dsv) { m_state.om.dsv = dsv; needsUpdate = true; ResolveOmSrvHazards(dsv); if (dsv && dsv->GetSampleCount() > 1u) isMultisampled = true; } m_state.om.maxRtv = NumRTVs; } if (unlikely(NumUAVs || m_state.om.maxUav)) { if (likely(NumUAVs != D3D11_KEEP_UNORDERED_ACCESS_VIEWS)) { uint32_t newMinUav = NumUAVs ? UAVStartSlot : D3D11_1_UAV_SLOT_COUNT; uint32_t newMaxUav = NumUAVs ? UAVStartSlot + NumUAVs : 0u; uint32_t oldMinUav = std::exchange(m_state.om.minUav, newMinUav); uint32_t oldMaxUav = std::exchange(m_state.om.maxUav, newMaxUav); for (uint32_t i = std::min(oldMinUav, newMinUav); i < std::max(oldMaxUav, newMaxUav); i++) { D3D11UnorderedAccessView* uav = nullptr; uint32_t ctr = ~0u; if (i >= UAVStartSlot && i < UAVStartSlot + NumUAVs) { uav = static_cast(ppUnorderedAccessViews[i - UAVStartSlot]); ctr = pUAVInitialCounts ? pUAVInitialCounts[i - UAVStartSlot] : ~0u; } if (ctr != ~0u && uav && uav->HasCounter()) UpdateUnorderedAccessViewCounter(uav, ctr); if (m_state.om.uavs[i] != uav) { m_state.om.uavs[i] = uav; if (!DirtyGraphicsUnorderedAccessView(i)) BindUnorderedAccessView(DxbcProgramType::PixelShader, i, uav); ResolveOmSrvHazards(uav); if (NumRTVs == D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL) needsUpdate |= ResolveOmRtvHazards(uav); } } } } if (needsUpdate) { BindFramebuffer(); if constexpr (!IsDeferred) { // Doing this makes it less likely to flush during render passes auto imm = GetTypedContext(); if (!imm->m_hasPendingMsaaResolve || !m_device->perfHints().preferRenderPassOps) imm->ConsiderFlush(GpuFlushType::ImplicitMediumHint); imm->m_hasPendingMsaaResolve |= isMultisampled; } } } template void D3D11CommonContext::SetDrawBuffers( ID3D11Buffer* pBufferForArgs, ID3D11Buffer* pBufferForCount) { auto argBuffer = static_cast(pBufferForArgs); auto cntBuffer = static_cast(pBufferForCount); auto argBufferCookie = argBuffer ? argBuffer->GetCookie() : 0u; auto cntBufferCookie = cntBuffer ? cntBuffer->GetCookie() : 0u; if (m_state.id.argBufferCookie != argBufferCookie || m_state.id.cntBufferCookie != cntBufferCookie) { m_state.id.argBufferCookie = argBufferCookie; m_state.id.cntBufferCookie = cntBufferCookie; BindDrawBuffers(argBuffer, cntBuffer); } } template bool D3D11CommonContext::TestRtvUavHazards( UINT NumRTVs, ID3D11RenderTargetView* const* ppRTVs, UINT NumUAVs, ID3D11UnorderedAccessView* const* ppUAVs) { if (NumRTVs == D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL) NumRTVs = 0; if (NumUAVs == D3D11_KEEP_UNORDERED_ACCESS_VIEWS) NumUAVs = 0; for (uint32_t i = 0; i < NumRTVs; i++) { auto rtv = static_cast(ppRTVs[i]); if (!rtv) continue; for (uint32_t j = 0; j < i; j++) { if (CheckViewOverlap(rtv, static_cast(ppRTVs[j]))) return true; } if (rtv->HasBindFlag(D3D11_BIND_UNORDERED_ACCESS)) { for (uint32_t j = 0; j < NumUAVs; j++) { if (CheckViewOverlap(rtv, static_cast(ppUAVs[j]))) return true; } } } for (uint32_t i = 0; i < NumUAVs; i++) { auto uav = static_cast(ppUAVs[i]); if (!uav) continue; for (uint32_t j = 0; j < i; j++) { if (CheckViewOverlap(uav, static_cast(ppUAVs[j]))) return true; } } return false; } template template bool D3D11CommonContext::TestSrvHazards( D3D11ShaderResourceView* pView) { bool hazard = false; if (ShaderStage == DxbcProgramType::ComputeShader) { int32_t uav = m_state.uav.mask.findNext(0); while (uav >= 0 && !hazard) { hazard = CheckViewOverlap(pView, m_state.uav.views[uav].ptr()); uav = m_state.uav.mask.findNext(uav + 1); } } else { hazard = CheckViewOverlap(pView, m_state.om.dsv.ptr()); for (uint32_t i = 0; !hazard && i < m_state.om.maxRtv; i++) hazard = CheckViewOverlap(pView, m_state.om.rtvs[i].ptr()); for (uint32_t i = 0; !hazard && i < m_state.om.maxUav; i++) hazard = CheckViewOverlap(pView, m_state.om.uavs[i].ptr()); } return hazard; } template void D3D11CommonContext::TrackResourceSequenceNumber( ID3D11Resource* pResource) { if (!pResource) return; D3D11CommonTexture* texture = GetCommonTexture(pResource); if (texture) { if (texture->HasSequenceNumber()) { for (uint32_t i = 0; i < texture->CountSubresources(); i++) GetTypedContext()->TrackTextureSequenceNumber(texture, i); } } else { D3D11Buffer* buffer = static_cast(pResource); if (buffer->HasSequenceNumber()) GetTypedContext()->TrackBufferSequenceNumber(buffer); } } template void D3D11CommonContext::UpdateBuffer( D3D11Buffer* pDstBuffer, UINT Offset, UINT Length, const void* pSrcData) { constexpr uint32_t MaxDirectUpdateSize = 64u; DxvkBufferSlice bufferSlice = pDstBuffer->GetBufferSlice(Offset, Length); if (Length <= MaxDirectUpdateSize && !((Offset | Length) & 0x3)) { // The backend has special code paths for small buffer updates, // however both offset and size must be aligned to four bytes. // Write the data directly to the CS chunk. uint32_t dwordCount = Length / sizeof(uint32_t); EmitCsCmd(D3D11CmdType::None, dwordCount, [ cBufferSlice = std::move(bufferSlice) ] (DxvkContext* ctx, const uint32_t* data, size_t) { ctx->updateBuffer( cBufferSlice.buffer(), cBufferSlice.offset(), cBufferSlice.length(), data); }); // Compiler should be able to vectorize here, but GCC only does // if we cast the destination pointer to the correct type first auto src = reinterpret_cast(pSrcData); auto dst = reinterpret_cast(m_csData->first()); for (uint32_t i = 0; i < dwordCount; i++) new (dst + i) uint32_t(src[i]); } else { // Write directly to a staging buffer and dispatch a copy DxvkBufferSlice stagingSlice = AllocStagingBuffer(Length); std::memcpy(stagingSlice.mapPtr(0), pSrcData, Length); EmitCs([ cStagingSlice = std::move(stagingSlice), cBufferSlice = std::move(bufferSlice) ] (DxvkContext* ctx) { ctx->copyBuffer( cBufferSlice.buffer(), cBufferSlice.offset(), cStagingSlice.buffer(), cStagingSlice.offset(), cBufferSlice.length()); }); } if (pDstBuffer->HasSequenceNumber()) GetTypedContext()->TrackBufferSequenceNumber(pDstBuffer); if constexpr (!IsDeferred) static_cast(this)->ThrottleAllocation(); } template void D3D11CommonContext::UpdateTexture( D3D11CommonTexture* pDstTexture, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch) { if (DstSubresource >= pDstTexture->CountSubresources()) return; VkFormat packedFormat = pDstTexture->GetPackedFormat(); auto formatInfo = lookupFormatInfo(packedFormat); auto subresource = pDstTexture->GetSubresourceFromIndex( formatInfo->aspectMask, DstSubresource); VkExtent3D mipExtent = pDstTexture->MipLevelExtent(subresource.mipLevel); VkOffset3D offset = { 0, 0, 0 }; VkExtent3D extent = mipExtent; if (pDstBox != nullptr) { if (pDstBox->left >= pDstBox->right || pDstBox->top >= pDstBox->bottom || pDstBox->front >= pDstBox->back) return; // no-op, but legal offset.x = pDstBox->left; offset.y = pDstBox->top; offset.z = pDstBox->front; extent.width = pDstBox->right - pDstBox->left; extent.height = pDstBox->bottom - pDstBox->top; extent.depth = pDstBox->back - pDstBox->front; } if (!util::isBlockAligned(offset, extent, formatInfo->blockSize, mipExtent)) return; auto stagingSlice = AllocStagingBuffer(util::computeImageDataSize(packedFormat, extent)); util::packImageData(stagingSlice.mapPtr(0), pSrcData, SrcRowPitch, SrcDepthPitch, 0, 0, pDstTexture->GetVkImageType(), extent, 1, formatInfo, formatInfo->aspectMask); UpdateImage(pDstTexture, &subresource, offset, extent, std::move(stagingSlice)); if constexpr (!IsDeferred) static_cast(this)->ThrottleAllocation(); } template void D3D11CommonContext::UpdateImage( D3D11CommonTexture* pDstTexture, const VkImageSubresource* pDstSubresource, VkOffset3D DstOffset, VkExtent3D DstExtent, DxvkBufferSlice StagingBuffer) { bool dstIsImage = pDstTexture->HasImage(); uint32_t dstSubresource = D3D11CalcSubresource(pDstSubresource->mipLevel, pDstSubresource->arrayLayer, pDstTexture->Desc()->MipLevels); if (dstIsImage) { EmitCs([ cDstImage = pDstTexture->GetImage(), cDstLayers = vk::makeSubresourceLayers(*pDstSubresource), cDstOffset = DstOffset, cDstExtent = DstExtent, cStagingSlice = std::move(StagingBuffer), cPackedFormat = pDstTexture->GetPackedFormat() ] (DxvkContext* ctx) { ctx->copyBufferToImage(cDstImage, cDstLayers, cDstOffset, cDstExtent, cStagingSlice.buffer(), cStagingSlice.offset(), 0, 0, cPackedFormat); }); } else { // If the destination image is backed only by a buffer, we need to use // the packed buffer copy function which does not know about planes and // format metadata, so deal with it manually here. VkExtent3D dstMipExtent = pDstTexture->MipLevelExtent(pDstSubresource->mipLevel); auto dstFormat = pDstTexture->GetPackedFormat(); auto dstFormatInfo = lookupFormatInfo(dstFormat); uint32_t planeCount = 1; if (dstFormatInfo->flags.test(DxvkFormatFlag::MultiPlane)) planeCount = vk::getPlaneCount(dstFormatInfo->aspectMask); // The source data isn't stored in an image so we'll also need to // track the offset for that while iterating over the planes. VkDeviceSize srcPlaneOffset = 0; for (uint32_t i = 0; i < planeCount; i++) { VkImageAspectFlags dstAspectMask = dstFormatInfo->aspectMask; VkDeviceSize elementSize = dstFormatInfo->elementSize; VkExtent3D blockSize = dstFormatInfo->blockSize; if (dstFormatInfo->flags.test(DxvkFormatFlag::MultiPlane)) { dstAspectMask = vk::getPlaneAspect(i); auto plane = &dstFormatInfo->planes[i]; blockSize.width *= plane->blockSize.width; blockSize.height *= plane->blockSize.height; elementSize = plane->elementSize; } VkExtent3D blockCount = util::computeBlockCount(DstExtent, blockSize); EmitCs([ cDstBuffer = pDstTexture->GetMappedBuffer(dstSubresource), cDstStart = pDstTexture->GetSubresourceLayout(dstAspectMask, dstSubresource).Offset, cDstOffset = util::computeBlockOffset(DstOffset, blockSize), cDstSize = util::computeBlockCount(dstMipExtent, blockSize), cDstExtent = blockCount, cSrcBuffer = StagingBuffer.buffer(), cSrcStart = StagingBuffer.offset() + srcPlaneOffset, cPixelSize = elementSize ] (DxvkContext* ctx) { ctx->copyPackedBufferImage( cDstBuffer, cDstStart, cDstOffset, cDstSize, cSrcBuffer, cSrcStart, VkOffset3D(), cDstExtent, cDstExtent, cPixelSize); }); srcPlaneOffset += util::flattenImageExtent(blockCount) * elementSize; } } if (pDstTexture->HasSequenceNumber()) GetTypedContext()->TrackTextureSequenceNumber(pDstTexture, dstSubresource); } template void D3D11CommonContext::UpdateResource( ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch, UINT CopyFlags) { auto context = static_cast(this); D3D10DeviceLock lock = context->LockContext(); if (!pDstResource) return; // We need a different code path for buffers D3D11_RESOURCE_DIMENSION resourceType; pDstResource->GetType(&resourceType); if (likely(resourceType == D3D11_RESOURCE_DIMENSION_BUFFER)) { const auto bufferResource = static_cast(pDstResource); uint64_t bufferSize = bufferResource->Desc()->ByteWidth; // Provide a fast path for mapped buffer updates since some // games use UpdateSubresource to update constant buffers. if (likely(bufferResource->GetMapMode() == D3D11_COMMON_BUFFER_MAP_MODE_DIRECT) && likely(!pDstBox)) { context->UpdateMappedBuffer(bufferResource, 0, bufferSize, pSrcData, 0); return; } // Validate buffer range to update uint64_t offset = 0; uint64_t length = bufferSize; if (pDstBox) { offset = pDstBox->left; length = pDstBox->right - offset; } if (unlikely(offset + length > bufferSize)) return; // Still try to be fast if a box is provided but we update the full buffer if (likely(bufferResource->GetMapMode() == D3D11_COMMON_BUFFER_MAP_MODE_DIRECT)) { CopyFlags &= D3D11_COPY_DISCARD | D3D11_COPY_NO_OVERWRITE; if (likely(length == bufferSize) || unlikely(CopyFlags != 0)) { context->UpdateMappedBuffer(bufferResource, offset, length, pSrcData, CopyFlags); return; } } // Otherwise we can't really do anything fancy, so just do a GPU copy context->UpdateBuffer(bufferResource, offset, length, pSrcData); } else { D3D11CommonTexture* textureResource = GetCommonTexture(pDstResource); context->UpdateTexture(textureResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); } } template void D3D11CommonContext::UpdateUnorderedAccessViewCounter( D3D11UnorderedAccessView* pUav, uint32_t CounterValue) { EmitCs([ cView = pUav->GetCounterView(), cCounter = CounterValue ] (DxvkContext* ctx) { ctx->updateBuffer(cView->buffer(), cView->info().offset, sizeof(cCounter), &cCounter); }); } template bool D3D11CommonContext::ValidateRenderTargets( UINT NumViews, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView* pDepthStencilView) { Rc refView; VkExtent3D dsvExtent = { 0u, 0u, 0u }; VkExtent3D rtvExtent = { 0u, 0u, 0u }; if (pDepthStencilView != nullptr) { refView = static_cast( pDepthStencilView)->GetImageView(); dsvExtent = refView->mipLevelExtent(0); } for (uint32_t i = 0; i < NumViews; i++) { if (ppRenderTargetViews[i] != nullptr) { auto curView = static_cast( ppRenderTargetViews[i])->GetImageView(); if (!rtvExtent.width) rtvExtent = curView->mipLevelExtent(0); if (refView != nullptr) { // Render target views must all have the same sample count, // layer count, and type. The size can mismatch under certain // conditions, the D3D11 documentation is wrong here. if (curView->info().viewType != refView->info().viewType || curView->info().layerCount != refView->info().layerCount) return false; if (curView->image()->info().sampleCount != refView->image()->info().sampleCount) return false; // Color targets must all be the same size VkExtent3D curExtent = curView->mipLevelExtent(0); if (curExtent.width != rtvExtent.width || curExtent.height != rtvExtent.height) return false; } else { // Set reference view. All remaining views // must be compatible to the reference view. refView = curView; } } } // Based on testing, the depth-stencil target is allowed // to be larger than all color targets, but not smaller if (rtvExtent.width && dsvExtent.width) { if (rtvExtent.width > dsvExtent.width || rtvExtent.height > dsvExtent.height) return false; } return true; } template DxvkInputAssemblyState D3D11CommonContext::InitDefaultPrimitiveTopology() { return DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, false); } template DxvkRasterizerState D3D11CommonContext::InitDefaultRasterizerState() { DxvkRasterizerState rsState = { }; rsState.setPolygonMode(VK_POLYGON_MODE_FILL); rsState.setCullMode(VK_CULL_MODE_BACK_BIT); rsState.setFrontFace(VK_FRONT_FACE_CLOCKWISE); rsState.setDepthClip(true); rsState.setDepthBias(false); rsState.setConservativeMode(VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT); rsState.setSampleCount(0); rsState.setFlatShading(false); rsState.setLineMode(VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT); return rsState; } template DxvkDepthStencilState D3D11CommonContext::InitDefaultDepthStencilState() { DxvkDepthStencilState dsState = { }; dsState.setDepthTest(true); dsState.setDepthWrite(true); dsState.setDepthCompareOp(VK_COMPARE_OP_LESS); return dsState; } template DxvkMultisampleState D3D11CommonContext::InitDefaultMultisampleState( UINT SampleMask) { DxvkMultisampleState msState = { }; msState.setSampleMask(SampleMask); return msState; } template DxvkLogicOpState D3D11CommonContext::InitDefaultLogicOpState() { DxvkLogicOpState loState = { }; loState.setLogicOp(false, VK_LOGIC_OP_NO_OP); return loState; } template DxvkBlendMode D3D11CommonContext::InitDefaultBlendState() { DxvkBlendMode cbState = { }; cbState.setBlendEnable(false); cbState.setColorOp(VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD); cbState.setAlphaOp(VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD); cbState.setWriteMask(VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT); return cbState; } // Explicitly instantiate here template class D3D11CommonContext; template class D3D11CommonContext; } dxvk-2.6.1/src/d3d11/d3d11_context.h000066400000000000000000001424671477473124000166570ustar00rootroot00000000000000#pragma once #include #include #include "../dxvk/dxvk_adapter.h" #include "../dxvk/dxvk_cs.h" #include "../dxvk/dxvk_device.h" #include "../dxvk/dxvk_staging.h" #include "../d3d10/d3d10_multithread.h" #include "../util/util_flush.h" #include "d3d11_annotation.h" #include "d3d11_buffer.h" #include "d3d11_cmd.h" #include "d3d11_context_ext.h" #include "d3d11_context_state.h" #include "d3d11_device_child.h" #include "d3d11_texture.h" namespace dxvk { class D3D11DeferredContext; class D3D11ImmediateContext; template struct D3D11ContextObjectForwarder; /** * \brief Object forwarder for immediate contexts * * Binding methods can use this to efficiently bind objects * to the DXVK context without redundant reference counting. */ template<> struct D3D11ContextObjectForwarder { template static T&& move(T& object) { return std::move(object); } }; /** * \brief Object forwarder for deferred contexts * * This forwarder will create a copy of the object passed * into it, so that CS chunks can be reused if necessary. */ template<> struct D3D11ContextObjectForwarder { template static T move(const T& object) { return object; } }; /** * \brief Common D3D11 device context implementation * * Implements all common device context methods, but since this is * templates with the actual context type (deferred or immediate), * all methods can call back into context-specific methods without * having to use virtual methods. */ template class D3D11CommonContext : public D3D11DeviceChild { constexpr static bool IsDeferred = std::is_same_v; using Forwarder = D3D11ContextObjectForwarder; template friend class D3D11DeviceContextExt; template friend class D3D11UserDefinedAnnotation; // Use a local staging buffer to handle tiny uploads, most // of the time we're fine with hitting the global allocator constexpr static VkDeviceSize StagingBufferSize = 256ull << 10; protected: // Compile-time debug flag to force lazy binding on (True) or off (False) constexpr static Tristate DebugLazyBinding = Tristate::Auto; public: D3D11CommonContext( D3D11Device* pParent, const Rc& Device, UINT ContextFlags, DxvkCsChunkFlags CsFlags); ~D3D11CommonContext(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); D3D11_DEVICE_CONTEXT_TYPE STDMETHODCALLTYPE GetType(); UINT STDMETHODCALLTYPE GetContextFlags(); void STDMETHODCALLTYPE ClearState(); void STDMETHODCALLTYPE DiscardResource(ID3D11Resource *pResource); void STDMETHODCALLTYPE DiscardView(ID3D11View* pResourceView); void STDMETHODCALLTYPE DiscardView1( ID3D11View* pResourceView, const D3D11_RECT* pRects, UINT NumRects); void STDMETHODCALLTYPE DiscardViewBase( ID3D11View* pResourceView, const D3D11_RECT* pRects, UINT NumRects); void STDMETHODCALLTYPE CopySubresourceRegion( ID3D11Resource* pDstResource, UINT DstSubresource, UINT DstX, UINT DstY, UINT DstZ, ID3D11Resource* pSrcResource, UINT SrcSubresource, const D3D11_BOX* pSrcBox); void STDMETHODCALLTYPE CopySubresourceRegion1( ID3D11Resource* pDstResource, UINT DstSubresource, UINT DstX, UINT DstY, UINT DstZ, ID3D11Resource* pSrcResource, UINT SrcSubresource, const D3D11_BOX* pSrcBox, UINT CopyFlags); void STDMETHODCALLTYPE CopySubresourceRegionBase( ID3D11Resource* pDstResource, UINT DstSubresource, UINT DstX, UINT DstY, UINT DstZ, ID3D11Resource* pSrcResource, UINT SrcSubresource, const D3D11_BOX* pSrcBox, UINT CopyFlags); void STDMETHODCALLTYPE CopyResource( ID3D11Resource* pDstResource, ID3D11Resource* pSrcResource); void STDMETHODCALLTYPE CopyStructureCount( ID3D11Buffer* pDstBuffer, UINT DstAlignedByteOffset, ID3D11UnorderedAccessView* pSrcView); void STDMETHODCALLTYPE ClearRenderTargetView( ID3D11RenderTargetView* pRenderTargetView, const FLOAT ColorRGBA[4]); void STDMETHODCALLTYPE ClearUnorderedAccessViewUint( ID3D11UnorderedAccessView* pUnorderedAccessView, const UINT Values[4]); void STDMETHODCALLTYPE ClearUnorderedAccessViewFloat( ID3D11UnorderedAccessView* pUnorderedAccessView, const FLOAT Values[4]); void STDMETHODCALLTYPE ClearDepthStencilView( ID3D11DepthStencilView* pDepthStencilView, UINT ClearFlags, FLOAT Depth, UINT8 Stencil); void STDMETHODCALLTYPE ClearView( ID3D11View *pView, const FLOAT Color[4], const D3D11_RECT *pRect, UINT NumRects); void STDMETHODCALLTYPE GenerateMips( ID3D11ShaderResourceView* pShaderResourceView); void STDMETHODCALLTYPE ResolveSubresource( ID3D11Resource* pDstResource, UINT DstSubresource, ID3D11Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format); void STDMETHODCALLTYPE UpdateSubresource( ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch); void STDMETHODCALLTYPE UpdateSubresource1( ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch, UINT CopyFlags); void STDMETHODCALLTYPE DrawAuto(); void STDMETHODCALLTYPE Draw( UINT VertexCount, UINT StartVertexLocation); void STDMETHODCALLTYPE DrawIndexed( UINT IndexCount, UINT StartIndexLocation, INT BaseVertexLocation); void STDMETHODCALLTYPE DrawInstanced( UINT VertexCountPerInstance, UINT InstanceCount, UINT StartVertexLocation, UINT StartInstanceLocation); void STDMETHODCALLTYPE DrawIndexedInstanced( UINT IndexCountPerInstance, UINT InstanceCount, UINT StartIndexLocation, INT BaseVertexLocation, UINT StartInstanceLocation); void STDMETHODCALLTYPE DrawIndexedInstancedIndirect( ID3D11Buffer* pBufferForArgs, UINT AlignedByteOffsetForArgs); void STDMETHODCALLTYPE DrawInstancedIndirect( ID3D11Buffer* pBufferForArgs, UINT AlignedByteOffsetForArgs); void STDMETHODCALLTYPE Dispatch( UINT ThreadGroupCountX, UINT ThreadGroupCountY, UINT ThreadGroupCountZ); void STDMETHODCALLTYPE DispatchIndirect( ID3D11Buffer* pBufferForArgs, UINT AlignedByteOffsetForArgs); void STDMETHODCALLTYPE IASetInputLayout( ID3D11InputLayout* pInputLayout); void STDMETHODCALLTYPE IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY Topology); void STDMETHODCALLTYPE IASetVertexBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppVertexBuffers, const UINT* pStrides, const UINT* pOffsets); void STDMETHODCALLTYPE IASetIndexBuffer( ID3D11Buffer* pIndexBuffer, DXGI_FORMAT Format, UINT Offset); void STDMETHODCALLTYPE IAGetInputLayout( ID3D11InputLayout** ppInputLayout); void STDMETHODCALLTYPE IAGetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY* pTopology); void STDMETHODCALLTYPE IAGetVertexBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppVertexBuffers, UINT* pStrides, UINT* pOffsets); void STDMETHODCALLTYPE IAGetIndexBuffer( ID3D11Buffer** ppIndexBuffer, DXGI_FORMAT* pFormat, UINT* pOffset); void STDMETHODCALLTYPE VSSetShader( ID3D11VertexShader* pVertexShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances); void STDMETHODCALLTYPE VSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers); void STDMETHODCALLTYPE VSSetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers, const UINT* pFirstConstant, const UINT* pNumConstants); void STDMETHODCALLTYPE VSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews); void STDMETHODCALLTYPE VSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers); void STDMETHODCALLTYPE VSGetShader( ID3D11VertexShader** ppVertexShader, ID3D11ClassInstance** ppClassInstances, UINT* pNumClassInstances); void STDMETHODCALLTYPE VSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers); void STDMETHODCALLTYPE VSGetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers, UINT* pFirstConstant, UINT* pNumConstants); void STDMETHODCALLTYPE VSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView** ppShaderResourceViews); void STDMETHODCALLTYPE VSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState** ppSamplers); void STDMETHODCALLTYPE HSSetShader( ID3D11HullShader* pHullShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances); void STDMETHODCALLTYPE HSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers); void STDMETHODCALLTYPE HSSetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers, const UINT* pFirstConstant, const UINT* pNumConstants); void STDMETHODCALLTYPE HSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews); void STDMETHODCALLTYPE HSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers); void STDMETHODCALLTYPE HSGetShader( ID3D11HullShader** ppHullShader, ID3D11ClassInstance** ppClassInstances, UINT* pNumClassInstances); void STDMETHODCALLTYPE HSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers); void STDMETHODCALLTYPE HSGetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers, UINT* pFirstConstant, UINT* pNumConstants); void STDMETHODCALLTYPE HSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView** ppShaderResourceViews); void STDMETHODCALLTYPE HSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState** ppSamplers); void STDMETHODCALLTYPE DSSetShader( ID3D11DomainShader* pDomainShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances); void STDMETHODCALLTYPE DSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers); void STDMETHODCALLTYPE DSSetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers, const UINT* pFirstConstant, const UINT* pNumConstants); void STDMETHODCALLTYPE DSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews); void STDMETHODCALLTYPE DSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers); void STDMETHODCALLTYPE DSGetShader( ID3D11DomainShader** ppDomainShader, ID3D11ClassInstance** ppClassInstances, UINT* pNumClassInstances); void STDMETHODCALLTYPE DSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers); void STDMETHODCALLTYPE DSGetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers, UINT* pFirstConstant, UINT* pNumConstants); void STDMETHODCALLTYPE DSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView** ppShaderResourceViews); void STDMETHODCALLTYPE DSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState** ppSamplers); void STDMETHODCALLTYPE GSSetShader( ID3D11GeometryShader* pShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances); void STDMETHODCALLTYPE GSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers); void STDMETHODCALLTYPE GSSetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers, const UINT* pFirstConstant, const UINT* pNumConstants); void STDMETHODCALLTYPE GSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews); void STDMETHODCALLTYPE GSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers); void STDMETHODCALLTYPE GSGetShader( ID3D11GeometryShader** ppGeometryShader, ID3D11ClassInstance** ppClassInstances, UINT* pNumClassInstances); void STDMETHODCALLTYPE GSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers); void STDMETHODCALLTYPE GSGetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers, UINT* pFirstConstant, UINT* pNumConstants); void STDMETHODCALLTYPE GSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView** ppShaderResourceViews); void STDMETHODCALLTYPE GSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState** ppSamplers); void STDMETHODCALLTYPE PSSetShader( ID3D11PixelShader* pPixelShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances); void STDMETHODCALLTYPE PSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers); void STDMETHODCALLTYPE PSSetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers, const UINT* pFirstConstant, const UINT* pNumConstants); void STDMETHODCALLTYPE PSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews); void STDMETHODCALLTYPE PSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers); void STDMETHODCALLTYPE PSGetShader( ID3D11PixelShader** ppPixelShader, ID3D11ClassInstance** ppClassInstances, UINT* pNumClassInstances); void STDMETHODCALLTYPE PSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers); void STDMETHODCALLTYPE PSGetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers, UINT* pFirstConstant, UINT* pNumConstants); void STDMETHODCALLTYPE PSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView** ppShaderResourceViews); void STDMETHODCALLTYPE PSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState** ppSamplers); void STDMETHODCALLTYPE CSSetShader( ID3D11ComputeShader* pComputeShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances); void STDMETHODCALLTYPE CSSetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers); void STDMETHODCALLTYPE CSSetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers, const UINT* pFirstConstant, const UINT* pNumConstants); void STDMETHODCALLTYPE CSSetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews); void STDMETHODCALLTYPE CSSetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers); void STDMETHODCALLTYPE CSSetUnorderedAccessViews( UINT StartSlot, UINT NumUAVs, ID3D11UnorderedAccessView* const* ppUnorderedAccessViews, const UINT* pUAVInitialCounts); void STDMETHODCALLTYPE CSGetShader( ID3D11ComputeShader** ppComputeShader, ID3D11ClassInstance** ppClassInstances, UINT* pNumClassInstances); void STDMETHODCALLTYPE CSGetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers); void STDMETHODCALLTYPE CSGetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers, UINT* pFirstConstant, UINT* pNumConstants); void STDMETHODCALLTYPE CSGetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView** ppShaderResourceViews); void STDMETHODCALLTYPE CSGetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState** ppSamplers); void STDMETHODCALLTYPE CSGetUnorderedAccessViews( UINT StartSlot, UINT NumUAVs, ID3D11UnorderedAccessView** ppUnorderedAccessViews); void STDMETHODCALLTYPE OMSetRenderTargets( UINT NumViews, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView* pDepthStencilView); void STDMETHODCALLTYPE OMSetRenderTargetsAndUnorderedAccessViews( UINT NumRTVs, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView* pDepthStencilView, UINT UAVStartSlot, UINT NumUAVs, ID3D11UnorderedAccessView* const* ppUnorderedAccessViews, const UINT* pUAVInitialCounts); void STDMETHODCALLTYPE OMSetBlendState( ID3D11BlendState* pBlendState, const FLOAT BlendFactor[4], UINT SampleMask); void STDMETHODCALLTYPE OMSetDepthStencilState( ID3D11DepthStencilState* pDepthStencilState, UINT StencilRef); void STDMETHODCALLTYPE OMGetRenderTargets( UINT NumViews, ID3D11RenderTargetView** ppRenderTargetViews, ID3D11DepthStencilView** ppDepthStencilView); void STDMETHODCALLTYPE OMGetRenderTargetsAndUnorderedAccessViews( UINT NumRTVs, ID3D11RenderTargetView** ppRenderTargetViews, ID3D11DepthStencilView** ppDepthStencilView, UINT UAVStartSlot, UINT NumUAVs, ID3D11UnorderedAccessView** ppUnorderedAccessViews); void STDMETHODCALLTYPE OMGetBlendState( ID3D11BlendState** ppBlendState, FLOAT BlendFactor[4], UINT* pSampleMask); void STDMETHODCALLTYPE OMGetDepthStencilState( ID3D11DepthStencilState** ppDepthStencilState, UINT* pStencilRef); void STDMETHODCALLTYPE RSSetState( ID3D11RasterizerState* pRasterizerState); void STDMETHODCALLTYPE RSSetViewports( UINT NumViewports, const D3D11_VIEWPORT* pViewports); void STDMETHODCALLTYPE RSSetScissorRects( UINT NumRects, const D3D11_RECT* pRects); void STDMETHODCALLTYPE RSGetState( ID3D11RasterizerState** ppRasterizerState); void STDMETHODCALLTYPE RSGetViewports( UINT* pNumViewports, D3D11_VIEWPORT* pViewports); void STDMETHODCALLTYPE RSGetScissorRects( UINT* pNumRects, D3D11_RECT* pRects); void STDMETHODCALLTYPE SOSetTargets( UINT NumBuffers, ID3D11Buffer* const* ppSOTargets, const UINT* pOffsets); void STDMETHODCALLTYPE SOGetTargets( UINT NumBuffers, ID3D11Buffer** ppSOTargets); void STDMETHODCALLTYPE SOGetTargetsWithOffsets( UINT NumBuffers, ID3D11Buffer** ppSOTargets, UINT* pOffsets); void STDMETHODCALLTYPE SetPredication( ID3D11Predicate* pPredicate, BOOL PredicateValue); void STDMETHODCALLTYPE GetPredication( ID3D11Predicate** ppPredicate, BOOL* pPredicateValue); void STDMETHODCALLTYPE SetResourceMinLOD( ID3D11Resource* pResource, FLOAT MinLOD); FLOAT STDMETHODCALLTYPE GetResourceMinLOD( ID3D11Resource* pResource); void STDMETHODCALLTYPE CopyTiles( ID3D11Resource* pTiledResource, const D3D11_TILED_RESOURCE_COORDINATE* pTileRegionStartCoordinate, const D3D11_TILE_REGION_SIZE* pTileRegionSize, ID3D11Buffer* pBuffer, UINT64 BufferStartOffsetInBytes, UINT Flags); HRESULT STDMETHODCALLTYPE CopyTileMappings( ID3D11Resource* pDestTiledResource, const D3D11_TILED_RESOURCE_COORDINATE* pDestRegionCoordinate, ID3D11Resource* pSourceTiledResource, const D3D11_TILED_RESOURCE_COORDINATE* pSourceRegionCoordinate, const D3D11_TILE_REGION_SIZE* pTileRegionSize, UINT Flags); HRESULT STDMETHODCALLTYPE ResizeTilePool( ID3D11Buffer* pTilePool, UINT64 NewSizeInBytes); void STDMETHODCALLTYPE TiledResourceBarrier( ID3D11DeviceChild* pTiledResourceOrViewAccessBeforeBarrier, ID3D11DeviceChild* pTiledResourceOrViewAccessAfterBarrier); HRESULT STDMETHODCALLTYPE UpdateTileMappings( ID3D11Resource* pTiledResource, UINT NumRegions, const D3D11_TILED_RESOURCE_COORDINATE* pRegionCoordinates, const D3D11_TILE_REGION_SIZE* pRegionSizes, ID3D11Buffer* pTilePool, UINT NumRanges, const UINT* pRangeFlags, const UINT* pRangeTileOffsets, const UINT* pRangeTileCounts, UINT Flags); void STDMETHODCALLTYPE UpdateTiles( ID3D11Resource* pDestTiledResource, const D3D11_TILED_RESOURCE_COORDINATE* pDestTileRegionStartCoordinate, const D3D11_TILE_REGION_SIZE* pDestTileRegionSize, const void* pSourceTileData, UINT Flags); BOOL STDMETHODCALLTYPE IsAnnotationEnabled(); void STDMETHODCALLTYPE SetMarkerInt( LPCWSTR pLabel, INT Data); void STDMETHODCALLTYPE BeginEventInt( LPCWSTR pLabel, INT Data); void STDMETHODCALLTYPE EndEvent(); void STDMETHODCALLTYPE GetHardwareProtectionState( BOOL* pHwProtectionEnable); void STDMETHODCALLTYPE SetHardwareProtectionState( BOOL HwProtectionEnable); void STDMETHODCALLTYPE TransitionSurfaceLayout( IDXGIVkInteropSurface* pSurface, const VkImageSubresourceRange* pSubresources, VkImageLayout OldLayout, VkImageLayout NewLayout); protected: D3D11DeviceContextExt m_contextExt; D3D11UserDefinedAnnotation m_annotation; Rc m_device; D3D11ContextState m_state; UINT m_flags; DxvkStagingBuffer m_staging; D3D11CmdType m_csDataType = D3D11CmdType::None; DxvkCsChunkFlags m_csFlags; DxvkCsChunkRef m_csChunk; DxvkCsDataBlock* m_csData = nullptr; DxvkLocalAllocationCache m_allocationCache; DxvkCsChunkRef AllocCsChunk(); DxvkBufferSlice AllocStagingBuffer( VkDeviceSize Size); void ApplyDirtyConstantBuffers( DxbcProgramType Stage, const DxbcBindingMask& BoundMask, DxbcBindingMask& DirtyMask); void ApplyDirtySamplers( DxbcProgramType Stage, const DxbcBindingMask& BoundMask, DxbcBindingMask& DirtyMask); void ApplyDirtyShaderResources( DxbcProgramType Stage, const DxbcBindingMask& BoundMask, DxbcBindingMask& DirtyMask); void ApplyDirtyUnorderedAccessViews( DxbcProgramType Stage, const DxbcBindingMask& BoundMask, DxbcBindingMask& DirtyMask); void ApplyDirtyGraphicsBindings(); void ApplyDirtyComputeBindings(); void ApplyInputLayout(); void ApplyPrimitiveTopology(); void ApplyBlendState(); void ApplyBlendFactor(); void ApplyDepthStencilState(); void ApplyStencilRef(); void ApplyRasterizerState(); void ApplyRasterizerSampleCount(); void ApplyViewportState(); void BatchDraw( const VkDrawIndirectCommand& draw); void BatchDrawIndexed( const VkDrawIndexedIndirectCommand& draw); template void BindShader( const D3D11CommonShader* pShaderModule); void BindFramebuffer(); void BindDrawBuffers( D3D11Buffer* pBufferForArgs, D3D11Buffer* pBufferForCount); void BindVertexBuffer( UINT Slot, D3D11Buffer* pBuffer, UINT Offset, UINT Stride); void BindVertexBufferRange( UINT Slot, D3D11Buffer* pBuffer, UINT Offset, UINT Stride); void BindIndexBuffer( D3D11Buffer* pBuffer, UINT Offset, DXGI_FORMAT Format); void BindIndexBufferRange( D3D11Buffer* pBuffer, UINT Offset, DXGI_FORMAT Format); void BindXfbBuffer( UINT Slot, D3D11Buffer* pBuffer, UINT Offset); void BindConstantBuffer( DxbcProgramType ShaderStage, UINT Slot, D3D11Buffer* pBuffer, UINT Offset, UINT Length); void BindConstantBufferRange( DxbcProgramType ShaderStage, UINT Slot, UINT Offset, UINT Length); void BindSampler( DxbcProgramType ShaderStage, UINT Slot, D3D11SamplerState* pSampler); void BindShaderResource( DxbcProgramType ShaderStage, UINT Slot, D3D11ShaderResourceView* pResource); void BindUnorderedAccessView( DxbcProgramType ShaderStage, UINT Slot, D3D11UnorderedAccessView* pUav); VkClearValue ConvertColorValue( const FLOAT Color[4], const DxvkFormatInfo* pFormatInfo); void CopyBuffer( D3D11Buffer* pDstBuffer, VkDeviceSize DstOffset, D3D11Buffer* pSrcBuffer, VkDeviceSize SrcOffset, VkDeviceSize ByteCount); void CopyImage( D3D11CommonTexture* pDstTexture, const VkImageSubresourceLayers* pDstLayers, VkOffset3D DstOffset, D3D11CommonTexture* pSrcTexture, const VkImageSubresourceLayers* pSrcLayers, VkOffset3D SrcOffset, VkExtent3D SrcExtent); void CopyTiledResourceData( ID3D11Resource* pResource, const D3D11_TILED_RESOURCE_COORDINATE* pRegionCoordinate, const D3D11_TILE_REGION_SIZE* pRegionSize, DxvkBufferSlice BufferSlice, UINT Flags); template bool DirtyBindingGeneric( DxbcProgramType ShaderStage, T BoundMask, T& DirtyMask, T DirtyBit, bool IsNull); bool DirtyConstantBuffer( DxbcProgramType ShaderStage, uint32_t Slot, bool IsNull); bool DirtySampler( DxbcProgramType ShaderStage, uint32_t Slot, bool IsNull); bool DirtyShaderResource( DxbcProgramType ShaderStage, uint32_t Slot, bool IsNull); bool DirtyComputeUnorderedAccessView( uint32_t Slot, bool IsNull); bool DirtyGraphicsUnorderedAccessView( uint32_t Slot); void DiscardBuffer( ID3D11Resource* pResource); void DiscardTexture( ID3D11Resource* pResource, UINT Subresource); template void GetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer** ppConstantBuffers, UINT* pFirstConstant, UINT* pNumConstants); template void GetShaderResources( UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView** ppShaderResourceViews); template void GetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState** ppSamplers); DxvkGlobalPipelineBarrier GetTiledResourceDependency( ID3D11DeviceChild* pObject); D3D11MaxUsedBindings GetMaxUsedBindings(); bool HasDirtyComputeBindings(); bool HasDirtyGraphicsBindings(); void ResetCommandListState(); void ResetContextState(); void ResetDirtyTracking(); void ResetStagingBuffer(); template void ResolveSrvHazards( T* pView); template void ResolveCsSrvHazards( T* pView); template void ResolveOmSrvHazards( T* pView); bool ResolveOmRtvHazards( D3D11UnorderedAccessView* pView); void ResolveOmUavHazards( D3D11RenderTargetView* pView); void RestoreCommandListState(); void RestoreConstantBuffers( DxbcProgramType Stage); void RestoreSamplers( DxbcProgramType Stage); void RestoreShaderResources( DxbcProgramType Stage); void RestoreUnorderedAccessViews( DxbcProgramType Stage); template void SetConstantBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers); template void SetConstantBuffers1( UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers, const UINT* pFirstConstant, const UINT* pNumConstants); template void SetShaderResources( UINT StartSlot, UINT NumResources, ID3D11ShaderResourceView* const* ppResources); template void SetSamplers( UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers); void SetRenderTargetsAndUnorderedAccessViews( UINT NumRTVs, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView* pDepthStencilView, UINT UAVStartSlot, UINT NumUAVs, ID3D11UnorderedAccessView* const* ppUnorderedAccessViews, const UINT* pUAVInitialCounts); void SetDrawBuffers( ID3D11Buffer* pBufferForArgs, ID3D11Buffer* pBufferForCount); bool TestRtvUavHazards( UINT NumRTVs, ID3D11RenderTargetView* const* ppRTVs, UINT NumUAVs, ID3D11UnorderedAccessView* const* ppUAVs); template bool TestSrvHazards( D3D11ShaderResourceView* pView); void TrackResourceSequenceNumber( ID3D11Resource* pResource); void UpdateBuffer( D3D11Buffer* pDstBuffer, UINT Offset, UINT Length, const void* pSrcData); void UpdateTexture( D3D11CommonTexture* pDstTexture, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch); void UpdateImage( D3D11CommonTexture* pDstTexture, const VkImageSubresource* pDstSubresource, VkOffset3D DstOffset, VkExtent3D DstExtent, DxvkBufferSlice StagingBuffer); void UpdateResource( ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch, UINT CopyFlags); void UpdateUnorderedAccessViewCounter( D3D11UnorderedAccessView* pUav, uint32_t CounterValue); bool ValidateRenderTargets( UINT NumViews, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView* pDepthStencilView); static DxvkInputAssemblyState InitDefaultPrimitiveTopology(); static DxvkRasterizerState InitDefaultRasterizerState(); static DxvkDepthStencilState InitDefaultDepthStencilState(); static DxvkMultisampleState InitDefaultMultisampleState( UINT SampleMask); static DxvkLogicOpState InitDefaultLogicOpState(); static DxvkBlendMode InitDefaultBlendState(); template void EmitCs(Cmd&& command) { if (unlikely(m_csDataType != D3D11CmdType::None)) { m_csData = nullptr; m_csDataType = D3D11CmdType::None; } if (unlikely(!m_csChunk->push(command))) { GetTypedContext()->EmitCsChunk(std::move(m_csChunk)); m_csChunk = AllocCsChunk(); if constexpr (!IsDeferred && AllowFlush) GetTypedContext()->ConsiderFlush(GpuFlushType::ImplicitWeakHint); m_csChunk->push(command); } } template void EmitCsCmd(D3D11CmdType type, size_t count, Cmd&& command) { m_csDataType = type; m_csData = m_csChunk->pushCmd(command, count); if (unlikely(!m_csData)) { GetTypedContext()->EmitCsChunk(std::move(m_csChunk)); m_csChunk = AllocCsChunk(); if constexpr (!IsDeferred && AllowFlush) GetTypedContext()->ConsiderFlush(GpuFlushType::ImplicitWeakHint); // We must record this command after the potential // flush since the caller may still access the data m_csData = m_csChunk->pushCmd(command, count); } } void FlushCsChunk() { if (likely(!m_csChunk->empty())) { m_csData = nullptr; m_csDataType = D3D11CmdType::None; GetTypedContext()->EmitCsChunk(std::move(m_csChunk)); m_csChunk = AllocCsChunk(); } } template const D3D11CommonShader* GetCommonShader(T* pShader) const { return pShader != nullptr ? pShader->GetCommonShader() : nullptr; } static uint32_t GetIndirectCommandStride(const D3D11CmdDrawIndirectData* cmdData, uint32_t offset, uint32_t minStride) { if (likely(cmdData->stride)) return cmdData->offset + cmdData->count * cmdData->stride == offset ? cmdData->stride : 0; uint32_t stride = offset - cmdData->offset; return stride >= minStride && stride <= 32 ? stride : 0; } static bool ValidateDrawBufferSize(ID3D11Buffer* pBuffer, UINT Offset, UINT Size) { UINT bufferSize = 0; if (likely(pBuffer != nullptr)) bufferSize = static_cast(pBuffer)->Desc()->ByteWidth; return uint64_t(bufferSize) >= uint64_t(Offset) + uint64_t(Size); } private: ContextType* GetTypedContext() { return static_cast(this); } D3D10DeviceLock LockContext() { return GetTypedContext()->LockContext(); } }; } dxvk-2.6.1/src/d3d11/d3d11_context_def.cpp000066400000000000000000000325151477473124000200200ustar00rootroot00000000000000#include "d3d11_context_def.h" #include "d3d11_device.h" namespace dxvk { D3D11DeferredContext::D3D11DeferredContext( D3D11Device* pParent, const Rc& Device, UINT ContextFlags) : D3D11CommonContext(pParent, Device, ContextFlags, 0u), m_commandList (CreateCommandList()) { ResetContextState(); } HRESULT STDMETHODCALLTYPE D3D11DeferredContext::GetData( ID3D11Asynchronous* pAsync, void* pData, UINT DataSize, UINT GetDataFlags) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D11: GetData called on a deferred context"); return DXGI_ERROR_INVALID_CALL; } void STDMETHODCALLTYPE D3D11DeferredContext::Begin( ID3D11Asynchronous* pAsync) { D3D10DeviceLock lock = LockContext(); if (unlikely(!pAsync)) return; Com query(static_cast(pAsync)); if (unlikely(!query->IsScoped())) return; auto entry = std::find( m_queriesBegun.begin(), m_queriesBegun.end(), query); if (unlikely(entry != m_queriesBegun.end())) return; EmitCs([cQuery = query] (DxvkContext* ctx) { cQuery->Begin(ctx); }); m_queriesBegun.push_back(std::move(query)); } void STDMETHODCALLTYPE D3D11DeferredContext::End( ID3D11Asynchronous* pAsync) { D3D10DeviceLock lock = LockContext(); if (unlikely(!pAsync)) return; Com query(static_cast(pAsync)); if (query->IsScoped()) { auto entry = std::find( m_queriesBegun.begin(), m_queriesBegun.end(), query); if (likely(entry != m_queriesBegun.end())) { m_queriesBegun.erase(entry); } else { EmitCs([cQuery = query] (DxvkContext* ctx) { cQuery->Begin(ctx); }); } } m_commandList->AddQuery(query.ptr()); EmitCs([cQuery = std::move(query)] (DxvkContext* ctx) { cQuery->End(ctx); }); } void STDMETHODCALLTYPE D3D11DeferredContext::Flush() { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D11: Flush called on a deferred context"); } void STDMETHODCALLTYPE D3D11DeferredContext::Flush1( D3D11_CONTEXT_TYPE ContextType, HANDLE hEvent) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D11: Flush1 called on a deferred context"); } HRESULT STDMETHODCALLTYPE D3D11DeferredContext::Signal( ID3D11Fence* pFence, UINT64 Value) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D11: Signal called on a deferred context"); return DXGI_ERROR_INVALID_CALL; } HRESULT STDMETHODCALLTYPE D3D11DeferredContext::Wait( ID3D11Fence* pFence, UINT64 Value) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D11: Wait called on a deferred context"); return DXGI_ERROR_INVALID_CALL; } void STDMETHODCALLTYPE D3D11DeferredContext::ExecuteCommandList( ID3D11CommandList* pCommandList, BOOL RestoreContextState) { D3D10DeviceLock lock = LockContext(); // Clear state so that the command list can't observe any // current context state. The command list itself will clean // up after execution to ensure that no state changes done // by the command list are visible to the immediate context. ResetCommandListState(); // Flush any outstanding commands so that // we don't mess up the execution order FlushCsChunk(); // Record any chunks from the given command list into the // current command list and deal with context state auto commandList = static_cast(pCommandList); m_chunkId = m_commandList->AddCommandList(commandList); // Restore deferred context state if (RestoreContextState) RestoreCommandListState(); else ResetContextState(); } HRESULT STDMETHODCALLTYPE D3D11DeferredContext::FinishCommandList( BOOL RestoreDeferredContextState, ID3D11CommandList **ppCommandList) { D3D10DeviceLock lock = LockContext(); // End all queries that were left active by the app FinalizeQueries(); // Clean up command list state so that the any state changed // by this command list does not affect the calling context. // This also ensures that the command list is never empty. ResetCommandListState(); // Make sure all commands are visible to the command list FlushCsChunk(); if (ppCommandList) *ppCommandList = m_commandList.ref(); // Create a clean command list, and if requested, restore all // previously set context state. Otherwise, reset the context. // Any use of ExecuteCommandList will reset command list state // before the command list is actually executed. m_commandList = CreateCommandList(); m_chunkId = 0; if (RestoreDeferredContextState) RestoreCommandListState(); else ResetContextState(); m_mappedResources.clear(); ResetStagingBuffer(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DeferredContext::Map( ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource) { D3D10DeviceLock lock = LockContext(); if (unlikely(!pResource || !pMappedResource)) return E_INVALIDARG; if (likely(MapType == D3D11_MAP_WRITE_DISCARD)) { D3D11_RESOURCE_DIMENSION resourceDim; pResource->GetType(&resourceDim); return likely(resourceDim == D3D11_RESOURCE_DIMENSION_BUFFER) ? MapBuffer(pResource, pMappedResource) : MapImage(pResource, Subresource, pMappedResource); } else if (likely(MapType == D3D11_MAP_WRITE_NO_OVERWRITE)) { // The resource must be mapped with D3D11_MAP_WRITE_DISCARD // before it can be mapped with D3D11_MAP_WRITE_NO_OVERWRITE. D3D11_RESOURCE_DIMENSION resourceDim; pResource->GetType(&resourceDim); if (likely(resourceDim == D3D11_RESOURCE_DIMENSION_BUFFER)) { D3D11_MAPPED_SUBRESOURCE sr = FindMapEntry(static_cast(pResource)->GetCookie()); pMappedResource->pData = sr.pData; if (unlikely(!sr.pData)) return D3D11_ERROR_DEFERRED_CONTEXT_MAP_WITHOUT_INITIAL_DISCARD; pMappedResource->RowPitch = sr.RowPitch; pMappedResource->DepthPitch = sr.DepthPitch; return S_OK; } else { // Images cannot be mapped with NO_OVERWRITE pMappedResource->pData = nullptr; return E_INVALIDARG; } } else { // Not allowed on deferred contexts pMappedResource->pData = nullptr; return E_INVALIDARG; } } void STDMETHODCALLTYPE D3D11DeferredContext::Unmap( ID3D11Resource* pResource, UINT Subresource) { // No-op, updates are committed in Map } void STDMETHODCALLTYPE D3D11DeferredContext::SwapDeviceContextState( ID3DDeviceContextState* pState, ID3DDeviceContextState** ppPreviousState) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D11: SwapDeviceContextState called on a deferred context"); } HRESULT D3D11DeferredContext::MapBuffer( ID3D11Resource* pResource, D3D11_MAPPED_SUBRESOURCE* pMappedResource) { D3D11Buffer* pBuffer = static_cast(pResource); if (unlikely(pBuffer->GetMapMode() == D3D11_COMMON_BUFFER_MAP_MODE_NONE)) { Logger::err("D3D11: Cannot map a device-local buffer"); pMappedResource->pData = nullptr; return E_INVALIDARG; } auto bufferSlice = pBuffer->AllocSlice(&m_allocationCache); pMappedResource->pData = bufferSlice->mapPtr(); pMappedResource->RowPitch = pBuffer->Desc()->ByteWidth; pMappedResource->DepthPitch = pBuffer->Desc()->ByteWidth; EmitCs([ cDstBuffer = pBuffer->GetBuffer(), cDstSlice = std::move(bufferSlice) ] (DxvkContext* ctx) { ctx->invalidateBuffer(cDstBuffer, Rc(cDstSlice)); }); AddMapEntry(pBuffer->GetCookie(), *pMappedResource); return S_OK; } HRESULT D3D11DeferredContext::MapImage( ID3D11Resource* pResource, UINT Subresource, D3D11_MAPPED_SUBRESOURCE* pMappedResource) { D3D11CommonTexture* pTexture = GetCommonTexture(pResource); if (unlikely(Subresource >= pTexture->CountSubresources())) { pMappedResource->pData = nullptr; return E_INVALIDARG; } if (unlikely(pTexture->Desc()->Usage != D3D11_USAGE_DYNAMIC)) { pMappedResource->pData = nullptr; return E_INVALIDARG; } VkFormat packedFormat = pTexture->GetPackedFormat(); auto formatInfo = lookupFormatInfo(packedFormat); auto layout = pTexture->GetSubresourceLayout(formatInfo->aspectMask, Subresource); if (pTexture->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT) { auto storage = pTexture->AllocStorage(); auto mapPtr = storage->mapPtr(); EmitCs([ cImage = pTexture->GetImage(), cStorage = std::move(storage) ] (DxvkContext* ctx) { ctx->invalidateImage(cImage, Rc(cStorage)); ctx->initImage(cImage, VK_IMAGE_LAYOUT_PREINITIALIZED); }); pMappedResource->RowPitch = layout.RowPitch; pMappedResource->DepthPitch = layout.DepthPitch; pMappedResource->pData = mapPtr; return S_OK; } else { auto dataSlice = AllocStagingBuffer(layout.Size); pMappedResource->RowPitch = layout.RowPitch; pMappedResource->DepthPitch = layout.DepthPitch; pMappedResource->pData = dataSlice.mapPtr(0); auto subresource = pTexture->GetSubresourceFromIndex(formatInfo->aspectMask, Subresource); auto mipExtent = pTexture->MipLevelExtent(subresource.mipLevel); UpdateImage(pTexture, &subresource, VkOffset3D { 0, 0, 0 }, mipExtent, std::move(dataSlice)); return S_OK; } } void D3D11DeferredContext::UpdateMappedBuffer( D3D11Buffer* pDstBuffer, UINT Offset, UINT Length, const void* pSrcData, UINT CopyFlags) { void* mapPtr = nullptr; if (unlikely(CopyFlags == D3D11_COPY_NO_OVERWRITE)) mapPtr = FindMapEntry(pDstBuffer->GetCookie()).pData; if (likely(!mapPtr)) { // The caller validates the map mode, so we can // safely ignore the MapBuffer return value here D3D11_MAPPED_SUBRESOURCE mapInfo; MapBuffer(pDstBuffer, &mapInfo); AddMapEntry(pDstBuffer->GetCookie(), mapInfo); mapPtr = mapInfo.pData; } std::memcpy(reinterpret_cast(mapPtr) + Offset, pSrcData, Length); } void D3D11DeferredContext::FinalizeQueries() { for (auto& query : m_queriesBegun) { m_commandList->AddQuery(query.ptr()); EmitCs([cQuery = std::move(query)] (DxvkContext* ctx) { cQuery->End(ctx); }); } m_queriesBegun.clear(); } Com D3D11DeferredContext::CreateCommandList() { return new D3D11CommandList(m_parent, m_flags); } void D3D11DeferredContext::EmitCsChunk(DxvkCsChunkRef&& chunk) { m_chunkId = m_commandList->AddChunk(std::move(chunk)); } uint64_t D3D11DeferredContext::GetCurrentChunkId() const { return m_csChunk->empty() ? m_chunkId : m_chunkId + 1; } void D3D11DeferredContext::TrackTextureSequenceNumber( D3D11CommonTexture* pResource, UINT Subresource) { m_commandList->TrackResourceUsage( pResource->GetInterface(), pResource->GetDimension(), Subresource, GetCurrentChunkId()); } void D3D11DeferredContext::TrackBufferSequenceNumber( D3D11Buffer* pResource) { m_commandList->TrackResourceUsage(pResource, D3D11_RESOURCE_DIMENSION_BUFFER, 0, GetCurrentChunkId()); } D3D11_MAPPED_SUBRESOURCE D3D11DeferredContext::FindMapEntry( uint64_t Cookie) { // Recently mapped resources as well as entries with // up-to-date map infos will be located at the end // of the resource array, so scan in reverse order. size_t size = m_mappedResources.size(); for (size_t i = 1; i <= size; i++) { const auto& entry = m_mappedResources[size - i]; if (entry.ResourceCookie == Cookie) return entry.MapInfo; } return D3D11_MAPPED_SUBRESOURCE(); } void D3D11DeferredContext::AddMapEntry( uint64_t Cookie, const D3D11_MAPPED_SUBRESOURCE& MapInfo) { m_mappedResources.push_back({ Cookie, MapInfo }); } } dxvk-2.6.1/src/d3d11/d3d11_context_def.h000066400000000000000000000104231477473124000174570ustar00rootroot00000000000000#pragma once #include "d3d11_cmdlist.h" #include "d3d11_context.h" #include namespace dxvk { struct D3D11DeferredContextMapEntry { uint64_t ResourceCookie = 0u; D3D11_MAPPED_SUBRESOURCE MapInfo = { }; }; class D3D11DeferredContext : public D3D11CommonContext { friend class D3D11CommonContext; public: D3D11DeferredContext( D3D11Device* pParent, const Rc& Device, UINT ContextFlags); HRESULT STDMETHODCALLTYPE GetData( ID3D11Asynchronous* pAsync, void* pData, UINT DataSize, UINT GetDataFlags); void STDMETHODCALLTYPE Begin( ID3D11Asynchronous* pAsync); void STDMETHODCALLTYPE End( ID3D11Asynchronous* pAsync); void STDMETHODCALLTYPE Flush(); void STDMETHODCALLTYPE Flush1( D3D11_CONTEXT_TYPE ContextType, HANDLE hEvent); HRESULT STDMETHODCALLTYPE Signal( ID3D11Fence* pFence, UINT64 Value); HRESULT STDMETHODCALLTYPE Wait( ID3D11Fence* pFence, UINT64 Value); void STDMETHODCALLTYPE ExecuteCommandList( ID3D11CommandList* pCommandList, BOOL RestoreContextState); HRESULT STDMETHODCALLTYPE FinishCommandList( BOOL RestoreDeferredContextState, ID3D11CommandList** ppCommandList); HRESULT STDMETHODCALLTYPE Map( ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource); void STDMETHODCALLTYPE Unmap( ID3D11Resource* pResource, UINT Subresource); void STDMETHODCALLTYPE SwapDeviceContextState( ID3DDeviceContextState* pState, ID3DDeviceContextState** ppPreviousState); D3D10DeviceLock LockContext() { return D3D10DeviceLock(); } private: // Command list that we're recording Com m_commandList; // Info about currently mapped (sub)resources. Using a vector // here is reasonable since there will usually only be a small // number of mapped resources per command list. std::vector m_mappedResources; // Begun and ended queries, will also be stored in command list std::vector> m_queriesBegun; // Chunk ID within the current command list uint64_t m_chunkId = 0ull; HRESULT MapBuffer( ID3D11Resource* pResource, D3D11_MAPPED_SUBRESOURCE* pMappedResource); HRESULT MapImage( ID3D11Resource* pResource, UINT Subresource, D3D11_MAPPED_SUBRESOURCE* pMappedResource); void UpdateMappedBuffer( D3D11Buffer* pDstBuffer, UINT Offset, UINT Length, const void* pSrcData, UINT CopyFlags); void FinalizeQueries(); Com CreateCommandList(); void EmitCsChunk(DxvkCsChunkRef&& chunk); uint64_t GetCurrentChunkId() const; void TrackTextureSequenceNumber( D3D11CommonTexture* pResource, UINT Subresource); void TrackBufferSequenceNumber( D3D11Buffer* pResource); D3D11_MAPPED_SUBRESOURCE FindMapEntry( uint64_t Coookie); void AddMapEntry( uint64_t Cookie, const D3D11_MAPPED_SUBRESOURCE& MapInfo); static DxvkCsChunkFlags GetCsChunkFlags( D3D11Device* pDevice); }; } dxvk-2.6.1/src/d3d11/d3d11_context_ext.cpp000066400000000000000000000210361477473124000200560ustar00rootroot00000000000000#include #include #include #include "d3d11_device.h" #include "d3d11_context_imm.h" #include "d3d11_context_def.h" #include "d3d11_cuda.h" #include "../util/log/log.h" namespace dxvk { template D3D11DeviceContextExt::D3D11DeviceContextExt( ContextType* pContext) : m_ctx(pContext) { } template ULONG STDMETHODCALLTYPE D3D11DeviceContextExt::AddRef() { return m_ctx->AddRef(); } template ULONG STDMETHODCALLTYPE D3D11DeviceContextExt::Release() { return m_ctx->Release(); } template HRESULT STDMETHODCALLTYPE D3D11DeviceContextExt::QueryInterface( REFIID riid, void** ppvObject) { return m_ctx->QueryInterface(riid, ppvObject); } template void STDMETHODCALLTYPE D3D11DeviceContextExt::MultiDrawIndirect( UINT DrawCount, ID3D11Buffer* pBufferForArgs, UINT ByteOffsetForArgs, UINT ByteStrideForArgs) { D3D10DeviceLock lock = m_ctx->LockContext(); m_ctx->SetDrawBuffers(pBufferForArgs, nullptr); if (unlikely(m_ctx->HasDirtyGraphicsBindings())) m_ctx->ApplyDirtyGraphicsBindings(); m_ctx->EmitCs([ cCount = DrawCount, cOffset = ByteOffsetForArgs, cStride = ByteStrideForArgs ] (DxvkContext* ctx) { ctx->drawIndirect(cOffset, cCount, cStride, false); }); } template void STDMETHODCALLTYPE D3D11DeviceContextExt::MultiDrawIndexedIndirect( UINT DrawCount, ID3D11Buffer* pBufferForArgs, UINT ByteOffsetForArgs, UINT ByteStrideForArgs) { D3D10DeviceLock lock = m_ctx->LockContext(); m_ctx->SetDrawBuffers(pBufferForArgs, nullptr); if (unlikely(m_ctx->HasDirtyGraphicsBindings())) m_ctx->ApplyDirtyGraphicsBindings(); m_ctx->EmitCs([ cCount = DrawCount, cOffset = ByteOffsetForArgs, cStride = ByteStrideForArgs ] (DxvkContext* ctx) { ctx->drawIndexedIndirect(cOffset, cCount, cStride, false); }); } template void STDMETHODCALLTYPE D3D11DeviceContextExt::MultiDrawIndirectCount( UINT MaxDrawCount, ID3D11Buffer* pBufferForCount, UINT ByteOffsetForCount, ID3D11Buffer* pBufferForArgs, UINT ByteOffsetForArgs, UINT ByteStrideForArgs) { D3D10DeviceLock lock = m_ctx->LockContext(); m_ctx->SetDrawBuffers(pBufferForArgs, pBufferForCount); if (unlikely(m_ctx->HasDirtyGraphicsBindings())) m_ctx->ApplyDirtyGraphicsBindings(); m_ctx->EmitCs([ cMaxCount = MaxDrawCount, cArgOffset = ByteOffsetForArgs, cCntOffset = ByteOffsetForCount, cStride = ByteStrideForArgs ] (DxvkContext* ctx) { ctx->drawIndirectCount(cArgOffset, cCntOffset, cMaxCount, cStride); }); } template void STDMETHODCALLTYPE D3D11DeviceContextExt::MultiDrawIndexedIndirectCount( UINT MaxDrawCount, ID3D11Buffer* pBufferForCount, UINT ByteOffsetForCount, ID3D11Buffer* pBufferForArgs, UINT ByteOffsetForArgs, UINT ByteStrideForArgs) { D3D10DeviceLock lock = m_ctx->LockContext(); m_ctx->SetDrawBuffers(pBufferForArgs, pBufferForCount); if (unlikely(m_ctx->HasDirtyGraphicsBindings())) m_ctx->ApplyDirtyGraphicsBindings(); m_ctx->EmitCs([ cMaxCount = MaxDrawCount, cArgOffset = ByteOffsetForArgs, cCntOffset = ByteOffsetForCount, cStride = ByteStrideForArgs ] (DxvkContext* ctx) { ctx->drawIndexedIndirectCount(cArgOffset, cCntOffset, cMaxCount, cStride); }); } template void STDMETHODCALLTYPE D3D11DeviceContextExt::SetDepthBoundsTest( BOOL Enable, FLOAT MinDepthBounds, FLOAT MaxDepthBounds) { D3D10DeviceLock lock = m_ctx->LockContext(); DxvkDepthBounds db; db.enableDepthBounds = Enable; db.minDepthBounds = MinDepthBounds; db.maxDepthBounds = MaxDepthBounds; m_ctx->EmitCs([cDepthBounds = db] (DxvkContext* ctx) { ctx->setDepthBounds(cDepthBounds); }); } template void STDMETHODCALLTYPE D3D11DeviceContextExt::SetBarrierControl( UINT ControlFlags) { D3D10DeviceLock lock = m_ctx->LockContext(); D3D11Device* parent = static_cast(m_ctx->GetParentInterface()); DxvkBarrierControlFlags flags = parent->GetOptionsBarrierControlFlags(); if (ControlFlags & D3D11_VK_BARRIER_CONTROL_IGNORE_WRITE_AFTER_WRITE) { flags.set(DxvkBarrierControl::ComputeAllowReadWriteOverlap, DxvkBarrierControl::GraphicsAllowReadWriteOverlap); } m_ctx->EmitCs([cFlags = flags] (DxvkContext* ctx) { ctx->setBarrierControl(cFlags); }); } template bool STDMETHODCALLTYPE D3D11DeviceContextExt::LaunchCubinShaderNVX(IUnknown* hShader, uint32_t GridX, uint32_t GridY, uint32_t GridZ, const void* pParams, uint32_t ParamSize, void* const* pReadResources, uint32_t NumReadResources, void* const* pWriteResources, uint32_t NumWriteResources) { D3D10DeviceLock lock = m_ctx->LockContext(); CubinShaderWrapper* cubinShader = static_cast(hShader); CubinShaderLaunchInfo launchInfo; const uint32_t maxResources = NumReadResources + NumWriteResources; launchInfo.buffers.reserve(maxResources); launchInfo.images.reserve(maxResources); for (uint32_t i = 0; i < NumReadResources; i++) launchInfo.insertResource(static_cast(pReadResources[i]), DxvkAccess::Read); for (uint32_t i = 0; i < NumWriteResources; i++) launchInfo.insertResource(static_cast(pWriteResources[i]), DxvkAccess::Write); launchInfo.paramSize = ParamSize; launchInfo.params.resize(launchInfo.paramSize); std::memcpy(launchInfo.params.data(), pParams, ParamSize); launchInfo.cuLaunchConfig[0] = reinterpret_cast(0x01); // CU_LAUNCH_PARAM_BUFFER_POINTER launchInfo.cuLaunchConfig[1] = launchInfo.params.data(); launchInfo.cuLaunchConfig[2] = reinterpret_cast(0x02); // CU_LAUNCH_PARAM_BUFFER_SIZE launchInfo.cuLaunchConfig[3] = &launchInfo.paramSize; // yes, this actually requires a pointer to a size_t containing the parameter size launchInfo.cuLaunchConfig[4] = reinterpret_cast(0x00); // CU_LAUNCH_PARAM_END launchInfo.nvxLaunchInfo.function = cubinShader->cuFunction(); launchInfo.nvxLaunchInfo.gridDimX = GridX; launchInfo.nvxLaunchInfo.gridDimY = GridY; launchInfo.nvxLaunchInfo.gridDimZ = GridZ; launchInfo.nvxLaunchInfo.blockDimX = cubinShader->blockDim().width; launchInfo.nvxLaunchInfo.blockDimY = cubinShader->blockDim().height; launchInfo.nvxLaunchInfo.blockDimZ = cubinShader->blockDim().depth; launchInfo.nvxLaunchInfo.sharedMemBytes = 0; launchInfo.nvxLaunchInfo.paramCount = 0; launchInfo.nvxLaunchInfo.pParams = nullptr; launchInfo.nvxLaunchInfo.extraCount = 1; launchInfo.nvxLaunchInfo.pExtras = launchInfo.cuLaunchConfig.data(); launchInfo.shader = cubinShader; /* Need to capture by value in case this gets called from a deferred context */ m_ctx->EmitCs([cLaunchInfo = std::move(launchInfo)] (DxvkContext* ctx) { ctx->launchCuKernelNVX(cLaunchInfo.nvxLaunchInfo, cLaunchInfo.buffers, cLaunchInfo.images); }); // Track resource usage as necessary for (uint32_t i = 0; i < NumReadResources; i++) m_ctx->TrackResourceSequenceNumber(static_cast(pReadResources[i])); for (uint32_t i = 0; i < NumWriteResources; i++) m_ctx->TrackResourceSequenceNumber(static_cast(pWriteResources[i])); return true; } template class D3D11DeviceContextExt; template class D3D11DeviceContextExt; } dxvk-2.6.1/src/d3d11/d3d11_context_ext.h000066400000000000000000000052371477473124000175300ustar00rootroot00000000000000#pragma once #include "d3d11_interfaces.h" namespace dxvk { class D3D11DeferredContext; class D3D11ImmediateContext; template class D3D11DeviceContextExt : public ID3D11VkExtContext1 { public: D3D11DeviceContextExt( ContextType* pContext); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); void STDMETHODCALLTYPE MultiDrawIndirect( UINT DrawCount, ID3D11Buffer* pBufferForArgs, UINT ByteOffsetForArgs, UINT ByteStrideForArgs); void STDMETHODCALLTYPE MultiDrawIndexedIndirect( UINT DrawCount, ID3D11Buffer* pBufferForArgs, UINT ByteOffsetForArgs, UINT ByteStrideForArgs); void STDMETHODCALLTYPE MultiDrawIndirectCount( UINT MaxDrawCount, ID3D11Buffer* pBufferForCount, UINT ByteOffsetForCount, ID3D11Buffer* pBufferForArgs, UINT ByteOffsetForArgs, UINT ByteStrideForArgs); void STDMETHODCALLTYPE MultiDrawIndexedIndirectCount( UINT MaxDrawCount, ID3D11Buffer* pBufferForCount, UINT ByteOffsetForCount, ID3D11Buffer* pBufferForArgs, UINT ByteOffsetForArgs, UINT ByteStrideForArgs); void STDMETHODCALLTYPE SetDepthBoundsTest( BOOL Enable, FLOAT MinDepthBounds, FLOAT MaxDepthBounds); void STDMETHODCALLTYPE SetBarrierControl( UINT ControlFlags); bool STDMETHODCALLTYPE LaunchCubinShaderNVX( IUnknown* hShader, uint32_t GridX, uint32_t GridY, uint32_t GridZ, const void* pParams, uint32_t paramSize, void* const* pReadResources, uint32_t NumReadResources, void* const* pWriteResources, uint32_t NumWriteResources); private: ContextType* m_ctx; }; } dxvk-2.6.1/src/d3d11/d3d11_context_imm.cpp000066400000000000000000001250651477473124000200470ustar00rootroot00000000000000#include "d3d11_cmdlist.h" #include "d3d11_context_imm.h" #include "d3d11_device.h" #include "d3d11_fence.h" #include "d3d11_texture.h" #include "../util/util_win32_compat.h" constexpr static uint32_t MinFlushIntervalUs = 750; constexpr static uint32_t IncFlushIntervalUs = 250; constexpr static uint32_t MaxPendingSubmits = 6; namespace dxvk { D3D11ImmediateContext::D3D11ImmediateContext( D3D11Device* pParent, const Rc& Device) : D3D11CommonContext(pParent, Device, 0, DxvkCsChunkFlag::SingleUse), m_csThread(Device, Device->createContext()), m_submissionFence(new sync::CallbackFence()), m_flushTracker(GetMaxFlushType(pParent, Device)), m_stagingBufferFence(new sync::Fence(0)), m_multithread(this, false, pParent->GetOptions()->enableContextLock), m_videoContext(this, Device) { EmitCs([ cDevice = m_device, cBarrierControlFlags = pParent->GetOptionsBarrierControlFlags() ] (DxvkContext* ctx) { ctx->beginRecording(cDevice->createCommandList()); ctx->setBarrierControl(cBarrierControlFlags); }); // Stall here so that external submissions to the // CS thread can actually access the command list SynchronizeCsThread(DxvkCsThread::SynchronizeAll); ClearState(); } D3D11ImmediateContext::~D3D11ImmediateContext() { // Avoids hanging when in this state, see comment // in DxvkDevice::~DxvkDevice. if (this_thread::isInModuleDetachment()) return; ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, true); SynchronizeCsThread(DxvkCsThread::SynchronizeAll); SynchronizeDevice(); } HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::QueryInterface(REFIID riid, void** ppvObject) { if (riid == __uuidof(ID3D10Multithread)) { *ppvObject = ref(&m_multithread); return S_OK; } if (riid == __uuidof(ID3D11VideoContext)) { *ppvObject = ref(&m_videoContext); return S_OK; } return D3D11CommonContext::QueryInterface(riid, ppvObject); } HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::GetData( ID3D11Asynchronous* pAsync, void* pData, UINT DataSize, UINT GetDataFlags) { if (!pAsync || (DataSize && !pData)) return E_INVALIDARG; // Check whether the data size is actually correct if (DataSize && DataSize != pAsync->GetDataSize()) return E_INVALIDARG; // Passing a non-null pData is actually allowed if // DataSize is 0, but we should ignore that pointer pData = DataSize ? pData : nullptr; // Get query status directly from the query object auto query = static_cast(pAsync); HRESULT hr = query->GetData(pData, GetDataFlags); // If we're likely going to spin on the asynchronous object, // flush the context so that we're keeping the GPU busy. if (hr == S_FALSE) { // Don't mark the event query as stalling if the app does // not intend to spin on it. This reduces flushes on End. if (!(GetDataFlags & D3D11_ASYNC_GETDATA_DONOTFLUSH)) query->NotifyStall(); // Ignore the DONOTFLUSH flag here as some games will spin // on queries without ever flushing the context otherwise. D3D10DeviceLock lock = LockContext(); if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture))) m_flushReason = "Query read-back"; ConsiderFlush(GpuFlushType::ImplicitSynchronization); } return hr; } void STDMETHODCALLTYPE D3D11ImmediateContext::Begin(ID3D11Asynchronous* pAsync) { D3D10DeviceLock lock = LockContext(); if (unlikely(!pAsync)) return; auto query = static_cast(pAsync); if (unlikely(!query->DoBegin())) return; EmitCs([cQuery = Com(query)] (DxvkContext* ctx) { cQuery->Begin(ctx); }); } void STDMETHODCALLTYPE D3D11ImmediateContext::End(ID3D11Asynchronous* pAsync) { D3D10DeviceLock lock = LockContext(); if (unlikely(!pAsync)) return; auto query = static_cast(pAsync); if (unlikely(!query->DoEnd())) { EmitCs([cQuery = Com(query)] (DxvkContext* ctx) { cQuery->Begin(ctx); }); } EmitCs([cQuery = Com(query)] (DxvkContext* ctx) { cQuery->End(ctx); }); if (unlikely(query->TrackStalls())) { query->NotifyEnd(); if (query->IsStalling()) ExecuteFlush(GpuFlushType::ImplicitSynchronization, nullptr, false); else if (query->IsEvent()) ConsiderFlush(GpuFlushType::ImplicitStrongHint); } } void STDMETHODCALLTYPE D3D11ImmediateContext::Flush() { D3D10DeviceLock lock = LockContext(); if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture))) m_flushReason = "Explicit Flush"; ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, true); } void STDMETHODCALLTYPE D3D11ImmediateContext::Flush1( D3D11_CONTEXT_TYPE ContextType, HANDLE hEvent) { D3D10DeviceLock lock = LockContext(); if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture))) m_flushReason = "Explicit Flush"; ExecuteFlush(GpuFlushType::ExplicitFlush, hEvent, true); } HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::Signal( ID3D11Fence* pFence, UINT64 Value) { D3D10DeviceLock lock = LockContext(); auto fence = static_cast(pFence); if (!fence) return E_INVALIDARG; EmitCs([ cFence = fence->GetFence(), cValue = Value ] (DxvkContext* ctx) { ctx->signalFence(cFence, cValue); }); if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture))) m_flushReason = "Fence signal"; ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, true); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::Wait( ID3D11Fence* pFence, UINT64 Value) { D3D10DeviceLock lock = LockContext(); auto fence = static_cast(pFence); if (!fence) return E_INVALIDARG; if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture))) m_flushReason = "Fence wait"; ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, true); EmitCs([ cFence = fence->GetFence(), cValue = Value ] (DxvkContext* ctx) { ctx->waitFence(cFence, cValue); }); return S_OK; } void STDMETHODCALLTYPE D3D11ImmediateContext::ExecuteCommandList( ID3D11CommandList* pCommandList, BOOL RestoreContextState) { D3D10DeviceLock lock = LockContext(); auto commandList = static_cast(pCommandList); // Reset dirty binding tracking before submitting any CS chunks. // This is needed so that any submission that might occur during // this call does not disrupt bindings set by the deferred context. ResetDirtyTracking(); // Clear state so that the command list can't observe any // current context state. The command list itself will clean // up after execution to ensure that no state changes done // by the command list are visible to the immediate context. ResetCommandListState(); // Flush any outstanding commands so that // we don't mess up the execution order FlushCsChunk(); // As an optimization, flush everything if the // number of pending draw calls is high enough. ConsiderFlush(GpuFlushType::ImplicitWeakHint); // Dispatch command list to the CS thread commandList->EmitToCsThread([this] (DxvkCsChunkRef&& chunk, GpuFlushType flushType) { EmitCsChunk(std::move(chunk)); // Return the sequence number from before the flush since // that is actually going to be needed for resource tracking uint64_t csSeqNum = m_csSeqNum; // Consider a flush after every chunk in case the app // submits a very large command list or the GPU is idle ConsiderFlush(flushType); return csSeqNum; }); // Restore the immediate context's state if (RestoreContextState) RestoreCommandListState(); else ResetContextState(); } HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::FinishCommandList( BOOL RestoreDeferredContextState, ID3D11CommandList **ppCommandList) { InitReturnPtr(ppCommandList); Logger::err("D3D11: FinishCommandList called on immediate context"); return DXGI_ERROR_INVALID_CALL; } HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::Map( ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource) { D3D10DeviceLock lock = LockContext(); if (unlikely(!pResource)) return E_INVALIDARG; D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; pResource->GetType(&resourceDim); if (likely(resourceDim == D3D11_RESOURCE_DIMENSION_BUFFER)) { return MapBuffer( static_cast(pResource), MapType, MapFlags, pMappedResource); } else { return MapImage(GetCommonTexture(pResource), Subresource, MapType, MapFlags, pMappedResource); } } void STDMETHODCALLTYPE D3D11ImmediateContext::Unmap( ID3D11Resource* pResource, UINT Subresource) { // Since it is very uncommon for images to be mapped compared // to buffers, we count the currently mapped images in order // to avoid a virtual method call in the common case. if (unlikely(m_mappedImageCount > 0)) { D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; pResource->GetType(&resourceDim); if (resourceDim != D3D11_RESOURCE_DIMENSION_BUFFER) { D3D10DeviceLock lock = LockContext(); UnmapImage(GetCommonTexture(pResource), Subresource); } } } HRESULT D3D11ImmediateContext::MapBuffer( D3D11Buffer* pResource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource) { if (unlikely(!pMappedResource)) return E_INVALIDARG; if (unlikely(pResource->GetMapMode() == D3D11_COMMON_BUFFER_MAP_MODE_NONE)) { Logger::err("D3D11: Cannot map a device-local buffer"); pMappedResource->pData = nullptr; return E_INVALIDARG; } VkDeviceSize bufferSize = pResource->Desc()->ByteWidth; if (likely(MapType == D3D11_MAP_WRITE_DISCARD)) { // Allocate a new backing slice for the buffer and set // it as the 'new' mapped slice. This assumes that the // only way to invalidate a buffer is by mapping it. auto bufferSlice = pResource->DiscardSlice(&m_allocationCache); pMappedResource->pData = bufferSlice->mapPtr(); pMappedResource->RowPitch = bufferSize; pMappedResource->DepthPitch = bufferSize; EmitCs([ cBuffer = pResource->GetBuffer(), cBufferSlice = std::move(bufferSlice) ] (DxvkContext* ctx) mutable { ctx->invalidateBuffer(cBuffer, std::move(cBufferSlice)); }); // Ignore small buffers here. These are often updated per // draw and won't contribute much to memory waste anyway. if (unlikely(bufferSize > DxvkPageAllocator::PageSize)) ThrottleDiscard(bufferSize); return S_OK; } else if (likely(MapType == D3D11_MAP_WRITE_NO_OVERWRITE)) { // Put this on a fast path without any extra checks since it's // a somewhat desired method to partially update large buffers pMappedResource->pData = pResource->GetMapPtr(); pMappedResource->RowPitch = bufferSize; pMappedResource->DepthPitch = bufferSize; return S_OK; } else { // Quantum Break likes using MAP_WRITE on resources which would force // us to synchronize with the GPU multiple times per frame. In those // situations, if there are no pending GPU writes to the resource, we // can promote it to MAP_WRITE_DISCARD, but preserve the data by doing // a CPU copy from the previous buffer slice, to avoid the sync point. bool doInvalidatePreserve = false; auto buffer = pResource->GetBuffer(); auto sequenceNumber = pResource->GetSequenceNumber(); if (MapType != D3D11_MAP_READ && !MapFlags && bufferSize <= D3D11Initializer::MaxMemoryPerSubmission) { SynchronizeCsThread(sequenceNumber); bool hasWoAccess = buffer->isInUse(DxvkAccess::Write); bool hasRwAccess = buffer->isInUse(DxvkAccess::Read); if (hasRwAccess && !hasWoAccess) { // Uncached reads can be so slow that a GPU sync may actually be faster doInvalidatePreserve = buffer->memFlags() & VK_MEMORY_PROPERTY_HOST_CACHED_BIT; } } if (doInvalidatePreserve) { auto srcPtr = pResource->GetMapPtr(); auto dstSlice = pResource->DiscardSlice(nullptr); auto dstPtr = dstSlice->mapPtr(); EmitCs([ cBuffer = std::move(buffer), cBufferSlice = std::move(dstSlice) ] (DxvkContext* ctx) mutable { ctx->invalidateBuffer(cBuffer, std::move(cBufferSlice)); }); std::memcpy(dstPtr, srcPtr, bufferSize); pMappedResource->pData = dstPtr; pMappedResource->RowPitch = bufferSize; pMappedResource->DepthPitch = bufferSize; ThrottleDiscard(bufferSize); return S_OK; } else { if (!WaitForResource(*buffer, sequenceNumber, MapType, MapFlags)) { pMappedResource->pData = nullptr; return DXGI_ERROR_WAS_STILL_DRAWING; } pMappedResource->pData = pResource->GetMapPtr(); pMappedResource->RowPitch = bufferSize; pMappedResource->DepthPitch = bufferSize; return S_OK; } } } HRESULT D3D11ImmediateContext::MapImage( D3D11CommonTexture* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource) { auto mapMode = pResource->GetMapMode(); if (pMappedResource) pMappedResource->pData = nullptr; if (unlikely(Subresource >= pResource->CountSubresources())) return E_INVALIDARG; switch (MapType) { case D3D11_MAP_READ: { if (!(pResource->Desc()->CPUAccessFlags & D3D11_CPU_ACCESS_READ)) return E_INVALIDARG; } break; case D3D11_MAP_READ_WRITE: { if (!(pResource->Desc()->CPUAccessFlags & D3D11_CPU_ACCESS_READ) || !(pResource->Desc()->CPUAccessFlags & D3D11_CPU_ACCESS_WRITE)) return E_INVALIDARG; } break; case D3D11_MAP_WRITE: { if (!(pResource->Desc()->CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) || (pResource->Desc()->Usage == D3D11_USAGE_DYNAMIC)) return E_INVALIDARG; } break; case D3D11_MAP_WRITE_DISCARD: { if (!(pResource->Desc()->CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) || pResource->Desc()->Usage != D3D11_USAGE_DYNAMIC) return E_INVALIDARG; } break; case D3D11_MAP_WRITE_NO_OVERWRITE: { // NO_OVERWRITE is explcitly banned for dynamic images if (!(pResource->Desc()->CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) || (pResource->Desc()->Usage != D3D11_USAGE_DEFAULT)) return E_INVALIDARG; } break; } if (likely(pMappedResource != nullptr)) { // Resources with an unknown memory layout cannot return a pointer if (pResource->Desc()->Usage == D3D11_USAGE_DEFAULT && pResource->Desc()->TextureLayout == D3D11_TEXTURE_LAYOUT_UNDEFINED) return E_INVALIDARG; } else { if (pResource->Desc()->Usage != D3D11_USAGE_DEFAULT) return E_INVALIDARG; } VkFormat packedFormat = m_parent->LookupPackedFormat( pResource->Desc()->Format, pResource->GetFormatMode()).Format; uint64_t sequenceNumber = pResource->GetSequenceNumber(Subresource); auto formatInfo = lookupFormatInfo(packedFormat); auto layout = pResource->GetSubresourceLayout(formatInfo->aspectMask, Subresource); if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT) { Rc mappedImage = pResource->GetImage(); if (MapType == D3D11_MAP_WRITE_DISCARD) { EmitCs([ cImage = std::move(mappedImage), cStorage = pResource->DiscardStorage() ] (DxvkContext* ctx) { ctx->invalidateImage(cImage, Rc(cStorage)); ctx->initImage(cImage, VK_IMAGE_LAYOUT_PREINITIALIZED); }); ThrottleDiscard(layout.Size); } else if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE) { if (!WaitForResource(*mappedImage, sequenceNumber, MapType, MapFlags)) return DXGI_ERROR_WAS_STILL_DRAWING; } } else if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC) { // Nothing else to really do here, NotifyMap will ensure that we // actually get a staging buffer that isn't currently in use. ThrottleDiscard(layout.Size); } else { Rc mappedBuffer = pResource->GetMappedBuffer(Subresource); constexpr uint32_t DoInvalidate = (1u << 0); constexpr uint32_t DoPreserve = (1u << 1); constexpr uint32_t DoWait = (1u << 2); uint32_t doFlags; if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER) { // If the image can be written by the GPU, we need to update the // mapped staging buffer to reflect the current image contents. if (pResource->Desc()->Usage == D3D11_USAGE_DEFAULT) { bool needsReadback = !pResource->NeedsDirtyRegionTracking(); needsReadback |= MapType == D3D11_MAP_READ || MapType == D3D11_MAP_READ_WRITE; if (needsReadback) ReadbackImageBuffer(pResource, Subresource); } } if (MapType == D3D11_MAP_READ) { // Reads will not change the image content, so we only need // to wait for the GPU to finish writing to the mapped buffer. doFlags = DoWait; } else if (MapType == D3D11_MAP_WRITE_DISCARD) { doFlags = DoInvalidate; // If we know for sure that the mapped buffer is currently not // in use by the GPU, we don't have to allocate a new slice. if (m_csThread.lastSequenceNumber() >= sequenceNumber && !mappedBuffer->isInUse(DxvkAccess::Read)) doFlags = 0; } else if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING && (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT)) { // Always respect DO_NOT_WAIT for mapped staging images doFlags = DoWait; } else if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE || mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER) { // Need to synchronize thread to determine pending GPU accesses SynchronizeCsThread(sequenceNumber); // Don't implicitly discard large very large resources // since that might lead to memory issues. VkDeviceSize bufferSize = mappedBuffer->info().size; if (bufferSize > D3D11Initializer::MaxMemoryPerSubmission) { // Don't check access flags, WaitForResource will return // early anyway if the resource is currently in use doFlags = DoWait; } else if (mappedBuffer->isInUse(DxvkAccess::Write)) { // There are pending GPU writes, need to wait for those doFlags = DoWait; } else if (mappedBuffer->isInUse(DxvkAccess::Read)) { // All pending GPU accesses are reads, so the buffer data // is still current, and we can prevent GPU synchronization // by creating a new slice with an exact copy of the data. doFlags = DoInvalidate | DoPreserve; } else { // There are no pending accesses, so we don't need to wait doFlags = 0; } } else { // No need to synchronize staging resources with NO_OVERWRITE // since the buffer will be used directly. doFlags = 0; } if (doFlags & DoInvalidate) { VkDeviceSize bufferSize = mappedBuffer->info().size; auto srcSlice = pResource->GetMappedSlice(Subresource); auto dstSlice = pResource->DiscardSlice(Subresource); auto srcPtr = srcSlice->mapPtr(); auto dstPtr = dstSlice->mapPtr(); EmitCs([ cImageBuffer = std::move(mappedBuffer), cImageBufferSlice = std::move(dstSlice) ] (DxvkContext* ctx) mutable { ctx->invalidateBuffer(cImageBuffer, std::move(cImageBufferSlice)); }); if (doFlags & DoPreserve) std::memcpy(dstPtr, srcPtr, bufferSize); ThrottleDiscard(bufferSize); } else { if (doFlags & DoWait) { // We cannot respect DO_NOT_WAIT for buffer-mapped resources since // our internal copies need to be transparent to the application. if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER) MapFlags &= ~D3D11_MAP_FLAG_DO_NOT_WAIT; // Wait for mapped buffer to become available if (!WaitForResource(*mappedBuffer, sequenceNumber, MapType, MapFlags)) return DXGI_ERROR_WAS_STILL_DRAWING; } } } // Mark the subresource as successfully mapped pResource->NotifyMap(Subresource, MapType); if (pMappedResource) { pMappedResource->pData = pResource->GetMapPtr(Subresource, layout.Offset); pMappedResource->RowPitch = layout.RowPitch; pMappedResource->DepthPitch = layout.DepthPitch; } m_mappedImageCount += 1; return S_OK; } void D3D11ImmediateContext::UnmapImage( D3D11CommonTexture* pResource, UINT Subresource) { auto mapType = pResource->GetMapType(Subresource); auto mapMode = pResource->GetMapMode(); if (mapType == D3D11CommonTexture::UnmappedSubresource) return; // Decrement mapped image counter only after making sure // the given subresource is actually mapped right now m_mappedImageCount -= 1; // If the texture has an image as well as a staging buffer, // upload the written buffer data to the image bool needsUpload = mapType != uint32_t(D3D11_MAP_READ) && (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER || mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC); if (needsUpload) { if (pResource->NeedsDirtyRegionTracking()) { for (uint32_t i = 0; i < pResource->GetDirtyRegionCount(Subresource); i++) { D3D11_COMMON_TEXTURE_REGION region = pResource->GetDirtyRegion(Subresource, i); UpdateDirtyImageRegion(pResource, Subresource, ®ion); } } else { UpdateDirtyImageRegion(pResource, Subresource, nullptr); } } // Unmap the subresource. This will implicitly destroy the // staging buffer for dynamically mapped images. pResource->NotifyUnmap(Subresource); } void D3D11ImmediateContext::ReadbackImageBuffer( D3D11CommonTexture* pResource, UINT Subresource) { VkImageAspectFlags aspectMask = lookupFormatInfo(pResource->GetPackedFormat())->aspectMask; VkImageSubresource subresource = pResource->GetSubresourceFromIndex(aspectMask, Subresource); EmitCs([ cSrcImage = pResource->GetImage(), cSrcSubresource = vk::makeSubresourceLayers(subresource), cDstBuffer = pResource->GetMappedBuffer(Subresource), cPackedFormat = pResource->GetPackedFormat() ] (DxvkContext* ctx) { VkOffset3D offset = { 0, 0, 0 }; VkExtent3D extent = cSrcImage->mipLevelExtent(cSrcSubresource.mipLevel); ctx->copyImageToBuffer(cDstBuffer, 0, 0, 0, cPackedFormat, cSrcImage, cSrcSubresource, offset, extent); }); if (pResource->HasSequenceNumber()) TrackTextureSequenceNumber(pResource, Subresource); } void D3D11ImmediateContext::UpdateDirtyImageRegion( D3D11CommonTexture* pResource, UINT Subresource, const D3D11_COMMON_TEXTURE_REGION* pRegion) { auto formatInfo = lookupFormatInfo(pResource->GetPackedFormat()); auto subresource = vk::makeSubresourceLayers( pResource->GetSubresourceFromIndex(formatInfo->aspectMask, Subresource)); // Update the entire image if no dirty region was specified D3D11_COMMON_TEXTURE_REGION region; if (pRegion) { region = *pRegion; } else { region.Offset = VkOffset3D { 0, 0, 0 }; region.Extent = pResource->MipLevelExtent(subresource.mipLevel); } auto subresourceLayout = pResource->GetSubresourceLayout(formatInfo->aspectMask, Subresource); // Update dirty region one aspect at a time, due to // how the data is laid out in the staging buffer. for (uint32_t i = 0; i < pResource->GetPlaneCount(); i++) { subresource.aspectMask = formatInfo->aspectMask; if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) subresource.aspectMask = vk::getPlaneAspect(i); EmitCs([ cDstImage = pResource->GetImage(), cDstSubresource = subresource, cDstOffset = region.Offset, cDstExtent = region.Extent, cSrcBuffer = pResource->GetMappedBuffer(Subresource), cSrcOffset = pResource->ComputeMappedOffset(Subresource, i, region.Offset), cSrcRowPitch = subresourceLayout.RowPitch, cSrcDepthPitch = subresourceLayout.DepthPitch, cPackedFormat = pResource->GetPackedFormat() ] (DxvkContext* ctx) { ctx->copyBufferToImage( cDstImage, cDstSubresource, cDstOffset, cDstExtent, cSrcBuffer, cSrcOffset, cSrcRowPitch, cSrcDepthPitch, cPackedFormat); }); } if (pResource->HasSequenceNumber()) TrackTextureSequenceNumber(pResource, Subresource); } void D3D11ImmediateContext::UpdateMappedBuffer( D3D11Buffer* pDstBuffer, UINT Offset, UINT Length, const void* pSrcData, UINT CopyFlags) { void* mapPtr = nullptr; if (likely(CopyFlags != D3D11_COPY_NO_OVERWRITE)) { auto bufferSlice = pDstBuffer->DiscardSlice(&m_allocationCache); mapPtr = bufferSlice->mapPtr(); EmitCs([ cBuffer = pDstBuffer->GetBuffer(), cBufferSlice = std::move(bufferSlice) ] (DxvkContext* ctx) mutable { ctx->invalidateBuffer(cBuffer, std::move(cBufferSlice)); }); } else { mapPtr = pDstBuffer->GetMapPtr(); } std::memcpy(reinterpret_cast(mapPtr) + Offset, pSrcData, Length); } void STDMETHODCALLTYPE D3D11ImmediateContext::SwapDeviceContextState( ID3DDeviceContextState* pState, ID3DDeviceContextState** ppPreviousState) { InitReturnPtr(ppPreviousState); if (!pState) return; // Clear dirty tracking here since all context state will be // re-applied anyway when the context state is swapped in again. ResetDirtyTracking(); // Reset all state affected by the current context state. ResetCommandListState(); Com oldState = std::move(m_stateObject); Com newState = static_cast(pState); if (oldState == nullptr) oldState = new D3D11DeviceContextState(m_parent); if (ppPreviousState) *ppPreviousState = oldState.ref(); m_stateObject = newState; oldState->SetState(m_state); newState->GetState(m_state); // Restore all state affected by the new context state RestoreCommandListState(); } void D3D11ImmediateContext::Acquire11on12Resource( ID3D11Resource* pResource, VkImageLayout SrcLayout) { D3D10DeviceLock lock = LockContext(); auto texture = GetCommonTexture(pResource); auto buffer = GetCommonBuffer(pResource); if (buffer) { EmitCs([ cBuffer = buffer->GetBuffer() ] (DxvkContext* ctx) { ctx->emitBufferBarrier(cBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT, cBuffer->info().stages, cBuffer->info().access); }); } else if (texture) { EmitCs([ cImage = texture->GetImage(), cLayout = SrcLayout ] (DxvkContext* ctx) { ctx->emitImageBarrier(cImage, cLayout, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT, cImage->info().layout, cImage->info().stages, cImage->info().access); }); } } void D3D11ImmediateContext::Release11on12Resource( ID3D11Resource* pResource, VkImageLayout DstLayout) { D3D10DeviceLock lock = LockContext(); auto texture = GetCommonTexture(pResource); auto buffer = GetCommonBuffer(pResource); if (buffer) { EmitCs([ cBuffer = buffer->GetBuffer() ] (DxvkContext* ctx) { ctx->emitBufferBarrier(cBuffer, cBuffer->info().stages, cBuffer->info().access, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT); }); } else if (texture) { EmitCs([ cImage = texture->GetImage(), cLayout = DstLayout ] (DxvkContext* ctx) { ctx->emitImageBarrier(cImage, cImage->info().layout, cImage->info().stages, cImage->info().access, cLayout, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT); }); } } void D3D11ImmediateContext::SynchronizeCsThread(uint64_t SequenceNumber) { D3D10DeviceLock lock = LockContext(); // Dispatch current chunk so that all commands // recorded prior to this function will be run if (SequenceNumber > m_csSeqNum) FlushCsChunk(); m_csThread.synchronize(SequenceNumber); } void D3D11ImmediateContext::SynchronizeDevice() { m_device->waitForIdle(); } void D3D11ImmediateContext::EndFrame( Rc LatencyTracker) { D3D10DeviceLock lock = LockContext(); // Don't keep draw buffers alive indefinitely. This cannot be // done in ExecuteFlush because command recording itself might // flush, so no state changes are allowed to happen there. SetDrawBuffers(nullptr, nullptr); EmitCs([ cTracker = std::move(LatencyTracker) ] (DxvkContext* ctx) { ctx->endFrame(); if (cTracker && cTracker->needsAutoMarkers()) ctx->endLatencyTracking(cTracker); }); } bool D3D11ImmediateContext::WaitForResource( const DxvkPagedResource& Resource, uint64_t SequenceNumber, D3D11_MAP MapType, UINT MapFlags) { // Determine access type to wait for based on map mode DxvkAccess access = MapType == D3D11_MAP_READ ? DxvkAccess::Write : DxvkAccess::Read; // Wait for any CS chunk using the resource to execute, since // otherwise we cannot accurately determine if the resource is // actually being used by the GPU right now. if (!Resource.isInUse(access)) { SynchronizeCsThread(SequenceNumber); if (!Resource.isInUse(access)) return true; } if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture))) { m_flushReason = str::format("Map ", Resource.getDebugName(), " (MAP", MapType != D3D11_MAP_WRITE ? "_READ" : "", MapType != D3D11_MAP_READ ? "_WRITE" : "", ")"); } if (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT) { // We don't have to wait, but misbehaving games may // still try to spin on `Map` until the resource is // idle, so we should flush pending commands ConsiderFlush(GpuFlushType::ImplicitSynchronization); return false; } else { // Make sure pending commands using the resource get // executed on the the GPU if we have to wait for it ExecuteFlush(GpuFlushType::ImplicitSynchronization, nullptr, false); SynchronizeCsThread(SequenceNumber); m_device->waitForResource(Resource, access); return true; } } void D3D11ImmediateContext::InjectCsChunk( DxvkCsQueue Queue, DxvkCsChunkRef&& Chunk, bool Synchronize) { // Do not update the sequence number when emitting a chunk // from an external source since that would break tracking m_csThread.injectChunk(Queue, std::move(Chunk), Synchronize); } void D3D11ImmediateContext::EmitCsChunk(DxvkCsChunkRef&& chunk) { // Flush init commands so that the CS thread // can processe them before the first use. m_parent->FlushInitCommands(); m_csSeqNum = m_csThread.dispatchChunk(std::move(chunk)); } void D3D11ImmediateContext::TrackTextureSequenceNumber( D3D11CommonTexture* pResource, UINT Subresource) { uint64_t sequenceNumber = GetCurrentSequenceNumber(); pResource->TrackSequenceNumber(Subresource, sequenceNumber); ConsiderFlush(GpuFlushType::ImplicitStrongHint); } void D3D11ImmediateContext::TrackBufferSequenceNumber( D3D11Buffer* pResource) { uint64_t sequenceNumber = GetCurrentSequenceNumber(); pResource->TrackSequenceNumber(sequenceNumber); ConsiderFlush(GpuFlushType::ImplicitStrongHint); } uint64_t D3D11ImmediateContext::GetCurrentSequenceNumber() { // We do not flush empty chunks, so if we are tracking a resource // immediately after a flush, we need to use the sequence number // of the previously submitted chunk to prevent deadlocks. return m_csChunk->empty() ? m_csSeqNum : m_csSeqNum + 1; } uint64_t D3D11ImmediateContext::GetPendingCsChunks() { return GetCurrentSequenceNumber() - m_flushSeqNum; } void D3D11ImmediateContext::ApplyDirtyNullBindings() { // At the end of a submission, set all bindings that have not been applied yet // to null on the DXVK context. This way, we avoid keeping resources alive that // are bound to the DXVK context but not to the immediate context. // // Note: This requires that all methods that may modify dirty bindings on the // DXVK context also reset the corresponding dirty bits *before* performing the // bind operation, or otherwise an implicit flush can potentially override them. auto& dirtyState = m_state.lazy.bindingsDirty; EmitCs([ cDirtyState = dirtyState ] (DxvkContext* ctx) { for (uint32_t i = 0; i < uint32_t(DxbcProgramType::Count); i++) { auto dxStage = DxbcProgramType(i); auto vkStage = GetShaderStage(dxStage); // Unbind all dirty constant buffers auto cbvSlot = computeConstantBufferBinding(dxStage, 0); for (uint32_t index : bit::BitMask(cDirtyState[dxStage].cbvMask)) ctx->bindUniformBuffer(vkStage, cbvSlot + index, DxvkBufferSlice()); // Unbind all dirty samplers auto samplerSlot = computeSamplerBinding(dxStage, 0); for (uint32_t index : bit::BitMask(cDirtyState[dxStage].samplerMask)) ctx->bindResourceSampler(vkStage, samplerSlot + index, nullptr); // Unbind all dirty shader resource views auto srvSlot = computeSrvBinding(dxStage, 0); for (uint32_t m = 0; m < cDirtyState[dxStage].srvMask.size(); m++) { for (uint32_t index : bit::BitMask(cDirtyState[dxStage].srvMask[m])) ctx->bindResourceImageView(vkStage, srvSlot + index + m * 64u, nullptr); } // Unbind all dirty unordered access views VkShaderStageFlags uavStages = 0u; if (dxStage == DxbcProgramType::ComputeShader) uavStages = VK_SHADER_STAGE_COMPUTE_BIT; else if (dxStage == DxbcProgramType::PixelShader) uavStages = VK_SHADER_STAGE_ALL_GRAPHICS; if (uavStages) { auto uavSlot = computeUavBinding(dxStage, 0); auto ctrSlot = computeUavCounterBinding(dxStage, 0); for (uint32_t index : bit::BitMask(cDirtyState[dxStage].uavMask)) { ctx->bindResourceImageView(vkStage, uavSlot + index, nullptr); ctx->bindResourceBufferView(vkStage, ctrSlot + index, nullptr); } } } }); // Since we set the DXVK context bindings to null, any bindings that are null // on the D3D context are no longer dirty, so we can clear the respective bits. for (uint32_t i = 0; i < uint32_t(DxbcProgramType::Count); i++) { auto stage = DxbcProgramType(i); for (uint32_t index : bit::BitMask(dirtyState[stage].cbvMask)) { if (!m_state.cbv[stage].buffers[index].buffer.ptr()) dirtyState[stage].cbvMask &= ~(1u << index); } for (uint32_t index : bit::BitMask(dirtyState[stage].samplerMask)) { if (!m_state.samplers[stage].samplers[index]) dirtyState[stage].samplerMask &= ~(1u << index); } for (uint32_t m = 0; m < dirtyState[stage].srvMask.size(); m++) { for (uint32_t index : bit::BitMask(dirtyState[stage].srvMask[m])) { if (!m_state.srv[stage].views[index + m * 64u].ptr()) dirtyState[stage].srvMask[m] &= ~(uint64_t(1u) << index); } } if (stage == DxbcProgramType::ComputeShader || stage == DxbcProgramType::PixelShader) { auto& uavs = stage == DxbcProgramType::ComputeShader ? m_state.uav.views : m_state.om.uavs; for (uint32_t index : bit::BitMask(dirtyState[stage].uavMask)) { if (!uavs[index].ptr()) dirtyState[stage].uavMask &= ~(uint64_t(1u) << index); } } if (dirtyState[stage].empty()) m_state.lazy.shadersDirty.clr(stage); } } void D3D11ImmediateContext::ConsiderFlush( GpuFlushType FlushType) { // In stress test mode, behave as if this would always flush if (DebugLazyBinding == Tristate::True) ApplyDirtyNullBindings(); uint64_t chunkId = GetCurrentSequenceNumber(); uint64_t submissionId = m_submissionFence->value(); if (m_flushTracker.considerFlush(FlushType, chunkId, submissionId)) ExecuteFlush(FlushType, nullptr, false); } void D3D11ImmediateContext::ExecuteFlush( GpuFlushType FlushType, HANDLE hEvent, BOOL Synchronize) { bool synchronizeSubmission = Synchronize && m_parent->Is11on12Device(); if (synchronizeSubmission) m_submitStatus.result = VK_NOT_READY; // Exit early if there's nothing to do if (!GetPendingCsChunks() && !hEvent) return; // Unbind unused resources ApplyDirtyNullBindings(); // Signal the submission fence and flush the command list uint64_t submissionId = ++m_submissionId; if (hEvent) { m_submissionFence->setCallback(submissionId, [hEvent] { SetEvent(hEvent); }); } EmitCs([ cSubmissionFence = m_submissionFence, cSubmissionId = submissionId, cSubmissionStatus = synchronizeSubmission ? &m_submitStatus : nullptr, cStagingFence = m_stagingBufferFence, cStagingMemory = GetStagingMemoryStatistics().allocatedTotal, cFlushReason = std::exchange(m_flushReason, std::string()) ] (DxvkContext* ctx) { auto debugLabel = vk::makeLabel(0xff5959, cFlushReason.c_str()); ctx->signal(cSubmissionFence, cSubmissionId); ctx->signal(cStagingFence, cStagingMemory); ctx->flushCommandList(&debugLabel, cSubmissionStatus); }); FlushCsChunk(); // Notify flush tracker about the flush m_flushSeqNum = m_csSeqNum; m_flushTracker.notifyFlush(m_flushSeqNum, submissionId); // If necessary, block calling thread until the // Vulkan queue submission is performed. if (synchronizeSubmission) m_device->waitForSubmission(&m_submitStatus); // Free local staging buffer so that we don't // end up with a persistent allocation ResetStagingBuffer(); // Reset counter for discarded memory in flight m_discardMemoryOnFlush = m_discardMemoryCounter; // Notify the device that the context has been flushed, // this resets some resource initialization heuristics. m_parent->NotifyContextFlush(); // No point in tracking this across submissions m_hasPendingMsaaResolve = false; } void D3D11ImmediateContext::ThrottleAllocation() { DxvkStagingBufferStats stats = GetStagingMemoryStatistics(); VkDeviceSize stagingMemoryInFlight = stats.allocatedTotal - m_stagingBufferFence->value(); if (stagingMemoryInFlight > stats.allocatedSinceLastReset + D3D11Initializer::MaxMemoryInFlight) { // Stall calling thread to avoid situation where we keep growing the staging // buffer indefinitely, but ignore the newly allocated amount so that we don't // wait for the GPU to go fully idle in case of a large allocation. ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, false); m_device->waitForFence(*m_stagingBufferFence, stats.allocatedTotal - stats.allocatedSinceLastReset - D3D11Initializer::MaxMemoryInFlight); } else if (stats.allocatedSinceLastReset >= D3D11Initializer::MaxMemoryPerSubmission) { // Flush somewhat aggressively if there's a lot of memory in flight ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, false); } } void D3D11ImmediateContext::ThrottleDiscard( VkDeviceSize Size) { m_discardMemoryCounter += Size; if (m_discardMemoryCounter - m_discardMemoryOnFlush >= D3D11Initializer::MaxMemoryPerSubmission) ThrottleAllocation(); } DxvkStagingBufferStats D3D11ImmediateContext::GetStagingMemoryStatistics() { DxvkStagingBufferStats stats = m_staging.getStatistics(); stats.allocatedTotal += m_discardMemoryCounter; stats.allocatedSinceLastReset += m_discardMemoryCounter - m_discardMemoryOnFlush; return stats; } GpuFlushType D3D11ImmediateContext::GetMaxFlushType( D3D11Device* pParent, const Rc& Device) { if (pParent->GetOptions()->reproducibleCommandStream) return GpuFlushType::ExplicitFlush; else if (Device->perfHints().preferRenderPassOps) return GpuFlushType::ImplicitMediumHint; else return GpuFlushType::ImplicitWeakHint; } } dxvk-2.6.1/src/d3d11/d3d11_context_imm.h000066400000000000000000000153551477473124000175140ustar00rootroot00000000000000#pragma once #include "../util/util_time.h" #include "../util/sync/sync_signal.h" #include "d3d11_context.h" #include "d3d11_state_object.h" #include "d3d11_video.h" namespace dxvk { class D3D11Buffer; class D3D11CommonTexture; class D3D11ImmediateContext : public D3D11CommonContext { friend class D3D11CommonContext; friend class D3D11SwapChain; friend class D3D11VideoContext; friend class D3D11DXGIKeyedMutex; public: D3D11ImmediateContext( D3D11Device* pParent, const Rc& Device); ~D3D11ImmediateContext(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE GetData( ID3D11Asynchronous* pAsync, void* pData, UINT DataSize, UINT GetDataFlags); void STDMETHODCALLTYPE Begin( ID3D11Asynchronous* pAsync); void STDMETHODCALLTYPE End( ID3D11Asynchronous* pAsync); void STDMETHODCALLTYPE Flush(); void STDMETHODCALLTYPE Flush1( D3D11_CONTEXT_TYPE ContextType, HANDLE hEvent); HRESULT STDMETHODCALLTYPE Signal( ID3D11Fence* pFence, UINT64 Value); HRESULT STDMETHODCALLTYPE Wait( ID3D11Fence* pFence, UINT64 Value); void STDMETHODCALLTYPE ExecuteCommandList( ID3D11CommandList* pCommandList, BOOL RestoreContextState); HRESULT STDMETHODCALLTYPE FinishCommandList( BOOL RestoreDeferredContextState, ID3D11CommandList **ppCommandList); HRESULT STDMETHODCALLTYPE Map( ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource); void STDMETHODCALLTYPE Unmap( ID3D11Resource* pResource, UINT Subresource); void STDMETHODCALLTYPE SwapDeviceContextState( ID3DDeviceContextState* pState, ID3DDeviceContextState** ppPreviousState); void Acquire11on12Resource( ID3D11Resource* pResource, VkImageLayout SrcLayout); void Release11on12Resource( ID3D11Resource* pResource, VkImageLayout DstLayout); void SynchronizeCsThread( uint64_t SequenceNumber); D3D10Multithread& GetMultithread() { return m_multithread; } D3D10DeviceLock LockContext() { return m_multithread.AcquireLock(); } void InjectCsChunk( DxvkCsQueue Queue, DxvkCsChunkRef&& Chunk, bool Synchronize); template void InjectCs( DxvkCsQueue Queue, Fn&& Command) { auto chunk = AllocCsChunk(); chunk->push(std::move(Command)); InjectCsChunk(Queue, std::move(chunk), false); } private: DxvkCsThread m_csThread; uint64_t m_csSeqNum = 0ull; uint32_t m_mappedImageCount = 0u; Rc m_submissionFence; uint64_t m_submissionId = 0ull; DxvkSubmitStatus m_submitStatus; uint64_t m_flushSeqNum = 0ull; GpuFlushTracker m_flushTracker; Rc m_stagingBufferFence; VkDeviceSize m_discardMemoryCounter = 0u; VkDeviceSize m_discardMemoryOnFlush = 0u; bool m_hasPendingMsaaResolve = false; D3D10Multithread m_multithread; D3D11VideoContext m_videoContext; Com m_stateObject; std::string m_flushReason; HRESULT MapBuffer( D3D11Buffer* pResource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource); HRESULT MapImage( D3D11CommonTexture* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource); void UnmapImage( D3D11CommonTexture* pResource, UINT Subresource); void ReadbackImageBuffer( D3D11CommonTexture* pResource, UINT Subresource); void UpdateDirtyImageRegion( D3D11CommonTexture* pResource, UINT Subresource, const D3D11_COMMON_TEXTURE_REGION* pRegion); void UpdateMappedBuffer( D3D11Buffer* pDstBuffer, UINT Offset, UINT Length, const void* pSrcData, UINT CopyFlags); void SynchronizeDevice(); void EndFrame( Rc LatencyTracker); bool WaitForResource( const DxvkPagedResource& Resource, uint64_t SequenceNumber, D3D11_MAP MapType, UINT MapFlags); void EmitCsChunk(DxvkCsChunkRef&& chunk); void TrackTextureSequenceNumber( D3D11CommonTexture* pResource, UINT Subresource); void TrackBufferSequenceNumber( D3D11Buffer* pResource); uint64_t GetCurrentSequenceNumber(); uint64_t GetPendingCsChunks(); void ApplyDirtyNullBindings(); void ConsiderFlush( GpuFlushType FlushType); void ExecuteFlush( GpuFlushType FlushType, HANDLE hEvent, BOOL Synchronize); void ThrottleAllocation(); void ThrottleDiscard( VkDeviceSize Size); DxvkStagingBufferStats GetStagingMemoryStatistics(); static GpuFlushType GetMaxFlushType( D3D11Device* pParent, const Rc& Device); }; } dxvk-2.6.1/src/d3d11/d3d11_context_state.h000066400000000000000000000225621477473124000200500ustar00rootroot00000000000000#pragma once #include #include "d3d11_buffer.h" #include "d3d11_input_layout.h" #include "d3d11_query.h" #include "d3d11_sampler.h" #include "d3d11_shader.h" #include "d3d11_state.h" #include "d3d11_view_dsv.h" #include "d3d11_view_rtv.h" #include "d3d11_view_srv.h" #include "d3d11_view_uav.h" namespace dxvk { /** * \brief Per-stage state * * Stores an object of the given type for each shader stage. * \tparam Object type */ template class D3D11ShaderStageState { public: T& operator [] (DxbcProgramType type) { return m_state[uint32_t(type)]; } const T& operator [] (DxbcProgramType type) const { return m_state[uint32_t(type)]; } /** * \brief Calls reset method on all objects */ void reset() { for (auto& state : m_state) state.reset(); } private: std::array m_state = { }; }; /** * \brief Constant buffer bindings * * Stores the bound buffer range from a runtime point of view, * as well as the range that is actually bound to the context. */ struct D3D11ConstantBufferBinding { Com buffer = nullptr; UINT constantOffset = 0; UINT constantCount = 0; UINT constantBound = 0; }; struct D3D11ShaderStageCbvBinding { std::array buffers = { }; uint32_t maxCount = 0; void reset() { for (uint32_t i = 0; i < maxCount; i++) buffers[i] = D3D11ConstantBufferBinding(); maxCount = 0; } }; using D3D11CbvBindings = D3D11ShaderStageState; /** * \brief Shader resource bindings * * Stores bound shader resource views, as well as a bit * set of views that are potentially hazardous. */ struct D3D11ShaderStageSrvBinding { std::array, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT> views = { }; DxvkBindingSet hazardous = { }; uint32_t maxCount = 0; void reset() { for (uint32_t i = 0; i < maxCount; i++) views[i] = nullptr; hazardous.clear(); maxCount = 0; } }; using D3D11SrvBindings = D3D11ShaderStageState; /** * \brief Sampler bindings * * Stores bound samplers. */ struct D3D11ShaderStageSamplerBinding { std::array samplers = { }; uint32_t maxCount = 0; void reset() { for (uint32_t i = 0; i < maxCount; i++) samplers[i] = nullptr; maxCount = 0; } }; using D3D11SamplerBindings = D3D11ShaderStageState; /** * \brief UAV bindings * * Stores bound UAVs. For compute shader UAVs, * we also store a bit mask of bound UAVs. */ using D3D11ShaderStageUavBinding = std::array, D3D11_1_UAV_SLOT_COUNT>; struct D3D11UavBindings { D3D11ShaderStageUavBinding views = { }; DxvkBindingSet mask = { }; uint32_t maxCount = 0; void reset() { for (uint32_t i = 0; i < maxCount; i++) views[i] = nullptr; mask.clear(); maxCount = 0; } }; /** * \brief Input assembly state * * Stores vertex buffers, the index buffer, the * input layout, and the dynamic primitive topology. */ struct D3D11VertexBufferBinding { Com buffer = nullptr; UINT offset = 0; UINT stride = 0; }; struct D3D11IndexBufferBinding { Com buffer = nullptr; UINT offset = 0; DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; }; struct D3D11ContextStateIA { Com inputLayout = nullptr; D3D11_PRIMITIVE_TOPOLOGY primitiveTopology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; std::array vertexBuffers = { }; D3D11IndexBufferBinding indexBuffer = { }; uint32_t maxVbCount = 0; void reset() { inputLayout = nullptr; primitiveTopology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; for (uint32_t i = 0; i < maxVbCount; i++) vertexBuffers[i] = D3D11VertexBufferBinding(); indexBuffer = D3D11IndexBufferBinding(); } }; /** * \brief Output merger state * * Stores RTV, DSV, and graphics UAV bindings, as well as related state. */ using D3D11RenderTargetViewBinding = std::array, D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT>; struct D3D11ContextStateOM { D3D11ShaderStageUavBinding uavs = { }; D3D11RenderTargetViewBinding rtvs = { }; Com dsv = { }; D3D11BlendState* cbState = nullptr; D3D11DepthStencilState* dsState = nullptr; FLOAT blendFactor[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; UINT sampleCount = 0u; UINT sampleMask = D3D11_DEFAULT_SAMPLE_MASK; UINT stencilRef = D3D11_DEFAULT_STENCIL_REFERENCE; UINT maxRtv = 0u; UINT minUav = D3D11_1_UAV_SLOT_COUNT; UINT maxUav = 0u; void reset() { for (uint32_t i = minUav; i < maxUav; i++) uavs[i] = nullptr; for (uint32_t i = 0; i < maxRtv; i++) rtvs[i] = nullptr; dsv = nullptr; cbState = nullptr; dsState = nullptr; for (uint32_t i = 0; i < 4; i++) blendFactor[i] = 1.0f; sampleCount = 0u; sampleMask = D3D11_DEFAULT_SAMPLE_MASK; stencilRef = D3D11_DEFAULT_STENCIL_REFERENCE; maxRtv = 0u; minUav = D3D11_1_UAV_SLOT_COUNT; maxUav = 0u; } }; /** * \brief Indirect draw state * * Stores the current indirct draw * argument and draw count buffer. */ struct D3D11ContextStateID { uint64_t argBufferCookie = 0u; uint64_t cntBufferCookie = 0u; void reset() { argBufferCookie = 0u; cntBufferCookie = 0u; } }; /** * \brief Rasterizer state * * Stores viewport info and the rasterizer state object. */ struct D3D11ContextStateRS { uint32_t numViewports = 0; uint32_t numScissors = 0; std::array viewports = { }; std::array scissors = { }; D3D11RasterizerState* state = nullptr; void reset() { for (uint32_t i = 0; i < numViewports; i++) viewports[i] = D3D11_VIEWPORT(); for (uint32_t i = 0; i < numScissors; i++) scissors[i] = D3D11_RECT(); numViewports = 0; numScissors = 0; state = nullptr; } }; /** * \brief Stream output binding * * Stores stream output buffers with offset. */ struct D3D11ContextSoTarget { Com buffer = nullptr; UINT offset = 0; }; struct D3D11ContextStateSO { std::array targets = { }; void reset() { for (uint32_t i = 0; i < targets.size(); i++) targets[i] = D3D11ContextSoTarget(); } }; /** * \brief Predication state * * Stores predication info. */ struct D3D11ContextStatePR { Com predicateObject = nullptr; BOOL predicateValue = false; void reset() { predicateObject = nullptr; predicateValue = false; } }; /** * \brief Lazy binding state * * Keeps track of what state needs to be * re-applied to the context. */ struct D3D11LazyBindings { DxbcProgramTypeFlags shadersUsed = 0u; DxbcProgramTypeFlags shadersDirty = 0u; DxbcProgramTypeFlags graphicsUavShaders = 0u; D3D11ShaderStageState bindingsUsed; D3D11ShaderStageState bindingsDirty; void reset() { shadersUsed = 0u; shadersDirty = 0u; graphicsUavShaders = 0u; bindingsUsed.reset(); bindingsDirty.reset(); } }; /** * \brief Context state */ struct D3D11ContextState { Com vs; Com hs; Com ds; Com gs; Com ps; Com cs; D3D11ContextStateID id; D3D11ContextStateIA ia; D3D11ContextStateOM om; D3D11ContextStateRS rs; D3D11ContextStateSO so; D3D11ContextStatePR pr; D3D11CbvBindings cbv; D3D11SrvBindings srv; D3D11UavBindings uav; D3D11SamplerBindings samplers; D3D11LazyBindings lazy; }; /** * \brief Maximum used binding numbers in a shader stage */ struct D3D11MaxUsedStageBindings { uint32_t cbvCount : 5; uint32_t srvCount : 9; uint32_t uavCount : 7; uint32_t samplerCount : 5; uint32_t reserved : 6; }; /** * \brief Maximum used binding numbers for all context state */ struct D3D11MaxUsedBindings { std::array stages; uint32_t vbCount; uint32_t soCount; }; } dxvk-2.6.1/src/d3d11/d3d11_cuda.cpp000066400000000000000000000030651477473124000164300ustar00rootroot00000000000000#include "d3d11_cuda.h" namespace dxvk { CubinShaderWrapper::CubinShaderWrapper(const Rc& dxvkDevice, VkCuModuleNVX cuModule, VkCuFunctionNVX cuFunction, VkExtent3D blockDim) : m_dxvkDevice(dxvkDevice), m_module(cuModule), m_function(cuFunction), m_blockDim(blockDim) { }; CubinShaderWrapper::~CubinShaderWrapper() { VkDevice vkDevice = m_dxvkDevice->handle(); m_dxvkDevice->vkd()->vkDestroyCuFunctionNVX(vkDevice, m_function, nullptr); m_dxvkDevice->vkd()->vkDestroyCuModuleNVX(vkDevice, m_module, nullptr); }; HRESULT STDMETHODCALLTYPE CubinShaderWrapper::QueryInterface(REFIID riid, void **ppvObject) { if (riid == __uuidof(IUnknown)) { *ppvObject = ref(this); return S_OK; } Logger::warn("CubinShaderWrapper::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); return E_NOINTERFACE; } void CubinShaderLaunchInfo::insertResource(ID3D11Resource* pResource, DxvkAccessFlags access) { auto img = GetCommonTexture(pResource); auto buf = GetCommonBuffer(pResource); if (img) insertUniqueResource(images, img->GetImage(), access); if (buf) insertUniqueResource(buffers, buf->GetBuffer(), access); } template void CubinShaderLaunchInfo::insertUniqueResource(std::vector>& list, const T& resource, DxvkAccessFlags access) { for (auto& entry : list) { if (entry.first == resource) { entry.second.set(access); return; } } list.push_back({ resource, access }); } } dxvk-2.6.1/src/d3d11/d3d11_cuda.h000066400000000000000000000044411477473124000160740ustar00rootroot00000000000000#pragma once #include #include #include "../dxvk/dxvk_memory.h" #include "../dxvk/dxvk_sparse.h" #include "../util/com/com_guid.h" #include "../util/com/com_object.h" #include "d3d11_buffer.h" #include "d3d11_texture.h" namespace dxvk { class CubinShaderWrapper : public ComObject { public: CubinShaderWrapper(const Rc& dxvkDevice, VkCuModuleNVX cuModule, VkCuFunctionNVX cuFunction, VkExtent3D blockDim); ~CubinShaderWrapper(); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); VkCuModuleNVX cuModule() const { return m_module; } VkCuFunctionNVX cuFunction() const { return m_function; } VkExtent3D blockDim() const { return m_blockDim; } private: Rc m_dxvkDevice; VkCuModuleNVX m_module; VkCuFunctionNVX m_function; VkExtent3D m_blockDim; }; struct CubinShaderLaunchInfo { CubinShaderLaunchInfo() = default; CubinShaderLaunchInfo(CubinShaderLaunchInfo&& other) { shader = std::move(other.shader); params = std::move(other.params); paramSize = std::move(other.paramSize); nvxLaunchInfo = std::move(other.nvxLaunchInfo); cuLaunchConfig = other.cuLaunchConfig; buffers = std::move(other.buffers); images = std::move(other.images); other.cuLaunchConfig[1] = nullptr; other.cuLaunchConfig[3] = nullptr; other.nvxLaunchInfo.pExtras = nullptr; // fix-up internally-pointing pointers cuLaunchConfig[1] = params.data(); cuLaunchConfig[3] = ¶mSize; nvxLaunchInfo.pExtras = cuLaunchConfig.data(); } Com shader; std::vector params; size_t paramSize; VkCuLaunchInfoNVX nvxLaunchInfo = { VK_STRUCTURE_TYPE_CU_LAUNCH_INFO_NVX }; std::array cuLaunchConfig; std::vector, DxvkAccessFlags>> buffers; std::vector, DxvkAccessFlags>> images; void insertResource(ID3D11Resource* pResource, DxvkAccessFlags access); template static void insertUniqueResource(std::vector>& list, const T& resource, DxvkAccessFlags access); }; } dxvk-2.6.1/src/d3d11/d3d11_depth_stencil.cpp000066400000000000000000000126041477473124000203400ustar00rootroot00000000000000#include "d3d11_depth_stencil.h" #include "d3d11_device.h" namespace dxvk { D3D11DepthStencilState::D3D11DepthStencilState( D3D11Device* device, const D3D11_DEPTH_STENCIL_DESC& desc) : D3D11StateObject(device), m_desc(desc), m_d3d10(this) { m_state.setDepthTest(desc.DepthEnable); m_state.setDepthWrite(desc.DepthWriteMask == D3D11_DEPTH_WRITE_MASK_ALL); m_state.setStencilTest(desc.StencilEnable); m_state.setDepthCompareOp(DecodeCompareOp(desc.DepthFunc)); m_state.setStencilOpFront(DecodeStencilOpState(desc.FrontFace, desc)); m_state.setStencilOpBack(DecodeStencilOpState(desc.BackFace, desc)); m_state.normalize(); } D3D11DepthStencilState::~D3D11DepthStencilState() { } HRESULT STDMETHODCALLTYPE D3D11DepthStencilState::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11DepthStencilState)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(ID3D10DepthStencilState)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11DepthStencilState), riid)) { Logger::warn("D3D11DepthStencilState::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11DepthStencilState::GetDesc(D3D11_DEPTH_STENCIL_DESC* pDesc) { *pDesc = m_desc; } HRESULT D3D11DepthStencilState::NormalizeDesc(D3D11_DEPTH_STENCIL_DESC* pDesc) { if (pDesc->DepthEnable) { pDesc->DepthEnable = TRUE; if (!ValidateDepthFunc(pDesc->DepthFunc)) return E_INVALIDARG; } else { pDesc->DepthFunc = D3D11_COMPARISON_LESS; pDesc->DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; } if (!ValidateDepthWriteMask(pDesc->DepthWriteMask)) return E_INVALIDARG; if (pDesc->StencilEnable) { pDesc->StencilEnable = TRUE; if (!ValidateStencilFunc(pDesc->FrontFace.StencilFunc) || !ValidateStencilOp(pDesc->FrontFace.StencilFailOp) || !ValidateStencilOp(pDesc->FrontFace.StencilDepthFailOp) || !ValidateStencilOp(pDesc->FrontFace.StencilPassOp)) return E_INVALIDARG; if (!ValidateStencilFunc(pDesc->BackFace.StencilFunc) || !ValidateStencilOp(pDesc->BackFace.StencilFailOp) || !ValidateStencilOp(pDesc->BackFace.StencilDepthFailOp) || !ValidateStencilOp(pDesc->BackFace.StencilPassOp)) return E_INVALIDARG; } else { D3D11_DEPTH_STENCILOP_DESC stencilOp; stencilOp.StencilFailOp = D3D11_STENCIL_OP_KEEP; stencilOp.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; stencilOp.StencilPassOp = D3D11_STENCIL_OP_KEEP; stencilOp.StencilFunc = D3D11_COMPARISON_ALWAYS; pDesc->FrontFace = stencilOp; pDesc->BackFace = stencilOp; pDesc->StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; pDesc->StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; } return S_OK; } DxvkStencilOp D3D11DepthStencilState::DecodeStencilOpState( const D3D11_DEPTH_STENCILOP_DESC& StencilDesc, const D3D11_DEPTH_STENCIL_DESC& Desc) const { DxvkStencilOp result = { }; if (Desc.StencilEnable) { result.setFailOp(DecodeStencilOp(StencilDesc.StencilFailOp)); result.setPassOp(DecodeStencilOp(StencilDesc.StencilPassOp)); result.setDepthFailOp(DecodeStencilOp(StencilDesc.StencilDepthFailOp)); result.setCompareOp(DecodeCompareOp(StencilDesc.StencilFunc)); result.setCompareMask(Desc.StencilReadMask); result.setWriteMask(Desc.StencilWriteMask); } return result; } VkStencilOp D3D11DepthStencilState::DecodeStencilOp(D3D11_STENCIL_OP Op) const { switch (Op) { case D3D11_STENCIL_OP_KEEP: return VK_STENCIL_OP_KEEP; case D3D11_STENCIL_OP_ZERO: return VK_STENCIL_OP_ZERO; case D3D11_STENCIL_OP_REPLACE: return VK_STENCIL_OP_REPLACE; case D3D11_STENCIL_OP_INCR_SAT: return VK_STENCIL_OP_INCREMENT_AND_CLAMP; case D3D11_STENCIL_OP_DECR_SAT: return VK_STENCIL_OP_DECREMENT_AND_CLAMP; case D3D11_STENCIL_OP_INVERT: return VK_STENCIL_OP_INVERT; case D3D11_STENCIL_OP_INCR: return VK_STENCIL_OP_INCREMENT_AND_WRAP; case D3D11_STENCIL_OP_DECR: return VK_STENCIL_OP_DECREMENT_AND_WRAP; default: return VK_STENCIL_OP_KEEP; } } bool D3D11DepthStencilState::ValidateDepthFunc(D3D11_COMPARISON_FUNC Comparison) { return Comparison >= D3D11_COMPARISON_NEVER && Comparison <= D3D11_COMPARISON_ALWAYS; } bool D3D11DepthStencilState::ValidateStencilFunc(D3D11_COMPARISON_FUNC Comparison) { return Comparison >= D3D11_COMPARISON_NEVER && Comparison <= D3D11_COMPARISON_ALWAYS; } bool D3D11DepthStencilState::ValidateStencilOp(D3D11_STENCIL_OP StencilOp) { return StencilOp >= D3D11_STENCIL_OP_KEEP && StencilOp <= D3D11_STENCIL_OP_DECR; } bool D3D11DepthStencilState::ValidateDepthWriteMask(D3D11_DEPTH_WRITE_MASK Mask) { return Mask == D3D11_DEPTH_WRITE_MASK_ZERO || Mask == D3D11_DEPTH_WRITE_MASK_ALL; } } dxvk-2.6.1/src/d3d11/d3d11_depth_stencil.h000066400000000000000000000032061477473124000200030ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_device.h" #include "../d3d10/d3d10_depth_stencil.h" #include "d3d11_device_child.h" #include "d3d11_util.h" namespace dxvk { class D3D11Device; class D3D11DepthStencilState : public D3D11StateObject { public: using DescType = D3D11_DEPTH_STENCIL_DESC; D3D11DepthStencilState( D3D11Device* device, const D3D11_DEPTH_STENCIL_DESC& desc); ~D3D11DepthStencilState(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) final; void STDMETHODCALLTYPE GetDesc( D3D11_DEPTH_STENCIL_DESC* pDesc) final; DxvkDepthStencilState GetState() const { return m_state; } D3D10DepthStencilState* GetD3D10Iface() { return &m_d3d10; } static HRESULT NormalizeDesc( D3D11_DEPTH_STENCIL_DESC* pDesc); private: D3D11_DEPTH_STENCIL_DESC m_desc; DxvkDepthStencilState m_state = { }; D3D10DepthStencilState m_d3d10; DxvkStencilOp DecodeStencilOpState( const D3D11_DEPTH_STENCILOP_DESC& StencilDesc, const D3D11_DEPTH_STENCIL_DESC& Desc) const; VkStencilOp DecodeStencilOp( D3D11_STENCIL_OP Op) const; static bool ValidateDepthFunc( D3D11_COMPARISON_FUNC Comparison); static bool ValidateStencilFunc( D3D11_COMPARISON_FUNC Comparison); static bool ValidateStencilOp( D3D11_STENCIL_OP StencilOp); static bool ValidateDepthWriteMask( D3D11_DEPTH_WRITE_MASK Mask); }; } dxvk-2.6.1/src/d3d11/d3d11_device.cpp000066400000000000000000003654171477473124000167670ustar00rootroot00000000000000#include #include #include "../dxgi/dxgi_monitor.h" #include "../dxgi/dxgi_surface.h" #include "../dxgi/dxgi_swapchain.h" #include "../dxvk/dxvk_adapter.h" #include "../dxvk/dxvk_instance.h" #include "d3d11_buffer.h" #include "d3d11_class_linkage.h" #include "d3d11_context_def.h" #include "d3d11_context_imm.h" #include "d3d11_device.h" #include "d3d11_fence.h" #include "d3d11_input_layout.h" #include "d3d11_interop.h" #include "d3d11_query.h" #include "d3d11_resource.h" #include "d3d11_sampler.h" #include "d3d11_shader.h" #include "d3d11_state_object.h" #include "d3d11_swapchain.h" #include "d3d11_texture.h" #include "d3d11_video.h" #include "../wsi/wsi_window.h" #include "../util/util_shared_res.h" namespace dxvk { constexpr uint32_t D3D11DXGIDevice::DefaultFrameLatency; D3D11Device::D3D11Device( D3D11DXGIDevice* pContainer, D3D_FEATURE_LEVEL FeatureLevel, UINT FeatureFlags) : m_container (pContainer), m_featureLevel (FeatureLevel), m_featureFlags (FeatureFlags), m_dxvkDevice (pContainer->GetDXVKDevice()), m_dxvkAdapter (m_dxvkDevice->adapter()), m_d3d11Formats (m_dxvkDevice), m_d3d11Options (m_dxvkDevice->instance()->config()), m_dxbcOptions (m_dxvkDevice, m_d3d11Options), m_maxFeatureLevel (GetMaxFeatureLevel(m_dxvkDevice->instance(), m_dxvkDevice->adapter())), m_deviceFeatures (m_dxvkDevice->instance(), m_dxvkDevice->adapter(), m_d3d11Options, m_featureLevel) { m_initializer = new D3D11Initializer(this); m_context = new D3D11ImmediateContext(this, m_dxvkDevice); m_d3d10Device = new D3D10Device(this, m_context.ptr()); } D3D11Device::~D3D11Device() { delete m_d3d10Device; m_context = nullptr; delete m_initializer; } ULONG STDMETHODCALLTYPE D3D11Device::AddRef() { return m_container->AddRef(); } ULONG STDMETHODCALLTYPE D3D11Device::Release() { return m_container->Release(); } HRESULT STDMETHODCALLTYPE D3D11Device::QueryInterface(REFIID riid, void** ppvObject) { return m_container->QueryInterface(riid, ppvObject); } HRESULT STDMETHODCALLTYPE D3D11Device::CreateBuffer( const D3D11_BUFFER_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Buffer** ppBuffer) { InitReturnPtr(ppBuffer); if (!pDesc) return E_INVALIDARG; D3D11_BUFFER_DESC desc = *pDesc; HRESULT hr = D3D11Buffer::NormalizeBufferProperties(&desc); if (FAILED(hr)) return hr; if ((desc.MiscFlags & (D3D11_RESOURCE_MISC_TILED | D3D11_RESOURCE_MISC_TILE_POOL)) && !m_deviceFeatures.GetTiledResourcesTier()) return E_INVALIDARG; if (!ppBuffer) return S_FALSE; try { const Com buffer = new D3D11Buffer(this, &desc, nullptr); if (!(desc.MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL)) m_initializer->InitBuffer(buffer.ptr(), pInitialData); *ppBuffer = buffer.ref(); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } } HRESULT STDMETHODCALLTYPE D3D11Device::CreateTexture1D( const D3D11_TEXTURE1D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture1D** ppTexture1D) { InitReturnPtr(ppTexture1D); if (!pDesc) return E_INVALIDARG; D3D11_COMMON_TEXTURE_DESC desc; desc.Width = pDesc->Width; desc.Height = 1; desc.Depth = 1; desc.MipLevels = pDesc->MipLevels; desc.ArraySize = pDesc->ArraySize; desc.Format = pDesc->Format; desc.SampleDesc = DXGI_SAMPLE_DESC { 1, 0 }; desc.Usage = pDesc->Usage; desc.BindFlags = pDesc->BindFlags; desc.CPUAccessFlags = pDesc->CPUAccessFlags; desc.MiscFlags = pDesc->MiscFlags; desc.TextureLayout = D3D11_TEXTURE_LAYOUT_UNDEFINED; HRESULT hr = D3D11CommonTexture::NormalizeTextureProperties(&desc); if (FAILED(hr)) return hr; if (desc.MiscFlags & D3D11_RESOURCE_MISC_TILED) return E_INVALIDARG; if (!ppTexture1D) return S_FALSE; try { const Com texture = new D3D11Texture1D(this, &desc, nullptr); m_initializer->InitTexture(texture->GetCommonTexture(), pInitialData); *ppTexture1D = texture.ref(); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } } HRESULT STDMETHODCALLTYPE D3D11Device::CreateTexture2D( const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) { InitReturnPtr(ppTexture2D); if (!pDesc) return E_INVALIDARG; D3D11_TEXTURE2D_DESC1 desc; desc.Width = pDesc->Width; desc.Height = pDesc->Height; desc.MipLevels = pDesc->MipLevels; desc.ArraySize = pDesc->ArraySize; desc.Format = pDesc->Format; desc.SampleDesc = pDesc->SampleDesc; desc.Usage = pDesc->Usage; desc.BindFlags = pDesc->BindFlags; desc.CPUAccessFlags = pDesc->CPUAccessFlags; desc.MiscFlags = pDesc->MiscFlags; desc.TextureLayout = D3D11_TEXTURE_LAYOUT_UNDEFINED; ID3D11Texture2D1* texture2D = nullptr; HRESULT hr = CreateTexture2DBase(&desc, pInitialData, ppTexture2D ? &texture2D : nullptr); if (hr != S_OK) return hr; *ppTexture2D = texture2D; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateTexture2D1( const D3D11_TEXTURE2D_DESC1* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D1** ppTexture2D) { InitReturnPtr(ppTexture2D); if (!pDesc) return E_INVALIDARG; return CreateTexture2DBase(pDesc, pInitialData, ppTexture2D); } HRESULT STDMETHODCALLTYPE D3D11Device::CreateTexture2DBase( const D3D11_TEXTURE2D_DESC1* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D1** ppTexture2D) { D3D11_COMMON_TEXTURE_DESC desc; desc.Width = pDesc->Width; desc.Height = pDesc->Height; desc.Depth = 1; desc.MipLevels = pDesc->MipLevels; desc.ArraySize = pDesc->ArraySize; desc.Format = pDesc->Format; desc.SampleDesc = pDesc->SampleDesc; desc.Usage = pDesc->Usage; desc.BindFlags = pDesc->BindFlags; desc.CPUAccessFlags = pDesc->CPUAccessFlags; desc.MiscFlags = pDesc->MiscFlags; desc.TextureLayout = pDesc->TextureLayout; HRESULT hr = D3D11CommonTexture::NormalizeTextureProperties(&desc); if ((desc.MiscFlags & D3D11_RESOURCE_MISC_TILED) && !m_deviceFeatures.GetTiledResourcesTier()) return E_INVALIDARG; if (FAILED(hr)) return hr; if (!ppTexture2D) return S_FALSE; try { Com texture = new D3D11Texture2D(this, &desc, nullptr, nullptr); m_initializer->InitTexture(texture->GetCommonTexture(), pInitialData); *ppTexture2D = texture.ref(); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } } HRESULT STDMETHODCALLTYPE D3D11Device::CreateTexture3D( const D3D11_TEXTURE3D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture3D** ppTexture3D) { InitReturnPtr(ppTexture3D); if (!pDesc) return E_INVALIDARG; D3D11_TEXTURE3D_DESC1 desc; desc.Width = pDesc->Width; desc.Height = pDesc->Height; desc.Depth = pDesc->Depth; desc.MipLevels = pDesc->MipLevels; desc.Format = pDesc->Format; desc.Usage = pDesc->Usage; desc.BindFlags = pDesc->BindFlags; desc.CPUAccessFlags = pDesc->CPUAccessFlags; desc.MiscFlags = pDesc->MiscFlags; desc.TextureLayout = D3D11_TEXTURE_LAYOUT_UNDEFINED; ID3D11Texture3D1* texture3D = nullptr; HRESULT hr = CreateTexture3DBase(&desc, pInitialData, ppTexture3D ? &texture3D : nullptr); if (hr != S_OK) return hr; *ppTexture3D = texture3D; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateTexture3D1( const D3D11_TEXTURE3D_DESC1* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture3D1** ppTexture3D) { InitReturnPtr(ppTexture3D); if (!pDesc) return E_INVALIDARG; return CreateTexture3DBase(pDesc, pInitialData, ppTexture3D); } HRESULT STDMETHODCALLTYPE D3D11Device::CreateTexture3DBase( const D3D11_TEXTURE3D_DESC1* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture3D1** ppTexture3D) { D3D11_COMMON_TEXTURE_DESC desc; desc.Width = pDesc->Width; desc.Height = pDesc->Height; desc.Depth = pDesc->Depth; desc.MipLevels = pDesc->MipLevels; desc.ArraySize = 1; desc.Format = pDesc->Format; desc.SampleDesc = DXGI_SAMPLE_DESC { 1, 0 }; desc.Usage = pDesc->Usage; desc.BindFlags = pDesc->BindFlags; desc.CPUAccessFlags = pDesc->CPUAccessFlags; desc.MiscFlags = pDesc->MiscFlags; desc.TextureLayout = pDesc->TextureLayout; HRESULT hr = D3D11CommonTexture::NormalizeTextureProperties(&desc); if (FAILED(hr)) return hr; if ((desc.MiscFlags & D3D11_RESOURCE_MISC_TILED) && (m_deviceFeatures.GetTiledResourcesTier() < D3D11_TILED_RESOURCES_TIER_3)) return E_INVALIDARG; if (!ppTexture3D) return S_FALSE; try { Com texture = new D3D11Texture3D(this, &desc, nullptr); m_initializer->InitTexture(texture->GetCommonTexture(), pInitialData); *ppTexture3D = texture.ref(); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } } HRESULT STDMETHODCALLTYPE D3D11Device::CreateShaderResourceView( ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRView) { InitReturnPtr(ppSRView); if (!pResource) return E_INVALIDARG; uint32_t plane = GetViewPlaneIndex(pResource, pDesc ? pDesc->Format : DXGI_FORMAT_UNKNOWN); D3D11_SHADER_RESOURCE_VIEW_DESC1 desc = pDesc ? D3D11ShaderResourceView::PromoteDesc(pDesc, plane) : D3D11_SHADER_RESOURCE_VIEW_DESC1(); ID3D11ShaderResourceView1* view = nullptr; HRESULT hr = CreateShaderResourceViewBase(pResource, pDesc ? &desc : nullptr, ppSRView ? &view : nullptr); if (hr != S_OK) return hr; *ppSRView = view; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateShaderResourceView1( ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc, ID3D11ShaderResourceView1** ppSRView) { InitReturnPtr(ppSRView); if (!pResource) return E_INVALIDARG; return CreateShaderResourceViewBase(pResource, pDesc, ppSRView); } HRESULT STDMETHODCALLTYPE D3D11Device::CreateShaderResourceViewBase( ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc, ID3D11ShaderResourceView1** ppSRView) { D3D11_COMMON_RESOURCE_DESC resourceDesc; GetCommonResourceDesc(pResource, &resourceDesc); // The description is optional. If omitted, we'll create // a view that covers all subresources of the image. D3D11_SHADER_RESOURCE_VIEW_DESC1 desc; if (!pDesc) { if (FAILED(D3D11ShaderResourceView::GetDescFromResource(pResource, &desc))) return E_INVALIDARG; } else { desc = *pDesc; if (FAILED(D3D11ShaderResourceView::NormalizeDesc(pResource, &desc))) return E_INVALIDARG; } uint32_t plane = D3D11ShaderResourceView::GetPlaneSlice(&desc); if (!CheckResourceViewCompatibility(pResource, D3D11_BIND_SHADER_RESOURCE, desc.Format, plane)) { Logger::err(str::format("D3D11: Cannot create shader resource view:", "\n Resource type: ", resourceDesc.Dim, "\n Resource usage: ", resourceDesc.BindFlags, "\n Resource format: ", resourceDesc.Format, "\n View format: ", desc.Format, "\n View plane: ", plane)); return E_INVALIDARG; } if (!ppSRView) return S_FALSE; try { *ppSRView = ref(new D3D11ShaderResourceView(this, pResource, &desc)); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } } HRESULT STDMETHODCALLTYPE D3D11Device::CreateUnorderedAccessView( ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, ID3D11UnorderedAccessView** ppUAView) { InitReturnPtr(ppUAView); if (!pResource) return E_INVALIDARG; uint32_t plane = GetViewPlaneIndex(pResource, pDesc ? pDesc->Format : DXGI_FORMAT_UNKNOWN); D3D11_UNORDERED_ACCESS_VIEW_DESC1 desc = pDesc ? D3D11UnorderedAccessView::PromoteDesc(pDesc, plane) : D3D11_UNORDERED_ACCESS_VIEW_DESC1(); ID3D11UnorderedAccessView1* view = nullptr; HRESULT hr = CreateUnorderedAccessViewBase(pResource, pDesc ? &desc : nullptr, ppUAView ? &view : nullptr); if (hr != S_OK) return hr; *ppUAView = view; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateUnorderedAccessView1( ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc, ID3D11UnorderedAccessView1** ppUAView) { InitReturnPtr(ppUAView); if (!pResource) return E_INVALIDARG; return CreateUnorderedAccessViewBase(pResource, pDesc, ppUAView); } HRESULT STDMETHODCALLTYPE D3D11Device::CreateUnorderedAccessViewBase( ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc, ID3D11UnorderedAccessView1** ppUAView) { D3D11_COMMON_RESOURCE_DESC resourceDesc; GetCommonResourceDesc(pResource, &resourceDesc); // The description is optional. If omitted, we'll create // a view that covers all subresources of the image. D3D11_UNORDERED_ACCESS_VIEW_DESC1 desc; if (!pDesc) { if (FAILED(D3D11UnorderedAccessView::GetDescFromResource(pResource, &desc))) return E_INVALIDARG; } else { desc = *pDesc; if (FAILED(D3D11UnorderedAccessView::NormalizeDesc(pResource, &desc))) return E_INVALIDARG; } uint32_t plane = D3D11UnorderedAccessView::GetPlaneSlice(&desc); if (!CheckResourceViewCompatibility(pResource, D3D11_BIND_UNORDERED_ACCESS, desc.Format, plane)) { Logger::err(str::format("D3D11: Cannot create unordered access view:", "\n Resource type: ", resourceDesc.Dim, "\n Resource usage: ", resourceDesc.BindFlags, "\n Resource format: ", resourceDesc.Format, "\n View format: ", desc.Format, "\n View plane: ", plane)); return E_INVALIDARG; } if (!ppUAView) return S_FALSE; try { auto uav = new D3D11UnorderedAccessView(this, pResource, &desc); m_initializer->InitUavCounter(uav); *ppUAView = ref(uav); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } } HRESULT STDMETHODCALLTYPE D3D11Device::CreateRenderTargetView( ID3D11Resource* pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) { InitReturnPtr(ppRTView); if (!pResource) return E_INVALIDARG; uint32_t plane = GetViewPlaneIndex(pResource, pDesc ? pDesc->Format : DXGI_FORMAT_UNKNOWN); D3D11_RENDER_TARGET_VIEW_DESC1 desc = pDesc ? D3D11RenderTargetView::PromoteDesc(pDesc, plane) : D3D11_RENDER_TARGET_VIEW_DESC1(); ID3D11RenderTargetView1* view = nullptr; HRESULT hr = CreateRenderTargetViewBase(pResource, pDesc ? &desc : nullptr, ppRTView ? &view : nullptr); if (hr != S_OK) return hr; *ppRTView = view; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateRenderTargetView1( ID3D11Resource* pResource, const D3D11_RENDER_TARGET_VIEW_DESC1* pDesc, ID3D11RenderTargetView1** ppRTView) { InitReturnPtr(ppRTView); if (!pResource) return E_INVALIDARG; return CreateRenderTargetViewBase(pResource, pDesc, ppRTView); } HRESULT STDMETHODCALLTYPE D3D11Device::CreateRenderTargetViewBase( ID3D11Resource* pResource, const D3D11_RENDER_TARGET_VIEW_DESC1* pDesc, ID3D11RenderTargetView1** ppRTView) { // DXVK only supports render target views for image resources D3D11_COMMON_RESOURCE_DESC resourceDesc; GetCommonResourceDesc(pResource, &resourceDesc); if (resourceDesc.Dim == D3D11_RESOURCE_DIMENSION_BUFFER) { Logger::warn("D3D11: Cannot create render target view for a buffer"); return S_OK; // It is required to run Battlefield 3 and Battlefield 4. } // The view description is optional. If not defined, it // will use the resource's format and all array layers. D3D11_RENDER_TARGET_VIEW_DESC1 desc; if (!pDesc) { if (FAILED(D3D11RenderTargetView::GetDescFromResource(pResource, &desc))) return E_INVALIDARG; } else { desc = *pDesc; if (FAILED(D3D11RenderTargetView::NormalizeDesc(pResource, &desc))) return E_INVALIDARG; } uint32_t plane = D3D11RenderTargetView::GetPlaneSlice(&desc); if (!CheckResourceViewCompatibility(pResource, D3D11_BIND_RENDER_TARGET, desc.Format, plane)) { Logger::err(str::format("D3D11: Cannot create render target view:", "\n Resource type: ", resourceDesc.Dim, "\n Resource usage: ", resourceDesc.BindFlags, "\n Resource format: ", resourceDesc.Format, "\n View format: ", desc.Format, "\n View plane: ", plane)); return E_INVALIDARG; } if (!ppRTView) return S_FALSE; try { *ppRTView = ref(new D3D11RenderTargetView(this, pResource, &desc)); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } } HRESULT STDMETHODCALLTYPE D3D11Device::CreateDepthStencilView( ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) { InitReturnPtr(ppDepthStencilView); if (pResource == nullptr) return E_INVALIDARG; D3D11_COMMON_RESOURCE_DESC resourceDesc; GetCommonResourceDesc(pResource, &resourceDesc); // The view description is optional. If not defined, it // will use the resource's format and all array layers. D3D11_DEPTH_STENCIL_VIEW_DESC desc; if (pDesc == nullptr) { if (FAILED(D3D11DepthStencilView::GetDescFromResource(pResource, &desc))) return E_INVALIDARG; } else { desc = *pDesc; if (FAILED(D3D11DepthStencilView::NormalizeDesc(pResource, &desc))) return E_INVALIDARG; } if (!CheckResourceViewCompatibility(pResource, D3D11_BIND_DEPTH_STENCIL, desc.Format, 0)) { Logger::err(str::format("D3D11: Cannot create depth-stencil view:", "\n Resource type: ", resourceDesc.Dim, "\n Resource usage: ", resourceDesc.BindFlags, "\n Resource format: ", resourceDesc.Format, "\n View format: ", desc.Format)); return E_INVALIDARG; } if (ppDepthStencilView == nullptr) return S_FALSE; try { *ppDepthStencilView = ref(new D3D11DepthStencilView(this, pResource, &desc)); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } } HRESULT STDMETHODCALLTYPE D3D11Device::CreateInputLayout( const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs, UINT NumElements, const void* pShaderBytecodeWithInputSignature, SIZE_T BytecodeLength, ID3D11InputLayout** ppInputLayout) { InitReturnPtr(ppInputLayout); // This check is somehow even correct, passing null with zero // size will always fail but passing non-null with zero size // works, provided the shader does not have any actual inputs if (!pInputElementDescs) return E_INVALIDARG; try { DxbcReader dxbcReader(reinterpret_cast( pShaderBytecodeWithInputSignature), BytecodeLength); DxbcModule dxbcModule(dxbcReader); const Rc inputSignature = dxbcModule.isgn(); uint32_t attrMask = 0; uint32_t bindMask = 0; uint32_t locationMask = 0; uint32_t bindingsDefined = 0; std::array attrList = { }; std::array bindList = { }; for (uint32_t i = 0; i < NumElements; i++) { const DxbcSgnEntry* entry = inputSignature->find( pInputElementDescs[i].SemanticName, pInputElementDescs[i].SemanticIndex, 0); // Create vertex input attribute description DxvkVertexAttribute attrib = { }; attrib.location = entry != nullptr ? entry->registerId : 0; attrib.binding = pInputElementDescs[i].InputSlot; attrib.format = LookupFormat(pInputElementDescs[i].Format, DXGI_VK_FORMAT_MODE_COLOR).Format; attrib.offset = pInputElementDescs[i].AlignedByteOffset; // The application may choose to let the implementation // generate the exact vertex layout. In that case we'll // pack attributes on the same binding in the order they // are declared, aligning each attribute to four bytes. const DxvkFormatInfo* formatInfo = lookupFormatInfo(attrib.format); VkDeviceSize alignment = std::min(formatInfo->elementSize, 4); if (attrib.offset == D3D11_APPEND_ALIGNED_ELEMENT) { attrib.offset = 0; for (uint32_t j = 1; j <= i; j++) { const DxvkVertexAttribute& prev = attrList.at(i - j); if (prev.binding == attrib.binding) { attrib.offset = align(prev.offset + lookupFormatInfo(prev.format)->elementSize, alignment); break; } } } else if (attrib.offset & (alignment - 1)) { return E_INVALIDARG; } attrList.at(i) = attrib; // Create vertex input binding description. The // stride is dynamic state in D3D11 and will be // set by D3D11DeviceContext::IASetVertexBuffers. DxvkVertexBinding binding = { }; binding.binding = pInputElementDescs[i].InputSlot; binding.divisor = pInputElementDescs[i].InstanceDataStepRate; binding.inputRate = pInputElementDescs[i].InputSlotClass == D3D11_INPUT_PER_INSTANCE_DATA ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; binding.extent = entry ? uint32_t(attrib.offset + formatInfo->elementSize) : 0u; // Check if the binding was already defined. If so, the // parameters must be identical (namely, the input rate). if (bindingsDefined & (1u << binding.binding)) { if (bindList.at(binding.binding).inputRate != binding.inputRate) return E_INVALIDARG; bindList.at(binding.binding).extent = std::max( bindList.at(binding.binding).extent, binding.extent); } else { bindList.at(binding.binding) = binding; bindingsDefined |= 1u << binding.binding; } if (entry) { attrMask |= 1u << i; bindMask |= 1u << binding.binding; locationMask |= 1u << attrib.location; } } // Ensure that all inputs used by the shader are defined for (auto i = inputSignature->begin(); i != inputSignature->end(); i++) { bool isBuiltIn = DxbcIsgn::compareSemanticNames(i->semanticName, "sv_instanceid") || DxbcIsgn::compareSemanticNames(i->semanticName, "sv_vertexid"); if (!isBuiltIn && !(locationMask & (1u << i->registerId))) return E_INVALIDARG; } // Compact the attribute and binding lists to filter // out attributes and bindings not used by the shader uint32_t attrCount = CompactSparseList(attrList.data(), attrMask); uint32_t bindCount = CompactSparseList(bindList.data(), bindMask); if (!ppInputLayout) return S_FALSE; *ppInputLayout = ref( new D3D11InputLayout(this, attrCount, attrList.data(), bindCount, bindList.data())); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } } HRESULT STDMETHODCALLTYPE D3D11Device::CreateVertexShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11VertexShader** ppVertexShader) { InitReturnPtr(ppVertexShader); D3D11CommonShader module; DxbcModuleInfo moduleInfo; moduleInfo.options = m_dxbcOptions; moduleInfo.tess = nullptr; moduleInfo.xfb = nullptr; Sha1Hash hash = Sha1Hash::compute( pShaderBytecode, BytecodeLength); HRESULT hr = CreateShaderModule(&module, DxvkShaderKey(VK_SHADER_STAGE_VERTEX_BIT, hash), pShaderBytecode, BytecodeLength, pClassLinkage, &moduleInfo); if (FAILED(hr)) return hr; if (!ppVertexShader) return S_FALSE; *ppVertexShader = ref(new D3D11VertexShader(this, module)); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateGeometryShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11GeometryShader** ppGeometryShader) { InitReturnPtr(ppGeometryShader); D3D11CommonShader module; DxbcModuleInfo moduleInfo; moduleInfo.options = m_dxbcOptions; moduleInfo.tess = nullptr; moduleInfo.xfb = nullptr; Sha1Hash hash = Sha1Hash::compute( pShaderBytecode, BytecodeLength); HRESULT hr = CreateShaderModule(&module, DxvkShaderKey(VK_SHADER_STAGE_GEOMETRY_BIT, hash), pShaderBytecode, BytecodeLength, pClassLinkage, &moduleInfo); if (FAILED(hr)) return hr; if (!ppGeometryShader) return S_FALSE; *ppGeometryShader = ref(new D3D11GeometryShader(this, module)); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateGeometryShaderWithStreamOutput( const void* pShaderBytecode, SIZE_T BytecodeLength, const D3D11_SO_DECLARATION_ENTRY* pSODeclaration, UINT NumEntries, const UINT* pBufferStrides, UINT NumStrides, UINT RasterizedStream, ID3D11ClassLinkage* pClassLinkage, ID3D11GeometryShader** ppGeometryShader) { InitReturnPtr(ppGeometryShader); D3D11CommonShader module; if (!m_dxvkDevice->features().extTransformFeedback.transformFeedback) return DXGI_ERROR_INVALID_CALL; // Zero-init some counterss so that we can increment // them while walking over the stream output entries DxbcXfbInfo xfb = { }; for (uint32_t i = 0; i < NumEntries; i++) { const D3D11_SO_DECLARATION_ENTRY* so = &pSODeclaration[i]; if (so->OutputSlot >= D3D11_SO_BUFFER_SLOT_COUNT) return E_INVALIDARG; if (so->SemanticName != nullptr) { if (so->Stream >= D3D11_SO_BUFFER_SLOT_COUNT || so->StartComponent >= 4 || so->ComponentCount < 1 || so->ComponentCount > 4) return E_INVALIDARG; DxbcXfbEntry* entry = &xfb.entries[xfb.entryCount++]; entry->semanticName = so->SemanticName; entry->semanticIndex = so->SemanticIndex; entry->componentIndex = so->StartComponent; entry->componentCount = so->ComponentCount; entry->streamId = so->Stream; entry->bufferId = so->OutputSlot; entry->offset = xfb.strides[so->OutputSlot]; } xfb.strides[so->OutputSlot] += so->ComponentCount * sizeof(uint32_t); } // If necessary, override the buffer strides for (uint32_t i = 0; i < NumStrides; i++) xfb.strides[i] = pBufferStrides[i]; // Set stream to rasterize, if any xfb.rasterizedStream = -1; if (RasterizedStream != D3D11_SO_NO_RASTERIZED_STREAM) Logger::err("D3D11: CreateGeometryShaderWithStreamOutput: Rasterized stream not supported"); // Compute hash from both the xfb info and the source // code, because both influence the generated code DxbcXfbInfo hashXfb = xfb; std::vector chunks = {{ { pShaderBytecode, BytecodeLength }, { &hashXfb, sizeof(hashXfb) }, }}; for (uint32_t i = 0; i < hashXfb.entryCount; i++) { const char* semantic = hashXfb.entries[i].semanticName; if (semantic) { chunks.push_back({ semantic, std::strlen(semantic) }); hashXfb.entries[i].semanticName = nullptr; } } Sha1Hash hash = Sha1Hash::compute(chunks.size(), chunks.data()); // Create the actual shader module DxbcModuleInfo moduleInfo; moduleInfo.options = m_dxbcOptions; moduleInfo.tess = nullptr; moduleInfo.xfb = &xfb; HRESULT hr = CreateShaderModule(&module, DxvkShaderKey(VK_SHADER_STAGE_GEOMETRY_BIT, hash), pShaderBytecode, BytecodeLength, pClassLinkage, &moduleInfo); if (FAILED(hr)) return E_INVALIDARG; if (!ppGeometryShader) return S_FALSE; *ppGeometryShader = ref(new D3D11GeometryShader(this, module)); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreatePixelShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11PixelShader** ppPixelShader) { InitReturnPtr(ppPixelShader); D3D11CommonShader module; DxbcModuleInfo moduleInfo; moduleInfo.options = m_dxbcOptions; moduleInfo.tess = nullptr; moduleInfo.xfb = nullptr; Sha1Hash hash = Sha1Hash::compute( pShaderBytecode, BytecodeLength); HRESULT hr = CreateShaderModule(&module, DxvkShaderKey(VK_SHADER_STAGE_FRAGMENT_BIT, hash), pShaderBytecode, BytecodeLength, pClassLinkage, &moduleInfo); if (FAILED(hr)) return hr; if (!ppPixelShader) return S_FALSE; *ppPixelShader = ref(new D3D11PixelShader(this, module)); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateHullShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11HullShader** ppHullShader) { InitReturnPtr(ppHullShader); D3D11CommonShader module; DxbcTessInfo tessInfo; tessInfo.maxTessFactor = float(m_d3d11Options.maxTessFactor); DxbcModuleInfo moduleInfo; moduleInfo.options = m_dxbcOptions; moduleInfo.tess = nullptr; moduleInfo.xfb = nullptr; if (tessInfo.maxTessFactor >= 8.0f) moduleInfo.tess = &tessInfo; Sha1Hash hash = Sha1Hash::compute( pShaderBytecode, BytecodeLength); HRESULT hr = CreateShaderModule(&module, DxvkShaderKey(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, hash), pShaderBytecode, BytecodeLength, pClassLinkage, &moduleInfo); if (FAILED(hr)) return hr; if (!ppHullShader) return S_FALSE; *ppHullShader = ref(new D3D11HullShader(this, module)); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateDomainShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11DomainShader** ppDomainShader) { InitReturnPtr(ppDomainShader); D3D11CommonShader module; DxbcModuleInfo moduleInfo; moduleInfo.options = m_dxbcOptions; moduleInfo.tess = nullptr; moduleInfo.xfb = nullptr; Sha1Hash hash = Sha1Hash::compute( pShaderBytecode, BytecodeLength); HRESULT hr = CreateShaderModule(&module, DxvkShaderKey(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, hash), pShaderBytecode, BytecodeLength, pClassLinkage, &moduleInfo); if (FAILED(hr)) return hr; if (ppDomainShader == nullptr) return S_FALSE; *ppDomainShader = ref(new D3D11DomainShader(this, module)); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateComputeShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11ComputeShader** ppComputeShader) { InitReturnPtr(ppComputeShader); D3D11CommonShader module; DxbcModuleInfo moduleInfo; moduleInfo.options = m_dxbcOptions; moduleInfo.tess = nullptr; moduleInfo.xfb = nullptr; Sha1Hash hash = Sha1Hash::compute( pShaderBytecode, BytecodeLength); HRESULT hr = CreateShaderModule(&module, DxvkShaderKey(VK_SHADER_STAGE_COMPUTE_BIT, hash), pShaderBytecode, BytecodeLength, pClassLinkage, &moduleInfo); if (FAILED(hr)) return hr; if (!ppComputeShader) return S_FALSE; *ppComputeShader = ref(new D3D11ComputeShader(this, module)); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateClassLinkage(ID3D11ClassLinkage** ppLinkage) { *ppLinkage = ref(new D3D11ClassLinkage(this)); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateBlendState( const D3D11_BLEND_DESC* pBlendStateDesc, ID3D11BlendState** ppBlendState) { InitReturnPtr(ppBlendState); if (!pBlendStateDesc) return E_INVALIDARG; D3D11_BLEND_DESC1 desc = D3D11BlendState::PromoteDesc(pBlendStateDesc); if (FAILED(D3D11BlendState::NormalizeDesc(&desc))) return E_INVALIDARG; if (ppBlendState != nullptr) { *ppBlendState = m_bsStateObjects.Create(this, desc); return S_OK; } return S_FALSE; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateBlendState1( const D3D11_BLEND_DESC1* pBlendStateDesc, ID3D11BlendState1** ppBlendState) { InitReturnPtr(ppBlendState); if (!pBlendStateDesc) return E_INVALIDARG; D3D11_BLEND_DESC1 desc = *pBlendStateDesc; if (FAILED(D3D11BlendState::NormalizeDesc(&desc))) return E_INVALIDARG; if (ppBlendState != nullptr) { *ppBlendState = m_bsStateObjects.Create(this, desc); return S_OK; } return S_FALSE; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateDepthStencilState( const D3D11_DEPTH_STENCIL_DESC* pDepthStencilDesc, ID3D11DepthStencilState** ppDepthStencilState) { InitReturnPtr(ppDepthStencilState); if (!pDepthStencilDesc) return E_INVALIDARG; D3D11_DEPTH_STENCIL_DESC desc = *pDepthStencilDesc; if (FAILED(D3D11DepthStencilState::NormalizeDesc(&desc))) return E_INVALIDARG; if (ppDepthStencilState != nullptr) { *ppDepthStencilState = m_dsStateObjects.Create(this, desc); return S_OK; } return S_FALSE; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateRasterizerState( const D3D11_RASTERIZER_DESC* pRasterizerDesc, ID3D11RasterizerState** ppRasterizerState) { InitReturnPtr(ppRasterizerState); if (!pRasterizerDesc) return E_INVALIDARG; D3D11_RASTERIZER_DESC2 desc = D3D11RasterizerState::PromoteDesc(pRasterizerDesc); if (FAILED(D3D11RasterizerState::NormalizeDesc(&desc))) return E_INVALIDARG; if (!ppRasterizerState) return S_FALSE; *ppRasterizerState = m_rsStateObjects.Create(this, desc); return S_OK; } HRESULT D3D11Device::CreateRasterizerState1( const D3D11_RASTERIZER_DESC1* pRasterizerDesc, ID3D11RasterizerState1** ppRasterizerState) { InitReturnPtr(ppRasterizerState); if (!pRasterizerDesc) return E_INVALIDARG; D3D11_RASTERIZER_DESC2 desc = D3D11RasterizerState::PromoteDesc(pRasterizerDesc); if (FAILED(D3D11RasterizerState::NormalizeDesc(&desc))) return E_INVALIDARG; if (!ppRasterizerState) return S_FALSE; *ppRasterizerState = m_rsStateObjects.Create(this, desc); return S_OK; } HRESULT D3D11Device::CreateRasterizerState2( const D3D11_RASTERIZER_DESC2* pRasterizerDesc, ID3D11RasterizerState2** ppRasterizerState) { InitReturnPtr(ppRasterizerState); if (!pRasterizerDesc) return E_INVALIDARG; D3D11_RASTERIZER_DESC2 desc = *pRasterizerDesc; if (FAILED(D3D11RasterizerState::NormalizeDesc(&desc))) return E_INVALIDARG; if (desc.ConservativeRaster != D3D11_CONSERVATIVE_RASTERIZATION_MODE_OFF && !m_deviceFeatures.GetConservativeRasterizationTier()) return E_INVALIDARG; if (!ppRasterizerState) return S_FALSE; *ppRasterizerState = m_rsStateObjects.Create(this, desc); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateSamplerState( const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState) { InitReturnPtr(ppSamplerState); if (pSamplerDesc == nullptr) return E_INVALIDARG; D3D11_SAMPLER_DESC desc = *pSamplerDesc; if (FAILED(D3D11SamplerState::NormalizeDesc(&desc))) return E_INVALIDARG; D3D11_TILED_RESOURCES_TIER tiledResourcesTier = m_deviceFeatures.GetTiledResourcesTier(); if (IsMinMaxFilter(desc.Filter) && tiledResourcesTier < D3D11_TILED_RESOURCES_TIER_2) return E_INVALIDARG; if (!ppSamplerState) return S_FALSE; try { *ppSamplerState = m_samplerObjects.Create(this, desc); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } } HRESULT STDMETHODCALLTYPE D3D11Device::CreateQuery( const D3D11_QUERY_DESC* pQueryDesc, ID3D11Query** ppQuery) { InitReturnPtr(ppQuery); if (!pQueryDesc) return E_INVALIDARG; D3D11_QUERY_DESC1 desc; desc.Query = pQueryDesc->Query; desc.MiscFlags = pQueryDesc->MiscFlags; desc.ContextType = D3D11_CONTEXT_TYPE_ALL; ID3D11Query1* query = nullptr; HRESULT hr = CreateQueryBase(&desc, ppQuery ? &query : nullptr); if (hr != S_OK) return hr; *ppQuery = query; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateQuery1( const D3D11_QUERY_DESC1* pQueryDesc, ID3D11Query1** ppQuery) { InitReturnPtr(ppQuery); if (!pQueryDesc) return E_INVALIDARG; return CreateQueryBase(pQueryDesc, ppQuery); } HRESULT STDMETHODCALLTYPE D3D11Device::CreateQueryBase( const D3D11_QUERY_DESC1* pQueryDesc, ID3D11Query1** ppQuery) { HRESULT hr = D3D11Query::ValidateDesc(pQueryDesc); if (FAILED(hr)) return hr; if (!ppQuery) return S_FALSE; try { *ppQuery = ref(new D3D11Query(this, *pQueryDesc)); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } } HRESULT STDMETHODCALLTYPE D3D11Device::CreatePredicate( const D3D11_QUERY_DESC* pPredicateDesc, ID3D11Predicate** ppPredicate) { InitReturnPtr(ppPredicate); if (!pPredicateDesc) return E_INVALIDARG; D3D11_QUERY_DESC1 desc; desc.Query = pPredicateDesc->Query; desc.MiscFlags = pPredicateDesc->MiscFlags; desc.ContextType = D3D11_CONTEXT_TYPE_ALL; if (desc.Query != D3D11_QUERY_OCCLUSION_PREDICATE) { Logger::warn(str::format("D3D11: Unhandled predicate type: ", pPredicateDesc->Query)); return E_INVALIDARG; } if (!ppPredicate) return S_FALSE; try { *ppPredicate = D3D11Query::AsPredicate( ref(new D3D11Query(this, desc))); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } } HRESULT STDMETHODCALLTYPE D3D11Device::CreateCounter( const D3D11_COUNTER_DESC* pCounterDesc, ID3D11Counter** ppCounter) { InitReturnPtr(ppCounter); Logger::err(str::format("D3D11: Unsupported counter: ", pCounterDesc->Counter)); return E_INVALIDARG; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateDeferredContext( UINT ContextFlags, ID3D11DeviceContext** ppDeferredContext) { *ppDeferredContext = ref(new D3D11DeferredContext(this, m_dxvkDevice, ContextFlags)); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateDeferredContext1( UINT ContextFlags, ID3D11DeviceContext1** ppDeferredContext) { *ppDeferredContext = ref(new D3D11DeferredContext(this, m_dxvkDevice, ContextFlags)); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateDeferredContext2( UINT ContextFlags, ID3D11DeviceContext2** ppDeferredContext) { *ppDeferredContext = ref(new D3D11DeferredContext(this, m_dxvkDevice, ContextFlags)); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateDeferredContext3( UINT ContextFlags, ID3D11DeviceContext3** ppDeferredContext) { *ppDeferredContext = ref(new D3D11DeferredContext(this, m_dxvkDevice, ContextFlags)); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateDeviceContextState( UINT Flags, const D3D_FEATURE_LEVEL* pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, REFIID EmulatedInterface, D3D_FEATURE_LEVEL* pChosenFeatureLevel, ID3DDeviceContextState** ppContextState) { InitReturnPtr(ppContextState); if (!pFeatureLevels || !FeatureLevels) return E_INVALIDARG; if (EmulatedInterface != __uuidof(ID3D10Device) && EmulatedInterface != __uuidof(ID3D10Device1) && EmulatedInterface != __uuidof(ID3D11Device) && EmulatedInterface != __uuidof(ID3D11Device1)) return E_INVALIDARG; D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL(); for (uint32_t flId = 0; flId < FeatureLevels; flId++) { if (pFeatureLevels[flId] <= m_maxFeatureLevel) { featureLevel = pFeatureLevels[flId]; break; } } if (!featureLevel) return E_INVALIDARG; if (m_featureLevel < featureLevel) { m_featureLevel = featureLevel; m_deviceFeatures = D3D11DeviceFeatures( m_dxvkDevice->instance(), m_dxvkDevice->adapter(), m_d3d11Options, m_featureLevel); } if (pChosenFeatureLevel) *pChosenFeatureLevel = featureLevel; if (!ppContextState) return S_FALSE; *ppContextState = ref(new D3D11DeviceContextState(this)); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Device::CreateFence( UINT64 InitialValue, D3D11_FENCE_FLAG Flags, REFIID riid, void** ppFence) { InitReturnPtr(ppFence); try { Com fence = new D3D11Fence(this, InitialValue, Flags, INVALID_HANDLE_VALUE); return fence->QueryInterface(riid, ppFence); } catch (const DxvkError& e) { Logger::err(e.message()); return E_FAIL; } } void STDMETHODCALLTYPE D3D11Device::ReadFromSubresource( void* pDstData, UINT DstRowPitch, UINT DstDepthPitch, ID3D11Resource* pSrcResource, UINT SrcSubresource, const D3D11_BOX* pSrcBox) { auto texture = GetCommonTexture(pSrcResource); if (!texture) return; if (texture->Desc()->Usage != D3D11_USAGE_DEFAULT || texture->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_NONE || texture->CountSubresources() <= SrcSubresource) return; uint32_t map = texture->GetMapType(SrcSubresource); if (map != uint32_t(D3D11_MAP_READ) && map != uint32_t(D3D11_MAP_READ_WRITE)) return; CopySubresourceData( pDstData, DstRowPitch, DstDepthPitch, texture, SrcSubresource, pSrcBox); } void STDMETHODCALLTYPE D3D11Device::WriteToSubresource( ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch) { auto texture = GetCommonTexture(pDstResource); if (!texture) return; if (texture->Desc()->Usage != D3D11_USAGE_DEFAULT || texture->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_NONE || texture->CountSubresources() <= DstSubresource) return; uint32_t map = texture->GetMapType(DstSubresource); if (map != uint32_t(D3D11_MAP_WRITE) && map != uint32_t(D3D11_MAP_WRITE_NO_OVERWRITE) && map != uint32_t(D3D11_MAP_READ_WRITE)) return; CopySubresourceData( pSrcData, SrcRowPitch, SrcRowPitch, texture, DstSubresource, pDstBox); } HRESULT STDMETHODCALLTYPE D3D11Device::OpenSharedResource( HANDLE hResource, REFIID ReturnedInterface, void** ppResource) { return OpenSharedResourceGeneric( hResource, ReturnedInterface, ppResource); } HRESULT STDMETHODCALLTYPE D3D11Device::OpenSharedResource1( HANDLE hResource, REFIID ReturnedInterface, void** ppResource) { return OpenSharedResourceGeneric( hResource, ReturnedInterface, ppResource); } HRESULT STDMETHODCALLTYPE D3D11Device::OpenSharedResourceByName( LPCWSTR lpName, DWORD dwDesiredAccess, REFIID returnedInterface, void** ppResource) { InitReturnPtr(ppResource); Logger::err("D3D11Device::OpenSharedResourceByName: Not implemented"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11Device::OpenSharedFence( HANDLE hFence, REFIID ReturnedInterface, void** ppFence) { InitReturnPtr(ppFence); if (ppFence == nullptr) return S_FALSE; try { Com fence = new D3D11Fence(this, 0, D3D11_FENCE_FLAG_SHARED, hFence); return fence->QueryInterface(ReturnedInterface, ppFence); } catch (const DxvkError& e) { Logger::err(e.message()); return E_FAIL; } } HRESULT STDMETHODCALLTYPE D3D11Device::CheckFormatSupport( DXGI_FORMAT Format, UINT* pFormatSupport) { return GetFormatSupportFlags(Format, pFormatSupport, nullptr); } HRESULT STDMETHODCALLTYPE D3D11Device::CheckMultisampleQualityLevels( DXGI_FORMAT Format, UINT SampleCount, UINT* pNumQualityLevels) { return CheckMultisampleQualityLevels1(Format, SampleCount, 0, pNumQualityLevels); } HRESULT STDMETHODCALLTYPE D3D11Device::CheckMultisampleQualityLevels1( DXGI_FORMAT Format, UINT SampleCount, UINT Flags, UINT* pNumQualityLevels) { // There are many error conditions, so we'll just assume // that we will fail and return a non-zero value in case // the device does actually support the format. if (!pNumQualityLevels) return E_INVALIDARG; // We don't support tiled resources, but it's unclear what // we are supposed to return in this case. Be conservative. if (Flags) { *pNumQualityLevels = 0; return E_FAIL; } // For some reason, we can query DXGI_FORMAT_UNKNOWN if (Format == DXGI_FORMAT_UNKNOWN) { *pNumQualityLevels = SampleCount == 1 ? 1 : 0; return SampleCount ? S_OK : E_FAIL; } // All other unknown formats should result in an error return. VkFormat format = LookupFormat(Format, DXGI_VK_FORMAT_MODE_ANY).Format; if (format == VK_FORMAT_UNDEFINED) return E_INVALIDARG; // Zero-init now, leave value undefined otherwise. // This does actually match native D3D11 behaviour. *pNumQualityLevels = 0; // Non-power of two sample counts are not supported, but querying // support for them is legal, so we return zero quality levels. VkSampleCountFlagBits sampleCountFlag = VK_SAMPLE_COUNT_1_BIT; if (FAILED(DecodeSampleCount(SampleCount, &sampleCountFlag))) return SampleCount && SampleCount <= 32 ? S_OK : E_FAIL; // Get image create flags depending on function arguments VkImageCreateFlags flags = 0; if (Flags & D3D11_CHECK_MULTISAMPLE_QUALITY_LEVELS_TILED_RESOURCE) { flags |= VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT | VK_IMAGE_CREATE_SPARSE_ALIASED_BIT; } // Check if the device supports the given combination of format // and sample count. D3D exposes the opaque concept of quality // levels to the application, we'll just define one such level. DxvkFormatQuery formatQuery = { }; formatQuery.format = format; formatQuery.type = VK_IMAGE_TYPE_2D; formatQuery.tiling = VK_IMAGE_TILING_OPTIMAL; formatQuery.usage = VK_IMAGE_USAGE_SAMPLED_BIT; formatQuery.flags = flags; auto properties = m_dxvkDevice->getFormatLimits(formatQuery); if (properties && (properties->sampleCounts & sampleCountFlag)) *pNumQualityLevels = 1; return S_OK; } void STDMETHODCALLTYPE D3D11Device::CheckCounterInfo(D3D11_COUNTER_INFO* pCounterInfo) { // We basically don't support counters pCounterInfo->LastDeviceDependentCounter = D3D11_COUNTER(0); pCounterInfo->NumSimultaneousCounters = 0; pCounterInfo->NumDetectableParallelUnits = 0; } HRESULT STDMETHODCALLTYPE D3D11Device::CheckCounter( const D3D11_COUNTER_DESC* pDesc, D3D11_COUNTER_TYPE* pType, UINT* pActiveCounters, LPSTR szName, UINT* pNameLength, LPSTR szUnits, UINT* pUnitsLength, LPSTR szDescription, UINT* pDescriptionLength) { Logger::err("D3D11: Counters not supported"); return E_INVALIDARG; } HRESULT STDMETHODCALLTYPE D3D11Device::CheckFeatureSupport( D3D11_FEATURE Feature, void* pFeatureSupportData, UINT FeatureSupportDataSize) { switch (Feature) { // Format support queries are special in that they use in-out // structs, and we need the Vulkan device to query them at all case D3D11_FEATURE_FORMAT_SUPPORT: { auto info = static_cast(pFeatureSupportData); if (FeatureSupportDataSize != sizeof(*info)) return E_INVALIDARG; return GetFormatSupportFlags(info->InFormat, &info->OutFormatSupport, nullptr); } return S_OK; case D3D11_FEATURE_FORMAT_SUPPORT2: { auto info = static_cast(pFeatureSupportData); if (FeatureSupportDataSize != sizeof(*info)) return E_INVALIDARG; return GetFormatSupportFlags(info->InFormat, nullptr, &info->OutFormatSupport2); } return S_OK; default: // For everything else, we can use the device feature struct // that we already initialized during device creation. return m_deviceFeatures.GetFeatureData(Feature, FeatureSupportDataSize, pFeatureSupportData); } } HRESULT STDMETHODCALLTYPE D3D11Device::GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) { return m_container->GetPrivateData(guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D11Device::SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) { return m_container->SetPrivateData(guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D11Device::SetPrivateDataInterface( REFGUID guid, const IUnknown* pData) { return m_container->SetPrivateDataInterface(guid, pData); } D3D_FEATURE_LEVEL STDMETHODCALLTYPE D3D11Device::GetFeatureLevel() { return m_featureLevel; } UINT STDMETHODCALLTYPE D3D11Device::GetCreationFlags() { return m_featureFlags; } HRESULT STDMETHODCALLTYPE D3D11Device::GetDeviceRemovedReason() { VkResult status = m_dxvkDevice->getDeviceStatus(); switch (status) { case VK_SUCCESS: return S_OK; default: return DXGI_ERROR_DEVICE_RESET; } } void STDMETHODCALLTYPE D3D11Device::GetImmediateContext(ID3D11DeviceContext** ppImmediateContext) { *ppImmediateContext = m_context.ref(); } void STDMETHODCALLTYPE D3D11Device::GetImmediateContext1(ID3D11DeviceContext1** ppImmediateContext) { *ppImmediateContext = m_context.ref(); } void STDMETHODCALLTYPE D3D11Device::GetImmediateContext2(ID3D11DeviceContext2** ppImmediateContext) { *ppImmediateContext = m_context.ref(); } void STDMETHODCALLTYPE D3D11Device::GetImmediateContext3(ID3D11DeviceContext3** ppImmediateContext) { *ppImmediateContext = m_context.ref(); } HRESULT STDMETHODCALLTYPE D3D11Device::SetExceptionMode(UINT RaiseFlags) { Logger::err("D3D11Device::SetExceptionMode: Not implemented"); return E_NOTIMPL; } UINT STDMETHODCALLTYPE D3D11Device::GetExceptionMode() { Logger::err("D3D11Device::GetExceptionMode: Not implemented"); return 0; } void STDMETHODCALLTYPE D3D11Device::GetResourceTiling( ID3D11Resource* pTiledResource, UINT* pNumTilesForEntireResource, D3D11_PACKED_MIP_DESC* pPackedMipDesc, D3D11_TILE_SHAPE* pStandardTileShapeForNonPackedMips, UINT* pNumSubresourceTilings, UINT FirstSubresourceTilingToGet, D3D11_SUBRESOURCE_TILING* pSubresourceTilingsForNonPackedMips) { D3D11_COMMON_RESOURCE_DESC desc = { }; GetCommonResourceDesc(pTiledResource, &desc); if (!(desc.MiscFlags & D3D11_RESOURCE_MISC_TILED)) { if (pNumTilesForEntireResource) *pNumTilesForEntireResource = 0; if (pPackedMipDesc) *pPackedMipDesc = D3D11_PACKED_MIP_DESC(); if (pStandardTileShapeForNonPackedMips) *pStandardTileShapeForNonPackedMips = D3D11_TILE_SHAPE(); if (pNumSubresourceTilings) { if (pSubresourceTilingsForNonPackedMips) { for (uint32_t i = 0; i < *pNumSubresourceTilings; i++) pSubresourceTilingsForNonPackedMips[i] = D3D11_SUBRESOURCE_TILING(); } *pNumSubresourceTilings = 0; } } else { DxvkSparsePageTable* sparseInfo = nullptr; uint32_t mipCount = 0; if (desc.Dim == D3D11_RESOURCE_DIMENSION_BUFFER) { Rc buffer = static_cast(pTiledResource)->GetBuffer(); sparseInfo = buffer->getSparsePageTable(); } else { Rc image = GetCommonTexture(pTiledResource)->GetImage(); sparseInfo = image->getSparsePageTable(); mipCount = image->info().mipLevels; } if (pNumTilesForEntireResource) *pNumTilesForEntireResource = sparseInfo->getPageCount(); if (pPackedMipDesc) { auto properties = sparseInfo->getProperties(); if (properties.mipTailSize) { pPackedMipDesc->NumStandardMips = properties.pagedMipCount; pPackedMipDesc->NumPackedMips = mipCount - properties.pagedMipCount; pPackedMipDesc->NumTilesForPackedMips = sparseInfo->getPageCount() - properties.mipTailPageIndex; pPackedMipDesc->StartTileIndexInOverallResource = properties.mipTailPageIndex; } else { pPackedMipDesc->NumStandardMips = mipCount; pPackedMipDesc->NumPackedMips = 0; pPackedMipDesc->NumTilesForPackedMips = 0; pPackedMipDesc->StartTileIndexInOverallResource = 0; } } if (pStandardTileShapeForNonPackedMips) { auto properties = sparseInfo->getProperties(); pStandardTileShapeForNonPackedMips->WidthInTexels = properties.pageRegionExtent.width; pStandardTileShapeForNonPackedMips->HeightInTexels = properties.pageRegionExtent.height; pStandardTileShapeForNonPackedMips->DepthInTexels = properties.pageRegionExtent.depth; } if (pNumSubresourceTilings) { uint32_t subresourceCount = sparseInfo->getSubresourceCount(); uint32_t tilingCount = subresourceCount - std::min(FirstSubresourceTilingToGet, subresourceCount); tilingCount = std::min(tilingCount, *pNumSubresourceTilings); for (uint32_t i = 0; i < tilingCount; i++) { auto subresourceInfo = sparseInfo->getSubresourceProperties(FirstSubresourceTilingToGet + i); auto dstInfo = &pSubresourceTilingsForNonPackedMips[i]; if (subresourceInfo.isMipTail) { dstInfo->WidthInTiles = 0u; dstInfo->HeightInTiles = 0u; dstInfo->DepthInTiles = 0u; dstInfo->StartTileIndexInOverallResource = D3D11_PACKED_TILE; } else { dstInfo->WidthInTiles = subresourceInfo.pageCount.width; dstInfo->HeightInTiles = subresourceInfo.pageCount.height; dstInfo->DepthInTiles = subresourceInfo.pageCount.depth; dstInfo->StartTileIndexInOverallResource = subresourceInfo.pageIndex; } } *pNumSubresourceTilings = tilingCount; } } } HRESULT STDMETHODCALLTYPE D3D11Device::RegisterDeviceRemovedEvent( HANDLE hEvent, DWORD* pdwCookie) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::err("D3D11Device::RegisterDeviceRemovedEvent: Not implemented"); return E_NOTIMPL; } void STDMETHODCALLTYPE D3D11Device::UnregisterDeviceRemoved( DWORD dwCookie) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::err("D3D11Device::UnregisterDeviceRemovedEvent: Not implemented"); } DXGI_VK_FORMAT_INFO D3D11Device::LookupFormat( DXGI_FORMAT Format, DXGI_VK_FORMAT_MODE Mode) const { return m_d3d11Formats.GetFormatInfo(Format, Mode); } DXGI_VK_FORMAT_INFO D3D11Device::LookupPackedFormat( DXGI_FORMAT Format, DXGI_VK_FORMAT_MODE Mode) const { return m_d3d11Formats.GetPackedFormatInfo(Format, Mode); } DXGI_VK_FORMAT_FAMILY D3D11Device::LookupFamily( DXGI_FORMAT Format, DXGI_VK_FORMAT_MODE Mode) const { return m_d3d11Formats.GetFormatFamily(Format, Mode); } bool D3D11Device::Is11on12Device() const { return m_container->Is11on12Device(); } D3D_FEATURE_LEVEL D3D11Device::GetMaxFeatureLevel( const Rc& Instance, const Rc& Adapter) { // Check whether baseline features are supported by the device DxvkDeviceFeatures features = GetDeviceFeatures(Adapter); if (!Adapter->checkFeatureSupport(features)) return D3D_FEATURE_LEVEL(); // The feature level override always takes precedence static const std::array, 9> s_featureLevels = {{ { "12_1", D3D_FEATURE_LEVEL_12_1 }, { "12_0", D3D_FEATURE_LEVEL_12_0 }, { "11_1", D3D_FEATURE_LEVEL_11_1 }, { "11_0", D3D_FEATURE_LEVEL_11_0 }, { "10_1", D3D_FEATURE_LEVEL_10_1 }, { "10_0", D3D_FEATURE_LEVEL_10_0 }, { "9_3", D3D_FEATURE_LEVEL_9_3 }, { "9_2", D3D_FEATURE_LEVEL_9_2 }, { "9_1", D3D_FEATURE_LEVEL_9_1 }, }}; std::string maxLevel = Instance->config().getOption("d3d11.maxFeatureLevel"); auto entry = std::find_if(s_featureLevels.begin(), s_featureLevels.end(), [&] (const std::pair& pair) { return pair.first == maxLevel; }); if (entry != s_featureLevels.end()) return entry->second; // Otherwise, check the actually available device features return D3D11DeviceFeatures::GetMaxFeatureLevel(Instance, Adapter); } DxvkDeviceFeatures D3D11Device::GetDeviceFeatures( const Rc& Adapter) { DxvkDeviceFeatures supported = Adapter->features(); DxvkDeviceFeatures enabled = {}; // Required for feature level 10_1 enabled.core.features.depthBiasClamp = VK_TRUE; enabled.core.features.depthClamp = VK_TRUE; enabled.core.features.dualSrcBlend = VK_TRUE; enabled.core.features.fillModeNonSolid = VK_TRUE; enabled.core.features.fullDrawIndexUint32 = VK_TRUE; enabled.core.features.geometryShader = VK_TRUE; enabled.core.features.imageCubeArray = VK_TRUE; enabled.core.features.independentBlend = VK_TRUE; enabled.core.features.multiViewport = VK_TRUE; enabled.core.features.occlusionQueryPrecise = VK_TRUE; enabled.core.features.pipelineStatisticsQuery = supported.core.features.pipelineStatisticsQuery; enabled.core.features.sampleRateShading = VK_TRUE; enabled.core.features.samplerAnisotropy = supported.core.features.samplerAnisotropy; enabled.core.features.shaderClipDistance = VK_TRUE; enabled.core.features.shaderCullDistance = VK_TRUE; enabled.core.features.shaderImageGatherExtended = VK_TRUE; enabled.core.features.textureCompressionBC = VK_TRUE; enabled.vk12.samplerMirrorClampToEdge = VK_TRUE; enabled.vk13.shaderDemoteToHelperInvocation = VK_TRUE; enabled.extCustomBorderColor.customBorderColors = supported.extCustomBorderColor.customBorderColorWithoutFormat; enabled.extCustomBorderColor.customBorderColorWithoutFormat = supported.extCustomBorderColor.customBorderColorWithoutFormat; enabled.extTransformFeedback.transformFeedback = VK_TRUE; enabled.extTransformFeedback.geometryStreams = VK_TRUE; enabled.extVertexAttributeDivisor.vertexAttributeInstanceRateDivisor = supported.extVertexAttributeDivisor.vertexAttributeInstanceRateDivisor; enabled.extVertexAttributeDivisor.vertexAttributeInstanceRateZeroDivisor = supported.extVertexAttributeDivisor.vertexAttributeInstanceRateZeroDivisor; // Required for Feature Level 11_0 enabled.core.features.drawIndirectFirstInstance = supported.core.features.drawIndirectFirstInstance; enabled.core.features.fragmentStoresAndAtomics = supported.core.features.fragmentStoresAndAtomics; enabled.core.features.tessellationShader = supported.core.features.tessellationShader; // Required for Feature Level 11_1 enabled.core.features.logicOp = supported.core.features.logicOp; enabled.core.features.vertexPipelineStoresAndAtomics = supported.core.features.vertexPipelineStoresAndAtomics; // Required for Feature Level 12_0 enabled.core.features.sparseBinding = supported.core.features.sparseBinding; enabled.core.features.sparseResidencyBuffer = supported.core.features.sparseResidencyBuffer; enabled.core.features.sparseResidencyImage2D = supported.core.features.sparseResidencyImage2D; enabled.core.features.sparseResidencyImage3D = supported.core.features.sparseResidencyImage3D; enabled.core.features.sparseResidency2Samples = supported.core.features.sparseResidency2Samples; enabled.core.features.sparseResidency4Samples = supported.core.features.sparseResidency4Samples; enabled.core.features.sparseResidency8Samples = supported.core.features.sparseResidency8Samples; enabled.core.features.sparseResidency16Samples = supported.core.features.sparseResidency16Samples; enabled.core.features.sparseResidencyAliased = supported.core.features.sparseResidencyAliased; enabled.core.features.shaderResourceResidency = supported.core.features.shaderResourceResidency; enabled.core.features.shaderResourceMinLod = supported.core.features.shaderResourceMinLod; enabled.vk12.samplerFilterMinmax = supported.vk12.samplerFilterMinmax; // Required for Feature Level 12_1 enabled.extFragmentShaderInterlock.fragmentShaderSampleInterlock = supported.extFragmentShaderInterlock.fragmentShaderSampleInterlock; enabled.extFragmentShaderInterlock.fragmentShaderPixelInterlock = supported.extFragmentShaderInterlock.fragmentShaderPixelInterlock; // Optional in any feature level enabled.core.features.depthBounds = supported.core.features.depthBounds; enabled.core.features.shaderFloat64 = supported.core.features.shaderFloat64; enabled.core.features.shaderInt64 = supported.core.features.shaderInt64; // Depth bias control enabled.extDepthBiasControl.depthBiasControl = supported.extDepthBiasControl.depthBiasControl; enabled.extDepthBiasControl.depthBiasExact = supported.extDepthBiasControl.depthBiasExact; enabled.extDepthBiasControl.leastRepresentableValueForceUnormRepresentation = supported.extDepthBiasControl.leastRepresentableValueForceUnormRepresentation; return enabled; } HRESULT D3D11Device::CreateShaderModule( D3D11CommonShader* pShaderModule, DxvkShaderKey ShaderKey, const void* pShaderBytecode, size_t BytecodeLength, ID3D11ClassLinkage* pClassLinkage, const DxbcModuleInfo* pModuleInfo) { if (!BytecodeLength || !pShaderBytecode) return E_INVALIDARG; if (pClassLinkage != nullptr) Logger::warn("D3D11Device::CreateShaderModule: Class linkage not supported"); D3D11CommonShader commonShader; HRESULT hr = m_shaderModules.GetShaderModule(this, &ShaderKey, pModuleInfo, pShaderBytecode, BytecodeLength, &commonShader); if (FAILED(hr)) return hr; auto shader = commonShader.GetShader(); if (shader->flags().test(DxvkShaderFlag::ExportsStencilRef) && !m_dxvkDevice->features().extShaderStencilExport) return E_INVALIDARG; if (shader->flags().test(DxvkShaderFlag::ExportsViewportIndexLayerFromVertexStage) && (!m_dxvkDevice->features().vk12.shaderOutputViewportIndex || !m_dxvkDevice->features().vk12.shaderOutputLayer)) return E_INVALIDARG; if (shader->flags().test(DxvkShaderFlag::UsesSparseResidency) && !m_dxvkDevice->features().core.features.shaderResourceResidency) return E_INVALIDARG; if (shader->flags().test(DxvkShaderFlag::UsesFragmentCoverage) && !m_dxvkDevice->properties().extConservativeRasterization.fullyCoveredFragmentShaderInputVariable) return E_INVALIDARG; *pShaderModule = std::move(commonShader); return S_OK; } HRESULT D3D11Device::GetFormatSupportFlags(DXGI_FORMAT Format, UINT* pFlags1, UINT* pFlags2) const { const DXGI_VK_FORMAT_INFO fmtMapping = LookupFormat(Format, DXGI_VK_FORMAT_MODE_ANY); // Reset output flags preemptively if (pFlags1 != nullptr) *pFlags1 = 0; if (pFlags2 != nullptr) *pFlags2 = 0; // Unsupported or invalid format if (Format && fmtMapping.Format == VK_FORMAT_UNDEFINED) return E_FAIL; // Query Vulkan format properties and supported features for it const DxvkFormatInfo* fmtProperties = lookupFormatInfo(fmtMapping.Format); DxvkFormatFeatures fmtSupport = fmtMapping.Format != VK_FORMAT_UNDEFINED ? m_dxvkDevice->getFormatFeatures(fmtMapping.Format) : DxvkFormatFeatures(); VkFormatFeatureFlags2 bufFeatures = fmtSupport.buffer; VkFormatFeatureFlags2 imgFeatures = fmtSupport.optimal | fmtSupport.linear; // For multi-plane images, we want to check available view formats as well if (fmtProperties->flags.test(DxvkFormatFlag::MultiPlane)) { const VkFormatFeatureFlags2 featureMask = VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT; DXGI_VK_FORMAT_FAMILY formatFamily = LookupFamily(Format, DXGI_VK_FORMAT_MODE_ANY); for (uint32_t i = 0; i < formatFamily.FormatCount; i++) { DxvkFormatFeatures viewFmtSupport = m_dxvkDevice->getFormatFeatures(formatFamily.Formats[i]); imgFeatures |= (viewFmtSupport.optimal | viewFmtSupport.linear) & featureMask; } } UINT flags1 = 0; UINT flags2 = 0; // Format can be used for shader resource views with buffers if ((bufFeatures & VK_FORMAT_FEATURE_2_UNIFORM_TEXEL_BUFFER_BIT) || !Format) flags1 |= D3D11_FORMAT_SUPPORT_BUFFER; // Format can be used for vertex data if (bufFeatures & VK_FORMAT_FEATURE_2_VERTEX_BUFFER_BIT) flags1 |= D3D11_FORMAT_SUPPORT_IA_VERTEX_BUFFER; // Format can be used for index data. Only // these two formats are supported by D3D11. if (Format == DXGI_FORMAT_R16_UINT || Format == DXGI_FORMAT_R32_UINT) flags1 |= D3D11_FORMAT_SUPPORT_IA_INDEX_BUFFER; // These formats are technically irrelevant since // SO buffers are passed in as raw buffers and not // as views, but the feature flag exists regardless if (Format == DXGI_FORMAT_R32_FLOAT || Format == DXGI_FORMAT_R32_UINT || Format == DXGI_FORMAT_R32_SINT || Format == DXGI_FORMAT_R32G32_FLOAT || Format == DXGI_FORMAT_R32G32_UINT || Format == DXGI_FORMAT_R32G32_SINT || Format == DXGI_FORMAT_R32G32B32_FLOAT || Format == DXGI_FORMAT_R32G32B32_UINT || Format == DXGI_FORMAT_R32G32B32_SINT || Format == DXGI_FORMAT_R32G32B32A32_FLOAT || Format == DXGI_FORMAT_R32G32B32A32_UINT || Format == DXGI_FORMAT_R32G32B32A32_SINT) flags1 |= D3D11_FORMAT_SUPPORT_SO_BUFFER; if (imgFeatures & (VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT)) { const VkFormat depthFormat = LookupFormat(Format, DXGI_VK_FORMAT_MODE_DEPTH).Format; if (GetImageTypeSupport(fmtMapping.Format, VK_IMAGE_TYPE_1D, 0)) flags1 |= D3D11_FORMAT_SUPPORT_TEXTURE1D; if (GetImageTypeSupport(fmtMapping.Format, VK_IMAGE_TYPE_2D, 0)) flags1 |= D3D11_FORMAT_SUPPORT_TEXTURE2D; if (GetImageTypeSupport(fmtMapping.Format, VK_IMAGE_TYPE_3D, 0)) flags1 |= D3D11_FORMAT_SUPPORT_TEXTURE3D; // We only support tiled resources with a single aspect D3D11_TILED_RESOURCES_TIER tiledResourcesTier = m_deviceFeatures.GetTiledResourcesTier(); VkImageAspectFlags sparseAspects = VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT; if (tiledResourcesTier && !(fmtProperties->aspectMask & ~sparseAspects)) { VkImageCreateFlags flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT | VK_IMAGE_CREATE_SPARSE_ALIASED_BIT; if (GetImageTypeSupport(fmtMapping.Format, VK_IMAGE_TYPE_2D, flags)) flags2 |= D3D11_FORMAT_SUPPORT2_TILED; } flags1 |= D3D11_FORMAT_SUPPORT_MIP | D3D11_FORMAT_SUPPORT_CAST_WITHIN_BIT_LAYOUT; // Format can be read if (imgFeatures & VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT) { flags1 |= D3D11_FORMAT_SUPPORT_TEXTURECUBE | D3D11_FORMAT_SUPPORT_SHADER_LOAD | D3D11_FORMAT_SUPPORT_SHADER_GATHER | D3D11_FORMAT_SUPPORT_SHADER_SAMPLE | D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_INPUT; if (depthFormat != VK_FORMAT_UNDEFINED) { flags1 |= D3D11_FORMAT_SUPPORT_SHADER_GATHER_COMPARISON | D3D11_FORMAT_SUPPORT_SHADER_SAMPLE_COMPARISON; } } // Format is a color format that can be used for rendering if (imgFeatures & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT) { flags1 |= D3D11_FORMAT_SUPPORT_RENDER_TARGET | D3D11_FORMAT_SUPPORT_MIP_AUTOGEN | D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_OUTPUT; if (m_dxvkDevice->features().core.features.logicOp) flags2 |= D3D11_FORMAT_SUPPORT2_OUTPUT_MERGER_LOGIC_OP; } // Format supports blending when used for rendering if (imgFeatures & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT) flags1 |= D3D11_FORMAT_SUPPORT_BLENDABLE; // Format is a depth-stencil format that can be used for rendering if (imgFeatures & VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT) flags1 |= D3D11_FORMAT_SUPPORT_DEPTH_STENCIL; // Report supported swap chain formats if (Format == DXGI_FORMAT_R8G8B8A8_UNORM || Format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB || Format == DXGI_FORMAT_B8G8R8A8_UNORM || Format == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB || Format == DXGI_FORMAT_R16G16B16A16_FLOAT || Format == DXGI_FORMAT_R10G10B10A2_UNORM || Format == DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM) flags1 |= D3D11_FORMAT_SUPPORT_DISPLAY; // Query multisample support for this format VkImageUsageFlags usage = (fmtProperties->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT : VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; DxvkFormatQuery formatQuery = { }; formatQuery.format = fmtMapping.Format; formatQuery.type = VK_IMAGE_TYPE_2D; formatQuery.tiling = VK_IMAGE_TILING_OPTIMAL; formatQuery.usage = usage; auto limits = m_dxvkDevice->getFormatLimits(formatQuery); if (limits && limits->sampleCounts > VK_SAMPLE_COUNT_1_BIT) { flags1 |= D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET | D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE | D3D11_FORMAT_SUPPORT_MULTISAMPLE_LOAD; } // Query whether the format is shareable if ((fmtProperties->aspectMask & (VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_PLANE_0_BIT)) && (m_dxvkDevice->features().khrExternalMemoryWin32)) { constexpr VkExternalMemoryFeatureFlags featureMask = VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT; formatQuery.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT; limits = m_dxvkDevice->getFormatLimits(formatQuery); if (limits && (limits->externalFeatures & featureMask)) flags2 |= D3D11_FORMAT_SUPPORT2_SHAREABLE; } } // Format can be used for storage images or storage texel buffers if ((bufFeatures & VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT) && (imgFeatures & VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT) && (imgFeatures & VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT)) { flags1 |= D3D11_FORMAT_SUPPORT_TYPED_UNORDERED_ACCESS_VIEW; flags2 |= D3D11_FORMAT_SUPPORT2_UAV_TYPED_STORE; if (m_dxbcOptions.supportsTypedUavLoadR32) { // If the R32 formats are supported without format declarations, // we can optionally support additional formats for typed loads if (imgFeatures & VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT) flags2 |= D3D11_FORMAT_SUPPORT2_UAV_TYPED_LOAD; } else { // Otherwise, we need to emit format declarations, so we can // only support the basic set of R32 formats for typed loads if (Format == DXGI_FORMAT_R32_FLOAT || Format == DXGI_FORMAT_R32_UINT || Format == DXGI_FORMAT_R32_SINT) flags2 |= D3D11_FORMAT_SUPPORT2_UAV_TYPED_LOAD; } if (Format == DXGI_FORMAT_R32_UINT || Format == DXGI_FORMAT_R32_SINT) { flags2 |= D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_ADD | D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_BITWISE_OPS | D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_COMPARE_STORE_OR_COMPARE_EXCHANGE | D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_EXCHANGE; } if (Format == DXGI_FORMAT_R32_SINT) flags2 |= D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_SIGNED_MIN_OR_MAX; if (Format == DXGI_FORMAT_R32_UINT) flags2 |= D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_UNSIGNED_MIN_OR_MAX; } // Mark everyting as CPU lockable if (flags1 | flags2) flags1 |= D3D11_FORMAT_SUPPORT_CPU_LOCKABLE; // Write back format support flags if (pFlags1 != nullptr) *pFlags1 = flags1; if (pFlags2 != nullptr) *pFlags2 = flags2; return (pFlags1 && flags1) || (pFlags2 && flags2) ? S_OK : E_FAIL; } BOOL D3D11Device::GetImageTypeSupport(VkFormat Format, VkImageType Type, VkImageCreateFlags Flags) const { DxvkFormatQuery formatQuery = { }; formatQuery.format = Format; formatQuery.type = Type; formatQuery.tiling = VK_IMAGE_TILING_OPTIMAL; formatQuery.usage = VK_IMAGE_USAGE_SAMPLED_BIT; formatQuery.flags = Flags; auto properties = m_dxvkDevice->getFormatLimits(formatQuery); if (!properties) { formatQuery.tiling = VK_IMAGE_TILING_LINEAR; properties = m_dxvkDevice->getFormatLimits(formatQuery); } return properties.has_value(); } uint32_t D3D11Device::GetViewPlaneIndex( ID3D11Resource* pResource, DXGI_FORMAT ViewFormat) { auto texture = GetCommonTexture(pResource); if (!texture) return 0; uint32_t planeCount = texture->GetPlaneCount(); if (planeCount == 1) return 0; auto formatMode = texture->GetFormatMode(); auto formatFamily = LookupFamily(texture->Desc()->Format, formatMode); auto viewFormat = LookupFormat(ViewFormat, formatMode); for (uint32_t i = 0; i < formatFamily.FormatCount; i++) { if (formatFamily.Formats[i] == viewFormat.Format) return i % planeCount; } return ~0u; } template HRESULT D3D11Device::OpenSharedResourceGeneric( HANDLE hResource, REFIID ReturnedInterface, void** ppResource) { InitReturnPtr(ppResource); if (ppResource == nullptr) return S_FALSE; #ifdef _WIN32 HANDLE ntHandle = IsKmtHandle ? openKmtHandle(hResource) : hResource; if (ntHandle == INVALID_HANDLE_VALUE) { Logger::warn(str::format("D3D11Device::OpenSharedResourceGeneric: Handle not found: ", hResource)); return E_INVALIDARG; } DxvkSharedTextureMetadata metadata; bool ret = getSharedMetadata(ntHandle, &metadata, sizeof(metadata), NULL); if (IsKmtHandle) ::CloseHandle(ntHandle); if (!ret) { Logger::warn("D3D11Device::OpenSharedResourceGeneric: Failed to get shared resource info for a texture"); return E_INVALIDARG; } D3D11_COMMON_TEXTURE_DESC d3d11Desc; d3d11Desc.Width = metadata.Width; d3d11Desc.Height = metadata.Height; d3d11Desc.Depth = 1, d3d11Desc.MipLevels = metadata.MipLevels; d3d11Desc.ArraySize = metadata.ArraySize; d3d11Desc.Format = metadata.Format; d3d11Desc.SampleDesc = metadata.SampleDesc; d3d11Desc.Usage = metadata.Usage; d3d11Desc.BindFlags = metadata.BindFlags; d3d11Desc.CPUAccessFlags = metadata.CPUAccessFlags; d3d11Desc.MiscFlags = metadata.MiscFlags; d3d11Desc.TextureLayout = metadata.TextureLayout; if ((d3d11Desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE) && !(d3d11Desc.MiscFlags & (D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX))) { Logger::warn("Fixing up wrong MiscFlags"); d3d11Desc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED; } // Only 2D textures may be shared try { const Com texture = new D3D11Texture2D(this, &d3d11Desc, nullptr, hResource); texture->QueryInterface(ReturnedInterface, ppResource); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } #else Logger::warn("D3D11Device::OpenSharedResourceGeneric: Not supported on this platform."); return E_INVALIDARG; #endif } template void D3D11Device::CopySubresourceData( Void* pData, UINT RowPitch, UINT DepthPitch, D3D11CommonTexture* pTexture, UINT Subresource, const D3D11_BOX* pBox) { // Validate box against subresource dimensions auto formatInfo = lookupFormatInfo(pTexture->GetPackedFormat()); auto subresource = pTexture->GetSubresourceFromIndex( formatInfo->aspectMask, Subresource); VkOffset3D offset = { 0, 0, 0 }; VkExtent3D extent = pTexture->MipLevelExtent(subresource.mipLevel); if (pBox) { if (pBox->left >= pBox->right || pBox->top >= pBox->bottom || pBox->front >= pBox->back) return; // legal, but no-op if (pBox->right > extent.width || pBox->bottom > extent.height || pBox->back > extent.depth) return; // out of bounds offset = VkOffset3D { int32_t(pBox->left), int32_t(pBox->top), int32_t(pBox->front) }; extent = VkExtent3D { pBox->right - pBox->left, pBox->bottom - pBox->top, pBox->back - pBox->front }; } // Copy image data, one plane at a time for multi-plane formats Rc image = pTexture->GetImage(); VkDeviceSize dataOffset = 0; for (uint32_t i = 0; i < pTexture->GetPlaneCount(); i++) { // Find current image aspects to process VkImageAspectFlags aspect = formatInfo->aspectMask; if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) aspect = vk::getPlaneAspect(i); // Compute data layout of the current subresource D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT layout = pTexture->GetSubresourceLayout(aspect, Subresource); // Compute actual map pointer, accounting for the region offset void* mapPtr = pTexture->GetMapPtr(Subresource, pTexture->ComputeMappedOffset(Subresource, i, offset)); if constexpr (std::is_const::value) { // WriteToSubresource auto srcData = reinterpret_cast(pData) + dataOffset; util::packImageData(mapPtr, srcData, RowPitch, DepthPitch, layout.RowPitch, layout.DepthPitch, image->info().type, extent, 1, formatInfo, aspect); } else { // ReadFromSubresource auto dstData = reinterpret_cast(pData) + dataOffset; util::packImageData(dstData, mapPtr, layout.RowPitch, layout.DepthPitch, RowPitch, DepthPitch, image->info().type, extent, 1, formatInfo, aspect); } // Advance linear data pointer by the size of the current aspect dataOffset += util::computeImageDataSize( pTexture->GetPackedFormat(), extent, aspect); } // Track dirty texture region if necessary if constexpr (std::is_const::value) pTexture->AddDirtyRegion(Subresource, offset, extent); } D3D11DeviceExt::D3D11DeviceExt( D3D11DXGIDevice* pContainer, D3D11Device* pDevice) : m_container(pContainer), m_device(pDevice) { } ULONG STDMETHODCALLTYPE D3D11DeviceExt::AddRef() { return m_container->AddRef(); } ULONG STDMETHODCALLTYPE D3D11DeviceExt::Release() { return m_container->Release(); } HRESULT STDMETHODCALLTYPE D3D11DeviceExt::QueryInterface( REFIID riid, void** ppvObject) { return m_container->QueryInterface(riid, ppvObject); } BOOL STDMETHODCALLTYPE D3D11DeviceExt::GetExtensionSupport( D3D11_VK_EXTENSION Extension) { const auto& deviceFeatures = m_device->GetDXVKDevice()->features(); switch (Extension) { case D3D11_VK_EXT_BARRIER_CONTROL: return true; case D3D11_VK_EXT_MULTI_DRAW_INDIRECT: return deviceFeatures.core.features.multiDrawIndirect; case D3D11_VK_EXT_MULTI_DRAW_INDIRECT_COUNT: return deviceFeatures.core.features.multiDrawIndirect && deviceFeatures.vk12.drawIndirectCount; case D3D11_VK_EXT_DEPTH_BOUNDS: return deviceFeatures.core.features.depthBounds; case D3D11_VK_NVX_IMAGE_VIEW_HANDLE: return deviceFeatures.nvxImageViewHandle; case D3D11_VK_NVX_BINARY_IMPORT: return deviceFeatures.nvxBinaryImport && deviceFeatures.vk12.bufferDeviceAddress; default: return false; } } bool STDMETHODCALLTYPE D3D11DeviceExt::GetCudaTextureObjectNVX(uint32_t srvDriverHandle, uint32_t samplerDriverHandle, uint32_t* pCudaTextureHandle) { ID3D11ShaderResourceView* srv = HandleToSrvNVX(srvDriverHandle); if (!srv) { Logger::warn(str::format("GetCudaTextureObjectNVX() failure - srv handle wasn't found: ", srvDriverHandle)); return false; } ID3D11SamplerState* samplerState = HandleToSamplerNVX(samplerDriverHandle); if (!samplerState) { Logger::warn(str::format("GetCudaTextureObjectNVX() failure - sampler handle wasn't found: ", samplerDriverHandle)); return false; } D3D11SamplerState* pSS = static_cast(samplerState); Rc pDSS = pSS->GetDXVKSampler(); D3D11ShaderResourceView* pSRV = static_cast(srv); Rc pIV = pSRV->GetImageView(); LockImage(pIV->image(), 0u); VkImageViewHandleInfoNVX imageViewHandleInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_HANDLE_INFO_NVX }; imageViewHandleInfo.imageView = pIV->handle(); imageViewHandleInfo.sampler = pDSS->handle(); imageViewHandleInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; // note: there's no implicit lifetime management here; it's up to the // app to keep the sampler and SRV alive as long as it wants to use this // derived handle. VkDevice vkDevice = m_device->GetDXVKDevice()->handle(); *pCudaTextureHandle = m_device->GetDXVKDevice()->vkd()->vkGetImageViewHandleNVX(vkDevice, &imageViewHandleInfo); if (!*pCudaTextureHandle) { Logger::warn("GetCudaTextureObjectNVX() handle==0 - failed"); return false; } return true; } bool STDMETHODCALLTYPE D3D11DeviceExt::CreateCubinComputeShaderWithNameNVX(const void* pCubin, uint32_t size, uint32_t blockX, uint32_t blockY, uint32_t blockZ, const char* pShaderName, IUnknown** phShader) { Rc dxvkDevice = m_device->GetDXVKDevice(); VkDevice vkDevice = dxvkDevice->handle(); VkCuModuleCreateInfoNVX moduleCreateInfo = { VK_STRUCTURE_TYPE_CU_MODULE_CREATE_INFO_NVX }; moduleCreateInfo.pData = pCubin; moduleCreateInfo.dataSize = size; VkCuModuleNVX cuModule; VkCuFunctionNVX cuFunction; VkResult result; if ((result = dxvkDevice->vkd()->vkCreateCuModuleNVX(vkDevice, &moduleCreateInfo, nullptr, &cuModule))) { Logger::warn(str::format("CreateCubinComputeShaderWithNameNVX() - failure to create module - result=", result, " pcubindata=", pCubin, " cubinsize=", size)); return false; // failure } VkCuFunctionCreateInfoNVX functionCreateInfo = { VK_STRUCTURE_TYPE_CU_FUNCTION_CREATE_INFO_NVX }; functionCreateInfo.module = cuModule; functionCreateInfo.pName = pShaderName; if ((result = dxvkDevice->vkd()->vkCreateCuFunctionNVX(vkDevice, &functionCreateInfo, nullptr, &cuFunction))) { dxvkDevice->vkd()->vkDestroyCuModuleNVX(vkDevice, cuModule, nullptr); Logger::warn(str::format("CreateCubinComputeShaderWithNameNVX() - failure to create function - result=", result)); return false; } *phShader = ref(new CubinShaderWrapper(dxvkDevice, cuModule, cuFunction, { blockX, blockY, blockZ })); return true; } bool STDMETHODCALLTYPE D3D11DeviceExt::GetResourceHandleGPUVirtualAddressAndSizeNVX(void* hObject, uint64_t* gpuVAStart, uint64_t* gpuVASize) { // The hObject 'opaque driver handle' is really just a straight cast // of the corresponding ID3D11Resource* in dxvk/dxvknvapi ID3D11Resource* pResource = static_cast(hObject); D3D11_COMMON_RESOURCE_DESC resourceDesc; if (FAILED(GetCommonResourceDesc(pResource, &resourceDesc))) { Logger::warn("GetResourceHandleGPUVirtualAddressAndSize: Invalid resource"); return false; } Rc dxvkDevice = m_device->GetDXVKDevice(); VkDevice vkDevice = dxvkDevice->handle(); if (resourceDesc.Dim == D3D11_RESOURCE_DIMENSION_TEXTURE2D) { D3D11CommonTexture *texture = GetCommonTexture(pResource); // Ensure that the image has a stable GPU address and // won't be relocated by the backend going forward Rc dxvkImage = texture->GetImage(); if (!LockImage(dxvkImage, VK_IMAGE_USAGE_SAMPLED_BIT)) return false; // The d3d11 nvapi provides us a texture, but vulkan only lets us // get the GPU address from an image view. So, make a private image // view and get the address from that. DxvkImageViewKey viewInfo; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.format = dxvkImage->info().format; viewInfo.aspects = dxvkImage->formatInfo()->aspectMask; viewInfo.mipIndex = 0; viewInfo.mipCount = dxvkImage->info().mipLevels; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; auto dxvkView = dxvkImage->createView(viewInfo); VkImageViewAddressPropertiesNVX imageViewAddressProperties = { VK_STRUCTURE_TYPE_IMAGE_VIEW_ADDRESS_PROPERTIES_NVX }; VkResult vr = dxvkDevice->vkd()->vkGetImageViewAddressNVX(vkDevice, dxvkView->handle(), &imageViewAddressProperties); if (vr != VK_SUCCESS) { Logger::warn(str::format("GetResourceHandleGPUVirtualAddressAndSize(): Failed: vr = ", vr)); return false; } *gpuVAStart = imageViewAddressProperties.deviceAddress; *gpuVASize = imageViewAddressProperties.size; } else if (resourceDesc.Dim == D3D11_RESOURCE_DIMENSION_BUFFER) { Rc dxvkBuffer = GetCommonBuffer(pResource)->GetBuffer(); LockBuffer(dxvkBuffer); *gpuVAStart = dxvkBuffer->gpuAddress(); *gpuVASize = dxvkBuffer->info().size; } else { Logger::warn(str::format("GetResourceHandleGPUVirtualAddressAndSize(): Unsupported resource type: ", resourceDesc.Dim)); return false; } if (!*gpuVAStart) Logger::warn("GetResourceHandleGPUVirtualAddressAndSize() addr==0 - unexpected"); // ... but not explicitly a failure; continue return true; } bool STDMETHODCALLTYPE D3D11DeviceExt::CreateUnorderedAccessViewAndGetDriverHandleNVX( ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, ID3D11UnorderedAccessView** ppUAV, uint32_t* pDriverHandle) { D3D11_COMMON_RESOURCE_DESC resourceDesc = { }; GetCommonResourceDesc(pResource, &resourceDesc); if (resourceDesc.Dim != D3D11_RESOURCE_DIMENSION_TEXTURE2D) { Logger::warn(str::format("CreateUnorderedAccessViewAndGetDriverHandleNVX(): Unsupported dimension: ", resourceDesc.Dim)); return false; } Rc dxvkImage = GetCommonTexture(pResource)->GetImage(); if (!(dxvkImage->info().usage & VK_IMAGE_USAGE_STORAGE_BIT)) { Logger::warn(str::format("CreateUnorderedAccessViewAndGetDriverHandleNVX(res=", pResource, "): Image not UAV compatible")); return false; } Com uav; if (FAILED(m_device->CreateUnorderedAccessView(pResource, pDesc, &uav))) return false; Rc dxvkImageView = static_cast(uav.ptr())->GetImageView(); LockImage(dxvkImageView->image(), 0u); VkImageViewHandleInfoNVX imageViewHandleInfo = {VK_STRUCTURE_TYPE_IMAGE_VIEW_HANDLE_INFO_NVX}; imageViewHandleInfo.imageView = dxvkImageView->handle(); imageViewHandleInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; Rc dxvkDevice = m_device->GetDXVKDevice(); *pDriverHandle = dxvkDevice->vkd()->vkGetImageViewHandleNVX( dxvkDevice->handle(), &imageViewHandleInfo); if (!*pDriverHandle) { Logger::warn("CreateUnorderedAccessViewAndGetDriverHandleNVX(): Handle is 0"); return false; } *ppUAV = uav.ref(); return true; } bool STDMETHODCALLTYPE D3D11DeviceExt::CreateShaderResourceViewAndGetDriverHandleNVX(ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRV, uint32_t* pDriverHandle) { D3D11_COMMON_RESOURCE_DESC resourceDesc = { }; GetCommonResourceDesc(pResource, &resourceDesc); if (resourceDesc.Dim != D3D11_RESOURCE_DIMENSION_TEXTURE2D) { Logger::warn(str::format("CreateShaderResourceViewAndGetDriverHandleNVX(): Unsupported dimension: ", resourceDesc.Dim)); return false; } Rc dxvkImage = GetCommonTexture(pResource)->GetImage(); if (!(dxvkImage->info().usage & VK_IMAGE_USAGE_SAMPLED_BIT)) { Logger::warn(str::format("CreateShaderResourceViewAndGetDriverHandleNVX(res=", pResource, "): Image not SRV compatible")); return false; } Com srv; if (FAILED(m_device->CreateShaderResourceView(pResource, pDesc, &srv))) return false; Rc dxvkImageView = static_cast(srv.ptr())->GetImageView(); LockImage(dxvkImageView->image(), 0u); VkImageViewHandleInfoNVX imageViewHandleInfo = {VK_STRUCTURE_TYPE_IMAGE_VIEW_HANDLE_INFO_NVX}; imageViewHandleInfo.imageView = dxvkImageView->handle(); imageViewHandleInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; Rc dxvkDevice = m_device->GetDXVKDevice(); *pDriverHandle = dxvkDevice->vkd()->vkGetImageViewHandleNVX( dxvkDevice->handle(), &imageViewHandleInfo); if (!*pDriverHandle) { Logger::warn("CreateShaderResourceViewAndGetDriverHandleNVX(): Handle is 0"); return false; } // will need to look-up resource from uint32 handle later *ppSRV = srv.ref(); AddSrvAndHandleNVX(srv.ptr(), *pDriverHandle); return true; } bool STDMETHODCALLTYPE D3D11DeviceExt::CreateSamplerStateAndGetDriverHandleNVX(const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState, uint32_t* pDriverHandle) { if (FAILED(m_device->CreateSamplerState(pSamplerDesc, ppSamplerState))) return false; // for our purposes the actual value doesn't matter, only its uniqueness static std::atomic s_seqNum = 0; *pDriverHandle = ++s_seqNum; // will need to look-up sampler from uint32 handle later AddSamplerAndHandleNVX(*ppSamplerState, *pDriverHandle); return true; } void D3D11DeviceExt::AddSamplerAndHandleNVX(ID3D11SamplerState* pSampler, uint32_t Handle) { std::lock_guard lock(m_mapLock); m_samplerHandleToPtr[Handle] = pSampler; } ID3D11SamplerState* D3D11DeviceExt::HandleToSamplerNVX(uint32_t Handle) { std::lock_guard lock(m_mapLock); auto got = m_samplerHandleToPtr.find(Handle); if (got == m_samplerHandleToPtr.end()) return nullptr; return static_cast(got->second); } void D3D11DeviceExt::AddSrvAndHandleNVX(ID3D11ShaderResourceView* pSrv, uint32_t Handle) { std::lock_guard lock(m_mapLock); m_srvHandleToPtr[Handle] = pSrv; } ID3D11ShaderResourceView* D3D11DeviceExt::HandleToSrvNVX(uint32_t Handle) { std::lock_guard lock(m_mapLock); auto got = m_srvHandleToPtr.find(Handle); if (got == m_srvHandleToPtr.end()) return nullptr; return static_cast(got->second); } bool D3D11DeviceExt::LockImage( const Rc& Image, VkImageUsageFlags Usage) { if (!Image->canRelocate() && (Image->info().usage & Usage)) return true; bool feedback = false; auto chunk = m_device->AllocCsChunk(DxvkCsChunkFlag::SingleUse); chunk->push([ cImage = Image, cUsage = Usage, &feedback ] (DxvkContext* ctx) { DxvkImageUsageInfo usageInfo; usageInfo.usage = cUsage; usageInfo.stableGpuAddress = VK_TRUE; feedback = ctx->ensureImageCompatibility(cImage, usageInfo); }); m_device->GetContext()->InjectCsChunk(DxvkCsQueue::HighPriority, std::move(chunk), true); if (!feedback) { Logger::err(str::format("Failed to lock image:" "\n Image format: ", Image->info().format, "\n Image usage: ", std::hex, Image->info().usage, "\n Desired usage: ", std::hex, Usage)); } return feedback; } void D3D11DeviceExt::LockBuffer( const Rc& Buffer) { if (!Buffer->canRelocate()) return; auto chunk = m_device->AllocCsChunk(DxvkCsChunkFlag::SingleUse); chunk->push([cBuffer = Buffer] (DxvkContext* ctx) { ctx->ensureBufferAddress(cBuffer); }); m_device->GetContext()->InjectCsChunk(DxvkCsQueue::HighPriority, std::move(chunk), true); } D3D11VideoDevice::D3D11VideoDevice( D3D11DXGIDevice* pContainer, D3D11Device* pDevice) : m_container(pContainer), m_device(pDevice) { } D3D11VideoDevice::~D3D11VideoDevice() { } ULONG STDMETHODCALLTYPE D3D11VideoDevice::AddRef() { return m_container->AddRef(); } ULONG STDMETHODCALLTYPE D3D11VideoDevice::Release() { return m_container->Release(); } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::QueryInterface( REFIID riid, void** ppvObject) { return m_container->QueryInterface(riid, ppvObject); } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateVideoDecoder( const D3D11_VIDEO_DECODER_DESC* pVideoDesc, const D3D11_VIDEO_DECODER_CONFIG* pConfig, ID3D11VideoDecoder** ppDecoder) { Logger::err("D3D11VideoDevice::CreateVideoDecoder: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateVideoProcessor( ID3D11VideoProcessorEnumerator* pEnum, UINT RateConversionIndex, ID3D11VideoProcessor** ppVideoProcessor) { try { auto enumerator = static_cast(pEnum); *ppVideoProcessor = ref(new D3D11VideoProcessor(m_device, enumerator, RateConversionIndex)); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_FAIL; } } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateAuthenticatedChannel( D3D11_AUTHENTICATED_CHANNEL_TYPE ChannelType, ID3D11AuthenticatedChannel** ppAuthenticatedChannel) { Logger::err("D3D11VideoDevice::CreateAuthenticatedChannel: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateCryptoSession( const GUID* pCryptoType, const GUID* pDecoderProfile, const GUID* pKeyExchangeType, ID3D11CryptoSession** ppCryptoSession) { Logger::err("D3D11VideoDevice::CreateCryptoSession: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateVideoDecoderOutputView( ID3D11Resource* pResource, const D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC* pDesc, ID3D11VideoDecoderOutputView** ppVDOVView) { Logger::err("D3D11VideoDevice::CreateVideoDecoderOutputView: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateVideoProcessorInputView( ID3D11Resource* pResource, ID3D11VideoProcessorEnumerator* pEnum, const D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC* pDesc, ID3D11VideoProcessorInputView** ppVPIView) { try { *ppVPIView = ref(new D3D11VideoProcessorInputView(m_device, pResource, *pDesc)); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_FAIL; } } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateVideoProcessorOutputView( ID3D11Resource* pResource, ID3D11VideoProcessorEnumerator* pEnum, const D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC* pDesc, ID3D11VideoProcessorOutputView** ppVPOView) { try { *ppVPOView = ref(new D3D11VideoProcessorOutputView(m_device, pResource, *pDesc)); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_FAIL; } } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateVideoProcessorEnumerator( const D3D11_VIDEO_PROCESSOR_CONTENT_DESC* pDesc, ID3D11VideoProcessorEnumerator** ppEnum) { try { *ppEnum = ref(new D3D11VideoProcessorEnumerator(m_device, *pDesc)); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_FAIL; } } UINT STDMETHODCALLTYPE D3D11VideoDevice::GetVideoDecoderProfileCount() { Logger::err("D3D11VideoDevice::GetVideoDecoderProfileCount: Stub"); return 0; } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::GetVideoDecoderProfile( UINT Index, GUID* pDecoderProfile) { Logger::err("D3D11VideoDevice::GetVideoDecoderProfile: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CheckVideoDecoderFormat( const GUID* pDecoderProfile, DXGI_FORMAT Format, BOOL* pSupported) { Logger::err("D3D11VideoDevice::CheckVideoDecoderFormat: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::GetVideoDecoderConfigCount( const D3D11_VIDEO_DECODER_DESC* pDesc, UINT* pCount) { Logger::err("D3D11VideoDevice::GetVideoDecoderConfigCount: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::GetVideoDecoderConfig( const D3D11_VIDEO_DECODER_DESC* pDesc, UINT Index, D3D11_VIDEO_DECODER_CONFIG* pConfig) { Logger::err("D3D11VideoDevice::GetVideoDecoderConfig: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::GetContentProtectionCaps( const GUID* pCryptoType, const GUID* pDecoderProfile, D3D11_VIDEO_CONTENT_PROTECTION_CAPS* pCaps) { Logger::err("D3D11VideoDevice::GetContentProtectionCaps: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CheckCryptoKeyExchange( const GUID* pCryptoType, const GUID* pDecoderProfile, UINT Index, GUID* pKeyExchangeType) { Logger::err("D3D11VideoDevice::CheckCryptoKeyExchange: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::SetPrivateData( REFGUID Name, UINT DataSize, const void* pData) { return m_container->SetPrivateData(Name, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D11VideoDevice::SetPrivateDataInterface( REFGUID Name, const IUnknown* pData) { return m_container->SetPrivateDataInterface(Name, pData); } D3D11ReflexDevice::D3D11ReflexDevice( D3D11DXGIDevice* pContainer, D3D11Device* pDevice) : m_container(pContainer), m_device(pDevice) { auto dxvkDevice = pDevice->GetDXVKDevice(); m_reflexEnabled = dxvkDevice->features().nvLowLatency2 && dxvkDevice->config().latencySleep == Tristate::Auto; } D3D11ReflexDevice::~D3D11ReflexDevice() { } ULONG STDMETHODCALLTYPE D3D11ReflexDevice::AddRef() { return m_container->AddRef(); } ULONG STDMETHODCALLTYPE D3D11ReflexDevice::Release() { return m_container->Release(); } HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::QueryInterface( REFIID riid, void** ppvObject) { return m_container->QueryInterface(riid, ppvObject); } BOOL STDMETHODCALLTYPE D3D11ReflexDevice::SupportsLowLatency() { return m_reflexEnabled; } HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::LatencySleep() { if (!m_reflexEnabled) return DXGI_ERROR_INVALID_CALL; // Don't keep object locked while sleeping Rc tracker; { std::lock_guard lock(m_mutex); tracker = m_tracker; } if (tracker) tracker->latencySleep(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::SetLatencySleepMode( BOOL LowLatencyEnable, BOOL LowLatencyBoost, UINT32 MinIntervalUs) { if (!m_reflexEnabled) return DXGI_ERROR_INVALID_CALL; std::lock_guard lock(m_mutex); if (m_tracker) { m_tracker->setLatencySleepMode( LowLatencyEnable, LowLatencyBoost, MinIntervalUs); } // Write back in case we have no swapchain yet m_enableLowLatency = LowLatencyEnable; m_enableBoost = LowLatencyBoost; m_minIntervalUs = MinIntervalUs; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::SetLatencyMarker( UINT64 FrameId, UINT32 MarkerType) { if (!m_reflexEnabled) return DXGI_ERROR_INVALID_CALL; std::lock_guard lock(m_mutex); if (m_tracker) { auto marker = VkLatencyMarkerNV(MarkerType); m_tracker->setLatencyMarker(FrameId, marker); if (marker == VK_LATENCY_MARKER_RENDERSUBMIT_START_NV) { m_device->GetContext()->InjectCs(DxvkCsQueue::Ordered, [ cTracker = m_tracker, cFrameId = FrameId ] (DxvkContext* ctx) { uint64_t frameId = cTracker->frameIdFromAppFrameId(cFrameId); if (frameId) ctx->beginLatencyTracking(cTracker, frameId); }); } else if (marker == VK_LATENCY_MARKER_RENDERSUBMIT_END_NV) { m_device->GetContext()->InjectCs(DxvkCsQueue::Ordered, [ cTracker = m_tracker ] (DxvkContext* ctx) { ctx->endLatencyTracking(cTracker); }); } } return S_OK; } HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::GetLatencyInfo( D3D_LOW_LATENCY_RESULTS* pLowLatencyResults) { constexpr static size_t FrameCount = 64; if (!pLowLatencyResults) return E_INVALIDARG; for (size_t i = 0; i < FrameCount; i++) pLowLatencyResults->frameReports[i] = D3D_LOW_LATENCY_FRAME_REPORT(); if (!m_reflexEnabled) return DXGI_ERROR_INVALID_CALL; std::lock_guard lock(m_mutex); if (!m_tracker) return S_OK; // Apparently we have to report all 64 frames, or nothing std::array reports = { }; uint32_t reportCount = m_tracker->getFrameReports(FrameCount, reports.data()); if (reportCount < FrameCount) return S_OK; for (uint32_t i = 0; i < FrameCount; i++) { auto& src = reports[i]; auto& dst = pLowLatencyResults->frameReports[i]; dst.frameID = src.report.presentID; dst.inputSampleTime = src.report.inputSampleTimeUs; dst.simStartTime = src.report.simStartTimeUs; dst.simEndTime = src.report.simEndTimeUs; dst.renderSubmitStartTime = src.report.renderSubmitStartTimeUs; dst.renderSubmitEndTime = src.report.renderSubmitEndTimeUs; dst.presentStartTime = src.report.presentStartTimeUs; dst.presentEndTime = src.report.presentEndTimeUs; dst.driverStartTime = src.report.driverStartTimeUs; dst.driverEndTime = src.report.driverEndTimeUs; dst.osRenderQueueStartTime = src.report.osRenderQueueStartTimeUs; dst.osRenderQueueEndTime = src.report.osRenderQueueEndTimeUs; dst.gpuRenderStartTime = src.report.gpuRenderStartTimeUs; dst.gpuRenderEndTime = src.report.gpuRenderEndTimeUs; dst.gpuActiveRenderTimeUs = src.gpuActiveTimeUs; dst.gpuFrameTimeUs = 0; if (i) { dst.gpuFrameTimeUs = reports[i - 0].report.gpuRenderEndTimeUs - reports[i - 1].report.gpuRenderEndTimeUs; } } return S_OK; } void D3D11ReflexDevice::RegisterLatencyTracker( Rc Tracker) { std::lock_guard lock(m_mutex); if (m_tracker) return; if ((m_tracker = dynamic_cast(Tracker.ptr()))) m_tracker->setLatencySleepMode(m_enableLowLatency, m_enableBoost, m_minIntervalUs); } void D3D11ReflexDevice::UnregisterLatencyTracker( Rc Tracker) { std::lock_guard lock(m_mutex); if (m_tracker == Tracker) m_tracker = nullptr; } DXGIVkSwapChainFactory::DXGIVkSwapChainFactory( D3D11DXGIDevice* pContainer, D3D11Device* pDevice) : m_container(pContainer), m_device(pDevice) { } ULONG STDMETHODCALLTYPE DXGIVkSwapChainFactory::AddRef() { return m_device->AddRef(); } ULONG STDMETHODCALLTYPE DXGIVkSwapChainFactory::Release() { return m_device->Release(); } HRESULT STDMETHODCALLTYPE DXGIVkSwapChainFactory::QueryInterface( REFIID riid, void** ppvObject) { return m_device->QueryInterface(riid, ppvObject); } HRESULT STDMETHODCALLTYPE DXGIVkSwapChainFactory::CreateSwapChain( IDXGIVkSurfaceFactory* pSurfaceFactory, const DXGI_SWAP_CHAIN_DESC1* pDesc, IDXGIVkSwapChain** ppSwapChain) { InitReturnPtr(ppSwapChain); try { auto vki = m_device->GetDXVKDevice()->adapter()->vki(); Com presenter = new D3D11SwapChain( m_container, m_device, pSurfaceFactory, pDesc); *ppSwapChain = presenter.ref(); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_FAIL; } } DXGIDXVKDevice::DXGIDXVKDevice(D3D11DXGIDevice* pContainer) : m_container(pContainer), m_apiVersion(11) { } ULONG STDMETHODCALLTYPE DXGIDXVKDevice::AddRef() { return m_container->AddRef(); } ULONG STDMETHODCALLTYPE DXGIDXVKDevice::Release() { return m_container->Release(); } HRESULT STDMETHODCALLTYPE DXGIDXVKDevice::QueryInterface( REFIID riid, void** ppvObject) { return m_container->QueryInterface(riid, ppvObject); } void STDMETHODCALLTYPE DXGIDXVKDevice::SetAPIVersion( UINT Version) { m_apiVersion = Version; } UINT STDMETHODCALLTYPE DXGIDXVKDevice::GetAPIVersion() { return m_apiVersion; } D3D11DXGIDevice::D3D11DXGIDevice( IDXGIAdapter* pAdapter, ID3D12Device* pD3D12Device, ID3D12CommandQueue* pD3D12Queue, Rc pDxvkInstance, Rc pDxvkAdapter, Rc pDxvkDevice, D3D_FEATURE_LEVEL FeatureLevel, UINT FeatureFlags) : m_dxgiAdapter (pAdapter), m_dxvkInstance (pDxvkInstance), m_dxvkAdapter (pDxvkAdapter), m_dxvkDevice (pDxvkDevice), m_d3d11Device (this, FeatureLevel, FeatureFlags), m_d3d11DeviceExt(this, &m_d3d11Device), m_d3d11Interop (this, &m_d3d11Device), m_d3d11Video (this, &m_d3d11Device), m_d3d11Reflex (this, &m_d3d11Device), m_d3d11on12 (this, &m_d3d11Device, pD3D12Device, pD3D12Queue), m_metaDevice (this), m_dxvkFactory (this, &m_d3d11Device) { } D3D11DXGIDevice::~D3D11DXGIDevice() { } HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(IDXGIObject) || riid == __uuidof(IDXGIDevice) || riid == __uuidof(IDXGIDevice1) || riid == __uuidof(IDXGIDevice2) || riid == __uuidof(IDXGIDevice3) || riid == __uuidof(IDXGIDevice4)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(IDXGIVkInteropDevice) || riid == __uuidof(IDXGIVkInteropDevice1)) { *ppvObject = ref(&m_d3d11Interop); return S_OK; } if (riid == __uuidof(ID3D10Device) || riid == __uuidof(ID3D10Device1)) { *ppvObject = ref(m_d3d11Device.GetD3D10Interface()); return S_OK; } if (riid == __uuidof(ID3D11Device) || riid == __uuidof(ID3D11Device1) || riid == __uuidof(ID3D11Device2) || riid == __uuidof(ID3D11Device3) || riid == __uuidof(ID3D11Device4) || riid == __uuidof(ID3D11Device5)) { *ppvObject = ref(&m_d3d11Device); return S_OK; } if (riid == __uuidof(ID3D11VkExtDevice) || riid == __uuidof(ID3D11VkExtDevice1)) { *ppvObject = ref(&m_d3d11DeviceExt); return S_OK; } if (riid == __uuidof(IDXGIDXVKDevice)) { *ppvObject = ref(&m_metaDevice); return S_OK; } if (riid == __uuidof(IDXGIVkSwapChainFactory)) { *ppvObject = ref(&m_dxvkFactory); return S_OK; } if (riid == __uuidof(ID3D11VideoDevice)) { *ppvObject = ref(&m_d3d11Video); return S_OK; } if (riid == __uuidof(ID3DLowLatencyDevice)) { *ppvObject = ref(&m_d3d11Reflex); return S_OK; } if (m_d3d11on12.Is11on12Device()) { if (riid == __uuidof(ID3D11On12Device) || riid == __uuidof(ID3D11On12Device1_DXVK)) { *ppvObject = ref(&m_d3d11on12); return S_OK; } } if (riid == __uuidof(ID3D10Multithread)) { Com context; m_d3d11Device.GetImmediateContext(&context); return context->QueryInterface(riid, ppvObject); } if (riid == __uuidof(ID3D11Debug)) return E_NOINTERFACE; // Undocumented interfaces that are queried by some games if (riid == GUID{0xd56e2a4c,0x5127,0x8437,{0x65,0x8a,0x98,0xc5,0xbb,0x78,0x94,0x98}}) return E_NOINTERFACE; if (logQueryInterfaceError(__uuidof(IDXGIDXVKDevice), riid)) { Logger::warn("D3D11DXGIDevice::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::GetParent( REFIID riid, void** ppParent) { return m_dxgiAdapter->QueryInterface(riid, ppParent); } HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::CreateSurface( const DXGI_SURFACE_DESC* pDesc, UINT NumSurfaces, DXGI_USAGE Usage, const DXGI_SHARED_RESOURCE* pSharedResource, IDXGISurface** ppSurface) { if (!pDesc || (NumSurfaces && !ppSurface)) return E_INVALIDARG; D3D11_TEXTURE2D_DESC desc; desc.Width = pDesc->Width; desc.Height = pDesc->Height; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = pDesc->Format; desc.SampleDesc = pDesc->SampleDesc; desc.BindFlags = 0; desc.MiscFlags = 0; // Handle bind flags if (Usage & DXGI_USAGE_RENDER_TARGET_OUTPUT) desc.BindFlags |= D3D11_BIND_RENDER_TARGET; if (Usage & DXGI_USAGE_SHADER_INPUT) desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; if (Usage & DXGI_USAGE_UNORDERED_ACCESS) desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; // Handle CPU access flags switch (Usage & DXGI_CPU_ACCESS_FIELD) { case DXGI_CPU_ACCESS_NONE: desc.Usage = D3D11_USAGE_DEFAULT; desc.CPUAccessFlags = 0; break; case DXGI_CPU_ACCESS_DYNAMIC: desc.Usage = D3D11_USAGE_DYNAMIC; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; break; case DXGI_CPU_ACCESS_READ_WRITE: case DXGI_CPU_ACCESS_SCRATCH: desc.Usage = D3D11_USAGE_STAGING; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; break; default: return E_INVALIDARG; } // Restrictions and limitations of CreateSurface are not // well-documented, so we'll be a lenient on validation. HRESULT hr = m_d3d11Device.CreateTexture2D(&desc, nullptr, nullptr); if (FAILED(hr)) return hr; // We don't support shared resources if (NumSurfaces && pSharedResource) Logger::err("D3D11: CreateSurface: Shared surfaces not supported"); // Try to create the given number of surfaces uint32_t surfacesCreated = 0; hr = S_OK; for (uint32_t i = 0; i < NumSurfaces; i++) { Com texture; hr = m_d3d11Device.CreateTexture2D(&desc, nullptr, &texture); if (SUCCEEDED(hr)) { hr = texture->QueryInterface(__uuidof(IDXGISurface), reinterpret_cast(&ppSurface[i])); surfacesCreated = i + 1; } if (FAILED(hr)) break; } // Don't leak surfaces if we failed to create one if (FAILED(hr)) { for (uint32_t i = 0; i < surfacesCreated; i++) ppSurface[i]->Release(); } return hr; } HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::GetAdapter( IDXGIAdapter** pAdapter) { if (pAdapter == nullptr) return DXGI_ERROR_INVALID_CALL; *pAdapter = m_dxgiAdapter.ref(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::GetGPUThreadPriority( INT* pPriority) { *pPriority = 0; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::QueryResourceResidency( IUnknown* const* ppResources, DXGI_RESIDENCY* pResidencyStatus, UINT NumResources) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::err("D3D11DXGIDevice::QueryResourceResidency: Stub"); if (!ppResources || !pResidencyStatus) return E_INVALIDARG; for (uint32_t i = 0; i < NumResources; i++) pResidencyStatus[i] = DXGI_RESIDENCY_FULLY_RESIDENT; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::SetGPUThreadPriority( INT Priority) { if (Priority < -7 || Priority > 7) return E_INVALIDARG; Logger::err("DXGI: SetGPUThreadPriority: Ignoring"); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::GetMaximumFrameLatency( UINT* pMaxLatency) { if (!pMaxLatency) return DXGI_ERROR_INVALID_CALL; *pMaxLatency = m_frameLatency; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::SetMaximumFrameLatency( UINT MaxLatency) { if (MaxLatency == 0) MaxLatency = DefaultFrameLatency; if (MaxLatency > DXGI_MAX_SWAP_CHAIN_BUFFERS) return DXGI_ERROR_INVALID_CALL; m_frameLatency = MaxLatency; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::OfferResources( UINT NumResources, IDXGIResource* const* ppResources, DXGI_OFFER_RESOURCE_PRIORITY Priority) { return OfferResources1(NumResources, ppResources, Priority, 0); } HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::OfferResources1( UINT NumResources, IDXGIResource* const* ppResources, DXGI_OFFER_RESOURCE_PRIORITY Priority, UINT Flags) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D11DXGIDevice::OfferResources1: Stub"); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::ReclaimResources( UINT NumResources, IDXGIResource* const* ppResources, BOOL* pDiscarded) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D11DXGIDevice::ReclaimResources: Stub"); if (pDiscarded) *pDiscarded = false; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::ReclaimResources1( UINT NumResources, IDXGIResource* const* ppResources, DXGI_RECLAIM_RESOURCE_RESULTS* pResults) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D11DXGIDevice::ReclaimResources1: Stub"); if (pResults) { for (uint32_t i = 0; i < NumResources; i++) pResults[i] = DXGI_RECLAIM_RESOURCE_RESULT_OK; } return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::EnqueueSetEvent(HANDLE hEvent) { auto immediateContext = m_d3d11Device.GetContext(); immediateContext->Flush1(D3D11_CONTEXT_TYPE_ALL, hEvent); return S_OK; } void STDMETHODCALLTYPE D3D11DXGIDevice::Trim() { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D11DXGIDevice::Trim: Stub"); } Rc STDMETHODCALLTYPE D3D11DXGIDevice::GetDXVKDevice() { return m_dxvkDevice; } } dxvk-2.6.1/src/d3d11/d3d11_device.h000066400000000000000000001053211477473124000164160ustar00rootroot00000000000000#pragma once #include #include #include "../dxbc/dxbc_options.h" #include "../dxgi/dxgi_object.h" #include "../dxgi/dxgi_interfaces.h" #include "../dxvk/dxvk_cs.h" #include "../dxvk/dxvk_latency_reflex.h" #include "../d3d10/d3d10_device.h" #include "../util/com/com_private_data.h" #include "d3d11_cmdlist.h" #include "d3d11_cuda.h" #include "d3d11_features.h" #include "d3d11_initializer.h" #include "d3d11_interfaces.h" #include "d3d11_interop.h" #include "d3d11_on_12.h" #include "d3d11_options.h" #include "d3d11_shader.h" #include "d3d11_state.h" #include "d3d11_util.h" namespace dxvk { class DxgiAdapter; class D3D11Buffer; class D3D11CommonShader; class D3D11CommonTexture; class D3D11Counter; class D3D11DXGIDevice; class D3D11ImmediateContext; class D3D11Predicate; class D3D11Query; class D3D11Texture1D; class D3D11Texture2D; class D3D11Texture3D; /** * \brief D3D11 device implementation * * Implements the ID3D11Device interfaces * as part of a \ref D3D11DeviceContainer. */ class D3D11Device final : public ID3D11Device5 { /// Maximum number of resource init commands per command buffer constexpr static uint64_t InitCommandThreshold = 50; public: D3D11Device( D3D11DXGIDevice* pContainer, D3D_FEATURE_LEVEL FeatureLevel, UINT FeatureFlags); ~D3D11Device(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE CreateBuffer( const D3D11_BUFFER_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Buffer** ppBuffer); HRESULT STDMETHODCALLTYPE CreateTexture1D( const D3D11_TEXTURE1D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture1D** ppTexture1D); HRESULT STDMETHODCALLTYPE CreateTexture2D( const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D); HRESULT STDMETHODCALLTYPE CreateTexture2D1( const D3D11_TEXTURE2D_DESC1* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D1** ppTexture2D); HRESULT STDMETHODCALLTYPE CreateTexture2DBase( const D3D11_TEXTURE2D_DESC1* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D1** ppTexture2D); HRESULT STDMETHODCALLTYPE CreateTexture3D( const D3D11_TEXTURE3D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture3D** ppTexture3D); HRESULT STDMETHODCALLTYPE CreateTexture3D1( const D3D11_TEXTURE3D_DESC1* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture3D1** ppTexture3D); HRESULT STDMETHODCALLTYPE CreateTexture3DBase( const D3D11_TEXTURE3D_DESC1* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture3D1** ppTexture3D); HRESULT STDMETHODCALLTYPE CreateShaderResourceView( ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRView); HRESULT STDMETHODCALLTYPE CreateShaderResourceView1( ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc, ID3D11ShaderResourceView1** ppSRView); HRESULT STDMETHODCALLTYPE CreateShaderResourceViewBase( ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc, ID3D11ShaderResourceView1** ppSRView); HRESULT STDMETHODCALLTYPE CreateUnorderedAccessView( ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, ID3D11UnorderedAccessView** ppUAView); HRESULT STDMETHODCALLTYPE CreateUnorderedAccessView1( ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc, ID3D11UnorderedAccessView1** ppUAView); HRESULT STDMETHODCALLTYPE CreateUnorderedAccessViewBase( ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc, ID3D11UnorderedAccessView1** ppUAView); HRESULT STDMETHODCALLTYPE CreateRenderTargetView( ID3D11Resource* pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView); HRESULT STDMETHODCALLTYPE CreateRenderTargetView1( ID3D11Resource* pResource, const D3D11_RENDER_TARGET_VIEW_DESC1* pDesc, ID3D11RenderTargetView1** ppRTView); HRESULT STDMETHODCALLTYPE CreateRenderTargetViewBase( ID3D11Resource* pResource, const D3D11_RENDER_TARGET_VIEW_DESC1* pDesc, ID3D11RenderTargetView1** ppRTView); HRESULT STDMETHODCALLTYPE CreateDepthStencilView( ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView); HRESULT STDMETHODCALLTYPE CreateInputLayout( const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs, UINT NumElements, const void* pShaderBytecodeWithInputSignature, SIZE_T BytecodeLength, ID3D11InputLayout** ppInputLayout); HRESULT STDMETHODCALLTYPE CreateVertexShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11VertexShader** ppVertexShader); HRESULT STDMETHODCALLTYPE CreateGeometryShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11GeometryShader** ppGeometryShader); HRESULT STDMETHODCALLTYPE CreateGeometryShaderWithStreamOutput( const void* pShaderBytecode, SIZE_T BytecodeLength, const D3D11_SO_DECLARATION_ENTRY* pSODeclaration, UINT NumEntries, const UINT* pBufferStrides, UINT NumStrides, UINT RasterizedStream, ID3D11ClassLinkage* pClassLinkage, ID3D11GeometryShader** ppGeometryShader); HRESULT STDMETHODCALLTYPE CreatePixelShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11PixelShader** ppPixelShader); HRESULT STDMETHODCALLTYPE CreateHullShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11HullShader** ppHullShader); HRESULT STDMETHODCALLTYPE CreateDomainShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11DomainShader** ppDomainShader); HRESULT STDMETHODCALLTYPE CreateComputeShader( const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11ComputeShader** ppComputeShader); HRESULT STDMETHODCALLTYPE CreateClassLinkage( ID3D11ClassLinkage** ppLinkage); HRESULT STDMETHODCALLTYPE CreateBlendState( const D3D11_BLEND_DESC* pBlendStateDesc, ID3D11BlendState** ppBlendState); HRESULT STDMETHODCALLTYPE CreateBlendState1( const D3D11_BLEND_DESC1* pBlendStateDesc, ID3D11BlendState1** ppBlendState); HRESULT STDMETHODCALLTYPE CreateDepthStencilState( const D3D11_DEPTH_STENCIL_DESC* pDepthStencilDesc, ID3D11DepthStencilState** ppDepthStencilState); HRESULT STDMETHODCALLTYPE CreateRasterizerState( const D3D11_RASTERIZER_DESC* pRasterizerDesc, ID3D11RasterizerState** ppRasterizerState); HRESULT STDMETHODCALLTYPE CreateRasterizerState1( const D3D11_RASTERIZER_DESC1* pRasterizerDesc, ID3D11RasterizerState1** ppRasterizerState); HRESULT STDMETHODCALLTYPE CreateRasterizerState2( const D3D11_RASTERIZER_DESC2* pRasterizerDesc, ID3D11RasterizerState2** ppRasterizerState); HRESULT STDMETHODCALLTYPE CreateSamplerState( const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState); HRESULT STDMETHODCALLTYPE CreateQuery( const D3D11_QUERY_DESC* pQueryDesc, ID3D11Query** ppQuery); HRESULT STDMETHODCALLTYPE CreateQuery1( const D3D11_QUERY_DESC1* pQueryDesc, ID3D11Query1** ppQuery); HRESULT STDMETHODCALLTYPE CreateQueryBase( const D3D11_QUERY_DESC1* pQueryDesc, ID3D11Query1** ppQuery); HRESULT STDMETHODCALLTYPE CreatePredicate( const D3D11_QUERY_DESC* pPredicateDesc, ID3D11Predicate** ppPredicate); HRESULT STDMETHODCALLTYPE CreateCounter( const D3D11_COUNTER_DESC* pCounterDesc, ID3D11Counter** ppCounter); HRESULT STDMETHODCALLTYPE CreateDeferredContext( UINT ContextFlags, ID3D11DeviceContext** ppDeferredContext); HRESULT STDMETHODCALLTYPE CreateDeferredContext1( UINT ContextFlags, ID3D11DeviceContext1** ppDeferredContext); HRESULT STDMETHODCALLTYPE CreateDeferredContext2( UINT ContextFlags, ID3D11DeviceContext2** ppDeferredContext); HRESULT STDMETHODCALLTYPE CreateDeferredContext3( UINT ContextFlags, ID3D11DeviceContext3** ppDeferredContext); HRESULT STDMETHODCALLTYPE CreateDeviceContextState( UINT Flags, const D3D_FEATURE_LEVEL* pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, REFIID EmulatedInterface, D3D_FEATURE_LEVEL* pChosenFeatureLevel, ID3DDeviceContextState** ppContextState); HRESULT STDMETHODCALLTYPE CreateFence( UINT64 InitialValue, D3D11_FENCE_FLAG Flags, REFIID riid, void** ppFence); void STDMETHODCALLTYPE ReadFromSubresource( void* pDstData, UINT DstRowPitch, UINT DstDepthPitch, ID3D11Resource* pSrcResource, UINT SrcSubresource, const D3D11_BOX* pSrcBox); void STDMETHODCALLTYPE WriteToSubresource( ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch); HRESULT STDMETHODCALLTYPE OpenSharedResource( HANDLE hResource, REFIID ReturnedInterface, void** ppResource); HRESULT STDMETHODCALLTYPE OpenSharedResource1( HANDLE hResource, REFIID returnedInterface, void** ppResource); HRESULT STDMETHODCALLTYPE OpenSharedResourceByName( LPCWSTR lpName, DWORD dwDesiredAccess, REFIID returnedInterface, void** ppResource); HRESULT STDMETHODCALLTYPE OpenSharedFence( HANDLE hFence, REFIID ReturnedInterface, void** ppFence); HRESULT STDMETHODCALLTYPE CheckFormatSupport( DXGI_FORMAT Format, UINT* pFormatSupport); HRESULT STDMETHODCALLTYPE CheckMultisampleQualityLevels( DXGI_FORMAT Format, UINT SampleCount, UINT* pNumQualityLevels); HRESULT STDMETHODCALLTYPE CheckMultisampleQualityLevels1( DXGI_FORMAT Format, UINT SampleCount, UINT Flags, UINT* pNumQualityLevels); void STDMETHODCALLTYPE CheckCounterInfo( D3D11_COUNTER_INFO* pCounterInfo); HRESULT STDMETHODCALLTYPE CheckCounter( const D3D11_COUNTER_DESC* pDesc, D3D11_COUNTER_TYPE* pType, UINT* pActiveCounters, LPSTR szName, UINT* pNameLength, LPSTR szUnits, UINT* pUnitsLength, LPSTR szDescription, UINT* pDescriptionLength); HRESULT STDMETHODCALLTYPE CheckFeatureSupport( D3D11_FEATURE Feature, void* pFeatureSupportData, UINT FeatureSupportDataSize); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID Name, UINT *pDataSize, void *pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID Name, UINT DataSize, const void *pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID Name, const IUnknown *pUnknown); D3D_FEATURE_LEVEL STDMETHODCALLTYPE GetFeatureLevel(); UINT STDMETHODCALLTYPE GetCreationFlags(); HRESULT STDMETHODCALLTYPE GetDeviceRemovedReason(); void STDMETHODCALLTYPE GetImmediateContext( ID3D11DeviceContext** ppImmediateContext); void STDMETHODCALLTYPE GetImmediateContext1( ID3D11DeviceContext1** ppImmediateContext); void STDMETHODCALLTYPE GetImmediateContext2( ID3D11DeviceContext2** ppImmediateContext); void STDMETHODCALLTYPE GetImmediateContext3( ID3D11DeviceContext3** ppImmediateContext); HRESULT STDMETHODCALLTYPE SetExceptionMode(UINT RaiseFlags); UINT STDMETHODCALLTYPE GetExceptionMode(); void STDMETHODCALLTYPE GetResourceTiling( ID3D11Resource* pTiledResource, UINT* pNumTilesForEntireResource, D3D11_PACKED_MIP_DESC* pPackedMipDesc, D3D11_TILE_SHAPE* pStandardTileShapeForNonPackedMips, UINT* pNumSubresourceTilings, UINT FirstSubresourceTilingToGet, D3D11_SUBRESOURCE_TILING* pSubresourceTilingsForNonPackedMips); HRESULT STDMETHODCALLTYPE RegisterDeviceRemovedEvent( HANDLE hEvent, DWORD* pdwCookie); void STDMETHODCALLTYPE UnregisterDeviceRemoved( DWORD dwCookie); Rc GetDXVKDevice() { return m_dxvkDevice; } void FlushInitCommands() { m_initializer->FlushCsChunk(); } void NotifyContextFlush() { m_initializer->NotifyContextFlush(); } void InitShaderIcb( D3D11CommonShader* pShader, size_t IcbSize, const void* pIcbData) { return m_initializer->InitShaderIcb(pShader, IcbSize, pIcbData); } VkPipelineStageFlags GetEnabledShaderStages() const { return m_dxvkDevice->getShaderPipelineStages(); } DXGI_VK_FORMAT_INFO LookupFormat( DXGI_FORMAT Format, DXGI_VK_FORMAT_MODE Mode) const; DXGI_VK_FORMAT_INFO LookupPackedFormat( DXGI_FORMAT Format, DXGI_VK_FORMAT_MODE Mode) const; DXGI_VK_FORMAT_FAMILY LookupFamily( DXGI_FORMAT Format, DXGI_VK_FORMAT_MODE Mode) const; DxvkCsChunkRef AllocCsChunk(DxvkCsChunkFlags flags) { DxvkCsChunk* chunk = m_csChunkPool.allocChunk(flags); return DxvkCsChunkRef(chunk, &m_csChunkPool); } const D3D11Options* GetOptions() const { return &m_d3d11Options; } D3D10Device* GetD3D10Interface() const { return m_d3d10Device; } D3D11ImmediateContext* GetContext() const { return m_context.ptr(); } bool Is11on12Device() const; static D3D_FEATURE_LEVEL GetMaxFeatureLevel( const Rc& Instance, const Rc& Adapter); static DxvkDeviceFeatures GetDeviceFeatures( const Rc& Adapter); DxvkBarrierControlFlags GetOptionsBarrierControlFlags() { DxvkBarrierControlFlags barrierControl = 0u; if (m_d3d11Options.relaxedBarriers) barrierControl.set(DxvkBarrierControl::ComputeAllowWriteOnlyOverlap); if (m_d3d11Options.relaxedBarriers || m_d3d11Options.relaxedGraphicsBarriers) barrierControl.set(DxvkBarrierControl::GraphicsAllowReadWriteOverlap); return barrierControl; } private: D3D11DXGIDevice* m_container; D3D_FEATURE_LEVEL m_featureLevel; UINT m_featureFlags; const Rc m_dxvkDevice; const Rc m_dxvkAdapter; const DXGIVkFormatTable m_d3d11Formats; const D3D11Options m_d3d11Options; const DxbcOptions m_dxbcOptions; DxvkCsChunkPool m_csChunkPool; D3D11Initializer* m_initializer = nullptr; D3D10Device* m_d3d10Device = nullptr; Com m_context; D3D11StateObjectSet m_bsStateObjects; D3D11StateObjectSet m_dsStateObjects; D3D11StateObjectSet m_rsStateObjects; D3D11StateObjectSet m_samplerObjects; D3D11ShaderModuleSet m_shaderModules; D3D_FEATURE_LEVEL m_maxFeatureLevel; D3D11DeviceFeatures m_deviceFeatures; HRESULT CreateShaderModule( D3D11CommonShader* pShaderModule, DxvkShaderKey ShaderKey, const void* pShaderBytecode, size_t BytecodeLength, ID3D11ClassLinkage* pClassLinkage, const DxbcModuleInfo* pModuleInfo); HRESULT GetFormatSupportFlags( DXGI_FORMAT Format, UINT* pFlags1, UINT* pFlags2) const; BOOL GetImageTypeSupport( VkFormat Format, VkImageType Type, VkImageCreateFlags Flags) const; template HRESULT OpenSharedResourceGeneric( HANDLE hResource, REFIID ReturnedInterface, void** ppResource); uint32_t GetViewPlaneIndex( ID3D11Resource* pResource, DXGI_FORMAT ViewFormat); template void CopySubresourceData( Void* pData, UINT RowPitch, UINT DepthPitch, D3D11CommonTexture* pTexture, UINT Subresource, const D3D11_BOX* pBox); }; /** * \brief Extended D3D11 device */ class D3D11DeviceExt : public ID3D11VkExtDevice1 { public: D3D11DeviceExt( D3D11DXGIDevice* pContainer, D3D11Device* pDevice); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); BOOL STDMETHODCALLTYPE GetExtensionSupport( D3D11_VK_EXTENSION Extension); bool STDMETHODCALLTYPE GetCudaTextureObjectNVX( uint32_t srvDriverHandle, uint32_t samplerDriverHandle, uint32_t* pCudaTextureHandle); bool STDMETHODCALLTYPE CreateCubinComputeShaderWithNameNVX( const void* pCubin, uint32_t size, uint32_t blockX, uint32_t blockY, uint32_t blockZ, const char* pShaderName, IUnknown** phShader); bool STDMETHODCALLTYPE GetResourceHandleGPUVirtualAddressAndSizeNVX( void* hObject, uint64_t* gpuVAStart, uint64_t* gpuVASize); bool STDMETHODCALLTYPE CreateUnorderedAccessViewAndGetDriverHandleNVX( ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, ID3D11UnorderedAccessView** ppUAV, uint32_t* pDriverHandle); bool STDMETHODCALLTYPE CreateShaderResourceViewAndGetDriverHandleNVX( ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRV, uint32_t* pDriverHandle); bool STDMETHODCALLTYPE CreateSamplerStateAndGetDriverHandleNVX( const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState, uint32_t* pDriverHandle); private: D3D11DXGIDevice* m_container; D3D11Device* m_device; void AddSamplerAndHandleNVX( ID3D11SamplerState* pSampler, uint32_t Handle); ID3D11SamplerState* HandleToSamplerNVX( uint32_t Handle); void AddSrvAndHandleNVX( ID3D11ShaderResourceView* pSrv, uint32_t Handle); ID3D11ShaderResourceView* HandleToSrvNVX( uint32_t Handle); bool LockImage( const Rc& Image, VkImageUsageFlags Usage); void LockBuffer( const Rc& Buffer); dxvk::mutex m_mapLock; std::unordered_map m_samplerHandleToPtr; std::unordered_map m_srvHandleToPtr; }; /** * \brief D3D11 video device */ class D3D11VideoDevice : public ID3D11VideoDevice { public: D3D11VideoDevice( D3D11DXGIDevice* pContainer, D3D11Device* pDevice); ~D3D11VideoDevice(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE CreateVideoDecoder( const D3D11_VIDEO_DECODER_DESC* pVideoDesc, const D3D11_VIDEO_DECODER_CONFIG* pConfig, ID3D11VideoDecoder** ppDecoder); HRESULT STDMETHODCALLTYPE CreateVideoProcessor( ID3D11VideoProcessorEnumerator* pEnum, UINT RateConversionIndex, ID3D11VideoProcessor** ppVideoProcessor); HRESULT STDMETHODCALLTYPE CreateAuthenticatedChannel( D3D11_AUTHENTICATED_CHANNEL_TYPE ChannelType, ID3D11AuthenticatedChannel** ppAuthenticatedChannel); HRESULT STDMETHODCALLTYPE CreateCryptoSession( const GUID* pCryptoType, const GUID* pDecoderProfile, const GUID* pKeyExchangeType, ID3D11CryptoSession** ppCryptoSession); HRESULT STDMETHODCALLTYPE CreateVideoDecoderOutputView( ID3D11Resource* pResource, const D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC* pDesc, ID3D11VideoDecoderOutputView** ppVDOVView); HRESULT STDMETHODCALLTYPE CreateVideoProcessorInputView( ID3D11Resource* pResource, ID3D11VideoProcessorEnumerator* pEnum, const D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC* pDesc, ID3D11VideoProcessorInputView** ppVPIView); HRESULT STDMETHODCALLTYPE CreateVideoProcessorOutputView( ID3D11Resource* pResource, ID3D11VideoProcessorEnumerator* pEnum, const D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC* pDesc, ID3D11VideoProcessorOutputView** ppVPOView); HRESULT STDMETHODCALLTYPE CreateVideoProcessorEnumerator( const D3D11_VIDEO_PROCESSOR_CONTENT_DESC* pDesc, ID3D11VideoProcessorEnumerator** ppEnum); UINT STDMETHODCALLTYPE GetVideoDecoderProfileCount(); HRESULT STDMETHODCALLTYPE GetVideoDecoderProfile( UINT Index, GUID* pDecoderProfile); HRESULT STDMETHODCALLTYPE CheckVideoDecoderFormat( const GUID* pDecoderProfile, DXGI_FORMAT Format, BOOL* pSupported); HRESULT STDMETHODCALLTYPE GetVideoDecoderConfigCount( const D3D11_VIDEO_DECODER_DESC* pDesc, UINT* pCount); HRESULT STDMETHODCALLTYPE GetVideoDecoderConfig( const D3D11_VIDEO_DECODER_DESC* pDesc, UINT Index, D3D11_VIDEO_DECODER_CONFIG* pConfig); HRESULT STDMETHODCALLTYPE GetContentProtectionCaps( const GUID* pCryptoType, const GUID* pDecoderProfile, D3D11_VIDEO_CONTENT_PROTECTION_CAPS* pCaps); HRESULT STDMETHODCALLTYPE CheckCryptoKeyExchange( const GUID* pCryptoType, const GUID* pDecoderProfile, UINT Index, GUID* pKeyExchangeType); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID Name, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID Name, const IUnknown* pData); private: D3D11DXGIDevice* m_container; D3D11Device* m_device; }; /** * \brief Nvidia Reflex interop */ class D3D11ReflexDevice : public ID3DLowLatencyDevice { public: D3D11ReflexDevice( D3D11DXGIDevice* pContainer, D3D11Device* pDevice); ~D3D11ReflexDevice(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); BOOL STDMETHODCALLTYPE SupportsLowLatency(); HRESULT STDMETHODCALLTYPE LatencySleep(); HRESULT STDMETHODCALLTYPE SetLatencySleepMode( BOOL LowLatencyEnable, BOOL LowLatencyBoost, UINT32 MinIntervalUs); HRESULT STDMETHODCALLTYPE SetLatencyMarker( UINT64 FrameId, UINT32 MarkerType); HRESULT STDMETHODCALLTYPE GetLatencyInfo( D3D_LOW_LATENCY_RESULTS* pLowLatencyResults); void RegisterLatencyTracker( Rc Tracker); void UnregisterLatencyTracker( Rc Tracker); private: D3D11DXGIDevice* m_container; D3D11Device* m_device; bool m_reflexEnabled = false; dxvk::mutex m_mutex; bool m_enableLowLatency = false; bool m_enableBoost = false; uint64_t m_minIntervalUs = 0u; Rc m_tracker; }; /** * \brief DXVK swap chain factory */ class DXGIVkSwapChainFactory : public IDXGIVkSwapChainFactory { public: DXGIVkSwapChainFactory( D3D11DXGIDevice* pContainer, D3D11Device* pDevice); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE CreateSwapChain( IDXGIVkSurfaceFactory* pSurfaceFactory, const DXGI_SWAP_CHAIN_DESC1* pDesc, IDXGIVkSwapChain** ppSwapChain); private: D3D11DXGIDevice* m_container; D3D11Device* m_device; }; /** * \brief D3D11 device metadata shenanigans */ class DXGIDXVKDevice : public IDXGIDXVKDevice { public: DXGIDXVKDevice(D3D11DXGIDevice* pContainer); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); void STDMETHODCALLTYPE SetAPIVersion( UINT Version); UINT STDMETHODCALLTYPE GetAPIVersion(); private: D3D11DXGIDevice* m_container; UINT m_apiVersion; }; /** * \brief D3D11 device container * * Stores all the objects that contribute to the D3D11 * device implementation, including the DXGI device. */ class D3D11DXGIDevice : public DxgiObject { constexpr static uint32_t DefaultFrameLatency = 3; public: D3D11DXGIDevice( IDXGIAdapter* pAdapter, ID3D12Device* pD3D12Device, ID3D12CommandQueue* pD3D12Queue, Rc pDxvkInstance, Rc pDxvkAdapter, Rc pDxvkDevice, D3D_FEATURE_LEVEL FeatureLevel, UINT FeatureFlags); ~D3D11DXGIDevice(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE GetParent( REFIID riid, void** ppParent); HRESULT STDMETHODCALLTYPE CreateSurface( const DXGI_SURFACE_DESC* pDesc, UINT NumSurfaces, DXGI_USAGE Usage, const DXGI_SHARED_RESOURCE* pSharedResource, IDXGISurface** ppSurface) final; HRESULT STDMETHODCALLTYPE GetAdapter( IDXGIAdapter** pAdapter) final; HRESULT STDMETHODCALLTYPE GetGPUThreadPriority( INT* pPriority) final; HRESULT STDMETHODCALLTYPE QueryResourceResidency( IUnknown* const* ppResources, DXGI_RESIDENCY* pResidencyStatus, UINT NumResources) final; HRESULT STDMETHODCALLTYPE SetGPUThreadPriority( INT Priority) final; HRESULT STDMETHODCALLTYPE GetMaximumFrameLatency( UINT* pMaxLatency) final; HRESULT STDMETHODCALLTYPE SetMaximumFrameLatency( UINT MaxLatency) final; HRESULT STDMETHODCALLTYPE OfferResources( UINT NumResources, IDXGIResource* const* ppResources, DXGI_OFFER_RESOURCE_PRIORITY Priority) final; HRESULT STDMETHODCALLTYPE OfferResources1( UINT NumResources, IDXGIResource* const* ppResources, DXGI_OFFER_RESOURCE_PRIORITY Priority, UINT Flags) final; HRESULT STDMETHODCALLTYPE ReclaimResources( UINT NumResources, IDXGIResource* const* ppResources, BOOL* pDiscarded) final; HRESULT STDMETHODCALLTYPE ReclaimResources1( UINT NumResources, IDXGIResource* const* ppResources, DXGI_RECLAIM_RESOURCE_RESULTS* pResults) final; HRESULT STDMETHODCALLTYPE EnqueueSetEvent( HANDLE hEvent) final; void STDMETHODCALLTYPE Trim() final; Rc STDMETHODCALLTYPE GetDXVKDevice(); BOOL Is11on12Device() const { return m_d3d11on12.Is11on12Device(); } private: Com m_dxgiAdapter; Rc m_dxvkInstance; Rc m_dxvkAdapter; Rc m_dxvkDevice; D3D11Device m_d3d11Device; D3D11DeviceExt m_d3d11DeviceExt; D3D11VkInterop m_d3d11Interop; D3D11VideoDevice m_d3d11Video; D3D11ReflexDevice m_d3d11Reflex; D3D11on12Device m_d3d11on12; DXGIDXVKDevice m_metaDevice; DXGIVkSwapChainFactory m_dxvkFactory; uint32_t m_frameLatency = DefaultFrameLatency; }; } dxvk-2.6.1/src/d3d11/d3d11_device_child.h000066400000000000000000000062401477473124000175610ustar00rootroot00000000000000#pragma once #include "d3d11_include.h" #include "../util/com/com_private_data.h" namespace dxvk { class D3D11Device; template class D3D11DeviceObject : public Base { public: D3D11DeviceObject(D3D11Device* pDevice) : m_parent(pDevice) { } HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID guid, UINT* pDataSize, void* pData) final { return m_privateData.getData( guid, pDataSize, pData); } HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID guid, UINT DataSize, const void* pData) final { // WKPDID_D3DDebugObjectName, can't use directly due to MSVC link errors if (guid == GUID{0x429b8c22,0x9188,0x4b0c,0x87,0x42,0xac,0xb0,0xbf,0x85,0xc2,0x00}) SetDebugName(static_cast(pData)); return m_privateData.setData( guid, DataSize, pData); } HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID guid, const IUnknown* pUnknown) final { return m_privateData.setInterface( guid, pUnknown); } void STDMETHODCALLTYPE GetDevice( ID3D11Device** ppDevice) final { *ppDevice = ref(GetParentInterface()); } virtual void STDMETHODCALLTYPE SetDebugName(const char* pName) { // No-op by default } protected: ID3D11Device* GetParentInterface() const { // We don't know the definition of ID3D11Device // here, because D3D11Device includes this file. return reinterpret_cast(m_parent); } D3D11Device* const m_parent; private: ComPrivateData m_privateData; }; template class D3D11DeviceChild : public D3D11DeviceObject> { public: D3D11DeviceChild(D3D11Device* pDevice) : D3D11DeviceObject>(pDevice) { } ULONG STDMETHODCALLTYPE AddRef() { uint32_t refCount = this->m_refCount++; if (unlikely(!refCount)) { this->AddRefPrivate(); this->GetParentInterface()->AddRef(); } return refCount + 1; } ULONG STDMETHODCALLTYPE Release() { uint32_t refCount = --this->m_refCount; if (unlikely(!refCount)) { auto* parent = this->GetParentInterface(); this->ReleasePrivate(); parent->Release(); } return refCount; } }; template class D3D11StateObject : public D3D11DeviceObject { public: D3D11StateObject(D3D11Device* pDevice) : D3D11DeviceObject(pDevice) { } ULONG STDMETHODCALLTYPE AddRef() { uint32_t refCount = this->m_refCount++; if (unlikely(!refCount)) this->GetParentInterface()->AddRef(); return refCount + 1; } ULONG STDMETHODCALLTYPE Release() { uint32_t refCount = --this->m_refCount; if (unlikely(!refCount)) this->GetParentInterface()->Release(); return refCount; } private: std::atomic m_refCount = { 0u }; }; } dxvk-2.6.1/src/d3d11/d3d11_enums.cpp000066400000000000000000000007441477473124000166440ustar00rootroot00000000000000#include "d3d11_enums.h" std::ostream& operator << (std::ostream& os, D3D_FEATURE_LEVEL e) { switch (e) { ENUM_NAME(D3D_FEATURE_LEVEL_9_1); ENUM_NAME(D3D_FEATURE_LEVEL_9_2); ENUM_NAME(D3D_FEATURE_LEVEL_9_3); ENUM_NAME(D3D_FEATURE_LEVEL_10_0); ENUM_NAME(D3D_FEATURE_LEVEL_10_1); ENUM_NAME(D3D_FEATURE_LEVEL_11_0); ENUM_NAME(D3D_FEATURE_LEVEL_11_1); ENUM_NAME(D3D_FEATURE_LEVEL_12_0); ENUM_NAME(D3D_FEATURE_LEVEL_12_1); ENUM_DEFAULT(e); } } dxvk-2.6.1/src/d3d11/d3d11_enums.h000066400000000000000000000002001477473124000162740ustar00rootroot00000000000000#pragma once #include #include "d3d11_include.h" std::ostream& operator << (std::ostream& os, D3D_FEATURE_LEVEL e);dxvk-2.6.1/src/d3d11/d3d11_features.cpp000066400000000000000000000405731477473124000173370ustar00rootroot00000000000000#include #include "d3d11_features.h" namespace dxvk { D3D11DeviceFeatures::D3D11DeviceFeatures() { } D3D11DeviceFeatures::D3D11DeviceFeatures( const Rc& Instance, const Rc& Adapter, const D3D11Options& Options, D3D_FEATURE_LEVEL FeatureLevel) : m_features (Adapter->features()), m_properties (Adapter->devicePropertiesExt()) { // Assume no TBDR. DXVK does not optimize for TBDR architectures // anyway, and D3D11 does not really provide meaningful support. m_architectureInfo.TileBasedDeferredRenderer = FALSE; // D3D9 options. We unconditionally support all of these. m_d3d9Options.FullNonPow2TextureSupport = TRUE; m_d3d9Options1.FullNonPow2TextureSupported = TRUE; m_d3d9Options1.DepthAsTextureWithLessEqualComparisonFilterSupported = TRUE; m_d3d9Options1.SimpleInstancingSupported = TRUE; m_d3d9Options1.TextureCubeFaceRenderTargetWithNonCubeDepthStencilSupported = TRUE; m_d3d9Shadow.SupportsDepthAsTextureWithLessEqualComparisonFilter = TRUE; m_d3d9SimpleInstancing.SimpleInstancingSupported = TRUE; // D3D10 options. We unconditionally support compute shaders. m_d3d10Options.ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x = TRUE; // D3D11.1 options. All of these are required for Feature Level 11_1. auto sharedResourceTier = DetermineSharedResourceTier(Adapter, FeatureLevel); bool hasDoublePrecisionSupport = m_features.core.features.shaderFloat64 && m_features.core.features.shaderInt64; m_d3d11Options.DiscardAPIsSeenByDriver = TRUE; m_d3d11Options.FlagsForUpdateAndCopySeenByDriver = TRUE; m_d3d11Options.ClearView = TRUE; m_d3d11Options.CopyWithOverlap = TRUE; m_d3d11Options.ConstantBufferPartialUpdate = TRUE; m_d3d11Options.ConstantBufferOffsetting = TRUE; m_d3d11Options.MapNoOverwriteOnDynamicConstantBuffer = TRUE; m_d3d11Options.MapNoOverwriteOnDynamicBufferSRV = TRUE; m_d3d11Options.ExtendedResourceSharing = sharedResourceTier > D3D11_SHARED_RESOURCE_TIER_0; if (FeatureLevel >= D3D_FEATURE_LEVEL_10_0) { m_d3d11Options.OutputMergerLogicOp = m_features.core.features.logicOp; m_d3d11Options.MultisampleRTVWithForcedSampleCountOne = TRUE; // Not really } if (FeatureLevel >= D3D_FEATURE_LEVEL_11_0) { m_d3d11Options.UAVOnlyRenderingForcedSampleCount = TRUE; m_d3d11Options.SAD4ShaderInstructions = TRUE; m_d3d11Options.ExtendedDoublesShaderInstructions = hasDoublePrecisionSupport; } // D3D11.2 options. auto tiledResourcesTier = DetermineTiledResourcesTier(FeatureLevel); m_d3d11Options1.TiledResourcesTier = tiledResourcesTier; m_d3d11Options1.MinMaxFiltering = tiledResourcesTier >= D3D11_TILED_RESOURCES_TIER_2; m_d3d11Options1.ClearViewAlsoSupportsDepthOnlyFormats = TRUE; if (FeatureLevel >= D3D_FEATURE_LEVEL_11_0) m_d3d11Options1.MapOnDefaultBuffers = TRUE; // D3D11.3 options m_d3d11Options2.TypedUAVLoadAdditionalFormats = DetermineUavExtendedTypedLoadSupport(Adapter, FeatureLevel); m_d3d11Options2.ConservativeRasterizationTier = DetermineConservativeRasterizationTier(FeatureLevel); m_d3d11Options2.TiledResourcesTier = tiledResourcesTier; m_d3d11Options2.StandardSwizzle = FALSE; m_d3d11Options2.UnifiedMemoryArchitecture = FALSE; if (FeatureLevel >= D3D_FEATURE_LEVEL_11_0) m_d3d11Options2.MapOnDefaultTextures = TRUE; if (FeatureLevel >= D3D_FEATURE_LEVEL_11_1) { m_d3d11Options2.ROVsSupported = m_features.extFragmentShaderInterlock.fragmentShaderPixelInterlock; m_d3d11Options2.PSSpecifiedStencilRefSupported = m_features.extShaderStencilExport; } // More D3D11.3 options if (FeatureLevel >= D3D_FEATURE_LEVEL_11_0) { m_d3d11Options3.VPAndRTArrayIndexFromAnyShaderFeedingRasterizer = m_features.vk12.shaderOutputViewportIndex && m_features.vk12.shaderOutputLayer; } // D3D11.4 options m_d3d11Options4.ExtendedNV12SharedTextureSupported = sharedResourceTier > D3D11_SHARED_RESOURCE_TIER_0; // More D3D11.4 options m_d3d11Options5.SharedResourceTier = sharedResourceTier; // Double-precision support if (FeatureLevel >= D3D_FEATURE_LEVEL_11_0) m_doubles.DoublePrecisionFloatShaderOps = hasDoublePrecisionSupport; // These numbers are not accurate, but we have no real way to query these m_gpuVirtualAddress.MaxGPUVirtualAddressBitsPerResource = 32; m_gpuVirtualAddress.MaxGPUVirtualAddressBitsPerProcess = 40; // Marker support only depends on the debug utils extension m_marker.Profile = static_cast(Instance->extensions().extDebugUtils); // DXVK will keep all shaders in memory once created, and all Vulkan // drivers that we know of that can run DXVK have an on-disk cache. m_shaderCache.SupportFlags = D3D11_SHADER_CACHE_SUPPORT_AUTOMATIC_INPROC_CACHE | D3D11_SHADER_CACHE_SUPPORT_AUTOMATIC_DISK_CACHE; // DXVK does not support min precision m_shaderMinPrecision.PixelShaderMinPrecision = 0; m_shaderMinPrecision.AllOtherShaderStagesMinPrecision = 0; // Report native support for command lists by default. Deferred context // usage can be beneficial for us as ExecuteCommandList has low overhead, // and we avoid having to deal with known UpdateSubresource bugs this way. m_threading.DriverConcurrentCreates = TRUE; m_threading.DriverCommandLists = Options.exposeDriverCommandLists; } D3D11DeviceFeatures::~D3D11DeviceFeatures() { } HRESULT D3D11DeviceFeatures::GetFeatureData( D3D11_FEATURE Feature, UINT FeatureDataSize, void* pFeatureData) const { switch (Feature) { case D3D11_FEATURE_ARCHITECTURE_INFO: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_architectureInfo); case D3D11_FEATURE_D3D9_OPTIONS: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d9Options); case D3D11_FEATURE_D3D9_OPTIONS1: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d9Options1); case D3D11_FEATURE_D3D9_SHADOW_SUPPORT: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d9Shadow); case D3D11_FEATURE_D3D9_SIMPLE_INSTANCING_SUPPORT: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d9SimpleInstancing); case D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d10Options); case D3D11_FEATURE_D3D11_OPTIONS: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d11Options); case D3D11_FEATURE_D3D11_OPTIONS1: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d11Options1); case D3D11_FEATURE_D3D11_OPTIONS2: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d11Options2); case D3D11_FEATURE_D3D11_OPTIONS3: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d11Options3); case D3D11_FEATURE_D3D11_OPTIONS4: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d11Options4); case D3D11_FEATURE_D3D11_OPTIONS5: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d11Options5); case D3D11_FEATURE_DOUBLES: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_doubles); case D3D11_FEATURE_GPU_VIRTUAL_ADDRESS_SUPPORT: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_gpuVirtualAddress); case D3D11_FEATURE_MARKER_SUPPORT: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_marker); case D3D11_FEATURE_SHADER_CACHE: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_shaderCache); case D3D11_FEATURE_SHADER_MIN_PRECISION_SUPPORT: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_shaderMinPrecision); case D3D11_FEATURE_THREADING: return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_threading); default: Logger::err(str::format("D3D11: Unknown feature: ", Feature)); return E_INVALIDARG; } } D3D_FEATURE_LEVEL D3D11DeviceFeatures::GetMaxFeatureLevel( const Rc& Instance, const Rc& Adapter) { D3D11Options options(Instance->config()); D3D11DeviceFeatures features(Instance, Adapter, options, D3D_FEATURE_LEVEL_12_1); return features.GetMaxFeatureLevel(); } D3D11_CONSERVATIVE_RASTERIZATION_TIER D3D11DeviceFeatures::DetermineConservativeRasterizationTier( D3D_FEATURE_LEVEL FeatureLevel) { if (FeatureLevel < D3D_FEATURE_LEVEL_11_1 || !m_features.extConservativeRasterization) return D3D11_CONSERVATIVE_RASTERIZATION_NOT_SUPPORTED; // We don't really have a way to query uncertainty regions, // so just check degenerate triangle behaviour if (!m_properties.extConservativeRasterization.degenerateTrianglesRasterized) return D3D11_CONSERVATIVE_RASTERIZATION_TIER_1; // Inner coverage is required for Tier 3 support if (!m_properties.extConservativeRasterization.fullyCoveredFragmentShaderInputVariable) return D3D11_CONSERVATIVE_RASTERIZATION_TIER_2; return D3D11_CONSERVATIVE_RASTERIZATION_TIER_3; } D3D11_SHARED_RESOURCE_TIER D3D11DeviceFeatures::DetermineSharedResourceTier( const Rc& Adapter, D3D_FEATURE_LEVEL FeatureLevel) { static std::atomic s_errorShown = { false }; // Lie about supporting Tier 1 since that's the // minimum required tier for Feature Level 11_1 if (!Adapter->features().khrExternalMemoryWin32) { if (!s_errorShown.exchange(true)) Logger::warn("D3D11DeviceFeatures: External memory features not supported"); return D3D11_SHARED_RESOURCE_TIER_1; } // Check support for extended formats. Ignore multi-plane // formats here since driver support varies too much. std::array requiredFormats = {{ VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R32G32B32A32_UINT, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_R16G16B16A16_UINT, VK_FORMAT_R16G16B16A16_SNORM, VK_FORMAT_R16G16B16A16_SINT, VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_FORMAT_A2B10G10R10_UINT_PACK32, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_R8G8B8A8_SNORM, VK_FORMAT_R8G8B8A8_SINT, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32_UINT, VK_FORMAT_R32_SINT, VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16_UNORM, VK_FORMAT_R16_UINT, VK_FORMAT_R16_SNORM, VK_FORMAT_R16_SINT, VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UINT, VK_FORMAT_R8_SNORM, VK_FORMAT_R8_SINT, }}; bool allKmtHandlesSupported = true; bool allNtHandlesSupported = true; for (auto f : requiredFormats) { allKmtHandlesSupported &= CheckFormatSharingSupport(Adapter, f, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT); allNtHandlesSupported &= CheckFormatSharingSupport(Adapter, f, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT); } // Again, lie about at least tier 1 support if (!allKmtHandlesSupported) { if (!s_errorShown.exchange(true)) Logger::warn("D3D11DeviceFeatures: Some formats not supported for resource sharing"); return D3D11_SHARED_RESOURCE_TIER_1; } // Tier 2 requires all the above formats to be shareable // with NT handles in order to support D3D12 interop if (!allNtHandlesSupported) return D3D11_SHARED_RESOURCE_TIER_1; // Tier 3 additionally requires R11G11B10 to be // shareable with D3D12 if (!CheckFormatSharingSupport(Adapter, VK_FORMAT_B10G11R11_UFLOAT_PACK32, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT)) return D3D11_SHARED_RESOURCE_TIER_2; return D3D11_SHARED_RESOURCE_TIER_3; } D3D11_TILED_RESOURCES_TIER D3D11DeviceFeatures::DetermineTiledResourcesTier( D3D_FEATURE_LEVEL FeatureLevel) { if (FeatureLevel < D3D_FEATURE_LEVEL_11_0 || !m_features.core.features.sparseBinding || !m_features.core.features.sparseResidencyBuffer || !m_features.core.features.sparseResidencyImage2D || !m_features.core.features.sparseResidencyAliased || !m_properties.core.properties.sparseProperties.residencyStandard2DBlockShape) return D3D11_TILED_RESOURCES_NOT_SUPPORTED; if (FeatureLevel < D3D_FEATURE_LEVEL_11_1 || !m_features.core.features.shaderResourceResidency || !m_features.core.features.shaderResourceMinLod || !m_features.vk12.samplerFilterMinmax || !m_properties.vk12.filterMinmaxSingleComponentFormats || !m_properties.core.properties.sparseProperties.residencyNonResidentStrict || m_properties.core.properties.sparseProperties.residencyAlignedMipSize) return D3D11_TILED_RESOURCES_TIER_1; if (!m_features.core.features.sparseResidencyImage3D || !m_properties.core.properties.sparseProperties.residencyStandard3DBlockShape) return D3D11_TILED_RESOURCES_TIER_2; return D3D11_TILED_RESOURCES_TIER_3; } BOOL D3D11DeviceFeatures::DetermineUavExtendedTypedLoadSupport( const Rc& Adapter, D3D_FEATURE_LEVEL FeatureLevel) { static const std::array s_formats = {{ VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32_UINT, VK_FORMAT_R32_SINT, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R32G32B32A32_UINT, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R16G16B16A16_UINT, VK_FORMAT_R16G16B16A16_SINT, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_R8G8B8A8_SINT, VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16_UINT, VK_FORMAT_R16_SINT, VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UINT, VK_FORMAT_R8_SINT, }}; if (FeatureLevel < D3D_FEATURE_LEVEL_11_0) return FALSE; for (auto f : s_formats) { DxvkFormatFeatures features = Adapter->getFormatFeatures(f); VkFormatFeatureFlags2 imgFeatures = features.optimal | features.linear; if (!(imgFeatures & VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT)) return FALSE; } return TRUE; } BOOL D3D11DeviceFeatures::CheckFormatSharingSupport( const Rc& Adapter, VkFormat Format, VkExternalMemoryHandleTypeFlagBits HandleType) { DxvkFormatQuery query = { }; query.format = Format; query.type = VK_IMAGE_TYPE_2D; query.tiling = VK_IMAGE_TILING_OPTIMAL; query.usage = VK_IMAGE_USAGE_SAMPLED_BIT; query.handleType = HandleType; constexpr VkExternalMemoryFeatureFlags featureMask = VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT; auto limits = Adapter->getFormatLimits(query); return limits && (limits->externalFeatures & featureMask); } D3D_FEATURE_LEVEL D3D11DeviceFeatures::GetMaxFeatureLevel() const { // Check Feature Level 11_0 features if (!m_features.core.features.drawIndirectFirstInstance || !m_features.core.features.fragmentStoresAndAtomics || !m_features.core.features.multiDrawIndirect || !m_features.core.features.tessellationShader) return D3D_FEATURE_LEVEL_10_1; // Check Feature Level 11_1 features if (!m_d3d11Options.OutputMergerLogicOp || !m_features.core.features.vertexPipelineStoresAndAtomics) return D3D_FEATURE_LEVEL_11_0; // Check Feature Level 12_0 features if (m_d3d11Options2.TiledResourcesTier < D3D11_TILED_RESOURCES_TIER_2 || !m_d3d11Options2.TypedUAVLoadAdditionalFormats) return D3D_FEATURE_LEVEL_11_1; // Check Feature Level 12_1 features if (!m_d3d11Options2.ConservativeRasterizationTier || !m_d3d11Options2.ROVsSupported) return D3D_FEATURE_LEVEL_12_0; return D3D_FEATURE_LEVEL_12_1; } } dxvk-2.6.1/src/d3d11/d3d11_features.h000066400000000000000000000107101477473124000167720ustar00rootroot00000000000000#pragma once #include "d3d11_include.h" #include "d3d11_options.h" #include "../dxvk/dxvk_adapter.h" #include "../dxvk/dxvk_instance.h" namespace dxvk { /** * \brief Device features * * Stores D3D device feature structs. */ class D3D11DeviceFeatures { public: D3D11DeviceFeatures(); D3D11DeviceFeatures( const Rc& Instance, const Rc& Adapter, const D3D11Options& Options, D3D_FEATURE_LEVEL FeatureLevel); ~D3D11DeviceFeatures(); /** * \brief Retrieves feature support data * * \param [in] Feature D3D feature to query * \param [in] FeatureDataSize Data size, in bytes * \param [out] pFeatureData Data * \returns Status of the operation */ HRESULT GetFeatureData( D3D11_FEATURE Feature, UINT FeatureDataSize, void* pFeatureData) const; /** * \brief Queries tiled resources tier * \returns Tiled resources tier */ D3D11_TILED_RESOURCES_TIER GetTiledResourcesTier() const { return m_d3d11Options2.TiledResourcesTier; } /** * \brief Queries conservative rasterization tier * \returns Conservative rasterization tier */ D3D11_CONSERVATIVE_RASTERIZATION_TIER GetConservativeRasterizationTier() const { return m_d3d11Options2.ConservativeRasterizationTier; } /** * \brief Tests maximum supported feature level * * \param [in] Instance DXVK instance * \param [in] Adapter DXVK adapter * \returns Highest supported feature level */ static D3D_FEATURE_LEVEL GetMaxFeatureLevel( const Rc& Instance, const Rc& Adapter); private: DxvkDeviceFeatures m_features; DxvkDeviceInfo m_properties; D3D11_FEATURE_DATA_ARCHITECTURE_INFO m_architectureInfo = { }; D3D11_FEATURE_DATA_D3D9_OPTIONS m_d3d9Options = { }; D3D11_FEATURE_DATA_D3D9_OPTIONS1 m_d3d9Options1 = { }; D3D11_FEATURE_DATA_D3D9_SHADOW_SUPPORT m_d3d9Shadow = { }; D3D11_FEATURE_DATA_D3D9_SIMPLE_INSTANCING_SUPPORT m_d3d9SimpleInstancing = { }; D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS m_d3d10Options = { }; D3D11_FEATURE_DATA_D3D11_OPTIONS m_d3d11Options = { }; D3D11_FEATURE_DATA_D3D11_OPTIONS1 m_d3d11Options1 = { }; D3D11_FEATURE_DATA_D3D11_OPTIONS2 m_d3d11Options2 = { }; D3D11_FEATURE_DATA_D3D11_OPTIONS3 m_d3d11Options3 = { }; D3D11_FEATURE_DATA_D3D11_OPTIONS4 m_d3d11Options4 = { }; D3D11_FEATURE_DATA_D3D11_OPTIONS5 m_d3d11Options5 = { }; D3D11_FEATURE_DATA_DOUBLES m_doubles = { }; D3D11_FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT m_gpuVirtualAddress = { }; D3D11_FEATURE_DATA_MARKER_SUPPORT m_marker = { }; D3D11_FEATURE_DATA_SHADER_CACHE m_shaderCache = { }; D3D11_FEATURE_DATA_SHADER_MIN_PRECISION_SUPPORT m_shaderMinPrecision = { }; D3D11_FEATURE_DATA_THREADING m_threading = { }; template static HRESULT GetTypedFeatureData(UINT Size, void* pDstData, const T* pSrcData) { if (Size != sizeof(T)) return E_INVALIDARG; *(reinterpret_cast(pDstData)) = *pSrcData; return S_OK; } D3D11_CONSERVATIVE_RASTERIZATION_TIER DetermineConservativeRasterizationTier( D3D_FEATURE_LEVEL FeatureLevel); D3D11_SHARED_RESOURCE_TIER DetermineSharedResourceTier( const Rc& Adapter, D3D_FEATURE_LEVEL FeatureLevel); D3D11_TILED_RESOURCES_TIER DetermineTiledResourcesTier( D3D_FEATURE_LEVEL FeatureLevel); BOOL DetermineUavExtendedTypedLoadSupport( const Rc& Adapter, D3D_FEATURE_LEVEL FeatureLevel); BOOL CheckFormatSharingSupport( const Rc& Adapter, VkFormat Format, VkExternalMemoryHandleTypeFlagBits HandleType); D3D_FEATURE_LEVEL GetMaxFeatureLevel() const; }; }dxvk-2.6.1/src/d3d11/d3d11_fence.cpp000066400000000000000000000055271477473124000166010ustar00rootroot00000000000000#include "d3d11_fence.h" #include "d3d11_device.h" #include "../util/util_win32_compat.h" namespace dxvk { D3D11Fence::D3D11Fence( D3D11Device* pDevice, UINT64 InitialValue, D3D11_FENCE_FLAG Flags, HANDLE hFence) : D3D11DeviceChild(pDevice) { DxvkFenceCreateInfo fenceInfo; fenceInfo.initialValue = InitialValue; m_flags = Flags; if (Flags & D3D11_FENCE_FLAG_SHARED) { fenceInfo.sharedType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D11_FENCE_BIT; if (hFence == nullptr) hFence = INVALID_HANDLE_VALUE; fenceInfo.sharedHandle = hFence; } if (Flags & ~D3D11_FENCE_FLAG_SHARED) Logger::err(str::format("Fence flags 0x", std::hex, Flags, " not supported")); m_fence = pDevice->GetDXVKDevice()->createFence(fenceInfo); } D3D11Fence::~D3D11Fence() { } HRESULT STDMETHODCALLTYPE D3D11Fence::QueryInterface( REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11Fence)) { *ppvObject = ref(this); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11Fence), riid)) { Logger::warn("D3D11Fence: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE D3D11Fence::CreateSharedHandle( const SECURITY_ATTRIBUTES* pAttributes, DWORD dwAccess, LPCWSTR lpName, HANDLE* pHandle) { if (!(m_flags & D3D11_FENCE_FLAG_SHARED)) return E_INVALIDARG; if (pAttributes) Logger::warn(str::format("CreateSharedHandle: attributes ", pAttributes, " not handled")); if (dwAccess) Logger::warn(str::format("CreateSharedHandle: access ", dwAccess, " not handled")); if (lpName) Logger::warn(str::format("CreateSharedHandle: name ", dxvk::str::fromws(lpName), " not handled")); HANDLE sharedHandle = m_fence->sharedHandle(); if (sharedHandle == INVALID_HANDLE_VALUE) return E_INVALIDARG; *pHandle = sharedHandle; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11Fence::SetEventOnCompletion( UINT64 Value, HANDLE hEvent) { if (hEvent) { m_fence->enqueueWait(Value, [hEvent] { SetEvent(hEvent); }); } else { m_fence->wait(Value); } return S_OK; } UINT64 STDMETHODCALLTYPE D3D11Fence::GetCompletedValue() { // TODO in the case of rewinds, the stored value may be higher. // For shared fences, calling vkGetSemaphoreCounterValue here could alleviate the issue. return m_fence->getValue(); } } dxvk-2.6.1/src/d3d11/d3d11_fence.h000066400000000000000000000021241477473124000162340ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_fence.h" #include "../dxvk/dxvk_gpu_query.h" #include "d3d11_device_child.h" namespace dxvk { class D3D11Fence : public D3D11DeviceChild { public: D3D11Fence( D3D11Device* pDevice, UINT64 InitialValue, D3D11_FENCE_FLAG Flags, HANDLE hFence); ~D3D11Fence(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE CreateSharedHandle( const SECURITY_ATTRIBUTES* pAttributes, DWORD dwAccess, LPCWSTR lpName, HANDLE* pHandle); HRESULT STDMETHODCALLTYPE SetEventOnCompletion( UINT64 Value, HANDLE hEvent); UINT64 STDMETHODCALLTYPE GetCompletedValue(); Rc GetFence() const { return m_fence; } private: Rc m_fence; D3D11_FENCE_FLAG m_flags; }; } dxvk-2.6.1/src/d3d11/d3d11_gdi.cpp000066400000000000000000000133151477473124000162560ustar00rootroot00000000000000#include "d3d11_context.h" #include "d3d11_device.h" #include "d3d11_gdi.h" #include "../util/util_gdi.h" #include "../util/util_win32_compat.h" namespace dxvk { D3D11GDISurface::D3D11GDISurface( ID3D11Resource* pResource, UINT Subresource) : m_resource (pResource), m_subresource (Subresource), m_readback (nullptr), m_hdc (nullptr), m_hbitmap (nullptr), m_acquired (false) { // Allocate memory for the bitmap auto tex = GetCommonTexture(m_resource)->Desc(); m_data.resize(tex->Width * tex->Height); // Create GDI DC D3DKMT_CREATEDCFROMMEMORY desc; desc.pMemory = m_data.data(); desc.Format = D3DFMT_A8R8G8B8; desc.Width = tex->Width; desc.Height = tex->Height; desc.Pitch = tex->Width * sizeof(uint32_t); desc.hDeviceDc = CreateCompatibleDC(nullptr); desc.pColorTable = nullptr; desc.hDc = nullptr; desc.hBitmap = nullptr; if (D3DKMTCreateDCFromMemory(&desc)) Logger::err(str::format("D3D11: Failed to create GDI DC")); m_hdc = desc.hDc; m_hbitmap = desc.hBitmap; } D3D11GDISurface::~D3D11GDISurface() { if (m_readback) m_readback->Release(); D3DKMT_DESTROYDCFROMMEMORY desc; desc.hDC = m_hdc; desc.hBitmap = m_hbitmap; D3DKMTDestroyDCFromMemory(&desc); } HRESULT D3D11GDISurface::Acquire(BOOL Discard, HDC* phdc) { if (!phdc) return E_INVALIDARG; *phdc = nullptr; if (m_acquired) return DXGI_ERROR_INVALID_CALL; if (!Discard) { // Create a staging resource that we can map if (!m_readback && FAILED(CreateReadbackResource())) { Logger::err("D3D11: Failed to create GDI readback resource"); return E_FAIL; } // Copy subresource to staging image Com device; Com context; m_resource->GetDevice(&device); device->GetImmediateContext(&context); context->CopySubresourceRegion(m_readback, 0, 0, 0, 0, m_resource, m_subresource, nullptr); // Copy staging image to DC memory auto tex = GetCommonTexture(m_resource)->Desc(); auto rowData = reinterpret_cast(m_data.data()); auto rowLength = sizeof(uint32_t) * tex->Width; D3D11_MAPPED_SUBRESOURCE sr; context->Map(m_readback, 0, D3D11_MAP_READ, 0, &sr); for (uint32_t i = 0; i < tex->Height; i++) { std::memcpy(rowData + rowLength * i, reinterpret_cast(sr.pData) + sr.RowPitch * i, rowLength); } context->Unmap(m_readback, 0); } m_acquired = true; *phdc = m_hdc; return S_OK; } HRESULT D3D11GDISurface::Release(const RECT* pDirtyRect) { if (!m_acquired) return DXGI_ERROR_INVALID_CALL; Com device; Com context; m_resource->GetDevice(&device); device->GetImmediateContext(&context); // Commit changes made to the DC auto tex = GetCommonTexture(m_resource)->Desc(); RECT rect; if (pDirtyRect) { rect.left = std::max(pDirtyRect->left, 0); rect.top = std::max(pDirtyRect->top, 0); rect.right = std::min(pDirtyRect->right, tex->Width); rect.bottom = std::min(pDirtyRect->bottom, tex->Height); } else { rect.left = 0; rect.top = 0; rect.right = tex->Width; rect.bottom = tex->Height; } if (rect.left < rect.right && rect.top < rect.bottom) { D3D11_BOX box; box.left = rect.left; box.top = rect.top; box.front = 0; box.right = rect.right; box.bottom = rect.bottom; box.back = 1; context->UpdateSubresource(m_resource, m_subresource, &box, m_data.data() + rect.left, sizeof(uint32_t) * tex->Width, sizeof(uint32_t) * tex->Width * tex->Height); } m_acquired = false; return S_OK; } HRESULT D3D11GDISurface::CreateReadbackResource() { auto tex = GetCommonTexture(m_resource); Com device; m_resource->GetDevice(&device); D3D11_RESOURCE_DIMENSION dim = { }; m_resource->GetType(&dim); VkImageSubresource sr = tex->GetSubresourceFromIndex( VK_IMAGE_ASPECT_COLOR_BIT, m_subresource); switch (dim) { case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { D3D11_TEXTURE1D_DESC desc; desc.Width = std::max(tex->Desc()->Width >> sr.mipLevel, 1); desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = tex->Desc()->Format; desc.Usage = D3D11_USAGE_STAGING; desc.BindFlags = 0; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.MiscFlags = 0; ID3D11Texture1D* tex1D = nullptr; HRESULT hr = device->CreateTexture1D(&desc, nullptr, &tex1D); m_readback = tex1D; return hr; } break; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { D3D11_TEXTURE2D_DESC desc; desc.Width = std::max(tex->Desc()->Width >> sr.mipLevel, 1); desc.Height = std::max(tex->Desc()->Height >> sr.mipLevel, 1); desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = tex->Desc()->Format; desc.SampleDesc= { 1, 0 }; desc.Usage = D3D11_USAGE_STAGING; desc.BindFlags = 0; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.MiscFlags = 0; ID3D11Texture2D* tex2D = nullptr; HRESULT hr = device->CreateTexture2D(&desc, nullptr, &tex2D); m_readback = tex2D; return hr; } break; default: return E_INVALIDARG; } } } dxvk-2.6.1/src/d3d11/d3d11_gdi.h000066400000000000000000000013041477473124000157160ustar00rootroot00000000000000#pragma once #include #include "d3d11_include.h" namespace dxvk { class D3D11GDISurface { public: D3D11GDISurface( ID3D11Resource* pResource, UINT Subresource); ~D3D11GDISurface(); HRESULT Acquire( BOOL Discard, HDC* phdc); HRESULT Release( const RECT* pDirtyRect); private: ID3D11Resource* m_resource; uint32_t m_subresource; ID3D11Resource* m_readback; HDC m_hdc; HANDLE m_hbitmap; bool m_acquired; std::vector m_data; HRESULT CreateReadbackResource(); }; } dxvk-2.6.1/src/d3d11/d3d11_include.h000066400000000000000000000001061477473124000165750ustar00rootroot00000000000000#pragma once #include "../dxgi/dxgi_include.h" #include dxvk-2.6.1/src/d3d11/d3d11_initializer.cpp000066400000000000000000000320221477473124000200320ustar00rootroot00000000000000#include #include "d3d11_context_imm.h" #include "d3d11_device.h" #include "d3d11_initializer.h" namespace dxvk { D3D11Initializer::D3D11Initializer( D3D11Device* pParent) : m_parent(pParent), m_device(pParent->GetDXVKDevice()), m_stagingBuffer(m_device, StagingBufferSize), m_stagingSignal(new sync::Fence(0)), m_csChunk(m_parent->AllocCsChunk(DxvkCsChunkFlag::SingleUse)) { } D3D11Initializer::~D3D11Initializer() { } void D3D11Initializer::NotifyContextFlush() { std::lock_guard lock(m_mutex); NotifyContextFlushLocked(); } void D3D11Initializer::InitBuffer( D3D11Buffer* pBuffer, const D3D11_SUBRESOURCE_DATA* pInitialData) { if (!(pBuffer->Desc()->MiscFlags & D3D11_RESOURCE_MISC_TILED)) { VkMemoryPropertyFlags memFlags = pBuffer->GetBuffer()->memFlags(); (memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ? InitHostVisibleBuffer(pBuffer, pInitialData) : InitDeviceLocalBuffer(pBuffer, pInitialData); } } void D3D11Initializer::InitTexture( D3D11CommonTexture* pTexture, const D3D11_SUBRESOURCE_DATA* pInitialData) { if (pTexture->Desc()->MiscFlags & D3D11_RESOURCE_MISC_TILED) InitTiledTexture(pTexture); else if (pTexture->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT) InitHostVisibleTexture(pTexture, pInitialData); else InitDeviceLocalTexture(pTexture, pInitialData); SyncSharedTexture(pTexture); } void D3D11Initializer::InitUavCounter( D3D11UnorderedAccessView* pUav) { auto counterView = pUav->GetCounterView(); if (counterView == nullptr) return; std::lock_guard lock(m_mutex); m_transferCommands += 1; EmitCs([ cCounterSlice = DxvkBufferSlice(counterView) ] (DxvkContext* ctx) { const uint32_t zero = 0; ctx->updateBuffer( cCounterSlice.buffer(), cCounterSlice.offset(), sizeof(zero), &zero); }); } void D3D11Initializer::InitShaderIcb( D3D11CommonShader* pShader, size_t IcbSize, const void* pIcbData) { std::lock_guard lock(m_mutex); m_transferCommands += 1; auto icbSlice = pShader->GetIcb(); auto srcSlice = m_stagingBuffer.alloc(icbSlice.length()); std::memcpy(srcSlice.mapPtr(0), pIcbData, IcbSize); if (IcbSize < icbSlice.length()) std::memset(srcSlice.mapPtr(IcbSize), 0, icbSlice.length() - IcbSize); EmitCs([ cIcbSlice = std::move(icbSlice), cSrcSlice = std::move(srcSlice) ] (DxvkContext* ctx) { ctx->copyBuffer(cIcbSlice.buffer(), cIcbSlice.offset(), cSrcSlice.buffer(), cSrcSlice.offset(), cIcbSlice.length()); }); ThrottleAllocationLocked(); } void D3D11Initializer::InitDeviceLocalBuffer( D3D11Buffer* pBuffer, const D3D11_SUBRESOURCE_DATA* pInitialData) { std::lock_guard lock(m_mutex); Rc buffer = pBuffer->GetBuffer(); if (pInitialData != nullptr && pInitialData->pSysMem != nullptr) { auto stagingSlice = m_stagingBuffer.alloc(buffer->info().size); std::memcpy(stagingSlice.mapPtr(0), pInitialData->pSysMem, stagingSlice.length()); m_transferCommands += 1; EmitCs([ cBuffer = buffer, cStagingSlice = std::move(stagingSlice) ] (DxvkContext* ctx) { ctx->uploadBuffer(cBuffer, cStagingSlice.buffer(), cStagingSlice.offset()); }); } else { m_transferCommands += 1; EmitCs([ cBuffer = buffer ] (DxvkContext* ctx) { ctx->initBuffer(cBuffer); }); } ThrottleAllocationLocked(); } void D3D11Initializer::InitHostVisibleBuffer( D3D11Buffer* pBuffer, const D3D11_SUBRESOURCE_DATA* pInitialData) { // If the buffer is mapped, we can write data directly // to the mapped memory region instead of doing it on // the GPU. Same goes for zero-initialization. if (pInitialData && pInitialData->pSysMem) std::memcpy(pBuffer->GetMapPtr(), pInitialData->pSysMem, pBuffer->Desc()->ByteWidth); else std::memset(pBuffer->GetMapPtr(), 0, pBuffer->Desc()->ByteWidth); } void D3D11Initializer::InitDeviceLocalTexture( D3D11CommonTexture* pTexture, const D3D11_SUBRESOURCE_DATA* pInitialData) { std::lock_guard lock(m_mutex); // Image migt be null if this is a staging resource Rc image = pTexture->GetImage(); auto desc = pTexture->Desc(); VkFormat packedFormat = m_parent->LookupPackedFormat(desc->Format, pTexture->GetFormatMode()).Format; auto formatInfo = lookupFormatInfo(packedFormat); if (pInitialData != nullptr && pInitialData->pSysMem != nullptr) { // Compute data size for all subresources and allocate staging buffer memory DxvkBufferSlice stagingSlice; if (pTexture->HasImage()) { VkDeviceSize dataSize = 0u; for (uint32_t mip = 0; mip < image->info().mipLevels; mip++) { dataSize += image->info().numLayers * align(util::computeImageDataSize( packedFormat, image->mipLevelExtent(mip), formatInfo->aspectMask), CACHE_LINE_SIZE); } stagingSlice = m_stagingBuffer.alloc(dataSize); } // Copy initial data for each subresource into the staging buffer, // as well as the mapped per-subresource buffers if available. VkDeviceSize dataOffset = 0u; for (uint32_t mip = 0; mip < desc->MipLevels; mip++) { for (uint32_t layer = 0; layer < desc->ArraySize; layer++) { uint32_t index = D3D11CalcSubresource(mip, layer, desc->MipLevels); VkExtent3D mipLevelExtent = pTexture->MipLevelExtent(mip); if (pTexture->HasImage()) { VkDeviceSize mipSizePerLayer = util::computeImageDataSize( packedFormat, image->mipLevelExtent(mip), formatInfo->aspectMask); m_transferCommands += 1; util::packImageData(stagingSlice.mapPtr(dataOffset), pInitialData[index].pSysMem, pInitialData[index].SysMemPitch, pInitialData[index].SysMemSlicePitch, 0, 0, pTexture->GetVkImageType(), mipLevelExtent, 1, formatInfo, formatInfo->aspectMask); dataOffset += align(mipSizePerLayer, CACHE_LINE_SIZE); } if (pTexture->HasPersistentBuffers()) { util::packImageData(pTexture->GetMapPtr(index, 0), pInitialData[index].pSysMem, pInitialData[index].SysMemPitch, pInitialData[index].SysMemSlicePitch, 0, 0, pTexture->GetVkImageType(), mipLevelExtent, 1, formatInfo, formatInfo->aspectMask); } } } // Upload all subresources of the image in one go if (pTexture->HasImage()) { EmitCs([ cImage = std::move(image), cStagingSlice = std::move(stagingSlice), cFormat = packedFormat ] (DxvkContext* ctx) { ctx->uploadImage(cImage, cStagingSlice.buffer(), cStagingSlice.offset(), CACHE_LINE_SIZE, cFormat); }); } } else { if (pTexture->HasImage()) { m_transferCommands += 1; // While the Microsoft docs state that resource contents are // undefined if no initial data is provided, some applications // expect a resource to be pre-cleared. EmitCs([ cImage = std::move(image) ] (DxvkContext* ctx) { ctx->initImage(cImage, VK_IMAGE_LAYOUT_UNDEFINED); }); } if (pTexture->HasPersistentBuffers()) { for (uint32_t i = 0; i < pTexture->CountSubresources(); i++) { auto layout = pTexture->GetSubresourceLayout(formatInfo->aspectMask, i); std::memset(pTexture->GetMapPtr(i, layout.Offset), 0, layout.Size); } } } ThrottleAllocationLocked(); } void D3D11Initializer::InitHostVisibleTexture( D3D11CommonTexture* pTexture, const D3D11_SUBRESOURCE_DATA* pInitialData) { Rc image = pTexture->GetImage(); auto formatInfo = image->formatInfo(); for (uint32_t layer = 0; layer < pTexture->Desc()->ArraySize; layer++) { for (uint32_t level = 0; level < pTexture->Desc()->MipLevels; level++) { uint32_t subresourceIndex = D3D11CalcSubresource(level, layer, pTexture->Desc()->MipLevels); VkImageSubresource subresource; subresource.aspectMask = formatInfo->aspectMask; subresource.mipLevel = level; subresource.arrayLayer = layer; VkExtent3D blockCount = util::computeBlockCount( image->mipLevelExtent(level), formatInfo->blockSize); auto layout = pTexture->GetSubresourceLayout( subresource.aspectMask, subresourceIndex); if (pInitialData && pInitialData[subresourceIndex].pSysMem) { const auto& initialData = pInitialData[subresourceIndex]; for (uint32_t z = 0; z < blockCount.depth; z++) { for (uint32_t y = 0; y < blockCount.height; y++) { auto size = blockCount.width * formatInfo->elementSize; auto dst = pTexture->GetMapPtr(subresourceIndex, layout.Offset + y * layout.RowPitch + z * layout.DepthPitch); auto src = reinterpret_cast(initialData.pSysMem) + y * initialData.SysMemPitch + z * initialData.SysMemSlicePitch; std::memcpy(dst, src, size); if (size < layout.RowPitch) std::memset(reinterpret_cast(dst) + size, 0, layout.RowPitch - size); } } } else { void* dst = pTexture->GetMapPtr(subresourceIndex, layout.Offset); std::memset(dst, 0, layout.Size); } } } // Initialize the image on the GPU std::lock_guard lock(m_mutex); EmitCs([ cImage = std::move(image) ] (DxvkContext* ctx) { ctx->initImage(cImage, VK_IMAGE_LAYOUT_PREINITIALIZED); }); m_transferCommands += 1; ThrottleAllocationLocked(); } void D3D11Initializer::InitTiledTexture( D3D11CommonTexture* pTexture) { std::lock_guard lock(m_mutex); EmitCs([ cImage = pTexture->GetImage() ] (DxvkContext* ctx) { ctx->initSparseImage(cImage); }); m_transferCommands += 1; ThrottleAllocationLocked(); } void D3D11Initializer::ThrottleAllocationLocked() { DxvkStagingBufferStats stats = m_stagingBuffer.getStatistics(); // If the amount of memory in flight exceeds the limit, stall the // calling thread and wait for some memory to actually get released. VkDeviceSize stagingMemoryInFlight = stats.allocatedTotal - m_stagingSignal->value(); if (stagingMemoryInFlight > MaxMemoryInFlight) { ExecuteFlushLocked(); m_stagingSignal->wait(stats.allocatedTotal - MaxMemoryInFlight); } else if (m_transferCommands >= MaxCommandsPerSubmission || stats.allocatedSinceLastReset >= MaxMemoryPerSubmission) { // Flush pending commands if there are a lot of updates in flight // to keep both execution time and staging memory in check. ExecuteFlushLocked(); } } void D3D11Initializer::ExecuteFlush() { std::lock_guard lock(m_mutex); ExecuteFlushLocked(); } void D3D11Initializer::ExecuteFlushLocked() { DxvkStagingBufferStats stats = m_stagingBuffer.getStatistics(); EmitCs([ cSignal = m_stagingSignal, cSignalValue = stats.allocatedTotal ] (DxvkContext* ctx) { ctx->signal(cSignal, cSignalValue); ctx->flushCommandList(nullptr, nullptr); }); FlushCsChunk(); NotifyContextFlushLocked(); } void D3D11Initializer::SyncSharedTexture(D3D11CommonTexture* pResource) { if (!(pResource->Desc()->MiscFlags & (D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX | D3D11_RESOURCE_MISC_SHARED_NTHANDLE))) return; // Ensure that initialization commands are submitted and waited on before // returning control to the application in order to avoid race conditions // in case the texture is used immediately on a secondary device. if (pResource->HasImage()) { ExecuteFlush(); m_device->waitForResource(*pResource->GetImage(), DxvkAccess::Write); } // If a keyed mutex is used, initialize that to the correct state as well. Com keyedMutex; if (SUCCEEDED(pResource->GetInterface()->QueryInterface( __uuidof(IDXGIKeyedMutex), reinterpret_cast(&keyedMutex)))) { keyedMutex->AcquireSync(0, 0); keyedMutex->ReleaseSync(0); } } void D3D11Initializer::FlushCsChunkLocked() { m_parent->GetContext()->InjectCsChunk(DxvkCsQueue::HighPriority, std::move(m_csChunk), false); m_csChunk = m_parent->AllocCsChunk(DxvkCsChunkFlag::SingleUse); } void D3D11Initializer::NotifyContextFlushLocked() { m_stagingBuffer.reset(); m_transferCommands = 0; } } dxvk-2.6.1/src/d3d11/d3d11_initializer.h000066400000000000000000000065041477473124000175050ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_staging.h" #include "d3d11_buffer.h" #include "d3d11_shader.h" #include "d3d11_texture.h" #include "d3d11_view_uav.h" namespace dxvk { class D3D11Device; /** * \brief Resource initialization context * * Manages a context which is used for resource * initialization. This includes initialization * with application-defined data, as well as * zero-initialization for buffers and images. */ class D3D11Initializer { // Use a staging buffer with a linear allocator to service small uploads constexpr static VkDeviceSize StagingBufferSize = 1ull << 20; public: // Maximum number of copy and clear commands to record before flushing constexpr static size_t MaxCommandsPerSubmission = 512u; // Maximum amount of staging memory to allocate before flushing constexpr static size_t MaxMemoryPerSubmission = (env::is32BitHostPlatform() ? 12u : 48u) << 20; // Maximum amount of memory in flight. If there are pending uploads while // this limit is exceeded, further initialization will be stalled. constexpr static size_t MaxMemoryInFlight = 3u * MaxMemoryPerSubmission; D3D11Initializer( D3D11Device* pParent); ~D3D11Initializer(); void FlushCsChunk() { std::lock_guard lock(m_csMutex); if (!m_csChunk->empty()) FlushCsChunkLocked(); } void NotifyContextFlush(); void InitBuffer( D3D11Buffer* pBuffer, const D3D11_SUBRESOURCE_DATA* pInitialData); void InitTexture( D3D11CommonTexture* pTexture, const D3D11_SUBRESOURCE_DATA* pInitialData); void InitUavCounter( D3D11UnorderedAccessView* pUav); void InitShaderIcb( D3D11CommonShader* pShader, size_t IcbSize, const void* pIcbData); private: dxvk::mutex m_mutex; D3D11Device* m_parent; Rc m_device; DxvkStagingBuffer m_stagingBuffer; Rc m_stagingSignal; size_t m_transferCommands = 0; dxvk::mutex m_csMutex; DxvkCsChunkRef m_csChunk; void InitDeviceLocalBuffer( D3D11Buffer* pBuffer, const D3D11_SUBRESOURCE_DATA* pInitialData); void InitHostVisibleBuffer( D3D11Buffer* pBuffer, const D3D11_SUBRESOURCE_DATA* pInitialData); void InitDeviceLocalTexture( D3D11CommonTexture* pTexture, const D3D11_SUBRESOURCE_DATA* pInitialData); void InitHostVisibleTexture( D3D11CommonTexture* pTexture, const D3D11_SUBRESOURCE_DATA* pInitialData); void InitTiledTexture( D3D11CommonTexture* pTexture); void ThrottleAllocationLocked(); void ExecuteFlush(); void ExecuteFlushLocked(); void SyncSharedTexture( D3D11CommonTexture* pResource); void FlushCsChunkLocked(); void NotifyContextFlushLocked(); template void EmitCs(Cmd&& command) { std::lock_guard lock(m_csMutex); if (unlikely(!m_csChunk->push(command))) { FlushCsChunkLocked(); m_csChunk->push(command); } } }; } dxvk-2.6.1/src/d3d11/d3d11_input_layout.cpp000066400000000000000000000040411477473124000202430ustar00rootroot00000000000000#include "d3d11_device.h" #include "d3d11_input_layout.h" namespace dxvk { D3D11InputLayout::D3D11InputLayout( D3D11Device* pDevice, uint32_t numAttributes, const DxvkVertexAttribute* pAttributes, uint32_t numBindings, const DxvkVertexBinding* pBindings) : D3D11DeviceChild(pDevice), m_attributeCount (numAttributes), m_bindingCount (numBindings), m_d3d10 (this) { for (uint32_t i = 0; i < numAttributes; i++) m_inputs[i] = DxvkVertexInput(pAttributes[i]); for (uint32_t i = 0; i < numBindings; i++) m_inputs[i + numAttributes] = DxvkVertexInput(pBindings[i]); } D3D11InputLayout::~D3D11InputLayout() { } HRESULT STDMETHODCALLTYPE D3D11InputLayout::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11InputLayout)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(ID3D10InputLayout)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11InputLayout), riid)) { Logger::warn("D3D11InputLayout::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } bool D3D11InputLayout::Compare(const D3D11InputLayout* pOther) const { if (m_attributeCount != pOther->m_attributeCount || m_bindingCount != pOther->m_bindingCount) return false; // Try to vectorize at least a little bit here. We can't use bcmpeq here // since there is no way at all to guaratee alignment for the array. for (uint32_t i = 0; i < m_attributeCount + m_bindingCount; i += 4u) { if (std::memcmp(&m_inputs[i], &pOther->m_inputs[i], 4u * sizeof(DxvkVertexInput))) return false; } return true; } } dxvk-2.6.1/src/d3d11/d3d11_input_layout.h000066400000000000000000000023571477473124000177200ustar00rootroot00000000000000#pragma once #include "d3d11_device_child.h" #include "../d3d10/d3d10_input_layout.h" namespace dxvk { class D3D11Device; class D3D11InputLayout : public D3D11DeviceChild { public: D3D11InputLayout( D3D11Device* pDevice, uint32_t numAttributes, const DxvkVertexAttribute* pAttributes, uint32_t numBindings, const DxvkVertexBinding* pBindings); ~D3D11InputLayout(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) final; uint32_t GetAttributeCount() const { return m_attributeCount; } uint32_t GetBindingCount() const { return m_bindingCount; } DxvkVertexInput GetInput(uint32_t Index) const { return m_inputs[Index]; } bool Compare( const D3D11InputLayout* pOther) const; D3D10InputLayout* GetD3D10Iface() { return &m_d3d10; } private: uint32_t m_attributeCount = 0; uint32_t m_bindingCount = 0; std::array m_inputs = { }; D3D10InputLayout m_d3d10; }; } dxvk-2.6.1/src/d3d11/d3d11_interfaces.h000066400000000000000000000205131477473124000173010ustar00rootroot00000000000000#pragma once #include "../dxgi/dxgi_interfaces.h" #include "d3d11_include.h" /** * \brief D3D11 extension * * Lists D3D11 extensions supported by DXVK. */ enum D3D11_VK_EXTENSION : uint32_t { D3D11_VK_EXT_MULTI_DRAW_INDIRECT = 0, D3D11_VK_EXT_MULTI_DRAW_INDIRECT_COUNT = 1, D3D11_VK_EXT_DEPTH_BOUNDS = 2, D3D11_VK_EXT_BARRIER_CONTROL = 3, D3D11_VK_NVX_BINARY_IMPORT = 4, D3D11_VK_NVX_IMAGE_VIEW_HANDLE = 5, }; /** * \brief Barrier control flags */ enum D3D11_VK_BARRIER_CONTROL : uint32_t { D3D11_VK_BARRIER_CONTROL_IGNORE_WRITE_AFTER_WRITE = 1 << 0, // Removed: // D3D11_VK_BARRIER_CONTROL_IGNORE_GRAPHICS_UAV = 1 << 1, }; /** * \brief Extended shader interface */ MIDL_INTERFACE("bb8a4fb9-3935-4762-b44b-35189a26414a") ID3D11VkExtShader : public IUnknown { /** * \brief Retrieves SPIR-V code from a shader object * * \param [in,out] pCodeSize Shader code size, in bytes. If * \ref pCode is \c nullptr, this will return the total * code size, otherwise the number of bytes written. * \param [out] pCode SPIR-V shader code * \returns \c S_OK, or \c S_FALSE if the buffer was too small */ virtual HRESULT STDMETHODCALLTYPE GetSpirvCode( SIZE_T* pCodeSize, void* pCode) = 0; }; /** * \brief Extended D3D11 device * * Introduces a method to check for extension support. */ MIDL_INTERFACE("8a6e3c42-f74c-45b7-8265-a231b677ca17") ID3D11VkExtDevice : public IUnknown { /** * \brief Checks whether an extension is supported * * \param [in] Extension The extension to check * \returns \c TRUE if the extension is supported */ virtual BOOL STDMETHODCALLTYPE GetExtensionSupport( D3D11_VK_EXTENSION Extension) = 0; }; /** * \brief Extended extended D3D11 device * * Introduces methods to get virtual addresses and driver * handles for resources, and create and destroy objects * for D3D11-Cuda interop. */ MIDL_INTERFACE("cfcf64ef-9586-46d0-bca4-97cf2ca61b06") ID3D11VkExtDevice1 : public ID3D11VkExtDevice { virtual bool STDMETHODCALLTYPE GetResourceHandleGPUVirtualAddressAndSizeNVX( void* hObject, uint64_t* gpuVAStart, uint64_t* gpuVASize) = 0; virtual bool STDMETHODCALLTYPE CreateUnorderedAccessViewAndGetDriverHandleNVX( ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, ID3D11UnorderedAccessView** ppUAV, uint32_t* pDriverHandle) = 0; virtual bool STDMETHODCALLTYPE CreateShaderResourceViewAndGetDriverHandleNVX( ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRV, uint32_t* pDriverHandle) = 0; virtual bool STDMETHODCALLTYPE CreateSamplerStateAndGetDriverHandleNVX( const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState, uint32_t* pDriverHandle) = 0; virtual bool STDMETHODCALLTYPE CreateCubinComputeShaderWithNameNVX( const void* pCubin, uint32_t size, uint32_t blockX, uint32_t blockY, uint32_t blockZ, const char* pShaderName, IUnknown** phShader) = 0; virtual bool STDMETHODCALLTYPE GetCudaTextureObjectNVX( uint32_t srvDriverHandle, uint32_t samplerDriverHandle, uint32_t* pCudaTextureHandle) = 0; }; /** * \brief Extended D3D11 context * * Provides functionality for various D3D11 * extensions. */ MIDL_INTERFACE("fd0bca13-5cb6-4c3a-987e-4750de2ca791") ID3D11VkExtContext : public IUnknown { virtual void STDMETHODCALLTYPE MultiDrawIndirect( UINT DrawCount, ID3D11Buffer* pBufferForArgs, UINT ByteOffsetForArgs, UINT ByteStrideForArgs) = 0; virtual void STDMETHODCALLTYPE MultiDrawIndexedIndirect( UINT DrawCount, ID3D11Buffer* pBufferForArgs, UINT ByteOffsetForArgs, UINT ByteStrideForArgs) = 0; virtual void STDMETHODCALLTYPE MultiDrawIndirectCount( UINT MaxDrawCount, ID3D11Buffer* pBufferForCount, UINT ByteOffsetForCount, ID3D11Buffer* pBufferForArgs, UINT ByteOffsetForArgs, UINT ByteStrideForArgs) = 0; virtual void STDMETHODCALLTYPE MultiDrawIndexedIndirectCount( UINT MaxDrawCount, ID3D11Buffer* pBufferForCount, UINT ByteOffsetForCount, ID3D11Buffer* pBufferForArgs, UINT ByteOffsetForArgs, UINT ByteStrideForArgs) = 0; virtual void STDMETHODCALLTYPE SetDepthBoundsTest( BOOL Enable, FLOAT MinDepthBounds, FLOAT MaxDepthBounds) = 0; virtual void STDMETHODCALLTYPE SetBarrierControl( UINT ControlFlags) = 0; }; /** * \brief Extended extended D3D11 context * * Provides functionality to launch a Cuda kernel */ MIDL_INTERFACE("874b09b2-ae0b-41d8-8476-5f3b7a0e879d") ID3D11VkExtContext1 : public ID3D11VkExtContext { virtual bool STDMETHODCALLTYPE LaunchCubinShaderNVX( IUnknown* hShader, uint32_t gridX, uint32_t gridY, uint32_t gridZ, const void* pParams, uint32_t paramSize, void* const* pReadResources, uint32_t numReadResources, void* const* pWriteResources, uint32_t numWriteResources) = 0; }; /** * \brief Frame reports used for Reflex interop */ struct D3D_LOW_LATENCY_FRAME_REPORT { UINT64 frameID; UINT64 inputSampleTime; UINT64 simStartTime; UINT64 simEndTime; UINT64 renderSubmitStartTime; UINT64 renderSubmitEndTime; UINT64 presentStartTime; UINT64 presentEndTime; UINT64 driverStartTime; UINT64 driverEndTime; UINT64 osRenderQueueStartTime; UINT64 osRenderQueueEndTime; UINT64 gpuRenderStartTime; UINT64 gpuRenderEndTime; UINT32 gpuActiveRenderTimeUs; UINT32 gpuFrameTimeUs; UINT8 rsvd[120]; }; /** * \brief Data structure used for Reflex interop */ struct D3D_LOW_LATENCY_RESULTS { UINT32 version; D3D_LOW_LATENCY_FRAME_REPORT frameReports[64]; UINT8 rsvd[32]; }; /** * \brief D3D interop interface for Nvidia Reflex */ MIDL_INTERFACE("f3112584-41f9-348d-a59b-00b7e1d285d6") ID3DLowLatencyDevice : public IUnknown { virtual BOOL STDMETHODCALLTYPE SupportsLowLatency() = 0; virtual HRESULT STDMETHODCALLTYPE LatencySleep() = 0; virtual HRESULT STDMETHODCALLTYPE SetLatencySleepMode( BOOL LowLatencyEnable, BOOL LowLatencyBoost, UINT32 MinIntervalUs) = 0; virtual HRESULT STDMETHODCALLTYPE SetLatencyMarker( UINT64 FrameId, UINT32 MarkerType) = 0; virtual HRESULT STDMETHODCALLTYPE GetLatencyInfo( D3D_LOW_LATENCY_RESULTS* pLowLatencyResults) = 0; }; #ifndef _MSC_VER __CRT_UUID_DECL(ID3D11VkExtShader, 0xbb8a4fb9,0x3935,0x4762,0xb4,0x4b,0x35,0x18,0x9a,0x26,0x41,0x4a); __CRT_UUID_DECL(ID3D11VkExtDevice, 0x8a6e3c42,0xf74c,0x45b7,0x82,0x65,0xa2,0x31,0xb6,0x77,0xca,0x17); __CRT_UUID_DECL(ID3D11VkExtDevice1, 0xcfcf64ef,0x9586,0x46d0,0xbc,0xa4,0x97,0xcf,0x2c,0xa6,0x1b,0x06); __CRT_UUID_DECL(ID3D11VkExtContext, 0xfd0bca13,0x5cb6,0x4c3a,0x98,0x7e,0x47,0x50,0xde,0x2c,0xa7,0x91); __CRT_UUID_DECL(ID3D11VkExtContext1, 0x874b09b2,0xae0b,0x41d8,0x84,0x76,0x5f,0x3b,0x7a,0x0e,0x87,0x9d); __CRT_UUID_DECL(ID3DLowLatencyDevice, 0xf3112584,0x41f9,0x348d,0xa5,0x9b,0x00,0xb7,0xe1,0xd2,0x85,0xd6); #endif dxvk-2.6.1/src/d3d11/d3d11_interop.cpp000066400000000000000000000107041477473124000171720ustar00rootroot00000000000000#include "d3d11_context_imm.h" #include "d3d11_interop.h" #include "d3d11_device.h" #include "../dxvk/dxvk_adapter.h" #include "../dxvk/dxvk_device.h" #include "../dxvk/dxvk_instance.h" namespace dxvk { D3D11VkInterop::D3D11VkInterop( IDXGIObject* pContainer, D3D11Device* pDevice) : m_container (pContainer), m_device (pDevice) { } D3D11VkInterop::~D3D11VkInterop() { } ULONG STDMETHODCALLTYPE D3D11VkInterop::AddRef() { return m_container->AddRef(); } ULONG STDMETHODCALLTYPE D3D11VkInterop::Release() { return m_container->Release(); } HRESULT STDMETHODCALLTYPE D3D11VkInterop::QueryInterface( REFIID riid, void** ppvObject) { return m_container->QueryInterface(riid, ppvObject); } void STDMETHODCALLTYPE D3D11VkInterop::GetVulkanHandles( VkInstance* pInstance, VkPhysicalDevice* pPhysDev, VkDevice* pDevice) { auto device = m_device->GetDXVKDevice(); auto adapter = device->adapter(); auto instance = device->instance(); if (pDevice != nullptr) *pDevice = device->handle(); if (pPhysDev != nullptr) *pPhysDev = adapter->handle(); if (pInstance != nullptr) *pInstance = instance->handle(); } void STDMETHODCALLTYPE D3D11VkInterop::GetSubmissionQueue( VkQueue* pQueue, uint32_t* pQueueFamilyIndex) { auto device = static_cast(m_device)->GetDXVKDevice(); DxvkDeviceQueue queue = device->queues().graphics; if (pQueue != nullptr) *pQueue = queue.queueHandle; if (pQueueFamilyIndex != nullptr) *pQueueFamilyIndex = queue.queueFamily; } void STDMETHODCALLTYPE D3D11VkInterop::TransitionSurfaceLayout( IDXGIVkInteropSurface* pSurface, const VkImageSubresourceRange* pSubresources, VkImageLayout OldLayout, VkImageLayout NewLayout) { auto immediateContext = m_device->GetContext(); immediateContext->TransitionSurfaceLayout( pSurface, pSubresources, OldLayout, NewLayout); } void STDMETHODCALLTYPE D3D11VkInterop::FlushRenderingCommands() { auto immediateContext = m_device->GetContext(); immediateContext->Flush(); immediateContext->SynchronizeCsThread(DxvkCsThread::SynchronizeAll); } void STDMETHODCALLTYPE D3D11VkInterop::LockSubmissionQueue() { m_device->GetDXVKDevice()->lockSubmission(); } void STDMETHODCALLTYPE D3D11VkInterop::ReleaseSubmissionQueue() { m_device->GetDXVKDevice()->unlockSubmission(); } void STDMETHODCALLTYPE D3D11VkInterop::GetSubmissionQueue1( VkQueue* pQueue, uint32_t* pQueueIndex, uint32_t* pQueueFamilyIndex) { auto device = static_cast(m_device)->GetDXVKDevice(); DxvkDeviceQueue queue = device->queues().graphics; if (pQueue != nullptr) *pQueue = queue.queueHandle; if (pQueueIndex != nullptr) *pQueueIndex = queue.queueIndex; if (pQueueFamilyIndex != nullptr) *pQueueFamilyIndex = queue.queueFamily; } HRESULT STDMETHODCALLTYPE D3D11VkInterop::CreateTexture2DFromVkImage( const D3D11_TEXTURE2D_DESC1 *pDesc, VkImage vkImage, ID3D11Texture2D **ppTexture2D) { InitReturnPtr(ppTexture2D); if (!pDesc) return E_INVALIDARG; D3D11_COMMON_TEXTURE_DESC desc; desc.Width = pDesc->Width; desc.Height = pDesc->Height; desc.Depth = 1; desc.MipLevels = pDesc->MipLevels; desc.ArraySize = pDesc->ArraySize; desc.Format = pDesc->Format; desc.SampleDesc = pDesc->SampleDesc; desc.Usage = pDesc->Usage; desc.BindFlags = pDesc->BindFlags; desc.CPUAccessFlags = pDesc->CPUAccessFlags; desc.MiscFlags = pDesc->MiscFlags; desc.TextureLayout = pDesc->TextureLayout; HRESULT hr = D3D11CommonTexture::NormalizeTextureProperties(&desc); if (FAILED(hr)) return hr; if (!ppTexture2D) return S_FALSE; try { Com texture = new D3D11Texture2D(m_device, &desc, 0, vkImage); *ppTexture2D = texture.ref(); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } } }dxvk-2.6.1/src/d3d11/d3d11_interop.h000066400000000000000000000034461477473124000166440ustar00rootroot00000000000000#pragma once #include "../dxgi/dxgi_interfaces.h" #include "d3d11_include.h" namespace dxvk { class D3D11Device; class D3D11VkInterop : public ComObject { public: D3D11VkInterop( IDXGIObject* pContainer, D3D11Device* pDevice); ~D3D11VkInterop(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); void STDMETHODCALLTYPE GetVulkanHandles( VkInstance* pInstance, VkPhysicalDevice* pPhysDev, VkDevice* pDevice); void STDMETHODCALLTYPE GetSubmissionQueue( VkQueue* pQueue, uint32_t* pQueueFamilyIndex); void STDMETHODCALLTYPE TransitionSurfaceLayout( IDXGIVkInteropSurface* pSurface, const VkImageSubresourceRange* pSubresources, VkImageLayout OldLayout, VkImageLayout NewLayout); void STDMETHODCALLTYPE FlushRenderingCommands(); void STDMETHODCALLTYPE LockSubmissionQueue(); void STDMETHODCALLTYPE ReleaseSubmissionQueue(); void STDMETHODCALLTYPE GetSubmissionQueue1( VkQueue* pQueue, uint32_t* pQueueIndex, uint32_t* pQueueFamilyIndex); HRESULT STDMETHODCALLTYPE CreateTexture2DFromVkImage( const D3D11_TEXTURE2D_DESC1* pDesc, VkImage vkImage, ID3D11Texture2D** ppTexture2D); private: IDXGIObject* m_container; D3D11Device* m_device; }; }dxvk-2.6.1/src/d3d11/d3d11_main.cpp000066400000000000000000000372031477473124000164410ustar00rootroot00000000000000#include #include "../dxgi/dxgi_adapter.h" #include "../dxvk/dxvk_instance.h" #include "d3d11_device.h" #include "d3d11_enums.h" #include "d3d11_interop.h" namespace dxvk { Logger Logger::s_instance("d3d11.log"); } extern "C" { using namespace dxvk; HRESULT D3D11InternalCreateDevice( IDXGIFactory* pFactory, IDXGIAdapter* pAdapter, UINT Flags, const D3D_FEATURE_LEVEL* pFeatureLevels, UINT FeatureLevels, ID3D11Device** ppDevice) { InitReturnPtr(ppDevice); Rc dxvkAdapter; Rc dxvkInstance; Com dxgiVkAdapter; // Try to find the corresponding Vulkan device for the DXGI adapter if (SUCCEEDED(pAdapter->QueryInterface(__uuidof(IDXGIDXVKAdapter), reinterpret_cast(&dxgiVkAdapter)))) { dxvkAdapter = dxgiVkAdapter->GetDXVKAdapter(); dxvkInstance = dxgiVkAdapter->GetDXVKInstance(); } else { Logger::warn("D3D11InternalCreateDevice: Adapter is not a DXVK adapter"); DXGI_ADAPTER_DESC desc; pAdapter->GetDesc(&desc); dxvkInstance = new DxvkInstance(0); dxvkAdapter = dxvkInstance->findAdapterByLuid(&desc.AdapterLuid); if (dxvkAdapter == nullptr) dxvkAdapter = dxvkInstance->findAdapterByDeviceId(desc.VendorId, desc.DeviceId); if (dxvkAdapter == nullptr) dxvkAdapter = dxvkInstance->enumAdapters(0); if (dxvkAdapter == nullptr) return E_FAIL; } // Feature levels to probe if the // application does not specify any. std::array defaultFeatureLevels = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1, }; if (!pFeatureLevels || !FeatureLevels) { pFeatureLevels = defaultFeatureLevels.data(); FeatureLevels = defaultFeatureLevels.size(); } // Find the highest feature level supported by the device. // This works because the feature level array is ordered. D3D_FEATURE_LEVEL maxFeatureLevel = D3D11Device::GetMaxFeatureLevel(dxvkInstance, dxvkAdapter); D3D_FEATURE_LEVEL minFeatureLevel = D3D_FEATURE_LEVEL(); D3D_FEATURE_LEVEL devFeatureLevel = D3D_FEATURE_LEVEL(); Logger::info(str::format("D3D11InternalCreateDevice: Maximum supported feature level: ", maxFeatureLevel)); for (uint32_t flId = 0 ; flId < FeatureLevels; flId++) { minFeatureLevel = pFeatureLevels[flId]; if (minFeatureLevel <= maxFeatureLevel) { devFeatureLevel = minFeatureLevel; break; } } if (!devFeatureLevel) { Logger::err(str::format("D3D11InternalCreateDevice: Minimum required feature level ", minFeatureLevel, " not supported")); return E_INVALIDARG; } try { Logger::info(str::format("D3D11InternalCreateDevice: Using feature level ", devFeatureLevel)); DxvkDeviceFeatures deviceFeatures = D3D11Device::GetDeviceFeatures(dxvkAdapter); Rc dxvkDevice = dxvkAdapter->createDevice(dxvkInstance, deviceFeatures); Com device = new D3D11DXGIDevice( pAdapter, nullptr, nullptr, dxvkInstance, dxvkAdapter, dxvkDevice, devFeatureLevel, Flags); return device->QueryInterface( __uuidof(ID3D11Device), reinterpret_cast(ppDevice)); } catch (const DxvkError& e) { Logger::err("D3D11InternalCreateDevice: Failed to create D3D11 device"); return E_FAIL; } } static HRESULT D3D11InternalCreateDeviceAndSwapChain( IDXGIAdapter* pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, const D3D_FEATURE_LEVEL* pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, const DXGI_SWAP_CHAIN_DESC* pSwapChainDesc, IDXGISwapChain** ppSwapChain, ID3D11Device** ppDevice, D3D_FEATURE_LEVEL* pFeatureLevel, ID3D11DeviceContext** ppImmediateContext) { InitReturnPtr(ppDevice); InitReturnPtr(ppSwapChain); InitReturnPtr(ppImmediateContext); if (pFeatureLevel) *pFeatureLevel = D3D_FEATURE_LEVEL(0); HRESULT hr; Com dxgiFactory = nullptr; Com dxgiAdapter = pAdapter; Com device = nullptr; if (ppSwapChain && !pSwapChainDesc) return E_INVALIDARG; if (!pAdapter) { // We'll treat everything as hardware, even if the // Vulkan device is actually a software device. if (DriverType != D3D_DRIVER_TYPE_HARDWARE) Logger::warn("D3D11CreateDevice: Unsupported driver type"); // We'll use the first adapter returned by a DXGI factory hr = CreateDXGIFactory1(__uuidof(IDXGIFactory), reinterpret_cast(&dxgiFactory)); if (FAILED(hr)) { Logger::err("D3D11CreateDevice: Failed to create a DXGI factory"); return hr; } hr = dxgiFactory->EnumAdapters(0, &dxgiAdapter); if (FAILED(hr)) { Logger::err("D3D11CreateDevice: No default adapter available"); return hr; } } else { // We should be able to query the DXGI factory from the adapter if (FAILED(dxgiAdapter->GetParent(__uuidof(IDXGIFactory), reinterpret_cast(&dxgiFactory)))) { Logger::err("D3D11CreateDevice: Failed to query DXGI factory from DXGI adapter"); return E_INVALIDARG; } // In theory we could ignore these, but the Microsoft docs explicitly // state that we need to return E_INVALIDARG in case the arguments are // invalid. Both the driver type and software parameter can only be // set if the adapter itself is unspecified. // See: https://msdn.microsoft.com/en-us/library/windows/desktop/ff476082(v=vs.85).aspx if (DriverType != D3D_DRIVER_TYPE_UNKNOWN || Software) return E_INVALIDARG; } // Create the actual device hr = D3D11InternalCreateDevice( dxgiFactory.ptr(), dxgiAdapter.ptr(), Flags, pFeatureLevels, FeatureLevels, &device); if (FAILED(hr)) return hr; // Create the swap chain, if requested if (ppSwapChain) { DXGI_SWAP_CHAIN_DESC desc = *pSwapChainDesc; hr = dxgiFactory->CreateSwapChain(device.ptr(), &desc, ppSwapChain); if (FAILED(hr)) { Logger::err("D3D11CreateDevice: Failed to create swap chain"); return hr; } } // Write back whatever info the application requested if (pFeatureLevel) *pFeatureLevel = device->GetFeatureLevel(); if (ppDevice) *ppDevice = device.ref(); if (ppImmediateContext) device->GetImmediateContext(ppImmediateContext); // If we were unable to write back the device and the // swap chain, the application has no way of working // with the device so we should report S_FALSE here. if (!ppDevice && !ppImmediateContext && !ppSwapChain) return S_FALSE; return S_OK; } DLLEXPORT HRESULT __stdcall D3D11CoreCreateDevice( IDXGIFactory* pFactory, IDXGIAdapter* pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, const D3D_FEATURE_LEVEL* pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, ID3D11Device** ppDevice, D3D_FEATURE_LEVEL* pFeatureLevel) { return D3D11InternalCreateDeviceAndSwapChain( pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, SDKVersion, nullptr, nullptr, ppDevice, pFeatureLevel, nullptr); } DLLEXPORT HRESULT __stdcall D3D11CreateDevice( IDXGIAdapter* pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, const D3D_FEATURE_LEVEL* pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, ID3D11Device** ppDevice, D3D_FEATURE_LEVEL* pFeatureLevel, ID3D11DeviceContext** ppImmediateContext) { return D3D11InternalCreateDeviceAndSwapChain( pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, SDKVersion, nullptr, nullptr, ppDevice, pFeatureLevel, ppImmediateContext); } DLLEXPORT HRESULT __stdcall D3D11CreateDeviceAndSwapChain( IDXGIAdapter* pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, const D3D_FEATURE_LEVEL* pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, const DXGI_SWAP_CHAIN_DESC* pSwapChainDesc, IDXGISwapChain** ppSwapChain, ID3D11Device** ppDevice, D3D_FEATURE_LEVEL* pFeatureLevel, ID3D11DeviceContext** ppImmediateContext) { return D3D11InternalCreateDeviceAndSwapChain( pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, SDKVersion, pSwapChainDesc, ppSwapChain, ppDevice, pFeatureLevel, ppImmediateContext); } DLLEXPORT HRESULT __stdcall D3D11On12CreateDevice( IUnknown* pDevice, UINT Flags, const D3D_FEATURE_LEVEL* pFeatureLevels, UINT FeatureLevels, IUnknown* const* ppCommandQueues, UINT NumQueues, UINT NodeMask, ID3D11Device** ppDevice, ID3D11DeviceContext** ppImmediateContext, D3D_FEATURE_LEVEL* pChosenFeatureLevel) { InitReturnPtr(ppDevice); InitReturnPtr(ppImmediateContext); if (pChosenFeatureLevel) *pChosenFeatureLevel = D3D_FEATURE_LEVEL(0); if (!pDevice) return E_INVALIDARG; // Figure out D3D12 objects Com d3d12Device; Com d3d12Queue; if (FAILED(pDevice->QueryInterface(__uuidof(ID3D12Device), reinterpret_cast(&d3d12Device)))) { Logger::err("D3D11On12CreateDevice: Device is not a valid D3D12 device"); return E_INVALIDARG; } if (NodeMask & (NodeMask - 1)) { Logger::err("D3D11On12CreateDevice: Invalid node mask"); return E_INVALIDARG; } if (!NumQueues || !ppCommandQueues || !ppCommandQueues[0]) { Logger::err("D3D11On12CreateDevice: No command queue specified"); return E_INVALIDARG; } if (NumQueues > 1) { // Not sure what to do with more than one graphics queue Logger::warn("D3D11On12CreateDevice: Only one queue supported"); } if (FAILED(ppCommandQueues[0]->QueryInterface(__uuidof(ID3D12CommandQueue), reinterpret_cast(&d3d12Queue)))) { Logger::err("D3D11On12CreateDevice: Queue is not a valid D3D12 command queue"); return E_INVALIDARG; } // Determine feature level for the D3D11 device std::array defaultFeatureLevels = {{ D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_12_1, }}; D3D12_FEATURE_DATA_FEATURE_LEVELS featureLevel = { }; if (!FeatureLevels || !pFeatureLevels) { featureLevel.NumFeatureLevels = defaultFeatureLevels.size(); featureLevel.pFeatureLevelsRequested = defaultFeatureLevels.data(); } else { featureLevel.NumFeatureLevels = FeatureLevels; featureLevel.pFeatureLevelsRequested = pFeatureLevels; } HRESULT hr = d3d12Device->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &featureLevel, sizeof(featureLevel)); if (FAILED(hr) || !featureLevel.MaxSupportedFeatureLevel) { Logger::err(str::format("D3D11On12CreateDevice: Minimum required feature level not supported")); return hr; } Logger::info(str::format("D3D11On12CreateDevice: Chosen feature level: ", featureLevel.MaxSupportedFeatureLevel)); Com interopDevice; if (FAILED(d3d12Device->QueryInterface(__uuidof(ID3D12DXVKInteropDevice), reinterpret_cast(&interopDevice)))) { Logger::err("D3D11On12CreateDevice: Device not a vkd3d-proton device."); return E_INVALIDARG; } Com dxgiAdapter; if (FAILED(interopDevice->GetDXGIAdapter(IID_PPV_ARGS(&dxgiAdapter)))) { Logger::err("D3D11On12CreateDevice: Failed to query DXGI adapter."); return E_INVALIDARG; } try { // Initialize DXVK instance DxvkInstanceImportInfo instanceInfo = { }; DxvkDeviceImportInfo deviceInfo = { }; VkPhysicalDevice vulkanAdapter = VK_NULL_HANDLE; interopDevice->GetVulkanHandles(&instanceInfo.instance, &vulkanAdapter, &deviceInfo.device); uint32_t instanceExtensionCount = 0; interopDevice->GetInstanceExtensions(&instanceExtensionCount, nullptr); std::vector instanceExtensions(instanceExtensionCount); interopDevice->GetInstanceExtensions(&instanceExtensionCount, instanceExtensions.data()); instanceInfo.extensionCount = instanceExtensions.size(); instanceInfo.extensionNames = instanceExtensions.data(); Rc dxvkInstance = new DxvkInstance(instanceInfo, 0); // Find adapter by physical device handle Rc dxvkAdapter; for (uint32_t i = 0; i < dxvkInstance->adapterCount(); i++) { Rc curr = dxvkInstance->enumAdapters(i); if (curr->handle() == vulkanAdapter) dxvkAdapter = std::move(curr); } if (dxvkAdapter == nullptr) { Logger::err("D3D11On12CreateDevice: No matching adapter found"); return E_INVALIDARG; } interopDevice->GetVulkanQueueInfo(d3d12Queue.ptr(), &deviceInfo.queue, &deviceInfo.queueFamily); interopDevice->GetDeviceFeatures(&deviceInfo.features); uint32_t deviceExtensionCount = 0; interopDevice->GetDeviceExtensions(&deviceExtensionCount, nullptr); std::vector deviceExtensions(deviceExtensionCount); interopDevice->GetDeviceExtensions(&deviceExtensionCount, deviceExtensions.data()); deviceInfo.extensionCount = deviceExtensions.size(); deviceInfo.extensionNames = deviceExtensions.data(); deviceInfo.queueCallback = [ cDevice = interopDevice, cQueue = d3d12Queue ] (bool doLock) { HRESULT hr = doLock ? cDevice->LockCommandQueue(cQueue.ptr()) : cDevice->UnlockCommandQueue(cQueue.ptr()); if (FAILED(hr)) Logger::err(str::format("Failed to lock vkd3d-proton device queue: ", hr)); }; Rc dxvkDevice = dxvkAdapter->importDevice(dxvkInstance, deviceInfo); // Create and return the actual D3D11 device Com device = new D3D11DXGIDevice( dxgiAdapter.ptr(), d3d12Device.ptr(), d3d12Queue.ptr(), dxvkInstance, dxvkAdapter, dxvkDevice, featureLevel.MaxSupportedFeatureLevel, Flags); Com d3d11Device; device->QueryInterface(__uuidof(ID3D11Device), reinterpret_cast(&d3d11Device)); if (ppDevice) *ppDevice = d3d11Device.ref(); if (ppImmediateContext) d3d11Device->GetImmediateContext(ppImmediateContext); if (pChosenFeatureLevel) *pChosenFeatureLevel = d3d11Device->GetFeatureLevel(); if (!ppDevice && !ppImmediateContext) return S_FALSE; return S_OK; } catch (const DxvkError& e) { Logger::err("D3D11On12CreateDevice: Failed to create D3D11 device"); return E_FAIL; } } }dxvk-2.6.1/src/d3d11/d3d11_on_12.cpp000066400000000000000000000125731477473124000164360ustar00rootroot00000000000000#include "d3d11_context_imm.h" #include "d3d11_device.h" #include "d3d11_on_12.h" namespace dxvk { D3D11on12Device::D3D11on12Device( D3D11DXGIDevice* pContainer, D3D11Device* pDevice, ID3D12Device* pD3D12Device, ID3D12CommandQueue* pD3D12Queue) : m_container (pContainer), m_device (pDevice), m_d3d12Device (pD3D12Device), m_d3d12Queue (pD3D12Queue) { } D3D11on12Device::~D3D11on12Device() { } ULONG STDMETHODCALLTYPE D3D11on12Device::AddRef() { return m_container->AddRef(); } ULONG STDMETHODCALLTYPE D3D11on12Device::Release() { return m_container->Release(); } HRESULT STDMETHODCALLTYPE D3D11on12Device::QueryInterface( REFIID riid, void** ppvObject) { return m_container->QueryInterface(riid, ppvObject); } HRESULT STDMETHODCALLTYPE D3D11on12Device::CreateWrappedResource( IUnknown* pResource12, const D3D11_RESOURCE_FLAGS* pResourceFlags, D3D12_RESOURCE_STATES InputState, D3D12_RESOURCE_STATES OutputState, REFIID riid, void** ppResource11) { Com interopDevice; m_d3d12Device->QueryInterface(__uuidof(ID3D12DXVKInteropDevice), reinterpret_cast(&interopDevice)); D3D11_ON_12_RESOURCE_INFO info = { }; info.InputState = InputState; info.OutputState = OutputState; info.IsWrappedResource = TRUE; // 11on12 technically allows importing D3D12 heaps as tile pools, // but we don't support importing sparse resources at this time. if (FAILED(pResource12->QueryInterface(__uuidof(ID3D12Resource), reinterpret_cast(&info.Resource)))) { Logger::err("D3D11on12Device::CreateWrappedResource: Resource not a valid D3D12 resource"); return E_INVALIDARG; } // Query Vulkan resource handle and buffer offset as necessary if (FAILED(interopDevice->GetVulkanResourceInfo(info.Resource.ptr(), &info.VulkanHandle, &info.VulkanOffset))) { Logger::err("D3D11on12Device::CreateWrappedResource: Failed to retrieve Vulkan resource info"); return E_INVALIDARG; } Com resource; D3D12_RESOURCE_DESC desc = info.Resource->GetDesc(); if (desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) { D3D11_BUFFER_DESC bufferDesc; if (FAILED(D3D11Buffer::GetDescFromD3D12(info.Resource.ptr(), pResourceFlags, &bufferDesc))) return E_INVALIDARG; resource = new D3D11Buffer(m_device, &bufferDesc, &info); } else { D3D11_COMMON_TEXTURE_DESC textureDesc; if (FAILED(D3D11CommonTexture::GetDescFromD3D12(info.Resource.ptr(), pResourceFlags, &textureDesc))) return E_INVALIDARG; switch (desc.Dimension) { case D3D12_RESOURCE_DIMENSION_TEXTURE1D: resource = new D3D11Texture1D(m_device, &textureDesc, &info); break; case D3D12_RESOURCE_DIMENSION_TEXTURE2D: resource = new D3D11Texture2D(m_device, &textureDesc, &info, nullptr); break; case D3D12_RESOURCE_DIMENSION_TEXTURE3D: resource = new D3D11Texture3D(m_device, &textureDesc, &info); break; default: Logger::err("D3D11on12Device::CreateWrappedResource: Unhandled resource dimension"); return E_INVALIDARG; } } return resource->QueryInterface(riid, ppResource11); } void STDMETHODCALLTYPE D3D11on12Device::ReleaseWrappedResources( ID3D11Resource* const* ppResources, UINT ResourceCount) { Com interopDevice; m_d3d12Device->QueryInterface(__uuidof(ID3D12DXVKInteropDevice), reinterpret_cast(&interopDevice)); for (uint32_t i = 0; i < ResourceCount; i++) { D3D11_ON_12_RESOURCE_INFO info; if (FAILED(GetResource11on12Info(ppResources[i], &info)) || !info.IsWrappedResource) { Logger::warn("D3D11on12Device::ReleaseWrappedResources: Resource not a wrapped resource, skipping"); continue; } VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; interopDevice->GetVulkanImageLayout(info.Resource.ptr(), info.OutputState, &layout); m_device->GetContext()->Release11on12Resource(ppResources[i], layout); } } void STDMETHODCALLTYPE D3D11on12Device::AcquireWrappedResources( ID3D11Resource* const* ppResources, UINT ResourceCount) { Com interopDevice; m_d3d12Device->QueryInterface(__uuidof(ID3D12DXVKInteropDevice), reinterpret_cast(&interopDevice)); for (uint32_t i = 0; i < ResourceCount; i++) { D3D11_ON_12_RESOURCE_INFO info; if (FAILED(GetResource11on12Info(ppResources[i], &info)) || !info.IsWrappedResource) { Logger::warn("D3D11on12Device::AcquireWrappedResources: Resource not a wrapped resource, skipping"); continue; } VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; interopDevice->GetVulkanImageLayout(info.Resource.ptr(), info.InputState, &layout); m_device->GetContext()->Acquire11on12Resource(ppResources[i], layout); } } HRESULT STDMETHODCALLTYPE D3D11on12Device::GetD3D12Device( REFIID riid, void** ppvDevice) { return m_d3d12Queue->GetDevice(riid, ppvDevice); } } dxvk-2.6.1/src/d3d11/d3d11_on_12.h000066400000000000000000000052751477473124000161040ustar00rootroot00000000000000#pragma once #include "d3d11_on_12_interfaces.h" #include "../util/log/log.h" /** * \brief Declaration of the ID3D11On12Device1 interface * * Various different headers that we need to be compatible with * can't seem to agree on the signature of GetD3D12Device, and * older wine/mingw headers don't support this interface at all. */ MIDL_INTERFACE("bdb64df4-ea2f-4c70-b861-aaab1258bb5d") ID3D11On12Device1_DXVK : public ID3D11On12Device { virtual HRESULT STDMETHODCALLTYPE GetD3D12Device( REFIID riid, void** ppvDevice) = 0; }; namespace dxvk { class D3D11Device; class D3D11DXGIDevice; /** * \brief Resource info for 11on12 resources */ struct D3D11_ON_12_RESOURCE_INFO { Com Resource; UINT64 VulkanHandle = 0; UINT64 VulkanOffset = 0; BOOL IsWrappedResource = FALSE; D3D12_RESOURCE_STATES InputState = D3D12_RESOURCE_STATE_COMMON; D3D12_RESOURCE_STATES OutputState = D3D12_RESOURCE_STATE_COMMON; }; class D3D11on12Device : public ID3D11On12Device1_DXVK { public: D3D11on12Device( D3D11DXGIDevice* pContainer, D3D11Device* pDevice, ID3D12Device* pD3D12Device, ID3D12CommandQueue* pD3D12Queue); ~D3D11on12Device(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE CreateWrappedResource( IUnknown* pResource12, const D3D11_RESOURCE_FLAGS* pResourceFlags, D3D12_RESOURCE_STATES InputState, D3D12_RESOURCE_STATES OutputState, REFIID riid, void** ppResource11); void STDMETHODCALLTYPE ReleaseWrappedResources( ID3D11Resource* const* ppResources, UINT ResourceCount); void STDMETHODCALLTYPE AcquireWrappedResources( ID3D11Resource* const* ppResources, UINT ResourceCount); HRESULT STDMETHODCALLTYPE GetD3D12Device( REFIID riid, void** ppvDevice); bool Is11on12Device() const { return m_d3d12Device != nullptr; } private: D3D11DXGIDevice* m_container; D3D11Device* m_device; Com m_d3d12Device; Com m_d3d12Queue; }; } #ifndef _MSC_VER __CRT_UUID_DECL(ID3D11On12Device1_DXVK, 0xbdb64df4,0xea2f,0x4c70,0xb8,0x61,0xaa,0xab,0x12,0x58,0xbb,0x5d); #endif dxvk-2.6.1/src/d3d11/d3d11_on_12_interfaces.h000066400000000000000000000037171477473124000203060ustar00rootroot00000000000000#pragma once #include "../vulkan/vulkan_loader.h" #include MIDL_INTERFACE("39da4e09-bd1c-4198-9fae-86bbe3be41fd") ID3D12DXVKInteropDevice : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetDXGIAdapter( REFIID iid, void** ppvObject) = 0; virtual HRESULT STDMETHODCALLTYPE GetInstanceExtensions( UINT* pExtensionCount, const char** ppExtensions) = 0; virtual HRESULT STDMETHODCALLTYPE GetDeviceExtensions( UINT* pExtensionCount, const char** ppExtensions) = 0; virtual HRESULT STDMETHODCALLTYPE GetDeviceFeatures( const VkPhysicalDeviceFeatures2** ppFeatures) = 0; virtual HRESULT STDMETHODCALLTYPE GetVulkanHandles( VkInstance* pVkInstance, VkPhysicalDevice* pVkPhysicalDevice, VkDevice* pVkDevice) = 0; virtual HRESULT STDMETHODCALLTYPE GetVulkanQueueInfo( ID3D12CommandQueue* pCommandQueue, VkQueue* pVkQueue, UINT32* pVkQueueFamily) = 0; virtual void STDMETHODCALLTYPE GetVulkanImageLayout( ID3D12Resource* pResource, D3D12_RESOURCE_STATES State, VkImageLayout* pVkLayout) = 0; virtual HRESULT STDMETHODCALLTYPE GetVulkanResourceInfo( ID3D12Resource* pResource, UINT64* pVkHandle, UINT64* pBufferOffset) = 0; virtual HRESULT STDMETHODCALLTYPE LockCommandQueue( ID3D12CommandQueue* pCommandQueue) = 0; virtual HRESULT STDMETHODCALLTYPE UnlockCommandQueue( ID3D12CommandQueue* pCommandQueue) = 0; }; #ifndef _MSC_VER __CRT_UUID_DECL(ID3D12DXVKInteropDevice, 0x39da4e09, 0xbd1c, 0x4198, 0x9f,0xae, 0x86,0xbb,0xe3,0xbe,0x41,0xfd) #endif dxvk-2.6.1/src/d3d11/d3d11_options.cpp000066400000000000000000000063621477473124000172120ustar00rootroot00000000000000#include "../util/util_math.h" #include "d3d11_options.h" namespace dxvk { static bool IsAPITracingDXGI() { #ifdef _WIN32 return !!::GetModuleHandle("dxgitrace.dll"); #else return false; #endif } D3D11Options::D3D11Options(const Config& config) { this->zeroInitWorkgroupMemory = config.getOption("d3d11.zeroInitWorkgroupMemory", false); this->forceVolatileTgsmAccess = config.getOption("d3d11.forceVolatileTgsmAccess", false); this->forceComputeUavBarriers = config.getOption("d3d11.forceComputeUavBarriers", false); this->relaxedBarriers = config.getOption("d3d11.relaxedBarriers", false); this->relaxedGraphicsBarriers = config.getOption("d3d11.relaxedGraphicsBarriers", false); this->maxTessFactor = config.getOption("d3d11.maxTessFactor", 0); this->samplerAnisotropy = config.getOption("d3d11.samplerAnisotropy", -1); this->samplerLodBias = config.getOption("d3d11.samplerLodBias", 0.0f); this->clampNegativeLodBias = config.getOption("d3d11.clampNegativeLodBias", false); this->invariantPosition = config.getOption("d3d11.invariantPosition", true); this->floatControls = config.getOption("d3d11.floatControls", true); this->forceSampleRateShading = config.getOption("d3d11.forceSampleRateShading", false); this->disableMsaa = config.getOption("d3d11.disableMsaa", false); this->enableContextLock = config.getOption("d3d11.enableContextLock", false); this->deferSurfaceCreation = config.getOption("dxgi.deferSurfaceCreation", false); this->maxFrameLatency = config.getOption("dxgi.maxFrameLatency", 0); this->exposeDriverCommandLists = config.getOption("d3d11.exposeDriverCommandLists", true); this->reproducibleCommandStream = config.getOption("d3d11.reproducibleCommandStream", false); // Clamp LOD bias so that people don't abuse this in unintended ways this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f); auto cachedDynamicResources = config.getOption("d3d11.cachedDynamicResources", std::string()); if (IsAPITracingDXGI()) { // apitrace reads back all mapped resources on the CPU, so // allocating everything in cached memory is necessary to // achieve acceptable performance this->cachedDynamicResources = ~0u; } else { this->cachedDynamicResources = 0u; for (char c : cachedDynamicResources) { switch (c) { case 'c': this->cachedDynamicResources |= D3D11_BIND_CONSTANT_BUFFER; break; case 'v': this->cachedDynamicResources |= D3D11_BIND_VERTEX_BUFFER; break; case 'i': this->cachedDynamicResources |= D3D11_BIND_INDEX_BUFFER; break; case 'r': this->cachedDynamicResources |= D3D11_BIND_SHADER_RESOURCE; break; case 'a': this->cachedDynamicResources = ~0u; break; default: Logger::warn(str::format("Unknown flag for d3d11.cachedDynamicResources option: ", c)); } } } // Shader dump path is only available via an environment variable this->shaderDumpPath = env::getEnvVar("DXVK_SHADER_DUMP_PATH"); } } dxvk-2.6.1/src/d3d11/d3d11_options.h000066400000000000000000000073161477473124000166570ustar00rootroot00000000000000#pragma once #include "../util/config/config.h" #include "../dxgi/dxgi_options.h" #include "../dxvk/dxvk_device.h" #include "d3d11_include.h" namespace dxvk { struct D3D11Options { D3D11Options(const Config& config); /// Zero-initialize workgroup memory /// /// Workargound for games that don't initialize /// TGSM in compute shaders before reading it. bool zeroInitWorkgroupMemory = false; /// Force thread-group shared memory accesses to be volatile /// /// Workaround for compute shaders that read and /// write from the same shared memory location /// without explicit synchronization. bool forceVolatileTgsmAccess = false; /// Force UAV synchronization insided compute shaders /// /// Workaround for compute shaders that access overlapping /// memory regions within a UAV without proper workgroup /// synchroniation. Will have a negative performance impact. bool forceComputeUavBarriers = false; /// Use relaxed memory barriers /// /// May improve performance in some games, /// but might also cause rendering issues. bool relaxedBarriers = false; /// Ignore graphics barriers /// /// May improve performance in some games, /// but might also cause rendering issues. bool relaxedGraphicsBarriers = false; /// Maximum tessellation factor. /// /// Limits tessellation factors in tessellation /// control shaders. Values from 8 to 64 are /// supported, other values will be ignored. int32_t maxTessFactor = 0; /// Anisotropic filter override /// /// Enforces anisotropic filtering with the /// given anisotropy value for all samplers. int32_t samplerAnisotropy = -1; /// Mipmap LOD bias /// /// Enforces the given LOD bias for all samplers. float samplerLodBias = 0.0f; /// Clamps negative LOD bias bool clampNegativeLodBias = false; /// Declare vertex positions in shaders as invariant bool invariantPosition = true; /// Enable float control bits bool floatControls = true; /// Override maximum frame latency if the app specifies /// a higher value. May help with frame timing issues. int32_t maxFrameLatency = 0; /// Defer surface creation until first present call. This /// fixes issues with games that create multiple swap chains /// for a single window that may interfere with each other. bool deferSurfaceCreation = false; /// Enables sample rate shading by interpolating fragment shader /// inputs at the sample location rather than pixel center, /// unless otherwise specified by the application. bool forceSampleRateShading = false; /// Forces the sample count of all textures to be 1, and /// performs the required shader and resolve fixups. bool disableMsaa = false; /// Dynamic resources with the given bind flags will be allocated /// in cached system memory. Enabled automatically when recording /// an api trace. uint32_t cachedDynamicResources = 0; /// Always lock immediate context on every API call. May be /// useful for debugging purposes or when applications have /// race conditions. bool enableContextLock = false; /// Whether to expose the driver command list feature. Enabled by /// default and generally beneficial, but some games may assume that /// this is not supported when running on an AMD GPU. bool exposeDriverCommandLists = true; /// Ensure that for the same D3D commands the output VK commands /// don't change between runs. Useful for comparative benchmarking, /// can negatively affect performance. bool reproducibleCommandStream = false; /// Shader dump path std::string shaderDumpPath; }; } dxvk-2.6.1/src/d3d11/d3d11_query.cpp000066400000000000000000000261171477473124000166640ustar00rootroot00000000000000#include "d3d11_device.h" #include "d3d11_query.h" namespace dxvk { D3D11Query::D3D11Query( D3D11Device* device, const D3D11_QUERY_DESC1& desc) : D3D11DeviceChild(device), m_desc(desc), m_state(D3D11_VK_QUERY_INITIAL), m_d3d10(this) { Rc dxvkDevice = m_parent->GetDXVKDevice(); switch (m_desc.Query) { case D3D11_QUERY_EVENT: m_event[0] = dxvkDevice->createGpuEvent(); break; case D3D11_QUERY_OCCLUSION: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_OCCLUSION, VK_QUERY_CONTROL_PRECISE_BIT, 0); break; case D3D11_QUERY_OCCLUSION_PREDICATE: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_OCCLUSION, 0, 0); break; case D3D11_QUERY_TIMESTAMP: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_TIMESTAMP, 0, 0); break; case D3D11_QUERY_TIMESTAMP_DISJOINT: for (uint32_t i = 0; i < 2; i++) { m_query[i] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_TIMESTAMP, 0, 0); } break; case D3D11_QUERY_PIPELINE_STATISTICS: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_PIPELINE_STATISTICS, 0, 0); break; case D3D11_QUERY_SO_STATISTICS: case D3D11_QUERY_SO_STATISTICS_STREAM0: case D3D11_QUERY_SO_OVERFLOW_PREDICATE: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM0: // FIXME it is technically incorrect to map // SO_OVERFLOW_PREDICATE to the first stream, // but this is good enough for D3D10 behaviour m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 0, 0); break; case D3D11_QUERY_SO_STATISTICS_STREAM1: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM1: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 0, 1); break; case D3D11_QUERY_SO_STATISTICS_STREAM2: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM2: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 0, 2); break; case D3D11_QUERY_SO_STATISTICS_STREAM3: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM3: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 0, 3); break; default: throw DxvkError(str::format("D3D11: Unhandled query type: ", desc.Query)); } } D3D11Query::~D3D11Query() { } HRESULT STDMETHODCALLTYPE D3D11Query::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11Asynchronous) || riid == __uuidof(ID3D11Query) || riid == __uuidof(ID3D11Query1)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(ID3D10Asynchronous) || riid == __uuidof(ID3D10Query)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (m_desc.Query == D3D11_QUERY_OCCLUSION_PREDICATE) { if (riid == __uuidof(ID3D11Predicate)) { *ppvObject = AsPredicate(ref(this)); return S_OK; } if (riid == __uuidof(ID3D10Predicate)) { *ppvObject = ref(&m_d3d10); return S_OK; } } if (logQueryInterfaceError(__uuidof(ID3D11Query), riid)) { Logger::warn("D3D11Query: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } UINT STDMETHODCALLTYPE D3D11Query::GetDataSize() { switch (m_desc.Query) { case D3D11_QUERY_EVENT: return sizeof(BOOL); case D3D11_QUERY_OCCLUSION: return sizeof(UINT64); case D3D11_QUERY_TIMESTAMP: return sizeof(UINT64); case D3D11_QUERY_TIMESTAMP_DISJOINT: return sizeof(D3D11_QUERY_DATA_TIMESTAMP_DISJOINT); case D3D11_QUERY_PIPELINE_STATISTICS: return sizeof(D3D11_QUERY_DATA_PIPELINE_STATISTICS); case D3D11_QUERY_OCCLUSION_PREDICATE: return sizeof(BOOL); case D3D11_QUERY_SO_STATISTICS: case D3D11_QUERY_SO_STATISTICS_STREAM0: case D3D11_QUERY_SO_STATISTICS_STREAM1: case D3D11_QUERY_SO_STATISTICS_STREAM2: case D3D11_QUERY_SO_STATISTICS_STREAM3: return sizeof(D3D11_QUERY_DATA_SO_STATISTICS); case D3D11_QUERY_SO_OVERFLOW_PREDICATE: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM0: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM1: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM2: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM3: return sizeof(BOOL); } Logger::err("D3D11Query: Failed to query data size"); return 0; } void STDMETHODCALLTYPE D3D11Query::GetDesc(D3D11_QUERY_DESC* pDesc) { pDesc->Query = m_desc.Query; pDesc->MiscFlags = m_desc.MiscFlags; } void STDMETHODCALLTYPE D3D11Query::GetDesc1(D3D11_QUERY_DESC1* pDesc) { *pDesc = m_desc; } void D3D11Query::Begin(DxvkContext* ctx) { switch (m_desc.Query) { case D3D11_QUERY_EVENT: case D3D11_QUERY_TIMESTAMP: break; case D3D11_QUERY_TIMESTAMP_DISJOINT: ctx->writeTimestamp(m_query[1]); break; default: ctx->beginQuery(m_query[0]); } } void D3D11Query::End(DxvkContext* ctx) { switch (m_desc.Query) { case D3D11_QUERY_EVENT: ctx->signalGpuEvent(m_event[0]); break; case D3D11_QUERY_TIMESTAMP: case D3D11_QUERY_TIMESTAMP_DISJOINT: ctx->writeTimestamp(m_query[0]); break; default: ctx->endQuery(m_query[0]); } m_resetCtr.fetch_sub(1, std::memory_order_release); } bool STDMETHODCALLTYPE D3D11Query::DoBegin() { if (!IsScoped() || m_state == D3D11_VK_QUERY_BEGUN) return false; m_state = D3D11_VK_QUERY_BEGUN; return true; } bool STDMETHODCALLTYPE D3D11Query::DoEnd() { // Apparently the D3D11 runtime implicitly begins the query // if it is in the wrong state at the time End is called, so // let the caller react to it instead of just failing here. bool result = m_state == D3D11_VK_QUERY_BEGUN || !IsScoped(); m_state = D3D11_VK_QUERY_ENDED; m_resetCtr.fetch_add(1, std::memory_order_acquire); return result; } HRESULT STDMETHODCALLTYPE D3D11Query::GetData( void* pData, UINT GetDataFlags) { if (m_state != D3D11_VK_QUERY_ENDED) return DXGI_ERROR_INVALID_CALL; if (m_resetCtr != 0u) return S_FALSE; if (m_desc.Query == D3D11_QUERY_EVENT) { DxvkGpuEventStatus status = m_event[0]->test(); if (status == DxvkGpuEventStatus::Invalid) return DXGI_ERROR_INVALID_CALL; bool signaled = status == DxvkGpuEventStatus::Signaled; if (pData != nullptr) *static_cast(pData) = signaled; return signaled ? S_OK : S_FALSE; } else { std::array queryData = { }; for (uint32_t i = 0; i < MaxGpuQueries && m_query[i] != nullptr; i++) { DxvkGpuQueryStatus status = m_query[i]->getData(queryData[i]); if (status == DxvkGpuQueryStatus::Invalid || status == DxvkGpuQueryStatus::Failed) return DXGI_ERROR_INVALID_CALL; if (status == DxvkGpuQueryStatus::Pending) return S_FALSE; } if (pData == nullptr) return S_OK; switch (m_desc.Query) { case D3D11_QUERY_OCCLUSION: *static_cast(pData) = queryData[0].occlusion.samplesPassed; return S_OK; case D3D11_QUERY_OCCLUSION_PREDICATE: *static_cast(pData) = queryData[0].occlusion.samplesPassed != 0; return S_OK; case D3D11_QUERY_TIMESTAMP: *static_cast(pData) = queryData[0].timestamp.time; return S_OK; case D3D11_QUERY_TIMESTAMP_DISJOINT: { auto data = static_cast(pData); data->Frequency = GetTimestampQueryFrequency(); data->Disjoint = queryData[0].timestamp.time < queryData[1].timestamp.time; } return S_OK; case D3D11_QUERY_PIPELINE_STATISTICS: { auto data = static_cast(pData); data->IAVertices = queryData[0].statistic.iaVertices; data->IAPrimitives = queryData[0].statistic.iaPrimitives; data->VSInvocations = queryData[0].statistic.vsInvocations; data->GSInvocations = queryData[0].statistic.gsInvocations; data->GSPrimitives = queryData[0].statistic.gsPrimitives; data->CInvocations = queryData[0].statistic.clipInvocations; data->CPrimitives = queryData[0].statistic.clipPrimitives; data->PSInvocations = queryData[0].statistic.fsInvocations; data->HSInvocations = queryData[0].statistic.tcsPatches; data->DSInvocations = queryData[0].statistic.tesInvocations; data->CSInvocations = queryData[0].statistic.csInvocations; } return S_OK; case D3D11_QUERY_SO_STATISTICS: case D3D11_QUERY_SO_STATISTICS_STREAM0: case D3D11_QUERY_SO_STATISTICS_STREAM1: case D3D11_QUERY_SO_STATISTICS_STREAM2: case D3D11_QUERY_SO_STATISTICS_STREAM3: { auto data = static_cast(pData); data->NumPrimitivesWritten = queryData[0].xfbStream.primitivesWritten; data->PrimitivesStorageNeeded = queryData[0].xfbStream.primitivesNeeded; } return S_OK; case D3D11_QUERY_SO_OVERFLOW_PREDICATE: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM0: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM1: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM2: case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM3: { auto data = static_cast(pData); *data = queryData[0].xfbStream.primitivesNeeded > queryData[0].xfbStream.primitivesWritten; } return S_OK; default: Logger::err(str::format("D3D11: Unhandled query type in GetData: ", m_desc.Query)); return E_INVALIDARG; } } } UINT64 D3D11Query::GetTimestampQueryFrequency() const { Rc device = m_parent->GetDXVKDevice(); Rc adapter = device->adapter(); VkPhysicalDeviceLimits limits = adapter->deviceProperties().limits; return uint64_t(1'000'000'000.0f / limits.timestampPeriod); } HRESULT D3D11Query::ValidateDesc(const D3D11_QUERY_DESC1* pDesc) { if (pDesc->Query >= D3D11_QUERY_PIPELINE_STATISTICS && pDesc->ContextType > D3D11_CONTEXT_TYPE_3D) return E_INVALIDARG; return S_OK; } } dxvk-2.6.1/src/d3d11/d3d11_query.h000066400000000000000000000055571477473124000163360ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_gpu_event.h" #include "../dxvk/dxvk_gpu_query.h" #include "../d3d10/d3d10_query.h" #include "d3d11_device_child.h" namespace dxvk { enum D3D11_VK_QUERY_STATE : uint32_t { D3D11_VK_QUERY_INITIAL, D3D11_VK_QUERY_BEGUN, D3D11_VK_QUERY_ENDED, }; class D3D11Query : public D3D11DeviceChild { constexpr static uint32_t MaxGpuQueries = 2; constexpr static uint32_t MaxGpuEvents = 1; public: D3D11Query( D3D11Device* device, const D3D11_QUERY_DESC1& desc); ~D3D11Query(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) final; UINT STDMETHODCALLTYPE GetDataSize(); void STDMETHODCALLTYPE GetDesc(D3D11_QUERY_DESC* pDesc) final; void STDMETHODCALLTYPE GetDesc1(D3D11_QUERY_DESC1* pDesc) final; void Begin(DxvkContext* ctx); void End(DxvkContext* ctx); bool STDMETHODCALLTYPE DoBegin(); bool STDMETHODCALLTYPE DoEnd(); HRESULT STDMETHODCALLTYPE GetData( void* pData, UINT GetDataFlags); void DoDeferredEnd() { m_state = D3D11_VK_QUERY_ENDED; m_resetCtr.fetch_add(1, std::memory_order_acquire); } bool IsScoped() const { return m_desc.Query != D3D11_QUERY_EVENT && m_desc.Query != D3D11_QUERY_TIMESTAMP; } bool IsEvent() const { return m_desc.Query == D3D11_QUERY_EVENT; } bool TrackStalls() const { return m_desc.Query == D3D11_QUERY_EVENT || m_desc.Query == D3D11_QUERY_TIMESTAMP || m_desc.Query == D3D11_QUERY_TIMESTAMP_DISJOINT; } bool IsStalling() const { return m_stallFlag; } void NotifyEnd() { m_stallMask <<= 1; } void NotifyStall() { m_stallMask |= 1; m_stallFlag |= bit::popcnt(m_stallMask) >= 16; } D3D10Query* GetD3D10Iface() { return &m_d3d10; } static HRESULT ValidateDesc(const D3D11_QUERY_DESC1* pDesc); static ID3D11Predicate* AsPredicate(ID3D11Query* pQuery) { // ID3D11Predicate and ID3D11Query have the same vtable. This // saves us some headache in all query-related functions. return static_cast(pQuery); } static D3D11Query* FromPredicate(ID3D11Predicate* pPredicate) { return static_cast(static_cast(pPredicate)); } private: D3D11_QUERY_DESC1 m_desc; D3D11_VK_QUERY_STATE m_state; std::array, MaxGpuQueries> m_query; std::array, MaxGpuEvents> m_event; D3D10Query m_d3d10; uint32_t m_stallMask = 0; bool m_stallFlag = false; std::atomic m_resetCtr = { 0u }; UINT64 GetTimestampQueryFrequency() const; }; } dxvk-2.6.1/src/d3d11/d3d11_rasterizer.cpp000066400000000000000000000171011477473124000177020ustar00rootroot00000000000000#include "d3d11_device.h" #include "d3d11_rasterizer.h" namespace dxvk { D3D11RasterizerState::D3D11RasterizerState( D3D11Device* device, const D3D11_RASTERIZER_DESC2& desc) : D3D11StateObject(device), m_desc(desc), m_d3d10(this) { // Polygon mode. Determines whether the rasterizer fills // a polygon or renders lines connecting the vertices. switch (desc.FillMode) { default: case D3D11_FILL_SOLID: m_state.setPolygonMode(VK_POLYGON_MODE_FILL); break; case D3D11_FILL_WIREFRAME: m_state.setPolygonMode(VK_POLYGON_MODE_LINE); break; } // Face culling properties. The rasterizer may discard // polygons that are facing towards or away from the // viewer, depending on the options below. switch (desc.CullMode) { default: case D3D11_CULL_NONE: m_state.setCullMode(VK_CULL_MODE_NONE); break; case D3D11_CULL_FRONT: m_state.setCullMode(VK_CULL_MODE_FRONT_BIT); break; case D3D11_CULL_BACK: m_state.setCullMode(VK_CULL_MODE_BACK_BIT); break; } m_state.setFrontFace(desc.FrontCounterClockwise ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE); // In the backend we treat depth bias as a dynamic state because // some games like to put random/uninitialized numbers here, but // we do not need to enable it in case the parameters are both 0. m_state.setDepthBias(desc.DepthBias != 0 || desc.SlopeScaledDepthBias != 0.0f); m_state.setDepthClip(desc.DepthClipEnable); m_state.setConservativeMode(DecodeConservativeRasterizationMode(desc.ConservativeRaster)); m_state.setSampleCount(desc.ForcedSampleCount); m_state.setFlatShading(false); m_state.setLineMode(VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT); if (m_state.depthBias()) { m_depthBias.depthBiasConstant = float(desc.DepthBias); m_depthBias.depthBiasSlope = desc.SlopeScaledDepthBias; m_depthBias.depthBiasClamp = desc.DepthBiasClamp; } // Set up line rasterization mode const auto& features = device->GetDXVKDevice()->features(); if (desc.MultisampleEnable) { if (features.extLineRasterization.rectangularLines) m_state.setLineMode(VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT); } else if (desc.AntialiasedLineEnable) { if (features.extLineRasterization.smoothLines) m_state.setLineMode(VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT); } } D3D11RasterizerState::~D3D11RasterizerState() { } HRESULT STDMETHODCALLTYPE D3D11RasterizerState::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11RasterizerState) || riid == __uuidof(ID3D11RasterizerState1) || riid == __uuidof(ID3D11RasterizerState2)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(ID3D10RasterizerState)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11RasterizerState), riid)) { Logger::warn("D3D11RasterizerState::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11RasterizerState::GetDesc(D3D11_RASTERIZER_DESC* pDesc) { pDesc->FillMode = m_desc.FillMode; pDesc->CullMode = m_desc.CullMode; pDesc->FrontCounterClockwise = m_desc.FrontCounterClockwise; pDesc->DepthBias = m_desc.DepthBias; pDesc->DepthBiasClamp = m_desc.DepthBiasClamp; pDesc->SlopeScaledDepthBias = m_desc.SlopeScaledDepthBias; pDesc->DepthClipEnable = m_desc.DepthClipEnable; pDesc->ScissorEnable = m_desc.ScissorEnable; pDesc->MultisampleEnable = m_desc.MultisampleEnable; pDesc->AntialiasedLineEnable = m_desc.AntialiasedLineEnable; } void STDMETHODCALLTYPE D3D11RasterizerState::GetDesc1(D3D11_RASTERIZER_DESC1* pDesc) { pDesc->FillMode = m_desc.FillMode; pDesc->CullMode = m_desc.CullMode; pDesc->FrontCounterClockwise = m_desc.FrontCounterClockwise; pDesc->DepthBias = m_desc.DepthBias; pDesc->DepthBiasClamp = m_desc.DepthBiasClamp; pDesc->SlopeScaledDepthBias = m_desc.SlopeScaledDepthBias; pDesc->DepthClipEnable = m_desc.DepthClipEnable; pDesc->ScissorEnable = m_desc.ScissorEnable; pDesc->MultisampleEnable = m_desc.MultisampleEnable; pDesc->AntialiasedLineEnable = m_desc.AntialiasedLineEnable; pDesc->ForcedSampleCount = m_desc.ForcedSampleCount; } void STDMETHODCALLTYPE D3D11RasterizerState::GetDesc2(D3D11_RASTERIZER_DESC2* pDesc) { *pDesc = m_desc; } D3D11_RASTERIZER_DESC2 D3D11RasterizerState::PromoteDesc( const D3D11_RASTERIZER_DESC* pSrcDesc) { D3D11_RASTERIZER_DESC2 dstDesc; dstDesc.FillMode = pSrcDesc->FillMode; dstDesc.CullMode = pSrcDesc->CullMode; dstDesc.FrontCounterClockwise = pSrcDesc->FrontCounterClockwise; dstDesc.DepthBias = pSrcDesc->DepthBias; dstDesc.DepthBiasClamp = pSrcDesc->DepthBiasClamp; dstDesc.SlopeScaledDepthBias = pSrcDesc->SlopeScaledDepthBias; dstDesc.DepthClipEnable = pSrcDesc->DepthClipEnable; dstDesc.ScissorEnable = pSrcDesc->ScissorEnable; dstDesc.MultisampleEnable = pSrcDesc->MultisampleEnable; dstDesc.AntialiasedLineEnable = pSrcDesc->AntialiasedLineEnable; dstDesc.ForcedSampleCount = 0; dstDesc.ConservativeRaster = D3D11_CONSERVATIVE_RASTERIZATION_MODE_OFF; return dstDesc; } D3D11_RASTERIZER_DESC2 D3D11RasterizerState::PromoteDesc( const D3D11_RASTERIZER_DESC1* pSrcDesc) { D3D11_RASTERIZER_DESC2 dstDesc; dstDesc.FillMode = pSrcDesc->FillMode; dstDesc.CullMode = pSrcDesc->CullMode; dstDesc.FrontCounterClockwise = pSrcDesc->FrontCounterClockwise; dstDesc.DepthBias = pSrcDesc->DepthBias; dstDesc.DepthBiasClamp = pSrcDesc->DepthBiasClamp; dstDesc.SlopeScaledDepthBias = pSrcDesc->SlopeScaledDepthBias; dstDesc.DepthClipEnable = pSrcDesc->DepthClipEnable; dstDesc.ScissorEnable = pSrcDesc->ScissorEnable; dstDesc.MultisampleEnable = pSrcDesc->MultisampleEnable; dstDesc.AntialiasedLineEnable = pSrcDesc->AntialiasedLineEnable; dstDesc.ForcedSampleCount = 0; dstDesc.ConservativeRaster = D3D11_CONSERVATIVE_RASTERIZATION_MODE_OFF; return dstDesc; } HRESULT D3D11RasterizerState::NormalizeDesc( D3D11_RASTERIZER_DESC2* pDesc) { if (pDesc->FillMode < D3D11_FILL_WIREFRAME || pDesc->FillMode > D3D11_FILL_SOLID) return E_INVALIDARG; if (pDesc->CullMode < D3D11_CULL_NONE || pDesc->CullMode > D3D11_CULL_BACK) return E_INVALIDARG; if (pDesc->FrontCounterClockwise) pDesc->FrontCounterClockwise = TRUE; if (pDesc->DepthClipEnable) pDesc->DepthClipEnable = TRUE; if (pDesc->ScissorEnable) pDesc->ScissorEnable = TRUE; if (pDesc->MultisampleEnable) pDesc->MultisampleEnable = TRUE; if (pDesc->AntialiasedLineEnable) pDesc->AntialiasedLineEnable = TRUE; if (pDesc->ForcedSampleCount != 0) { if (FAILED(DecodeSampleCount(pDesc->ForcedSampleCount, nullptr))) return E_INVALIDARG; } return S_OK; } } dxvk-2.6.1/src/d3d11/d3d11_rasterizer.h000066400000000000000000000031551477473124000173530ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_device.h" #include "../d3d10/d3d10_rasterizer.h" #include "d3d11_device_child.h" namespace dxvk { class D3D11Device; class D3D11RasterizerState : public D3D11StateObject { public: using DescType = D3D11_RASTERIZER_DESC2; D3D11RasterizerState( D3D11Device* device, const D3D11_RASTERIZER_DESC2& desc); ~D3D11RasterizerState(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) final; void STDMETHODCALLTYPE GetDesc( D3D11_RASTERIZER_DESC* pDesc) final; void STDMETHODCALLTYPE GetDesc1( D3D11_RASTERIZER_DESC1* pDesc) final; void STDMETHODCALLTYPE GetDesc2( D3D11_RASTERIZER_DESC2* pDesc) final; const D3D11_RASTERIZER_DESC2* Desc() const { return &m_desc; } DxvkRasterizerState GetState() const { return m_state; } DxvkDepthBias GetDepthBias() const { return m_depthBias; } D3D10RasterizerState* GetD3D10Iface() { return &m_d3d10; } static D3D11_RASTERIZER_DESC2 PromoteDesc( const D3D11_RASTERIZER_DESC* pDesc); static D3D11_RASTERIZER_DESC2 PromoteDesc( const D3D11_RASTERIZER_DESC1* pDesc); static HRESULT NormalizeDesc( D3D11_RASTERIZER_DESC2* pDesc); private: D3D11_RASTERIZER_DESC2 m_desc; DxvkRasterizerState m_state = { }; DxvkDepthBias m_depthBias = { }; D3D10RasterizerState m_d3d10; }; } dxvk-2.6.1/src/d3d11/d3d11_resource.cpp000066400000000000000000000327301477473124000173440ustar00rootroot00000000000000#include "d3d11_buffer.h" #include "d3d11_texture.h" #include "d3d11_resource.h" #include "d3d11_context_imm.h" #include "d3d11_device.h" #include "../util/util_shared_res.h" namespace dxvk { D3D11DXGIKeyedMutex::D3D11DXGIKeyedMutex( ID3D11Resource* pResource, D3D11Device* pDevice) : m_resource(pResource), m_device(pDevice) { m_supported = m_device->GetDXVKDevice()->features().khrWin32KeyedMutex && m_device->GetDXVKDevice()->vkd()->wine_vkAcquireKeyedMutex != nullptr && m_device->GetDXVKDevice()->vkd()->wine_vkReleaseKeyedMutex != nullptr; } D3D11DXGIKeyedMutex::~D3D11DXGIKeyedMutex() { } ULONG STDMETHODCALLTYPE D3D11DXGIKeyedMutex::AddRef() { return m_resource->AddRef(); } ULONG STDMETHODCALLTYPE D3D11DXGIKeyedMutex::Release() { return m_resource->Release(); } HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::QueryInterface( REFIID riid, void** ppvObject) { return m_resource->QueryInterface(riid, ppvObject); } HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::GetPrivateData( REFGUID Name, UINT* pDataSize, void* pData) { return m_resource->GetPrivateData(Name, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::SetPrivateData( REFGUID Name, UINT DataSize, const void* pData) { return m_resource->SetPrivateData(Name, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::SetPrivateDataInterface( REFGUID Name, const IUnknown* pUnknown) { return m_resource->SetPrivateDataInterface(Name, pUnknown); } HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::GetParent( REFIID riid, void** ppParent) { return GetDevice(riid, ppParent); } HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::GetDevice( REFIID riid, void** ppDevice) { Com device; m_resource->GetDevice(&device); return device->QueryInterface(riid, ppDevice); } HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::AcquireSync( UINT64 Key, DWORD dwMilliseconds) { if (!m_supported) { if (!m_warned) { m_warned = true; Logger::err("D3D11DXGIKeyedMutex::AcquireSync: Not supported"); } return S_OK; } D3D11CommonTexture* texture = GetCommonTexture(m_resource); Rc dxvkDevice = m_device->GetDXVKDevice(); VkResult vr = dxvkDevice->vkd()->wine_vkAcquireKeyedMutex( dxvkDevice->handle(), texture->GetImage()->getMemoryInfo().memory, Key, dwMilliseconds); switch (vr) { case VK_SUCCESS: return S_OK; case VK_TIMEOUT: return WAIT_TIMEOUT; default: return DXGI_ERROR_INVALID_CALL; } } HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::ReleaseSync( UINT64 Key) { if (!m_supported) return S_OK; D3D11CommonTexture* texture = GetCommonTexture(m_resource); Rc dxvkDevice = m_device->GetDXVKDevice(); { D3D11ImmediateContext* context = m_device->GetContext(); D3D10Multithread& multithread = context->GetMultithread(); static bool s_errorShown = false; if (!multithread.GetMultithreadProtected() && !std::exchange(s_errorShown, true)) Logger::warn("D3D11DXGIKeyedMutex::ReleaseSync: Called without context locking enabled."); D3D10DeviceLock lock = context->LockContext(); context->WaitForResource(*texture->GetImage(), DxvkCsThread::SynchronizeAll, D3D11_MAP_READ_WRITE, 0); } VkResult vr = dxvkDevice->vkd()->wine_vkReleaseKeyedMutex( dxvkDevice->handle(), texture->GetImage()->getMemoryInfo().memory, Key); return vr == VK_SUCCESS ? S_OK : DXGI_ERROR_INVALID_CALL; } D3D11DXGIResource::D3D11DXGIResource( ID3D11Resource* pResource, D3D11Device* pDevice) : m_resource(pResource), m_keyedMutex(pResource, pDevice) { } D3D11DXGIResource::~D3D11DXGIResource() { } ULONG STDMETHODCALLTYPE D3D11DXGIResource::AddRef() { return m_resource->AddRef(); } ULONG STDMETHODCALLTYPE D3D11DXGIResource::Release() { return m_resource->Release(); } HRESULT STDMETHODCALLTYPE D3D11DXGIResource::QueryInterface( REFIID riid, void** ppvObject) { return m_resource->QueryInterface(riid, ppvObject); } HRESULT STDMETHODCALLTYPE D3D11DXGIResource::GetPrivateData( REFGUID Name, UINT* pDataSize, void* pData) { return m_resource->GetPrivateData(Name, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D11DXGIResource::SetPrivateData( REFGUID Name, UINT DataSize, const void* pData) { return m_resource->SetPrivateData(Name, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D11DXGIResource::SetPrivateDataInterface( REFGUID Name, const IUnknown* pUnknown) { return m_resource->SetPrivateDataInterface(Name, pUnknown); } HRESULT STDMETHODCALLTYPE D3D11DXGIResource::GetParent( REFIID riid, void** ppParent) { return GetDevice(riid, ppParent); } HRESULT STDMETHODCALLTYPE D3D11DXGIResource::GetDevice( REFIID riid, void** ppDevice) { Com device; m_resource->GetDevice(&device); return device->QueryInterface(riid, ppDevice); } HRESULT STDMETHODCALLTYPE D3D11DXGIResource::GetEvictionPriority( UINT* pEvictionPriority) { *pEvictionPriority = m_resource->GetEvictionPriority(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGIResource::GetSharedHandle( HANDLE* pSharedHandle) { auto texture = GetCommonTexture(m_resource); if (texture == nullptr || pSharedHandle == nullptr || (texture->Desc()->MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE)) return E_INVALIDARG; if (!(texture->Desc()->MiscFlags & (D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX))) { *pSharedHandle = NULL; return S_OK; } HANDLE kmtHandle = texture->GetImage()->sharedHandle(); if (kmtHandle == INVALID_HANDLE_VALUE) return E_INVALIDARG; *pSharedHandle = kmtHandle; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGIResource::GetUsage( DXGI_USAGE* pUsage) { D3D11_COMMON_RESOURCE_DESC desc; HRESULT hr = GetCommonResourceDesc(m_resource, &desc); if (FAILED(hr)) return hr; DXGI_USAGE usage = desc.DxgiUsage; switch (desc.Usage) { case D3D11_USAGE_IMMUTABLE: usage |= DXGI_CPU_ACCESS_NONE; break; case D3D11_USAGE_DEFAULT: usage |= DXGI_CPU_ACCESS_NONE; break; case D3D11_USAGE_DYNAMIC: usage |= DXGI_CPU_ACCESS_DYNAMIC; break; case D3D11_USAGE_STAGING: usage |= DXGI_CPU_ACCESS_READ_WRITE; break; } // TODO add flags for swap chain back buffers if (desc.BindFlags & (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_CONSTANT_BUFFER)) usage |= DXGI_USAGE_SHADER_INPUT; if (desc.BindFlags & D3D11_BIND_RENDER_TARGET) usage |= DXGI_USAGE_RENDER_TARGET_OUTPUT; if (desc.BindFlags & D3D11_BIND_UNORDERED_ACCESS) usage |= DXGI_USAGE_UNORDERED_ACCESS; *pUsage = usage; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGIResource::SetEvictionPriority( UINT EvictionPriority) { m_resource->SetEvictionPriority(EvictionPriority); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGIResource::CreateSharedHandle( const SECURITY_ATTRIBUTES* pAttributes, DWORD dwAccess, LPCWSTR lpName, HANDLE* pHandle) { auto texture = GetCommonTexture(m_resource); if (pHandle) *pHandle = nullptr; if (texture == nullptr || pHandle == nullptr || !(texture->Desc()->MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE)) return E_INVALIDARG; if (lpName) Logger::warn("Naming shared resources not supported"); HANDLE handle = texture->GetImage()->sharedHandle(); if (handle == INVALID_HANDLE_VALUE) return E_INVALIDARG; *pHandle = handle; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGIResource::CreateSubresourceSurface( UINT index, IDXGISurface2** ppSurface) { InitReturnPtr(ppSurface); Logger::err("D3D11DXGIResource::CreateSubresourceSurface: Stub"); return E_NOTIMPL; } HRESULT D3D11DXGIResource::GetKeyedMutex( void **ppvObject) { auto texture = GetCommonTexture(m_resource); if (texture == nullptr || !(texture->Desc()->MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX)) return E_NOINTERFACE; *ppvObject = ref(&m_keyedMutex); return S_OK; } HRESULT GetResource11on12Info( ID3D11Resource* pResource, D3D11_ON_12_RESOURCE_INFO* p11on12Info) { auto buffer = GetCommonBuffer (pResource); auto texture = GetCommonTexture(pResource); if (buffer != nullptr) *p11on12Info = buffer->Get11on12Info(); else if (texture != nullptr) *p11on12Info = texture->Get11on12Info(); else return E_INVALIDARG; if (p11on12Info->Resource == nullptr) return E_INVALIDARG; return S_OK; } HRESULT GetCommonResourceDesc( ID3D11Resource* pResource, D3D11_COMMON_RESOURCE_DESC* pDesc) { auto buffer = GetCommonBuffer (pResource); auto texture = GetCommonTexture(pResource); if (buffer != nullptr) { pDesc->Dim = D3D11_RESOURCE_DIMENSION_BUFFER; pDesc->Format = DXGI_FORMAT_UNKNOWN; pDesc->Usage = buffer->Desc()->Usage; pDesc->BindFlags = buffer->Desc()->BindFlags; pDesc->CPUAccessFlags = buffer->Desc()->CPUAccessFlags; pDesc->MiscFlags = buffer->Desc()->MiscFlags; pDesc->DxgiUsage = 0; return S_OK; } else if (texture != nullptr) { pResource->GetType(&pDesc->Dim); pDesc->Format = texture->Desc()->Format; pDesc->Usage = texture->Desc()->Usage; pDesc->BindFlags = texture->Desc()->BindFlags; pDesc->CPUAccessFlags = texture->Desc()->CPUAccessFlags; pDesc->MiscFlags = texture->Desc()->MiscFlags; pDesc->DxgiUsage = texture->GetDxgiUsage(); return S_OK; } else { pDesc->Dim = D3D11_RESOURCE_DIMENSION_UNKNOWN; pDesc->Format = DXGI_FORMAT_UNKNOWN; pDesc->Usage = D3D11_USAGE_DEFAULT; pDesc->BindFlags = 0; pDesc->CPUAccessFlags = 0; pDesc->MiscFlags = 0; pDesc->DxgiUsage = 0; return E_INVALIDARG; } } Rc GetPagedResource( ID3D11Resource* pResource) { auto texture = GetCommonTexture(pResource); if (texture) return texture->GetImage(); return static_cast(pResource)->GetBuffer(); } BOOL CheckResourceViewCompatibility( ID3D11Resource* pResource, UINT BindFlags, DXGI_FORMAT Format, UINT Plane) { auto texture = GetCommonTexture(pResource); auto buffer = GetCommonBuffer (pResource); return texture != nullptr ? texture->CheckViewCompatibility(BindFlags, Format, Plane) : buffer ->CheckViewCompatibility(BindFlags, Format); } HRESULT ResourceAddRefPrivate(ID3D11Resource* pResource, D3D11_RESOURCE_DIMENSION Type) { switch (Type) { case D3D11_RESOURCE_DIMENSION_BUFFER: static_cast (pResource)->AddRefPrivate(); return S_OK; case D3D11_RESOURCE_DIMENSION_TEXTURE1D: static_cast(pResource)->AddRefPrivate(); return S_OK; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: static_cast(pResource)->AddRefPrivate(); return S_OK; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: static_cast(pResource)->AddRefPrivate(); return S_OK; default: return E_INVALIDARG; } } HRESULT ResourceAddRefPrivate(ID3D11Resource* pResource) { D3D11_RESOURCE_DIMENSION dim; pResource->GetType(&dim); return ResourceAddRefPrivate(pResource, dim); } HRESULT ResourceReleasePrivate(ID3D11Resource* pResource, D3D11_RESOURCE_DIMENSION Type) { switch (Type) { case D3D11_RESOURCE_DIMENSION_BUFFER: static_cast (pResource)->ReleasePrivate(); return S_OK; case D3D11_RESOURCE_DIMENSION_TEXTURE1D: static_cast(pResource)->ReleasePrivate(); return S_OK; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: static_cast(pResource)->ReleasePrivate(); return S_OK; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: static_cast(pResource)->ReleasePrivate(); return S_OK; default: return E_INVALIDARG; } } HRESULT ResourceReleasePrivate(ID3D11Resource* pResource) { D3D11_RESOURCE_DIMENSION dim; pResource->GetType(&dim); return ResourceReleasePrivate(pResource, dim); } } dxvk-2.6.1/src/d3d11/d3d11_resource.h000066400000000000000000000233151477473124000170100ustar00rootroot00000000000000#pragma once #include "d3d11_include.h" namespace dxvk { /** * \brief Common resource description * * Stores the usage and bind flags of a resource * Can be used to quickly determine whether it is * legal to create a view for a given resource. */ struct D3D11_COMMON_RESOURCE_DESC { D3D11_RESOURCE_DIMENSION Dim; DXGI_FORMAT Format; D3D11_USAGE Usage; UINT BindFlags; UINT CPUAccessFlags; UINT MiscFlags; UINT DxgiUsage; }; /** * \brief IDXGIKeyedMutex implementation */ class D3D11DXGIKeyedMutex : public IDXGIKeyedMutex { public: D3D11DXGIKeyedMutex( ID3D11Resource* pResource, D3D11Device* pDevice); ~D3D11DXGIKeyedMutex(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID Name, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID Name, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID Name, const IUnknown* pUnknown); HRESULT STDMETHODCALLTYPE GetParent( REFIID riid, void** ppParent); HRESULT STDMETHODCALLTYPE GetDevice( REFIID riid, void** ppDevice); HRESULT STDMETHODCALLTYPE AcquireSync( UINT64 Key, DWORD dwMilliseconds); HRESULT STDMETHODCALLTYPE ReleaseSync( UINT64 Key); private: ID3D11Resource* m_resource; D3D11Device* m_device; bool m_warned = false; bool m_supported = false; }; /** * \brief IDXGIResource implementation for D3D11 resources */ class D3D11DXGIResource : public IDXGIResource1 { public: D3D11DXGIResource( ID3D11Resource* pResource, D3D11Device* pDevice); ~D3D11DXGIResource(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID Name, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID Name, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID Name, const IUnknown* pUnknown); HRESULT STDMETHODCALLTYPE GetParent( REFIID riid, void** ppParent); HRESULT STDMETHODCALLTYPE GetDevice( REFIID riid, void** ppDevice); HRESULT STDMETHODCALLTYPE GetEvictionPriority( UINT* pEvictionPriority); HRESULT STDMETHODCALLTYPE GetSharedHandle( HANDLE* pSharedHandle); HRESULT STDMETHODCALLTYPE GetUsage( DXGI_USAGE* pUsage); HRESULT STDMETHODCALLTYPE SetEvictionPriority( UINT EvictionPriority); HRESULT STDMETHODCALLTYPE CreateSharedHandle( const SECURITY_ATTRIBUTES* pAttributes, DWORD dwAccess, LPCWSTR lpName, HANDLE* pHandle); HRESULT STDMETHODCALLTYPE CreateSubresourceSurface( UINT index, IDXGISurface2** ppSurface); HRESULT GetKeyedMutex(void **ppvObject); private: ID3D11Resource* m_resource; D3D11DXGIKeyedMutex m_keyedMutex; }; /** * \brief Queries D3D11on12 resource info * * \param [in] pResource The resource to query * \param [out] p11on12Info 11on12 info * \returns \c S_OK on success, or \c E_INVALIDARG */ HRESULT GetResource11on12Info( ID3D11Resource* pResource, D3D11_ON_12_RESOURCE_INFO* p11on12Info); /** * \brief Queries common resource description * * \param [in] pResource The resource to query * \param [out] pDesc Resource description * \returns \c S_OK on success, or \c E_INVALIDARG */ HRESULT GetCommonResourceDesc( ID3D11Resource* pResource, D3D11_COMMON_RESOURCE_DESC* pDesc); /** * \brief Checks whether a format can be used to view a resource * * Depending on whether the resource is a buffer or a * texture, certain restrictions apply on which formats * can be used to view the resource. * \param [in] pResource The resource to check * \param [in] BindFlags Bind flags required for the view * \param [in] Format The desired view format * \param [in] Plane Plane slice for planar formats * \returns \c true if the format is compatible */ BOOL CheckResourceViewCompatibility( ID3D11Resource* pResource, UINT BindFlags, DXGI_FORMAT Format, UINT Plane); /** * \brief Queries paged resource from resource pointer * * \param [in] resource The resource * \returns Paged resource object */ Rc GetPagedResource( ID3D11Resource* pResource); /** * \brief Increments private reference count of a resource * * Helper method that figures out the exact type of * the resource and calls its \c AddRefPrivate method. * \param [in] pResource The resource to reference * \param [in] Type Resource type * \returns \c S_OK, or \c E_INVALIDARG for an invalid resource */ HRESULT ResourceAddRefPrivate( ID3D11Resource* pResource, D3D11_RESOURCE_DIMENSION Type); HRESULT ResourceAddRefPrivate( ID3D11Resource* pResource); /** * \brief Decrements private reference count of a resource * * Helper method that figures out the exact type of * the resource and calls its \c ReleasePrivate method. * \param [in] pResource The resource to reference * \param [in] Type Resource type * \returns \c S_OK, or \c E_INVALIDARG for an invalid resource */ HRESULT ResourceReleasePrivate( ID3D11Resource* pResource, D3D11_RESOURCE_DIMENSION Type); HRESULT ResourceReleasePrivate( ID3D11Resource* pResource); /** * \brief Typed private resource pointer * * Stores a resource and its type, in order to avoid * unnecessary GetType calls. Also optionally stores * a subresource index to avoid struct padding. */ class D3D11ResourceRef { public: D3D11ResourceRef() : m_type(D3D11_RESOURCE_DIMENSION_UNKNOWN), m_subresource(0), m_resource(nullptr) { } D3D11ResourceRef(ID3D11Resource* pResource) : D3D11ResourceRef(pResource, 0) { } D3D11ResourceRef(ID3D11Resource* pResource, UINT Subresource) : m_type(D3D11_RESOURCE_DIMENSION_UNKNOWN), m_subresource(Subresource), m_resource(pResource) { if (m_resource) { m_resource->GetType(&m_type); ResourceAddRefPrivate(m_resource, m_type); } } D3D11ResourceRef(ID3D11Resource* pResource, UINT Subresource, D3D11_RESOURCE_DIMENSION Type) : m_type(Type), m_subresource(Subresource), m_resource(pResource) { if (m_resource) ResourceAddRefPrivate(m_resource, m_type); } D3D11ResourceRef(D3D11ResourceRef&& other) : m_type(other.m_type), m_subresource(other.m_subresource), m_resource(other.m_resource) { other.m_type = D3D11_RESOURCE_DIMENSION_UNKNOWN; other.m_subresource = 0; other.m_resource = nullptr; } D3D11ResourceRef(const D3D11ResourceRef& other) : m_type(other.m_type), m_subresource(other.m_subresource), m_resource(other.m_resource) { if (m_resource) ResourceAddRefPrivate(m_resource, m_type); } ~D3D11ResourceRef() { if (m_resource) ResourceReleasePrivate(m_resource, m_type); } D3D11ResourceRef& operator = (D3D11ResourceRef&& other) { if (m_resource) ResourceReleasePrivate(m_resource, m_type); m_type = other.m_type; m_subresource = other.m_subresource; m_resource = other.m_resource; other.m_type = D3D11_RESOURCE_DIMENSION_UNKNOWN; other.m_subresource = 0; other.m_resource = nullptr; return *this; } D3D11ResourceRef& operator = (const D3D11ResourceRef& other) { if (other.m_resource) ResourceAddRefPrivate(other.m_resource, other.m_type); if (m_resource) ResourceReleasePrivate(m_resource, m_type); m_type = other.m_type; m_subresource = other.m_subresource; m_resource = other.m_resource; return *this; } D3D11_RESOURCE_DIMENSION GetType() const { return m_type; } UINT GetSubresource() const { return m_subresource; } ID3D11Resource* Get() const { return m_resource; } private: D3D11_RESOURCE_DIMENSION m_type; UINT m_subresource; ID3D11Resource* m_resource; }; } dxvk-2.6.1/src/d3d11/d3d11_sampler.cpp000066400000000000000000000112641477473124000171570ustar00rootroot00000000000000#include "d3d11_device.h" #include "d3d11_sampler.h" #include "d3d11_util.h" namespace dxvk { D3D11SamplerState::D3D11SamplerState( D3D11Device* device, const D3D11_SAMPLER_DESC& desc) : D3D11StateObject(device), m_desc(desc), m_d3d10(this) { DxvkSamplerKey info = { }; // While D3D11_FILTER is technically an enum, its value bits // can be used to decode the filter properties more efficiently. const uint32_t filterBits = uint32_t(desc.Filter); VkFilter minFilter = (filterBits & 0x10) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; VkFilter magFilter = (filterBits & 0x04) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; info.setFilter(minFilter, magFilter, (filterBits & 0x01) ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST); // Enforce LOD bias specified in the device options float lodBias = desc.MipLODBias; if (minFilter == VK_FILTER_LINEAR && magFilter == VK_FILTER_LINEAR) { lodBias += device->GetOptions()->samplerLodBias; if (device->GetOptions()->clampNegativeLodBias) lodBias = std::max(lodBias, 0.0f); } info.setLodRange(desc.MinLOD, desc.MaxLOD, lodBias); // Enforce anisotropy specified in the device options uint32_t anisotropy = (filterBits & 0x40) ? desc.MaxAnisotropy : 0u; int32_t samplerAnisotropyOption = device->GetOptions()->samplerAnisotropy; if (samplerAnisotropyOption >= 0 && minFilter == VK_FILTER_LINEAR) anisotropy = samplerAnisotropyOption; info.setAniso(anisotropy); // Set up the remaining properties, which are // stored directly in the sampler description info.setAddressModes( DecodeAddressMode(desc.AddressU), DecodeAddressMode(desc.AddressV), DecodeAddressMode(desc.AddressW)); info.setDepthCompare((filterBits & 0x180) == 0x80, DecodeCompareOp(desc.ComparisonFunc)); info.setReduction(DecodeReductionMode(filterBits)); for (uint32_t i = 0; i < 4; i++) info.borderColor.float32[i] = desc.BorderColor[i]; m_sampler = device->GetDXVKDevice()->createSampler(info); } D3D11SamplerState::~D3D11SamplerState() { } HRESULT STDMETHODCALLTYPE D3D11SamplerState::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11SamplerState)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(ID3D10SamplerState)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11SamplerState), riid)) { Logger::warn("D3D11SamplerState::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11SamplerState::GetDesc(D3D11_SAMPLER_DESC* pDesc) { *pDesc = m_desc; } HRESULT D3D11SamplerState::NormalizeDesc(D3D11_SAMPLER_DESC* pDesc) { const uint32_t filterBits = uint32_t(pDesc->Filter); if (filterBits & 0xFFFFFE2A) { Logger::err(str::format( "D3D11SamplerState: Unhandled filter: ", filterBits)); return E_INVALIDARG; } if (pDesc->MaxAnisotropy > 16) { return E_INVALIDARG; } else if ((filterBits & 0x40) == 0 /* not anisotropic */) { // Reset anisotropy if it is not used pDesc->MaxAnisotropy = 0; } if ((filterBits & 0x180) == 0x80 /* compare-to-depth */) { if (!ValidateComparisonFunc(pDesc->ComparisonFunc)) return E_INVALIDARG; } else { // Reset compare func if it is not used pDesc->ComparisonFunc = D3D11_COMPARISON_NEVER; } if (!ValidateAddressMode(pDesc->AddressU) || !ValidateAddressMode(pDesc->AddressV) || !ValidateAddressMode(pDesc->AddressW)) return E_INVALIDARG; // Clear BorderColor to 0 if none of the address // modes are D3D11_TEXTURE_ADDRESS_BORDER if (pDesc->AddressU != D3D11_TEXTURE_ADDRESS_BORDER && pDesc->AddressV != D3D11_TEXTURE_ADDRESS_BORDER && pDesc->AddressW != D3D11_TEXTURE_ADDRESS_BORDER) { for (int i = 0; i < 4; i++) pDesc->BorderColor[i] = 0.0f; } return S_OK; } bool D3D11SamplerState::ValidateAddressMode(D3D11_TEXTURE_ADDRESS_MODE Mode) { return Mode >= D3D11_TEXTURE_ADDRESS_WRAP && Mode <= D3D11_TEXTURE_ADDRESS_MIRROR_ONCE; } bool D3D11SamplerState::ValidateComparisonFunc(D3D11_COMPARISON_FUNC Comparison) { return Comparison >= D3D11_COMPARISON_NEVER && Comparison <= D3D11_COMPARISON_ALWAYS; } } dxvk-2.6.1/src/d3d11/d3d11_sampler.h000066400000000000000000000023021477473124000166150ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_device.h" #include "../d3d10/d3d10_sampler.h" #include "d3d11_device_child.h" namespace dxvk { class D3D11Device; class D3D11SamplerState : public D3D11StateObject { public: using DescType = D3D11_SAMPLER_DESC; D3D11SamplerState( D3D11Device* device, const D3D11_SAMPLER_DESC& desc); ~D3D11SamplerState(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) final; void STDMETHODCALLTYPE GetDesc( D3D11_SAMPLER_DESC* pDesc) final; Rc GetDXVKSampler() const { return m_sampler; } D3D10SamplerState* GetD3D10Iface() { return &m_d3d10; } static HRESULT NormalizeDesc( D3D11_SAMPLER_DESC* pDesc); private: D3D11_SAMPLER_DESC m_desc; Rc m_sampler; D3D10SamplerState m_d3d10; std::atomic m_refCount = { 0u }; static bool ValidateAddressMode( D3D11_TEXTURE_ADDRESS_MODE Mode); static bool ValidateComparisonFunc( D3D11_COMPARISON_FUNC Comparison); }; } dxvk-2.6.1/src/d3d11/d3d11_shader.cpp000066400000000000000000000133151477473124000167610ustar00rootroot00000000000000#include "d3d11_device.h" #include "d3d11_shader.h" namespace dxvk { D3D11CommonShader:: D3D11CommonShader() { } D3D11CommonShader::~D3D11CommonShader() { } D3D11CommonShader::D3D11CommonShader( D3D11Device* pDevice, const DxvkShaderKey* pShaderKey, const DxbcModuleInfo* pDxbcModuleInfo, const void* pShaderBytecode, size_t BytecodeLength) { const std::string name = pShaderKey->toString(); Logger::debug(str::format("Compiling shader ", name)); DxbcReader reader( reinterpret_cast(pShaderBytecode), BytecodeLength); // If requested by the user, dump both the raw DXBC // shader and the compiled SPIR-V module to a file. const std::string& dumpPath = pDevice->GetOptions()->shaderDumpPath; if (dumpPath.size() != 0) { reader.store(std::ofstream(str::topath(str::format(dumpPath, "/", name, ".dxbc").c_str()).c_str(), std::ios_base::binary | std::ios_base::trunc)); } // Error out if the shader is invalid DxbcModule module(reader); auto programInfo = module.programInfo(); if (!programInfo) throw DxvkError("Invalid shader binary."); // Decide whether we need to create a pass-through // geometry shader for vertex shader stream output bool passthroughShader = pDxbcModuleInfo->xfb != nullptr && (programInfo->type() == DxbcProgramType::VertexShader || programInfo->type() == DxbcProgramType::DomainShader); if (programInfo->shaderStage() != pShaderKey->type() && !passthroughShader) throw DxvkError("Mismatching shader type."); m_shader = passthroughShader ? module.compilePassthroughShader(*pDxbcModuleInfo, name) : module.compile (*pDxbcModuleInfo, name); m_shader->setShaderKey(*pShaderKey); if (dumpPath.size() != 0) { std::ofstream dumpStream( str::topath(str::format(dumpPath, "/", name, ".spv").c_str()).c_str(), std::ios_base::binary | std::ios_base::trunc); m_shader->dump(dumpStream); } // Create shader constant buffer if necessary auto icb = module.icbInfo(); if (icb.size) { DxvkBufferCreateInfo info = { }; info.size = align(icb.size, 256u); info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; info.stages = util::pipelineStages(m_shader->info().stage); info.access = VK_ACCESS_UNIFORM_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; info.debugName = "Icb"; m_buffer = pDevice->GetDXVKDevice()->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); // Upload immediate constant buffer to VRAM pDevice->InitShaderIcb(this, icb.size, icb.data); } pDevice->GetDXVKDevice()->registerShader(m_shader); // Write back binding mask auto bindings = module.bindings(); if (bindings) m_bindings = *bindings; } D3D11ShaderModuleSet:: D3D11ShaderModuleSet() { } D3D11ShaderModuleSet::~D3D11ShaderModuleSet() { } HRESULT D3D11ShaderModuleSet::GetShaderModule( D3D11Device* pDevice, const DxvkShaderKey* pShaderKey, const DxbcModuleInfo* pDxbcModuleInfo, const void* pShaderBytecode, size_t BytecodeLength, D3D11CommonShader* pShader) { // Use the shader's unique key for the lookup { std::unique_lock lock(m_mutex); auto entry = m_modules.find(*pShaderKey); if (entry != m_modules.end()) { *pShader = entry->second; return S_OK; } } // This shader has not been compiled yet, so we have to create a // new module. This takes a while, so we won't lock the structure. D3D11CommonShader module; try { module = D3D11CommonShader(pDevice, pShaderKey, pDxbcModuleInfo, pShaderBytecode, BytecodeLength); } catch (const DxvkError& e) { Logger::err(e.message()); return E_INVALIDARG; } // Insert the new module into the lookup table. If another thread // has compiled the same shader in the meantime, we should return // that object instead and discard the newly created module. { std::unique_lock lock(m_mutex); auto status = m_modules.insert({ *pShaderKey, module }); if (!status.second) { *pShader = status.first->second; return S_OK; } } *pShader = std::move(module); return S_OK; } D3D11ExtShader::D3D11ExtShader( ID3D11DeviceChild* pParent, D3D11CommonShader* pShader) : m_parent(pParent), m_shader(pShader) { } D3D11ExtShader::~D3D11ExtShader() { } ULONG STDMETHODCALLTYPE D3D11ExtShader::AddRef() { return m_parent->AddRef(); } ULONG STDMETHODCALLTYPE D3D11ExtShader::Release() { return m_parent->Release(); } HRESULT STDMETHODCALLTYPE D3D11ExtShader::QueryInterface( REFIID riid, void** ppvObject) { return m_parent->QueryInterface(riid, ppvObject); } HRESULT STDMETHODCALLTYPE D3D11ExtShader::GetSpirvCode( SIZE_T* pCodeSize, void* pCode) { auto shader = m_shader->GetShader(); auto code = shader->getRawCode(); HRESULT hr = S_OK; if (pCode) { size_t size = code.size(); if (size > *pCodeSize) { size = *pCodeSize; hr = S_FALSE; } std::memcpy(pCode, code.data(), size); *pCodeSize = size; return hr; } else { *pCodeSize = code.size(); return hr; } } } dxvk-2.6.1/src/d3d11/d3d11_shader.h000066400000000000000000000120161477473124000164230ustar00rootroot00000000000000#pragma once #include #include #include "../dxbc/dxbc_module.h" #include "../dxvk/dxvk_device.h" #include "../d3d10/d3d10_shader.h" #include "../util/sha1/sha1_util.h" #include "../util/util_env.h" #include "d3d11_device_child.h" #include "d3d11_interfaces.h" namespace dxvk { class D3D11Device; /** * \brief Common shader object * * Stores the compiled SPIR-V shader and the SHA-1 * hash of the original DXBC shader, which can be * used to identify the shader. */ class D3D11CommonShader { public: D3D11CommonShader(); D3D11CommonShader( D3D11Device* pDevice, const DxvkShaderKey* pShaderKey, const DxbcModuleInfo* pDxbcModuleInfo, const void* pShaderBytecode, size_t BytecodeLength); ~D3D11CommonShader(); Rc GetShader() const { return m_shader; } DxvkBufferSlice GetIcb() const { return m_buffer != nullptr ? DxvkBufferSlice(m_buffer) : DxvkBufferSlice(); } std::string GetName() const { return m_shader->debugName(); } DxbcBindingMask GetBindingMask() const { return m_bindings; } private: Rc m_shader; Rc m_buffer; DxbcBindingMask m_bindings = { }; }; /** * \brief Extended shader interface */ class D3D11ExtShader : public ID3D11VkExtShader { public: D3D11ExtShader( ID3D11DeviceChild* pParent, D3D11CommonShader* pShader); ~D3D11ExtShader(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE GetSpirvCode( SIZE_T* pCodeSize, void* pCode); private: ID3D11DeviceChild* m_parent; D3D11CommonShader* m_shader; }; /** * \brief Common shader interface * * Implements methods for all D3D11*Shader * interfaces and stores the actual shader * module object. */ template class D3D11Shader : public D3D11DeviceChild { using D3D10ShaderClass = D3D10Shader; public: D3D11Shader(D3D11Device* device, const D3D11CommonShader& shader) : D3D11DeviceChild(device), m_shader(shader), m_d3d10(this), m_shaderExt(this, &m_shader) { } ~D3D11Shader() { } HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) final { *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(D3D11Interface)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(D3D10Interface)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (riid == __uuidof(ID3D11VkExtShader)) { *ppvObject = ref(&m_shaderExt); return S_OK; } if (logQueryInterfaceError(__uuidof(D3D11Interface), riid)) { Logger::warn("D3D11Shader::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } const D3D11CommonShader* GetCommonShader() const { return &m_shader; } D3D10ShaderClass* GetD3D10Iface() { return &m_d3d10; } private: D3D11CommonShader m_shader; D3D10ShaderClass m_d3d10; D3D11ExtShader m_shaderExt; }; using D3D11VertexShader = D3D11Shader; using D3D11HullShader = D3D11Shader; using D3D11DomainShader = D3D11Shader; using D3D11GeometryShader = D3D11Shader; using D3D11PixelShader = D3D11Shader; using D3D11ComputeShader = D3D11Shader; /** * \brief Shader module set * * Some applications may compile the same shader multiple * times, so we should cache the resulting shader modules * and reuse them rather than creating new ones. This * class is thread-safe. */ class D3D11ShaderModuleSet { public: D3D11ShaderModuleSet(); ~D3D11ShaderModuleSet(); HRESULT GetShaderModule( D3D11Device* pDevice, const DxvkShaderKey* pShaderKey, const DxbcModuleInfo* pDxbcModuleInfo, const void* pShaderBytecode, size_t BytecodeLength, D3D11CommonShader* pShader); private: dxvk::mutex m_mutex; std::unordered_map< DxvkShaderKey, D3D11CommonShader, DxvkHash, DxvkEq> m_modules; }; } dxvk-2.6.1/src/d3d11/d3d11_state.cpp000066400000000000000000000150241477473124000166320ustar00rootroot00000000000000#include "d3d11_state.h" namespace dxvk { size_t D3D11StateDescHash::operator () ( const D3D11_BLEND_DESC1& desc) const { DxvkHashState hash; hash.add(desc.AlphaToCoverageEnable); hash.add(desc.IndependentBlendEnable); // Render targets 1 to 7 are ignored and may contain // undefined data if independent blend is disabled const uint32_t usedRenderTargets = desc.IndependentBlendEnable ? 8 : 1; for (uint32_t i = 0; i < usedRenderTargets; i++) hash.add(this->operator () (desc.RenderTarget[i])); return hash; } size_t D3D11StateDescHash::operator () ( const D3D11_DEPTH_STENCILOP_DESC& desc) const { DxvkHashState hash; hash.add(desc.StencilFunc); hash.add(desc.StencilDepthFailOp); hash.add(desc.StencilPassOp); hash.add(desc.StencilFailOp); return hash; } size_t D3D11StateDescHash::operator () ( const D3D11_DEPTH_STENCIL_DESC& desc) const { DxvkHashState hash; hash.add(desc.DepthEnable); hash.add(desc.DepthWriteMask); hash.add(desc.DepthFunc); hash.add(desc.StencilEnable); hash.add(desc.StencilReadMask); hash.add(desc.StencilWriteMask); hash.add(this->operator () (desc.FrontFace)); hash.add(this->operator () (desc.BackFace)); return hash; } size_t D3D11StateDescHash::operator () ( const D3D11_RASTERIZER_DESC2& desc) const { std::hash fhash; DxvkHashState hash; hash.add(desc.FillMode); hash.add(desc.CullMode); hash.add(desc.FrontCounterClockwise); hash.add(desc.DepthBias); hash.add(fhash(desc.SlopeScaledDepthBias)); hash.add(fhash(desc.DepthBiasClamp)); hash.add(desc.DepthClipEnable); hash.add(desc.ScissorEnable); hash.add(desc.MultisampleEnable); hash.add(desc.AntialiasedLineEnable); hash.add(desc.ForcedSampleCount); hash.add(desc.ConservativeRaster); return hash; } size_t D3D11StateDescHash::operator () ( const D3D11_RENDER_TARGET_BLEND_DESC1& desc) const { DxvkHashState hash; hash.add(desc.BlendEnable); hash.add(desc.LogicOpEnable); hash.add(desc.SrcBlend); hash.add(desc.DestBlend); hash.add(desc.BlendOp); hash.add(desc.SrcBlendAlpha); hash.add(desc.DestBlendAlpha); hash.add(desc.BlendOpAlpha); hash.add(desc.LogicOp); hash.add(desc.RenderTargetWriteMask); return hash; } size_t D3D11StateDescHash::operator () ( const D3D11_SAMPLER_DESC& desc) const { std::hash fhash; DxvkHashState hash; hash.add(desc.Filter); hash.add(desc.AddressU); hash.add(desc.AddressV); hash.add(desc.AddressW); hash.add(fhash(desc.MipLODBias)); hash.add(desc.MaxAnisotropy); hash.add(desc.ComparisonFunc); for (uint32_t i = 0; i < 4; i++) hash.add(fhash(desc.BorderColor[i])); hash.add(fhash(desc.MinLOD)); hash.add(fhash(desc.MaxLOD)); return hash; } bool D3D11StateDescEqual::operator () ( const D3D11_BLEND_DESC1& a, const D3D11_BLEND_DESC1& b) const { bool eq = a.AlphaToCoverageEnable == b.AlphaToCoverageEnable && a.IndependentBlendEnable == b.IndependentBlendEnable; // Render targets 1 to 7 are ignored and may contain // undefined data if independent blend is disabled const uint32_t usedRenderTargets = a.IndependentBlendEnable ? 8 : 1; for (uint32_t i = 0; eq && (i < usedRenderTargets); i++) eq &= this->operator () (a.RenderTarget[i], b.RenderTarget[i]); return eq; } bool D3D11StateDescEqual::operator () ( const D3D11_DEPTH_STENCILOP_DESC& a, const D3D11_DEPTH_STENCILOP_DESC& b) const { return a.StencilFunc == b.StencilFunc && a.StencilDepthFailOp == b.StencilDepthFailOp && a.StencilPassOp == b.StencilPassOp && a.StencilFailOp == b.StencilFailOp; } bool D3D11StateDescEqual::operator () ( const D3D11_DEPTH_STENCIL_DESC& a, const D3D11_DEPTH_STENCIL_DESC& b) const { return a.DepthEnable == b.DepthEnable && a.DepthWriteMask == b.DepthWriteMask && a.DepthFunc == b.DepthFunc && a.StencilEnable == b.StencilEnable && a.StencilReadMask == b.StencilReadMask && a.StencilWriteMask == b.StencilWriteMask && this->operator () (a.FrontFace, b.FrontFace) && this->operator () (a.BackFace, b.BackFace); } bool D3D11StateDescEqual::operator () ( const D3D11_RASTERIZER_DESC2& a, const D3D11_RASTERIZER_DESC2& b) const { return a.FillMode == b.FillMode && a.CullMode == b.CullMode && a.FrontCounterClockwise == b.FrontCounterClockwise && a.DepthBias == b.DepthBias && a.SlopeScaledDepthBias == b.SlopeScaledDepthBias && a.DepthBiasClamp == b.DepthBiasClamp && a.DepthClipEnable == b.DepthClipEnable && a.ScissorEnable == b.ScissorEnable && a.MultisampleEnable == b.MultisampleEnable && a.AntialiasedLineEnable == b.AntialiasedLineEnable && a.ForcedSampleCount == b.ForcedSampleCount && a.ConservativeRaster == b.ConservativeRaster; } bool D3D11StateDescEqual::operator () ( const D3D11_RENDER_TARGET_BLEND_DESC1& a, const D3D11_RENDER_TARGET_BLEND_DESC1& b) const { return a.BlendEnable == b.BlendEnable && a.LogicOpEnable == b.LogicOpEnable && a.SrcBlend == b.SrcBlend && a.DestBlend == b.DestBlend && a.BlendOp == b.BlendOp && a.SrcBlendAlpha == b.SrcBlendAlpha && a.DestBlendAlpha == b.DestBlendAlpha && a.BlendOpAlpha == b.BlendOpAlpha && a.LogicOp == b.LogicOp && a.RenderTargetWriteMask == b.RenderTargetWriteMask; } bool D3D11StateDescEqual::operator () ( const D3D11_SAMPLER_DESC& a, const D3D11_SAMPLER_DESC& b) const { return a.Filter == b.Filter && a.AddressU == b.AddressU && a.AddressV == b.AddressV && a.AddressW == b.AddressW && a.MipLODBias == b.MipLODBias && a.MaxAnisotropy == b.MaxAnisotropy && a.ComparisonFunc == b.ComparisonFunc && a.BorderColor[0] == b.BorderColor[0] && a.BorderColor[1] == b.BorderColor[1] && a.BorderColor[2] == b.BorderColor[2] && a.BorderColor[3] == b.BorderColor[3] && a.MinLOD == b.MinLOD && a.MaxLOD == b.MaxLOD; } }dxvk-2.6.1/src/d3d11/d3d11_state.h000066400000000000000000000047741477473124000163110ustar00rootroot00000000000000#pragma once #include #include "d3d11_blend.h" #include "d3d11_depth_stencil.h" #include "d3d11_rasterizer.h" #include "d3d11_sampler.h" namespace dxvk { class D3D11Device; struct D3D11StateDescHash { size_t operator () (const D3D11_BLEND_DESC1& desc) const; size_t operator () (const D3D11_DEPTH_STENCILOP_DESC& desc) const; size_t operator () (const D3D11_DEPTH_STENCIL_DESC& desc) const; size_t operator () (const D3D11_RASTERIZER_DESC2& desc) const; size_t operator () (const D3D11_RENDER_TARGET_BLEND_DESC1& desc) const; size_t operator () (const D3D11_SAMPLER_DESC& desc) const; }; struct D3D11StateDescEqual { bool operator () (const D3D11_BLEND_DESC1& a, const D3D11_BLEND_DESC1& b) const; bool operator () (const D3D11_DEPTH_STENCILOP_DESC& a, const D3D11_DEPTH_STENCILOP_DESC& b) const; bool operator () (const D3D11_DEPTH_STENCIL_DESC& a, const D3D11_DEPTH_STENCIL_DESC& b) const; bool operator () (const D3D11_RASTERIZER_DESC2& a, const D3D11_RASTERIZER_DESC2& b) const; bool operator () (const D3D11_RENDER_TARGET_BLEND_DESC1& a, const D3D11_RENDER_TARGET_BLEND_DESC1& b) const; bool operator () (const D3D11_SAMPLER_DESC& a, const D3D11_SAMPLER_DESC& b) const; }; /** * \brief Unique state object set * * When creating state objects, D3D11 first checks if * an object with the same description already exists * and returns it if that is the case. This class * implements that behaviour. */ template class D3D11StateObjectSet { using DescType = typename T::DescType; public: /** * \brief Retrieves a state object * * Returns an object with the same description or * creates a new one if no such object exists. * \param [in] device The calling D3D11 device * \param [in] desc State object description * \returns Pointer to the state object */ T* Create(D3D11Device* device, const DescType& desc) { std::lock_guard lock(m_mutex); auto entry = m_objects.find(desc); if (entry != m_objects.end()) return ref(&entry->second); auto result = m_objects.emplace( std::piecewise_construct, std::tuple(desc), std::tuple(device, desc)); return ref(&result.first->second); } private: dxvk::mutex m_mutex; std::unordered_map m_objects; }; } dxvk-2.6.1/src/d3d11/d3d11_state_object.cpp000066400000000000000000000016511477473124000201610ustar00rootroot00000000000000#include "d3d11_state_object.h" namespace dxvk { D3D11DeviceContextState::D3D11DeviceContextState( D3D11Device* pDevice) : D3D11DeviceChild(pDevice) { } D3D11DeviceContextState::~D3D11DeviceContextState() { } HRESULT STDMETHODCALLTYPE D3D11DeviceContextState::QueryInterface( REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3DDeviceContextState)) { *ppvObject = ref(this); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3DDeviceContextState), riid)) { Logger::warn("D3D11DeviceContextState::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } } dxvk-2.6.1/src/d3d11/d3d11_state_object.h000066400000000000000000000017031477473124000176240ustar00rootroot00000000000000#pragma once #include "d3d11_device.h" #include "d3d11_context_state.h" #include "d3d11_device_child.h" namespace dxvk { /** * \brief Device context state implementation * * This is an opaque interface in D3D11, and we only * implement the state block-like functionality, not * the methods to disable certain context and device * interfaces based on the emulated device IID. */ class D3D11DeviceContextState : public D3D11DeviceChild { public: D3D11DeviceContextState( D3D11Device* pDevice); ~D3D11DeviceContextState(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); void SetState(const D3D11ContextState& State) { m_state = State; } void GetState(D3D11ContextState& State) const { State = m_state; } private: D3D11ContextState m_state; }; } dxvk-2.6.1/src/d3d11/d3d11_swapchain.cpp000066400000000000000000000535551477473124000175020ustar00rootroot00000000000000#include "d3d11_context_imm.h" #include "d3d11_device.h" #include "d3d11_swapchain.h" #include "../dxvk/dxvk_latency_builtin.h" #include "../util/util_win32_compat.h" namespace dxvk { static uint16_t MapGammaControlPoint(float x) { if (x < 0.0f) x = 0.0f; if (x > 1.0f) x = 1.0f; return uint16_t(65535.0f * x); } static VkColorSpaceKHR ConvertColorSpace(DXGI_COLOR_SPACE_TYPE colorspace) { switch (colorspace) { case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709: return VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; case DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020: return VK_COLOR_SPACE_HDR10_ST2084_EXT; case DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709: return VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT; default: Logger::warn(str::format("DXGI: ConvertColorSpace: Unknown colorspace ", colorspace)); return VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; } } static VkXYColorEXT ConvertXYColor(const UINT16 (&dxgiColor)[2]) { return VkXYColorEXT{ float(dxgiColor[0]) / 50000.0f, float(dxgiColor[1]) / 50000.0f }; } static float ConvertMaxLuminance(UINT dxgiLuminance) { return float(dxgiLuminance); } static float ConvertMinLuminance(UINT dxgiLuminance) { return float(dxgiLuminance) * 0.0001f; } static float ConvertLevel(UINT16 dxgiLevel) { return float(dxgiLevel); } static VkHdrMetadataEXT ConvertHDRMetadata(const DXGI_HDR_METADATA_HDR10& dxgiMetadata) { VkHdrMetadataEXT vkMetadata = { VK_STRUCTURE_TYPE_HDR_METADATA_EXT }; vkMetadata.displayPrimaryRed = ConvertXYColor(dxgiMetadata.RedPrimary); vkMetadata.displayPrimaryGreen = ConvertXYColor(dxgiMetadata.GreenPrimary); vkMetadata.displayPrimaryBlue = ConvertXYColor(dxgiMetadata.BluePrimary); vkMetadata.whitePoint = ConvertXYColor(dxgiMetadata.WhitePoint); vkMetadata.maxLuminance = ConvertMaxLuminance(dxgiMetadata.MaxMasteringLuminance); vkMetadata.minLuminance = ConvertMinLuminance(dxgiMetadata.MinMasteringLuminance); vkMetadata.maxContentLightLevel = ConvertLevel(dxgiMetadata.MaxContentLightLevel); vkMetadata.maxFrameAverageLightLevel = ConvertLevel(dxgiMetadata.MaxFrameAverageLightLevel); return vkMetadata; } D3D11SwapChain::D3D11SwapChain( D3D11DXGIDevice* pContainer, D3D11Device* pDevice, IDXGIVkSurfaceFactory* pSurfaceFactory, const DXGI_SWAP_CHAIN_DESC1* pDesc) : m_dxgiDevice(pContainer), m_parent(pDevice), m_surfaceFactory(pSurfaceFactory), m_desc(*pDesc), m_device(pDevice->GetDXVKDevice()), m_frameLatencyCap(pDevice->GetOptions()->maxFrameLatency) { CreateFrameLatencyEvent(); CreatePresenter(); CreateBackBuffers(); CreateBlitter(); } D3D11SwapChain::~D3D11SwapChain() { // Avoids hanging when in this state, see comment // in DxvkDevice::~DxvkDevice. if (this_thread::isInModuleDetachment()) return; m_presenter->destroyResources(); DestroyFrameLatencyEvent(); DestroyLatencyTracker(); } HRESULT STDMETHODCALLTYPE D3D11SwapChain::QueryInterface( REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; InitReturnPtr(ppvObject); if (riid == __uuidof(IUnknown) || riid == __uuidof(IDXGIVkSwapChain) || riid == __uuidof(IDXGIVkSwapChain1) || riid == __uuidof(IDXGIVkSwapChain2)) { *ppvObject = ref(this); return S_OK; } if (logQueryInterfaceError(__uuidof(IDXGIVkSwapChain), riid)) { Logger::warn("D3D11SwapChain::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE D3D11SwapChain::GetDesc( DXGI_SWAP_CHAIN_DESC1* pDesc) { *pDesc = m_desc; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11SwapChain::GetAdapter( REFIID riid, void** ppvObject) { return m_dxgiDevice->GetParent(riid, ppvObject); } HRESULT STDMETHODCALLTYPE D3D11SwapChain::GetDevice( REFIID riid, void** ppDevice) { return m_dxgiDevice->QueryInterface(riid, ppDevice); } HRESULT STDMETHODCALLTYPE D3D11SwapChain::GetImage( UINT BufferId, REFIID riid, void** ppBuffer) { InitReturnPtr(ppBuffer); if (BufferId >= m_backBuffers.size()) { Logger::err("D3D11: GetImage: Invalid buffer ID"); return DXGI_ERROR_UNSUPPORTED; } return m_backBuffers[BufferId]->QueryInterface(riid, ppBuffer); } UINT STDMETHODCALLTYPE D3D11SwapChain::GetImageIndex() { return 0; } UINT STDMETHODCALLTYPE D3D11SwapChain::GetFrameLatency() { return m_frameLatency; } HANDLE STDMETHODCALLTYPE D3D11SwapChain::GetFrameLatencyEvent() { HANDLE result = nullptr; HANDLE processHandle = GetCurrentProcess(); if (!DuplicateHandle(processHandle, m_frameLatencyEvent, processHandle, &result, 0, FALSE, DUPLICATE_SAME_ACCESS)) { Logger::err("DxgiSwapChain::GetFrameLatencyWaitableObject: DuplicateHandle failed"); return nullptr; } return result; } HRESULT STDMETHODCALLTYPE D3D11SwapChain::ChangeProperties( const DXGI_SWAP_CHAIN_DESC1* pDesc, const UINT* pNodeMasks, IUnknown* const* ppPresentQueues) { if (m_desc.Format != pDesc->Format) m_presenter->setSurfaceFormat(GetSurfaceFormat(pDesc->Format)); if (m_desc.Width != pDesc->Width || m_desc.Height != pDesc->Height) m_presenter->setSurfaceExtent({ m_desc.Width, m_desc.Height }); m_desc = *pDesc; CreateBackBuffers(); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetPresentRegion( const RECT* pRegion) { // TODO implement return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetGammaControl( UINT NumControlPoints, const DXGI_RGB* pControlPoints) { bool isIdentity = true; if (NumControlPoints > 1) { std::array cp; if (NumControlPoints > cp.size()) return E_INVALIDARG; for (uint32_t i = 0; i < NumControlPoints; i++) { uint16_t identity = MapGammaControlPoint(float(i) / float(NumControlPoints - 1)); cp[i].r = MapGammaControlPoint(pControlPoints[i].Red); cp[i].g = MapGammaControlPoint(pControlPoints[i].Green); cp[i].b = MapGammaControlPoint(pControlPoints[i].Blue); cp[i].a = 0; isIdentity &= cp[i].r == identity && cp[i].g == identity && cp[i].b == identity; } if (!isIdentity) m_blitter->setGammaRamp(NumControlPoints, cp.data()); } if (isIdentity) m_blitter->setGammaRamp(0, nullptr); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetFrameLatency( UINT MaxLatency) { if (MaxLatency == 0 || MaxLatency > DXGI_MAX_SWAP_CHAIN_BUFFERS) return DXGI_ERROR_INVALID_CALL; if (m_frameLatencyEvent) { // Windows DXGI does not seem to handle the case where the new maximum // latency is less than the current value, and some games relying on // this behaviour will hang if we attempt to decrement the semaphore. // Thus, only increment the semaphore as necessary. if (MaxLatency > m_frameLatency) ReleaseSemaphore(m_frameLatencyEvent, MaxLatency - m_frameLatency, nullptr); } m_frameLatency = MaxLatency; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11SwapChain::Present( UINT SyncInterval, UINT PresentFlags, const DXGI_PRESENT_PARAMETERS* pPresentParameters) { HRESULT hr = S_OK; if (m_device->getDeviceStatus() != VK_SUCCESS) hr = DXGI_ERROR_DEVICE_RESET; if (PresentFlags & DXGI_PRESENT_TEST) { if (hr != S_OK) return hr; VkResult status = m_presenter->checkSwapChainStatus(); return status == VK_SUCCESS ? S_OK : DXGI_STATUS_OCCLUDED; } if (hr != S_OK) { SyncFrameLatency(); return hr; } try { hr = PresentImage(SyncInterval); } catch (const DxvkError& e) { Logger::err(e.message()); hr = E_FAIL; } // Ensure to synchronize and release the frame latency semaphore // even if presentation failed with STATUS_OCCLUDED, or otherwise // applications using the semaphore may deadlock. This works because // we do not increment the frame ID in those situations. SyncFrameLatency(); // Ignore latency stuff if presentation failed DxvkLatencyStats latencyStats = { }; if (hr == S_OK && m_latency) { latencyStats = m_latency->getStatistics(m_frameId); m_latency->sleepAndBeginFrame(m_frameId + 1, std::abs(m_targetFrameRate)); } if (m_latencyHud) m_latencyHud->accumulateStats(latencyStats); return hr; } UINT STDMETHODCALLTYPE D3D11SwapChain::CheckColorSpaceSupport( DXGI_COLOR_SPACE_TYPE ColorSpace) { UINT supportFlags = 0; VkColorSpaceKHR vkColorSpace = ConvertColorSpace(ColorSpace); if (m_presenter->supportsColorSpace(vkColorSpace)) supportFlags |= DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT; return supportFlags; } HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetColorSpace( DXGI_COLOR_SPACE_TYPE ColorSpace) { VkColorSpaceKHR colorSpace = ConvertColorSpace(ColorSpace); if (!m_presenter->supportsColorSpace(colorSpace)) return E_INVALIDARG; m_colorSpace = colorSpace; m_presenter->setSurfaceFormat(GetSurfaceFormat(m_desc.Format)); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetHDRMetaData( const DXGI_VK_HDR_METADATA* pMetaData) { // For some reason this call always seems to succeed on Windows if (pMetaData->Type == DXGI_HDR_METADATA_TYPE_HDR10) m_presenter->setHdrMetadata(ConvertHDRMetadata(pMetaData->HDR10)); return S_OK; } void STDMETHODCALLTYPE D3D11SwapChain::GetLastPresentCount( UINT64* pLastPresentCount) { *pLastPresentCount = UINT64(m_frameId - DXGI_MAX_SWAP_CHAIN_BUFFERS); } void STDMETHODCALLTYPE D3D11SwapChain::GetFrameStatistics( DXGI_VK_FRAME_STATISTICS* pFrameStatistics) { std::lock_guard lock(m_frameStatisticsLock); *pFrameStatistics = m_frameStatistics; } void STDMETHODCALLTYPE D3D11SwapChain::SetTargetFrameRate( double FrameRate) { m_targetFrameRate = FrameRate; if (m_presenter != nullptr) m_presenter->setFrameRateLimit(m_targetFrameRate, GetActualFrameLatency()); } Rc D3D11SwapChain::GetBackBufferView() { Rc image = GetCommonTexture(m_backBuffers[0].ptr())->GetImage(); DxvkImageViewKey key; key.viewType = VK_IMAGE_VIEW_TYPE_2D; key.usage = VK_IMAGE_USAGE_SAMPLED_BIT; key.format = image->info().format; key.aspects = VK_IMAGE_ASPECT_COLOR_BIT; key.mipIndex = 0u; key.mipCount = 1u; key.layerIndex = 0u; key.layerCount = 1u; return image->createView(key); } HRESULT D3D11SwapChain::PresentImage(UINT SyncInterval) { // Flush pending rendering commands before auto immediateContext = m_parent->GetContext(); auto immediateContextLock = immediateContext->LockContext(); immediateContext->EndFrame(m_latency); immediateContext->ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, true); m_presenter->setSyncInterval(SyncInterval); // Presentation semaphores and WSI swap chain image if (m_latency) m_latency->notifyCpuPresentBegin(m_frameId + 1u); PresenterSync sync; Rc backBuffer; VkResult status = m_presenter->acquireNextImage(sync, backBuffer); if (status != VK_SUCCESS && m_latency) m_latency->discardTimings(); if (status < 0) return E_FAIL; if (status == VK_NOT_READY) return DXGI_STATUS_OCCLUDED; m_frameId += 1; // Present from CS thread so that we don't // have to synchronize with it first. DxvkImageViewKey viewInfo = { }; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; viewInfo.format = backBuffer->info().format; viewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT; viewInfo.mipIndex = 0u; viewInfo.mipCount = 1u; viewInfo.layerIndex = 0u; viewInfo.layerCount = 1u; immediateContext->EmitCs([ cDevice = m_device, cBlitter = m_blitter, cBackBuffer = backBuffer->createView(viewInfo), cSwapImage = GetBackBufferView(), cSync = sync, cPresenter = m_presenter, cLatency = m_latency, cColorSpace = m_colorSpace, cFrameId = m_frameId ] (DxvkContext* ctx) { // Update back buffer color space as necessary if (cSwapImage->image()->info().colorSpace != cColorSpace) { DxvkImageUsageInfo usage = { }; usage.colorSpace = cColorSpace; ctx->ensureImageCompatibility(cSwapImage->image(), usage); } // Blit the D3D back buffer onto the actual Vulkan // swap chain and render the HUD if we have one. auto contextObjects = ctx->beginExternalRendering(); cBlitter->present(contextObjects, cBackBuffer, VkRect2D(), cSwapImage, VkRect2D()); // Submit current command list and present ctx->synchronizeWsi(cSync); ctx->flushCommandList(nullptr, nullptr); cDevice->presentImage(cPresenter, cLatency, cFrameId, nullptr); }); if (m_backBuffers.size() > 1u) RotateBackBuffers(immediateContext); immediateContext->FlushCsChunk(); if (m_latency) { m_latency->notifyCpuPresentEnd(m_frameId); if (m_latency->needsAutoMarkers()) { immediateContext->EmitCs([ cLatency = m_latency, cFrameId = m_frameId ] (DxvkContext* ctx) { ctx->beginLatencyTracking(cLatency, cFrameId + 1u); }); } } return S_OK; } void D3D11SwapChain::RotateBackBuffers(D3D11ImmediateContext* ctx) { small_vector, 4> images; for (uint32_t i = 0; i < m_backBuffers.size(); i++) images.push_back(GetCommonTexture(m_backBuffers[i].ptr())->GetImage()); ctx->EmitCs([ cImages = std::move(images) ] (DxvkContext* ctx) { auto allocation = cImages[0]->storage(); for (size_t i = 0u; i + 1 < cImages.size(); i++) ctx->invalidateImage(cImages[i], cImages[i + 1]->storage()); ctx->invalidateImage(cImages[cImages.size() - 1u], std::move(allocation)); }); } void D3D11SwapChain::CreateFrameLatencyEvent() { m_frameLatencySignal = new sync::CallbackFence(m_frameId); if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT) m_frameLatencyEvent = CreateSemaphore(nullptr, m_frameLatency, DXGI_MAX_SWAP_CHAIN_BUFFERS, nullptr); } void D3D11SwapChain::CreatePresenter() { PresenterDesc presenterDesc = { }; presenterDesc.deferSurfaceCreation = m_parent->GetOptions()->deferSurfaceCreation; m_presenter = new Presenter(m_device, m_frameLatencySignal, presenterDesc, [ cAdapter = m_device->adapter(), cFactory = m_surfaceFactory ] (VkSurfaceKHR* surface) { return cFactory->CreateSurface( cAdapter->vki()->instance(), cAdapter->handle(), surface); }); m_presenter->setSurfaceFormat(GetSurfaceFormat(m_desc.Format)); m_presenter->setSurfaceExtent({ m_desc.Width, m_desc.Height }); m_presenter->setFrameRateLimit(m_targetFrameRate, GetActualFrameLatency()); m_latency = m_device->createLatencyTracker(m_presenter); Com reflex = GetReflexDevice(); reflex->RegisterLatencyTracker(m_latency); } void D3D11SwapChain::CreateBackBuffers() { // Explicitly destroy current swap image before // creating a new one to free up resources m_backBuffers.clear(); bool sequential = m_desc.SwapEffect == DXGI_SWAP_EFFECT_SEQUENTIAL || m_desc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; uint32_t backBufferCount = sequential ? m_desc.BufferCount : 1u; // Create new back buffer D3D11_COMMON_TEXTURE_DESC desc; desc.Width = std::max(m_desc.Width, 1u); desc.Height = std::max(m_desc.Height, 1u); desc.Depth = 1; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = m_desc.Format; desc.SampleDesc = m_desc.SampleDesc; desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = 0; desc.CPUAccessFlags = 0; desc.MiscFlags = 0; desc.TextureLayout = D3D11_TEXTURE_LAYOUT_UNDEFINED; if (m_desc.BufferUsage & DXGI_USAGE_RENDER_TARGET_OUTPUT) desc.BindFlags |= D3D11_BIND_RENDER_TARGET; if (m_desc.BufferUsage & DXGI_USAGE_SHADER_INPUT) desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; if (m_desc.BufferUsage & DXGI_USAGE_UNORDERED_ACCESS) desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE) desc.MiscFlags |= D3D11_RESOURCE_MISC_GDI_COMPATIBLE; DXGI_USAGE dxgiUsage = DXGI_USAGE_BACK_BUFFER; for (uint32_t i = 0; i < backBufferCount; i++) { if (m_desc.SwapEffect == DXGI_SWAP_EFFECT_DISCARD || m_desc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_DISCARD) dxgiUsage |= DXGI_USAGE_DISCARD_ON_PRESENT; m_backBuffers.push_back(new D3D11Texture2D( m_parent, this, &desc, dxgiUsage)); dxgiUsage |= DXGI_USAGE_READ_ONLY; } small_vector, 4> images; for (uint32_t i = 0; i < backBufferCount; i++) images.push_back(GetCommonTexture(m_backBuffers[i].ptr())->GetImage()); // Initialize images so that we can use them. Clearing // to black prevents garbled output for the first frame. m_parent->GetContext()->InjectCs(DxvkCsQueue::HighPriority, [ cImages = std::move(images) ] (DxvkContext* ctx) { for (size_t i = 0; i < cImages.size(); i++) { ctx->setDebugName(cImages[i], str::format("Back buffer ", i).c_str()); ctx->initImage(cImages[i], VK_IMAGE_LAYOUT_UNDEFINED); } }); } void D3D11SwapChain::CreateBlitter() { Rc hud = hud::Hud::createHud(m_device); if (hud) { hud->addItem("api", 1, GetApiName()); if (m_latency) m_latencyHud = hud->addItem("latency", 4); } m_blitter = new DxvkSwapchainBlitter(m_device, std::move(hud)); } void D3D11SwapChain::DestroyFrameLatencyEvent() { CloseHandle(m_frameLatencyEvent); } void D3D11SwapChain::DestroyLatencyTracker() { // Need to make sure the context stops using // the tracker for submissions m_parent->GetContext()->InjectCs(DxvkCsQueue::Ordered, [ cLatency = m_latency ] (DxvkContext* ctx) { ctx->endLatencyTracking(cLatency); }); Com reflex = GetReflexDevice(); reflex->UnregisterLatencyTracker(m_latency); } void D3D11SwapChain::SyncFrameLatency() { // Wait for the sync event so that we respect the maximum frame latency m_frameLatencySignal->wait(m_frameId - GetActualFrameLatency()); m_frameLatencySignal->setCallback(m_frameId, [this, cFrameId = m_frameId, cFrameLatencyEvent = m_frameLatencyEvent ] () { if (cFrameLatencyEvent) ReleaseSemaphore(cFrameLatencyEvent, 1, nullptr); std::lock_guard lock(m_frameStatisticsLock); m_frameStatistics.PresentCount = cFrameId - DXGI_MAX_SWAP_CHAIN_BUFFERS; m_frameStatistics.PresentQPCTime = dxvk::high_resolution_clock::get_counter(); }); } uint32_t D3D11SwapChain::GetActualFrameLatency() { // DXGI does not seem to implicitly synchronize waitable swap chains, // so in that case we should just respect the user config. For regular // swap chains, pick the latency from the DXGI device. uint32_t maxFrameLatency = DXGI_MAX_SWAP_CHAIN_BUFFERS; if (!(m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)) m_dxgiDevice->GetMaximumFrameLatency(&maxFrameLatency); if (m_frameLatencyCap) maxFrameLatency = std::min(maxFrameLatency, m_frameLatencyCap); maxFrameLatency = std::min(maxFrameLatency, m_desc.BufferCount); return maxFrameLatency; } VkSurfaceFormatKHR D3D11SwapChain::GetSurfaceFormat(DXGI_FORMAT Format) { switch (Format) { default: Logger::warn(str::format("D3D11SwapChain: Unexpected format: ", m_desc.Format)); [[fallthrough]]; case DXGI_FORMAT_R8G8B8A8_UNORM: case DXGI_FORMAT_B8G8R8A8_UNORM: return { VK_FORMAT_R8G8B8A8_UNORM, m_colorSpace }; case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: return { VK_FORMAT_R8G8B8A8_SRGB, m_colorSpace }; case DXGI_FORMAT_R10G10B10A2_UNORM: return { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorSpace }; case DXGI_FORMAT_R16G16B16A16_FLOAT: return { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorSpace }; } } Com D3D11SwapChain::GetReflexDevice() { Com llDevice; m_parent->QueryInterface(__uuidof(ID3DLowLatencyDevice), reinterpret_cast(&llDevice)); return static_cast(llDevice.ptr()); } std::string D3D11SwapChain::GetApiName() const { Com device; m_parent->QueryInterface(__uuidof(IDXGIDXVKDevice), reinterpret_cast(&device)); uint32_t apiVersion = device->GetAPIVersion(); uint32_t featureLevel = m_parent->GetFeatureLevel(); uint32_t flHi = (featureLevel >> 12); uint32_t flLo = (featureLevel >> 8) & 0x7; return str::format("D3D", apiVersion, " FL", flHi, "_", flLo); } } dxvk-2.6.1/src/d3d11/d3d11_swapchain.h000066400000000000000000000106301477473124000171320ustar00rootroot00000000000000#pragma once #include "d3d11_texture.h" #include "../dxvk/hud/dxvk_hud.h" #include "../dxvk/dxvk_latency.h" #include "../dxvk/dxvk_swapchain_blitter.h" #include "../util/sync/sync_signal.h" namespace dxvk { class D3D11Device; class D3D11DXGIDevice; class D3D11SwapChain : public ComObject { constexpr static uint32_t DefaultFrameLatency = 1; public: D3D11SwapChain( D3D11DXGIDevice* pContainer, D3D11Device* pDevice, IDXGIVkSurfaceFactory* pSurfaceFactory, const DXGI_SWAP_CHAIN_DESC1* pDesc); ~D3D11SwapChain(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE GetDesc( DXGI_SWAP_CHAIN_DESC1* pDesc); HRESULT STDMETHODCALLTYPE GetAdapter( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE GetDevice( REFIID riid, void** ppDevice); HRESULT STDMETHODCALLTYPE GetImage( UINT BufferId, REFIID riid, void** ppBuffer); UINT STDMETHODCALLTYPE GetImageIndex(); UINT STDMETHODCALLTYPE GetFrameLatency(); HANDLE STDMETHODCALLTYPE GetFrameLatencyEvent(); HRESULT STDMETHODCALLTYPE ChangeProperties( const DXGI_SWAP_CHAIN_DESC1* pDesc, const UINT* pNodeMasks, IUnknown* const* ppPresentQueues); HRESULT STDMETHODCALLTYPE SetPresentRegion( const RECT* pRegion); HRESULT STDMETHODCALLTYPE SetGammaControl( UINT NumControlPoints, const DXGI_RGB* pControlPoints); HRESULT STDMETHODCALLTYPE SetFrameLatency( UINT MaxLatency); HRESULT STDMETHODCALLTYPE Present( UINT SyncInterval, UINT PresentFlags, const DXGI_PRESENT_PARAMETERS* pPresentParameters); UINT STDMETHODCALLTYPE CheckColorSpaceSupport( DXGI_COLOR_SPACE_TYPE ColorSpace); HRESULT STDMETHODCALLTYPE SetColorSpace( DXGI_COLOR_SPACE_TYPE ColorSpace); HRESULT STDMETHODCALLTYPE SetHDRMetaData( const DXGI_VK_HDR_METADATA* pMetaData); void STDMETHODCALLTYPE GetLastPresentCount( UINT64* pLastPresentCount); void STDMETHODCALLTYPE GetFrameStatistics( DXGI_VK_FRAME_STATISTICS* pFrameStatistics); void STDMETHODCALLTYPE SetTargetFrameRate( double FrameRate); private: enum BindingIds : uint32_t { Image = 0, Gamma = 1, }; Com m_dxgiDevice; D3D11Device* m_parent; Com m_surfaceFactory; DXGI_SWAP_CHAIN_DESC1 m_desc; Rc m_device; Rc m_presenter; Rc m_blitter; Rc m_latency; small_vector, 4> m_backBuffers; uint64_t m_frameId = DXGI_MAX_SWAP_CHAIN_BUFFERS; uint32_t m_frameLatency = DefaultFrameLatency; uint32_t m_frameLatencyCap = 0; HANDLE m_frameLatencyEvent = nullptr; Rc m_frameLatencySignal; VkColorSpaceKHR m_colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; double m_targetFrameRate = 0.0; dxvk::mutex m_frameStatisticsLock; DXGI_VK_FRAME_STATISTICS m_frameStatistics = { }; Rc m_latencyHud; Rc GetBackBufferView(); HRESULT PresentImage(UINT SyncInterval); void RotateBackBuffers(D3D11ImmediateContext* ctx); void CreateFrameLatencyEvent(); void CreatePresenter(); void CreateBackBuffers(); void CreateBlitter(); void DestroyFrameLatencyEvent(); void DestroyLatencyTracker(); void SyncFrameLatency(); uint32_t GetActualFrameLatency(); VkSurfaceFormatKHR GetSurfaceFormat(DXGI_FORMAT Format); Com GetReflexDevice(); std::string GetApiName() const; }; }dxvk-2.6.1/src/d3d11/d3d11_texture.cpp000066400000000000000000001562061477473124000172220ustar00rootroot00000000000000#include "d3d11_device.h" #include "d3d11_context_imm.h" #include "d3d11_gdi.h" #include "d3d11_texture.h" #include "../util/util_shared_res.h" #include "../util/util_win32_compat.h" namespace dxvk { D3D11CommonTexture::D3D11CommonTexture( ID3D11Resource* pInterface, D3D11Device* pDevice, const D3D11_COMMON_TEXTURE_DESC* pDesc, const D3D11_ON_12_RESOURCE_INFO* p11on12Info, D3D11_RESOURCE_DIMENSION Dimension, DXGI_USAGE DxgiUsage, VkImage vkImage, HANDLE hSharedHandle) : m_interface(pInterface), m_device(pDevice), m_dimension(Dimension), m_desc(*pDesc), m_11on12(p11on12Info ? *p11on12Info : D3D11_ON_12_RESOURCE_INFO()), m_dxgiUsage(DxgiUsage) { DXGI_VK_FORMAT_MODE formatMode = GetFormatMode(); DXGI_VK_FORMAT_INFO formatInfo = m_device->LookupFormat(m_desc.Format, formatMode); DXGI_VK_FORMAT_FAMILY formatFamily = m_device->LookupFamily(m_desc.Format, formatMode); DXGI_VK_FORMAT_INFO formatPacked = m_device->LookupPackedFormat(m_desc.Format, formatMode); m_packedFormat = formatPacked.Format; DxvkImageCreateInfo imageInfo; imageInfo.type = GetVkImageType(); imageInfo.format = formatInfo.Format; imageInfo.flags = 0; imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT; imageInfo.extent.width = m_desc.Width; imageInfo.extent.height = m_desc.Height; imageInfo.extent.depth = m_desc.Depth; imageInfo.numLayers = m_desc.ArraySize; imageInfo.mipLevels = m_desc.MipLevels; imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; imageInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT; imageInfo.access = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.layout = VK_IMAGE_LAYOUT_GENERAL; imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageInfo.shared = vkImage != VK_NULL_HANDLE; // Normalise hSharedhandle to INVALID_HANDLE_VALUE to allow passing in nullptr if (hSharedHandle == nullptr) hSharedHandle = INVALID_HANDLE_VALUE; const auto sharingFlags = D3D11_RESOURCE_MISC_SHARED|D3D11_RESOURCE_MISC_SHARED_NTHANDLE|D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; if (m_desc.MiscFlags & sharingFlags) { if (pDevice->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0 || (m_desc.MiscFlags & (D3D11_RESOURCE_MISC_SHARED|D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX)) == (D3D11_RESOURCE_MISC_SHARED|D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) || (m_desc.MiscFlags & sharingFlags) == D3D11_RESOURCE_MISC_SHARED_NTHANDLE) throw DxvkError(str::format("D3D11: Cannot create shared texture:", "\n MiscFlags: ", m_desc.MiscFlags, "\n FeatureLevel: ", pDevice->GetFeatureLevel())); imageInfo.shared = true; imageInfo.sharing.mode = hSharedHandle == INVALID_HANDLE_VALUE ? DxvkSharedHandleMode::Export : DxvkSharedHandleMode::Import; imageInfo.sharing.type = (m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE) ? VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT : VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT; imageInfo.sharing.handle = hSharedHandle; } if (!pDevice->GetOptions()->disableMsaa) DecodeSampleCount(m_desc.SampleDesc.Count, &imageInfo.sampleCount); if ((m_desc.BindFlags & D3D11_BIND_UNORDERED_ACCESS) && IsR32UavCompatibleFormat(m_desc.Format)) { formatFamily.Add(formatInfo.Format); formatFamily.Add(VK_FORMAT_R32_SFLOAT); formatFamily.Add(VK_FORMAT_R32_UINT); formatFamily.Add(VK_FORMAT_R32_SINT); } // The image must be marked as mutable if it can be reinterpreted // by a view with a different format. Depth-stencil formats cannot // be reinterpreted in Vulkan, so we'll ignore those. auto formatProperties = lookupFormatInfo(formatInfo.Format); bool isMutable = formatFamily.FormatCount > 1; bool isMultiPlane = (formatProperties->aspectMask & VK_IMAGE_ASPECT_PLANE_0_BIT) != 0; bool isColorFormat = (formatProperties->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0; if (isMutable && (isColorFormat || isMultiPlane)) { imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; imageInfo.viewFormatCount = formatFamily.FormatCount; imageInfo.viewFormats = formatFamily.Formats; } // Adjust image flags based on the corresponding D3D flags if (m_desc.BindFlags & D3D11_BIND_SHADER_RESOURCE) { imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT; imageInfo.stages |= pDevice->GetEnabledShaderStages(); imageInfo.access |= VK_ACCESS_SHADER_READ_BIT; } if (m_desc.BindFlags & D3D11_BIND_RENDER_TARGET) { imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; imageInfo.stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; imageInfo.access |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; } if (m_desc.BindFlags & D3D11_BIND_DEPTH_STENCIL) { imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; imageInfo.stages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; imageInfo.access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; } if (m_desc.BindFlags & D3D11_BIND_UNORDERED_ACCESS) { imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT; imageInfo.stages |= pDevice->GetEnabledShaderStages(); imageInfo.access |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; // UAVs are not supported for sRGB formats on most drivers, // but we can still create linear views for the image if (formatProperties->flags.test(DxvkFormatFlag::ColorSpaceSrgb)) imageInfo.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT; } // Multi-plane formats need views to be created with color formats, and // may not report all relevant usage flags as supported on their own. // Also, enable sampled bit to enable use with video processor APIs. if (isMultiPlane) { imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT; imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT; } // Access pattern for meta-resolve operations if (imageInfo.sampleCount != VK_SAMPLE_COUNT_1_BIT && isColorFormat) { imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT; imageInfo.stages |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; imageInfo.access |= VK_ACCESS_SHADER_READ_BIT; } if (m_desc.MiscFlags & D3D11_RESOURCE_MISC_TEXTURECUBE) imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; if (m_desc.MiscFlags & D3D11_RESOURCE_MISC_TILED) { imageInfo.flags |= VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT | VK_IMAGE_CREATE_SPARSE_ALIASED_BIT; } if (Dimension == D3D11_RESOURCE_DIMENSION_TEXTURE3D && (m_desc.BindFlags & D3D11_BIND_RENDER_TARGET)) imageInfo.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT; // Swap chain back buffers need to be shader readable if (DxgiUsage & DXGI_USAGE_BACK_BUFFER) { imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT; imageInfo.stages |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; imageInfo.access |= VK_ACCESS_SHADER_READ_BIT; imageInfo.shared = VK_TRUE; } // Some image formats (i.e. the R32G32B32 ones) are // only supported with linear tiling on most GPUs if (!CheckImageSupport(&imageInfo, VK_IMAGE_TILING_OPTIMAL)) imageInfo.tiling = VK_IMAGE_TILING_LINEAR; // Determine map mode based on our findings VkMemoryPropertyFlags memoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; std::tie(m_mapMode, memoryProperties) = DetermineMapMode(&imageInfo); // If the image is mapped directly to host memory, we need // to enable linear tiling, and DXVK needs to be aware that // the image can be accessed by the host. if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT) { imageInfo.tiling = VK_IMAGE_TILING_LINEAR; imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; if (pDesc->Usage != D3D11_USAGE_DYNAMIC) { imageInfo.stages |= VK_PIPELINE_STAGE_HOST_BIT; imageInfo.access |= VK_ACCESS_HOST_READ_BIT; if (pDesc->CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) imageInfo.access |= VK_ACCESS_HOST_WRITE_BIT; } } // If necessary, create the mapped linear buffer uint32_t subresourceCount = m_desc.ArraySize * m_desc.MipLevels; if (m_mapMode != D3D11_COMMON_TEXTURE_MAP_MODE_NONE) { m_mapInfo.resize(subresourceCount); for (uint32_t i = 0; i < subresourceCount; i++) { m_mapInfo[i].layout = DetermineSubresourceLayout(&imageInfo, GetSubresourceFromIndex(formatProperties->aspectMask, i)); } } if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER || m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING || m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC) { m_buffers.resize(subresourceCount); if (m_mapMode != D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC) { for (uint32_t i = 0; i < subresourceCount; i++) CreateMappedBuffer(i); } } // Skip image creation if possible if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING) return; // We must keep LINEAR images in GENERAL layout, but we // can choose a better layout for the image based on how // it is going to be used by the game. if (imageInfo.tiling == VK_IMAGE_TILING_OPTIMAL && !isMultiPlane && imageInfo.sharing.mode == DxvkSharedHandleMode::None) imageInfo.layout = OptimizeLayout(imageInfo.usage); // Check if we can actually create the image if (!CheckImageSupport(&imageInfo, imageInfo.tiling)) { throw DxvkError(str::format( "D3D11: Cannot create texture:", "\n Format: ", m_desc.Format, "\n Extent: ", m_desc.Width, "x", m_desc.Height, "x", m_desc.Depth, "\n Samples: ", m_desc.SampleDesc.Count, "\n Layers: ", m_desc.ArraySize, "\n Levels: ", m_desc.MipLevels, "\n Usage: ", std::hex, m_desc.BindFlags, "\n Flags: ", std::hex, m_desc.MiscFlags)); } if (m_11on12.Resource != nullptr) vkImage = VkImage(m_11on12.VulkanHandle); if (!vkImage) m_image = m_device->GetDXVKDevice()->createImage(imageInfo, memoryProperties); else m_image = m_device->GetDXVKDevice()->importImage(imageInfo, vkImage, memoryProperties); if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT) m_mapPtr = m_image->mapPtr(0); if (imageInfo.sharing.mode == DxvkSharedHandleMode::Export) ExportImageInfo(); } D3D11CommonTexture::~D3D11CommonTexture() { } VkDeviceSize D3D11CommonTexture::ComputeMappedOffset(UINT Subresource, UINT Plane, VkOffset3D Offset) const { auto packedFormatInfo = lookupFormatInfo(m_packedFormat); VkImageAspectFlags aspectMask = packedFormatInfo->aspectMask; VkDeviceSize elementSize = packedFormatInfo->elementSize; if (packedFormatInfo->flags.test(DxvkFormatFlag::MultiPlane)) { auto plane = &packedFormatInfo->planes[Plane]; elementSize = plane->elementSize; Offset.x /= plane->blockSize.width; Offset.y /= plane->blockSize.height; aspectMask = vk::getPlaneAspect(Plane); } auto layout = GetSubresourceLayout(aspectMask, Subresource); auto blockOffset = util::computeBlockOffset(Offset, packedFormatInfo->blockSize); return VkDeviceSize(blockOffset.z) * layout.DepthPitch + VkDeviceSize(blockOffset.y) * layout.RowPitch + VkDeviceSize(blockOffset.x) * elementSize + VkDeviceSize(layout.Offset); } D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT D3D11CommonTexture::GetSubresourceLayout( VkImageAspectFlags AspectMask, UINT Subresource) const { // Color is mapped directly and depth-stencil are interleaved // in packed formats, so just use the cached subresource layout constexpr VkImageAspectFlags PlaneAspects = VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT | VK_IMAGE_ASPECT_PLANE_2_BIT; if ((Subresource < m_mapInfo.size()) && !(AspectMask & PlaneAspects)) return m_mapInfo[Subresource].layout; // Safe-guard against invalid subresource index if (Subresource >= m_desc.ArraySize * m_desc.MipLevels) return D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT(); // Image info is only needed for direct-mapped images VkImageSubresource subresource = GetSubresourceFromIndex(AspectMask, Subresource); return DetermineSubresourceLayout(nullptr, subresource); } DXGI_VK_FORMAT_MODE D3D11CommonTexture::GetFormatMode() const { if (m_desc.BindFlags & D3D11_BIND_RENDER_TARGET) return DXGI_VK_FORMAT_MODE_COLOR; if (m_desc.BindFlags & D3D11_BIND_DEPTH_STENCIL) return DXGI_VK_FORMAT_MODE_DEPTH; return DXGI_VK_FORMAT_MODE_ANY; } uint32_t D3D11CommonTexture::GetPlaneCount() const { return vk::getPlaneCount(m_image->formatInfo()->aspectMask); } bool D3D11CommonTexture::CheckViewCompatibility(UINT BindFlags, DXGI_FORMAT Format, UINT Plane) const { const DxvkImageCreateInfo& imageInfo = m_image->info(); // Check whether the given bind flags are supported if ((m_desc.BindFlags & BindFlags) != BindFlags) return false; // Check whether the view format is compatible DXGI_VK_FORMAT_MODE formatMode = GetFormatMode(); DXGI_VK_FORMAT_INFO viewFormat = m_device->LookupFormat(Format, formatMode); DXGI_VK_FORMAT_INFO baseFormat = m_device->LookupFormat(m_desc.Format, formatMode); // Check whether the plane index is valid for the given format uint32_t planeCount = GetPlaneCount(); if (Plane >= planeCount) return false; if (imageInfo.flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) { // Check whether the given combination of image // view type and view format is actually supported VkFormatFeatureFlags2 features = GetImageFormatFeatures(BindFlags); if (!CheckFormatFeatureSupport(viewFormat.Format, features)) return false; // Using the image format itself is supported for non-planar formats if (viewFormat.Format == baseFormat.Format && planeCount == 1) return true; // If there is a list of compatible formats, the view format must be // included in that list. For planar formats, the list is laid out in // such a way that the n-th format is supported for the n-th plane. for (size_t i = Plane; i < imageInfo.viewFormatCount; i += planeCount) { if (imageInfo.viewFormats[i] == viewFormat.Format) { return true; } } // Otherwise, all bit-compatible formats can be used. if (imageInfo.viewFormatCount == 0 && planeCount == 1) { auto baseFormatInfo = lookupFormatInfo(baseFormat.Format); auto viewFormatInfo = lookupFormatInfo(viewFormat.Format); return baseFormatInfo->aspectMask == viewFormatInfo->aspectMask && baseFormatInfo->elementSize == viewFormatInfo->elementSize; } return false; } else { // For non-mutable images, the view format // must be identical to the image format. return viewFormat.Format == baseFormat.Format && planeCount == 1; } } void D3D11CommonTexture::SetDebugName(const char* pName) { if (m_image) { m_device->GetContext()->InjectCs(DxvkCsQueue::HighPriority, [ cImage = m_image, cName = std::string(pName ? pName : "") ] (DxvkContext* ctx) { ctx->setDebugName(cImage, cName.c_str()); }); } if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING) { for (uint32_t i = 0; i < m_buffers.size(); i++) { m_device->GetContext()->InjectCs(DxvkCsQueue::HighPriority, [ cBuffer = m_buffers[i].buffer, cName = std::string(pName ? pName : "") ] (DxvkContext* ctx) { ctx->setDebugName(cBuffer, cName.c_str()); }); } } } HRESULT D3D11CommonTexture::NormalizeTextureProperties(D3D11_COMMON_TEXTURE_DESC* pDesc) { if (pDesc->Width == 0 || pDesc->Height == 0 || pDesc->Depth == 0 || pDesc->ArraySize == 0) return E_INVALIDARG; if (FAILED(DecodeSampleCount(pDesc->SampleDesc.Count, nullptr))) return E_INVALIDARG; if ((pDesc->MiscFlags & D3D11_RESOURCE_MISC_GDI_COMPATIBLE) && (pDesc->Usage == D3D11_USAGE_STAGING || (pDesc->Format != DXGI_FORMAT_B8G8R8A8_TYPELESS && pDesc->Format != DXGI_FORMAT_B8G8R8A8_UNORM && pDesc->Format != DXGI_FORMAT_B8G8R8A8_UNORM_SRGB))) return E_INVALIDARG; if ((pDesc->MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS) && (pDesc->BindFlags & (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET)) != (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET)) return E_INVALIDARG; // TILE_POOL is invalid for textures if (pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL) return E_INVALIDARG; // Perform basic validation for tiled resources if (pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILED) { UINT invalidFlags = D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX | D3D11_RESOURCE_MISC_GDI_COMPATIBLE; if ((pDesc->MiscFlags & invalidFlags) || (pDesc->Usage != D3D11_USAGE_DEFAULT) || (pDesc->CPUAccessFlags)) return E_INVALIDARG; } // Use the maximum possible mip level count if the supplied // mip level count is either unspecified (0) or invalid const uint32_t maxMipLevelCount = (pDesc->SampleDesc.Count <= 1) ? util::computeMipLevelCount({ pDesc->Width, pDesc->Height, pDesc->Depth }) : 1u; if (pDesc->MipLevels == 0 || pDesc->MipLevels > maxMipLevelCount) pDesc->MipLevels = maxMipLevelCount; // Row-major is only supported for textures with one single // subresource and one sample and cannot have bind flags. if (pDesc->TextureLayout == D3D11_TEXTURE_LAYOUT_ROW_MAJOR && (pDesc->MipLevels != 1 || pDesc->SampleDesc.Count != 1 || pDesc->BindFlags)) return E_INVALIDARG; // Standard swizzle is unsupported if (pDesc->TextureLayout == D3D11_TEXTURE_LAYOUT_64K_STANDARD_SWIZZLE) return E_INVALIDARG; return S_OK; } HRESULT D3D11CommonTexture::GetDescFromD3D12( ID3D12Resource* pResource, const D3D11_RESOURCE_FLAGS* pResourceFlags, D3D11_COMMON_TEXTURE_DESC* pTextureDesc) { D3D12_RESOURCE_DESC desc12 = pResource->GetDesc(); pTextureDesc->Width = desc12.Width; pTextureDesc->Height = desc12.Height; if (desc12.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D) { pTextureDesc->Depth = desc12.DepthOrArraySize; pTextureDesc->ArraySize = 1; } else { pTextureDesc->Depth = 1; pTextureDesc->ArraySize = desc12.DepthOrArraySize; } pTextureDesc->MipLevels = desc12.MipLevels; pTextureDesc->Format = desc12.Format; pTextureDesc->SampleDesc = desc12.SampleDesc; pTextureDesc->Usage = D3D11_USAGE_DEFAULT; pTextureDesc->BindFlags = 0; pTextureDesc->CPUAccessFlags = 0; pTextureDesc->MiscFlags = 0; if (!(desc12.Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE)) pTextureDesc->BindFlags |= D3D11_BIND_SHADER_RESOURCE; if (desc12.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) pTextureDesc->BindFlags |= D3D11_BIND_RENDER_TARGET; if (desc12.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) pTextureDesc->BindFlags |= D3D11_BIND_DEPTH_STENCIL; if (desc12.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) pTextureDesc->BindFlags |= D3D11_BIND_UNORDERED_ACCESS; if (pResourceFlags) { pTextureDesc->BindFlags = pResourceFlags->BindFlags; pTextureDesc->MiscFlags |= pResourceFlags->MiscFlags; pTextureDesc->CPUAccessFlags = pResourceFlags->CPUAccessFlags; } return S_OK; } BOOL D3D11CommonTexture::CheckImageSupport( const DxvkImageCreateInfo* pImageInfo, VkImageTiling Tiling) const { // D3D12 images always use optimal tiling if (m_11on12.Resource != nullptr && Tiling != VK_IMAGE_TILING_OPTIMAL) return FALSE; DxvkFormatQuery formatQuery = { }; formatQuery.format = pImageInfo->format; formatQuery.type = pImageInfo->type; formatQuery.tiling = Tiling; formatQuery.usage = pImageInfo->usage; formatQuery.flags = pImageInfo->flags; if (pImageInfo->flags & VK_IMAGE_CREATE_EXTENDED_USAGE_BIT) formatQuery.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; auto properties = m_device->GetDXVKDevice()->getFormatLimits(formatQuery); if (!properties) return FALSE; return (pImageInfo->extent.width <= properties->maxExtent.width) && (pImageInfo->extent.height <= properties->maxExtent.height) && (pImageInfo->extent.depth <= properties->maxExtent.depth) && (pImageInfo->numLayers <= properties->maxArrayLayers) && (pImageInfo->mipLevels <= properties->maxMipLevels) && (pImageInfo->sampleCount & properties->sampleCounts); } BOOL D3D11CommonTexture::CheckFormatFeatureSupport( VkFormat Format, VkFormatFeatureFlags2 Features) const { DxvkFormatFeatures support = m_device->GetDXVKDevice()->getFormatFeatures(Format); return (support.linear & Features) == Features || (support.optimal & Features) == Features; } std::pair D3D11CommonTexture::DetermineMapMode( const DxvkImageCreateInfo* pImageInfo) const { // Don't map an image unless the application requests it if (!m_desc.CPUAccessFlags) return { D3D11_COMMON_TEXTURE_MAP_MODE_NONE, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT }; // For default images, always use a persistent staging buffer. Readback // may cause a GPU sync, but nobody seems to be using this feature anyway. if (m_desc.Usage == D3D11_USAGE_DEFAULT) return { D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT }; // If the resource cannot be used in the actual rendering pipeline, we // do not need to create an actual image and can instead implement copy // functions as buffer-to-image and image-to-buffer copies. if (m_desc.Usage == D3D11_USAGE_STAGING) return { D3D11_COMMON_TEXTURE_MAP_MODE_STAGING, 0u }; // If the packed format and image format don't match, we need to use // a staging buffer and perform format conversion when mapping. if (m_packedFormat != pImageInfo->format) return { D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT }; // Multi-plane and depth-stencil images have a special memory layout // in D3D11, so we can't expose those directly to the app auto formatInfo = lookupFormatInfo(pImageInfo->format); if (formatInfo->aspectMask != VK_IMAGE_ASPECT_COLOR_BIT) return { D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT }; // If we can't use linear tiling for this image, we have to use a buffer if (!CheckImageSupport(pImageInfo, VK_IMAGE_TILING_LINEAR)) return { D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT }; // Determine memory flags for the actual image if we use direct mapping. // Depending on the concrete use case, we may fall back to different // memory types. VkMemoryPropertyFlags memoryFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; bool useCached = (m_device->GetOptions()->cachedDynamicResources == ~0u) || (m_device->GetOptions()->cachedDynamicResources & m_desc.BindFlags) || (m_desc.CPUAccessFlags & D3D11_CPU_ACCESS_READ); if (m_desc.Usage == D3D11_USAGE_STAGING || useCached) memoryFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT; else if (m_desc.BindFlags) memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; // If there are multiple subresources, go through a buffer because // we can otherwise not really discard individual subresources. if (m_desc.ArraySize > 1u || m_desc.MipLevels != 1u) return { D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT }; // If the image is essentially linear already, expose it directly since // there won't be any tangible benefit to using optimal tiling anyway. VkExtent3D blockCount = util::computeBlockCount(pImageInfo->extent, formatInfo->blockSize); if (blockCount.height == 1u && blockCount.depth == 1u) return { D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT, memoryFlags }; // If the image looks like a video, we can generally expect it to get // updated and read once per frame. This is one of the most common use // cases for a mapped image, expose it directly in order to avoid copies. if (blockCount.depth == 1u && blockCount.height >= 160 && formatInfo->elementSize <= 4u) { static const std::array, 3> videoApectRatios = {{ { 4, 3 }, { 16, 9 }, { 21, 9 }, }}; bool isVideoAspectRatio = false; for (const auto& a : videoApectRatios) { // Due to codec limitations, video dimensions are often rounded to // a multiple of 8. Account for this when checking the size. isVideoAspectRatio |= blockCount.width > (a.first * (blockCount.height - 8u)) / a.second && blockCount.width < (a.first * (blockCount.height + 8u)) / a.second; } if (isVideoAspectRatio) { // Keep video images in system memory to not waste precious HVV space return { D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT, memoryFlags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT }; } } // If the image exceeds a certain size, map it directly because the overhead // of potentially copying the whole thing every frame likely outweighs any // benefit we might get from faster memory and tiling. This solves such an // issue in Warhammer III, which discards a 48 MB texture every single frame. constexpr VkDeviceSize MaxImageStagingBufferSize = 1ull << 20; VkDeviceSize imageSize = util::flattenImageExtent(blockCount) * formatInfo->elementSize; if (imageSize > MaxImageStagingBufferSize) return { D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT, memoryFlags }; // For smaller images, use a staging buffer. There are some common use // cases where the image will only get written once, e.g. SMAA look-up // tables in some games, which will benefit from faster GPU access. return { D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT }; } D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT D3D11CommonTexture::DetermineSubresourceLayout( const DxvkImageCreateInfo* pImageInfo, const VkImageSubresource& subresource) const { auto formatInfo = lookupFormatInfo(m_packedFormat); if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT) { VkSubresourceLayout vkLayout = m_device->GetDXVKDevice()->queryImageSubresourceLayout(*pImageInfo, subresource); D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT result = { }; result.Offset = vkLayout.offset; result.RowPitch = vkLayout.rowPitch; result.DepthPitch = vkLayout.depthPitch; // We will only ever use direct mapping for single-aspect images, // so ignore any sort of multi-plane shenanigans on this path auto mipExtent = MipLevelExtent(subresource.mipLevel); auto blockCount = util::computeBlockCount(mipExtent, formatInfo->blockSize); // If the image dimensions support it, try to look as close to a // linear buffer as we can. Some games use the depth pitch as a // subresource size and will crash if it includes any padding. if (blockCount.depth == 1u) { if (blockCount.height == 1u) { result.RowPitch = formatInfo->elementSize * blockCount.width; result.DepthPitch = result.RowPitch; } else { result.DepthPitch = vkLayout.rowPitch * blockCount.height; } } result.Size = blockCount.depth * result.DepthPitch; return result; } else { D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT result = { }; VkImageAspectFlags aspects = formatInfo->aspectMask; VkExtent3D mipExtent = MipLevelExtent(subresource.mipLevel); while (aspects) { auto aspect = vk::getNextAspect(aspects); auto extent = mipExtent; auto elementSize = formatInfo->elementSize; if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) { auto plane = &formatInfo->planes[vk::getPlaneIndex(aspect)]; extent.width /= plane->blockSize.width; extent.height /= plane->blockSize.height; elementSize = plane->elementSize; } auto blockCount = util::computeBlockCount(extent, formatInfo->blockSize); if (!result.RowPitch) { result.RowPitch = elementSize * blockCount.width; result.DepthPitch = elementSize * blockCount.width * blockCount.height; } VkDeviceSize size = elementSize * blockCount.width * blockCount.height * blockCount.depth; if (aspect & subresource.aspectMask) result.Size += size; else if (!result.Size) result.Offset += size; } return result; } } void D3D11CommonTexture::ExportImageInfo() { HANDLE hSharedHandle; if (m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE) hSharedHandle = m_image->sharedHandle(); else hSharedHandle = openKmtHandle( m_image->sharedHandle() ); DxvkSharedTextureMetadata metadata; metadata.Width = m_desc.Width; metadata.Height = m_desc.Height; metadata.MipLevels = m_desc.MipLevels; metadata.ArraySize = m_desc.ArraySize; metadata.Format = m_desc.Format; metadata.SampleDesc = m_desc.SampleDesc; metadata.Usage = m_desc.Usage; metadata.BindFlags = m_desc.BindFlags; metadata.CPUAccessFlags = m_desc.CPUAccessFlags; metadata.MiscFlags = m_desc.MiscFlags; metadata.TextureLayout = m_desc.TextureLayout; if (hSharedHandle == INVALID_HANDLE_VALUE || !setSharedMetadata(hSharedHandle, &metadata, sizeof(metadata))) { Logger::warn("D3D11: Failed to write shared resource info for a texture"); } if (hSharedHandle != INVALID_HANDLE_VALUE) CloseHandle(hSharedHandle); } BOOL D3D11CommonTexture::IsR32UavCompatibleFormat( DXGI_FORMAT Format) { return Format == DXGI_FORMAT_R8G8B8A8_TYPELESS || Format == DXGI_FORMAT_B8G8R8A8_TYPELESS || Format == DXGI_FORMAT_B8G8R8X8_TYPELESS || Format == DXGI_FORMAT_R10G10B10A2_TYPELESS || Format == DXGI_FORMAT_R16G16_TYPELESS || Format == DXGI_FORMAT_R32_TYPELESS; } void D3D11CommonTexture::CreateMappedBuffer(UINT Subresource) { const DxvkFormatInfo* formatInfo = lookupFormatInfo( m_device->LookupPackedFormat(m_desc.Format, GetFormatMode()).Format); DxvkBufferCreateInfo info; info.size = GetSubresourceLayout(formatInfo->aspectMask, Subresource).Size; info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; info.access = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; info.debugName = "Image buffer"; // We may read mapped buffers even if it is // marked as CPU write-only on the D3D11 side. if (m_desc.Usage != D3D11_USAGE_DYNAMIC) { info.stages |= VK_PIPELINE_STAGE_HOST_BIT; info.access |= VK_ACCESS_HOST_READ_BIT; if (m_desc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) info.access |= VK_ACCESS_HOST_WRITE_BIT; } VkMemoryPropertyFlags memType = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; bool useCached = m_device->GetOptions()->cachedDynamicResources == ~0u; if (m_desc.Usage == D3D11_USAGE_STAGING || useCached) memType |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT; auto& entry = m_buffers[Subresource]; entry.buffer = m_device->GetDXVKDevice()->createBuffer(info, memType); entry.slice = entry.buffer->storage(); } void D3D11CommonTexture::FreeMappedBuffer( UINT Subresource) { auto& entry = m_buffers[Subresource]; entry.buffer = nullptr; entry.slice = nullptr; } VkImageType D3D11CommonTexture::GetImageTypeFromResourceDim(D3D11_RESOURCE_DIMENSION Dimension) { switch (Dimension) { case D3D11_RESOURCE_DIMENSION_TEXTURE1D: return VK_IMAGE_TYPE_1D; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: return VK_IMAGE_TYPE_2D; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: return VK_IMAGE_TYPE_3D; default: throw DxvkError("D3D11CommonTexture: Unhandled resource dimension"); } } VkImageLayout D3D11CommonTexture::OptimizeLayout(VkImageUsageFlags Usage) { const VkImageUsageFlags usageFlags = Usage; // Filter out unnecessary flags. Transfer operations // are handled by the backend in a transparent manner. Usage &= VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; // Storage images require GENERAL. if (Usage & VK_IMAGE_USAGE_STORAGE_BIT) return VK_IMAGE_LAYOUT_GENERAL; // Also use GENERAL if the image cannot be rendered to. This // should not harm any hardware in practice and may avoid some // redundant layout transitions for regular textures. if (!(Usage & ~VK_IMAGE_USAGE_SAMPLED_BIT)) return VK_IMAGE_LAYOUT_GENERAL; // If the image is used only as an attachment, we never // have to transform the image back to a different layout. if (Usage == VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; if (Usage == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // Otherwise, pick a layout that can be used for reading. return usageFlags & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } D3D11DXGISurface::D3D11DXGISurface( ID3D11Resource* pResource, D3D11CommonTexture* pTexture) : m_resource (pResource), m_texture (pTexture), m_gdiSurface(nullptr) { if (pTexture->Desc()->MiscFlags & D3D11_RESOURCE_MISC_GDI_COMPATIBLE) m_gdiSurface = new D3D11GDISurface(m_resource, 0); } D3D11DXGISurface::~D3D11DXGISurface() { if (m_gdiSurface) delete m_gdiSurface; } ULONG STDMETHODCALLTYPE D3D11DXGISurface::AddRef() { return m_resource->AddRef(); } ULONG STDMETHODCALLTYPE D3D11DXGISurface::Release() { return m_resource->Release(); } HRESULT STDMETHODCALLTYPE D3D11DXGISurface::QueryInterface( REFIID riid, void** ppvObject) { return m_resource->QueryInterface(riid, ppvObject); } HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetPrivateData( REFGUID Name, UINT* pDataSize, void* pData) { return m_resource->GetPrivateData(Name, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D11DXGISurface::SetPrivateData( REFGUID Name, UINT DataSize, const void* pData) { return m_resource->SetPrivateData(Name, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D11DXGISurface::SetPrivateDataInterface( REFGUID Name, const IUnknown* pUnknown) { return m_resource->SetPrivateDataInterface(Name, pUnknown); } HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetParent( REFIID riid, void** ppParent) { return GetDevice(riid, ppParent); } HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetDevice( REFIID riid, void** ppDevice) { Com device; m_resource->GetDevice(&device); return device->QueryInterface(riid, ppDevice); } HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetDesc( DXGI_SURFACE_DESC* pDesc) { if (!pDesc) return DXGI_ERROR_INVALID_CALL; auto desc = m_texture->Desc(); pDesc->Width = desc->Width; pDesc->Height = desc->Height; pDesc->Format = desc->Format; pDesc->SampleDesc = desc->SampleDesc; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGISurface::Map( DXGI_MAPPED_RECT* pLockedRect, UINT MapFlags) { Com device; Com context; m_resource->GetDevice(&device); device->GetImmediateContext(&context); if (pLockedRect) { pLockedRect->Pitch = 0; pLockedRect->pBits = nullptr; } D3D11_MAP mapType; if (MapFlags & (DXGI_MAP_READ | DXGI_MAP_WRITE)) mapType = D3D11_MAP_READ_WRITE; else if (MapFlags & DXGI_MAP_READ) mapType = D3D11_MAP_READ; else if (MapFlags & (DXGI_MAP_WRITE | DXGI_MAP_DISCARD)) mapType = D3D11_MAP_WRITE_DISCARD; else if (MapFlags & DXGI_MAP_WRITE) mapType = D3D11_MAP_WRITE; else return DXGI_ERROR_INVALID_CALL; D3D11_MAPPED_SUBRESOURCE sr; HRESULT hr = context->Map(m_resource, 0, mapType, 0, pLockedRect ? &sr : nullptr); if (hr != S_OK) return hr; pLockedRect->Pitch = sr.RowPitch; pLockedRect->pBits = reinterpret_cast(sr.pData); return hr; } HRESULT STDMETHODCALLTYPE D3D11DXGISurface::Unmap() { Com device; Com context; m_resource->GetDevice(&device); device->GetImmediateContext(&context); context->Unmap(m_resource, 0); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetDC( BOOL Discard, HDC* phdc) { if (!m_gdiSurface) return DXGI_ERROR_INVALID_CALL; return m_gdiSurface->Acquire(Discard, phdc); } HRESULT STDMETHODCALLTYPE D3D11DXGISurface::ReleaseDC( RECT* pDirtyRect) { if (!m_gdiSurface) return DXGI_ERROR_INVALID_CALL; return m_gdiSurface->Release(pDirtyRect); } HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetResource( REFIID riid, void** ppParentResource, UINT* pSubresourceIndex) { HRESULT hr = m_resource->QueryInterface(riid, ppParentResource); if (pSubresourceIndex) *pSubresourceIndex = 0; return hr; } bool D3D11DXGISurface::isSurfaceCompatible() const { auto desc = m_texture->Desc(); return desc->ArraySize == 1 && desc->MipLevels == 1; } D3D11VkInteropSurface::D3D11VkInteropSurface( ID3D11Resource* pResource, D3D11CommonTexture* pTexture) : m_resource(pResource), m_texture (pTexture) { } D3D11VkInteropSurface::~D3D11VkInteropSurface() { } ULONG STDMETHODCALLTYPE D3D11VkInteropSurface::AddRef() { return m_resource->AddRef(); } ULONG STDMETHODCALLTYPE D3D11VkInteropSurface::Release() { return m_resource->Release(); } HRESULT STDMETHODCALLTYPE D3D11VkInteropSurface::QueryInterface( REFIID riid, void** ppvObject) { return m_resource->QueryInterface(riid, ppvObject); } HRESULT STDMETHODCALLTYPE D3D11VkInteropSurface::GetDevice( IDXGIVkInteropDevice** ppDevice) { Com device; m_resource->GetDevice(&device); return device->QueryInterface( __uuidof(IDXGIVkInteropDevice), reinterpret_cast(ppDevice)); } HRESULT STDMETHODCALLTYPE D3D11VkInteropSurface::GetVulkanImageInfo( VkImage* pHandle, VkImageLayout* pLayout, VkImageCreateInfo* pInfo) { const Rc image = m_texture->GetImage(); const DxvkImageCreateInfo& info = image->info(); if (pHandle != nullptr) *pHandle = image->handle(); if (pLayout != nullptr) *pLayout = info.layout; if (pInfo != nullptr) { // We currently don't support any extended structures if (pInfo->sType != VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO || pInfo->pNext != nullptr) return E_INVALIDARG; pInfo->flags = 0; pInfo->imageType = info.type; pInfo->format = info.format; pInfo->extent = info.extent; pInfo->mipLevels = info.mipLevels; pInfo->arrayLayers = info.numLayers; pInfo->samples = info.sampleCount; pInfo->tiling = info.tiling; pInfo->usage = info.usage; pInfo->sharingMode = VK_SHARING_MODE_EXCLUSIVE; pInfo->queueFamilyIndexCount = 0; pInfo->initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; } return S_OK; } /////////////////////////////////////////// // D 3 D 1 1 T E X T U R E 1 D D3D11Texture1D::D3D11Texture1D( D3D11Device* pDevice, const D3D11_COMMON_TEXTURE_DESC* pDesc, const D3D11_ON_12_RESOURCE_INFO* p11on12Info) : D3D11DeviceChild(pDevice), m_texture (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE1D, 0, VK_NULL_HANDLE, nullptr), m_interop (this, &m_texture), m_surface (this, &m_texture), m_resource(this, pDevice), m_d3d10 (this) { } D3D11Texture1D::~D3D11Texture1D() { } HRESULT STDMETHODCALLTYPE D3D11Texture1D::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11Resource) || riid == __uuidof(ID3D11Texture1D)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(ID3D10Resource) || riid == __uuidof(ID3D10Texture1D)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (m_surface.isSurfaceCompatible() && (riid == __uuidof(IDXGISurface) || riid == __uuidof(IDXGISurface1) || riid == __uuidof(IDXGISurface2))) { *ppvObject = ref(&m_surface); return S_OK; } if (riid == __uuidof(IDXGIObject) || riid == __uuidof(IDXGIDeviceSubObject) || riid == __uuidof(IDXGIResource) || riid == __uuidof(IDXGIResource1)) { *ppvObject = ref(&m_resource); return S_OK; } if (riid == __uuidof(IDXGIKeyedMutex)) return m_resource.GetKeyedMutex(ppvObject); if (riid == __uuidof(IDXGIVkInteropSurface)) { *ppvObject = ref(&m_interop); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D10Texture1D), riid)) { Logger::warn("D3D11Texture1D::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11Texture1D::GetType(D3D11_RESOURCE_DIMENSION *pResourceDimension) { *pResourceDimension = D3D11_RESOURCE_DIMENSION_TEXTURE1D; } UINT STDMETHODCALLTYPE D3D11Texture1D::GetEvictionPriority() { return DXGI_RESOURCE_PRIORITY_NORMAL; } void STDMETHODCALLTYPE D3D11Texture1D::SetEvictionPriority(UINT EvictionPriority) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D11Texture1D::SetEvictionPriority: Stub"); } void STDMETHODCALLTYPE D3D11Texture1D::GetDesc(D3D11_TEXTURE1D_DESC *pDesc) { pDesc->Width = m_texture.Desc()->Width; pDesc->MipLevels = m_texture.Desc()->MipLevels; pDesc->ArraySize = m_texture.Desc()->ArraySize; pDesc->Format = m_texture.Desc()->Format; pDesc->Usage = m_texture.Desc()->Usage; pDesc->BindFlags = m_texture.Desc()->BindFlags; pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags; pDesc->MiscFlags = m_texture.Desc()->MiscFlags; } void STDMETHODCALLTYPE D3D11Texture1D::SetDebugName(const char* pName) { m_texture.SetDebugName(pName); } /////////////////////////////////////////// // D 3 D 1 1 T E X T U R E 2 D D3D11Texture2D::D3D11Texture2D( D3D11Device* pDevice, const D3D11_COMMON_TEXTURE_DESC* pDesc, const D3D11_ON_12_RESOURCE_INFO* p11on12Info, HANDLE hSharedHandle) : D3D11DeviceChild(pDevice), m_texture (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE2D, 0, VK_NULL_HANDLE, hSharedHandle), m_interop (this, &m_texture), m_surface (this, &m_texture), m_resource (this, pDevice), m_d3d10 (this), m_swapChain (nullptr) { } D3D11Texture2D::D3D11Texture2D( D3D11Device* pDevice, const D3D11_COMMON_TEXTURE_DESC* pDesc, DXGI_USAGE DxgiUsage, VkImage vkImage) : D3D11DeviceChild(pDevice), m_texture (this, pDevice, pDesc, nullptr, D3D11_RESOURCE_DIMENSION_TEXTURE2D, DxgiUsage, vkImage, nullptr), m_interop (this, &m_texture), m_surface (this, &m_texture), m_resource (this, pDevice), m_d3d10 (this), m_swapChain (nullptr) { } D3D11Texture2D::D3D11Texture2D( D3D11Device* pDevice, IUnknown* pSwapChain, const D3D11_COMMON_TEXTURE_DESC* pDesc, DXGI_USAGE DxgiUsage) : D3D11DeviceChild(pDevice), m_texture (this, pDevice, pDesc, nullptr, D3D11_RESOURCE_DIMENSION_TEXTURE2D, DxgiUsage, VK_NULL_HANDLE, nullptr), m_interop (this, &m_texture), m_surface (this, &m_texture), m_resource (this, pDevice), m_d3d10 (this), m_swapChain (pSwapChain) { } D3D11Texture2D::~D3D11Texture2D() { } ULONG STDMETHODCALLTYPE D3D11Texture2D::AddRef() { uint32_t refCount = D3D11DeviceChild::AddRef(); if (unlikely(m_swapChain != nullptr)) { if (refCount == 1) m_swapChain->AddRef(); } return refCount; } ULONG STDMETHODCALLTYPE D3D11Texture2D::Release() { IUnknown* swapChain = m_swapChain; uint32_t refCount = D3D11DeviceChild::Release(); if (unlikely(swapChain != nullptr)) { if (refCount == 0) swapChain->Release(); } return refCount; } HRESULT STDMETHODCALLTYPE D3D11Texture2D::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11Resource) || riid == __uuidof(ID3D11Texture2D) || riid == __uuidof(ID3D11Texture2D1)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(ID3D10Resource) || riid == __uuidof(ID3D10Texture2D)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (m_surface.isSurfaceCompatible() && (riid == __uuidof(IDXGISurface) || riid == __uuidof(IDXGISurface1) || riid == __uuidof(IDXGISurface2))) { *ppvObject = ref(&m_surface); return S_OK; } if (riid == __uuidof(IDXGIObject) || riid == __uuidof(IDXGIDeviceSubObject) || riid == __uuidof(IDXGIResource) || riid == __uuidof(IDXGIResource1)) { *ppvObject = ref(&m_resource); return S_OK; } if (riid == __uuidof(IDXGIKeyedMutex)) return m_resource.GetKeyedMutex(ppvObject); if (riid == __uuidof(IDXGIVkInteropSurface)) { *ppvObject = ref(&m_interop); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D10Texture2D), riid)) { Logger::warn("D3D11Texture2D::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11Texture2D::GetType(D3D11_RESOURCE_DIMENSION *pResourceDimension) { *pResourceDimension = D3D11_RESOURCE_DIMENSION_TEXTURE2D; } UINT STDMETHODCALLTYPE D3D11Texture2D::GetEvictionPriority() { return DXGI_RESOURCE_PRIORITY_NORMAL; } void STDMETHODCALLTYPE D3D11Texture2D::SetEvictionPriority(UINT EvictionPriority) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D11Texture2D::SetEvictionPriority: Stub"); } void STDMETHODCALLTYPE D3D11Texture2D::GetDesc(D3D11_TEXTURE2D_DESC* pDesc) { pDesc->Width = m_texture.Desc()->Width; pDesc->Height = m_texture.Desc()->Height; pDesc->MipLevels = m_texture.Desc()->MipLevels; pDesc->ArraySize = m_texture.Desc()->ArraySize; pDesc->Format = m_texture.Desc()->Format; pDesc->SampleDesc = m_texture.Desc()->SampleDesc; pDesc->Usage = m_texture.Desc()->Usage; pDesc->BindFlags = m_texture.Desc()->BindFlags; pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags; pDesc->MiscFlags = m_texture.Desc()->MiscFlags; } void STDMETHODCALLTYPE D3D11Texture2D::GetDesc1(D3D11_TEXTURE2D_DESC1* pDesc) { pDesc->Width = m_texture.Desc()->Width; pDesc->Height = m_texture.Desc()->Height; pDesc->MipLevels = m_texture.Desc()->MipLevels; pDesc->ArraySize = m_texture.Desc()->ArraySize; pDesc->Format = m_texture.Desc()->Format; pDesc->SampleDesc = m_texture.Desc()->SampleDesc; pDesc->Usage = m_texture.Desc()->Usage; pDesc->BindFlags = m_texture.Desc()->BindFlags; pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags; pDesc->MiscFlags = m_texture.Desc()->MiscFlags; pDesc->TextureLayout = m_texture.Desc()->TextureLayout; } void STDMETHODCALLTYPE D3D11Texture2D::SetDebugName(const char* pName) { m_texture.SetDebugName(pName); } /////////////////////////////////////////// // D 3 D 1 1 T E X T U R E 3 D D3D11Texture3D::D3D11Texture3D( D3D11Device* pDevice, const D3D11_COMMON_TEXTURE_DESC* pDesc, const D3D11_ON_12_RESOURCE_INFO* p11on12Info) : D3D11DeviceChild(pDevice), m_texture (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE3D, 0, VK_NULL_HANDLE, nullptr), m_interop (this, &m_texture), m_resource(this, pDevice), m_d3d10 (this) { } D3D11Texture3D::~D3D11Texture3D() { } HRESULT STDMETHODCALLTYPE D3D11Texture3D::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11Resource) || riid == __uuidof(ID3D11Texture3D) || riid == __uuidof(ID3D11Texture3D1)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(ID3D10Resource) || riid == __uuidof(ID3D10Texture3D)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (riid == __uuidof(IDXGIObject) || riid == __uuidof(IDXGIDeviceSubObject) || riid == __uuidof(IDXGIResource) || riid == __uuidof(IDXGIResource1)) { *ppvObject = ref(&m_resource); return S_OK; } if (riid == __uuidof(IDXGIKeyedMutex)) return m_resource.GetKeyedMutex(ppvObject); if (riid == __uuidof(IDXGIVkInteropSurface)) { *ppvObject = ref(&m_interop); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D10Texture3D), riid)) { Logger::warn("D3D11Texture3D::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11Texture3D::GetType(D3D11_RESOURCE_DIMENSION *pResourceDimension) { *pResourceDimension = D3D11_RESOURCE_DIMENSION_TEXTURE3D; } UINT STDMETHODCALLTYPE D3D11Texture3D::GetEvictionPriority() { return DXGI_RESOURCE_PRIORITY_NORMAL; } void STDMETHODCALLTYPE D3D11Texture3D::SetEvictionPriority(UINT EvictionPriority) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D11Texture3D::SetEvictionPriority: Stub"); } void STDMETHODCALLTYPE D3D11Texture3D::GetDesc(D3D11_TEXTURE3D_DESC* pDesc) { pDesc->Width = m_texture.Desc()->Width; pDesc->Height = m_texture.Desc()->Height; pDesc->Depth = m_texture.Desc()->Depth; pDesc->MipLevels = m_texture.Desc()->MipLevels; pDesc->Format = m_texture.Desc()->Format; pDesc->Usage = m_texture.Desc()->Usage; pDesc->BindFlags = m_texture.Desc()->BindFlags; pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags; pDesc->MiscFlags = m_texture.Desc()->MiscFlags; } void STDMETHODCALLTYPE D3D11Texture3D::GetDesc1(D3D11_TEXTURE3D_DESC1* pDesc) { pDesc->Width = m_texture.Desc()->Width; pDesc->Height = m_texture.Desc()->Height; pDesc->Depth = m_texture.Desc()->Depth; pDesc->MipLevels = m_texture.Desc()->MipLevels; pDesc->Format = m_texture.Desc()->Format; pDesc->Usage = m_texture.Desc()->Usage; pDesc->BindFlags = m_texture.Desc()->BindFlags; pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags; pDesc->MiscFlags = m_texture.Desc()->MiscFlags; } void STDMETHODCALLTYPE D3D11Texture3D::SetDebugName(const char* pName) { m_texture.SetDebugName(pName); } D3D11CommonTexture* GetCommonTexture(ID3D11Resource* pResource) { D3D11_RESOURCE_DIMENSION dimension = D3D11_RESOURCE_DIMENSION_UNKNOWN; pResource->GetType(&dimension); switch (dimension) { case D3D11_RESOURCE_DIMENSION_TEXTURE1D: return static_cast(pResource)->GetCommonTexture(); case D3D11_RESOURCE_DIMENSION_TEXTURE2D: return static_cast(pResource)->GetCommonTexture(); case D3D11_RESOURCE_DIMENSION_TEXTURE3D: return static_cast(pResource)->GetCommonTexture(); default: return nullptr; } } } dxvk-2.6.1/src/d3d11/d3d11_texture.h000066400000000000000000000646441477473124000166730ustar00rootroot00000000000000#pragma once #include "../util/util_small_vector.h" #include "../dxvk/dxvk_cs.h" #include "../dxvk/dxvk_device.h" #include "../d3d10/d3d10_texture.h" #include "d3d11_device_child.h" #include "d3d11_interfaces.h" #include "d3d11_on_12.h" #include "d3d11_resource.h" namespace dxvk { class D3D11Device; class D3D11GDISurface; /** * \brief Image memory mapping mode * * Determines how exactly \c Map will * behave when mapping an image. */ enum D3D11_COMMON_TEXTURE_MAP_MODE { D3D11_COMMON_TEXTURE_MAP_MODE_NONE, ///< Not mapped D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER, ///< Mapped through buffer D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC, ///< Mapped through temporary buffer D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT, ///< Directly mapped to host mem D3D11_COMMON_TEXTURE_MAP_MODE_STAGING, ///< Buffer only, no image }; /** * \brief Common texture description * * Contains all members that can be * defined for 1D, 2D and 3D textures. */ struct D3D11_COMMON_TEXTURE_DESC { UINT Width; UINT Height; UINT Depth; UINT MipLevels; UINT ArraySize; DXGI_FORMAT Format; DXGI_SAMPLE_DESC SampleDesc; D3D11_USAGE Usage; UINT BindFlags; UINT CPUAccessFlags; UINT MiscFlags; D3D11_TEXTURE_LAYOUT TextureLayout; }; /** * \brief Packed subresource layout */ struct D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT { UINT64 Offset; UINT64 Size; UINT RowPitch; UINT DepthPitch; }; /** * \brief Region */ struct D3D11_COMMON_TEXTURE_REGION { VkOffset3D Offset; VkExtent3D Extent; }; /** * \brief D3D11 common texture object * * This class implements common texture methods and * aims to work around the issue that there are three * different interfaces for basically the same thing. */ class D3D11CommonTexture { public: static constexpr uint32_t UnmappedSubresource = ~0u; D3D11CommonTexture( ID3D11Resource* pInterface, D3D11Device* pDevice, const D3D11_COMMON_TEXTURE_DESC* pDesc, const D3D11_ON_12_RESOURCE_INFO* p11on12Info, D3D11_RESOURCE_DIMENSION Dimension, DXGI_USAGE DxgiUsage, VkImage vkImage, HANDLE hSharedHandle); ~D3D11CommonTexture(); /** * \brief Retrieves resource interface * \returns Resource interface */ ID3D11Resource* GetInterface() const { return m_interface; } /** * \brief Texture properties * * The returned data can be used to fill in * \c D3D11_TEXTURE2D_DESC and similar structs. * \returns Pointer to texture description */ const D3D11_COMMON_TEXTURE_DESC* Desc() const { return &m_desc; } /** * \brief Retrieves D3D11 texture type * \returns D3D11 resource dimension */ D3D11_RESOURCE_DIMENSION GetDimension() const { return m_dimension; } /** * \brief Retrieves Vulkan image type * * Returns the image type based on the D3D11 resource * dimension. Also works if there is no actual image. * \returns Vulkan image type */ VkImageType GetVkImageType() const { return GetImageTypeFromResourceDim(m_dimension); } /** * \brief Computes extent of a given mip level * * This also works for staging resources that have no image. * \param [in] Level Mip level to compute the size of */ VkExtent3D MipLevelExtent(uint32_t Level) const { return util::computeMipLevelExtent( VkExtent3D { m_desc.Width, m_desc.Height, m_desc.Depth }, Level); } /** * \brief Special DXGI usage flags * * Flags that are set in addition to the bind * flags. Zero for application-created textures. * \returns DXGI usage flags */ DXGI_USAGE GetDxgiUsage() const { return m_dxgiUsage; } /** * \brief Counts number of subresources * \returns Number of subresources */ UINT CountSubresources() const { return m_desc.ArraySize * m_desc.MipLevels; } /** * \brief Map mode * \returns Map mode */ D3D11_COMMON_TEXTURE_MAP_MODE GetMapMode() const { return m_mapMode; } /** * \brief Checks whether this texture has an image * * Staging textures will not use an image, only mapped buffers. * \returns \c true for non-staging textures. */ bool HasImage() const { return m_mapMode != D3D11_COMMON_TEXTURE_MAP_MODE_STAGING; } /** * \brief Checks whether this texture has persistent buffers * \returns \c true for buffer-mapped textures or staging textures. */ bool HasPersistentBuffers() const { return m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER || m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING; } /** * \brief Map type of a given subresource * * \param [in] Subresource Subresource index * \returns Current map mode of that subresource */ uint32_t GetMapType(UINT Subresource) const { return Subresource < m_mapInfo.size() ? m_mapInfo[Subresource].mapType : UnmappedSubresource; } /** * \brief Sets map type for a given subresource * * Also ensures taht a staging buffer is created * in case of dynamic mapping. * \param [in] Subresource The subresource * \param [in] MapType The map type */ void NotifyMap(UINT Subresource, D3D11_MAP MapType) { if (likely(Subresource < m_mapInfo.size())) { m_mapInfo[Subresource].mapType = uint32_t(MapType); if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC) CreateMappedBuffer(Subresource); } } /** * \brief Resets map info for a given subresource * * For dynamic mapping, this will also free the * staging buffer. * \param [in] Subresource The subresource */ void NotifyUnmap(UINT Subresource) { if (likely(Subresource < m_mapInfo.size())) { m_mapInfo[Subresource].mapType = UnmappedSubresource; if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC) FreeMappedBuffer(Subresource); if (Subresource < m_buffers.size()) m_buffers[Subresource].dirtyRegions.clear(); } } /** * \brief The DXVK image * \returns The DXVK image */ Rc GetImage() const { return m_image; } /** * \brief Mapped subresource buffer * * \param [in] Subresource Subresource index * \returns Mapped subresource buffer */ Rc GetMappedBuffer(UINT Subresource) const { return Subresource < m_buffers.size() ? m_buffers[Subresource].buffer : Rc(); } /** * \brief Discards mapped buffer slice for a given subresource * * \param [in] Subresource Subresource to discard * \returns Newly allocated mapped buffer slice */ Rc DiscardSlice(UINT Subresource) { if (Subresource < m_buffers.size()) { Rc slice = m_buffers[Subresource].buffer->allocateStorage(); m_buffers[Subresource].slice = slice; return slice; } else { return nullptr; } } /** * \brief Retrieves mapped buffer slice for a given subresource * * \param [in] Subresource Subresource index to query * \returns Currently mapped buffer slice */ Rc GetMappedSlice(UINT Subresource) const { return Subresource < m_buffers.size() ? m_buffers[Subresource].slice : nullptr; } /** * \brief Returns underlying packed Vulkan format * * This works even for staging resources that have no image. * Note that for depth-stencil resources, the returned format * may be different from the image format on some systems. * \returns Packed Vulkan format */ VkFormat GetPackedFormat() const { return m_packedFormat; } /** * \brief Checks whether the resource is eligible for tracking * * Mapped resources with no bind flags can be tracked so that * mapping them will not necessarily cause a CS thread sync. * \returns \c true if tracking is supported for this resource */ bool HasSequenceNumber() const { return m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING; } /** * \brief Tracks sequence number for a given subresource * * Stores which CS chunk the resource was last used on. * \param [in] Subresource Subresource index * \param [in] Seq Sequence number */ void TrackSequenceNumber(UINT Subresource, uint64_t Seq) { if (Subresource < m_mapInfo.size()) m_mapInfo[Subresource].seq = Seq; } /** * \brief Queries sequence number for a given subresource * * Returns which CS chunk the resource was last used on. * \param [in] Subresource Subresource index * \returns Sequence number for the given subresource */ uint64_t GetSequenceNumber(UINT Subresource) { if (HasSequenceNumber()) { return Subresource < m_buffers.size() ? m_mapInfo[Subresource].seq : 0ull; } else { return DxvkCsThread::SynchronizeAll; } } /** * \brief Allocates new backing storage * \returns New backing storage for the image */ Rc AllocStorage() { return m_image->allocateStorage(); } /** * \brief Discards backing storage * * Also updates the mapped pointer if the image is mapped. * \returns New backing storage for the image */ Rc DiscardStorage() { auto storage = m_image->allocateStorage(); m_mapPtr = storage->mapPtr(); return storage; } /** * \brief Queries map pointer of the raw image * * If the image is mapped directly, the returned pointer will * point directly to the image, otherwise it will point to a * buffer that contains image data. * \param [in] Subresource Subresource index * \param [in] Offset Offset derived from the subresource layout */ void* GetMapPtr(uint32_t Subresource, size_t Offset) const { switch (m_mapMode) { case D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT: return reinterpret_cast(m_mapPtr) + Offset; case D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER: case D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC: case D3D11_COMMON_TEXTURE_MAP_MODE_STAGING: return reinterpret_cast(m_buffers[Subresource].slice->mapPtr()) + Offset; case D3D11_COMMON_TEXTURE_MAP_MODE_NONE: return nullptr; } return nullptr; } /** * \brief Adds a dirty region * * This region will be updated on Unmap. * \param [in] Subresource Subresource index * \param [in] Offset Region offset * \param [in] Extent Region extent */ void AddDirtyRegion(UINT Subresource, VkOffset3D Offset, VkExtent3D Extent) { D3D11_COMMON_TEXTURE_REGION region; region.Offset = Offset; region.Extent = Extent; if (Subresource < m_buffers.size()) m_buffers[Subresource].dirtyRegions.push_back(region); } /** * \brief Counts dirty regions * * \param [in] Subresource Subresource index * \returns Dirty region count */ UINT GetDirtyRegionCount(UINT Subresource) { return (Subresource < m_buffers.size()) ? UINT(m_buffers[Subresource].dirtyRegions.size()) : UINT(0); } /** * \brief Queries a dirty regions * * \param [in] Subresource Subresource index * \param [in] Region Region index * \returns Dirty region */ D3D11_COMMON_TEXTURE_REGION GetDirtyRegion(UINT Subresource, UINT Region) { return m_buffers[Subresource].dirtyRegions[Region]; } /** * \brief Checks whether or not to track dirty regions * * If this returns true, then any functions that update the * mapped staging buffer must also track dirty regions while * the image is mapped. Otherwise, the entire image is dirty. * \returns \c true if dirty regions must be tracked */ bool NeedsDirtyRegionTracking() const { // Only set this for images where Map can't return a pointer return m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER && m_desc.Usage == D3D11_USAGE_DEFAULT && m_desc.TextureLayout == D3D11_TEXTURE_LAYOUT_UNDEFINED; } /** * \brief Computes pixel offset into mapped buffer * * \param [in] Subresource Subresource index * \param [in] Subresource Plane index * \param [in] Offset Pixel coordinate to compute offset for * \returns Offset into mapped subresource buffer, in pixels */ VkDeviceSize ComputeMappedOffset(UINT Subresource, UINT Plane, VkOffset3D Offset) const; /** * \brief Computes subresource from the subresource index * * Used by some functions that operate on only * one subresource, such as \c UpdateSubresource. * \param [in] Aspect The image aspect * \param [in] Subresource Subresource index * \returns The Vulkan image subresource */ VkImageSubresource GetSubresourceFromIndex( VkImageAspectFlags Aspect, UINT Subresource) const { VkImageSubresource result; result.aspectMask = Aspect; result.mipLevel = Subresource % m_desc.MipLevels; result.arrayLayer = Subresource / m_desc.MipLevels; return result; } /** * \brief Computes subresource layout for the given subresource * * \param [in] AspectMask The image aspect * \param [in] Subresource Subresource index * \returns Memory layout of the mapped subresource */ D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT GetSubresourceLayout( VkImageAspectFlags AspectMask, UINT Subresource) const; /** * \brief Format mode * * Determines whether the image is going to * be used as a color image or a depth image. * \returns Format mode */ DXGI_VK_FORMAT_MODE GetFormatMode() const; /** * \brief Computes plane count * * For non-planar formats, the plane count will be 1. * \returns Image plane count */ uint32_t GetPlaneCount() const; /** * \brief Checks whether a view can be created for this textue * * View formats are only compatible if they are either identical * or from the same family of typeless formats, where the resource * format must be typeless and the view format must be typed. This * will also check whether the required bind flags are supported. * \param [in] BindFlags Bind flags for the view * \param [in] Format The desired view format * \param [in] Plane Plane slice for planar formats * \returns \c true if the format is compatible */ bool CheckViewCompatibility( UINT BindFlags, DXGI_FORMAT Format, UINT Plane) const; /** * \brief Retrieves D3D11on12 resource info * \returns 11on12 resource info */ D3D11_ON_12_RESOURCE_INFO Get11on12Info() const { return m_11on12; } /** * \brief Sets debug name for texture * * Passes the given name to the backing image or buffer. * \param [in] name Debug name */ void SetDebugName(const char* pName); /** * \brief Normalizes and validates texture description * * Fills in undefined values and validates the texture * parameters. Any error returned by this method should * be forwarded to the application. * \param [in,out] pDesc Texture description * \returns \c S_OK if the parameters are valid */ static HRESULT NormalizeTextureProperties( D3D11_COMMON_TEXTURE_DESC* pDesc); /** * \brief Initializes D3D11 texture description from D3D12 * * \param [in] pResource D3D12 resource * \param [in] pResourceFlags D3D11 flag overrides * \param [out] pTextureDesc D3D11 buffer description * \returns \c S_OK if the parameters are valid */ static HRESULT GetDescFromD3D12( ID3D12Resource* pResource, const D3D11_RESOURCE_FLAGS* pResourceFlags, D3D11_COMMON_TEXTURE_DESC* pTextureDesc); private: struct MappedBuffer { Rc buffer; Rc slice; std::vector dirtyRegions; }; struct MappedInfo { D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT layout = { }; uint32_t mapType = UnmappedSubresource; uint64_t seq = 0u; }; ID3D11Resource* m_interface; D3D11Device* m_device; D3D11_RESOURCE_DIMENSION m_dimension; D3D11_COMMON_TEXTURE_DESC m_desc; D3D11_ON_12_RESOURCE_INFO m_11on12; D3D11_COMMON_TEXTURE_MAP_MODE m_mapMode; DXGI_USAGE m_dxgiUsage; VkFormat m_packedFormat; Rc m_image; small_vector m_buffers; small_vector m_mapInfo; void* m_mapPtr = nullptr; void CreateMappedBuffer( UINT Subresource); void FreeMappedBuffer( UINT Subresource); BOOL CheckImageSupport( const DxvkImageCreateInfo* pImageInfo, VkImageTiling Tiling) const; BOOL CheckFormatFeatureSupport( VkFormat Format, VkFormatFeatureFlags2 Features) const; std::pair DetermineMapMode( const DxvkImageCreateInfo* pImageInfo) const; D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT DetermineSubresourceLayout( const DxvkImageCreateInfo* pImageInfo, const VkImageSubresource& subresource) const; void ExportImageInfo(); static BOOL IsR32UavCompatibleFormat( DXGI_FORMAT Format); static VkImageType GetImageTypeFromResourceDim( D3D11_RESOURCE_DIMENSION Dimension); static VkImageLayout OptimizeLayout( VkImageUsageFlags Usage); }; /** * \brief IDXGISurface implementation for D3D11 textures * * Provides an implementation for 2D textures that * have only one array layer and one mip level. */ class D3D11DXGISurface : public IDXGISurface2 { public: D3D11DXGISurface( ID3D11Resource* pResource, D3D11CommonTexture* pTexture); ~D3D11DXGISurface(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID Name, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID Name, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID Name, const IUnknown* pUnknown); HRESULT STDMETHODCALLTYPE GetParent( REFIID riid, void** ppParent); HRESULT STDMETHODCALLTYPE GetDevice( REFIID riid, void** ppDevice); HRESULT STDMETHODCALLTYPE GetDesc( DXGI_SURFACE_DESC* pDesc); HRESULT STDMETHODCALLTYPE Map( DXGI_MAPPED_RECT* pLockedRect, UINT MapFlags); HRESULT STDMETHODCALLTYPE Unmap(); HRESULT STDMETHODCALLTYPE GetDC( BOOL Discard, HDC* phdc); HRESULT STDMETHODCALLTYPE ReleaseDC( RECT* pDirtyRect); HRESULT STDMETHODCALLTYPE GetResource( REFIID riid, void** ppParentResource, UINT* pSubresourceIndex); bool isSurfaceCompatible() const; private: ID3D11Resource* m_resource; D3D11CommonTexture* m_texture; D3D11GDISurface* m_gdiSurface; }; /** * \brief Common texture interop class * * Provides access to the underlying Vulkan * texture to external Vulkan libraries. */ class D3D11VkInteropSurface : public IDXGIVkInteropSurface { public: D3D11VkInteropSurface( ID3D11Resource* pResource, D3D11CommonTexture* pTexture); ~D3D11VkInteropSurface(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE GetDevice( IDXGIVkInteropDevice** ppDevice); HRESULT STDMETHODCALLTYPE GetVulkanImageInfo( VkImage* pHandle, VkImageLayout* pLayout, VkImageCreateInfo* pInfo); private: ID3D11Resource* m_resource; D3D11CommonTexture* m_texture; }; /////////////////////////////////////////// // D 3 D 1 1 T E X T U R E 1 D class D3D11Texture1D : public D3D11DeviceChild { public: D3D11Texture1D( D3D11Device* pDevice, const D3D11_COMMON_TEXTURE_DESC* pDesc, const D3D11_ON_12_RESOURCE_INFO* p11on12Info); ~D3D11Texture1D(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) final; void STDMETHODCALLTYPE GetType( D3D11_RESOURCE_DIMENSION *pResourceDimension) final; UINT STDMETHODCALLTYPE GetEvictionPriority() final; void STDMETHODCALLTYPE SetEvictionPriority(UINT EvictionPriority) final; void STDMETHODCALLTYPE GetDesc( D3D11_TEXTURE1D_DESC *pDesc) final; void STDMETHODCALLTYPE SetDebugName(const char* pName) final; D3D11CommonTexture* GetCommonTexture() { return &m_texture; } D3D10Texture1D* GetD3D10Iface() { return &m_d3d10; } private: D3D11CommonTexture m_texture; D3D11VkInteropSurface m_interop; D3D11DXGISurface m_surface; D3D11DXGIResource m_resource; D3D10Texture1D m_d3d10; }; /////////////////////////////////////////// // D 3 D 1 1 T E X T U R E 2 D class D3D11Texture2D : public D3D11DeviceChild { public: D3D11Texture2D( D3D11Device* pDevice, const D3D11_COMMON_TEXTURE_DESC* pDesc, const D3D11_ON_12_RESOURCE_INFO* p11on12Info, HANDLE hSharedHandle); D3D11Texture2D( D3D11Device* pDevice, const D3D11_COMMON_TEXTURE_DESC* pDesc, DXGI_USAGE DxgiUsage, VkImage vkImage); D3D11Texture2D( D3D11Device* pDevice, IUnknown* pSwapChain, const D3D11_COMMON_TEXTURE_DESC* pDesc, DXGI_USAGE DxgiUsage); ~D3D11Texture2D(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) final; void STDMETHODCALLTYPE GetType( D3D11_RESOURCE_DIMENSION *pResourceDimension) final; UINT STDMETHODCALLTYPE GetEvictionPriority() final; void STDMETHODCALLTYPE SetEvictionPriority(UINT EvictionPriority) final; void STDMETHODCALLTYPE GetDesc( D3D11_TEXTURE2D_DESC* pDesc) final; void STDMETHODCALLTYPE GetDesc1( D3D11_TEXTURE2D_DESC1* pDesc) final; void STDMETHODCALLTYPE SetDebugName(const char* pName) final; D3D11CommonTexture* GetCommonTexture() { return &m_texture; } D3D10Texture2D* GetD3D10Iface() { return &m_d3d10; } private: D3D11CommonTexture m_texture; D3D11VkInteropSurface m_interop; D3D11DXGISurface m_surface; D3D11DXGIResource m_resource; D3D10Texture2D m_d3d10; IUnknown* m_swapChain; }; /////////////////////////////////////////// // D 3 D 1 1 T E X T U R E 3 D class D3D11Texture3D : public D3D11DeviceChild { public: D3D11Texture3D( D3D11Device* pDevice, const D3D11_COMMON_TEXTURE_DESC* pDesc, const D3D11_ON_12_RESOURCE_INFO* p11on12Info); ~D3D11Texture3D(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) final; void STDMETHODCALLTYPE GetType( D3D11_RESOURCE_DIMENSION *pResourceDimension) final; UINT STDMETHODCALLTYPE GetEvictionPriority() final; void STDMETHODCALLTYPE SetEvictionPriority(UINT EvictionPriority) final; void STDMETHODCALLTYPE GetDesc( D3D11_TEXTURE3D_DESC* pDesc) final; void STDMETHODCALLTYPE GetDesc1( D3D11_TEXTURE3D_DESC1* pDesc) final; void STDMETHODCALLTYPE SetDebugName(const char* pName) final; D3D11CommonTexture* GetCommonTexture() { return &m_texture; } D3D10Texture3D* GetD3D10Iface() { return &m_d3d10; } private: D3D11CommonTexture m_texture; D3D11VkInteropSurface m_interop; D3D11DXGIResource m_resource; D3D10Texture3D m_d3d10; }; /** * \brief Retrieves texture from resource pointer * * \param [in] pResource The resource to query * \returns Pointer to texture info, or \c nullptr */ D3D11CommonTexture* GetCommonTexture( ID3D11Resource* pResource); } dxvk-2.6.1/src/d3d11/d3d11_util.cpp000066400000000000000000000113231477473124000164650ustar00rootroot00000000000000#include "d3d11_util.h" namespace dxvk { HRESULT DecodeSampleCount(UINT Count, VkSampleCountFlagBits* pCount) { VkSampleCountFlagBits flag; switch (Count) { case 1: flag = VK_SAMPLE_COUNT_1_BIT; break; case 2: flag = VK_SAMPLE_COUNT_2_BIT; break; case 4: flag = VK_SAMPLE_COUNT_4_BIT; break; case 8: flag = VK_SAMPLE_COUNT_8_BIT; break; case 16: flag = VK_SAMPLE_COUNT_16_BIT; break; default: return E_INVALIDARG; } if (pCount != nullptr) { *pCount = flag; return S_OK; } return S_FALSE; } VkSamplerAddressMode DecodeAddressMode( D3D11_TEXTURE_ADDRESS_MODE mode) { switch (mode) { case D3D11_TEXTURE_ADDRESS_WRAP: return VK_SAMPLER_ADDRESS_MODE_REPEAT; case D3D11_TEXTURE_ADDRESS_MIRROR: return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; case D3D11_TEXTURE_ADDRESS_CLAMP: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; case D3D11_TEXTURE_ADDRESS_BORDER: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; case D3D11_TEXTURE_ADDRESS_MIRROR_ONCE: return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; default: Logger::err(str::format("D3D11: Unsupported address mode: ", mode)); return VK_SAMPLER_ADDRESS_MODE_REPEAT; } } VkCompareOp DecodeCompareOp(D3D11_COMPARISON_FUNC Mode) { switch (Mode) { case D3D11_COMPARISON_NEVER: return VK_COMPARE_OP_NEVER; case D3D11_COMPARISON_LESS: return VK_COMPARE_OP_LESS; case D3D11_COMPARISON_EQUAL: return VK_COMPARE_OP_EQUAL; case D3D11_COMPARISON_LESS_EQUAL: return VK_COMPARE_OP_LESS_OR_EQUAL; case D3D11_COMPARISON_GREATER: return VK_COMPARE_OP_GREATER; case D3D11_COMPARISON_NOT_EQUAL: return VK_COMPARE_OP_NOT_EQUAL; case D3D11_COMPARISON_GREATER_EQUAL: return VK_COMPARE_OP_GREATER_OR_EQUAL; case D3D11_COMPARISON_ALWAYS: return VK_COMPARE_OP_ALWAYS; } if (Mode != 0) // prevent log spamming when apps use ZeroMemory Logger::err(str::format("D3D11: Unsupported compare op: ", Mode)); return VK_COMPARE_OP_NEVER; } VkSamplerReductionMode DecodeReductionMode( UINT Filter) { switch (Filter & 0x180) { default: return VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE; case 0x100: return VK_SAMPLER_REDUCTION_MODE_MIN; case 0x180: return VK_SAMPLER_REDUCTION_MODE_MAX; } } VkConservativeRasterizationModeEXT DecodeConservativeRasterizationMode( D3D11_CONSERVATIVE_RASTERIZATION_MODE Mode) { switch (Mode) { case D3D11_CONSERVATIVE_RASTERIZATION_MODE_OFF: return VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT; case D3D11_CONSERVATIVE_RASTERIZATION_MODE_ON: return VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT; } Logger::err(str::format("D3D11: Unsupported conservative raster mode: ", Mode)); return VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT; } VkFormatFeatureFlags2 GetBufferFormatFeatures(UINT BindFlags) { VkFormatFeatureFlags2 features = 0; if (BindFlags & D3D11_BIND_SHADER_RESOURCE) features |= VK_FORMAT_FEATURE_2_UNIFORM_TEXEL_BUFFER_BIT; if (BindFlags & D3D11_BIND_UNORDERED_ACCESS) features |= VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT; return features; } VkFormatFeatureFlags2 GetImageFormatFeatures(UINT BindFlags) { VkFormatFeatureFlags2 features = 0; if (BindFlags & D3D11_BIND_DEPTH_STENCIL) features |= VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT; if (BindFlags & D3D11_BIND_RENDER_TARGET) features |= VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT; if (BindFlags & D3D11_BIND_SHADER_RESOURCE) features |= VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT; if (BindFlags & D3D11_BIND_UNORDERED_ACCESS) features |= VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT; return features; } VkFormat GetPackedDepthStencilFormat(DXGI_FORMAT Format) { switch (Format) { case DXGI_FORMAT_R24G8_TYPELESS: case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: case DXGI_FORMAT_X24_TYPELESS_G8_UINT: case DXGI_FORMAT_D24_UNORM_S8_UINT: return VK_FORMAT_D24_UNORM_S8_UINT; case DXGI_FORMAT_R32G8X24_TYPELESS: case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: return VK_FORMAT_D32_SFLOAT_S8_UINT; default: return VK_FORMAT_UNDEFINED; } } BOOL IsMinMaxFilter(D3D11_FILTER Filter) { return DecodeReductionMode(uint32_t(Filter)) != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE; } }dxvk-2.6.1/src/d3d11/d3d11_util.h000066400000000000000000000042701477473124000161350ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_device.h" #include "../dxbc/dxbc_util.h" #include "d3d11_include.h" namespace dxvk { template UINT CompactSparseList(T* pData, UINT Mask) { uint32_t count = 0; for (uint32_t id : bit::BitMask(Mask)) pData[count++] = pData[id]; return count; } HRESULT DecodeSampleCount( UINT Count, VkSampleCountFlagBits* pCount); VkSamplerAddressMode DecodeAddressMode( D3D11_TEXTURE_ADDRESS_MODE mode); VkCompareOp DecodeCompareOp( D3D11_COMPARISON_FUNC Mode); VkSamplerReductionMode DecodeReductionMode( UINT Filter); VkConservativeRasterizationModeEXT DecodeConservativeRasterizationMode( D3D11_CONSERVATIVE_RASTERIZATION_MODE Mode); VkFormatFeatureFlags2 GetBufferFormatFeatures( UINT BindFlags); VkFormatFeatureFlags2 GetImageFormatFeatures( UINT BindFlags); VkFormat GetPackedDepthStencilFormat( DXGI_FORMAT Format); BOOL IsMinMaxFilter(D3D11_FILTER Filter); /** * \brief Translates D3D11 shader stage to corresponding Vulkan stage * * \param [in] ProgramType DXBC program type * \returns Corresponding Vulkan shader stage */ constexpr VkShaderStageFlagBits GetShaderStage(DxbcProgramType ProgramType) { constexpr uint64_t lut = (uint64_t(VK_SHADER_STAGE_VERTEX_BIT) << (8u * uint32_t(DxbcProgramType::VertexShader))) | (uint64_t(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) << (8u * uint32_t(DxbcProgramType::HullShader))) | (uint64_t(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) << (8u * uint32_t(DxbcProgramType::DomainShader))) | (uint64_t(VK_SHADER_STAGE_GEOMETRY_BIT) << (8u * uint32_t(DxbcProgramType::GeometryShader))) | (uint64_t(VK_SHADER_STAGE_FRAGMENT_BIT) << (8u * uint32_t(DxbcProgramType::PixelShader))) | (uint64_t(VK_SHADER_STAGE_COMPUTE_BIT) << (8u * uint32_t(DxbcProgramType::ComputeShader))); return VkShaderStageFlagBits((lut >> (8u * uint32_t(ProgramType))) & 0xff); } } dxvk-2.6.1/src/d3d11/d3d11_video.cpp000066400000000000000000001344311477473124000166240ustar00rootroot00000000000000#include #include "d3d11_context_imm.h" #include "d3d11_video.h" #include #include namespace dxvk { D3D11VideoProcessorEnumerator::D3D11VideoProcessorEnumerator( D3D11Device* pDevice, const D3D11_VIDEO_PROCESSOR_CONTENT_DESC& Desc) : D3D11DeviceChild(pDevice), m_desc(Desc) { } D3D11VideoProcessorEnumerator::~D3D11VideoProcessorEnumerator() { } HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::QueryInterface( REFIID riid, void** ppvObject) { if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11VideoProcessorEnumerator)) { *ppvObject = ref(this); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11VideoProcessorEnumerator), riid)) { Logger::warn("D3D11VideoProcessorEnumerator::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorContentDesc( D3D11_VIDEO_PROCESSOR_CONTENT_DESC* pContentDesc) { *pContentDesc = m_desc; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::CheckVideoProcessorFormat( DXGI_FORMAT Format, UINT* pFlags) { Logger::err(str::format("D3D11VideoProcessorEnumerator::CheckVideoProcessorFormat: stub, format ", Format)); if (!pFlags) return E_INVALIDARG; *pFlags = D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT | D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorCaps( D3D11_VIDEO_PROCESSOR_CAPS* pCaps) { Logger::err("D3D11VideoProcessorEnumerator::GetVideoProcessorCaps: semi-stub"); if (!pCaps) return E_INVALIDARG; *pCaps = {}; pCaps->RateConversionCapsCount = 1; pCaps->MaxInputStreams = 52; pCaps->MaxStreamStates = 52; return S_OK; } HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorRateConversionCaps( UINT TypeIndex, D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS* pCaps) { Logger::err("D3D11VideoProcessorEnumerator::GetVideoProcessorRateConversionCaps: semi-stub"); if (!pCaps || TypeIndex) return E_INVALIDARG; *pCaps = {}; if (m_desc.InputFrameFormat == D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE) { pCaps->ProcessorCaps = D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_FRAME_RATE_CONVERSION; } else { pCaps->ProcessorCaps = D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB; pCaps->PastFrames = 1; pCaps->FutureFrames = 1; } return S_OK; } HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorCustomRate( UINT TypeIndex, UINT CustomRateIndex, D3D11_VIDEO_PROCESSOR_CUSTOM_RATE* pRate) { Logger::err("D3D11VideoProcessorEnumerator::GetVideoProcessorCustomRate: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorFilterRange( D3D11_VIDEO_PROCESSOR_FILTER Filter, D3D11_VIDEO_PROCESSOR_FILTER_RANGE* pRange) { Logger::err("D3D11VideoProcessorEnumerator::GetVideoProcessorFilterRange: Stub"); return E_NOTIMPL; } D3D11VideoProcessor::D3D11VideoProcessor( D3D11Device* pDevice, D3D11VideoProcessorEnumerator* pEnumerator, UINT RateConversionIndex) : D3D11DeviceChild(pDevice), m_enumerator(pEnumerator), m_rateConversionIndex(RateConversionIndex) { } D3D11VideoProcessor::~D3D11VideoProcessor() { } HRESULT STDMETHODCALLTYPE D3D11VideoProcessor::QueryInterface( REFIID riid, void** ppvObject) { if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11VideoProcessor)) { *ppvObject = ref(this); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11VideoProcessor), riid)) { Logger::warn("D3D11VideoProcessor::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11VideoProcessor::GetContentDesc( D3D11_VIDEO_PROCESSOR_CONTENT_DESC *pDesc) { m_enumerator->GetVideoProcessorContentDesc(pDesc); } void STDMETHODCALLTYPE D3D11VideoProcessor::GetRateConversionCaps( D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS *pCaps) { m_enumerator->GetVideoProcessorRateConversionCaps(m_rateConversionIndex, pCaps); } D3D11VideoProcessorInputView::D3D11VideoProcessorInputView( D3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC& Desc) : D3D11DeviceChild(pDevice), m_resource(pResource), m_desc(Desc) { D3D11_COMMON_RESOURCE_DESC resourceDesc = { }; GetCommonResourceDesc(pResource, &resourceDesc); Rc dxvkImage = GetCommonTexture(pResource)->GetImage(); DXGI_VK_FORMAT_INFO formatInfo = pDevice->LookupFormat(resourceDesc.Format, DXGI_VK_FORMAT_MODE_COLOR); DXGI_VK_FORMAT_FAMILY formatFamily = pDevice->LookupFamily(resourceDesc.Format, DXGI_VK_FORMAT_MODE_COLOR); VkImageAspectFlags aspectMask = lookupFormatInfo(formatInfo.Format)->aspectMask; DxvkImageViewKey viewInfo; viewInfo.format = formatInfo.Format; viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(formatInfo.Swizzle); switch (m_desc.ViewDimension) { case D3D11_VPIV_DIMENSION_TEXTURE2D: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.mipIndex = m_desc.Texture2D.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = m_desc.Texture2D.ArraySlice; viewInfo.layerCount = 1; break; case D3D11_VPIV_DIMENSION_UNKNOWN: throw DxvkError("Invalid view dimension"); } m_subresources.aspectMask = aspectMask; m_subresources.baseArrayLayer = viewInfo.layerIndex; m_subresources.layerCount = viewInfo.layerCount; m_subresources.mipLevel = viewInfo.mipIndex; for (uint32_t i = 0; aspectMask && i < m_views.size(); i++) { viewInfo.aspects = vk::getNextAspect(aspectMask); if (viewInfo.aspects != VK_IMAGE_ASPECT_COLOR_BIT) viewInfo.format = formatFamily.Formats[i]; m_views[i] = dxvkImage->createView(viewInfo); } m_isYCbCr = IsYCbCrFormat(resourceDesc.Format); } D3D11VideoProcessorInputView::~D3D11VideoProcessorInputView() { } bool D3D11VideoProcessorInputView::IsYCbCrFormat(DXGI_FORMAT Format) { static const std::array s_formats = {{ DXGI_FORMAT_NV12, DXGI_FORMAT_YUY2, DXGI_FORMAT_AYUV, }}; return std::find(s_formats.begin(), s_formats.end(), Format) != s_formats.end(); } HRESULT STDMETHODCALLTYPE D3D11VideoProcessorInputView::QueryInterface( REFIID riid, void** ppvObject) { if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11View) || riid == __uuidof(ID3D11VideoProcessorInputView)) { *ppvObject = ref(this); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11VideoProcessorInputView), riid)) { Logger::warn("D3D11VideoProcessorInputView::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11VideoProcessorInputView::GetResource( ID3D11Resource** ppResource) { *ppResource = m_resource.ref(); } void STDMETHODCALLTYPE D3D11VideoProcessorInputView::GetDesc( D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC* pDesc) { *pDesc = m_desc; } D3D11VideoProcessorOutputView::D3D11VideoProcessorOutputView( D3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC& Desc) : D3D11DeviceChild(pDevice), m_resource(pResource), m_desc(Desc) { D3D11_COMMON_RESOURCE_DESC resourceDesc = { }; GetCommonResourceDesc(pResource, &resourceDesc); DXGI_VK_FORMAT_INFO formatInfo = pDevice->LookupFormat( resourceDesc.Format, DXGI_VK_FORMAT_MODE_COLOR); DxvkImageViewKey viewInfo; viewInfo.format = formatInfo.Format; viewInfo.aspects = lookupFormatInfo(viewInfo.format)->aspectMask; viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(formatInfo.Swizzle); viewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; switch (m_desc.ViewDimension) { case D3D11_VPOV_DIMENSION_TEXTURE2D: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.mipIndex = m_desc.Texture2D.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; break; case D3D11_VPOV_DIMENSION_TEXTURE2DARRAY: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; viewInfo.mipIndex = m_desc.Texture2DArray.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = m_desc.Texture2DArray.FirstArraySlice; viewInfo.layerCount = m_desc.Texture2DArray.ArraySize; break; case D3D11_VPOV_DIMENSION_UNKNOWN: throw DxvkError("Invalid view dimension"); } m_view = GetCommonTexture(pResource)->GetImage()->createView(viewInfo); } D3D11VideoProcessorOutputView::~D3D11VideoProcessorOutputView() { } HRESULT STDMETHODCALLTYPE D3D11VideoProcessorOutputView::QueryInterface( REFIID riid, void** ppvObject) { if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11View) || riid == __uuidof(ID3D11VideoProcessorOutputView)) { *ppvObject = ref(this); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11VideoProcessorOutputView), riid)) { Logger::warn("D3D11VideoProcessorOutputView::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11VideoProcessorOutputView::GetResource( ID3D11Resource** ppResource) { *ppResource = m_resource.ref(); } void STDMETHODCALLTYPE D3D11VideoProcessorOutputView::GetDesc( D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC* pDesc) { *pDesc = m_desc; } D3D11VideoContext::D3D11VideoContext( D3D11ImmediateContext* pContext, const Rc& Device) : m_ctx(pContext), m_device(Device) { } D3D11VideoContext::~D3D11VideoContext() { } ULONG STDMETHODCALLTYPE D3D11VideoContext::AddRef() { return m_ctx->AddRef(); } ULONG STDMETHODCALLTYPE D3D11VideoContext::Release() { return m_ctx->Release(); } HRESULT STDMETHODCALLTYPE D3D11VideoContext::QueryInterface( REFIID riid, void** ppvObject) { return m_ctx->QueryInterface(riid, ppvObject); } HRESULT STDMETHODCALLTYPE D3D11VideoContext::GetPrivateData( REFGUID Name, UINT* pDataSize, void* pData) { return m_ctx->GetPrivateData(Name, pDataSize, pData); } HRESULT STDMETHODCALLTYPE D3D11VideoContext::SetPrivateData( REFGUID Name, UINT DataSize, const void* pData) { return m_ctx->SetPrivateData(Name, DataSize, pData); } HRESULT STDMETHODCALLTYPE D3D11VideoContext::SetPrivateDataInterface( REFGUID Name, const IUnknown* pUnknown) { return m_ctx->SetPrivateDataInterface(Name, pUnknown); } void STDMETHODCALLTYPE D3D11VideoContext::GetDevice( ID3D11Device** ppDevice) { return m_ctx->GetDevice(ppDevice); } HRESULT STDMETHODCALLTYPE D3D11VideoContext::GetDecoderBuffer( ID3D11VideoDecoder* pDecoder, D3D11_VIDEO_DECODER_BUFFER_TYPE Type, UINT* BufferSize, void** ppBuffer) { Logger::err("D3D11VideoContext::GetDecoderBuffer: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoContext::ReleaseDecoderBuffer( ID3D11VideoDecoder* pDecoder, D3D11_VIDEO_DECODER_BUFFER_TYPE Type) { Logger::err("D3D11VideoContext::ReleaseDecoderBuffer: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoContext::DecoderBeginFrame( ID3D11VideoDecoder* pDecoder, ID3D11VideoDecoderOutputView* pView, UINT KeySize, const void* pKey) { Logger::err("D3D11VideoContext::DecoderBeginFrame: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoContext::DecoderEndFrame( ID3D11VideoDecoder* pDecoder) { Logger::err("D3D11VideoContext::DecoderEndFrame: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoContext::SubmitDecoderBuffers( ID3D11VideoDecoder* pDecoder, UINT BufferCount, const D3D11_VIDEO_DECODER_BUFFER_DESC* pBufferDescs) { Logger::err("D3D11VideoContext::SubmitDecoderBuffers: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoContext::DecoderExtension( ID3D11VideoDecoder* pDecoder, const D3D11_VIDEO_DECODER_EXTENSION* pExtension) { Logger::err("D3D11VideoContext::DecoderExtension: Stub"); return E_NOTIMPL; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputTargetRect( ID3D11VideoProcessor* pVideoProcessor, BOOL Enable, const RECT* pRect) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetState(); state->outputTargetRectEnabled = Enable; if (Enable) state->outputTargetRect = *pRect; static bool errorShown = false; if (!std::exchange(errorShown, true)) Logger::err("D3D11VideoContext::VideoProcessorSetOutputTargetRect: Stub."); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputBackgroundColor( ID3D11VideoProcessor* pVideoProcessor, BOOL YCbCr, const D3D11_VIDEO_COLOR* pColor) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetState(); state->outputBackgroundColorIsYCbCr = YCbCr; state->outputBackgroundColor = *pColor; static bool errorShown = false; if (!std::exchange(errorShown, true)) Logger::err("D3D11VideoContext::VideoProcessorSetOutputBackgroundColor: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputColorSpace( ID3D11VideoProcessor* pVideoProcessor, const D3D11_VIDEO_PROCESSOR_COLOR_SPACE *pColorSpace) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetState(); state->outputColorSpace = *pColorSpace; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputAlphaFillMode( ID3D11VideoProcessor* pVideoProcessor, D3D11_VIDEO_PROCESSOR_ALPHA_FILL_MODE AlphaFillMode, UINT StreamIndex) { Logger::err("D3D11VideoContext::VideoProcessorSetOutputAlphaFillMode: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputConstriction( ID3D11VideoProcessor* pVideoProcessor, BOOL Enable, SIZE Size) { Logger::err("D3D11VideoContext::VideoProcessorSetOutputConstriction: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputStereoMode( ID3D11VideoProcessor* pVideoProcessor, BOOL Enable) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetState(); state->outputStereoModeEnabled = Enable; if (Enable) Logger::err("D3D11VideoContext: Stereo output not supported"); } HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputExtension( ID3D11VideoProcessor* pVideoProcessor, const GUID* pExtensionGuid, UINT DataSize, void* pData) { Logger::err("D3D11VideoContext::VideoProcessorSetOutputExtension: Stub"); return E_NOTIMPL; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamFrameFormat( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, D3D11_VIDEO_FRAME_FORMAT Format) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetStreamState(StreamIndex); if (!state) return; state->frameFormat = Format; if (Format != D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE) Logger::err(str::format("D3D11VideoContext: Unsupported frame format: ", Format)); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamColorSpace( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, const D3D11_VIDEO_PROCESSOR_COLOR_SPACE *pColorSpace) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetStreamState(StreamIndex); if (!state) return; state->colorSpace = *pColorSpace; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamOutputRate( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, D3D11_VIDEO_PROCESSOR_OUTPUT_RATE Rate, BOOL Repeat, const DXGI_RATIONAL* CustomRate) { Logger::err(str::format("D3D11VideoContext::VideoProcessorSetStreamOutputRate: Stub, Rate ", Rate)); if (CustomRate) Logger::err(str::format("CustomRate ", CustomRate->Numerator, "/", CustomRate->Denominator)); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamSourceRect( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable, const RECT* pRect) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetStreamState(StreamIndex); if (!state) return; state->srcRectEnabled = Enable; if (Enable) state->srcRect = *pRect; static bool errorShown = false; if (!std::exchange(errorShown, true)) Logger::err("D3D11VideoContext::VideoProcessorSetStreamSourceRect: Stub."); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamDestRect( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable, const RECT* pRect) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetStreamState(StreamIndex); if (!state) return; state->dstRectEnabled = Enable; if (Enable) state->dstRect = *pRect; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamAlpha( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable, FLOAT Alpha) { Logger::err("D3D11VideoContext::VideoProcessorSetStreamAlpha: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamPalette( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, UINT EntryCount, const UINT* pEntries) { Logger::err("D3D11VideoContext::VideoProcessorSetStreamPalette: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamPixelAspectRatio( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable, const DXGI_RATIONAL* pSrcAspectRatio, const DXGI_RATIONAL* pDstAspectRatio) { Logger::err("D3D11VideoContext::VideoProcessorSetStreamPixelAspectRatio: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamLumaKey( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable, FLOAT Lower, FLOAT Upper) { Logger::err("D3D11VideoContext::VideoProcessorSetStreamLumaKey: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamStereoFormat( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable, D3D11_VIDEO_PROCESSOR_STEREO_FORMAT Format, BOOL LeftViewFrame0, BOOL BaseViewFrame0, D3D11_VIDEO_PROCESSOR_STEREO_FLIP_MODE FlipMode, int MonoOffset) { Logger::err("D3D11VideoContext::VideoProcessorSetStreamStereoFormat: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamAutoProcessingMode( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetStreamState(StreamIndex); if (!state) return; state->autoProcessingEnabled = Enable; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamFilter( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, D3D11_VIDEO_PROCESSOR_FILTER Filter, BOOL Enable, int Level) { Logger::err("D3D11VideoContext::VideoProcessorSetStreamFilter: Stub"); } HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamExtension( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, const GUID* pExtensionGuid, UINT DataSize, void* pData) { Logger::err("D3D11VideoContext::VideoProcessorSetStreamExtension: Stub"); return E_NOTIMPL; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamRotation( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable, D3D11_VIDEO_PROCESSOR_ROTATION Rotation) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetStreamState(StreamIndex); if (!state) return; state->rotationEnabled = Enable; state->rotation = Rotation; if (Enable && Rotation != D3D11_VIDEO_PROCESSOR_ROTATION_IDENTITY) Logger::err(str::format("D3D11VideoContext: Unsupported rotation: ", Rotation)); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputTargetRect( ID3D11VideoProcessor* pVideoProcessor, BOOL* pEnabled, RECT* pRect) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetState(); if (pEnabled) *pEnabled = state->outputTargetRectEnabled; if (pRect) *pRect = state->outputTargetRect; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputBackgroundColor( ID3D11VideoProcessor* pVideoProcessor, BOOL* pYCbCr, D3D11_VIDEO_COLOR* pColor) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetState(); if (pYCbCr) *pYCbCr = state->outputBackgroundColorIsYCbCr; if (pColor) *pColor = state->outputBackgroundColor; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputColorSpace( ID3D11VideoProcessor* pVideoProcessor, D3D11_VIDEO_PROCESSOR_COLOR_SPACE* pColorSpace) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetState(); if (pColorSpace) *pColorSpace = state->outputColorSpace; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputAlphaFillMode( ID3D11VideoProcessor* pVideoProcessor, D3D11_VIDEO_PROCESSOR_ALPHA_FILL_MODE* pAlphaFillMode, UINT* pStreamIndex) { Logger::err("D3D11VideoContext::VideoProcessorGetOutputAlphaFillMode: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputConstriction( ID3D11VideoProcessor* pVideoProcessor, BOOL* pEnabled, SIZE* pSize) { Logger::err("D3D11VideoContext::VideoProcessorGetOutputConstriction: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputStereoMode( ID3D11VideoProcessor* pVideoProcessor, BOOL* pEnabled) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetState(); if (pEnabled) *pEnabled = state->outputStereoModeEnabled; } HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputExtension( ID3D11VideoProcessor* pVideoProcessor, const GUID* pExtensionGuid, UINT DataSize, void* pData) { Logger::err("D3D11VideoContext::VideoProcessorGetOutputExtension: Stub"); return E_NOTIMPL; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamFrameFormat( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, D3D11_VIDEO_FRAME_FORMAT* pFormat) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetStreamState(StreamIndex); if (!state) return; if (pFormat) *pFormat = state->frameFormat; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamColorSpace( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, D3D11_VIDEO_PROCESSOR_COLOR_SPACE* pColorSpace) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetStreamState(StreamIndex); if (!state) return; if (pColorSpace) *pColorSpace = state->colorSpace; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamOutputRate( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, D3D11_VIDEO_PROCESSOR_OUTPUT_RATE* pRate, BOOL* pRepeat, DXGI_RATIONAL* pCustomRate) { Logger::err("D3D11VideoContext::VideoProcessorGetStreamOutputRate: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamSourceRect( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnabled, RECT* pRect) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetStreamState(StreamIndex); if (!state) return; if (pEnabled) *pEnabled = state->srcRectEnabled; if (pRect) *pRect = state->srcRect; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamDestRect( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnabled, RECT* pRect) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetStreamState(StreamIndex); if (!state) return; if (pEnabled) *pEnabled = state->dstRectEnabled; if (pRect) *pRect = state->dstRect; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamAlpha( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnabled, FLOAT* pAlpha) { Logger::err("D3D11VideoContext::VideoProcessorGetStreamAlpha: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamPalette( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, UINT EntryCount, UINT* pEntries) { Logger::err("D3D11VideoContext::VideoProcessorGetStreamPalette: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamPixelAspectRatio( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnabled, DXGI_RATIONAL* pSrcAspectRatio, DXGI_RATIONAL* pDstAspectRatio) { Logger::err("D3D11VideoContext::VideoProcessorGetStreamPixelAspectRatio: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamLumaKey( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnabled, FLOAT* pLower, FLOAT* pUpper) { Logger::err("D3D11VideoContext::VideoProcessorGetStreamLumaKey: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamStereoFormat( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnabled, D3D11_VIDEO_PROCESSOR_STEREO_FORMAT* pFormat, BOOL* pLeftViewFrame0, BOOL* pBaseViewFrame0, D3D11_VIDEO_PROCESSOR_STEREO_FLIP_MODE* pFlipMode, int* pMonoOffset) { Logger::err("D3D11VideoContext::VideoProcessorGetStreamStereoFormat: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamAutoProcessingMode( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnabled) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetStreamState(StreamIndex); if (!state) return; *pEnabled = state->autoProcessingEnabled; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamFilter( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, D3D11_VIDEO_PROCESSOR_FILTER Filter, BOOL* pEnabled, int* pLevel) { Logger::err("D3D11VideoContext::VideoProcessorGetStreamFilter: Stub"); } HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamExtension( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, const GUID* pExtensionGuid, UINT DataSize, void* pData) { Logger::err("D3D11VideoContext::VideoProcessorGetStreamExtension: Stub"); return E_NOTIMPL; } void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamRotation( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnable, D3D11_VIDEO_PROCESSOR_ROTATION* pRotation) { D3D10DeviceLock lock = m_ctx->LockContext(); auto state = static_cast(pVideoProcessor)->GetStreamState(StreamIndex); if (!state) return; if (pEnable) *pEnable = state->rotationEnabled; if (pRotation) *pRotation = state->rotation; } HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorBlt( ID3D11VideoProcessor* pVideoProcessor, ID3D11VideoProcessorOutputView* pOutputView, UINT FrameIdx, UINT StreamCount, const D3D11_VIDEO_PROCESSOR_STREAM* pStreams) { D3D10DeviceLock lock = m_ctx->LockContext(); m_ctx->EmitCs([] (DxvkContext* ctx) { ctx->beginDebugLabel(vk::makeLabel(0x59eaff, "Video blit")); }); auto videoProcessor = static_cast(pVideoProcessor); bool hasStreamsEnabled = false; // Resetting and restoring all context state incurs // a lot of overhead, so only do it as necessary for (uint32_t i = 0; i < StreamCount; i++) { auto streamState = videoProcessor->GetStreamState(i); if (!pStreams[i].Enable || !streamState) continue; if (!hasStreamsEnabled) { m_ctx->ResetDirtyTracking(); m_ctx->ResetCommandListState(); BindOutputView(pOutputView); hasStreamsEnabled = true; } BlitStream(streamState, &pStreams[i]); } if (hasStreamsEnabled) { UnbindResources(); m_ctx->RestoreCommandListState(); } m_ctx->EmitCs([] (DxvkContext* ctx) { ctx->endDebugLabel(); }); return S_OK; } HRESULT STDMETHODCALLTYPE D3D11VideoContext::NegotiateCryptoSessionKeyExchange( ID3D11CryptoSession* pSession, UINT DataSize, void* pData) { Logger::err("D3D11VideoContext::NegotiateCryptoSessionKeyExchange: Stub"); return E_NOTIMPL; } void STDMETHODCALLTYPE D3D11VideoContext::EncryptionBlt( ID3D11CryptoSession* pSession, ID3D11Texture2D* pSrcSurface, ID3D11Texture2D* pDstSurface, UINT IVSize, void* pIV) { Logger::err("D3D11VideoContext::EncryptionBlt: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::DecryptionBlt( ID3D11CryptoSession* pSession, ID3D11Texture2D* pSrcSurface, ID3D11Texture2D* pDstSurface, D3D11_ENCRYPTED_BLOCK_INFO* pBlockInfo, UINT KeySize, const void* pKey, UINT IVSize, void* pIV) { Logger::err("D3D11VideoContext::DecryptionBlt: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::StartSessionKeyRefresh( ID3D11CryptoSession* pSession, UINT RandomNumberSize, void* pRandomNumber) { Logger::err("D3D11VideoContext::StartSessionKeyRefresh: Stub"); } void STDMETHODCALLTYPE D3D11VideoContext::FinishSessionKeyRefresh( ID3D11CryptoSession* pSession) { Logger::err("D3D11VideoContext::FinishSessionKeyRefresh: Stub"); } HRESULT STDMETHODCALLTYPE D3D11VideoContext::GetEncryptionBltKey( ID3D11CryptoSession* pSession, UINT KeySize, void* pKey) { Logger::err("D3D11VideoContext::GetEncryptionBltKey: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoContext::NegotiateAuthenticatedChannelKeyExchange( ID3D11AuthenticatedChannel* pChannel, UINT DataSize, void* pData) { Logger::err("D3D11VideoContext::NegotiateAuthenticatedChannelKeyExchange: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoContext::QueryAuthenticatedChannel( ID3D11AuthenticatedChannel* pChannel, UINT InputSize, const void* pInput, UINT OutputSize, void* pOutput) { Logger::err("D3D11VideoContext::QueryAuthenticatedChannel: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE D3D11VideoContext::ConfigureAuthenticatedChannel( ID3D11AuthenticatedChannel* pChannel, UINT InputSize, const void* pInput, D3D11_AUTHENTICATED_CONFIGURE_OUTPUT* pOutput) { Logger::err("D3D11VideoContext::ConfigureAuthenticatedChannel: Stub"); return E_NOTIMPL; } void D3D11VideoContext::ApplyColorMatrix(float pDst[3][4], const float pSrc[3][4]) { float result[3][4]; for (uint32_t i = 0; i < 3; i++) { for (uint32_t j = 0; j < 4; j++) { result[i][j] = pSrc[i][0] * pDst[0][j] + pSrc[i][1] * pDst[1][j] + pSrc[i][2] * pDst[2][j] + pSrc[i][3] * float(j == 3); } } memcpy(pDst, &result[0][0], sizeof(result)); } void D3D11VideoContext::ApplyYCbCrMatrix(float pColorMatrix[3][4], bool UseBt709) { static const float pretransform[3][4] = { { 0.0f, 1.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f, -0.5f }, { 1.0f, 0.0f, 0.0f, -0.5f }, }; static const float bt601[3][4] = { { 1.0f, 0.000000f, 1.402000f, 0.0f }, { 1.0f, -0.344136f, -0.714136f, 0.0f }, { 1.0f, 1.772000f, 0.000000f, 0.0f }, }; static const float bt709[3][4] = { { 1.0f, 0.000000f, 1.574800f, 0.0f }, { 1.0f, -0.187324f, -0.468124f, 0.0f }, { 1.0f, 1.855600f, 0.000000f, 0.0f }, }; ApplyColorMatrix(pColorMatrix, pretransform); ApplyColorMatrix(pColorMatrix, UseBt709 ? bt709 : bt601); } void D3D11VideoContext::BindOutputView( ID3D11VideoProcessorOutputView* pOutputView) { auto dxvkView = static_cast(pOutputView)->GetView(); m_ctx->EmitCs([this, cView = dxvkView] (DxvkContext* ctx) { DxvkImageUsageInfo usage = { }; usage.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; usage.stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; usage.access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; ctx->ensureImageCompatibility(cView->image(), usage); DxvkRenderTargets rt; rt.color[0].view = cView; rt.color[0].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; ctx->bindRenderTargets(std::move(rt), 0u); DxvkInputAssemblyState iaState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, false); ctx->setInputAssemblyState(iaState); }); VkExtent3D viewExtent = dxvkView->mipLevelExtent(0); m_dstExtent = { viewExtent.width, viewExtent.height }; } void D3D11VideoContext::BlitStream( const D3D11VideoProcessorStreamState* pStreamState, const D3D11_VIDEO_PROCESSOR_STREAM* pStream) { CreateResources(); if (pStream->PastFrames || pStream->FutureFrames) Logger::err("D3D11VideoContext: Ignoring non-zero PastFrames and FutureFrames"); if (pStream->OutputIndex) Logger::err("D3D11VideoContext: Ignoring non-zero OutputIndex"); if (pStream->InputFrameOrField) Logger::err("D3D11VideoContext: Ignoring non-zero InputFrameOrField"); auto view = static_cast(pStream->pInputSurface); m_ctx->EmitCs([this, cStreamState = *pStreamState, cImage = view->GetImage(), cViews = view->GetViews(), cIsYCbCr = view->IsYCbCr() ] (DxvkContext* ctx) { DxvkImageUsageInfo usage = { }; usage.usage = VK_IMAGE_USAGE_SAMPLED_BIT; usage.stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; usage.access = VK_ACCESS_SHADER_READ_BIT; ctx->ensureImageCompatibility(cImage, usage); VkViewport viewport; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = float(m_dstExtent.width); viewport.height = float(m_dstExtent.height); viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; VkRect2D scissor; scissor.offset = { 0, 0 }; scissor.extent = m_dstExtent; if (cStreamState.dstRectEnabled) { viewport.x = float(cStreamState.dstRect.left); viewport.y = float(cStreamState.dstRect.top); viewport.width = float(cStreamState.dstRect.right) - viewport.x; viewport.height = float(cStreamState.dstRect.bottom) - viewport.y; } VkExtent3D viewExtent = cViews[0]->mipLevelExtent(0); VkRect2D srcRect; srcRect.offset = { 0, 0 }; srcRect.extent = { viewExtent.width, viewExtent.height }; if (cStreamState.srcRectEnabled) { srcRect.offset.x = cStreamState.srcRect.left; srcRect.offset.y = cStreamState.srcRect.top; srcRect.extent.width = cStreamState.srcRect.right - srcRect.offset.x; srcRect.extent.height = cStreamState.srcRect.bottom - srcRect.offset.y; } UboData uboData = { }; uboData.colorMatrix[0][0] = 1.0f; uboData.colorMatrix[1][1] = 1.0f; uboData.colorMatrix[2][2] = 1.0f; uboData.coordMatrix[0][0] = float(srcRect.extent.width) / float(viewExtent.width); uboData.coordMatrix[1][1] = float(srcRect.extent.height) / float(viewExtent.height); uboData.coordMatrix[2][0] = float(srcRect.offset.x) / float(viewExtent.width); uboData.coordMatrix[2][1] = float(srcRect.offset.y) / float(viewExtent.height); uboData.srcRect = srcRect; uboData.yMin = 0.0f; uboData.yMax = 1.0f; uboData.isPlanar = cViews[1] != nullptr; if (cIsYCbCr) ApplyYCbCrMatrix(uboData.colorMatrix, cStreamState.colorSpace.YCbCr_Matrix); if (cStreamState.colorSpace.Nominal_Range) { uboData.yMin = 0.0627451f; uboData.yMax = 0.9215686f; } Rc uboSlice = m_ubo->allocateStorage(); memcpy(uboSlice->mapPtr(), &uboData, sizeof(uboData)); DxvkViewport vp = { viewport, scissor }; ctx->invalidateBuffer(m_ubo, std::move(uboSlice)); ctx->setViewports(1, &vp); ctx->bindShader(Rc(m_vs)); ctx->bindShader(Rc(m_fs)); ctx->bindUniformBuffer(VK_SHADER_STAGE_FRAGMENT_BIT, 0, DxvkBufferSlice(m_ubo)); for (uint32_t i = 0; i < cViews.size(); i++) ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 1 + i, Rc(cViews[i])); VkDrawIndirectCommand draw = { }; draw.vertexCount = 3u; draw.instanceCount = 1u; ctx->draw(1, &draw); for (uint32_t i = 0; i < cViews.size(); i++) ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 1 + i, nullptr); }); } void D3D11VideoContext::CreateUniformBuffer() { DxvkBufferCreateInfo bufferInfo; bufferInfo.size = sizeof(UboData); bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; bufferInfo.stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; bufferInfo.access = VK_ACCESS_UNIFORM_READ_BIT; bufferInfo.debugName = "Video blit parameters"; m_ubo = m_device->createBuffer(bufferInfo, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); } void D3D11VideoContext::CreateShaders() { SpirvCodeBuffer vsCode(d3d11_video_blit_vert); SpirvCodeBuffer fsCode(d3d11_video_blit_frag); const std::array fsBindings = {{ { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_UNIFORM_READ_BIT, DxvkAccessOp::None, true }, { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_IMAGE_VIEW_TYPE_2D, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT }, { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 2, VK_IMAGE_VIEW_TYPE_2D, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT }, }}; DxvkShaderCreateInfo vsInfo; vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; vsInfo.outputMask = 0x1; m_vs = new DxvkShader(vsInfo, std::move(vsCode)); DxvkShaderCreateInfo fsInfo; fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; fsInfo.bindingCount = fsBindings.size(); fsInfo.bindings = fsBindings.data(); fsInfo.inputMask = 0x1; fsInfo.outputMask = 0x1; m_fs = new DxvkShader(fsInfo, std::move(fsCode)); } void D3D11VideoContext::CreateResources() { if (std::exchange(m_resourcesCreated, true)) return; CreateUniformBuffer(); CreateShaders(); } void D3D11VideoContext::UnbindResources() { m_ctx->EmitCs([] (DxvkContext* ctx) { ctx->bindRenderTargets(DxvkRenderTargets(), 0u); ctx->bindShader(nullptr); ctx->bindShader(nullptr); ctx->bindUniformBuffer(VK_SHADER_STAGE_FRAGMENT_BIT, 0, DxvkBufferSlice()); }); } } dxvk-2.6.1/src/d3d11/d3d11_video.h000066400000000000000000000564751477473124000163040ustar00rootroot00000000000000#pragma once #include "d3d11_device.h" namespace dxvk { static constexpr uint32_t D3D11_VK_VIDEO_STREAM_COUNT = 8; class D3D11VideoProcessorEnumerator : public D3D11DeviceChild { public: D3D11VideoProcessorEnumerator( D3D11Device* pDevice, const D3D11_VIDEO_PROCESSOR_CONTENT_DESC& Desc); ~D3D11VideoProcessorEnumerator(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE GetVideoProcessorContentDesc( D3D11_VIDEO_PROCESSOR_CONTENT_DESC* pContentDesc); HRESULT STDMETHODCALLTYPE CheckVideoProcessorFormat( DXGI_FORMAT Format, UINT* pFlags); HRESULT STDMETHODCALLTYPE GetVideoProcessorCaps( D3D11_VIDEO_PROCESSOR_CAPS* pCaps); HRESULT STDMETHODCALLTYPE GetVideoProcessorRateConversionCaps( UINT TypeIndex, D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS* pCaps); HRESULT STDMETHODCALLTYPE GetVideoProcessorCustomRate( UINT TypeIndex, UINT CustomRateIndex, D3D11_VIDEO_PROCESSOR_CUSTOM_RATE* pRate); HRESULT STDMETHODCALLTYPE GetVideoProcessorFilterRange( D3D11_VIDEO_PROCESSOR_FILTER Filter, D3D11_VIDEO_PROCESSOR_FILTER_RANGE* pRange); private: D3D11_VIDEO_PROCESSOR_CONTENT_DESC m_desc; }; struct D3D11VideoProcessorStreamState { BOOL autoProcessingEnabled = TRUE; BOOL dstRectEnabled = FALSE; BOOL srcRectEnabled = FALSE; BOOL rotationEnabled = FALSE; RECT dstRect = RECT(); RECT srcRect = RECT(); D3D11_VIDEO_FRAME_FORMAT frameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE; D3D11_VIDEO_PROCESSOR_ROTATION rotation = D3D11_VIDEO_PROCESSOR_ROTATION_IDENTITY; D3D11_VIDEO_PROCESSOR_COLOR_SPACE colorSpace = D3D11_VIDEO_PROCESSOR_COLOR_SPACE(); }; struct D3D11VideoProcessorState { BOOL outputStereoModeEnabled = FALSE; BOOL outputBackgroundColorIsYCbCr = FALSE; BOOL outputTargetRectEnabled = FALSE; RECT outputTargetRect = RECT(); D3D11_VIDEO_COLOR outputBackgroundColor = D3D11_VIDEO_COLOR(); D3D11_VIDEO_PROCESSOR_COLOR_SPACE outputColorSpace = D3D11_VIDEO_PROCESSOR_COLOR_SPACE(); }; class D3D11VideoProcessor : public D3D11DeviceChild { public: D3D11VideoProcessor( D3D11Device* pDevice, D3D11VideoProcessorEnumerator* pEnumerator, UINT RateConversionIndex); ~D3D11VideoProcessor(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); void STDMETHODCALLTYPE GetContentDesc( D3D11_VIDEO_PROCESSOR_CONTENT_DESC *pDesc); void STDMETHODCALLTYPE GetRateConversionCaps( D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS *pCaps); D3D11VideoProcessorState* GetState() { return &m_state; } D3D11VideoProcessorStreamState* GetStreamState(UINT StreamIndex) { return StreamIndex < D3D11_VK_VIDEO_STREAM_COUNT ? &m_streams[StreamIndex] : nullptr; } private: D3D11VideoProcessorEnumerator* m_enumerator; uint32_t m_rateConversionIndex; D3D11VideoProcessorState m_state; D3D11VideoProcessorStreamState m_streams[D3D11_VK_VIDEO_STREAM_COUNT]; }; class D3D11VideoProcessorInputView : public D3D11DeviceChild { public: D3D11VideoProcessorInputView( D3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC& Desc); ~D3D11VideoProcessorInputView(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); void STDMETHODCALLTYPE GetResource( ID3D11Resource** ppResource); void STDMETHODCALLTYPE GetDesc( D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC* pDesc); bool IsYCbCr() const { return m_isYCbCr; } Rc GetImage() const { return GetCommonTexture(m_resource.ptr())->GetImage(); } VkImageSubresourceLayers GetImageSubresources() const { return m_subresources; } std::array, 2> GetViews() const { return m_views; } private: Com m_resource; D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC m_desc; VkImageSubresourceLayers m_subresources; std::array, 2> m_views; bool m_isYCbCr = false; static bool IsYCbCrFormat(DXGI_FORMAT Format); }; class D3D11VideoProcessorOutputView : public D3D11DeviceChild { public: D3D11VideoProcessorOutputView( D3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC& Desc); ~D3D11VideoProcessorOutputView(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); void STDMETHODCALLTYPE GetResource( ID3D11Resource** ppResource); void STDMETHODCALLTYPE GetDesc( D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC* pDesc); Rc GetView() const { return m_view; } private: Com m_resource; D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC m_desc; Rc m_view; }; class D3D11VideoContext : public ID3D11VideoContext { public: D3D11VideoContext( D3D11ImmediateContext* pContext, const Rc& Device); ~D3D11VideoContext(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID Name, UINT* pDataSize, void* pData); HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID Name, UINT DataSize, const void* pData); HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID Name, const IUnknown* pUnknown); void STDMETHODCALLTYPE GetDevice( ID3D11Device** ppDevice); HRESULT STDMETHODCALLTYPE GetDecoderBuffer( ID3D11VideoDecoder* pDecoder, D3D11_VIDEO_DECODER_BUFFER_TYPE Type, UINT* BufferSize, void** ppBuffer); HRESULT STDMETHODCALLTYPE ReleaseDecoderBuffer( ID3D11VideoDecoder* pDecoder, D3D11_VIDEO_DECODER_BUFFER_TYPE Type); HRESULT STDMETHODCALLTYPE DecoderBeginFrame( ID3D11VideoDecoder* pDecoder, ID3D11VideoDecoderOutputView* pView, UINT KeySize, const void* pKey); HRESULT STDMETHODCALLTYPE DecoderEndFrame( ID3D11VideoDecoder* pDecoder); HRESULT STDMETHODCALLTYPE SubmitDecoderBuffers( ID3D11VideoDecoder* pDecoder, UINT BufferCount, const D3D11_VIDEO_DECODER_BUFFER_DESC* pBufferDescs); HRESULT STDMETHODCALLTYPE DecoderExtension( ID3D11VideoDecoder* pDecoder, const D3D11_VIDEO_DECODER_EXTENSION* pExtension); void STDMETHODCALLTYPE VideoProcessorSetOutputTargetRect( ID3D11VideoProcessor* pVideoProcessor, BOOL Enable, const RECT* pRect); void STDMETHODCALLTYPE VideoProcessorSetOutputBackgroundColor( ID3D11VideoProcessor* pVideoProcessor, BOOL YCbCr, const D3D11_VIDEO_COLOR* pColor); void STDMETHODCALLTYPE VideoProcessorSetOutputColorSpace( ID3D11VideoProcessor* pVideoProcessor, const D3D11_VIDEO_PROCESSOR_COLOR_SPACE *pColorSpace); void STDMETHODCALLTYPE VideoProcessorSetOutputAlphaFillMode( ID3D11VideoProcessor* pVideoProcessor, D3D11_VIDEO_PROCESSOR_ALPHA_FILL_MODE AlphaFillMode, UINT StreamIndex); void STDMETHODCALLTYPE VideoProcessorSetOutputConstriction( ID3D11VideoProcessor* pVideoProcessor, BOOL Enable, SIZE Size); void STDMETHODCALLTYPE VideoProcessorSetOutputStereoMode( ID3D11VideoProcessor* pVideoProcessor, BOOL Enable); HRESULT STDMETHODCALLTYPE VideoProcessorSetOutputExtension( ID3D11VideoProcessor* pVideoProcessor, const GUID* pExtensionGuid, UINT DataSize, void* pData); void STDMETHODCALLTYPE VideoProcessorSetStreamFrameFormat( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, D3D11_VIDEO_FRAME_FORMAT Format); void STDMETHODCALLTYPE VideoProcessorSetStreamColorSpace( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, const D3D11_VIDEO_PROCESSOR_COLOR_SPACE *pColorSpace); void STDMETHODCALLTYPE VideoProcessorSetStreamOutputRate( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, D3D11_VIDEO_PROCESSOR_OUTPUT_RATE Rate, BOOL Repeat, const DXGI_RATIONAL* CustomRate); void STDMETHODCALLTYPE VideoProcessorSetStreamSourceRect( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable, const RECT* pRect); void STDMETHODCALLTYPE VideoProcessorSetStreamDestRect( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable, const RECT* pRect); void STDMETHODCALLTYPE VideoProcessorSetStreamAlpha( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable, FLOAT Alpha); void STDMETHODCALLTYPE VideoProcessorSetStreamPalette( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, UINT EntryCount, const UINT* pEntries); void STDMETHODCALLTYPE VideoProcessorSetStreamPixelAspectRatio( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable, const DXGI_RATIONAL* pSrcAspectRatio, const DXGI_RATIONAL* pDstAspectRatio); void STDMETHODCALLTYPE VideoProcessorSetStreamLumaKey( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable, FLOAT Lower, FLOAT Upper); void STDMETHODCALLTYPE VideoProcessorSetStreamStereoFormat( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable, D3D11_VIDEO_PROCESSOR_STEREO_FORMAT Format, BOOL LeftViewFrame0, BOOL BaseViewFrame0, D3D11_VIDEO_PROCESSOR_STEREO_FLIP_MODE FlipMode, int MonoOffset); void STDMETHODCALLTYPE VideoProcessorSetStreamAutoProcessingMode( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable); void STDMETHODCALLTYPE VideoProcessorSetStreamFilter( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, D3D11_VIDEO_PROCESSOR_FILTER Filter, BOOL Enable, int Level); HRESULT STDMETHODCALLTYPE VideoProcessorSetStreamExtension( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, const GUID* pExtensionGuid, UINT DataSize, void* pData); void STDMETHODCALLTYPE VideoProcessorSetStreamRotation( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL Enable, D3D11_VIDEO_PROCESSOR_ROTATION Rotation); void STDMETHODCALLTYPE VideoProcessorGetOutputTargetRect( ID3D11VideoProcessor* pVideoProcessor, BOOL* pEnabled, RECT* pRect); void STDMETHODCALLTYPE VideoProcessorGetOutputBackgroundColor( ID3D11VideoProcessor* pVideoProcessor, BOOL* pYCbCr, D3D11_VIDEO_COLOR* pColor); void STDMETHODCALLTYPE VideoProcessorGetOutputColorSpace( ID3D11VideoProcessor* pVideoProcessor, D3D11_VIDEO_PROCESSOR_COLOR_SPACE* pColorSpace); void STDMETHODCALLTYPE VideoProcessorGetOutputAlphaFillMode( ID3D11VideoProcessor* pVideoProcessor, D3D11_VIDEO_PROCESSOR_ALPHA_FILL_MODE* pAlphaFillMode, UINT* pStreamIndex); void STDMETHODCALLTYPE VideoProcessorGetOutputConstriction( ID3D11VideoProcessor* pVideoProcessor, BOOL* pEnabled, SIZE* pSize); void STDMETHODCALLTYPE VideoProcessorGetOutputStereoMode( ID3D11VideoProcessor* pVideoProcessor, BOOL* pEnabled); HRESULT STDMETHODCALLTYPE VideoProcessorGetOutputExtension( ID3D11VideoProcessor* pVideoProcessor, const GUID* pExtensionGuid, UINT DataSize, void* pData); void STDMETHODCALLTYPE VideoProcessorGetStreamFrameFormat( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, D3D11_VIDEO_FRAME_FORMAT* pFormat); void STDMETHODCALLTYPE VideoProcessorGetStreamColorSpace( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, D3D11_VIDEO_PROCESSOR_COLOR_SPACE* pColorSpace); void STDMETHODCALLTYPE VideoProcessorGetStreamOutputRate( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, D3D11_VIDEO_PROCESSOR_OUTPUT_RATE* pRate, BOOL* pRepeat, DXGI_RATIONAL* pCustomRate); void STDMETHODCALLTYPE VideoProcessorGetStreamSourceRect( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnabled, RECT* pRect); void STDMETHODCALLTYPE VideoProcessorGetStreamDestRect( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnabled, RECT* pRect); void STDMETHODCALLTYPE VideoProcessorGetStreamAlpha( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnabled, FLOAT* pAlpha); void STDMETHODCALLTYPE VideoProcessorGetStreamPalette( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, UINT EntryCount, UINT* pEntries); void STDMETHODCALLTYPE VideoProcessorGetStreamPixelAspectRatio( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnabled, DXGI_RATIONAL* pSrcAspectRatio, DXGI_RATIONAL* pDstAspectRatio); void STDMETHODCALLTYPE VideoProcessorGetStreamLumaKey( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnabled, FLOAT* pLower, FLOAT* pUpper); void STDMETHODCALLTYPE VideoProcessorGetStreamStereoFormat( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnabled, D3D11_VIDEO_PROCESSOR_STEREO_FORMAT* pFormat, BOOL* pLeftViewFrame0, BOOL* pBaseViewFrame0, D3D11_VIDEO_PROCESSOR_STEREO_FLIP_MODE* pFlipMode, int* pMonoOffset); void STDMETHODCALLTYPE VideoProcessorGetStreamAutoProcessingMode( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnabled); void STDMETHODCALLTYPE VideoProcessorGetStreamFilter( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, D3D11_VIDEO_PROCESSOR_FILTER Filter, BOOL* pEnabled, int* pLevel); HRESULT STDMETHODCALLTYPE VideoProcessorGetStreamExtension( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, const GUID* pExtensionGuid, UINT DataSize, void* pData); void STDMETHODCALLTYPE VideoProcessorGetStreamRotation( ID3D11VideoProcessor* pVideoProcessor, UINT StreamIndex, BOOL* pEnable, D3D11_VIDEO_PROCESSOR_ROTATION* pRotation); HRESULT STDMETHODCALLTYPE VideoProcessorBlt( ID3D11VideoProcessor* pVideoProcessor, ID3D11VideoProcessorOutputView* pOutputView, UINT FrameIdx, UINT StreamCount, const D3D11_VIDEO_PROCESSOR_STREAM* pStreams); HRESULT STDMETHODCALLTYPE NegotiateCryptoSessionKeyExchange( ID3D11CryptoSession* pSession, UINT DataSize, void* pData); void STDMETHODCALLTYPE EncryptionBlt( ID3D11CryptoSession* pSession, ID3D11Texture2D* pSrcSurface, ID3D11Texture2D* pDstSurface, UINT IVSize, void* pIV); void STDMETHODCALLTYPE DecryptionBlt( ID3D11CryptoSession* pSession, ID3D11Texture2D* pSrcSurface, ID3D11Texture2D* pDstSurface, D3D11_ENCRYPTED_BLOCK_INFO* pBlockInfo, UINT KeySize, const void* pKey, UINT IVSize, void* pIV); void STDMETHODCALLTYPE StartSessionKeyRefresh( ID3D11CryptoSession* pSession, UINT RandomNumberSize, void* pRandomNumber); void STDMETHODCALLTYPE FinishSessionKeyRefresh( ID3D11CryptoSession* pSession); HRESULT STDMETHODCALLTYPE GetEncryptionBltKey( ID3D11CryptoSession* pSession, UINT KeySize, void* pKey); HRESULT STDMETHODCALLTYPE NegotiateAuthenticatedChannelKeyExchange( ID3D11AuthenticatedChannel* pChannel, UINT DataSize, void* pData); HRESULT STDMETHODCALLTYPE QueryAuthenticatedChannel( ID3D11AuthenticatedChannel* pChannel, UINT InputSize, const void* pInput, UINT OutputSize, void* pOutput); HRESULT STDMETHODCALLTYPE ConfigureAuthenticatedChannel( ID3D11AuthenticatedChannel* pChannel, UINT InputSize, const void* pInput, D3D11_AUTHENTICATED_CONFIGURE_OUTPUT* pOutput); private: struct alignas(16) UboData { float colorMatrix[3][4]; float coordMatrix[3][2]; VkRect2D srcRect; float yMin, yMax; VkBool32 isPlanar; }; D3D11ImmediateContext* m_ctx; Rc m_device; Rc m_vs; Rc m_fs; Rc m_ubo; VkExtent2D m_dstExtent = { 0u, 0u }; bool m_resourcesCreated = false; void ApplyColorMatrix(float pDst[3][4], const float pSrc[3][4]); void ApplyYCbCrMatrix(float pColorMatrix[3][4], bool UseBt709); void BindOutputView( ID3D11VideoProcessorOutputView* pOutputView); void BlitStream( const D3D11VideoProcessorStreamState* pStreamState, const D3D11_VIDEO_PROCESSOR_STREAM* pStream); void CreateUniformBuffer(); void CreateShaders(); void CreateResources(); void UnbindResources(); }; } dxvk-2.6.1/src/d3d11/d3d11_view.h000066400000000000000000000044421477473124000161330ustar00rootroot00000000000000#pragma once #include "d3d11_include.h" namespace dxvk { /** * \brief Buffer view info * * Stores the byte range covered * by a buffer view. */ struct D3D11_VK_BUFFER_VIEW_INFO { VkDeviceSize Offset; VkDeviceSize Length; }; /** * \brief Image view info * * Stores the subresource range * covered by an image view. */ struct D3D11_VK_IMAGE_VIEW_INFO { VkImageAspectFlags Aspects; uint32_t MinLevel; uint32_t MinLayer; uint32_t NumLevels; uint32_t NumLayers; }; /** * \brief Common view info * * Stores a pointer to the resource as * well as the type-specific range that * is affected by the view. */ struct D3D11_VK_VIEW_INFO { ID3D11Resource* pResource; D3D11_RESOURCE_DIMENSION Dimension; UINT BindFlags; union { D3D11_VK_BUFFER_VIEW_INFO Buffer; D3D11_VK_IMAGE_VIEW_INFO Image; }; }; /** * \brief Checks whether two views overlap * * Overlapping views may conflict in case * one or both views are used for writing. * \param [in] a First view to check * \param [in] b Second view to check * \returns \c true if the views overlap */ inline bool CheckViewOverlap(const D3D11_VK_VIEW_INFO& a, const D3D11_VK_VIEW_INFO& b) { if (likely(a.pResource != b.pResource)) return false; if (a.Dimension == D3D11_RESOURCE_DIMENSION_BUFFER) { // Just check whether the buffer ranges overlap return (a.Buffer.Offset < b.Buffer.Offset + b.Buffer.Length) && (a.Buffer.Offset + a.Buffer.Length > b.Buffer.Offset); } else { // Check whether the subresource ranges overlap return (a.Image.Aspects & b.Image.Aspects) && (a.Image.MinLevel < b.Image.MinLevel + b.Image.NumLevels) && (a.Image.MinLayer < b.Image.MinLayer + b.Image.NumLayers) && (a.Image.MinLevel + a.Image.NumLevels > b.Image.MinLevel) && (a.Image.MinLayer + a.Image.NumLayers > b.Image.MinLayer); } } template bool CheckViewOverlap(const T1* a, const T2* b) { return a && b && CheckViewOverlap(a->GetViewInfo(), b->GetViewInfo()); } } dxvk-2.6.1/src/d3d11/d3d11_view_dsv.cpp000066400000000000000000000237641477473124000173520ustar00rootroot00000000000000#include "d3d11_device.h" #include "d3d11_buffer.h" #include "d3d11_resource.h" #include "d3d11_texture.h" #include "d3d11_view_dsv.h" namespace dxvk { D3D11DepthStencilView::D3D11DepthStencilView( D3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc) : D3D11DeviceChild(pDevice), m_resource(pResource), m_desc(*pDesc), m_d3d10(this) { ResourceAddRefPrivate(m_resource); D3D11_COMMON_RESOURCE_DESC resourceDesc; GetCommonResourceDesc(pResource, &resourceDesc); DxvkImageViewKey viewInfo; viewInfo.format = pDevice->LookupFormat(pDesc->Format, DXGI_VK_FORMAT_MODE_DEPTH).Format; viewInfo.aspects = lookupFormatInfo(viewInfo.format)->aspectMask; viewInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; switch (pDesc->ViewDimension) { case D3D11_DSV_DIMENSION_TEXTURE1D: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D; viewInfo.mipIndex = pDesc->Texture1D.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; break; case D3D11_DSV_DIMENSION_TEXTURE1DARRAY: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY; viewInfo.mipIndex = pDesc->Texture1DArray.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = pDesc->Texture1DArray.FirstArraySlice; viewInfo.layerCount = pDesc->Texture1DArray.ArraySize; break; case D3D11_DSV_DIMENSION_TEXTURE2D: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.mipIndex = pDesc->Texture2D.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; break; case D3D11_DSV_DIMENSION_TEXTURE2DARRAY: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; viewInfo.mipIndex = pDesc->Texture2DArray.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = pDesc->Texture2DArray.FirstArraySlice; viewInfo.layerCount = pDesc->Texture2DArray.ArraySize; break; case D3D11_DSV_DIMENSION_TEXTURE2DMS: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.mipIndex = 0; viewInfo.mipCount = 1; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; break; case D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; viewInfo.mipIndex = 0; viewInfo.mipCount = 1; viewInfo.layerIndex = pDesc->Texture2DMSArray.FirstArraySlice; viewInfo.layerCount = pDesc->Texture2DMSArray.ArraySize; break; default: throw DxvkError("D3D11: Invalid view dimension for DSV"); } // Normalize view type so that we won't accidentally // bind 2D array views and 2D views at the same time if (viewInfo.layerCount == 1) { if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY) viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D; if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; } // Populate view info struct m_info.pResource = pResource; m_info.Dimension = resourceDesc.Dim; m_info.BindFlags = resourceDesc.BindFlags; m_info.Image.Aspects = viewInfo.aspects; m_info.Image.MinLevel = viewInfo.mipIndex; m_info.Image.MinLayer = viewInfo.layerIndex; m_info.Image.NumLevels = viewInfo.mipCount; m_info.Image.NumLayers = viewInfo.layerCount; if (m_desc.Flags & D3D11_DSV_READ_ONLY_DEPTH) m_info.Image.Aspects &= ~VK_IMAGE_ASPECT_DEPTH_BIT; if (m_desc.Flags & D3D11_DSV_READ_ONLY_STENCIL) m_info.Image.Aspects &= ~VK_IMAGE_ASPECT_STENCIL_BIT; // Create the underlying image view object m_view = GetCommonTexture(pResource)->GetImage()->createView(viewInfo); } D3D11DepthStencilView::~D3D11DepthStencilView() { ResourceReleasePrivate(m_resource); m_resource = nullptr; m_view = nullptr; } HRESULT STDMETHODCALLTYPE D3D11DepthStencilView::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11View) || riid == __uuidof(ID3D11DepthStencilView)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(ID3D10View) || riid == __uuidof(ID3D10DepthStencilView)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11DepthStencilView), riid)) { Logger::warn("D3D11DepthStencilView::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11DepthStencilView::GetResource(ID3D11Resource** ppResource) { *ppResource = ref(m_resource); } void STDMETHODCALLTYPE D3D11DepthStencilView::GetDesc(D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc) { *pDesc = m_desc; } HRESULT D3D11DepthStencilView::GetDescFromResource( ID3D11Resource* pResource, D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc) { D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; pResource->GetType(&resourceDim); pDesc->Flags = 0; switch (resourceDim) { case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { D3D11_TEXTURE1D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); pDesc->Format = resourceDesc.Format; if (resourceDesc.ArraySize == 1) { pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE1D; pDesc->Texture1D.MipSlice = 0; } else { pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE1DARRAY; pDesc->Texture1DArray.MipSlice = 0; pDesc->Texture1DArray.FirstArraySlice = 0; pDesc->Texture1DArray.ArraySize = resourceDesc.ArraySize; } } return S_OK; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { D3D11_TEXTURE2D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); pDesc->Format = resourceDesc.Format; if (resourceDesc.SampleDesc.Count == 1) { if (resourceDesc.ArraySize == 1) { pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; pDesc->Texture2D.MipSlice = 0; } else { pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY; pDesc->Texture2DArray.MipSlice = 0; pDesc->Texture2DArray.FirstArraySlice = 0; pDesc->Texture2DArray.ArraySize = resourceDesc.ArraySize; } } else { if (resourceDesc.ArraySize == 1) { pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; } else { pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY; pDesc->Texture2DMSArray.FirstArraySlice = 0; pDesc->Texture2DMSArray.ArraySize = resourceDesc.ArraySize; } } } return S_OK; default: Logger::err(str::format( "D3D11: Unsupported dimension for depth stencil view: ", resourceDim)); return E_INVALIDARG; } } HRESULT D3D11DepthStencilView::NormalizeDesc( ID3D11Resource* pResource, D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc) { D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; pResource->GetType(&resourceDim); DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; uint32_t numLayers = 0; switch (resourceDim) { case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { D3D11_TEXTURE1D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); if (pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE1D && pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE1DARRAY) { Logger::err("D3D11: Incompatible view dimension for Texture1D"); return E_INVALIDARG; } format = resourceDesc.Format; numLayers = resourceDesc.ArraySize; } break; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { D3D11_TEXTURE2D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); if (pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE2D && pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE2DARRAY && pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE2DMS && pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY) { Logger::err("D3D11: Incompatible view dimension for Texture2D"); return E_INVALIDARG; } format = resourceDesc.Format; numLayers = resourceDesc.ArraySize; } break; default: return E_INVALIDARG; } if (pDesc->Format == DXGI_FORMAT_UNKNOWN) pDesc->Format = format; switch (pDesc->ViewDimension) { case D3D11_DSV_DIMENSION_TEXTURE1DARRAY: if (pDesc->Texture1DArray.ArraySize > numLayers - pDesc->Texture1DArray.FirstArraySlice) pDesc->Texture1DArray.ArraySize = numLayers - pDesc->Texture1DArray.FirstArraySlice; break; case D3D11_DSV_DIMENSION_TEXTURE2DARRAY: if (pDesc->Texture2DArray.ArraySize > numLayers - pDesc->Texture2DArray.FirstArraySlice) pDesc->Texture2DArray.ArraySize = numLayers - pDesc->Texture2DArray.FirstArraySlice; break; case D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY: if (pDesc->Texture2DMSArray.ArraySize > numLayers - pDesc->Texture2DMSArray.FirstArraySlice) pDesc->Texture2DMSArray.ArraySize = numLayers - pDesc->Texture2DMSArray.FirstArraySlice; break; default: break; } return S_OK; } } dxvk-2.6.1/src/d3d11/d3d11_view_dsv.h000066400000000000000000000060001477473124000167770ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_device.h" #include "../d3d10/d3d10_view_dsv.h" #include "d3d11_device_child.h" #include "d3d11_view.h" namespace dxvk { class D3D11Device; /** * \brief Depth-stencil view * * Unordered access views are special in that they can * have counters, which can be used inside shaders to * atomically append or consume structures. */ class D3D11DepthStencilView : public D3D11DeviceChild { public: D3D11DepthStencilView( D3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc); ~D3D11DepthStencilView(); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) final; void STDMETHODCALLTYPE GetResource(ID3D11Resource** ppResource) final; void STDMETHODCALLTYPE GetDesc(D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc) final; const D3D11_VK_VIEW_INFO& GetViewInfo() const { return m_info; } D3D11_RESOURCE_DIMENSION GetResourceType() const { D3D11_RESOURCE_DIMENSION type; m_resource->GetType(&type); return type; } Rc GetImageView() const { return m_view; } VkImageLayout GetRenderLayout() const { switch (m_desc.Flags & (D3D11_DSV_READ_ONLY_DEPTH | D3D11_DSV_READ_ONLY_STENCIL)) { default: // case 0 return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; case D3D11_DSV_READ_ONLY_DEPTH: return VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR; case D3D11_DSV_READ_ONLY_STENCIL: return VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR; case D3D11_DSV_READ_ONLY_DEPTH | D3D11_DSV_READ_ONLY_STENCIL: return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; } } UINT GetSampleCount() const { return UINT(m_view->image()->info().sampleCount); } VkImageAspectFlags GetWritableAspectMask() const { VkImageAspectFlags mask = m_view->formatInfo()->aspectMask; if (m_desc.Flags & D3D11_DSV_READ_ONLY_DEPTH) mask &= ~VK_IMAGE_ASPECT_DEPTH_BIT; if (m_desc.Flags & D3D11_DSV_READ_ONLY_STENCIL) mask &= ~VK_IMAGE_ASPECT_STENCIL_BIT; return mask; } DXGI_FORMAT GetViewFormat() const { return m_desc.Format; } D3D10DepthStencilView* GetD3D10Iface() { return &m_d3d10; } static HRESULT GetDescFromResource( ID3D11Resource* pResource, D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc); static HRESULT NormalizeDesc( ID3D11Resource* pResource, D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc); private: ID3D11Resource* m_resource; D3D11_DEPTH_STENCIL_VIEW_DESC m_desc; D3D11_VK_VIEW_INFO m_info; Rc m_view; D3D10DepthStencilView m_d3d10; }; } dxvk-2.6.1/src/d3d11/d3d11_view_rtv.cpp000066400000000000000000000364041477473124000173640ustar00rootroot00000000000000#include "d3d11_device.h" #include "d3d11_buffer.h" #include "d3d11_resource.h" #include "d3d11_texture.h" #include "d3d11_view_rtv.h" namespace dxvk { D3D11RenderTargetView::D3D11RenderTargetView( D3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_RENDER_TARGET_VIEW_DESC1* pDesc) : D3D11DeviceChild(pDevice), m_resource(pResource), m_desc(*pDesc), m_d3d10(this) { ResourceAddRefPrivate(m_resource); auto texture = GetCommonTexture(pResource); D3D11_COMMON_RESOURCE_DESC resourceDesc; GetCommonResourceDesc(pResource, &resourceDesc); DXGI_VK_FORMAT_INFO formatInfo = pDevice->LookupFormat( pDesc->Format, DXGI_VK_FORMAT_MODE_COLOR); DxvkImageViewKey viewInfo; viewInfo.format = formatInfo.Format; viewInfo.aspects = lookupFormatInfo(viewInfo.format)->aspectMask; viewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(formatInfo.Swizzle); switch (pDesc->ViewDimension) { case D3D11_RTV_DIMENSION_TEXTURE1D: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D; viewInfo.mipIndex = pDesc->Texture1D.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; break; case D3D11_RTV_DIMENSION_TEXTURE1DARRAY: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY; viewInfo.mipIndex = pDesc->Texture1DArray.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = pDesc->Texture1DArray.FirstArraySlice; viewInfo.layerCount = pDesc->Texture1DArray.ArraySize; break; case D3D11_RTV_DIMENSION_TEXTURE2D: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.mipIndex = pDesc->Texture2D.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; break; case D3D11_RTV_DIMENSION_TEXTURE2DARRAY: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; viewInfo.mipIndex = pDesc->Texture2DArray.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = pDesc->Texture2DArray.FirstArraySlice; viewInfo.layerCount = pDesc->Texture2DArray.ArraySize; break; case D3D11_RTV_DIMENSION_TEXTURE2DMS: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.mipIndex = 0; viewInfo.mipCount = 1; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; break; case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; viewInfo.mipIndex = 0; viewInfo.mipCount = 1; viewInfo.layerIndex = pDesc->Texture2DMSArray.FirstArraySlice; viewInfo.layerCount = pDesc->Texture2DMSArray.ArraySize; break; case D3D11_RTV_DIMENSION_TEXTURE3D: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; viewInfo.mipIndex = pDesc->Texture3D.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = pDesc->Texture3D.FirstWSlice; viewInfo.layerCount = pDesc->Texture3D.WSize; break; default: throw DxvkError("D3D11: Invalid view dimension for RTV"); } if (texture->GetPlaneCount() > 1) viewInfo.aspects = vk::getPlaneAspect(GetPlaneSlice(pDesc)); // Normalize view type so that we won't accidentally // bind 2D array views and 2D views at the same time if (viewInfo.layerCount == 1) { if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY) viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D; if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; } // Populate view info struct m_info.pResource = pResource; m_info.Dimension = resourceDesc.Dim; m_info.BindFlags = resourceDesc.BindFlags; m_info.Image.Aspects = viewInfo.aspects; m_info.Image.MinLevel = viewInfo.mipIndex; m_info.Image.MinLayer = viewInfo.layerIndex; m_info.Image.NumLevels = viewInfo.mipCount; m_info.Image.NumLayers = viewInfo.layerCount; // Create the underlying image view object m_view = texture->GetImage()->createView(viewInfo); } D3D11RenderTargetView::~D3D11RenderTargetView() { ResourceReleasePrivate(m_resource); m_resource = nullptr; m_view = nullptr; } HRESULT STDMETHODCALLTYPE D3D11RenderTargetView::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11View) || riid == __uuidof(ID3D11RenderTargetView) || riid == __uuidof(ID3D11RenderTargetView1)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(ID3D10View) || riid == __uuidof(ID3D10RenderTargetView)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11RenderTargetView), riid)) { Logger::warn("D3D11RenderTargetView::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11RenderTargetView::GetResource(ID3D11Resource** ppResource) { *ppResource = ref(m_resource); } void STDMETHODCALLTYPE D3D11RenderTargetView::GetDesc(D3D11_RENDER_TARGET_VIEW_DESC* pDesc) { pDesc->Format = m_desc.Format; pDesc->ViewDimension = m_desc.ViewDimension; switch (m_desc.ViewDimension) { case D3D11_RTV_DIMENSION_UNKNOWN: break; case D3D11_RTV_DIMENSION_BUFFER: pDesc->Buffer = m_desc.Buffer; break; case D3D11_RTV_DIMENSION_TEXTURE1D: pDesc->Texture1D = m_desc.Texture1D; break; case D3D11_RTV_DIMENSION_TEXTURE1DARRAY: pDesc->Texture1DArray = m_desc.Texture1DArray; break; case D3D11_RTV_DIMENSION_TEXTURE2D: pDesc->Texture2D.MipSlice = m_desc.Texture2D.MipSlice; break; case D3D11_RTV_DIMENSION_TEXTURE2DARRAY: pDesc->Texture2DArray.MipSlice = m_desc.Texture2DArray.MipSlice; pDesc->Texture2DArray.FirstArraySlice = m_desc.Texture2DArray.FirstArraySlice; pDesc->Texture2DArray.ArraySize = m_desc.Texture2DArray.ArraySize; break; case D3D11_RTV_DIMENSION_TEXTURE2DMS: pDesc->Texture2DMS = m_desc.Texture2DMS; break; case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY: pDesc->Texture2DMSArray = m_desc.Texture2DMSArray; break; case D3D11_RTV_DIMENSION_TEXTURE3D: pDesc->Texture3D = m_desc.Texture3D; break; } } void STDMETHODCALLTYPE D3D11RenderTargetView::GetDesc1(D3D11_RENDER_TARGET_VIEW_DESC1* pDesc) { *pDesc = m_desc; } HRESULT D3D11RenderTargetView::GetDescFromResource( ID3D11Resource* pResource, D3D11_RENDER_TARGET_VIEW_DESC1* pDesc) { D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; pResource->GetType(&resourceDim); switch (resourceDim) { case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { D3D11_TEXTURE1D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); pDesc->Format = resourceDesc.Format; if (resourceDesc.ArraySize == 1) { pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1D; pDesc->Texture1D.MipSlice = 0; } else { pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1DARRAY; pDesc->Texture1DArray.MipSlice = 0; pDesc->Texture1DArray.FirstArraySlice = 0; pDesc->Texture1DArray.ArraySize = resourceDesc.ArraySize; } } return S_OK; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { D3D11_TEXTURE2D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); pDesc->Format = resourceDesc.Format; if (resourceDesc.SampleDesc.Count == 1) { if (resourceDesc.ArraySize == 1) { pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; pDesc->Texture2D.MipSlice = 0; pDesc->Texture2D.PlaneSlice = 0; } else { pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; pDesc->Texture2DArray.MipSlice = 0; pDesc->Texture2DArray.FirstArraySlice = 0; pDesc->Texture2DArray.ArraySize = resourceDesc.ArraySize; pDesc->Texture2DArray.PlaneSlice = 0; } } else { if (resourceDesc.ArraySize == 1) { pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; } else { pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY; pDesc->Texture2DMSArray.FirstArraySlice = 0; pDesc->Texture2DMSArray.ArraySize = resourceDesc.ArraySize; } } } return S_OK; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: { D3D11_TEXTURE3D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); pDesc->Format = resourceDesc.Format; pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; pDesc->Texture3D.MipSlice = 0; pDesc->Texture3D.FirstWSlice = 0; pDesc->Texture3D.WSize = resourceDesc.Depth; } return S_OK; default: Logger::err(str::format( "D3D11: Unsupported dimension for render target view: ", resourceDim)); return E_INVALIDARG; } } D3D11_RENDER_TARGET_VIEW_DESC1 D3D11RenderTargetView::PromoteDesc( const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, UINT Plane) { D3D11_RENDER_TARGET_VIEW_DESC1 dstDesc; dstDesc.Format = pDesc->Format; dstDesc.ViewDimension = pDesc->ViewDimension; switch (pDesc->ViewDimension) { case D3D11_RTV_DIMENSION_UNKNOWN: break; case D3D11_RTV_DIMENSION_BUFFER: dstDesc.Buffer = pDesc->Buffer; break; case D3D11_RTV_DIMENSION_TEXTURE1D: dstDesc.Texture1D = pDesc->Texture1D; break; case D3D11_RTV_DIMENSION_TEXTURE1DARRAY: dstDesc.Texture1DArray = pDesc->Texture1DArray; break; case D3D11_RTV_DIMENSION_TEXTURE2D: dstDesc.Texture2D.MipSlice = pDesc->Texture2D.MipSlice; dstDesc.Texture2D.PlaneSlice = Plane; break; case D3D11_RTV_DIMENSION_TEXTURE2DARRAY: dstDesc.Texture2DArray.MipSlice = pDesc->Texture2DArray.MipSlice; dstDesc.Texture2DArray.FirstArraySlice = pDesc->Texture2DArray.FirstArraySlice; dstDesc.Texture2DArray.ArraySize = pDesc->Texture2DArray.ArraySize; dstDesc.Texture2DArray.PlaneSlice = Plane; break; case D3D11_RTV_DIMENSION_TEXTURE2DMS: dstDesc.Texture2DMS = pDesc->Texture2DMS; break; case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY: dstDesc.Texture2DMSArray = pDesc->Texture2DMSArray; break; case D3D11_RTV_DIMENSION_TEXTURE3D: dstDesc.Texture3D = pDesc->Texture3D; break; } return dstDesc; } HRESULT D3D11RenderTargetView::NormalizeDesc( ID3D11Resource* pResource, D3D11_RENDER_TARGET_VIEW_DESC1* pDesc) { D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; pResource->GetType(&resourceDim); DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; uint32_t numLayers = 0; switch (resourceDim) { case D3D11_RESOURCE_DIMENSION_BUFFER: { if (pDesc->ViewDimension != D3D11_RTV_DIMENSION_BUFFER) { Logger::err("D3D11: Incompatible view dimension for Buffer"); return E_INVALIDARG; } } break; case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { D3D11_TEXTURE1D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); if (pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE1D && pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE1DARRAY) { Logger::err("D3D11: Incompatible view dimension for Texture1D"); return E_INVALIDARG; } format = resourceDesc.Format; numLayers = resourceDesc.ArraySize; } break; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { D3D11_TEXTURE2D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); if (pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE2D && pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE2DARRAY && pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE2DMS && pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY) { Logger::err("D3D11: Incompatible view dimension for Texture2D"); return E_INVALIDARG; } format = resourceDesc.Format; numLayers = resourceDesc.ArraySize; } break; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: { D3D11_TEXTURE3D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); if (pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE3D) { Logger::err("D3D11: Incompatible view dimension for Texture3D"); return E_INVALIDARG; } format = resourceDesc.Format; numLayers = std::max(resourceDesc.Depth >> pDesc->Texture3D.MipSlice, 1u); } break; default: return E_INVALIDARG; } if (pDesc->Format == DXGI_FORMAT_UNKNOWN) pDesc->Format = format; switch (pDesc->ViewDimension) { case D3D11_RTV_DIMENSION_TEXTURE1DARRAY: if (pDesc->Texture1DArray.ArraySize > numLayers - pDesc->Texture1DArray.FirstArraySlice) pDesc->Texture1DArray.ArraySize = numLayers - pDesc->Texture1DArray.FirstArraySlice; break; case D3D11_RTV_DIMENSION_TEXTURE2D: break; case D3D11_RTV_DIMENSION_TEXTURE2DARRAY: if (pDesc->Texture2DArray.ArraySize > numLayers - pDesc->Texture2DArray.FirstArraySlice) pDesc->Texture2DArray.ArraySize = numLayers - pDesc->Texture2DArray.FirstArraySlice; break; case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY: if (pDesc->Texture2DMSArray.ArraySize > numLayers - pDesc->Texture2DMSArray.FirstArraySlice) pDesc->Texture2DMSArray.ArraySize = numLayers - pDesc->Texture2DMSArray.FirstArraySlice; break; case D3D11_RTV_DIMENSION_TEXTURE3D: if (pDesc->Texture3D.WSize > numLayers - pDesc->Texture3D.FirstWSlice) pDesc->Texture3D.WSize = numLayers - pDesc->Texture3D.FirstWSlice; break; default: break; } return S_OK; } UINT D3D11RenderTargetView::GetPlaneSlice(const D3D11_RENDER_TARGET_VIEW_DESC1* pDesc) { switch (pDesc->ViewDimension) { case D3D11_RTV_DIMENSION_TEXTURE2D: return pDesc->Texture2D.PlaneSlice; case D3D11_RTV_DIMENSION_TEXTURE2DARRAY: return pDesc->Texture2DArray.PlaneSlice; default: return 0; } } } dxvk-2.6.1/src/d3d11/d3d11_view_rtv.h000066400000000000000000000045411477473124000170260ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_device.h" #include "../d3d10/d3d10_view_rtv.h" #include "d3d11_device_child.h" #include "d3d11_view.h" namespace dxvk { class D3D11Device; /** * \brief Render target view */ class D3D11RenderTargetView : public D3D11DeviceChild { public: D3D11RenderTargetView( D3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_RENDER_TARGET_VIEW_DESC1* pDesc); ~D3D11RenderTargetView(); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) final; void STDMETHODCALLTYPE GetResource(ID3D11Resource** ppResource) final; void STDMETHODCALLTYPE GetDesc(D3D11_RENDER_TARGET_VIEW_DESC* pDesc) final; void STDMETHODCALLTYPE GetDesc1(D3D11_RENDER_TARGET_VIEW_DESC1* pDesc) final; const D3D11_VK_VIEW_INFO& GetViewInfo() const { return m_info; } BOOL HasBindFlag(UINT Flags) const { return m_info.BindFlags & Flags; } D3D11_RESOURCE_DIMENSION GetResourceType() const { D3D11_RESOURCE_DIMENSION type; m_resource->GetType(&type); return type; } Rc GetImageView() const { return m_view; } VkImageLayout GetRenderLayout() const { return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; } UINT GetSampleCount() const { return UINT(m_view->image()->info().sampleCount); } D3D10RenderTargetView* GetD3D10Iface() { return &m_d3d10; } static HRESULT GetDescFromResource( ID3D11Resource* pResource, D3D11_RENDER_TARGET_VIEW_DESC1* pDesc); static D3D11_RENDER_TARGET_VIEW_DESC1 PromoteDesc( const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, UINT Plane); static HRESULT NormalizeDesc( ID3D11Resource* pResource, D3D11_RENDER_TARGET_VIEW_DESC1* pDesc); static UINT GetPlaneSlice( const D3D11_RENDER_TARGET_VIEW_DESC1* pDesc); private: ID3D11Resource* m_resource; D3D11_RENDER_TARGET_VIEW_DESC1 m_desc; D3D11_VK_VIEW_INFO m_info; Rc m_view; D3D10RenderTargetView m_d3d10; }; } dxvk-2.6.1/src/d3d11/d3d11_view_srv.cpp000066400000000000000000000550361477473124000173650ustar00rootroot00000000000000#include "d3d11_device.h" #include "d3d11_buffer.h" #include "d3d11_resource.h" #include "d3d11_texture.h" #include "d3d11_view_srv.h" namespace dxvk { D3D11ShaderResourceView::D3D11ShaderResourceView( D3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc) : D3D11DeviceChild(pDevice), m_resource(pResource), m_desc(*pDesc), m_d3d10(this) { ResourceAddRefPrivate(m_resource); D3D11_COMMON_RESOURCE_DESC resourceDesc; GetCommonResourceDesc(pResource, &resourceDesc); // Basic view resource info m_info.pResource = pResource; m_info.Dimension = resourceDesc.Dim; m_info.BindFlags = resourceDesc.BindFlags; if (resourceDesc.Dim == D3D11_RESOURCE_DIMENSION_BUFFER) { auto buffer = static_cast(pResource); // Move buffer description to a common struct to // avoid having to handle the two cases separately D3D11_BUFFEREX_SRV bufInfo; if (pDesc->ViewDimension == D3D11_SRV_DIMENSION_BUFFEREX) { bufInfo.FirstElement = pDesc->BufferEx.FirstElement; bufInfo.NumElements = pDesc->BufferEx.NumElements; bufInfo.Flags = pDesc->BufferEx.Flags; } else if (pDesc->ViewDimension == D3D11_SRV_DIMENSION_BUFFER) { bufInfo.FirstElement = pDesc->Buffer.FirstElement; bufInfo.NumElements = pDesc->Buffer.NumElements; bufInfo.Flags = 0; } else { throw DxvkError("D3D11: Invalid view dimension for buffer SRV"); } // Fill in buffer view info DxvkBufferViewKey viewInfo; viewInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; if (bufInfo.Flags & D3D11_BUFFEREX_SRV_FLAG_RAW) { // Raw buffer view. We'll represent this as a // uniform texel buffer with UINT32 elements. viewInfo.format = VK_FORMAT_R32_UINT; viewInfo.offset = sizeof(uint32_t) * bufInfo.FirstElement; viewInfo.size = sizeof(uint32_t) * bufInfo.NumElements; } else if (pDesc->Format == DXGI_FORMAT_UNKNOWN) { // Structured buffer view viewInfo.format = VK_FORMAT_R32_UINT; viewInfo.offset = buffer->Desc()->StructureByteStride * bufInfo.FirstElement; viewInfo.size = buffer->Desc()->StructureByteStride * bufInfo.NumElements; } else { viewInfo.format = pDevice->LookupFormat(pDesc->Format, DXGI_VK_FORMAT_MODE_COLOR).Format; const DxvkFormatInfo* formatInfo = lookupFormatInfo(viewInfo.format); viewInfo.offset = formatInfo->elementSize * bufInfo.FirstElement; viewInfo.size = formatInfo->elementSize * bufInfo.NumElements; } // Populate view info struct m_info.Buffer.Offset = viewInfo.offset; m_info.Buffer.Length = viewInfo.size; // Create underlying buffer view object m_bufferView = buffer->GetBuffer()->createView(viewInfo); } else { auto texture = GetCommonTexture(pResource); auto formatInfo = pDevice->LookupFormat(pDesc->Format, texture->GetFormatMode()); DxvkImageViewKey viewInfo; viewInfo.format = formatInfo.Format; viewInfo.aspects = formatInfo.Aspect; viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(formatInfo.Swizzle); // Shaders expect the stencil value in the G component if (viewInfo.aspects == VK_IMAGE_ASPECT_STENCIL_BIT) { viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle({ VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO }); } switch (pDesc->ViewDimension) { case D3D11_SRV_DIMENSION_TEXTURE1D: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D; viewInfo.mipIndex = pDesc->Texture1D.MostDetailedMip; viewInfo.mipCount = pDesc->Texture1D.MipLevels; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; break; case D3D11_SRV_DIMENSION_TEXTURE1DARRAY: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY; viewInfo.mipIndex = pDesc->Texture1DArray.MostDetailedMip; viewInfo.mipCount = pDesc->Texture1DArray.MipLevels; viewInfo.layerIndex = pDesc->Texture1DArray.FirstArraySlice; viewInfo.layerCount = pDesc->Texture1DArray.ArraySize; break; case D3D11_SRV_DIMENSION_TEXTURE2D: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.mipIndex = pDesc->Texture2D.MostDetailedMip; viewInfo.mipCount = pDesc->Texture2D.MipLevels; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; break; case D3D11_SRV_DIMENSION_TEXTURE2DARRAY: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; viewInfo.mipIndex = pDesc->Texture2DArray.MostDetailedMip; viewInfo.mipCount = pDesc->Texture2DArray.MipLevels; viewInfo.layerIndex = pDesc->Texture2DArray.FirstArraySlice; viewInfo.layerCount = pDesc->Texture2DArray.ArraySize; break; case D3D11_SRV_DIMENSION_TEXTURE2DMS: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.mipIndex = 0; viewInfo.mipCount = 1; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; break; case D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; viewInfo.mipIndex = 0; viewInfo.mipCount = 1; viewInfo.layerIndex = pDesc->Texture2DMSArray.FirstArraySlice; viewInfo.layerCount = pDesc->Texture2DMSArray.ArraySize; break; case D3D11_SRV_DIMENSION_TEXTURE3D: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_3D; viewInfo.mipIndex = pDesc->Texture3D.MostDetailedMip; viewInfo.mipCount = pDesc->Texture3D.MipLevels; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; break; case D3D11_SRV_DIMENSION_TEXTURECUBE: { const bool cubeArraysEnabled = pDevice->GetDXVKDevice()->features().core.features.imageCubeArray; viewInfo.viewType = cubeArraysEnabled ? VK_IMAGE_VIEW_TYPE_CUBE_ARRAY : VK_IMAGE_VIEW_TYPE_CUBE; viewInfo.mipIndex = pDesc->TextureCube.MostDetailedMip; viewInfo.mipCount = pDesc->TextureCube.MipLevels; viewInfo.layerIndex = 0; viewInfo.layerCount = 6; } break; case D3D11_SRV_DIMENSION_TEXTURECUBEARRAY: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY; viewInfo.mipIndex = pDesc->TextureCubeArray.MostDetailedMip; viewInfo.mipCount = pDesc->TextureCubeArray.MipLevels; viewInfo.layerIndex = pDesc->TextureCubeArray.First2DArrayFace; viewInfo.layerCount = pDesc->TextureCubeArray.NumCubes * 6; break; default: throw DxvkError("D3D11: Invalid view dimension for image SRV"); } if (texture->GetPlaneCount() > 1) viewInfo.aspects = vk::getPlaneAspect(GetPlaneSlice(pDesc)); // Populate view info struct m_info.Image.Aspects = viewInfo.aspects; m_info.Image.MinLevel = viewInfo.mipIndex; m_info.Image.MinLayer = viewInfo.layerIndex; m_info.Image.NumLevels = viewInfo.mipCount; m_info.Image.NumLayers = viewInfo.layerCount; // Create the underlying image view object m_imageView = texture->GetImage()->createView(viewInfo); } } D3D11ShaderResourceView::~D3D11ShaderResourceView() { ResourceReleasePrivate(m_resource); m_resource = nullptr; m_imageView = nullptr; m_bufferView = nullptr; } HRESULT STDMETHODCALLTYPE D3D11ShaderResourceView::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11View) || riid == __uuidof(ID3D11ShaderResourceView) || riid == __uuidof(ID3D11ShaderResourceView1)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(ID3D10DeviceChild) || riid == __uuidof(ID3D10View) || riid == __uuidof(ID3D10ShaderResourceView) || riid == __uuidof(ID3D10ShaderResourceView1)) { *ppvObject = ref(&m_d3d10); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11ShaderResourceView), riid)) { Logger::warn("D3D11ShaderResourceView::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11ShaderResourceView::GetResource(ID3D11Resource** ppResource) { *ppResource = ref(m_resource); } void STDMETHODCALLTYPE D3D11ShaderResourceView::GetDesc(D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc) { pDesc->Format = m_desc.Format; pDesc->ViewDimension = m_desc.ViewDimension; switch (m_desc.ViewDimension) { case D3D11_SRV_DIMENSION_UNKNOWN: break; case D3D11_SRV_DIMENSION_BUFFER: pDesc->Buffer = m_desc.Buffer; break; case D3D11_SRV_DIMENSION_TEXTURE1D: pDesc->Texture1D = m_desc.Texture1D; break; case D3D11_SRV_DIMENSION_TEXTURE1DARRAY: pDesc->Texture1DArray = m_desc.Texture1DArray; break; case D3D11_SRV_DIMENSION_TEXTURE2D: pDesc->Texture2D.MostDetailedMip = m_desc.Texture2D.MostDetailedMip; pDesc->Texture2D.MipLevels = m_desc.Texture2D.MipLevels; break; case D3D11_SRV_DIMENSION_TEXTURE2DARRAY: pDesc->Texture2DArray.MostDetailedMip = m_desc.Texture2DArray.MostDetailedMip; pDesc->Texture2DArray.MipLevels = m_desc.Texture2DArray.MipLevels; pDesc->Texture2DArray.FirstArraySlice = m_desc.Texture2DArray.FirstArraySlice; pDesc->Texture2DArray.ArraySize = m_desc.Texture2DArray.ArraySize; break; case D3D11_SRV_DIMENSION_TEXTURE2DMS: pDesc->Texture2DMS = m_desc.Texture2DMS; break; case D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY: pDesc->Texture2DMSArray = m_desc.Texture2DMSArray; break; case D3D11_SRV_DIMENSION_TEXTURE3D: pDesc->Texture3D = m_desc.Texture3D; break; case D3D11_SRV_DIMENSION_TEXTURECUBE: pDesc->TextureCube = m_desc.TextureCube; break; case D3D11_SRV_DIMENSION_TEXTURECUBEARRAY: pDesc->TextureCubeArray = m_desc.TextureCubeArray; break; case D3D11_SRV_DIMENSION_BUFFEREX: pDesc->BufferEx = m_desc.BufferEx; break; } } void STDMETHODCALLTYPE D3D11ShaderResourceView::GetDesc1(D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc) { *pDesc = m_desc; } HRESULT D3D11ShaderResourceView::GetDescFromResource( ID3D11Resource* pResource, D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc) { D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; pResource->GetType(&resourceDim); switch (resourceDim) { case D3D11_RESOURCE_DIMENSION_BUFFER: { D3D11_BUFFER_DESC bufferDesc; static_cast(pResource)->GetDesc(&bufferDesc); if (bufferDesc.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) { pDesc->Format = DXGI_FORMAT_UNKNOWN; pDesc->ViewDimension = D3D11_SRV_DIMENSION_BUFFER; pDesc->Buffer.FirstElement = 0; pDesc->Buffer.NumElements = bufferDesc.ByteWidth / bufferDesc.StructureByteStride; return S_OK; } } return E_INVALIDARG; case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { D3D11_TEXTURE1D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); pDesc->Format = resourceDesc.Format; if (resourceDesc.ArraySize == 1) { pDesc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D; pDesc->Texture1D.MostDetailedMip = 0; pDesc->Texture1D.MipLevels = resourceDesc.MipLevels; } else { pDesc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY; pDesc->Texture1DArray.MostDetailedMip = 0; pDesc->Texture1DArray.MipLevels = resourceDesc.MipLevels; pDesc->Texture1DArray.FirstArraySlice = 0; pDesc->Texture1DArray.ArraySize = resourceDesc.ArraySize; } } return S_OK; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { D3D11_TEXTURE2D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); pDesc->Format = resourceDesc.Format; if (resourceDesc.SampleDesc.Count == 1) { if (resourceDesc.ArraySize == 1) { pDesc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; pDesc->Texture2D.MostDetailedMip = 0; pDesc->Texture2D.MipLevels = resourceDesc.MipLevels; pDesc->Texture2D.PlaneSlice = 0; } else { pDesc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; pDesc->Texture2DArray.MostDetailedMip = 0; pDesc->Texture2DArray.MipLevels = resourceDesc.MipLevels; pDesc->Texture2DArray.FirstArraySlice = 0; pDesc->Texture2DArray.ArraySize = resourceDesc.ArraySize; pDesc->Texture2DArray.PlaneSlice = 0; } } else { if (resourceDesc.ArraySize == 1) { pDesc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS; } else { pDesc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY; pDesc->Texture2DMSArray.FirstArraySlice = 0; pDesc->Texture2DMSArray.ArraySize = resourceDesc.ArraySize; } } } return S_OK; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: { D3D11_TEXTURE3D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); pDesc->Format = resourceDesc.Format; pDesc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; pDesc->Texture3D.MostDetailedMip = 0; pDesc->Texture3D.MipLevels = resourceDesc.MipLevels; } return S_OK; default: Logger::err(str::format( "D3D11: Unsupported dimension for shader resource view: ", resourceDim)); return E_INVALIDARG; } } D3D11_SHADER_RESOURCE_VIEW_DESC1 D3D11ShaderResourceView::PromoteDesc( const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, UINT Plane) { D3D11_SHADER_RESOURCE_VIEW_DESC1 dstDesc; dstDesc.Format = pDesc->Format; dstDesc.ViewDimension = pDesc->ViewDimension; switch (pDesc->ViewDimension) { case D3D11_SRV_DIMENSION_UNKNOWN: break; case D3D11_SRV_DIMENSION_BUFFER: dstDesc.Buffer = pDesc->Buffer; break; case D3D11_SRV_DIMENSION_TEXTURE1D: dstDesc.Texture1D = pDesc->Texture1D; break; case D3D11_SRV_DIMENSION_TEXTURE1DARRAY: dstDesc.Texture1DArray = pDesc->Texture1DArray; break; case D3D11_SRV_DIMENSION_TEXTURE2D: dstDesc.Texture2D.MostDetailedMip = pDesc->Texture2D.MostDetailedMip; dstDesc.Texture2D.MipLevels = pDesc->Texture2D.MipLevels; dstDesc.Texture2D.PlaneSlice = Plane; break; case D3D11_SRV_DIMENSION_TEXTURE2DARRAY: dstDesc.Texture2DArray.MostDetailedMip = pDesc->Texture2DArray.MostDetailedMip; dstDesc.Texture2DArray.MipLevels = pDesc->Texture2DArray.MipLevels; dstDesc.Texture2DArray.FirstArraySlice = pDesc->Texture2DArray.FirstArraySlice; dstDesc.Texture2DArray.ArraySize = pDesc->Texture2DArray.ArraySize; dstDesc.Texture2DArray.PlaneSlice = Plane; break; case D3D11_SRV_DIMENSION_TEXTURE2DMS: dstDesc.Texture2DMS = pDesc->Texture2DMS; break; case D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY: dstDesc.Texture2DMSArray = pDesc->Texture2DMSArray; break; case D3D11_SRV_DIMENSION_TEXTURE3D: dstDesc.Texture3D = pDesc->Texture3D; break; case D3D11_SRV_DIMENSION_TEXTURECUBE: dstDesc.TextureCube = pDesc->TextureCube; break; case D3D11_SRV_DIMENSION_TEXTURECUBEARRAY: dstDesc.TextureCubeArray = pDesc->TextureCubeArray; break; case D3D11_SRV_DIMENSION_BUFFEREX: dstDesc.BufferEx = pDesc->BufferEx; break; } return dstDesc; } HRESULT D3D11ShaderResourceView::NormalizeDesc( ID3D11Resource* pResource, D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc) { D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; pResource->GetType(&resourceDim); DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; uint32_t mipLevels = 0; uint32_t numLayers = 0; switch (resourceDim) { case D3D11_RESOURCE_DIMENSION_BUFFER: { if (pDesc->ViewDimension != D3D11_SRV_DIMENSION_BUFFER && pDesc->ViewDimension != D3D11_SRV_DIMENSION_BUFFEREX) { Logger::err("D3D11: Incompatible view dimension for Buffer"); return E_INVALIDARG; } } break; case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { D3D11_TEXTURE1D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); if (pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURE1D && pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURE1DARRAY) { Logger::err("D3D11: Incompatible view dimension for Texture1D"); return E_INVALIDARG; } format = resourceDesc.Format; mipLevels = resourceDesc.MipLevels; numLayers = resourceDesc.ArraySize; } break; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { D3D11_TEXTURE2D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); if (pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURE2D && pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURE2DARRAY && pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURE2DMS && pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY && pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURECUBE && pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURECUBEARRAY) { Logger::err("D3D11: Incompatible view dimension for Texture2D"); return E_INVALIDARG; } format = resourceDesc.Format; mipLevels = resourceDesc.MipLevels; numLayers = resourceDesc.ArraySize; } break; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: { D3D11_TEXTURE3D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); if (pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURE3D) { Logger::err("D3D11: Incompatible view dimension for Texture3D"); return E_INVALIDARG; } format = resourceDesc.Format; mipLevels = resourceDesc.MipLevels; numLayers = 1; } break; default: return E_INVALIDARG; } if (pDesc->Format == DXGI_FORMAT_UNKNOWN) pDesc->Format = format; switch (pDesc->ViewDimension) { case D3D11_SRV_DIMENSION_BUFFER: if (pDesc->Buffer.NumElements == 0) return E_INVALIDARG; break; case D3D11_SRV_DIMENSION_BUFFEREX: if (pDesc->BufferEx.NumElements == 0) return E_INVALIDARG; break; case D3D11_SRV_DIMENSION_TEXTURE1D: if (pDesc->Texture1D.MipLevels > mipLevels - pDesc->Texture1D.MostDetailedMip) pDesc->Texture1D.MipLevels = mipLevels - pDesc->Texture1D.MostDetailedMip; break; case D3D11_SRV_DIMENSION_TEXTURE1DARRAY: if (pDesc->Texture1DArray.MipLevels > mipLevels - pDesc->Texture1DArray.MostDetailedMip) pDesc->Texture1DArray.MipLevels = mipLevels - pDesc->Texture1DArray.MostDetailedMip; if (pDesc->Texture1DArray.ArraySize > numLayers - pDesc->Texture1DArray.FirstArraySlice) pDesc->Texture1DArray.ArraySize = numLayers - pDesc->Texture1DArray.FirstArraySlice; break; case D3D11_SRV_DIMENSION_TEXTURE2D: if (pDesc->Texture2D.MipLevels > mipLevels - pDesc->Texture2D.MostDetailedMip) pDesc->Texture2D.MipLevels = mipLevels - pDesc->Texture2D.MostDetailedMip; break; case D3D11_SRV_DIMENSION_TEXTURE2DARRAY: if (pDesc->Texture2DArray.MipLevels > mipLevels - pDesc->Texture2DArray.MostDetailedMip) pDesc->Texture2DArray.MipLevels = mipLevels - pDesc->Texture2DArray.MostDetailedMip; if (pDesc->Texture2DArray.ArraySize > numLayers - pDesc->Texture2DArray.FirstArraySlice) pDesc->Texture2DArray.ArraySize = numLayers - pDesc->Texture2DArray.FirstArraySlice; break; case D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY: if (pDesc->Texture2DMSArray.ArraySize > numLayers - pDesc->Texture2DMSArray.FirstArraySlice) pDesc->Texture2DMSArray.ArraySize = numLayers - pDesc->Texture2DMSArray.FirstArraySlice; break; case D3D11_SRV_DIMENSION_TEXTURECUBE: if (pDesc->TextureCube.MipLevels > mipLevels - pDesc->TextureCube.MostDetailedMip) pDesc->TextureCube.MipLevels = mipLevels - pDesc->TextureCube.MostDetailedMip; break; case D3D11_SRV_DIMENSION_TEXTURECUBEARRAY: if (pDesc->TextureCubeArray.MipLevels > mipLevels - pDesc->TextureCubeArray.MostDetailedMip) pDesc->TextureCubeArray.MipLevels = mipLevels - pDesc->TextureCubeArray.MostDetailedMip; if (pDesc->TextureCubeArray.NumCubes > (numLayers - pDesc->TextureCubeArray.First2DArrayFace) / 6) pDesc->TextureCubeArray.NumCubes = (numLayers - pDesc->TextureCubeArray.First2DArrayFace) / 6; break; case D3D11_SRV_DIMENSION_TEXTURE3D: if (pDesc->Texture3D.MipLevels > mipLevels - pDesc->Texture3D.MostDetailedMip) pDesc->Texture3D.MipLevels = mipLevels - pDesc->Texture3D.MostDetailedMip; break; default: break; } return S_OK; } UINT D3D11ShaderResourceView::GetPlaneSlice(const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc) { switch (pDesc->ViewDimension) { case D3D11_SRV_DIMENSION_TEXTURE2D: return pDesc->Texture2D.PlaneSlice; case D3D11_SRV_DIMENSION_TEXTURE2DARRAY: return pDesc->Texture2DArray.PlaneSlice; default: return 0; } } } dxvk-2.6.1/src/d3d11/d3d11_view_srv.h000066400000000000000000000050451477473124000170250ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_device.h" #include "../d3d10/d3d10_view_srv.h" #include "d3d11_device_child.h" #include "d3d11_view.h" namespace dxvk { class D3D11Device; /** * \brief Shader resource view */ class D3D11ShaderResourceView : public D3D11DeviceChild { public: D3D11ShaderResourceView( D3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc); ~D3D11ShaderResourceView(); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) final; void STDMETHODCALLTYPE GetResource(ID3D11Resource** ppResource) final; void STDMETHODCALLTYPE GetDesc(D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc) final; void STDMETHODCALLTYPE GetDesc1(D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc) final; const D3D11_VK_VIEW_INFO& GetViewInfo() const { return m_info; } BOOL TestHazards() const { return m_info.BindFlags & (D3D11_BIND_RENDER_TARGET | D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_UNORDERED_ACCESS); } D3D11_RESOURCE_DIMENSION GetResourceType() const { D3D11_RESOURCE_DIMENSION type; m_resource->GetType(&type); return type; } D3D11_COMMON_RESOURCE_DESC GetResourceDesc() const { D3D11_COMMON_RESOURCE_DESC desc; GetCommonResourceDesc(m_resource, &desc); return desc; } Rc GetBufferView() const { return m_bufferView; } Rc GetImageView() const { return m_imageView; } D3D10ShaderResourceView* GetD3D10Iface() { return &m_d3d10; } static HRESULT GetDescFromResource( ID3D11Resource* pResource, D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc); static D3D11_SHADER_RESOURCE_VIEW_DESC1 PromoteDesc( const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, UINT Plane); static HRESULT NormalizeDesc( ID3D11Resource* pResource, D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc); static UINT GetPlaneSlice( const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc); private: ID3D11Resource* m_resource; D3D11_SHADER_RESOURCE_VIEW_DESC1 m_desc; D3D11_VK_VIEW_INFO m_info; Rc m_bufferView; Rc m_imageView; D3D10ShaderResourceView m_d3d10; }; } dxvk-2.6.1/src/d3d11/d3d11_view_uav.cpp000066400000000000000000000404631477473124000173440ustar00rootroot00000000000000#include "d3d11_device.h" #include "d3d11_buffer.h" #include "d3d11_resource.h" #include "d3d11_texture.h" #include "d3d11_view_uav.h" namespace dxvk { D3D11UnorderedAccessView::D3D11UnorderedAccessView( D3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc) : D3D11DeviceChild(pDevice), m_resource(pResource), m_desc(*pDesc) { ResourceAddRefPrivate(m_resource); D3D11_COMMON_RESOURCE_DESC resourceDesc; GetCommonResourceDesc(pResource, &resourceDesc); // Basic view resource info m_info.pResource = pResource; m_info.Dimension = resourceDesc.Dim; m_info.BindFlags = resourceDesc.BindFlags; if (resourceDesc.Dim == D3D11_RESOURCE_DIMENSION_BUFFER) { auto buffer = static_cast(pResource); DxvkBufferViewKey viewInfo; viewInfo.usage = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; if (pDesc->Buffer.Flags & D3D11_BUFFEREX_SRV_FLAG_RAW) { viewInfo.format = VK_FORMAT_R32_UINT; viewInfo.offset = sizeof(uint32_t) * pDesc->Buffer.FirstElement; viewInfo.size = sizeof(uint32_t) * pDesc->Buffer.NumElements; } else if (pDesc->Format == DXGI_FORMAT_UNKNOWN) { viewInfo.format = VK_FORMAT_R32_UINT; viewInfo.offset = buffer->Desc()->StructureByteStride * pDesc->Buffer.FirstElement; viewInfo.size = buffer->Desc()->StructureByteStride * pDesc->Buffer.NumElements; } else { viewInfo.format = pDevice->LookupFormat(pDesc->Format, DXGI_VK_FORMAT_MODE_COLOR).Format; const DxvkFormatInfo* formatInfo = lookupFormatInfo(viewInfo.format); viewInfo.offset = formatInfo->elementSize * pDesc->Buffer.FirstElement; viewInfo.size = formatInfo->elementSize * pDesc->Buffer.NumElements; } if (pDesc->Buffer.Flags & (D3D11_BUFFER_UAV_FLAG_APPEND | D3D11_BUFFER_UAV_FLAG_COUNTER)) m_counterView = CreateCounterBufferView(); // Populate view info struct m_info.Buffer.Offset = viewInfo.offset; m_info.Buffer.Length = viewInfo.size; m_bufferView = buffer->GetBuffer()->createView(viewInfo); } else { auto texture = GetCommonTexture(pResource); auto formatInfo = pDevice->LookupFormat(pDesc->Format, texture->GetFormatMode()); DxvkImageViewKey viewInfo; viewInfo.format = formatInfo.Format; viewInfo.aspects = formatInfo.Aspect; viewInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT; if (!util::isIdentityMapping(formatInfo.Swizzle)) Logger::warn(str::format("UAV format ", pDesc->Format, " has non-identity swizzle, but UAV swizzles are not supported")); switch (pDesc->ViewDimension) { case D3D11_UAV_DIMENSION_TEXTURE1D: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D; viewInfo.mipIndex = pDesc->Texture1D.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; break; case D3D11_UAV_DIMENSION_TEXTURE1DARRAY: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY; viewInfo.mipIndex = pDesc->Texture1DArray.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = pDesc->Texture1DArray.FirstArraySlice; viewInfo.layerCount = pDesc->Texture1DArray.ArraySize; break; case D3D11_UAV_DIMENSION_TEXTURE2D: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.mipIndex = pDesc->Texture2D.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; break; case D3D11_UAV_DIMENSION_TEXTURE2DARRAY: viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; viewInfo.mipIndex = pDesc->Texture2DArray.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = pDesc->Texture2DArray.FirstArraySlice; viewInfo.layerCount = pDesc->Texture2DArray.ArraySize; break; case D3D11_UAV_DIMENSION_TEXTURE3D: // FIXME we actually have to map this to a // 2D array view in order to support W slices viewInfo.viewType = VK_IMAGE_VIEW_TYPE_3D; viewInfo.mipIndex = pDesc->Texture3D.MipSlice; viewInfo.mipCount = 1; viewInfo.layerIndex = 0; viewInfo.layerCount = 1; break; default: throw DxvkError("D3D11: Invalid view dimension for image UAV"); } if (texture->GetPlaneCount() > 1) viewInfo.aspects = vk::getPlaneAspect(GetPlaneSlice(pDesc)); // Populate view info struct m_info.Image.Aspects = viewInfo.aspects; m_info.Image.MinLevel = viewInfo.mipIndex; m_info.Image.MinLayer = viewInfo.layerIndex; m_info.Image.NumLevels = viewInfo.mipCount; m_info.Image.NumLayers = viewInfo.layerCount; m_imageView = GetCommonTexture(pResource)->GetImage()->createView(viewInfo); } } D3D11UnorderedAccessView::~D3D11UnorderedAccessView() { ResourceReleasePrivate(m_resource); m_resource = nullptr; m_bufferView = nullptr; m_counterView = nullptr; m_imageView = nullptr; } HRESULT STDMETHODCALLTYPE D3D11UnorderedAccessView::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D11DeviceChild) || riid == __uuidof(ID3D11View) || riid == __uuidof(ID3D11UnorderedAccessView) || riid == __uuidof(ID3D11UnorderedAccessView1)) { *ppvObject = ref(this); return S_OK; } if (logQueryInterfaceError(__uuidof(ID3D11UnorderedAccessView), riid)) { Logger::warn("D3D11UnorderedAccessView::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } void STDMETHODCALLTYPE D3D11UnorderedAccessView::GetResource(ID3D11Resource** ppResource) { *ppResource = ref(m_resource); } void STDMETHODCALLTYPE D3D11UnorderedAccessView::GetDesc(D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc) { pDesc->Format = m_desc.Format; pDesc->ViewDimension = m_desc.ViewDimension; switch (m_desc.ViewDimension) { case D3D11_UAV_DIMENSION_UNKNOWN: break; case D3D11_UAV_DIMENSION_BUFFER: pDesc->Buffer = m_desc.Buffer; break; case D3D11_UAV_DIMENSION_TEXTURE1D: pDesc->Texture1D = m_desc.Texture1D; break; case D3D11_UAV_DIMENSION_TEXTURE1DARRAY: pDesc->Texture1DArray = m_desc.Texture1DArray; break; case D3D11_UAV_DIMENSION_TEXTURE2D: pDesc->Texture2D.MipSlice = m_desc.Texture2D.MipSlice; break; case D3D11_UAV_DIMENSION_TEXTURE2DARRAY: pDesc->Texture2DArray.MipSlice = m_desc.Texture2DArray.MipSlice; pDesc->Texture2DArray.FirstArraySlice = m_desc.Texture2DArray.FirstArraySlice; pDesc->Texture2DArray.ArraySize = m_desc.Texture2DArray.ArraySize; break; case D3D11_UAV_DIMENSION_TEXTURE3D: pDesc->Texture3D = m_desc.Texture3D; break; } } void STDMETHODCALLTYPE D3D11UnorderedAccessView::GetDesc1(D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc) { *pDesc = m_desc; } HRESULT D3D11UnorderedAccessView::GetDescFromResource( ID3D11Resource* pResource, D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc) { D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; pResource->GetType(&resourceDim); switch (resourceDim) { case D3D11_RESOURCE_DIMENSION_BUFFER: { D3D11_BUFFER_DESC bufferDesc; static_cast(pResource)->GetDesc(&bufferDesc); if (bufferDesc.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) { pDesc->Format = DXGI_FORMAT_UNKNOWN; pDesc->ViewDimension = D3D11_UAV_DIMENSION_BUFFER; pDesc->Buffer.FirstElement = 0; pDesc->Buffer.NumElements = bufferDesc.ByteWidth / bufferDesc.StructureByteStride; pDesc->Buffer.Flags = 0; return S_OK; } } return E_INVALIDARG; case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { D3D11_TEXTURE1D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); pDesc->Format = resourceDesc.Format; if (resourceDesc.ArraySize == 1) { pDesc->ViewDimension = D3D11_UAV_DIMENSION_TEXTURE1D; pDesc->Texture1D.MipSlice = 0; } else { pDesc->ViewDimension = D3D11_UAV_DIMENSION_TEXTURE1DARRAY; pDesc->Texture1DArray.MipSlice = 0; pDesc->Texture1DArray.FirstArraySlice = 0; pDesc->Texture1DArray.ArraySize = resourceDesc.ArraySize; } } return S_OK; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { D3D11_TEXTURE2D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); pDesc->Format = resourceDesc.Format; if (resourceDesc.ArraySize == 1) { pDesc->ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; pDesc->Texture2D.MipSlice = 0; pDesc->Texture2D.PlaneSlice = 0; } else { pDesc->ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY; pDesc->Texture2DArray.MipSlice = 0; pDesc->Texture2DArray.FirstArraySlice = 0; pDesc->Texture2DArray.ArraySize = resourceDesc.ArraySize; pDesc->Texture2DArray.PlaneSlice = 0; } } return S_OK; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: { D3D11_TEXTURE3D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); pDesc->Format = resourceDesc.Format; pDesc->ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D; pDesc->Texture3D.MipSlice = 0; pDesc->Texture3D.WSize = resourceDesc.Depth; } return S_OK; default: Logger::err(str::format( "D3D11: Unsupported dimension for unordered access view: ", resourceDim)); return E_INVALIDARG; } } D3D11_UNORDERED_ACCESS_VIEW_DESC1 D3D11UnorderedAccessView::PromoteDesc( const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, UINT Plane) { D3D11_UNORDERED_ACCESS_VIEW_DESC1 dstDesc; dstDesc.Format = pDesc->Format; dstDesc.ViewDimension = pDesc->ViewDimension; switch (pDesc->ViewDimension) { case D3D11_UAV_DIMENSION_UNKNOWN: break; case D3D11_UAV_DIMENSION_BUFFER: dstDesc.Buffer = pDesc->Buffer; break; case D3D11_UAV_DIMENSION_TEXTURE1D: dstDesc.Texture1D = pDesc->Texture1D; break; case D3D11_UAV_DIMENSION_TEXTURE1DARRAY: dstDesc.Texture1DArray = pDesc->Texture1DArray; break; case D3D11_UAV_DIMENSION_TEXTURE2D: dstDesc.Texture2D.MipSlice = pDesc->Texture2D.MipSlice; dstDesc.Texture2D.PlaneSlice = Plane; break; case D3D11_UAV_DIMENSION_TEXTURE2DARRAY: dstDesc.Texture2DArray.MipSlice = pDesc->Texture2DArray.MipSlice; dstDesc.Texture2DArray.FirstArraySlice = pDesc->Texture2DArray.FirstArraySlice; dstDesc.Texture2DArray.ArraySize = pDesc->Texture2DArray.ArraySize; dstDesc.Texture2DArray.PlaneSlice = Plane; break; case D3D11_UAV_DIMENSION_TEXTURE3D: dstDesc.Texture3D = pDesc->Texture3D; break; } return dstDesc; } HRESULT D3D11UnorderedAccessView::NormalizeDesc( ID3D11Resource* pResource, D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc) { D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; pResource->GetType(&resourceDim); DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; uint32_t numLayers = 0; switch (resourceDim) { case D3D11_RESOURCE_DIMENSION_BUFFER: { if (pDesc->ViewDimension != D3D11_UAV_DIMENSION_BUFFER) { Logger::err("D3D11: Incompatible view dimension for Buffer"); return E_INVALIDARG; } } break; case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { D3D11_TEXTURE1D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); if (pDesc->ViewDimension != D3D11_UAV_DIMENSION_TEXTURE1D && pDesc->ViewDimension != D3D11_UAV_DIMENSION_TEXTURE1DARRAY) { Logger::err("D3D11: Incompatible view dimension for Texture1D"); return E_INVALIDARG; } format = resourceDesc.Format; numLayers = resourceDesc.ArraySize; } break; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { D3D11_TEXTURE2D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); if (pDesc->ViewDimension != D3D11_UAV_DIMENSION_TEXTURE2D && pDesc->ViewDimension != D3D11_UAV_DIMENSION_TEXTURE2DARRAY) { Logger::err("D3D11: Incompatible view dimension for Texture2D"); return E_INVALIDARG; } format = resourceDesc.Format; numLayers = resourceDesc.ArraySize; } break; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: { D3D11_TEXTURE3D_DESC resourceDesc; static_cast(pResource)->GetDesc(&resourceDesc); if (pDesc->ViewDimension != D3D11_UAV_DIMENSION_TEXTURE3D) { Logger::err("D3D11: Incompatible view dimension for Texture3D"); return E_INVALIDARG; } format = resourceDesc.Format; numLayers = std::max(resourceDesc.Depth >> pDesc->Texture3D.MipSlice, 1u); } break; default: return E_INVALIDARG; } if (pDesc->Format == DXGI_FORMAT_UNKNOWN) pDesc->Format = format; switch (pDesc->ViewDimension) { case D3D11_UAV_DIMENSION_BUFFER: if (pDesc->Buffer.NumElements == 0) return E_INVALIDARG; break; case D3D11_UAV_DIMENSION_TEXTURE1DARRAY: if (pDesc->Texture1DArray.ArraySize > numLayers - pDesc->Texture1DArray.FirstArraySlice) pDesc->Texture1DArray.ArraySize = numLayers - pDesc->Texture1DArray.FirstArraySlice; break; case D3D11_UAV_DIMENSION_TEXTURE2D: break; case D3D11_UAV_DIMENSION_TEXTURE2DARRAY: if (pDesc->Texture2DArray.ArraySize > numLayers - pDesc->Texture2DArray.FirstArraySlice) pDesc->Texture2DArray.ArraySize = numLayers - pDesc->Texture2DArray.FirstArraySlice; break; case D3D11_UAV_DIMENSION_TEXTURE3D: if (pDesc->Texture3D.WSize > numLayers - pDesc->Texture3D.FirstWSlice) pDesc->Texture3D.WSize = numLayers - pDesc->Texture3D.FirstWSlice; break; default: break; } return S_OK; } UINT D3D11UnorderedAccessView::GetPlaneSlice(const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc) { switch (pDesc->ViewDimension) { case D3D11_UAV_DIMENSION_TEXTURE2D: return pDesc->Texture2D.PlaneSlice; case D3D11_UAV_DIMENSION_TEXTURE2DARRAY: return pDesc->Texture2DArray.PlaneSlice; default: return 0; } } Rc D3D11UnorderedAccessView::CreateCounterBufferView() { Rc device = m_parent->GetDXVKDevice(); DxvkBufferCreateInfo info; info.size = sizeof(uint32_t); info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT; info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT | device->getShaderPipelineStages(); info.access = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT; info.debugName = "UAV counter"; Rc buffer = device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); DxvkBufferViewKey viewInfo; viewInfo.format = VK_FORMAT_UNDEFINED; viewInfo.offset = 0; viewInfo.size = sizeof(uint32_t); viewInfo.usage = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; return buffer->createView(viewInfo); } } dxvk-2.6.1/src/d3d11/d3d11_view_uav.h000066400000000000000000000051371477473124000170100ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_device.h" #include "d3d11_device_child.h" #include "d3d11_view.h" namespace dxvk { class D3D11Device; /** * \brief Unordered access view * * Unordered access views are special in that they can * have counters, which can be used inside shaders to * atomically append or consume structures. */ class D3D11UnorderedAccessView : public D3D11DeviceChild { public: D3D11UnorderedAccessView( D3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc); ~D3D11UnorderedAccessView(); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) final; void STDMETHODCALLTYPE GetResource(ID3D11Resource** ppResource) final; void STDMETHODCALLTYPE GetDesc(D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc) final; void STDMETHODCALLTYPE GetDesc1(D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc) final; const D3D11_VK_VIEW_INFO& GetViewInfo() const { return m_info; } BOOL HasBindFlag(UINT Flags) const { return m_info.BindFlags & Flags; } BOOL HasCounter() const { return m_counterView != nullptr; } D3D11_RESOURCE_DIMENSION GetResourceType() const { D3D11_RESOURCE_DIMENSION type; m_resource->GetType(&type); return type; } Rc GetBufferView() const { return m_bufferView; } Rc GetImageView() const { return m_imageView; } Rc GetCounterView() const { return m_counterView; } static HRESULT GetDescFromResource( ID3D11Resource* pResource, D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc); static D3D11_UNORDERED_ACCESS_VIEW_DESC1 PromoteDesc( const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, UINT Plane); static HRESULT NormalizeDesc( ID3D11Resource* pResource, D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc); static UINT GetPlaneSlice( const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc); private: ID3D11Resource* m_resource; D3D11_UNORDERED_ACCESS_VIEW_DESC1 m_desc; D3D11_VK_VIEW_INFO m_info; Rc m_bufferView; Rc m_imageView; Rc m_counterView; Rc CreateCounterBufferView(); }; } dxvk-2.6.1/src/d3d11/meson.build000066400000000000000000000050141477473124000162520ustar00rootroot00000000000000d3d11_res = wrc_generator.process('version.rc') dxgi_common_src = [ '../dxgi/dxgi_format.cpp', ] d3d10_src = [ '../d3d10/d3d10_blend.cpp', '../d3d10/d3d10_buffer.cpp', '../d3d10/d3d10_depth_stencil.cpp', '../d3d10/d3d10_device.cpp', '../d3d10/d3d10_input_layout.cpp', '../d3d10/d3d10_multithread.cpp', '../d3d10/d3d10_query.cpp', '../d3d10/d3d10_rasterizer.cpp', '../d3d10/d3d10_sampler.cpp', '../d3d10/d3d10_texture.cpp', '../d3d10/d3d10_util.cpp', '../d3d10/d3d10_view_dsv.cpp', '../d3d10/d3d10_view_rtv.cpp', '../d3d10/d3d10_view_srv.cpp', ] d3d11_src = [ 'd3d11_annotation.cpp', 'd3d11_blend.cpp', 'd3d11_buffer.cpp', 'd3d11_class_linkage.cpp', 'd3d11_cmdlist.cpp', 'd3d11_context.cpp', 'd3d11_context_def.cpp', 'd3d11_context_ext.cpp', 'd3d11_context_imm.cpp', 'd3d11_cuda.cpp', 'd3d11_depth_stencil.cpp', 'd3d11_device.cpp', 'd3d11_enums.cpp', 'd3d11_features.cpp', 'd3d11_fence.cpp', 'd3d11_gdi.cpp', 'd3d11_initializer.cpp', 'd3d11_input_layout.cpp', 'd3d11_interop.cpp', 'd3d11_main.cpp', 'd3d11_on_12.cpp', 'd3d11_options.cpp', 'd3d11_query.cpp', 'd3d11_rasterizer.cpp', 'd3d11_resource.cpp', 'd3d11_sampler.cpp', 'd3d11_shader.cpp', 'd3d11_state.cpp', 'd3d11_state_object.cpp', 'd3d11_swapchain.cpp', 'd3d11_texture.cpp', 'd3d11_util.cpp', 'd3d11_video.cpp', 'd3d11_view_dsv.cpp', 'd3d11_view_rtv.cpp', 'd3d11_view_srv.cpp', 'd3d11_view_uav.cpp', ] d3d11_shaders = files([ 'shaders/d3d11_video_blit_frag.frag', 'shaders/d3d11_video_blit_vert.vert', ]) d3d11_ld_args = [] d3d11_link_depends = [] if platform == 'windows' d3d11_dxgi_dep = lib_dxgi else d3d11_ld_args += [ '-Wl,--version-script', join_paths(meson.current_source_dir(), 'd3d11.sym') ] d3d11_link_depends += files('d3d11.sym') d3d11_dxgi_dep = dxgi_dep endif d3d11_dll = shared_library(dxvk_name_prefix+'d3d11', dxgi_common_src + d3d11_src + d3d10_src, glsl_generator.process(d3d11_shaders), d3d11_res, dependencies : [ d3d11_dxgi_dep, dxbc_dep, dxvk_dep ], include_directories : dxvk_include_path, install : true, vs_module_defs : 'd3d11'+def_spec_ext, link_args : d3d11_ld_args, link_depends : [ d3d11_link_depends ], kwargs : dxvk_so_version, ) d3d11_dep = declare_dependency( link_with : [ d3d11_dll ], include_directories : [ dxvk_include_path ], ) if platform != 'windows' pkg.generate(d3d11_dll, filebase: dxvk_pkg_prefix + 'd3d11', subdirs: 'dxvk', ) endif dxvk-2.6.1/src/d3d11/shaders/000077500000000000000000000000001477473124000155415ustar00rootroot00000000000000dxvk-2.6.1/src/d3d11/shaders/d3d11_video_blit_frag.frag000066400000000000000000000051511477473124000224170ustar00rootroot00000000000000#version 450 #extension GL_EXT_samplerless_texture_functions : require // Can't use matrix types here since even a two-row // matrix will be padded to 16 bytes per column for // absolutely no reason layout(std140, set = 0, binding = 0) uniform ubo_t { vec4 color_matrix_r1; vec4 color_matrix_r2; vec4 color_matrix_r3; vec2 coord_matrix_c1; vec2 coord_matrix_c2; vec2 coord_matrix_c3; uvec2 src_offset; uvec2 src_extent; float y_min; float y_max; bool is_planar; }; layout(location = 0) in vec2 i_texcoord; layout(location = 0) out vec4 o_color; layout(set = 0, binding = 1) uniform texture2D s_inputY; layout(set = 0, binding = 2) uniform texture2D s_inputCbCr; void main() { // Transform input texture coordinates to // account for rotation and source rectangle mat3x2 coord_matrix = mat3x2( coord_matrix_c1, coord_matrix_c2, coord_matrix_c3); // Load color space transform mat3x4 color_matrix = mat3x4( color_matrix_r1, color_matrix_r2, color_matrix_r3); // Compute actual pixel coordinates to sample. We filter // manually in order to avoid bleeding from pixels outside // the source rectangle. vec2 abs_size_y = vec2(textureSize(s_inputY, 0)); vec2 abs_size_c = vec2(textureSize(s_inputCbCr, 0)); vec2 coord = coord_matrix * vec3(i_texcoord, 1.0f); coord -= 0.5f / abs_size_y; vec2 size_factor = abs_size_c / abs_size_y; vec2 src_lo = vec2(src_offset); vec2 src_hi = vec2(src_offset + src_extent - 1u); vec2 abs_coord = coord * abs_size_y; vec2 fract_coord = fract(clamp(abs_coord, src_lo, src_hi)); vec4 accum = vec4(0.0f, 0.0f, 0.0f, 0.0f); for (int i = 0; i < 4; i++) { ivec2 offset = ivec2(i & 1, i >> 1); // Compute exact pixel coordinates for the current // iteration and clamp it to the source rectangle. vec2 fetch_coord = clamp(abs_coord + vec2(offset), src_lo, src_hi); // Fetch actual pixel color in source color space vec4 color; if (is_planar) { color.g = texelFetch(s_inputY, ivec2(fetch_coord), 0).r; color.rb = texelFetch(s_inputCbCr, ivec2(fetch_coord * size_factor), 0).gr; color.g = clamp((color.g - y_min) / (y_max - y_min), 0.0f, 1.0f); color.a = 1.0f; } else { color = texelFetch(s_inputY, ivec2(fetch_coord), 0); } // Transform color space before accumulation color.rgb = vec4(color.rgb, 1.0f) * color_matrix; // Filter and accumulate final pixel color vec2 factor = fract_coord; if (offset.x == 0) factor.x = 1.0f - factor.x; if (offset.y == 0) factor.y = 1.0f - factor.y; accum += factor.x * factor.y * color; } o_color = accum; } dxvk-2.6.1/src/d3d11/shaders/d3d11_video_blit_vert.vert000066400000000000000000000003641477473124000225220ustar00rootroot00000000000000#version 450 layout(location = 0) out vec2 o_texcoord; void main() { vec2 coord = vec2( float(gl_VertexIndex & 1) * 2.0f, float(gl_VertexIndex & 2)); o_texcoord = coord; gl_Position = vec4(-1.0f + 2.0f * coord, 0.0f, 1.0f); } dxvk-2.6.1/src/d3d11/version.rc000066400000000000000000000014721477473124000161270ustar00rootroot00000000000000#include // DLL version information. VS_VERSION_INFO VERSIONINFO FILEVERSION 10,0,17763,1 PRODUCTVERSION 10,0,17763,1 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS 0 FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "080904b0" BEGIN VALUE "CompanyName", "DXVK" VALUE "FileDescription", "Direct3D 11 Runtime" VALUE "FileVersion", "10.0.17763.1 (WinBuild.160101.0800)" VALUE "InternalName", "D3D11.dll" VALUE "LegalCopyright", "zlib/libpng license" VALUE "OriginalFilename", "D3D11.dll" VALUE "ProductName", "DXVK" VALUE "ProductVersion", "10.0.17763.1" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0809, 1200 END END dxvk-2.6.1/src/d3d8/000077500000000000000000000000001477473124000140365ustar00rootroot00000000000000dxvk-2.6.1/src/d3d8/d3d8.def000066400000000000000000000001671477473124000152640ustar00rootroot00000000000000LIBRARY D3D8.DLL EXPORTS ValidatePixelShader @ 2 ValidateVertexShader @ 3 DebugSetMute @ 4 Direct3DCreate8 @ 5 dxvk-2.6.1/src/d3d8/d3d8.sym000066400000000000000000000001721477473124000153320ustar00rootroot00000000000000{ global: ValidatePixelShader; ValidateVertexShader; DebugSetMute; Direct3DCreate8; local: *; }; dxvk-2.6.1/src/d3d8/d3d8_batch.h000066400000000000000000000174151477473124000161220ustar00rootroot00000000000000#pragma once #include "d3d8_include.h" #include "d3d8_buffer.h" #include "d3d8_format.h" #include #include namespace dxvk { inline constexpr size_t D3DPT_COUNT = size_t(D3DPT_TRIANGLEFAN) + 1; inline constexpr D3DPRIMITIVETYPE D3DPT_INVALID = D3DPRIMITIVETYPE(0); // Vertex buffer that can handle many tiny locks while // still maintaing the lock ordering of direct-mapped buffers. class D3D8BatchBuffer final : public D3D8VertexBuffer { public: D3D8BatchBuffer( D3D8Device* pDevice, D3DPOOL Pool, DWORD Usage, UINT Length, DWORD FVF) : D3D8VertexBuffer(pDevice, nullptr, Pool, Usage) , m_data(Length) , m_fvf(FVF) { } HRESULT STDMETHODCALLTYPE Lock( UINT OffsetToLock, UINT SizeToLock, BYTE** ppbData, DWORD Flags) final { *ppbData = m_data.data() + OffsetToLock; return D3D_OK; } HRESULT STDMETHODCALLTYPE Unlock() final { return D3D_OK; } HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc) final { if (unlikely(pDesc == nullptr)) return D3DERR_INVALIDCALL; pDesc->Format = D3DFMT_VERTEXDATA; pDesc->Type = D3DRTYPE_VERTEXBUFFER; pDesc->Usage = m_usage; pDesc->Pool = m_pool; pDesc->Size = m_data.size(); pDesc->FVF = m_fvf; return D3D_OK; } void STDMETHODCALLTYPE PreLoad() final { } const void* GetPtr(UINT byteOffset = 0) const { return m_data.data() + byteOffset; } UINT Size() const { return m_data.size(); } private: std::vector m_data; DWORD m_fvf; }; // Main handler for batching D3D8 draw calls. class D3D8Batcher { struct Batch { D3DPRIMITIVETYPE PrimitiveType = D3DPT_INVALID; std::vector Indices; UINT Offset = 0; UINT MinVertex = std::numeric_limits::max(); UINT MaxVertex = 0; UINT PrimitiveCount = 0; UINT DrawCallCount = 0; }; public: D3D8Batcher(D3D8Device* pDevice8, Com&& pDevice9) : m_device8(pDevice8) , m_device(std::move(pDevice9)) { } inline D3D8BatchBuffer* CreateVertexBuffer(UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool) { return ref(new D3D8BatchBuffer(m_device8, Pool, Usage, Length, FVF)); } inline void StateChange() { if (likely(m_batches.empty())) return; for (auto& draw : m_batches) { if (draw.PrimitiveType == D3DPT_INVALID) continue; for (auto& index : draw.Indices) index -= draw.MinVertex; m_device->DrawIndexedPrimitiveUP( d3d9::D3DPRIMITIVETYPE(draw.PrimitiveType), 0, draw.MaxVertex - draw.MinVertex, draw.PrimitiveCount, draw.Indices.data(), d3d9::D3DFMT_INDEX16, m_stream->GetPtr(draw.MinVertex * m_stride), m_stride); m_device->SetStreamSource(0, D3D8VertexBuffer::GetD3D9Nullable(m_stream), 0, m_stride); m_device->SetIndices(D3D8IndexBuffer::GetD3D9Nullable(m_indices)); draw.PrimitiveType = D3DPRIMITIVETYPE(0); draw.Offset = 0; draw.MinVertex = std::numeric_limits::max(); draw.MaxVertex = 0; draw.PrimitiveCount = 0; draw.DrawCallCount = 0; } } inline void EndFrame() { // Nothing to be done. } inline HRESULT DrawPrimitive( D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount) { // None of this linestrip or fan malarkey D3DPRIMITIVETYPE batchedPrimType = PrimitiveType; switch (PrimitiveType) { case D3DPT_LINESTRIP: batchedPrimType = D3DPT_LINELIST; break; case D3DPT_TRIANGLEFAN: batchedPrimType = D3DPT_TRIANGLELIST; break; default: break; } Batch* batch = &m_batches[size_t(batchedPrimType)]; batch->PrimitiveType = batchedPrimType; //UINT vertices = GetVertexCount8(PrimitiveType, PrimitiveCount); switch (PrimitiveType) { case D3DPT_POINTLIST: batch->Indices.resize(batch->Offset + PrimitiveCount); for (UINT i = 0; i < PrimitiveCount; i++) batch->Indices[batch->Offset++] = (StartVertex + i); break; case D3DPT_LINELIST: batch->Indices.resize(batch->Offset + PrimitiveCount * 2); for (UINT i = 0; i < PrimitiveCount; i++) { batch->Indices[batch->Offset++] = (StartVertex + i * 2 + 0); batch->Indices[batch->Offset++] = (StartVertex + i * 2 + 1); } break; case D3DPT_LINESTRIP: batch->Indices.resize(batch->Offset + PrimitiveCount * 2); for (UINT i = 0; i < PrimitiveCount; i++) { batch->Indices[batch->Offset++] = (StartVertex + i + 0); batch->Indices[batch->Offset++] = (StartVertex + i + 1); } break; case D3DPT_TRIANGLELIST: batch->Indices.resize(batch->Offset + PrimitiveCount * 3); for (UINT i = 0; i < PrimitiveCount; i++) { batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 0); batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 1); batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 2); } break; case D3DPT_TRIANGLESTRIP: // Join with degenerate triangle // 1 2 3, 3 4, 4 5 6 batch->Indices.resize(batch->Offset + PrimitiveCount + 2); if (batch->Offset > 0) { batch->Indices[batch->Offset + 1] = batch->Indices[batch->Offset-2]; batch->Indices[batch->Offset += 2] = StartVertex; } for (UINT i = 0; i < PrimitiveCount; i++) { batch->Indices[batch->Offset++] = (StartVertex + i + 0); } break; // 1 2 3 4 5 6 7 -> 1 2 3, 1 3 4, 1 4 5, 1 5 6, 1 6 7 case D3DPT_TRIANGLEFAN: batch->Indices.resize(batch->Offset + PrimitiveCount * 3); for (UINT i = 0; i < PrimitiveCount; i++) { batch->Indices[batch->Offset++] = (StartVertex + 0); batch->Indices[batch->Offset++] = (StartVertex + i + 1); batch->Indices[batch->Offset++] = (StartVertex + i + 2); } break; default: return D3DERR_INVALIDCALL; } batch->MinVertex = std::min(batch->MinVertex, StartVertex); if (!batch->Indices.empty()) batch->MaxVertex = std::max(batch->MaxVertex, UINT(batch->Indices.back() + 1)); batch->PrimitiveCount += PrimitiveCount; batch->DrawCallCount++; return D3D_OK; } inline void SetStream(UINT num, D3D8VertexBuffer* stream, UINT stride) { if (unlikely(num != 0)) { StateChange(); return; } if (unlikely(m_stream != stream || m_stride != stride)) { StateChange(); m_stream = static_cast(stream); m_stride = stride; } } inline void SetIndices(D3D8IndexBuffer* indices, INT baseVertexIndex) { if (m_indices != indices || m_baseVertexIndex != baseVertexIndex) { StateChange(); m_indices = indices; m_baseVertexIndex = baseVertexIndex; } } private: D3D8Device* m_device8; Com m_device; D3D8BatchBuffer* m_stream = nullptr; UINT m_stride = 0; D3D8IndexBuffer* m_indices = nullptr; INT m_baseVertexIndex = 0; std::array m_batches; }; } dxvk-2.6.1/src/d3d8/d3d8_buffer.cpp000066400000000000000000000024351477473124000166410ustar00rootroot00000000000000#include "d3d8_buffer.h" #include "d3d8_device.h" namespace dxvk { // D3D8VertexBuffer D3D8VertexBuffer::D3D8VertexBuffer( D3D8Device* pDevice, Com&& pBuffer, D3DPOOL Pool, DWORD Usage) : D3D8VertexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) { } D3DRESOURCETYPE STDMETHODCALLTYPE D3D8VertexBuffer::GetType() { return D3DRTYPE_VERTEXBUFFER; } HRESULT STDMETHODCALLTYPE D3D8VertexBuffer::GetDesc(D3DVERTEXBUFFER_DESC* pDesc) { return GetD3D9()->GetDesc(reinterpret_cast(pDesc)); } // D3D8IndexBuffer D3D8IndexBuffer::D3D8IndexBuffer( D3D8Device* pDevice, Com&& pBuffer, D3DPOOL Pool, DWORD Usage) : D3D8IndexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) { } D3DRESOURCETYPE STDMETHODCALLTYPE D3D8IndexBuffer::GetType() { return D3DRTYPE_INDEXBUFFER; } HRESULT STDMETHODCALLTYPE D3D8IndexBuffer::GetDesc(D3DINDEXBUFFER_DESC* pDesc) { return GetD3D9()->GetDesc(reinterpret_cast(pDesc)); } }dxvk-2.6.1/src/d3d8/d3d8_buffer.h000066400000000000000000000045441477473124000163110ustar00rootroot00000000000000#pragma once #include "d3d8_include.h" #include "d3d8_options.h" #include "d3d8_resource.h" namespace dxvk { template class D3D8Buffer : public D3D8Resource { public: D3D8Buffer( D3D8Device* pDevice, Com&& pBuffer, D3DPOOL Pool, DWORD Usage) : D3D8Resource (pDevice, Pool, std::move(pBuffer)) , m_usage (Usage) { m_options = this->GetParent()->GetOptions(); } HRESULT STDMETHODCALLTYPE Lock( UINT OffsetToLock, UINT SizeToLock, BYTE** ppbData, DWORD Flags) { if (m_options->forceLegacyDiscard && (Flags & D3DLOCK_DISCARD) && !((m_usage & D3DUSAGE_DYNAMIC) && (m_usage & D3DUSAGE_WRITEONLY))) Flags &= ~D3DLOCK_DISCARD; return this->GetD3D9()->Lock( OffsetToLock, SizeToLock, reinterpret_cast(ppbData), Flags); } HRESULT STDMETHODCALLTYPE Unlock() { return this->GetD3D9()->Unlock(); } void STDMETHODCALLTYPE PreLoad() { this->GetD3D9()->PreLoad(); } protected: const D3D8Options* m_options; const DWORD m_usage; }; using D3D8VertexBufferBase = D3D8Buffer; class D3D8VertexBuffer : public D3D8VertexBufferBase { public: D3D8VertexBuffer( D3D8Device* pDevice, Com&& pBuffer, D3DPOOL Pool, DWORD Usage); D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final; HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc); }; using D3D8IndexBufferBase = D3D8Buffer; class D3D8IndexBuffer final : public D3D8IndexBufferBase { public: D3D8IndexBuffer( D3D8Device* pDevice, Com&& pBuffer, D3DPOOL Pool, DWORD Usage); D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final; HRESULT STDMETHODCALLTYPE GetDesc(D3DINDEXBUFFER_DESC* pDesc) final; }; }dxvk-2.6.1/src/d3d8/d3d8_caps.h000066400000000000000000000002141477473124000157540ustar00rootroot00000000000000#pragma once namespace dxvk::d8caps { inline constexpr uint32_t MAX_TEXTURE_STAGES = 8; inline constexpr uint32_t MAX_STREAMS = 16; }dxvk-2.6.1/src/d3d8/d3d8_d3d9_util.h000066400000000000000000000206041477473124000166330ustar00rootroot00000000000000#pragma once // Utility functions for converting // between DirectX8 and DirectX9 types. #include "d3d8_include.h" #include "d3d8_format.h" #include "d3d8_options.h" #include namespace dxvk { // (8<-9) D3DCAPSX: Writes to D3DCAPS8 from D3DCAPS9 inline void ConvertCaps8(const d3d9::D3DCAPS9& caps9, D3DCAPS8* pCaps8) { // should be aligned std::memcpy(pCaps8, &caps9, sizeof(D3DCAPS8)); // Max supported shader model is PS 1.4 and VS 1.1 pCaps8->VertexShaderVersion = D3DVS_VERSION(1, 1); // Late fixed-function capable hardware will advertise VS 1.1 // support, but will not advertise any support for PS if (likely(caps9.PixelShaderVersion != D3DPS_VERSION(0, 0))) pCaps8->PixelShaderVersion = D3DPS_VERSION(1, 4); // Remove D3D9-specific caps: pCaps8->Caps2 &= ~D3DCAPS2_CANAUTOGENMIPMAP; pCaps8->Caps3 &= ~D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION & ~D3DCAPS3_COPY_TO_VIDMEM & ~D3DCAPS3_COPY_TO_SYSTEMMEM; pCaps8->PrimitiveMiscCaps &= ~D3DPMISCCAPS_INDEPENDENTWRITEMASKS & ~D3DPMISCCAPS_PERSTAGECONSTANT & ~D3DPMISCCAPS_FOGANDSPECULARALPHA & ~D3DPMISCCAPS_SEPARATEALPHABLEND & ~D3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS & ~D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING & ~D3DPMISCCAPS_FOGVERTEXCLAMPED & ~D3DPMISCCAPS_POSTBLENDSRGBCONVERT; pCaps8->RasterCaps &= ~D3DPRASTERCAPS_SCISSORTEST & ~D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS & ~D3DPRASTERCAPS_DEPTHBIAS & ~D3DPRASTERCAPS_MULTISAMPLE_TOGGLE; pCaps8->SrcBlendCaps &= ~D3DPBLENDCAPS_BLENDFACTOR; pCaps8->DestBlendCaps &= ~D3DPBLENDCAPS_BLENDFACTOR; pCaps8->LineCaps &= ~D3DLINECAPS_ANTIALIAS; pCaps8->StencilCaps &= ~D3DSTENCILCAPS_TWOSIDED; pCaps8->VertexProcessingCaps &= ~D3DVTXPCAPS_TEXGEN_SPHEREMAP; // Add D3D8-specific caps: // Removed in D3D9, since it can always render windowed pCaps8->Caps2 |= D3DCAPS2_CANRENDERWINDOWED // A remnant from a bygone age of ddraw interop most likely /* | D3DCAPS2_NO2DDURING3DSCENE*/; // Used in conjunction with D3DPRASTERCAPS_PAT, but generally unadvertised /*pCaps8->PrimitiveMiscCaps |= D3DPMISCCAPS_LINEPATTERNREP;*/ // Replaced by D3DPRASTERCAPS_DEPTHBIAS in D3D9 pCaps8->RasterCaps |= D3DPRASTERCAPS_ZBIAS // Advertised on Nvidia cards by modern drivers, but not on AMD or Intel /* | D3DPRASTERCAPS_ANTIALIASEDGES*/ // Advertised on Nvidia cards, but not on AMD or Intel /* | D3DPRASTERCAPS_STRETCHBLTMULTISAMPLE*/ // TODO: Implement D3DRS_LINEPATTERN - vkCmdSetLineRasterizationModeEXT /* | D3DPRASTERCAPS_PAT*/; // MAG only filter caps, generally unsupported /*pCaps8->TextureFilterCaps |= D3DPTFILTERCAPS_MAGFAFLATCUBIC*/ /* | D3DPTFILTERCAPS_MAGFGAUSSIANCUBIC;*/ /*pCaps8->CubeTextureFilterCaps = pCaps8->TextureFilterCaps;*/ /*pCaps8->VolumeTextureFilterCaps = pCaps8->TextureFilterCaps;*/ // Not advertised on any modern hardware /*pCaps8->VertexProcessingCaps |= D3DVTXPCAPS_NO_VSDT_UBYTE4;*/ } // (9<-8) D3DD3DPRESENT_PARAMETERS: Returns D3D9's params given an input for D3D8 inline d3d9::D3DPRESENT_PARAMETERS ConvertPresentParameters9(D3DPRESENT_PARAMETERS* pParams) { // A 0 back buffer count needs to be corrected and made visible to the D3D8 application as well pParams->BackBufferCount = std::max(pParams->BackBufferCount, 1u); if (pParams->BackBufferFormat == D3DFMT_UNKNOWN) pParams->BackBufferFormat = D3DFMT_X8R8G8B8; d3d9::D3DPRESENT_PARAMETERS params; params.BackBufferWidth = pParams->BackBufferWidth; params.BackBufferHeight = pParams->BackBufferHeight; params.BackBufferFormat = d3d9::D3DFORMAT(pParams->BackBufferFormat); params.BackBufferCount = pParams->BackBufferCount; params.MultiSampleType = d3d9::D3DMULTISAMPLE_TYPE(pParams->MultiSampleType); // MultiSampleQuality is only used with D3DMULTISAMPLE_NONMASKABLE, which is not available in D3D8 params.MultiSampleQuality = 0; // If an application passes multiple D3DPRESENT_INTERVAL flags, this will be // validated appropriately by D3D9. Simply copy the values here. UINT PresentationInterval = pParams->FullScreen_PresentationInterval; if (pParams->Windowed) { // D3D8: For windowed swap chain, the back buffer is copied to the window immediately. PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; } D3DSWAPEFFECT SwapEffect = pParams->SwapEffect; if (SwapEffect == D3DSWAPEFFECT_COPY_VSYNC) { // D3DSWAPEFFECT_COPY_VSYNC has been removed from D3D9, use D3DSWAPEFFECT_COPY SwapEffect = D3DSWAPEFFECT_COPY; // D3D8: In windowed mode, D3DSWAPEFFECT_COPY_VSYNC enables VSYNC. // In fullscreen, D3DPRESENT_INTERVAL_IMMEDIATE is meaningless. if (pParams->Windowed || PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE) { PresentationInterval = D3DPRESENT_INTERVAL_ONE; } } params.SwapEffect = d3d9::D3DSWAPEFFECT(SwapEffect); params.hDeviceWindow = pParams->hDeviceWindow; params.Windowed = pParams->Windowed; params.EnableAutoDepthStencil = pParams->EnableAutoDepthStencil; params.AutoDepthStencilFormat = d3d9::D3DFORMAT(pParams->AutoDepthStencilFormat); params.Flags = pParams->Flags; // D3DPRESENT_RATE_UNLIMITED is unsupported, use D3DPRESENT_RATE_DEFAULT (or 0) if (unlikely(pParams->FullScreen_RefreshRateInHz == D3DPRESENT_RATE_UNLIMITED)) { params.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; } else { params.FullScreen_RefreshRateInHz = pParams->FullScreen_RefreshRateInHz; } // FullScreen_PresentationInterval -> PresentationInterval params.PresentationInterval = PresentationInterval; return params; } // (8<-9) Convert D3DSURFACE_DESC inline void ConvertSurfaceDesc8(const d3d9::D3DSURFACE_DESC* pSurf9, D3DSURFACE_DESC* pSurf8) { pSurf8->Format = D3DFORMAT(pSurf9->Format); pSurf8->Type = D3DRESOURCETYPE(pSurf9->Type); pSurf8->Usage = pSurf9->Usage; pSurf8->Pool = D3DPOOL(pSurf9->Pool); pSurf8->Size = getSurfaceSize(pSurf8->Format, pSurf9->Width, pSurf9->Height); pSurf8->MultiSampleType = D3DMULTISAMPLE_TYPE(pSurf9->MultiSampleType); // DX8: No multisample quality pSurf8->Width = pSurf9->Width; pSurf8->Height = pSurf9->Height; } // (8<-9) Convert D3DVOLUME_DESC inline void ConvertVolumeDesc8(const d3d9::D3DVOLUME_DESC* pVol9, D3DVOLUME_DESC* pVol8) { pVol8->Format = D3DFORMAT(pVol9->Format); pVol8->Type = D3DRESOURCETYPE(pVol9->Type); pVol8->Usage = pVol9->Usage; pVol8->Pool = D3DPOOL(pVol9->Pool); pVol8->Size = getSurfaceSize(pVol8->Format, pVol9->Width, pVol9->Height) * pVol9->Depth; pVol8->Width = pVol9->Width; pVol8->Height = pVol9->Height; pVol8->Depth = pVol9->Depth; } // If this D3DTEXTURESTAGESTATETYPE has been remapped to a d3d9::D3DSAMPLERSTATETYPE // it will be returned, otherwise returns -1u inline d3d9::D3DSAMPLERSTATETYPE GetSamplerStateType9(const D3DTEXTURESTAGESTATETYPE StageType) { switch (StageType) { // 13-21: case D3DTSS_ADDRESSU: return d3d9::D3DSAMP_ADDRESSU; case D3DTSS_ADDRESSV: return d3d9::D3DSAMP_ADDRESSV; case D3DTSS_BORDERCOLOR: return d3d9::D3DSAMP_BORDERCOLOR; case D3DTSS_MAGFILTER: return d3d9::D3DSAMP_MAGFILTER; case D3DTSS_MINFILTER: return d3d9::D3DSAMP_MINFILTER; case D3DTSS_MIPFILTER: return d3d9::D3DSAMP_MIPFILTER; case D3DTSS_MIPMAPLODBIAS: return d3d9::D3DSAMP_MIPMAPLODBIAS; case D3DTSS_MAXMIPLEVEL: return d3d9::D3DSAMP_MAXMIPLEVEL; case D3DTSS_MAXANISOTROPY: return d3d9::D3DSAMP_MAXANISOTROPY; // 25: case D3DTSS_ADDRESSW: return d3d9::D3DSAMP_ADDRESSW; default: return d3d9::D3DSAMPLERSTATETYPE(-1u); } } } dxvk-2.6.1/src/d3d8/d3d8_device.cpp000066400000000000000000002174231477473124000166340ustar00rootroot00000000000000#include "d3d8_device.h" #include "d3d8_interface.h" #include "d3d8_shader.h" #ifdef MSC_VER #pragma fenv_access (on) #endif namespace dxvk { static constexpr DWORD isFVF(DWORD Handle) { return (Handle & D3DFVF_RESERVED0) == 0; } static constexpr DWORD getShaderHandle(DWORD Index) { return (Index << 1) | D3DFVF_RESERVED0; } static constexpr DWORD getShaderIndex(DWORD Handle) { if ((Handle & D3DFVF_RESERVED0) != 0) { return ((Handle & ~(D3DFVF_RESERVED0)) >> 1) - 1; } else { return Handle; } } struct D3D8VertexShaderInfo { Com pVertexDecl; Com pVertexShader; std::vector declaration; std::vector function; }; D3D8Device::D3D8Device( D3D8Interface* pParent, Com&& pDevice, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pParams) : D3D8DeviceBase(std::move(pDevice)) , m_d3d8Options(pParent->GetOptions()) , m_parent(pParent) , m_presentParams(*pParams) , m_deviceType(DeviceType) , m_window(hFocusWindow) , m_behaviorFlags(BehaviorFlags) , m_multithread(BehaviorFlags & D3DCREATE_MULTITHREADED) { // Get the bridge interface to D3D9. if (FAILED(GetD3D9()->QueryInterface(__uuidof(IDxvkD3D8Bridge), reinterpret_cast(&m_bridge)))) { throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); } ResetState(); RecreateBackBuffersAndAutoDepthStencil(); if (m_d3d8Options.batching) m_batcher = new D3D8Batcher(this, GetD3D9()); d3d9::D3DCAPS9 caps9; HRESULT res = GetD3D9()->GetDeviceCaps(&caps9); if (unlikely(SUCCEEDED(res) && caps9.PixelShaderVersion == D3DPS_VERSION(0, 0))) m_isFixedFunctionOnly = true; } D3D8Device::~D3D8Device() { if (m_batcher) delete m_batcher; } HRESULT STDMETHODCALLTYPE D3D8Device::GetInfo(DWORD DevInfoID, void* pDevInfoStruct, DWORD DevInfoStructSize) { Logger::debug(str::format("D3D8Device::GetInfo: ", DevInfoID)); if (unlikely(pDevInfoStruct == nullptr || DevInfoStructSize == 0)) return D3DERR_INVALIDCALL; HRESULT res; Com pQuery; switch (DevInfoID) { // pre-D3D8 queries case 0: case D3DDEVINFOID_TEXTUREMANAGER: case D3DDEVINFOID_D3DTEXTUREMANAGER: case D3DDEVINFOID_TEXTURING: return E_FAIL; case D3DDEVINFOID_VCACHE: // The query will return D3D_OK on Nvidia and D3DERR_NOTAVAILABLE on AMD/Intel // in D3D9, however in the case of the latter we'll need to return a // zeroed out query result and S_FALSE. This behavior has been observed both // on modern native AMD drivers and D3D8-era native ATI drivers. res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_VCACHE, &pQuery); struct D3DDEVINFO_VCACHE { DWORD Pattern; DWORD OptMethod; DWORD CacheSize; DWORD MagicNumber; }; if (FAILED(res)) { // The struct size needs to be at least equal or larger if (DevInfoStructSize < sizeof(D3DDEVINFO_VCACHE)) return D3DERR_INVALIDCALL; memset(pDevInfoStruct, 0, sizeof(D3DDEVINFO_VCACHE)); return S_FALSE; } break; case D3DDEVINFOID_RESOURCEMANAGER: // Not yet implemented by D9VK. res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_RESOURCEMANAGER, &pQuery); break; case D3DDEVINFOID_VERTEXSTATS: // Not yet implemented by D9VK. res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_VERTEXSTATS, &pQuery); break; default: Logger::warn(str::format("D3D8Device::GetInfo: Unsupported device info ID: ", DevInfoID)); return E_FAIL; } if (FAILED(res)) { if (res == D3DERR_NOTAVAILABLE) // unsupported return E_FAIL; else // any unknown error return S_FALSE; } if (pQuery != nullptr) { // Immediately issue the query. D3D9 will begin it automatically before ending. pQuery->Issue(D3DISSUE_END); // TODO: Will immediately issuing the query actually yield meaingful results? // // Only relevant once RESOURCEMANAGER or VERTEXSTATS are implemented by D9VK, // since VCACHE queries will immediately return data during this call. res = pQuery->GetData(pDevInfoStruct, DevInfoStructSize, D3DGETDATA_FLUSH); } return res; } HRESULT STDMETHODCALLTYPE D3D8Device::TestCooperativeLevel() { // Equivalent of D3D11/DXGI present tests. return GetD3D9()->TestCooperativeLevel(); } UINT STDMETHODCALLTYPE D3D8Device::GetAvailableTextureMem() { return GetD3D9()->GetAvailableTextureMem(); } HRESULT STDMETHODCALLTYPE D3D8Device::ResourceManagerDiscardBytes(DWORD bytes) { return GetD3D9()->EvictManagedResources(); } HRESULT STDMETHODCALLTYPE D3D8Device::GetDirect3D(IDirect3D8** ppD3D8) { if (ppD3D8 == nullptr) return D3DERR_INVALIDCALL; *ppD3D8 = m_parent.ref(); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D8Device::GetDeviceCaps(D3DCAPS8* pCaps) { d3d9::D3DCAPS9 caps9; HRESULT res = GetD3D9()->GetDeviceCaps(&caps9); if (likely(SUCCEEDED(res))) ConvertCaps8(caps9, pCaps); return res; } HRESULT STDMETHODCALLTYPE D3D8Device::GetDisplayMode(D3DDISPLAYMODE* pMode) { // swap chain 0 return GetD3D9()->GetDisplayMode(0, (d3d9::D3DDISPLAYMODE*)pMode); } HRESULT STDMETHODCALLTYPE D3D8Device::GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS* pParameters) { return GetD3D9()->GetCreationParameters((d3d9::D3DDEVICE_CREATION_PARAMETERS*)pParameters); } HRESULT STDMETHODCALLTYPE D3D8Device::SetCursorProperties( UINT XHotSpot, UINT YHotSpot, IDirect3DSurface8* pCursorBitmap) { D3D8Surface* surf = static_cast(pCursorBitmap); return GetD3D9()->SetCursorProperties(XHotSpot, YHotSpot, D3D8Surface::GetD3D9Nullable(surf)); } void STDMETHODCALLTYPE D3D8Device::SetCursorPosition(UINT XScreenSpace, UINT YScreenSpace, DWORD Flags) { GetD3D9()->SetCursorPosition(XScreenSpace, YScreenSpace, Flags); } // Microsoft d3d8.h in the DirectX 9 SDK uses a different function signature... void STDMETHODCALLTYPE D3D8Device::SetCursorPosition(int X, int Y, DWORD Flags) { GetD3D9()->SetCursorPosition(X, Y, Flags); } BOOL STDMETHODCALLTYPE D3D8Device::ShowCursor(BOOL bShow) { return GetD3D9()->ShowCursor(bShow); } HRESULT STDMETHODCALLTYPE D3D8Device::CreateAdditionalSwapChain( D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DSwapChain8** ppSwapChain) { InitReturnPtr(ppSwapChain); if (unlikely(pPresentationParameters == nullptr || ppSwapChain == nullptr)) return D3DERR_INVALIDCALL; Com pSwapChain9; d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters); HRESULT res = GetD3D9()->CreateAdditionalSwapChain( ¶ms, &pSwapChain9 ); if (likely(SUCCEEDED(res))) *ppSwapChain = ref(new D3D8SwapChain(this, pPresentationParameters, std::move(pSwapChain9))); return res; } HRESULT STDMETHODCALLTYPE D3D8Device::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) { D3D8DeviceLock lock = LockDevice(); StateChange(); if (unlikely(pPresentationParameters == nullptr)) return D3DERR_INVALIDCALL; // D3DSWAPEFFECT_COPY can not be used with more than one back buffer. // This is also technically true for D3DSWAPEFFECT_COPY_VSYNC, however // RC Cars depends on it not being rejected. if (unlikely(pPresentationParameters->SwapEffect == D3DSWAPEFFECT_COPY && pPresentationParameters->BackBufferCount > 1)) return D3DERR_INVALIDCALL; // In D3D8 nothing except D3DPRESENT_INTERVAL_DEFAULT can be used // as a flag for windowed presentation. if (unlikely(pPresentationParameters->Windowed && pPresentationParameters->FullScreen_PresentationInterval != D3DPRESENT_INTERVAL_DEFAULT)) return D3DERR_INVALIDCALL; m_presentParams = *pPresentationParameters; ResetState(); d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters); HRESULT res = GetD3D9()->Reset(¶ms); if (likely(SUCCEEDED(res))) RecreateBackBuffersAndAutoDepthStencil(); return res; } HRESULT STDMETHODCALLTYPE D3D8Device::Present( const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion) { D3D8DeviceLock lock = LockDevice(); m_batcher->EndFrame(); StateChange(); return GetD3D9()->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); } HRESULT STDMETHODCALLTYPE D3D8Device::GetBackBuffer( UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface8** ppBackBuffer) { D3D8DeviceLock lock = LockDevice(); InitReturnPtr(ppBackBuffer); if (unlikely(ppBackBuffer == nullptr)) return D3DERR_INVALIDCALL; if (iBackBuffer >= m_backBuffers.size() || m_backBuffers[iBackBuffer] == nullptr) { Com pSurface9; HRESULT res = GetD3D9()->GetBackBuffer(0, iBackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9); if (likely(SUCCEEDED(res))) { m_backBuffers[iBackBuffer] = new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pSurface9)); *ppBackBuffer = m_backBuffers[iBackBuffer].ref(); } return res; } *ppBackBuffer = m_backBuffers[iBackBuffer].ref(); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D8Device::GetRasterStatus(D3DRASTER_STATUS* pRasterStatus) { return GetD3D9()->GetRasterStatus(0, reinterpret_cast(pRasterStatus)); } void STDMETHODCALLTYPE D3D8Device::SetGammaRamp(DWORD Flags, const D3DGAMMARAMP* pRamp) { StateChange(); // For swap chain 0 GetD3D9()->SetGammaRamp(0, Flags, reinterpret_cast(pRamp)); } void STDMETHODCALLTYPE D3D8Device::GetGammaRamp(D3DGAMMARAMP* pRamp) { // For swap chain 0 GetD3D9()->GetGammaRamp(0, reinterpret_cast(pRamp)); } HRESULT STDMETHODCALLTYPE D3D8Device::CreateTexture( UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DTexture8** ppTexture) { // D3D8 returns D3DERR_INVALIDCALL for D3DFMT_UNKNOWN // before clearing the content of ppTexture. if (unlikely(Format == D3DFMT_UNKNOWN)) return D3DERR_INVALIDCALL; InitReturnPtr(ppTexture); if (unlikely(ppTexture == nullptr)) return D3DERR_INVALIDCALL; // Nvidia & Intel workaround for The Lord of the Rings: The Fellowship of the Ring if (m_d3d8Options.placeP8InScratch && Format == D3DFMT_P8) Pool = D3DPOOL_SCRATCH; Com pTex9 = nullptr; HRESULT res = GetD3D9()->CreateTexture( Width, Height, Levels, Usage, d3d9::D3DFORMAT(Format), d3d9::D3DPOOL(Pool), &pTex9, NULL); if (likely(SUCCEEDED(res))) *ppTexture = ref(new D3D8Texture2D(this, Pool, std::move(pTex9))); return res; } HRESULT STDMETHODCALLTYPE D3D8Device::CreateVolumeTexture( UINT Width, UINT Height, UINT Depth, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DVolumeTexture8** ppVolumeTexture) { // D3D8 returns D3DERR_INVALIDCALL for D3DFMT_UNKNOWN // before clearing the content of ppVolumeTexture. if (unlikely(Format == D3DFMT_UNKNOWN)) return D3DERR_INVALIDCALL; InitReturnPtr(ppVolumeTexture); if (unlikely(ppVolumeTexture == nullptr)) return D3DERR_INVALIDCALL; Com pVolume9 = nullptr; HRESULT res = GetD3D9()->CreateVolumeTexture( Width, Height, Depth, Levels, Usage, d3d9::D3DFORMAT(Format), d3d9::D3DPOOL(Pool), &pVolume9, NULL); if (likely(SUCCEEDED(res))) *ppVolumeTexture = ref(new D3D8Texture3D(this, Pool, std::move(pVolume9))); return res; } HRESULT STDMETHODCALLTYPE D3D8Device::CreateCubeTexture( UINT EdgeLength, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DCubeTexture8** ppCubeTexture) { // D3D8 returns D3DERR_INVALIDCALL for D3DFMT_UNKNOWN // before clearing the content of ppCubeTexture. if (unlikely(Format == D3DFMT_UNKNOWN)) return D3DERR_INVALIDCALL; InitReturnPtr(ppCubeTexture); if (unlikely(ppCubeTexture == nullptr)) return D3DERR_INVALIDCALL; Com pCube9 = nullptr; HRESULT res = GetD3D9()->CreateCubeTexture( EdgeLength, Levels, Usage, d3d9::D3DFORMAT(Format), d3d9::D3DPOOL(Pool), &pCube9, NULL); if (likely(SUCCEEDED(res))) *ppCubeTexture = ref(new D3D8TextureCube(this, Pool, std::move(pCube9))); return res; } HRESULT STDMETHODCALLTYPE D3D8Device::CreateVertexBuffer( UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer8** ppVertexBuffer) { InitReturnPtr(ppVertexBuffer); if (unlikely(ppVertexBuffer == nullptr)) return D3DERR_INVALIDCALL; if (ShouldBatch()) { *ppVertexBuffer = m_batcher->CreateVertexBuffer(Length, Usage, FVF, Pool); return D3D_OK; } Com pVertexBuffer9 = nullptr; HRESULT res = GetD3D9()->CreateVertexBuffer(Length, Usage, FVF, d3d9::D3DPOOL(Pool), &pVertexBuffer9, NULL); if (likely(SUCCEEDED(res))) *ppVertexBuffer = ref(new D3D8VertexBuffer(this, std::move(pVertexBuffer9), Pool, Usage)); return res; } HRESULT STDMETHODCALLTYPE D3D8Device::CreateIndexBuffer( UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer8** ppIndexBuffer) { InitReturnPtr(ppIndexBuffer); if (unlikely(ppIndexBuffer == nullptr)) return D3DERR_INVALIDCALL; Com pIndexBuffer9 = nullptr; HRESULT res = GetD3D9()->CreateIndexBuffer(Length, Usage, d3d9::D3DFORMAT(Format), d3d9::D3DPOOL(Pool), &pIndexBuffer9, NULL); if (likely(SUCCEEDED(res))) *ppIndexBuffer = ref(new D3D8IndexBuffer(this, std::move(pIndexBuffer9), Pool, Usage)); return res; } HRESULT STDMETHODCALLTYPE D3D8Device::CreateRenderTarget( UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, BOOL Lockable, IDirect3DSurface8** ppSurface) { // D3D8 returns D3DERR_INVALIDCALL for D3DFMT_UNKNOWN // before clearing the content of ppSurface. if (unlikely(Format == D3DFMT_UNKNOWN)) return D3DERR_INVALIDCALL; InitReturnPtr(ppSurface); if (unlikely(ppSurface == nullptr)) return D3DERR_INVALIDCALL; Com pSurf9 = nullptr; HRESULT res = GetD3D9()->CreateRenderTarget( Width, Height, d3d9::D3DFORMAT(Format), d3d9::D3DMULTISAMPLE_TYPE(MultiSample), 0, Lockable, &pSurf9, NULL); if (likely(SUCCEEDED(res))) *ppSurface = ref(new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pSurf9))); return res; } HRESULT STDMETHODCALLTYPE D3D8Device::CreateDepthStencilSurface( UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, IDirect3DSurface8** ppSurface) { // D3D8 returns D3DERR_INVALIDCALL for D3DFMT_UNKNOWN // before clearing the content of ppSurface. if (unlikely(Format == D3DFMT_UNKNOWN)) return D3DERR_INVALIDCALL; InitReturnPtr(ppSurface); if (unlikely(ppSurface == nullptr)) return D3DERR_INVALIDCALL; Com pSurf9 = nullptr; HRESULT res = GetD3D9()->CreateDepthStencilSurface( Width, Height, d3d9::D3DFORMAT(Format), d3d9::D3DMULTISAMPLE_TYPE(MultiSample), 0, FALSE, // z-buffer discarding is not used in D3D8 &pSurf9, NULL); if (likely(SUCCEEDED(res))) *ppSurface = ref(new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pSurf9))); return res; } HRESULT STDMETHODCALLTYPE D3D8Device::CreateImageSurface( UINT Width, UINT Height, D3DFORMAT Format, IDirect3DSurface8** ppSurface) { // Only D3D8 CreateImageSurface clears the content of ppSurface // before checking if Format is equal to D3DFMT_UNKNOWN. InitReturnPtr(ppSurface); if (unlikely(Format == D3DFMT_UNKNOWN)) return D3DERR_INVALIDCALL; if (unlikely(ppSurface == nullptr)) return D3DERR_INVALIDCALL; D3DPOOL pool = isUnsupportedSurfaceFormat(Format) ? D3DPOOL_SCRATCH : D3DPOOL_SYSTEMMEM; Com pSurf = nullptr; HRESULT res = GetD3D9()->CreateOffscreenPlainSurface( Width, Height, d3d9::D3DFORMAT(Format), d3d9::D3DPOOL(pool), &pSurf, NULL); if (likely(SUCCEEDED(res))) *ppSurface = ref(new D3D8Surface(this, pool, std::move(pSurf))); return res; } // Copies texture rect in system mem using memcpy. // Rects must be congruent, but need not be aligned. HRESULT copyTextureBuffers( D3D8Surface* src, D3D8Surface* dst, const d3d9::D3DSURFACE_DESC& srcDesc, const d3d9::D3DSURFACE_DESC& dstDesc, const RECT& srcRect, const RECT& dstRect) { HRESULT res = D3D_OK; D3DLOCKED_RECT srcLocked, dstLocked; bool compressed = isDXT(srcDesc.Format); res = src->LockRect(&srcLocked, &srcRect, D3DLOCK_READONLY); if (unlikely(FAILED(res))) return res; res = dst->LockRect(&dstLocked, &dstRect, 0); if (unlikely(FAILED(res))) { src->UnlockRect(); return res; } auto rows = srcRect.bottom - srcRect.top; auto cols = srcRect.right - srcRect.left; auto bpp = srcLocked.Pitch / srcDesc.Width; if (!compressed && srcRect.left == 0 && srcRect.right == LONG(srcDesc.Width) && srcDesc.Width == dstDesc.Width && srcLocked.Pitch == dstLocked.Pitch) { // If copying the entire texture into a congruent destination, // we can do this in one continuous copy. std::memcpy(dstLocked.pBits, srcLocked.pBits, srcLocked.Pitch * rows); } else { // Bytes per row of the rect auto amplitude = cols * bpp; // Handle DXT compressed textures. if (compressed) { // DXT blocks are always 4x4 pixels. constexpr UINT blockWidth = 4; constexpr UINT blockHeight = 4; // Compute rect dimensions in 4x4 blocks UINT rectWidthBlocks = cols / blockWidth; UINT rectHeightBlocks = rows / blockHeight; // Compute total texture width in blocks // to derive block size in bytes using the pitch. UINT texWidthBlocks = std::max(srcDesc.Width / blockWidth, 1u); UINT bytesPerBlock = srcLocked.Pitch / texWidthBlocks; // Copy H/4 rows of W/4 blocks amplitude = rectWidthBlocks * bytesPerBlock; rows = rectHeightBlocks; } // Copy one row at a time size_t srcOffset = 0, dstOffset = 0; for (auto i = 0; i < rows; i++) { std::memcpy( reinterpret_cast(dstLocked.pBits) + dstOffset, reinterpret_cast(srcLocked.pBits) + srcOffset, amplitude); srcOffset += srcLocked.Pitch; dstOffset += dstLocked.Pitch; } } res = dst->UnlockRect(); if (unlikely(FAILED(res))) { src->UnlockRect(); return res; } res = src->UnlockRect(); return res; } /** * \brief D3D8 CopyRects implementation * * \details * The following table shows the possible combinations of source * and destination surface pools, and how we handle each of them. * * ┌────────────┬───────────────────────────┬───────────────────────┬───────────────────────┬──────────────────────┐ * │ Src/Dst │ DEFAULT │ MANAGED │ SYSTEMMEM │ SCRATCH │ * ├────────────┼───────────────────────────┼───────────────────────┼───────────────────────┼──────────────────────┤ * │ DEFAULT │ StretchRect │ GetRenderTargetData │ GetRenderTargetData │ GetRenderTargetData │ * │ MANAGED │ UpdateTextureFromBuffer │ memcpy │ memcpy │ memcpy │ * │ SYSTEMMEM │ UpdateSurface │ memcpy │ memcpy │ memcpy │ * │ SCRATCH │ memcpy + UpdateSurface │ memcpy │ memcpy │ memcpy │ * └────────────┴───────────────────────────┴───────────────────────┴───────────────────────┴──────────────────────┘ */ HRESULT STDMETHODCALLTYPE D3D8Device::CopyRects( IDirect3DSurface8* pSourceSurface, const RECT* pSourceRectsArray, UINT cRects, IDirect3DSurface8* pDestinationSurface, const POINT* pDestPointsArray) { D3D8DeviceLock lock = LockDevice(); // The source and destination surfaces can not be identical. if (unlikely(pSourceSurface == nullptr || pDestinationSurface == nullptr || pSourceSurface == pDestinationSurface)) { return D3DERR_INVALIDCALL; } // TODO: No stretching or clipping of either source or destination rectangles. // All src/dest rectangles must fit within the dest surface. Com src = static_cast(pSourceSurface); Com dst = static_cast(pDestinationSurface); d3d9::D3DSURFACE_DESC srcDesc, dstDesc; src->GetD3D9()->GetDesc(&srcDesc); dst->GetD3D9()->GetDesc(&dstDesc); // This method does not support format conversion. if (unlikely(srcDesc.Format != dstDesc.Format)) return D3DERR_INVALIDCALL; // This method cannot be applied to surfaces whose formats // are classified as depth stencil formats. if (unlikely(isDepthStencilFormat(D3DFORMAT(srcDesc.Format)))) return D3DERR_INVALIDCALL; StateChange(); // If pSourceRectsArray is NULL, then the entire surface is copied RECT rect; POINT point = { 0, 0 }; if (pSourceRectsArray == NULL) { cRects = 1; rect.top = rect.left = 0; rect.right = srcDesc.Width; rect.bottom = srcDesc.Height; pSourceRectsArray = ▭ pDestPointsArray = &point; } for (UINT i = 0; i < cRects; i++) { RECT srcRect, dstRect; srcRect = pSourceRectsArray[i]; // True if the copy is asymmetric bool asymmetric = true; // True if the copy requires stretching (not technically supported) bool stretch = true; // True if the copy is not perfectly aligned (supported) bool offset = true; if (pDestPointsArray != NULL) { dstRect.left = pDestPointsArray[i].x; dstRect.right = dstRect.left + (srcRect.right - srcRect.left); dstRect.top = pDestPointsArray[i].y; dstRect.bottom = dstRect.top + (srcRect.bottom - srcRect.top); asymmetric = dstRect.left != srcRect.left || dstRect.top != srcRect.top || dstRect.right != srcRect.right || dstRect.bottom != srcRect.bottom; stretch = (dstRect.right-dstRect.left) != (srcRect.right-srcRect.left) || (dstRect.bottom-dstRect.top) != (srcRect.bottom-srcRect.top); offset = !stretch && asymmetric; } else { dstRect = srcRect; asymmetric = stretch = offset = false; } POINT dstPt = { dstRect.left, dstRect.top }; auto unsupported = [&] { Logger::err(str::format("D3D8Device::CopyRects: Unsupported case from src pool ", srcDesc.Pool, " to dst pool ", dstDesc.Pool)); return D3DERR_INVALIDCALL; }; auto logError = [&] (HRESULT res) { if (FAILED(res)) { // Only a debug message because some games mess up CopyRects every frame in a way // that fails on native too but are perfectly fine with it. Logger::debug(str::format("D3D8Device::CopyRects: Failed to copy from src pool ", srcDesc.Pool, " to dst pool ", dstDesc.Pool)); } return res; }; switch (dstDesc.Pool) { // Dest: DEFAULT case d3d9::D3DPOOL_DEFAULT: switch (srcDesc.Pool) { case d3d9::D3DPOOL_DEFAULT: { // DEFAULT -> DEFAULT: use StretchRect return logError(GetD3D9()->StretchRect( src->GetD3D9(), &srcRect, dst->GetD3D9(), &dstRect, d3d9::D3DTEXF_NONE )); } case d3d9::D3DPOOL_MANAGED: { // MANAGED -> DEFAULT: UpdateTextureFromBuffer return logError(m_bridge->UpdateTextureFromBuffer( src->GetD3D9(), dst->GetD3D9(), &srcRect, &dstPt )); } case d3d9::D3DPOOL_SYSTEMMEM: { // SYSTEMMEM -> DEFAULT: use UpdateSurface return logError(GetD3D9()->UpdateSurface( src->GetD3D9(), &srcRect, dst->GetD3D9(), &dstPt )); } case d3d9::D3DPOOL_SCRATCH: { // SCRATCH -> DEFAULT: memcpy to a SYSTEMMEM temporary buffer and use UpdateSurface // UpdateSurface will not work on surface formats unsupported by D3DPOOL_DEFAULT if (unlikely(isUnsupportedSurfaceFormat(D3DFORMAT(srcDesc.Format)))) { return logError(D3DERR_INVALIDCALL); } Com pTempImageSurface; // The temporary image surface is guaranteed to end up in SYSTEMMEM for supported formats HRESULT res = CreateImageSurface( srcDesc.Width, srcDesc.Height, D3DFORMAT(srcDesc.Format), &pTempImageSurface ); if (FAILED(res)) { return logError(res); } Com pBlitImage = static_cast(pTempImageSurface.ptr()); // Temporary image surface dimensions are identical, so we can reuse srcDesc/Rect res = copyTextureBuffers(src.ptr(), pBlitImage.ptr(), srcDesc, srcDesc, srcRect, srcRect); if (FAILED(res)) { return logError(res); } return logError(GetD3D9()->UpdateSurface( pBlitImage->GetD3D9(), &srcRect, dst->GetD3D9(), &dstPt )); } default: { return unsupported(); } } break; // Dest: MANAGED case d3d9::D3DPOOL_MANAGED: switch (srcDesc.Pool) { // TODO: Copy on GPU (handle MANAGED similarly to SYSTEMMEM for now) case d3d9::D3DPOOL_DEFAULT: { // Get temporary off-screen surface for stretching. Com pBlitImage = dst->GetBlitImage(); // Stretch the source RT to the temporary surface. HRESULT res = GetD3D9()->StretchRect( src->GetD3D9(), &srcRect, pBlitImage.ptr(), &dstRect, d3d9::D3DTEXF_NONE); if (FAILED(res)) { return logError(res); } // Now sync the rendertarget data into main memory. return logError(GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9())); } case d3d9::D3DPOOL_MANAGED: case d3d9::D3DPOOL_SYSTEMMEM: case d3d9::D3DPOOL_SCRATCH: { // MANAGED/SYSMEM/SCRATCH -> MANAGED: LockRect / memcpy if (stretch) { return logError(D3DERR_INVALIDCALL); } return logError(copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect)); } default: { return unsupported(); } } break; // DEST: SYSTEMMEM case d3d9::D3DPOOL_SYSTEMMEM: { // RT (DEFAULT) -> SYSTEMMEM: Use GetRenderTargetData as fast path if possible if ((srcDesc.Usage & D3DUSAGE_RENDERTARGET || m_renderTarget == src.ptr())) { // GetRenderTargetData works if the formats and sizes match if (srcDesc.MultiSampleType == d3d9::D3DMULTISAMPLE_NONE && srcDesc.Width == dstDesc.Width && srcDesc.Height == dstDesc.Height && srcDesc.Format == dstDesc.Format && !asymmetric) { return logError(GetD3D9()->GetRenderTargetData(src->GetD3D9(), dst->GetD3D9())); } } switch (srcDesc.Pool) { case d3d9::D3DPOOL_DEFAULT: { // Get temporary off-screen surface for stretching. Com pBlitImage = dst->GetBlitImage(); // Stretch the source RT to the temporary surface. HRESULT res = GetD3D9()->StretchRect( src->GetD3D9(), &srcRect, pBlitImage.ptr(), &dstRect, d3d9::D3DTEXF_NONE); if (FAILED(res)) { return logError(res); } // Now sync the rendertarget data into main memory. return logError(GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9())); } // MANAGED/SYSMEM/SCRATCH -> SYSMEM: LockRect / memcpy case d3d9::D3DPOOL_MANAGED: case d3d9::D3DPOOL_SYSTEMMEM: case d3d9::D3DPOOL_SCRATCH: { if (stretch) { return logError(D3DERR_INVALIDCALL); } return logError(copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect)); } default: { return unsupported(); } } break; } // DEST: SCRATCH case d3d9::D3DPOOL_SCRATCH: { // RT (DEFAULT) -> SCRATCH: Use GetRenderTargetData as fast path if possible if ((srcDesc.Usage & D3DUSAGE_RENDERTARGET || m_renderTarget == src.ptr())) { // GetRenderTargetData works if the formats and sizes match if (srcDesc.MultiSampleType == d3d9::D3DMULTISAMPLE_NONE && srcDesc.Width == dstDesc.Width && srcDesc.Height == dstDesc.Height && srcDesc.Format == dstDesc.Format && !asymmetric) { return logError(GetD3D9()->GetRenderTargetData(src->GetD3D9(), dst->GetD3D9())); } } switch (srcDesc.Pool) { case d3d9::D3DPOOL_DEFAULT: { // Get temporary off-screen surface for stretching. Com pBlitImage = dst->GetBlitImage(); // Stretch the source RT to the temporary surface. HRESULT res = GetD3D9()->StretchRect( src->GetD3D9(), &srcRect, pBlitImage.ptr(), &dstRect, d3d9::D3DTEXF_NONE); if (FAILED(res)) { return logError(res); } // Now sync the rendertarget data into main memory. return logError(GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9())); } // MANAGED/SYSMEM/SCRATCH -> SCRATCH: LockRect / memcpy case d3d9::D3DPOOL_MANAGED: case d3d9::D3DPOOL_SYSTEMMEM: case d3d9::D3DPOOL_SCRATCH: { if (stretch) { return logError(D3DERR_INVALIDCALL); } return logError(copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect)); } default: { return unsupported(); } } break; } default: { return unsupported(); } } } return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D8Device::UpdateTexture( IDirect3DBaseTexture8* pSourceTexture, IDirect3DBaseTexture8* pDestinationTexture) { if (unlikely(pSourceTexture == nullptr || pDestinationTexture == nullptr)) return D3DERR_INVALIDCALL; D3D8Texture2D* src = static_cast(pSourceTexture); D3D8Texture2D* dst = static_cast(pDestinationTexture); StateChange(); return GetD3D9()->UpdateTexture(D3D8Texture2D::GetD3D9Nullable(src), D3D8Texture2D::GetD3D9Nullable(dst)); } HRESULT STDMETHODCALLTYPE D3D8Device::GetFrontBuffer(IDirect3DSurface8* pDestSurface) { if (unlikely(pDestSurface == nullptr)) return D3DERR_INVALIDCALL; Com surf = static_cast(pDestSurface); StateChange(); // This actually gets a copy of the front buffer and writes it to pDestSurface return GetD3D9()->GetFrontBufferData(0, D3D8Surface::GetD3D9Nullable(surf)); } HRESULT STDMETHODCALLTYPE D3D8Device::SetRenderTarget(IDirect3DSurface8* pRenderTarget, IDirect3DSurface8* pNewZStencil) { D3D8DeviceLock lock = LockDevice(); HRESULT res; if (pRenderTarget != nullptr) { D3D8Surface* surf = static_cast(pRenderTarget); // This will always be a state change and needs to be forwarded to // D3D9, even when the same render target is set, as the viewport // needs to be readjusted and reset. StateChange(); res = GetD3D9()->SetRenderTarget(0, D3D8Surface::GetD3D9Nullable(surf)); if (unlikely(FAILED(res))) return res; m_renderTarget = surf; } // SetDepthStencilSurface is a separate call D3D8Surface* zStencil = static_cast(pNewZStencil); // Depth stencil dimensions can not be lower than // those of the currently set render target. if (m_renderTarget != nullptr && zStencil != nullptr) { D3DSURFACE_DESC rtDesc; res = m_renderTarget->GetDesc(&rtDesc); if (unlikely(FAILED(res))) return res; D3DSURFACE_DESC dsDesc; res = zStencil->GetDesc(&dsDesc); if (unlikely(FAILED(res))) return res; if (unlikely(dsDesc.Width < rtDesc.Width || dsDesc.Height < rtDesc.Height)) return D3DERR_INVALIDCALL; } StateChange(); res = GetD3D9()->SetDepthStencilSurface(D3D8Surface::GetD3D9Nullable(zStencil)); if (unlikely(FAILED(res))) return res; m_depthStencil = zStencil; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D8Device::GetRenderTarget(IDirect3DSurface8** ppRenderTarget) { D3D8DeviceLock lock = LockDevice(); InitReturnPtr(ppRenderTarget); if (unlikely(ppRenderTarget == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(m_renderTarget == nullptr)) { Com pRT9 = nullptr; HRESULT res = GetD3D9()->GetRenderTarget(0, &pRT9); // use RT index 0 if (likely(SUCCEEDED(res))) { m_renderTarget = new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pRT9)); *ppRenderTarget = m_renderTarget.ref(); } return res; } *ppRenderTarget = m_renderTarget.ref(); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D8Device::GetDepthStencilSurface(IDirect3DSurface8** ppZStencilSurface) { D3D8DeviceLock lock = LockDevice(); InitReturnPtr(ppZStencilSurface); if (unlikely(ppZStencilSurface == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(m_depthStencil == nullptr)) { Com pStencil9 = nullptr; HRESULT res = GetD3D9()->GetDepthStencilSurface(&pStencil9); if (likely(SUCCEEDED(res))) { m_depthStencil = new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pStencil9)); *ppZStencilSurface = m_depthStencil.ref(); } return res; } *ppZStencilSurface = m_depthStencil.ref(); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D8Device::BeginScene() { return GetD3D9()->BeginScene(); } HRESULT STDMETHODCALLTYPE D3D8Device::EndScene() { StateChange(); return GetD3D9()->EndScene(); } HRESULT STDMETHODCALLTYPE D3D8Device::Clear( DWORD Count, const D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil) { StateChange(); return GetD3D9()->Clear(Count, pRects, Flags, Color, Z, Stencil); } HRESULT STDMETHODCALLTYPE D3D8Device::SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix) { StateChange(); return GetD3D9()->SetTransform(d3d9::D3DTRANSFORMSTATETYPE(State), pMatrix); } HRESULT STDMETHODCALLTYPE D3D8Device::GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix) { return GetD3D9()->GetTransform(d3d9::D3DTRANSFORMSTATETYPE(State), pMatrix); } HRESULT STDMETHODCALLTYPE D3D8Device::MultiplyTransform(D3DTRANSFORMSTATETYPE TransformState, const D3DMATRIX* pMatrix) { StateChange(); return GetD3D9()->MultiplyTransform(d3d9::D3DTRANSFORMSTATETYPE(TransformState), pMatrix); } HRESULT STDMETHODCALLTYPE D3D8Device::SetViewport(const D3DVIEWPORT8* pViewport) { D3D8DeviceLock lock = LockDevice(); if (likely(pViewport != nullptr)) { // We need a valid render target to validate the viewport if (unlikely(m_renderTarget == nullptr)) return D3DERR_INVALIDCALL; D3DSURFACE_DESC rtDesc; HRESULT res = m_renderTarget->GetDesc(&rtDesc); // D3D8 will fail when setting a viewport that's outside of the // current render target, although this apparently works in D3D9 if (likely(SUCCEEDED(res)) && unlikely(pViewport->X + pViewport->Width > rtDesc.Width || pViewport->Y + pViewport->Height > rtDesc.Height)) { // On Linux/Wine and in windowed mode, we can get in situations // where the actual render target dimensions are off by one // pixel to what the game sets them to. Allow this corner case // to skip the validation, in order to prevent issues. bool isOnePixelWider = pViewport->X + pViewport->Width == rtDesc.Width + 1; bool isOnePixelTaller = pViewport->Y + pViewport->Height == rtDesc.Height + 1; if (m_presentParams.Windowed && (isOnePixelWider || isOnePixelTaller)) { Logger::debug("D3D8Device::SetViewport: Viewport exceeds render target dimensions by one pixel"); } else { return D3DERR_INVALIDCALL; } } } StateChange(); return GetD3D9()->SetViewport(reinterpret_cast(pViewport)); } HRESULT STDMETHODCALLTYPE D3D8Device::GetViewport(D3DVIEWPORT8* pViewport) { D3D8DeviceLock lock = LockDevice(); return GetD3D9()->GetViewport(reinterpret_cast(pViewport)); } HRESULT STDMETHODCALLTYPE D3D8Device::SetMaterial(const D3DMATERIAL8* pMaterial) { StateChange(); return GetD3D9()->SetMaterial((const d3d9::D3DMATERIAL9*)pMaterial); } HRESULT STDMETHODCALLTYPE D3D8Device::GetMaterial(D3DMATERIAL8* pMaterial) { return GetD3D9()->GetMaterial((d3d9::D3DMATERIAL9*)pMaterial); } HRESULT STDMETHODCALLTYPE D3D8Device::SetLight(DWORD Index, const D3DLIGHT8* pLight) { StateChange(); return GetD3D9()->SetLight(Index, (const d3d9::D3DLIGHT9*)pLight); } HRESULT STDMETHODCALLTYPE D3D8Device::GetLight(DWORD Index, D3DLIGHT8* pLight) { return GetD3D9()->GetLight(Index, (d3d9::D3DLIGHT9*)pLight); } HRESULT STDMETHODCALLTYPE D3D8Device::LightEnable(DWORD Index, BOOL Enable) { StateChange(); return GetD3D9()->LightEnable(Index, Enable); } HRESULT STDMETHODCALLTYPE D3D8Device::GetLightEnable(DWORD Index, BOOL* pEnable) { return GetD3D9()->GetLightEnable(Index, pEnable); } HRESULT STDMETHODCALLTYPE D3D8Device::SetClipPlane(DWORD Index, const float* pPlane) { StateChange(); return GetD3D9()->SetClipPlane(Index, pPlane); } HRESULT STDMETHODCALLTYPE D3D8Device::GetClipPlane(DWORD Index, float* pPlane) { return GetD3D9()->GetClipPlane(Index, pPlane); } HRESULT STDMETHODCALLTYPE D3D8Device::CreateStateBlock( D3DSTATEBLOCKTYPE Type, DWORD* pToken) { D3D8DeviceLock lock = LockDevice(); if (unlikely(pToken == nullptr)) return D3DERR_INVALIDCALL; // Applications cannot create a state block while another is being recorded if (unlikely(ShouldRecord())) return D3DERR_INVALIDCALL; Com pStateBlock9; HRESULT res = GetD3D9()->CreateStateBlock(d3d9::D3DSTATEBLOCKTYPE(Type), &pStateBlock9); if (likely(SUCCEEDED(res))) { m_token++; auto stateBlockIterPair = m_stateBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(m_token), std::forward_as_tuple(this, Type, pStateBlock9.ref())); *pToken = m_token; // D3D8 state blocks automatically capture state on creation. stateBlockIterPair.first->second.Capture(); } return res; } HRESULT STDMETHODCALLTYPE D3D8Device::CaptureStateBlock(DWORD Token) { D3D8DeviceLock lock = LockDevice(); // Applications cannot capture a state block while another is being recorded if (unlikely(ShouldRecord())) return D3DERR_INVALIDCALL; auto stateBlockIter = m_stateBlocks.find(Token); if (unlikely(stateBlockIter == m_stateBlocks.end())) { Logger::warn(str::format("D3D8Device::CaptureStateBlock: Invalid token: ", std::hex, Token)); return D3D_OK; } return stateBlockIter->second.Capture(); } HRESULT STDMETHODCALLTYPE D3D8Device::ApplyStateBlock(DWORD Token) { D3D8DeviceLock lock = LockDevice(); // Applications cannot apply a state block while another is being recorded if (unlikely(ShouldRecord())) return D3DERR_INVALIDCALL; StateChange(); auto stateBlockIter = m_stateBlocks.find(Token); if (unlikely(stateBlockIter == m_stateBlocks.end())) { Logger::warn(str::format("D3D8Device::ApplyStateBlock: Invalid token: ", std::hex, Token)); return D3D_OK; } return stateBlockIter->second.Apply(); } HRESULT STDMETHODCALLTYPE D3D8Device::DeleteStateBlock(DWORD Token) { D3D8DeviceLock lock = LockDevice(); // Applications cannot delete a state block while another is being recorded if (unlikely(ShouldRecord())) return D3DERR_INVALIDCALL; auto stateBlockIter = m_stateBlocks.find(Token); if (unlikely(stateBlockIter == m_stateBlocks.end())) { Logger::warn(str::format("D3D8Device::DeleteStateBlock: Invalid token: ", std::hex, Token)); return D3D_OK; } m_stateBlocks.erase(stateBlockIter); // native apparently does drop the token counter in // situations where the token being removed is the // last allocated token, which allows some reuse if (m_token == Token) m_token--; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D8Device::BeginStateBlock() { D3D8DeviceLock lock = LockDevice(); if (unlikely(m_recorder != nullptr)) return D3DERR_INVALIDCALL; HRESULT res = GetD3D9()->BeginStateBlock(); if (likely(SUCCEEDED(res))) { m_token++; auto stateBlockIterPair = m_stateBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(m_token), std::forward_as_tuple(this)); m_recorder = &stateBlockIterPair.first->second; m_recorderToken = m_token; } return res; } HRESULT STDMETHODCALLTYPE D3D8Device::EndStateBlock(DWORD* pToken) { D3D8DeviceLock lock = LockDevice(); if (unlikely(pToken == nullptr || m_recorder == nullptr)) return D3DERR_INVALIDCALL; Com pStateBlock; HRESULT res = GetD3D9()->EndStateBlock(&pStateBlock); if (likely(SUCCEEDED(res))) { m_recorder->SetD3D9(std::move(pStateBlock)); *pToken = m_recorderToken; m_recorder = nullptr; m_recorderToken = 0; } return res; } HRESULT STDMETHODCALLTYPE D3D8Device::SetClipStatus(const D3DCLIPSTATUS8* pClipStatus) { StateChange(); return GetD3D9()->SetClipStatus(reinterpret_cast(pClipStatus)); } HRESULT STDMETHODCALLTYPE D3D8Device::GetClipStatus(D3DCLIPSTATUS8* pClipStatus) { return GetD3D9()->GetClipStatus(reinterpret_cast(pClipStatus)); } HRESULT STDMETHODCALLTYPE D3D8Device::GetTexture(DWORD Stage, IDirect3DBaseTexture8** ppTexture) { D3D8DeviceLock lock = LockDevice(); InitReturnPtr(ppTexture); if (unlikely(ppTexture == nullptr)) return D3DERR_INVALIDCALL; *ppTexture = m_textures[Stage].ref(); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D8Device::SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) { D3D8DeviceLock lock = LockDevice(); if (unlikely(Stage >= d8caps::MAX_TEXTURE_STAGES)) return D3DERR_INVALIDCALL; if (unlikely(ShouldRecord())) return m_recorder->SetTexture(Stage, pTexture); D3D8Texture2D* tex = static_cast(pTexture); // Splinter Cell: Force perspective divide when a shadow map is bound to slot 0 if (unlikely(m_d3d8Options.shadowPerspectiveDivide && Stage == 0)) { if (tex) { D3DSURFACE_DESC surf; tex->GetLevelDesc(0, &surf); if (isDepthStencilFormat(surf.Format)) { // If we bound a depth texture to stage 0 then we need to set the projected flag for stage 0 and 1 // Stage 1 is a non-depth light cookie texture but still requires perspective divide to work GetD3D9()->SetTextureStageState(0, d3d9::D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_PROJECTED); GetD3D9()->SetTextureStageState(1, d3d9::D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_PROJECTED); m_shadowPerspectiveDivide = true; } else if (m_shadowPerspectiveDivide) { // Non-depth texture bound. Game will reset the transform flags to 0 on its own m_shadowPerspectiveDivide = false; } } else if (m_shadowPerspectiveDivide) { // Texture unbound. Game will reset the transform flags to 0 on its own m_shadowPerspectiveDivide = false; } } if (unlikely(m_textures[Stage] == tex)) return D3D_OK; StateChange(); HRESULT res = GetD3D9()->SetTexture(Stage, D3D8Texture2D::GetD3D9Nullable(tex)); if (likely(SUCCEEDED(res))) m_textures[Stage] = tex; return res; } HRESULT STDMETHODCALLTYPE D3D8Device::GetTextureStageState( DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD* pValue) { d3d9::D3DSAMPLERSTATETYPE stateType = GetSamplerStateType9(Type); if (stateType != -1u) { // if the type has been remapped to a sampler state type: return GetD3D9()->GetSamplerState(Stage, stateType, pValue); } else { return GetD3D9()->GetTextureStageState(Stage, d3d9::D3DTEXTURESTAGESTATETYPE(Type), pValue); } } HRESULT STDMETHODCALLTYPE D3D8Device::SetTextureStageState( DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value) { d3d9::D3DSAMPLERSTATETYPE stateType = GetSamplerStateType9(Type); if (unlikely(m_d3d8Options.shadowPerspectiveDivide && Type == D3DTSS_TEXTURETRANSFORMFLAGS)) { // Splinter Cell: Ignore requests to change texture transform flags // to 0 while shadow mapping perspective divide mode is enabled if (m_shadowPerspectiveDivide && (Stage == 0 || Stage == 1)) return D3D_OK; } StateChange(); if (stateType != -1u) { // if the type has been remapped to a sampler state type: return GetD3D9()->SetSamplerState(Stage, stateType, Value); } else { return GetD3D9()->SetTextureStageState(Stage, d3d9::D3DTEXTURESTAGESTATETYPE(Type), Value); } } HRESULT STDMETHODCALLTYPE D3D8Device::ValidateDevice(DWORD* pNumPasses) { return GetD3D9()->ValidateDevice(pNumPasses); } HRESULT STDMETHODCALLTYPE D3D8Device::SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries) { StateChange(); return GetD3D9()->SetPaletteEntries(PaletteNumber, pEntries); } HRESULT STDMETHODCALLTYPE D3D8Device::GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries) { return GetD3D9()->GetPaletteEntries(PaletteNumber, pEntries); } HRESULT STDMETHODCALLTYPE D3D8Device::SetCurrentTexturePalette(UINT PaletteNumber) { StateChange(); return GetD3D9()->SetCurrentTexturePalette(PaletteNumber); } HRESULT STDMETHODCALLTYPE D3D8Device::GetCurrentTexturePalette(UINT* PaletteNumber) { return GetD3D9()->GetCurrentTexturePalette(PaletteNumber); } HRESULT STDMETHODCALLTYPE D3D8Device::DrawPrimitive( D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount) { D3D8DeviceLock lock = LockDevice(); if (ShouldBatch()) return m_batcher->DrawPrimitive(PrimitiveType, StartVertex, PrimitiveCount); return GetD3D9()->DrawPrimitive(d3d9::D3DPRIMITIVETYPE(PrimitiveType), StartVertex, PrimitiveCount); } HRESULT STDMETHODCALLTYPE D3D8Device::DrawIndexedPrimitive( D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT StartIndex, UINT PrimitiveCount) { D3D8DeviceLock lock = LockDevice(); return GetD3D9()->DrawIndexedPrimitive( d3d9::D3DPRIMITIVETYPE(PrimitiveType), static_cast(std::min(m_baseVertexIndex, static_cast(std::numeric_limits::max()))), // set by SetIndices MinVertexIndex, NumVertices, StartIndex, PrimitiveCount); } HRESULT STDMETHODCALLTYPE D3D8Device::DrawPrimitiveUP( D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride) { D3D8DeviceLock lock = LockDevice(); StateChange(); // Stream 0 is set to null by this call m_streams[0] = D3D8VBO {nullptr, 0}; return GetD3D9()->DrawPrimitiveUP(d3d9::D3DPRIMITIVETYPE(PrimitiveType), PrimitiveCount, pVertexStreamZeroData, VertexStreamZeroStride); } HRESULT STDMETHODCALLTYPE D3D8Device::DrawIndexedPrimitiveUP( D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT PrimitiveCount, const void* pIndexData, D3DFORMAT IndexDataFormat, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride) { D3D8DeviceLock lock = LockDevice(); StateChange(); // Stream 0 and the index buffer are set to null by this call m_streams[0] = D3D8VBO {nullptr, 0}; m_indices = nullptr; m_baseVertexIndex = 0; return GetD3D9()->DrawIndexedPrimitiveUP( d3d9::D3DPRIMITIVETYPE(PrimitiveType), MinVertexIndex, NumVertices, PrimitiveCount, pIndexData, d3d9::D3DFORMAT(IndexDataFormat), pVertexStreamZeroData, VertexStreamZeroStride); } HRESULT STDMETHODCALLTYPE D3D8Device::ProcessVertices( UINT SrcStartIndex, UINT DestIndex, UINT VertexCount, IDirect3DVertexBuffer8* pDestBuffer, DWORD Flags) { D3D8VertexBuffer* buffer = static_cast(pDestBuffer); return GetD3D9()->ProcessVertices( SrcStartIndex, DestIndex, VertexCount, D3D8VertexBuffer::GetD3D9Nullable(buffer), nullptr, Flags ); } HRESULT STDMETHODCALLTYPE D3D8Device::SetVertexShaderConstant( DWORD StartRegister, const void* pConstantData, DWORD ConstantCount) { StateChange(); // ConstantCount is actually the same as Vector4fCount return GetD3D9()->SetVertexShaderConstantF(StartRegister, reinterpret_cast(pConstantData), ConstantCount); } HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount) { return GetD3D9()->GetVertexShaderConstantF(Register, reinterpret_cast(pConstantData), ConstantCount); } HRESULT STDMETHODCALLTYPE D3D8Device::SetStreamSource( UINT StreamNumber, IDirect3DVertexBuffer8* pStreamData, UINT Stride) { D3D8DeviceLock lock = LockDevice(); if (unlikely(StreamNumber >= d8caps::MAX_STREAMS)) return D3DERR_INVALIDCALL; if (unlikely(ShouldRecord())) return m_recorder->SetStreamSource(StreamNumber, pStreamData, Stride); D3D8VertexBuffer* buffer = static_cast(pStreamData); HRESULT res = GetD3D9()->SetStreamSource(StreamNumber, D3D8VertexBuffer::GetD3D9Nullable(buffer), 0, Stride); if (likely(SUCCEEDED(res))) { if (ShouldBatch()) m_batcher->SetStream(StreamNumber, buffer, Stride); m_streams[StreamNumber].buffer = buffer; // The previous stride is preserved if pStreamData is NULL if (likely(buffer != nullptr)) m_streams[StreamNumber].stride = Stride; } return res; } HRESULT STDMETHODCALLTYPE D3D8Device::GetStreamSource( UINT StreamNumber, IDirect3DVertexBuffer8** ppStreamData, UINT* pStride) { D3D8DeviceLock lock = LockDevice(); InitReturnPtr(ppStreamData); if (likely(pStride != nullptr)) *pStride = 0; if (unlikely(ppStreamData == nullptr || pStride == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(StreamNumber >= d8caps::MAX_STREAMS)) return D3DERR_INVALIDCALL; const D3D8VBO& vbo = m_streams[StreamNumber]; *ppStreamData = vbo.buffer.ref(); *pStride = vbo.stride; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D8Device::SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex) { D3D8DeviceLock lock = LockDevice(); if (unlikely(ShouldRecord())) return m_recorder->SetIndices(pIndexData, BaseVertexIndex); if (unlikely(BaseVertexIndex > std::numeric_limits::max())) Logger::warn("D3D8Device::SetIndices: BaseVertexIndex exceeds INT_MAX"); // used by DrawIndexedPrimitive m_baseVertexIndex = BaseVertexIndex; D3D8IndexBuffer* buffer = static_cast(pIndexData); HRESULT res = GetD3D9()->SetIndices(D3D8IndexBuffer::GetD3D9Nullable(buffer)); if (likely(SUCCEEDED(res))) { if (ShouldBatch()) m_batcher->SetIndices(buffer, m_baseVertexIndex); m_indices = buffer; } return res; } HRESULT STDMETHODCALLTYPE D3D8Device::GetIndices( IDirect3DIndexBuffer8** ppIndexData, UINT* pBaseVertexIndex) { D3D8DeviceLock lock = LockDevice(); InitReturnPtr(ppIndexData); if (unlikely(ppIndexData == nullptr || pBaseVertexIndex == nullptr)) return D3DERR_INVALIDCALL; *ppIndexData = m_indices.ref(); *pBaseVertexIndex = m_baseVertexIndex; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount) { return GetD3D9()->GetPixelShaderConstantF(Register, reinterpret_cast(pConstantData), ConstantCount); } HRESULT STDMETHODCALLTYPE D3D8Device::SetPixelShaderConstant( DWORD StartRegister, const void* pConstantData, DWORD ConstantCount) { StateChange(); // ConstantCount is actually the same as Vector4fCount return GetD3D9()->SetPixelShaderConstantF(StartRegister, reinterpret_cast(pConstantData), ConstantCount); } HRESULT STDMETHODCALLTYPE D3D8Device::DrawRectPatch( UINT Handle, const float* pNumSegs, const D3DRECTPATCH_INFO* pRectPatchInfo) { return GetD3D9()->DrawRectPatch(Handle, pNumSegs, reinterpret_cast(pRectPatchInfo)); } HRESULT STDMETHODCALLTYPE D3D8Device::DrawTriPatch( UINT Handle, const float* pNumSegs, const D3DTRIPATCH_INFO* pTriPatchInfo) { return GetD3D9()->DrawTriPatch(Handle, pNumSegs, reinterpret_cast(pTriPatchInfo)); } HRESULT STDMETHODCALLTYPE D3D8Device::DeletePatch(UINT Handle) { return GetD3D9()->DeletePatch(Handle); } // Render States // // ZBIAS can be an integer from 0 to 16 and needs to be remapped to float static constexpr float ZBIAS_SCALE = -0.000005f; static constexpr float ZBIAS_SCALE_INV = 1 / ZBIAS_SCALE; HRESULT STDMETHODCALLTYPE D3D8Device::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) { D3D8DeviceLock lock = LockDevice(); d3d9::D3DRENDERSTATETYPE State9 = (d3d9::D3DRENDERSTATETYPE)State; bool stateChange = true; switch (State) { // Most render states translate 1:1 to D3D9 default: break; // TODO: Implement D3DRS_LINEPATTERN - vkCmdSetLineRasterizationModeEXT // and advertise support with D3DPRASTERCAPS_PAT once that is done case D3DRS_LINEPATTERN: Logger::warn("D3D8Device::SetRenderState: Unimplemented render state D3DRS_LINEPATTERN"); m_linePattern = bit::cast(Value); return D3D_OK; // Not supported by D3D8, but its value is stored. case D3DRS_ZVISIBLE: m_zVisible = Value; return D3D_OK; // TODO: Implement D3DRS_ANTIALIASEDLINEENABLE in D9VK. case D3DRS_EDGEANTIALIAS: State9 = d3d9::D3DRS_ANTIALIASEDLINEENABLE; break; case D3DRS_ZBIAS: State9 = d3d9::D3DRS_DEPTHBIAS; Value = bit::cast(static_cast(Value) * ZBIAS_SCALE); break; case D3DRS_SOFTWAREVERTEXPROCESSING: // D3D9 can return D3DERR_INVALIDCALL, but we don't care. if (!(m_behaviorFlags & D3DCREATE_MIXED_VERTEXPROCESSING)) return D3D_OK; // This was a very easy footgun for D3D8 applications. if (unlikely(ShouldRecord())) return m_recorder->SetSoftwareVertexProcessing(Value); return GetD3D9()->SetSoftwareVertexProcessing(Value); // TODO: Implement D3DRS_PATCHSEGMENTS case D3DRS_PATCHSEGMENTS: Logger::warn("D3D8Device::SetRenderState: Unimplemented render state D3DRS_PATCHSEGMENTS"); m_patchSegments = bit::cast(Value); return D3D_OK; } if (stateChange) { DWORD value; // Value at this point is converted for use with D3D9, // so we need to compare it against D3D9 directly HRESULT res = GetD3D9()->GetRenderState(State9, &value); if (likely(SUCCEEDED(res)) && value != Value) StateChange(); } // This call will never fail return GetD3D9()->SetRenderState(State9, Value); } HRESULT STDMETHODCALLTYPE D3D8Device::GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue) { D3D8DeviceLock lock = LockDevice(); if (unlikely(pValue == nullptr)) return D3DERR_INVALIDCALL; d3d9::D3DRENDERSTATETYPE State9 = (d3d9::D3DRENDERSTATETYPE)State; switch (State) { // Most render states translate 1:1 to D3D9 default: break; case D3DRS_LINEPATTERN: *pValue = bit::cast(m_linePattern); return D3D_OK; // Not supported by D3D8, but its value is stored. case D3DRS_ZVISIBLE: *pValue = m_zVisible; return D3D_OK; case D3DRS_EDGEANTIALIAS: State9 = d3d9::D3DRS_ANTIALIASEDLINEENABLE; break; case D3DRS_ZBIAS: { DWORD bias = 0; HRESULT res = GetD3D9()->GetRenderState(d3d9::D3DRS_DEPTHBIAS, &bias); *pValue = static_cast(bit::cast(bias) * ZBIAS_SCALE_INV); return res; } break; case D3DRS_SOFTWAREVERTEXPROCESSING: *pValue = GetD3D9()->GetSoftwareVertexProcessing(); return D3D_OK; case D3DRS_PATCHSEGMENTS: *pValue = bit::cast(m_patchSegments); return D3D_OK; } // This call will never fail return GetD3D9()->GetRenderState(State9, pValue); } // Vertex Shaders // HRESULT STDMETHODCALLTYPE D3D8Device::CreateVertexShader( const DWORD* pDeclaration, const DWORD* pFunction, DWORD* pHandle, DWORD Usage ) { D3D8DeviceLock lock = LockDevice(); if (unlikely(pDeclaration == nullptr || pHandle == nullptr)) return D3DERR_INVALIDCALL; D3D9VertexShaderCode translatedVS; HRESULT res = TranslateVertexShader8(pDeclaration, pFunction, m_d3d8Options, translatedVS); if (unlikely(FAILED(res))) return res; // Create vertex declaration Com pVertexDecl; res = GetD3D9()->CreateVertexDeclaration(translatedVS.declaration, &pVertexDecl); if (unlikely(FAILED(res))) return res; Com pVertexShader; if (pFunction != nullptr) { res = GetD3D9()->CreateVertexShader(translatedVS.function.data(), &pVertexShader); } else { // pFunction is NULL: fixed function pipeline pVertexShader = nullptr; } if (likely(SUCCEEDED(res))) { D3D8VertexShaderInfo& info = m_vertexShaders.emplace_back(); info.pVertexDecl = std::move(pVertexDecl); info.pVertexShader = std::move(pVertexShader); // Store D3D8 bytecodes in the shader info for (UINT i = 0; pDeclaration[i] != D3DVSD_END(); i++) info.declaration.push_back(pDeclaration[i]); info.declaration.push_back(D3DVSD_END()); if (pFunction != nullptr) { for (UINT i = 0; pFunction[i] != D3DVS_END(); i++) info.function.push_back(pFunction[i]); info.function.push_back(D3DVS_END()); } // Set bit to indicate this is not an FVF *pHandle = getShaderHandle(m_vertexShaders.size()); } return res; } inline D3D8VertexShaderInfo* getVertexShaderInfo(D3D8Device* device, DWORD Handle) { Handle = getShaderIndex(Handle); if (unlikely(Handle >= device->m_vertexShaders.size())) { Logger::debug(str::format("D3D8: Invalid vertex shader index ", std::hex, Handle)); return nullptr; } D3D8VertexShaderInfo& info = device->m_vertexShaders[Handle]; if (unlikely(info.pVertexDecl == nullptr && info.pVertexShader == nullptr)) { Logger::debug(str::format("D3D8: Application provided deleted vertex shader ", std::hex, Handle)); return nullptr; } return &info; } HRESULT STDMETHODCALLTYPE D3D8Device::SetVertexShader(DWORD Handle) { D3D8DeviceLock lock = LockDevice(); HRESULT res; if (unlikely(ShouldRecord())) { return m_recorder->SetVertexShader(Handle); } // Check for extra bit that indicates this is not an FVF if (!isFVF(Handle)) { D3D8VertexShaderInfo* info = getVertexShaderInfo(this, Handle); if (!info) return D3DERR_INVALIDCALL; StateChange(); GetD3D9()->SetVertexDeclaration(info->pVertexDecl.ptr()); res = GetD3D9()->SetVertexShader(info->pVertexShader.ptr()); if (likely(SUCCEEDED(res))) { // Cache current shader m_currentVertexShader = Handle; } return res; } else if (m_currentVertexShader != Handle) { StateChange(); //GetD3D9()->SetVertexDeclaration(nullptr); GetD3D9()->SetVertexShader(nullptr); res = GetD3D9()->SetFVF(Handle); if (likely(SUCCEEDED(res))) { // Cache current FVF m_currentVertexShader = Handle; } return res; } return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShader(DWORD* pHandle) { D3D8DeviceLock lock = LockDevice(); if (unlikely(pHandle == nullptr)) return D3DERR_INVALIDCALL; // Return cached shader *pHandle = m_currentVertexShader; return D3D_OK; /* // Slow path. Use to debug cached shader validation. // d3d9::IDirect3DVertexShader9* pVertexShader; HRESULT res = GetD3D9()->GetVertexShader(&pVertexShader); if (FAILED(res) || pVertexShader == nullptr) { return GetD3D9()->GetFVF(pHandle); } for (unsigned int i = 0; i < m_vertexShaders.size(); i++) { D3D8VertexShaderInfo& info = m_vertexShaders[i]; if (info.pVertexShader == pVertexShader) { *pHandle = getShaderHandle(DWORD(i)); return res; } } return res; */ } HRESULT STDMETHODCALLTYPE D3D8Device::DeleteVertexShader(DWORD Handle) { D3D8DeviceLock lock = LockDevice(); if (!isFVF(Handle)) { D3D8VertexShaderInfo* info = getVertexShaderInfo(this, Handle); if (!info) return D3DERR_INVALIDCALL; info->pVertexDecl = nullptr; info->pVertexShader = nullptr; info->declaration.clear(); info->function.clear(); if (m_currentVertexShader == Handle) m_currentVertexShader = 0; } return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderDeclaration(DWORD Handle, void* pData, DWORD* pSizeOfData) { D3D8DeviceLock lock = LockDevice(); D3D8VertexShaderInfo* pInfo = getVertexShaderInfo(this, Handle); if (unlikely(!pInfo)) return D3DERR_INVALIDCALL; UINT SizeOfData = *pSizeOfData; // Get actual size UINT ActualSize = pInfo->declaration.size() * sizeof(DWORD); if (pData == nullptr) { *pSizeOfData = ActualSize; return D3D_OK; } // D3D8-specific behavior if (SizeOfData < ActualSize) { // D3DERR_MOREDATA should be returned according to the D3D8 documentation, // along with a correction to the ActualSize, however tests have shown that // D3DERR_INVALIDCALL is returned and no size correction is performed. return D3DERR_INVALIDCALL; } memcpy(pData, pInfo->declaration.data(), ActualSize); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData) { D3D8DeviceLock lock = LockDevice(); D3D8VertexShaderInfo* pInfo = getVertexShaderInfo(this, Handle); if (unlikely(!pInfo)) return D3DERR_INVALIDCALL; UINT SizeOfData = *pSizeOfData; // Get actual size UINT ActualSize = pInfo->function.size() * sizeof(DWORD); if (pData == nullptr) { *pSizeOfData = ActualSize; return D3D_OK; } // D3D8-specific behavior if (SizeOfData < ActualSize) { // D3DERR_MOREDATA should be returned according to the D3D8 documentation, // along with a correction to the ActualSize, however tests have shown that // D3DERR_INVALIDCALL is returned and no size correction is performed. return D3DERR_INVALIDCALL; } memcpy(pData, pInfo->function.data(), ActualSize); return D3D_OK; } // Pixel Shaders // HRESULT STDMETHODCALLTYPE D3D8Device::CreatePixelShader( const DWORD* pFunction, DWORD* pHandle) { D3D8DeviceLock lock = LockDevice(); if (unlikely(pFunction == nullptr || pHandle == nullptr)) return D3DERR_INVALIDCALL; Com pPixelShader; HRESULT res = GetD3D9()->CreatePixelShader(pFunction, &pPixelShader); if (likely(SUCCEEDED(res))) { m_pixelShaders.push_back(std::move(pPixelShader)); // Still set the shader bit, to prevent conflicts with NULL. *pHandle = getShaderHandle(m_pixelShaders.size()); } return res; } inline d3d9::IDirect3DPixelShader9* getPixelShaderPtr(D3D8Device* device, DWORD Handle) { Handle = getShaderIndex(Handle); if (unlikely(Handle >= device->m_pixelShaders.size())) { Logger::debug(str::format("D3D8: Invalid pixel shader index ", std::hex, Handle)); return nullptr; } d3d9::IDirect3DPixelShader9* pPixelShader = device->m_pixelShaders[Handle].ptr(); if (unlikely(pPixelShader == nullptr)) { Logger::debug(str::format("D3D8: Application provided deleted pixel shader ", std::hex, Handle)); return nullptr; } return pPixelShader; } HRESULT STDMETHODCALLTYPE D3D8Device::SetPixelShader(DWORD Handle) { D3D8DeviceLock lock = LockDevice(); if (unlikely(ShouldRecord())) { return m_recorder->SetPixelShader(Handle); } if (Handle == DWORD(NULL)) { StateChange(); m_currentPixelShader = DWORD(NULL); return GetD3D9()->SetPixelShader(nullptr); } d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle); if (unlikely(!pPixelShader)) { return D3DERR_INVALIDCALL; } StateChange(); HRESULT res = GetD3D9()->SetPixelShader(pPixelShader); if (likely(SUCCEEDED(res))) { // Cache current pixel shader m_currentPixelShader = Handle; } return res; } HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShader(DWORD* pHandle) { D3D8DeviceLock lock = LockDevice(); if (unlikely(pHandle == nullptr)) return D3DERR_INVALIDCALL; // Return cached shader *pHandle = m_currentPixelShader; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D8Device::DeletePixelShader(DWORD Handle) { D3D8DeviceLock lock = LockDevice(); d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle); if (unlikely(!pPixelShader)) { return D3DERR_INVALIDCALL; } m_pixelShaders[getShaderIndex(Handle)] = nullptr; if (m_currentPixelShader == Handle) m_currentPixelShader = 0; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData) { D3D8DeviceLock lock = LockDevice(); d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle); if (unlikely(!pPixelShader)) return D3DERR_INVALIDCALL; UINT SizeOfData = *pSizeOfData; // Get actual size UINT ActualSize = 0; pPixelShader->GetFunction(nullptr, &ActualSize); if (pData == nullptr) { *pSizeOfData = ActualSize; return D3D_OK; } // D3D8-specific behavior if (SizeOfData < ActualSize) { // D3DERR_MOREDATA should be returned according to the D3D8 documentation, // along with a correction to the ActualSize, however tests have shown that // D3DERR_INVALIDCALL is returned and no size correction is performed. return D3DERR_INVALIDCALL; } return pPixelShader->GetFunction(pData, &SizeOfData); } } dxvk-2.6.1/src/d3d8/d3d8_device.h000066400000000000000000000415611477473124000162770ustar00rootroot00000000000000#pragma once #include "d3d8_include.h" #include "d3d8_multithread.h" #include "d3d8_texture.h" #include "d3d8_buffer.h" #include "d3d8_swapchain.h" #include "d3d8_state_block.h" #include "d3d8_d3d9_util.h" #include "d3d8_caps.h" #include "d3d8_batch.h" #include "../d3d9/d3d9_bridge.h" #include #include #include #include namespace dxvk { class D3D8Interface; struct D3D8VertexShaderInfo; using D3D8DeviceBase = D3D8WrappedObject; class D3D8Device final : public D3D8DeviceBase { friend class D3D8StateBlock; public: D3D8Device( D3D8Interface* pParent, Com&& pDevice, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pParams); ~D3D8Device(); HRESULT STDMETHODCALLTYPE TestCooperativeLevel(); UINT STDMETHODCALLTYPE GetAvailableTextureMem(); HRESULT STDMETHODCALLTYPE ResourceManagerDiscardBytes(DWORD bytes); HRESULT STDMETHODCALLTYPE GetDirect3D(IDirect3D8** ppD3D8); HRESULT STDMETHODCALLTYPE GetDeviceCaps(D3DCAPS8* pCaps); HRESULT STDMETHODCALLTYPE GetDisplayMode(D3DDISPLAYMODE* pMode); HRESULT STDMETHODCALLTYPE GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS* pParameters); HRESULT STDMETHODCALLTYPE SetCursorProperties( UINT XHotSpot, UINT YHotSpot, IDirect3DSurface8* pCursorBitmap); void STDMETHODCALLTYPE SetCursorPosition(UINT XScreenSpace, UINT YScreenSpace, DWORD Flags); // Microsoft d3d8.h in the DirectX 9 SDK uses a different function signature... void STDMETHODCALLTYPE SetCursorPosition(int X, int Y, DWORD Flags); BOOL STDMETHODCALLTYPE ShowCursor(BOOL bShow); HRESULT STDMETHODCALLTYPE CreateAdditionalSwapChain( D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DSwapChain8** ppSwapChain); HRESULT STDMETHODCALLTYPE Reset(D3DPRESENT_PARAMETERS* pPresentationParameters); HRESULT STDMETHODCALLTYPE Present( const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion); HRESULT STDMETHODCALLTYPE GetBackBuffer( UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface8** ppBackBuffer); HRESULT STDMETHODCALLTYPE GetRasterStatus(D3DRASTER_STATUS* pRasterStatus); void STDMETHODCALLTYPE SetGammaRamp(DWORD Flags, const D3DGAMMARAMP* pRamp); void STDMETHODCALLTYPE GetGammaRamp(D3DGAMMARAMP* pRamp); HRESULT STDMETHODCALLTYPE CreateTexture( UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DTexture8** ppTexture); HRESULT STDMETHODCALLTYPE CreateVolumeTexture( UINT Width, UINT Height, UINT Depth, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DVolumeTexture8** ppVolumeTexture); HRESULT STDMETHODCALLTYPE CreateCubeTexture( UINT EdgeLength, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DCubeTexture8** ppCubeTexture); HRESULT STDMETHODCALLTYPE CreateVertexBuffer( UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer8** ppVertexBuffer); HRESULT STDMETHODCALLTYPE CreateIndexBuffer( UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer8** ppIndexBuffer); HRESULT STDMETHODCALLTYPE CreateRenderTarget( UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, BOOL Lockable, IDirect3DSurface8** ppSurface); HRESULT STDMETHODCALLTYPE CreateDepthStencilSurface( UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, IDirect3DSurface8** ppSurface); HRESULT STDMETHODCALLTYPE CreateImageSurface(UINT Width, UINT Height, D3DFORMAT Format, IDirect3DSurface8** ppSurface); HRESULT STDMETHODCALLTYPE CopyRects( IDirect3DSurface8* pSourceSurface, const RECT* pSourceRectsArray, UINT cRects, IDirect3DSurface8* pDestinationSurface, const POINT* pDestPointsArray); HRESULT STDMETHODCALLTYPE UpdateTexture( IDirect3DBaseTexture8* pSourceTexture, IDirect3DBaseTexture8* pDestinationTexture); HRESULT STDMETHODCALLTYPE GetFrontBuffer(IDirect3DSurface8* pDestSurface); HRESULT STDMETHODCALLTYPE SetRenderTarget(IDirect3DSurface8* pRenderTarget, IDirect3DSurface8* pNewZStencil); HRESULT STDMETHODCALLTYPE GetRenderTarget(IDirect3DSurface8** ppRenderTarget); HRESULT STDMETHODCALLTYPE GetDepthStencilSurface(IDirect3DSurface8** ppZStencilSurface); HRESULT STDMETHODCALLTYPE BeginScene(); HRESULT STDMETHODCALLTYPE EndScene(); HRESULT STDMETHODCALLTYPE Clear( DWORD Count, const D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil); HRESULT STDMETHODCALLTYPE SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix); HRESULT STDMETHODCALLTYPE GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix); HRESULT STDMETHODCALLTYPE MultiplyTransform(D3DTRANSFORMSTATETYPE TransformState, const D3DMATRIX* pMatrix); HRESULT STDMETHODCALLTYPE SetViewport(const D3DVIEWPORT8* pViewport); HRESULT STDMETHODCALLTYPE GetViewport(D3DVIEWPORT8* pViewport); HRESULT STDMETHODCALLTYPE SetMaterial(const D3DMATERIAL8* pMaterial); HRESULT STDMETHODCALLTYPE GetMaterial(D3DMATERIAL8* pMaterial); HRESULT STDMETHODCALLTYPE SetLight(DWORD Index, const D3DLIGHT8* pLight); HRESULT STDMETHODCALLTYPE GetLight(DWORD Index, D3DLIGHT8* pLight); HRESULT STDMETHODCALLTYPE LightEnable(DWORD Index, BOOL Enable); HRESULT STDMETHODCALLTYPE GetLightEnable(DWORD Index, BOOL* pEnable); HRESULT STDMETHODCALLTYPE SetClipPlane(DWORD Index, const float* pPlane); HRESULT STDMETHODCALLTYPE GetClipPlane(DWORD Index, float* pPlane); HRESULT STDMETHODCALLTYPE SetRenderState(D3DRENDERSTATETYPE State, DWORD Value); HRESULT STDMETHODCALLTYPE GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue); HRESULT STDMETHODCALLTYPE CreateStateBlock( D3DSTATEBLOCKTYPE Type, DWORD* pToken); HRESULT STDMETHODCALLTYPE CaptureStateBlock(DWORD Token); HRESULT STDMETHODCALLTYPE ApplyStateBlock(DWORD Token); HRESULT STDMETHODCALLTYPE DeleteStateBlock(DWORD Token); HRESULT STDMETHODCALLTYPE BeginStateBlock(); HRESULT STDMETHODCALLTYPE EndStateBlock(DWORD* pToken); HRESULT STDMETHODCALLTYPE SetClipStatus(const D3DCLIPSTATUS8* pClipStatus); HRESULT STDMETHODCALLTYPE GetClipStatus(D3DCLIPSTATUS8* pClipStatus); HRESULT STDMETHODCALLTYPE GetTexture(DWORD Stage, IDirect3DBaseTexture8** ppTexture); HRESULT STDMETHODCALLTYPE SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture); HRESULT STDMETHODCALLTYPE GetTextureStageState( DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD* pValue); HRESULT STDMETHODCALLTYPE SetTextureStageState( DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value); HRESULT STDMETHODCALLTYPE ValidateDevice(DWORD* pNumPasses); HRESULT STDMETHODCALLTYPE GetInfo(DWORD DevInfoID, void* pDevInfoStruct, DWORD DevInfoStructSize); HRESULT STDMETHODCALLTYPE SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries); HRESULT STDMETHODCALLTYPE GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries); HRESULT STDMETHODCALLTYPE SetCurrentTexturePalette(UINT PaletteNumber); HRESULT STDMETHODCALLTYPE GetCurrentTexturePalette(UINT* PaletteNumber); HRESULT STDMETHODCALLTYPE DrawPrimitive( D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount); HRESULT STDMETHODCALLTYPE DrawIndexedPrimitive( D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT StartIndex, UINT PrimitiveCount); HRESULT STDMETHODCALLTYPE DrawPrimitiveUP( D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride); HRESULT STDMETHODCALLTYPE DrawIndexedPrimitiveUP( D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT PrimitiveCount, const void* pIndexData, D3DFORMAT IndexDataFormat, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride); HRESULT STDMETHODCALLTYPE ProcessVertices( UINT SrcStartIndex, UINT DestIndex, UINT VertexCount, IDirect3DVertexBuffer8* pDestBuffer, DWORD Flags); HRESULT STDMETHODCALLTYPE CreateVertexShader( const DWORD* pDeclaration, const DWORD* pFunction, DWORD* pHandle, DWORD Usage); HRESULT STDMETHODCALLTYPE SetVertexShader(DWORD Handle); HRESULT STDMETHODCALLTYPE GetVertexShader(DWORD* pHandle); HRESULT STDMETHODCALLTYPE DeleteVertexShader(DWORD Handle); HRESULT STDMETHODCALLTYPE SetVertexShaderConstant( DWORD StartRegister, const void* pConstantData, DWORD ConstantCount); HRESULT STDMETHODCALLTYPE GetVertexShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount); HRESULT STDMETHODCALLTYPE GetVertexShaderDeclaration(DWORD Handle, void* pData, DWORD* pSizeOfData); HRESULT STDMETHODCALLTYPE GetVertexShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData); HRESULT STDMETHODCALLTYPE SetStreamSource( UINT StreamNumber, IDirect3DVertexBuffer8* pStreamData, UINT Stride); HRESULT STDMETHODCALLTYPE GetStreamSource( UINT StreamNumber, IDirect3DVertexBuffer8** ppStreamData, UINT* pStride); HRESULT STDMETHODCALLTYPE SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex); HRESULT STDMETHODCALLTYPE GetIndices( IDirect3DIndexBuffer8** ppIndexData, UINT* pBaseVertexIndex); HRESULT STDMETHODCALLTYPE CreatePixelShader( const DWORD* pFunction, DWORD* pHandle); HRESULT STDMETHODCALLTYPE SetPixelShader(DWORD Handle); HRESULT STDMETHODCALLTYPE GetPixelShader(DWORD* pHandle); HRESULT STDMETHODCALLTYPE DeletePixelShader(THIS_ DWORD Handle); HRESULT STDMETHODCALLTYPE GetPixelShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount); HRESULT STDMETHODCALLTYPE SetPixelShaderConstant( DWORD StartRegister, const void* pConstantData, DWORD ConstantCount); HRESULT STDMETHODCALLTYPE GetPixelShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData); HRESULT STDMETHODCALLTYPE DrawRectPatch( UINT Handle, const float* pNumSegs, const D3DRECTPATCH_INFO* pRectPatchInfo); HRESULT STDMETHODCALLTYPE DrawTriPatch( UINT Handle, const float* pNumSegs, const D3DTRIPATCH_INFO* pTriPatchInfo); HRESULT STDMETHODCALLTYPE DeletePatch(UINT Handle); const D3D8Options* GetOptions() const { return &m_d3d8Options; } inline bool ShouldRecord() { return m_recorder != nullptr; } inline bool ShouldBatch() { return m_batcher != nullptr; } D3D8DeviceLock LockDevice() { return m_multithread.AcquireLock(); } /** * Marks any state change in the device, so we can signal * the batcher to emit draw calls. StateChange should be * called immediately before changing any D3D9 state. */ inline void StateChange() { if (ShouldBatch()) m_batcher->StateChange(); } inline void ResetState() { // Mirrors how D3D9 handles the BackBufferCount m_presentParams.BackBufferCount = std::max(m_presentParams.BackBufferCount, 1u); // Purge cached objects m_textures.fill(nullptr); m_streams.fill(D3D8VBO()); m_indices = nullptr; m_renderTarget = nullptr; m_depthStencil = nullptr; m_backBuffers.clear(); m_backBuffers.resize(m_presentParams.BackBufferCount); m_autoDepthStencil = nullptr; m_shadowPerspectiveDivide = false; } inline void RecreateBackBuffersAndAutoDepthStencil() { for (UINT i = 0; i < m_presentParams.BackBufferCount; i++) { Com pSurface9; GetD3D9()->GetBackBuffer(0, i, d3d9::D3DBACKBUFFER_TYPE_MONO, &pSurface9); m_backBuffers[i] = new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pSurface9)); } Com pStencil9; // This call will fail if the D3D9 device is created without // the EnableAutoDepthStencil presentation parameter set to TRUE. HRESULT res = GetD3D9()->GetDepthStencilSurface(&pStencil9); m_autoDepthStencil = FAILED(res) ? nullptr : new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pStencil9)); m_renderTarget = m_backBuffers[0]; m_depthStencil = m_autoDepthStencil; } friend d3d9::IDirect3DPixelShader9* getPixelShaderPtr(D3D8Device* device, DWORD Handle); friend D3D8VertexShaderInfo* getVertexShaderInfo(D3D8Device* device, DWORD Handle); private: Com m_bridge; const D3D8Options& m_d3d8Options; Com m_parent; D3DPRESENT_PARAMETERS m_presentParams; // Value of D3DRS_LINEPATTERN D3DLINEPATTERN m_linePattern = {}; // Value of D3DRS_ZVISIBLE (although the RS is not supported, its value is stored) DWORD m_zVisible = 0; // Value of D3DRS_PATCHSEGMENTS float m_patchSegments = 1.0f; // Controls fixed-function exclusive mode (no PS support) bool m_isFixedFunctionOnly = false; bool m_shadowPerspectiveDivide = false; D3D8StateBlock* m_recorder = nullptr; DWORD m_recorderToken = 0; DWORD m_token = 0; std::unordered_map m_stateBlocks; D3D8Batcher* m_batcher = nullptr; struct D3D8VBO { Com buffer = nullptr; UINT stride = 0; }; std::array, d8caps::MAX_TEXTURE_STAGES> m_textures; std::array m_streams; Com m_indices; UINT m_baseVertexIndex = 0; std::vector> m_backBuffers; Com m_autoDepthStencil; Com m_renderTarget; Com m_depthStencil; std::vector m_vertexShaders; std::vector> m_pixelShaders; DWORD m_currentVertexShader = 0; // can be FVF or vs index (marked by D3DFVF_RESERVED0) DWORD m_currentPixelShader = 0; D3DDEVTYPE m_deviceType; HWND m_window; DWORD m_behaviorFlags; D3D8Multithread m_multithread; }; } dxvk-2.6.1/src/d3d8/d3d8_device_child.h000066400000000000000000000036621477473124000174420ustar00rootroot00000000000000#pragma once // Common methods for device-tied objects. // - AddRef, Release from IUnknown // - GetDevice from various classes including IDirect3DResource8 #include "d3d8_include.h" #include "d3d8_wrapped_object.h" namespace dxvk { class D3D8Device; template class D3D8DeviceChild : public D3D8WrappedObject { public: D3D8DeviceChild(D3D8Device* pDevice, Com&& Object) : D3D8WrappedObject(std::move(Object)) , m_parent( pDevice ) { } ULONG STDMETHODCALLTYPE AddRef() { uint32_t refCount = this->m_refCount++; if (unlikely(!refCount)) { this->AddRefPrivate(); GetDevice()->AddRef(); } return refCount + 1; } ULONG STDMETHODCALLTYPE Release() { uint32_t oldRefCount, refCount; do { oldRefCount = this->m_refCount.load(std::memory_order_acquire); // clamp value to 0 to prevent underruns if (unlikely(!oldRefCount)) return 0; refCount = oldRefCount - 1; } while (!this->m_refCount.compare_exchange_weak(oldRefCount, refCount, std::memory_order_release, std::memory_order_acquire)); if (unlikely(!refCount)) { auto* pDevice = GetDevice(); this->ReleasePrivate(); pDevice->Release(); } return refCount; } HRESULT STDMETHODCALLTYPE GetDevice(IDirect3DDevice8** ppDevice) { InitReturnPtr(ppDevice); if (ppDevice == nullptr) return D3DERR_INVALIDCALL; *ppDevice = ref(GetDevice()); return D3D_OK; } IDirect3DDevice8* GetDevice() { return reinterpret_cast(m_parent); } D3D8Device* GetParent() { return m_parent; } protected: D3D8Device* m_parent; }; }dxvk-2.6.1/src/d3d8/d3d8_format.h000066400000000000000000000125761477473124000163340ustar00rootroot00000000000000#pragma once #include "d3d8_include.h" namespace dxvk { constexpr bool isDXT(D3DFORMAT fmt) { return fmt == D3DFMT_DXT1 || fmt == D3DFMT_DXT2 || fmt == D3DFMT_DXT3 || fmt == D3DFMT_DXT4 || fmt == D3DFMT_DXT5; } constexpr bool isDXT(d3d9::D3DFORMAT fmt) { return isDXT(D3DFORMAT(fmt)); } constexpr bool isUnsupportedSurfaceFormat(D3DFORMAT fmt) { // mirror what dxvk doesn't support in terms of d3d9 surface formats return fmt == D3DFMT_R8G8B8 || fmt == D3DFMT_R3G3B2 || fmt == D3DFMT_A8R3G3B2 || fmt == D3DFMT_A8P8 || fmt == D3DFMT_P8; // not included in the d3d8 spec //|| fmt == D3DFMT_CXV8U8; } constexpr bool isSupportedDepthStencilFormat(D3DFORMAT fmt) { // native d3d8 doesn't support D3DFMT_D32, D3DFMT_D15S1 or D3DFMT_D24X4S4 return fmt == D3DFMT_D16_LOCKABLE || fmt == D3DFMT_D16 //|| fmt == D3DFMT_D32 //|| fmt == D3DFMT_D15S1 //|| fmt == D3DFMT_D24X4S4 || fmt == D3DFMT_D24S8 || fmt == D3DFMT_D24X8; } constexpr bool isDepthStencilFormat(D3DFORMAT fmt) { return fmt == D3DFMT_D16_LOCKABLE || fmt == D3DFMT_D16 || fmt == D3DFMT_D32 || fmt == D3DFMT_D15S1 || fmt == D3DFMT_D24X4S4 || fmt == D3DFMT_D24S8 || fmt == D3DFMT_D24X8; } // Get bytes per pixel (or 4x4 block for DXT) constexpr UINT getFormatStride(D3DFORMAT fmt) { switch (fmt) { default: case D3DFMT_UNKNOWN: return 0; case D3DFMT_R3G3B2: case D3DFMT_A8: case D3DFMT_P8: case D3DFMT_L8: case D3DFMT_A4L4: return 1; case D3DFMT_R5G6B5: case D3DFMT_X1R5G5B5: case D3DFMT_A1R5G5B5: case D3DFMT_A4R4G4B4: case D3DFMT_A8R3G3B2: case D3DFMT_X4R4G4B4: case D3DFMT_A8P8: case D3DFMT_A8L8: case D3DFMT_V8U8: case D3DFMT_L6V5U5: case D3DFMT_D16_LOCKABLE: case D3DFMT_D15S1: case D3DFMT_D16: case D3DFMT_UYVY: case D3DFMT_YUY2: return 2; case D3DFMT_R8G8B8: return 3; case D3DFMT_A8R8G8B8: case D3DFMT_X8R8G8B8: case D3DFMT_A2B10G10R10: //case D3DFMT_A8B8G8R8: //case D3DFMT_X8B8G8R8: case D3DFMT_G16R16: case D3DFMT_X8L8V8U8: case D3DFMT_Q8W8V8U8: case D3DFMT_V16U16: case D3DFMT_W11V11U10: case D3DFMT_A2W10V10U10: case D3DFMT_D32: case D3DFMT_D24S8: case D3DFMT_D24X8: case D3DFMT_D24X4S4: return 4; case D3DFMT_DXT1: return 8; case D3DFMT_DXT2: case D3DFMT_DXT3: case D3DFMT_DXT4: case D3DFMT_DXT5: return 16; } } constexpr uint32_t GetVertexCount8(D3DPRIMITIVETYPE type, UINT count) { switch (type) { default: case D3DPT_TRIANGLELIST: return count * 3; case D3DPT_POINTLIST: return count; case D3DPT_LINELIST: return count * 2; case D3DPT_LINESTRIP: return count + 1; case D3DPT_TRIANGLESTRIP: return count + 2; case D3DPT_TRIANGLEFAN: return count + 2; } } // Essentially the same logic as D3D9VertexDecl::SetFVF constexpr UINT GetFVFStride(DWORD FVF) { uint32_t texCount = 0; uint32_t betas = 0; uint8_t betaIdx = 0xFF; UINT size = 0; switch (FVF & D3DFVF_POSITION_MASK) { case D3DFVF_XYZ: case D3DFVF_XYZB1: case D3DFVF_XYZB2: case D3DFVF_XYZB3: case D3DFVF_XYZB4: case D3DFVF_XYZB5: size += sizeof(float) * 3; if ((FVF & D3DFVF_POSITION_MASK) == D3DFVF_XYZ) break; betas = (((FVF & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1) + 1; if (FVF & D3DFVF_LASTBETA_D3DCOLOR) betaIdx = sizeof(D3DCOLOR); else if (FVF & D3DFVF_LASTBETA_UBYTE4) betaIdx = sizeof(BYTE) * 4; else if ((FVF & D3DFVF_XYZB5) == D3DFVF_XYZB5) betaIdx = sizeof(float); if (betaIdx != 0xFF) betas--; if (betas > 0) { if (betas <= 4) size += sizeof(float) * betas; } if (betaIdx != 0xFF) { size += betaIdx; } break; case D3DFVF_XYZW: case D3DFVF_XYZRHW: size += sizeof(float) * 4; break; default: break; } if (FVF & D3DFVF_NORMAL) { size += sizeof(float) * 3; } if (FVF & D3DFVF_PSIZE) { size += sizeof(float); } if (FVF & D3DFVF_DIFFUSE) { size += sizeof(D3DCOLOR); } if (FVF & D3DFVF_SPECULAR) { size += sizeof(D3DCOLOR); } texCount = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT; texCount = std::min(texCount, 8u); for (uint32_t i = 0; i < texCount; i++) { switch ((FVF >> (16 + i * 2)) & 0x3) { case D3DFVF_TEXTUREFORMAT1: size += sizeof(float); break; case D3DFVF_TEXTUREFORMAT2: size += sizeof(float) * 2; break; case D3DFVF_TEXTUREFORMAT3: size += sizeof(float) * 3; break; case D3DFVF_TEXTUREFORMAT4: size += sizeof(float) * 4; break; default: break; } } return size; } constexpr UINT getSurfaceSize(D3DFORMAT Format, UINT Width, UINT Height) { if (isDXT(Format)) { Width = ((Width + 3) >> 2); Height = ((Height + 3) >> 2); } return Width * Height * getFormatStride(Format); } } dxvk-2.6.1/src/d3d8/d3d8_include.h000066400000000000000000000167601477473124000164660ustar00rootroot00000000000000#pragma once #ifndef _MSC_VER #ifdef _WIN32_WINNT #undef _WIN32_WINNT #endif #define _WIN32_WINNT 0x0A00 #endif #include #include // Declare __uuidof for D3D8 interfaces #ifdef __CRT_UUID_DECL __CRT_UUID_DECL(IDirect3D8, 0x1DD9E8DA,0x1C77,0x4D40,0xB0,0xCF,0x98,0xFE,0xFD,0xFF,0x95,0x12); __CRT_UUID_DECL(IDirect3DDevice8, 0x7385E5DF,0x8FE8,0x41D5,0x86,0xB6,0xD7,0xB4,0x85,0x47,0xB6,0xCF); __CRT_UUID_DECL(IDirect3DResource8, 0x1B36BB7B,0x09B7,0x410A,0xB4,0x45,0x7D,0x14,0x30,0xD7,0xB3,0x3F); __CRT_UUID_DECL(IDirect3DVertexBuffer8, 0x8AEEEAC7,0x05F9,0x44D4,0xB5,0x91,0x00,0x0B,0x0D,0xF1,0xCB,0x95); __CRT_UUID_DECL(IDirect3DVolume8, 0xBD7349F5,0x14F1,0x42E4,0x9C,0x79,0x97,0x23,0x80,0xDB,0x40,0xC0); __CRT_UUID_DECL(IDirect3DSwapChain8, 0x928C088B,0x76B9,0x4C6B,0xA5,0x36,0xA5,0x90,0x85,0x38,0x76,0xCD); __CRT_UUID_DECL(IDirect3DSurface8, 0xB96EEBCA,0xB326,0x4EA5,0x88,0x2F,0x2F,0xF5,0xBA,0xE0,0x21,0xDD); __CRT_UUID_DECL(IDirect3DIndexBuffer8, 0x0E689C9A,0x053D,0x44A0,0x9D,0x92,0xDB,0x0E,0x3D,0x75,0x0F,0x86); __CRT_UUID_DECL(IDirect3DBaseTexture8, 0xB4211CFA,0x51B9,0x4A9F,0xAB,0x78,0xDB,0x99,0xB2,0xBB,0x67,0x8E); __CRT_UUID_DECL(IDirect3DTexture8, 0xE4CDD575,0x2866,0x4F01,0xB1,0x2E,0x7E,0xEC,0xE1,0xEC,0x93,0x58); __CRT_UUID_DECL(IDirect3DCubeTexture8, 0x3EE5B968,0x2ACA,0x4C34,0x8B,0xB5,0x7E,0x0C,0x3D,0x19,0xB7,0x50); __CRT_UUID_DECL(IDirect3DVolumeTexture8, 0x4B8AAAFA,0x140F,0x42BA,0x91,0x31,0x59,0x7E,0xAF,0xAA,0x2E,0xAD); #elif defined(_MSC_VER) interface DECLSPEC_UUID("1DD9E8DA-1C77-4D40-B0CF-98FEFDFF9512") IDirect3D8; interface DECLSPEC_UUID("7385E5DF-8FE8-41D5-86B6-D7B48547B6CF") IDirect3DDevice8; interface DECLSPEC_UUID("1B36BB7B-09B7-410A-B445-7D1430D7B33F") IDirect3DResource8; interface DECLSPEC_UUID("8AEEEAC7-05F9-44D4-B591-000B0DF1CB95") IDirect3DVertexBuffer8; interface DECLSPEC_UUID("BD7349F5-14F1-42E4-9C79-972380DB40C0") IDirect3DVolume8; interface DECLSPEC_UUID("928C088B-76B9-4C6B-A536-A590853876CD") IDirect3DSwapChain8; interface DECLSPEC_UUID("B96EEBCA-B326-4EA5-882F-2FF5BAE021DD") IDirect3DSurface8; interface DECLSPEC_UUID("0E689C9A-053D-44A0-9D92-DB0E3D750F86") IDirect3DIndexBuffer8; interface DECLSPEC_UUID("B4211CFA-51B9-4A9F-AB78-DB99B2BB678E") IDirect3DBaseTexture8; interface DECLSPEC_UUID("E4CDD575-2866-4F01-B12E-7EECE1EC9358") IDirect3DTexture8; interface DECLSPEC_UUID("3EE5B968-2ACA-4C34-8BB5-7E0C3D19B750") IDirect3DCubeTexture8; interface DECLSPEC_UUID("4B8AAAFA-140F-42BA-9131-597EAFAA2EAD") IDirect3DVolumeTexture8; #endif // Undefine D3D8 macros #undef DIRECT3D_VERSION #undef D3D_SDK_VERSION #undef D3DCS_ALL // parentheses added in D3D9 #undef D3DFVF_POSITION_MASK // changed from 0x00E to 0x400E in D3D9 #undef D3DFVF_RESERVED2 // reduced from 4 to 2 in DX9 #undef D3DSP_REGNUM_MASK // changed from 0x00000FFF to 0x000007FF in D3D9 #if defined(__MINGW32__) || defined(__GNUC__) // Avoid redundant definitions (add D3D*_DEFINED macros here) #define D3DRECT_DEFINED #define D3DMATRIX_DEFINED // Temporarily override __CRT_UUID_DECL to allow usage in d3d9 namespace #pragma push_macro("__CRT_UUID_DECL") #ifdef __CRT_UUID_DECL #undef __CRT_UUID_DECL #endif #ifdef __MINGW32__ #define __CRT_UUID_DECL(type,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ } \ extern "C++" template<> struct __mingw_uuidof_s { static constexpr IID __uuid_inst = {l,w1,w2, {b1,b2,b3,b4,b5,b6,b7,b8}}; }; \ extern "C++" template<> constexpr const GUID &__mingw_uuidof() { return __mingw_uuidof_s::__uuid_inst; } \ extern "C++" template<> constexpr const GUID &__mingw_uuidof() { return __mingw_uuidof_s::__uuid_inst; } \ namespace d3d9 { #elif defined(__GNUC__) #define __CRT_UUID_DECL(type, a, b, c, d, e, f, g, h, i, j, k) \ } \ extern "C++" { template <> constexpr GUID __uuidof_helper() { return GUID{a,b,c,{d,e,f,g,h,i,j,k}}; } } \ extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ namespace d3d9 { #endif #endif // defined(__MINGW32__) || defined(__GNUC__) /** * \brief Direct3D 9 * * All D3D9 interfaces are included within * a namespace, so as not to collide with * D3D8 interfaces. */ namespace d3d9 { #include } // Indicates d3d9:: namespace is in-use. #define DXVK_D3D9_NAMESPACE #if defined(__MINGW32__) || defined(__GNUC__) #pragma pop_macro("__CRT_UUID_DECL") #endif //for some reason we need to specify __declspec(dllexport) for MinGW #if defined(__WINE__) || !defined(_WIN32) #define DLLEXPORT __attribute__((visibility("default"))) #else #define DLLEXPORT #endif #include "../util/com/com_guid.h" #include "../util/com/com_object.h" #include "../util/com/com_pointer.h" #include "../util/log/log.h" #include "../util/log/log_debug.h" #include "../util/sync/sync_recursive.h" #include "../util/util_error.h" #include "../util/util_likely.h" #include "../util/util_string.h" // Missed definitions in Wine/MinGW. #ifndef D3DPRESENT_BACK_BUFFERS_MAX_EX #define D3DPRESENT_BACK_BUFFERS_MAX_EX 30 #endif #ifndef D3DSI_OPCODE_MASK #define D3DSI_OPCODE_MASK 0x0000FFFF #endif #ifndef D3DSP_TEXTURETYPE_MASK #define D3DSP_TEXTURETYPE_MASK 0x78000000 #endif #ifndef D3DUSAGE_AUTOGENMIPMAP #define D3DUSAGE_AUTOGENMIPMAP 0x00000400L #endif #ifndef D3DSP_DCL_USAGE_MASK #define D3DSP_DCL_USAGE_MASK 0x0000000f #endif #ifndef D3DSP_OPCODESPECIFICCONTROL_MASK #define D3DSP_OPCODESPECIFICCONTROL_MASK 0x00ff0000 #endif #ifndef D3DSP_OPCODESPECIFICCONTROL_SHIFT #define D3DSP_OPCODESPECIFICCONTROL_SHIFT 16 #endif #ifndef D3DCURSOR_IMMEDIATE_UPDATE #define D3DCURSOR_IMMEDIATE_UPDATE 0x00000001L #endif #ifndef D3DPRESENT_FORCEIMMEDIATE #define D3DPRESENT_FORCEIMMEDIATE 0x00000100L #endif // From d3dtypes.h #ifndef D3DDEVINFOID_TEXTUREMANAGER #define D3DDEVINFOID_TEXTUREMANAGER 1 #endif #ifndef D3DDEVINFOID_D3DTEXTUREMANAGER #define D3DDEVINFOID_D3DTEXTUREMANAGER 2 #endif #ifndef D3DDEVINFOID_TEXTURING #define D3DDEVINFOID_TEXTURING 3 #endif // From d3dhal.h #ifndef D3DDEVINFOID_VCACHE #define D3DDEVINFOID_VCACHE 4 #endif // MinGW headers are broken. Who'dve guessed? #ifndef _MSC_VER // Missing from d3d8types.h #ifndef D3DDEVINFOID_RESOURCEMANAGER #define D3DDEVINFOID_RESOURCEMANAGER 5 #endif #ifndef D3DDEVINFOID_VERTEXSTATS #define D3DDEVINFOID_VERTEXSTATS 6 // Aka D3DDEVINFOID_D3DVERTEXSTATS #endif #ifndef D3DPRESENT_RATE_UNLIMITED #define D3DPRESENT_RATE_UNLIMITED 0x7FFFFFFF #endif #else // _MSC_VER // These are enum typedefs in the MinGW headers, but not defined by Microsoft #define D3DVSDT_TYPE DWORD #define D3DVSDE_REGISTER DWORD #endif dxvk-2.6.1/src/d3d8/d3d8_interface.cpp000066400000000000000000000122701477473124000173260ustar00rootroot00000000000000#include "d3d8_interface.h" #include "d3d8_device.h" #include "d3d8_texture.h" #include namespace dxvk { D3D8Interface::D3D8Interface() : m_d3d9(d3d9::Direct3DCreate9(D3D_SDK_VERSION)) { // Get the bridge interface to D3D9. if (FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast(&m_bridge)))) { throw DxvkError("D3D8Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); } m_bridge->EnableD3D8CompatibilityMode(); m_d3d8Options = D3D8Options(*m_bridge->GetConfig()); m_adapterCount = m_d3d9->GetAdapterCount(); m_adapterModeCounts.resize(m_adapterCount); m_adapterModes.reserve(m_adapterCount); for (UINT adapter = 0; adapter < m_adapterCount; adapter++) { m_adapterModes.emplace_back(); // cache adapter modes and mode counts for each d3d9 format for (d3d9::D3DFORMAT fmt : ADAPTER_FORMATS) { const UINT modeCount = m_d3d9->GetAdapterModeCount(adapter, fmt); for (UINT mode = 0; mode < modeCount; mode++) { m_adapterModes[adapter].emplace_back(); m_d3d9->EnumAdapterModes(adapter, fmt, mode, &(m_adapterModes[adapter].back())); // can't use modeCount as it's only for one fmt m_adapterModeCounts[adapter]++; } } } } HRESULT STDMETHODCALLTYPE D3D8Interface::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(IDirect3D8)) { *ppvObject = ref(this); return S_OK; } Logger::warn("D3D8Interface::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE D3D8Interface::GetAdapterIdentifier( UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER8* pIdentifier) { if (unlikely(pIdentifier == nullptr)) return D3DERR_INVALIDCALL; // This flag now has the opposite effect. // Either way, WHQLevel will be 1 with Direct3D9Ex if (Flags & D3DENUM_NO_WHQL_LEVEL) Flags &= ~D3DENUM_WHQL_LEVEL; else Flags |= D3DENUM_WHQL_LEVEL; d3d9::D3DADAPTER_IDENTIFIER9 identifier9; HRESULT res = m_d3d9->GetAdapterIdentifier(Adapter, Flags, &identifier9); if (likely(SUCCEEDED(res))) { strncpy(pIdentifier->Driver, identifier9.Driver, MAX_DEVICE_IDENTIFIER_STRING); strncpy(pIdentifier->Description, identifier9.Description, MAX_DEVICE_IDENTIFIER_STRING); pIdentifier->DriverVersion = identifier9.DriverVersion; pIdentifier->VendorId = identifier9.VendorId; pIdentifier->DeviceId = identifier9.DeviceId; pIdentifier->SubSysId = identifier9.SubSysId; pIdentifier->Revision = identifier9.Revision; pIdentifier->DeviceIdentifier = identifier9.DeviceIdentifier; pIdentifier->WHQLLevel = identifier9.WHQLLevel; } return res; } HRESULT __stdcall D3D8Interface::EnumAdapterModes( UINT Adapter, UINT Mode, D3DDISPLAYMODE* pMode) { if (Adapter >= m_adapterCount || Mode >= m_adapterModeCounts[Adapter] || pMode == nullptr) { return D3DERR_INVALIDCALL; } pMode->Width = m_adapterModes[Adapter][Mode].Width; pMode->Height = m_adapterModes[Adapter][Mode].Height; pMode->RefreshRate = m_adapterModes[Adapter][Mode].RefreshRate; pMode->Format = D3DFORMAT(m_adapterModes[Adapter][Mode].Format); return D3D_OK; } HRESULT __stdcall D3D8Interface::CreateDevice( UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice8** ppReturnedDeviceInterface) { InitReturnPtr(ppReturnedDeviceInterface); if (unlikely(pPresentationParameters == nullptr || ppReturnedDeviceInterface == nullptr)) return D3DERR_INVALIDCALL; // D3DSWAPEFFECT_COPY can not be used with more than one back buffer. // This is also technically true for D3DSWAPEFFECT_COPY_VSYNC, however // RC Cars depends on it not being rejected. if (unlikely(pPresentationParameters->SwapEffect == D3DSWAPEFFECT_COPY && pPresentationParameters->BackBufferCount > 1)) return D3DERR_INVALIDCALL; // In D3D8 nothing except D3DPRESENT_INTERVAL_DEFAULT can be used // as a flag for windowed presentation. if (unlikely(pPresentationParameters->Windowed && pPresentationParameters->FullScreen_PresentationInterval != D3DPRESENT_INTERVAL_DEFAULT)) return D3DERR_INVALIDCALL; Com pDevice9 = nullptr; d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters); HRESULT res = m_d3d9->CreateDevice( Adapter, (d3d9::D3DDEVTYPE)DeviceType, hFocusWindow, BehaviorFlags, ¶ms, &pDevice9 ); if (likely(SUCCEEDED(res))) *ppReturnedDeviceInterface = ref(new D3D8Device( this, std::move(pDevice9), DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters )); return res; } }dxvk-2.6.1/src/d3d8/d3d8_interface.h000066400000000000000000000122511477473124000167720ustar00rootroot00000000000000#pragma once #include "d3d8_include.h" #include "d3d8_d3d9_util.h" #include "d3d8_options.h" #include "d3d8_format.h" #include "../d3d9/d3d9_bridge.h" namespace dxvk { /** * \brief D3D8 interface implementation * * Implements the IDirect3DDevice8 interfaces * which provides the way to get adapters and create other objects such as \ref IDirect3DDevice8. * similar to \ref DxgiFactory but for D3D8. */ class D3D8Interface final : public ComObjectClamp { static constexpr d3d9::D3DFORMAT ADAPTER_FORMATS[] = { d3d9::D3DFMT_A1R5G5B5, //d3d9::D3DFMT_A2R10G10B10, (not in D3D8) d3d9::D3DFMT_A8R8G8B8, d3d9::D3DFMT_R5G6B5, d3d9::D3DFMT_X1R5G5B5, d3d9::D3DFMT_X8R8G8B8 }; public: D3D8Interface(); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE RegisterSoftwareDevice(void* pInitializeFunction) { return m_d3d9->RegisterSoftwareDevice(pInitializeFunction); } UINT STDMETHODCALLTYPE GetAdapterCount() { return m_d3d9->GetAdapterCount(); } HRESULT STDMETHODCALLTYPE GetAdapterIdentifier( UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER8* pIdentifier); UINT STDMETHODCALLTYPE GetAdapterModeCount(UINT Adapter) { return m_adapterModeCounts[Adapter]; } HRESULT STDMETHODCALLTYPE EnumAdapterModes( UINT Adapter, UINT Mode, D3DDISPLAYMODE* pMode); HRESULT STDMETHODCALLTYPE GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode) { return m_d3d9->GetAdapterDisplayMode(Adapter, (d3d9::D3DDISPLAYMODE*)pMode); } HRESULT STDMETHODCALLTYPE CheckDeviceType( UINT Adapter, D3DDEVTYPE DevType, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, BOOL bWindowed) { // Ignore the bWindowed parameter when querying D3D9. D3D8 does // identical validations between windowed and fullscreen modes, adhering // to the stricter fullscreen adapter and back buffer format validations. return m_d3d9->CheckDeviceType( Adapter, (d3d9::D3DDEVTYPE)DevType, (d3d9::D3DFORMAT)AdapterFormat, (d3d9::D3DFORMAT)BackBufferFormat, FALSE ); } HRESULT STDMETHODCALLTYPE CheckDeviceFormat( UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat) { return m_d3d9->CheckDeviceFormat( Adapter, (d3d9::D3DDEVTYPE)DeviceType, (d3d9::D3DFORMAT)AdapterFormat, Usage, (d3d9::D3DRESOURCETYPE)RType, (d3d9::D3DFORMAT)CheckFormat ); } HRESULT STDMETHODCALLTYPE CheckDeviceMultiSampleType( UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType) { DWORD* pQualityLevels = nullptr; return m_d3d9->CheckDeviceMultiSampleType( Adapter, (d3d9::D3DDEVTYPE)DeviceType, (d3d9::D3DFORMAT)SurfaceFormat, Windowed, (d3d9::D3DMULTISAMPLE_TYPE)MultiSampleType, pQualityLevels ); } HRESULT STDMETHODCALLTYPE CheckDepthStencilMatch( UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat) { if (isSupportedDepthStencilFormat(DepthStencilFormat)) return m_d3d9->CheckDepthStencilMatch( Adapter, (d3d9::D3DDEVTYPE)DeviceType, (d3d9::D3DFORMAT)AdapterFormat, (d3d9::D3DFORMAT)RenderTargetFormat, (d3d9::D3DFORMAT)DepthStencilFormat ); return D3DERR_NOTAVAILABLE; } HRESULT STDMETHODCALLTYPE GetDeviceCaps( UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS8* pCaps) { if (unlikely(pCaps == nullptr)) return D3DERR_INVALIDCALL; d3d9::D3DCAPS9 caps9; HRESULT res = m_d3d9->GetDeviceCaps(Adapter, (d3d9::D3DDEVTYPE)DeviceType, &caps9); if (likely(SUCCEEDED(res))) ConvertCaps8(caps9, pCaps); return res; } HMONITOR STDMETHODCALLTYPE GetAdapterMonitor(UINT Adapter) { return m_d3d9->GetAdapterMonitor(Adapter); } HRESULT STDMETHODCALLTYPE CreateDevice( UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice8** ppReturnedDeviceInterface); const D3D8Options& GetOptions() { return m_d3d8Options; } private: UINT m_adapterCount; std::vector m_adapterModeCounts; std::vector> m_adapterModes; Com m_d3d9; Com m_bridge; D3D8Options m_d3d8Options; }; }dxvk-2.6.1/src/d3d8/d3d8_main.cpp000066400000000000000000000076641477473124000163250ustar00rootroot00000000000000#include "d3d8_interface.h" namespace dxvk { Logger Logger::s_instance("d3d8.log"); HRESULT CreateD3D8(IDirect3D8** ppDirect3D8) { if (!ppDirect3D8) return D3DERR_INVALIDCALL; *ppDirect3D8 = ref(new D3D8Interface()); return D3D_OK; } } extern "C" { DLLEXPORT HRESULT __stdcall ValidatePixelShader( const DWORD* pPixelShader, const D3DCAPS8* pCaps, BOOL ErrorReturn, char** pErrorString) { HRESULT res = S_OK; std::string errorMessage = ""; // ValidatePixelShader returns immediately in case of a NULL pPixelShader if (unlikely(pPixelShader == nullptr)) { dxvk::Logger::warn("D3D8: ValidatePixelShader: Null pPixelShader"); return E_FAIL; } else { const uint32_t majorVersion = D3DSHADER_VERSION_MAJOR(pPixelShader[0]); const uint32_t minorVersion = D3DSHADER_VERSION_MINOR(pPixelShader[0]); if (unlikely(majorVersion != 1 || minorVersion > 4)) { errorMessage = dxvk::str::format("D3D8: ValidatePixelShader: Unsupported PS version ", majorVersion, ".", minorVersion); res = E_FAIL; } else if (unlikely(pCaps && pPixelShader[0] > pCaps->PixelShaderVersion)) { errorMessage = dxvk::str::format("D3D8: ValidatePixelShader: Caps: Unsupported PS version ", majorVersion, ".", minorVersion); res = E_FAIL; } } if (unlikely(res != S_OK)) { dxvk::Logger::warn(errorMessage); if (!ErrorReturn) errorMessage = ""; } #ifdef _WIN32 if (pErrorString != nullptr) { const size_t errorMessageSize = errorMessage.size() + 1; // Wine tests call HeapFree() on the returned error string, // so the expectation is for it to be allocated on the heap. *pErrorString = (char*) HeapAlloc(GetProcessHeap(), 0, errorMessageSize); if (*pErrorString) memcpy(*pErrorString, errorMessage.c_str(), errorMessageSize); } #endif return res; } DLLEXPORT HRESULT __stdcall ValidateVertexShader( const DWORD* pVertexShader, const DWORD* pVertexDecl, const D3DCAPS8* pCaps, BOOL ErrorReturn, char** pErrorString) { HRESULT res = S_OK; std::string errorMessage = ""; if (unlikely(pVertexShader == nullptr)) { errorMessage = "D3D8: ValidateVertexShader: Null pVertexShader"; res = E_FAIL; } else { const uint32_t majorVersion = D3DSHADER_VERSION_MAJOR(pVertexShader[0]); const uint32_t minorVersion = D3DSHADER_VERSION_MINOR(pVertexShader[0]); if (unlikely(majorVersion != 1 || minorVersion > 1)) { errorMessage = dxvk::str::format("D3D8: ValidateVertexShader: Unsupported VS version ", majorVersion, ".", minorVersion); res = E_FAIL; } else if (unlikely(pCaps && pVertexShader[0] > pCaps->VertexShaderVersion)) { errorMessage = dxvk::str::format("D3D8: ValidateVertexShader: Caps: Unsupported VS version ", majorVersion, ".", minorVersion); res = E_FAIL; } } if (unlikely(res != S_OK)) { dxvk::Logger::warn(errorMessage); if (!ErrorReturn) errorMessage = ""; } #ifdef _WIN32 if (pErrorString != nullptr) { const size_t errorMessageSize = errorMessage.size() + 1; // Wine tests call HeapFree() on the returned error string, // so the expectation is for it to be allocated on the heap. *pErrorString = (char*) HeapAlloc(GetProcessHeap(), 0, errorMessageSize); if (*pErrorString) memcpy(*pErrorString, errorMessage.c_str(), errorMessageSize); } #endif return res; } DLLEXPORT void __stdcall DebugSetMute() {} DLLEXPORT IDirect3D8* __stdcall Direct3DCreate8(UINT nSDKVersion) { IDirect3D8* pDirect3D = nullptr; dxvk::CreateD3D8(&pDirect3D); return pDirect3D; } } dxvk-2.6.1/src/d3d8/d3d8_multithread.cpp000066400000000000000000000002401477473124000177020ustar00rootroot00000000000000#include "d3d8_device.h" namespace dxvk { D3D8Multithread::D3D8Multithread( BOOL Protected) : m_protected( Protected ) { } }dxvk-2.6.1/src/d3d8/d3d8_multithread.h000066400000000000000000000024111477473124000173510ustar00rootroot00000000000000#pragma once #include "d3d8_include.h" namespace dxvk { /** * \brief Device lock * * Lightweight RAII wrapper that implements * a subset of the functionality provided by * \c std::unique_lock, with the goal of being * cheaper to construct and destroy. */ class D3D8DeviceLock { public: D3D8DeviceLock() : m_mutex(nullptr) { } D3D8DeviceLock(sync::RecursiveSpinlock& mutex) : m_mutex(&mutex) { mutex.lock(); } D3D8DeviceLock(D3D8DeviceLock&& other) : m_mutex(other.m_mutex) { other.m_mutex = nullptr; } D3D8DeviceLock& operator = (D3D8DeviceLock&& other) { if (m_mutex) m_mutex->unlock(); m_mutex = other.m_mutex; other.m_mutex = nullptr; return *this; } ~D3D8DeviceLock() { if (m_mutex != nullptr) m_mutex->unlock(); } private: sync::RecursiveSpinlock* m_mutex; }; /** * \brief D3D8 context lock */ class D3D8Multithread { public: D3D8Multithread( BOOL Protected); D3D8DeviceLock AcquireLock() { return m_protected ? D3D8DeviceLock(m_mutex) : D3D8DeviceLock(); } private: BOOL m_protected; sync::RecursiveSpinlock m_mutex; }; }dxvk-2.6.1/src/d3d8/d3d8_options.cpp000066400000000000000000000032621477473124000170620ustar00rootroot00000000000000#include "d3d8_options.h" #include "../d3d9/d3d9_bridge.h" #include "../util/config/config.h" #include "../util/util_string.h" #include namespace dxvk { static inline uint32_t parseDword(std::string_view str) { uint32_t value = std::numeric_limits::max(); std::from_chars(str.data(), str.data() + str.size(), value); return value; } void D3D8Options::parseVsDecl(const std::string& decl) { if (decl.empty()) return; if (decl.find_first_of("0123456789") == std::string::npos) { Logger::warn(str::format("D3D8: Invalid forceVsDecl value: ", decl)); Logger::warn("D3D8: Expected numbers."); return; } if (decl.find_first_of(":,;") == std::string::npos) { Logger::warn(str::format("D3D8: Invalid forceVsDecl value: ", decl)); Logger::warn("D3D8: Expected a comma-separated list of colon-separated number pairs."); return; } std::vector decls = str::split(decl, ":,;"); if (decls.size() % 2 != 0) { Logger::warn(str::format("D3D8: Invalid forceVsDecl value: ", decl)); Logger::warn("D3D8: Expected an even number of numbers."); return; } for (size_t i = 0; i < decls.size(); i += 2) { uint32_t reg = parseDword(decls[i]); uint32_t type = parseDword(decls[i+1]); if (reg > D3DVSDE_NORMAL2) { Logger::warn(str::format("D3D8: Invalid forceVsDecl register number: ", decls[i])); return; } if (type > D3DVSDT_SHORT4) { Logger::warn(str::format("D3D8: Invalid forceVsDecl type: ", decls[i+1])); return; } forceVsDecl.emplace_back(D3DVSDE_REGISTER(reg), D3DVSDT_TYPE(type)); } } } dxvk-2.6.1/src/d3d8/d3d8_options.h000066400000000000000000000025121477473124000165240ustar00rootroot00000000000000#pragma once #include "d3d8_include.h" #include "../d3d9/d3d9_bridge.h" #include "../util/config/config.h" namespace dxvk { struct D3D8Options { /// Override application vertex shader declarations. std::vector> forceVsDecl; /// Enable/disable the drawcall batcher. bool batching; /// Place all P8 textures in D3DPOOL_SCRATCH. bool placeP8InScratch; /// Ignore D3DLOCK_DISCARD for everything except D3DUSAGE_DYNAMIC + D3DUSAGE_WRITEONLY buffers. bool forceLegacyDiscard; /// Force D3DTTFF_PROJECTED for the necessary stages when a depth texture is bound to slot 0. bool shadowPerspectiveDivide; D3D8Options() {} D3D8Options(const Config& config) { auto forceVsDeclStr = config.getOption("d3d8.forceVsDecl", ""); batching = config.getOption ("d3d8.batching", false); placeP8InScratch = config.getOption ("d3d8.placeP8InScratch", false); forceLegacyDiscard = config.getOption ("d3d8.forceLegacyDiscard", false); shadowPerspectiveDivide = config.getOption ("d3d8.shadowPerspectiveDivide", false); parseVsDecl(forceVsDeclStr); } void parseVsDecl(const std::string& decl); }; } dxvk-2.6.1/src/d3d8/d3d8_resource.h000066400000000000000000000056221477473124000166650ustar00rootroot00000000000000#pragma once /** Implements IDirect3DResource8 * * - SetPrivateData, GetPrivateData, FreePrivateData * - SetPriority, GetPriority * * - Subclasses provide: PreLoad, GetType */ #include "d3d8_device_child.h" #include "../util/com/com_private_data.h" namespace dxvk { template class D3D8Resource : public D3D8DeviceChild { public: D3D8Resource(D3D8Device* pDevice, D3DPOOL Pool, Com&& Object) : D3D8DeviceChild(pDevice, std::move(Object)) , m_pool ( Pool ) , m_priority ( 0 ) { } HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID refguid, const void* pData, DWORD SizeOfData, DWORD Flags) final { HRESULT hr; if (Flags & D3DSPD_IUNKNOWN) { if(unlikely(SizeOfData != sizeof(IUnknown*))) return D3DERR_INVALIDCALL; IUnknown* unknown = const_cast( reinterpret_cast(pData)); hr = m_privateData.setInterface( refguid, unknown); } else hr = m_privateData.setData( refguid, SizeOfData, pData); if (unlikely(FAILED(hr))) return D3DERR_INVALIDCALL; return D3D_OK; } HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID refguid, void* pData, DWORD* pSizeOfData) final { if (unlikely(pData == nullptr && pSizeOfData == nullptr)) return D3DERR_NOTFOUND; HRESULT hr = m_privateData.getData( refguid, reinterpret_cast(pSizeOfData), pData); if (unlikely(FAILED(hr))) { if(hr == DXGI_ERROR_MORE_DATA) return D3DERR_MOREDATA; else if (hr == DXGI_ERROR_NOT_FOUND) return D3DERR_NOTFOUND; else return D3DERR_INVALIDCALL; } return D3D_OK; } HRESULT STDMETHODCALLTYPE FreePrivateData(REFGUID refguid) final { HRESULT hr = m_privateData.setData(refguid, 0, nullptr); if (unlikely(FAILED(hr))) return D3DERR_INVALIDCALL; return D3D_OK; } DWORD STDMETHODCALLTYPE SetPriority(DWORD PriorityNew) { // Priority can only be set for D3DPOOL_MANAGED resources if (likely(m_pool == D3DPOOL_MANAGED)) { DWORD oldPriority = m_priority; m_priority = PriorityNew; return oldPriority; } return m_priority; } DWORD STDMETHODCALLTYPE GetPriority() { return m_priority; } virtual IUnknown* GetInterface(REFIID riid) override try { return D3D8DeviceChild::GetInterface(riid); } catch (const DxvkError& e) { if (riid == __uuidof(IDirect3DResource8)) return this; throw e; } protected: const D3DPOOL m_pool; DWORD m_priority; private: ComPrivateData m_privateData; }; }dxvk-2.6.1/src/d3d8/d3d8_shader.cpp000066400000000000000000000303571477473124000166420ustar00rootroot00000000000000#include "d3d8_shader.h" #define VSD_SHIFT_MASK(token, field) ((token & field ## MASK) >> field ## SHIFT) #define VSD_ENCODE(token, field) ((token << field ## _SHIFT) & field ## _MASK) // Magic number from D3DVSD_SKIP(...) #define VSD_SKIP_FLAG 0x10000000 // This bit is set on all parameter (non-instruction) tokens. #define VS_BIT_PARAM 0x80000000 namespace dxvk { static constexpr int D3D8_NUM_VERTEX_INPUT_REGISTERS = 17; /** * Standard mapping of vertex input registers v0-v16 to D3D9 usages and usage indices * (See D3DVSDE_REGISTER values in d3d8types.h or DirectX 8 docs for vertex shader input registers vN) * * \cite https://learn.microsoft.com/en-us/windows/win32/direct3d9/mapping-between-a-directx-9-declaration-and-directx-8 */ static constexpr BYTE D3D8_VERTEX_INPUT_REGISTERS[D3D8_NUM_VERTEX_INPUT_REGISTERS][2] = { {d3d9::D3DDECLUSAGE_POSITION, 0}, // dcl_position v0 {d3d9::D3DDECLUSAGE_BLENDWEIGHT, 0}, // dcl_blendweight v1 {d3d9::D3DDECLUSAGE_BLENDINDICES, 0}, // dcl_blendindices v2 {d3d9::D3DDECLUSAGE_NORMAL, 0}, // dcl_normal v3 {d3d9::D3DDECLUSAGE_PSIZE, 0}, // dcl_psize v4 {d3d9::D3DDECLUSAGE_COLOR, 0}, // dcl_color v5 ; diffuse {d3d9::D3DDECLUSAGE_COLOR, 1}, // dcl_color1 v6 ; specular {d3d9::D3DDECLUSAGE_TEXCOORD, 0}, // dcl_texcoord0 v7 {d3d9::D3DDECLUSAGE_TEXCOORD, 1}, // dcl_texcoord1 v8 {d3d9::D3DDECLUSAGE_TEXCOORD, 2}, // dcl_texcoord2 v9 {d3d9::D3DDECLUSAGE_TEXCOORD, 3}, // dcl_texcoord3 v10 {d3d9::D3DDECLUSAGE_TEXCOORD, 4}, // dcl_texcoord4 v11 {d3d9::D3DDECLUSAGE_TEXCOORD, 5}, // dcl_texcoord5 v12 {d3d9::D3DDECLUSAGE_TEXCOORD, 6}, // dcl_texcoord6 v13 {d3d9::D3DDECLUSAGE_TEXCOORD, 7}, // dcl_texcoord7 v14 {d3d9::D3DDECLUSAGE_POSITION, 1}, // dcl_position1 v15 ; position 2 {d3d9::D3DDECLUSAGE_NORMAL, 1}, // dcl_normal1 v16 ; normal 2 }; /** Width in bytes of each d3d9::D3DDECLTYPE or d3d8 D3DVSDT_TYPE */ static constexpr BYTE D3D9_DECL_TYPE_SIZES[d3d9::MAXD3DDECLTYPE + 1] = { 4, // FLOAT1 8, // FLOAT2 12, // FLOAT3 16, // FLOAT4 4, // D3DCOLOR 4, // UBYTE4 4, // SHORT2 8, // SHORT4 // The following are for vs2.0+ // 4, // UBYTE4N 4, // SHORT2N 8, // SHORT4N 4, // USHORT2N 8, // USHORT4N 6, // UDEC3 6, // DEC3N 8, // FLOAT16_2 16, // FLOAT16_4 0 // UNUSED }; /** * Encodes a \ref DxsoShaderInstruction * * \param [in] opcode DxsoOpcode * \cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/instruction-token */ constexpr DWORD encodeInstruction(d3d9::D3DSHADER_INSTRUCTION_OPCODE_TYPE opcode) { DWORD token = 0; token |= opcode & 0xFFFF; // bits 0:15 return token; } /** * Encodes a \ref DxsoRegister * * \param [in] regType DxsoRegisterType * \cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/destination-parameter-token */ constexpr DWORD encodeDestRegister(d3d9::D3DSHADER_PARAM_REGISTER_TYPE type, UINT reg) { DWORD token = 0; token |= reg & 0x7FF; // bits 0:10 num token |= ((type & 0x07) << 28); // bits 28:30 type[0:2] token |= ((type & 0x18) >> 3) << 11; // bits 11:12 type[3:4] // UINT addrMode : 1; // bit 13 hasRelative token |= 0b1111 << 16; // bits 16:19 DxsoRegMask // UINT resultModifier : 3; // bits 20:23 // UINT resultShift : 3; // bits 24:27 token |= 1 << 31; // bit 31 always 1 return token; } /** * Encodes a \ref DxsoDeclaration * * \cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/dcl-instruction */ constexpr DWORD encodeDeclaration(d3d9::D3DDECLUSAGE usage, DWORD index) { DWORD token = 0; token |= VSD_ENCODE(usage, D3DSP_DCL_USAGE); // bits 0:4 DxsoUsage (TODO: missing MSB) token |= VSD_ENCODE(index, D3DSP_DCL_USAGEINDEX); // bits 16:19 usageIndex token |= 1 << 31; // bit 31 always 1 return token; } /** * Validates and converts a D3D8 vertex shader * + declaration to a D3D9 vertex shader + declaration. */ HRESULT TranslateVertexShader8( const DWORD* pDeclaration, const DWORD* pFunction, const D3D8Options& options, D3D9VertexShaderCode& pTranslatedVS) { using d3d9::D3DDECLTYPE; using d3d9::D3DDECLTYPE_UNUSED; HRESULT res = D3D_OK; std::vector& tokens = pTranslatedVS.function; std::vector defs; // Constant definitions // shaderInputRegisters: // set bit N to enable input register vN DWORD shaderInputRegisters = 0; d3d9::D3DVERTEXELEMENT9* vertexElements = pTranslatedVS.declaration; unsigned int elementIdx = 0; // These are used for pDeclaration and pFunction int i = 0; DWORD token; std::stringstream dbg; dbg << "D3D8: Vertex Declaration Tokens:\n\t"; WORD currentStream = 0; WORD currentOffset = 0; auto addVertexElement = [&] (D3DVSDE_REGISTER reg, D3DVSDT_TYPE type) { vertexElements[elementIdx].Stream = currentStream; vertexElements[elementIdx].Offset = currentOffset; vertexElements[elementIdx].Method = d3d9::D3DDECLMETHOD_DEFAULT; vertexElements[elementIdx].Type = D3DDECLTYPE(type); // (D3DVSDT_TYPE values map directly to D3DDECLTYPE) vertexElements[elementIdx].Usage = D3D8_VERTEX_INPUT_REGISTERS[reg][0]; vertexElements[elementIdx].UsageIndex = D3D8_VERTEX_INPUT_REGISTERS[reg][1]; // Increase stream offset currentOffset += D3D9_DECL_TYPE_SIZES[type]; // Enable register vn shaderInputRegisters |= 1 << reg; // Finished with this element elementIdx++; }; // Remap d3d8 decl tokens to d3d9 vertex elements, // and enable bits on shaderInputRegisters for each. if (options.forceVsDecl.size() == 0) do { token = pDeclaration[i++]; D3DVSD_TOKENTYPE tokenType = D3DVSD_TOKENTYPE(VSD_SHIFT_MASK(token, D3DVSD_TOKENTYPE)); switch (tokenType) { case D3DVSD_TOKEN_NOP: dbg << "NOP"; break; case D3DVSD_TOKEN_STREAM: { dbg << "STREAM "; // TODO: D3DVSD_STREAM_TESS if (token & D3DVSD_STREAMTESSMASK) { dbg << "TESS"; } DWORD streamNum = VSD_SHIFT_MASK(token, D3DVSD_STREAMNUMBER); currentStream = WORD(streamNum); currentOffset = 0; // reset offset dbg << ", num=" << streamNum; break; } case D3DVSD_TOKEN_STREAMDATA: { dbg << "STREAMDATA "; // D3DVSD_SKIP if (token & VSD_SKIP_FLAG) { auto skipCount = VSD_SHIFT_MASK(token, D3DVSD_SKIPCOUNT); dbg << "SKIP " << " count=" << skipCount; currentOffset += WORD(skipCount) * sizeof(DWORD); break; } // D3DVSD_REG DWORD dataLoadType = VSD_SHIFT_MASK(token, D3DVSD_DATALOADTYPE); if ( dataLoadType == 0 ) { // vertex D3DVSDT_TYPE type = D3DVSDT_TYPE(VSD_SHIFT_MASK(token, D3DVSD_DATATYPE)); D3DVSDE_REGISTER reg = D3DVSDE_REGISTER(VSD_SHIFT_MASK(token, D3DVSD_VERTEXREG)); // FVF normals are expected to only have 3 components if (unlikely(pFunction == nullptr && reg == D3DVSDE_NORMAL && type != D3DVSDT_FLOAT3)) { Logger::err("D3D8Device::CreateVertexShader: Invalid FVF declaration: D3DVSDE_NORMAL must use D3DVSDT_FLOAT3"); return D3DERR_INVALIDCALL; } addVertexElement(reg, type); dbg << "type=" << type << ", register=" << reg; } else { // TODO: When would this bit be 1? dbg << "D3DVSD_DATALOADTYPE " << dataLoadType; } break; } case D3DVSD_TOKEN_TESSELLATOR: dbg << "TESSELLATOR " << std::hex << token; // TODO: D3DVSD_TOKEN_TESSELLATOR break; case D3DVSD_TOKEN_CONSTMEM: { dbg << "CONSTMEM "; DWORD count = VSD_SHIFT_MASK(token, D3DVSD_CONSTCOUNT); DWORD regCount = count * 4; DWORD addr = VSD_SHIFT_MASK(token, D3DVSD_CONSTADDRESS); DWORD rs = VSD_SHIFT_MASK(token, D3DVSD_CONSTRS); dbg << "count=" << count << ", addr=" << addr << ", rs=" << rs; // Add a DEF instruction for each constant for (DWORD j = 0; j < regCount; j += 4) { defs.push_back(encodeInstruction(d3d9::D3DSIO_DEF)); defs.push_back(encodeDestRegister(d3d9::D3DSPR_CONST2, addr)); defs.push_back(pDeclaration[i+j+0]); defs.push_back(pDeclaration[i+j+1]); defs.push_back(pDeclaration[i+j+2]); defs.push_back(pDeclaration[i+j+3]); addr++; } i += regCount; break; } case D3DVSD_TOKEN_EXT: { dbg << "EXT " << std::hex << token << " "; DWORD extInfo = VSD_SHIFT_MASK(token, D3DVSD_EXTINFO); DWORD extCount = VSD_SHIFT_MASK(token, D3DVSD_EXTCOUNT); dbg << "info=" << extInfo << ", count=" << extCount; break; } case D3DVSD_TOKEN_END: { vertexElements[elementIdx++] = D3DDECL_END(); dbg << "END"; break; } default: dbg << "UNKNOWN TYPE"; break; } dbg << "\n\t"; //dbg << std::hex << token << " "; } while (token != D3DVSD_END()); Logger::debug(dbg.str()); // If forceVsDecl is set, use that decl instead. if (options.forceVsDecl.size() > 0) { for (auto [reg, type] : options.forceVsDecl) { addVertexElement(reg, type); } vertexElements[elementIdx++] = D3DDECL_END(); } if (pFunction != nullptr) { // Copy first token (version) tokens.push_back(pFunction[0]); DWORD vsMajor = D3DSHADER_VERSION_MAJOR(pFunction[0]); DWORD vsMinor = D3DSHADER_VERSION_MINOR(pFunction[0]); Logger::debug(str::format("VS version: ", vsMajor, ".", vsMinor)); // Insert dcl instructions for (int vn = 0; vn < D3D8_NUM_VERTEX_INPUT_REGISTERS; vn++) { // If bit N is set then we need to dcl register vN if ((shaderInputRegisters & (1 << vn)) != 0) { Logger::debug(str::format("\tShader Input Regsiter: v", vn)); DWORD usage = D3D8_VERTEX_INPUT_REGISTERS[vn][0]; DWORD index = D3D8_VERTEX_INPUT_REGISTERS[vn][1]; tokens.push_back(encodeInstruction(d3d9::D3DSIO_DCL)); // dcl opcode tokens.push_back(encodeDeclaration(d3d9::D3DDECLUSAGE(usage), index)); // usage token tokens.push_back(encodeDestRegister(d3d9::D3DSPR_INPUT, vn)); // dest register num } } // Copy constant defs for (DWORD def : defs) { tokens.push_back(def); } // Copy shader tokens from input, // skip first token (we already copied it) i = 1; do { token = pFunction[i++]; DWORD opcode = token & D3DSI_OPCODE_MASK; // Instructions if ((token & VS_BIT_PARAM) == 0) { // Swizzle fixup for opcodes that require explicit use of a replicate swizzle. if (opcode == D3DSIO_RSQ || opcode == D3DSIO_RCP || opcode == D3DSIO_EXP || opcode == D3DSIO_LOG || opcode == D3DSIO_EXPP || opcode == D3DSIO_LOGP) { tokens.push_back(token); // instr tokens.push_back(token = pFunction[i++]); // dest token = pFunction[i++]; // src0 // If no swizzling is done, then use the w-component. // See d8vk#43 for more information as this may need to change in some cases. if (((token & D3DVS_NOSWIZZLE) == D3DVS_NOSWIZZLE)) { token &= ~D3DVS_SWIZZLE_MASK; token |= (D3DVS_X_W | D3DVS_Y_W | D3DVS_Z_W | D3DVS_W_W); } } } tokens.push_back(token); } while (token != D3DVS_END()); } return res; } } dxvk-2.6.1/src/d3d8/d3d8_shader.h000066400000000000000000000006551477473124000163050ustar00rootroot00000000000000#pragma once #include "d3d8_include.h" #include "d3d8_options.h" namespace dxvk { struct D3D9VertexShaderCode { d3d9::D3DVERTEXELEMENT9 declaration[MAXD3DDECLLENGTH + 1]; std::vector function; }; HRESULT TranslateVertexShader8( const DWORD* pDeclaration, const DWORD* pFunction, const D3D8Options& overrides, D3D9VertexShaderCode& pTranslatedVS); }dxvk-2.6.1/src/d3d8/d3d8_state_block.cpp000066400000000000000000000067571477473124000176750ustar00rootroot00000000000000#include "d3d8_device.h" #include "d3d8_state_block.h" namespace dxvk { D3D8StateBlock::D3D8StateBlock( D3D8Device* pDevice, D3DSTATEBLOCKTYPE Type, Com&& pStateBlock) : m_device(pDevice) , m_stateBlock(std::move(pStateBlock)) , m_type(Type) , m_isSWVP(pDevice->GetD3D9()->GetSoftwareVertexProcessing()) { if (Type == D3DSBT_VERTEXSTATE || Type == D3DSBT_ALL) { // Lights, D3DTSS_TEXCOORDINDEX and D3DTSS_TEXTURETRANSFORMFLAGS, // vertex shader, VS constants, and various render states. m_capture.vs = true; } if (Type == D3DSBT_PIXELSTATE || Type == D3DSBT_ALL) { // Pixel shader, PS constants, and various RS/TSS states. m_capture.ps = true; } if (Type == D3DSBT_ALL) { m_capture.indices = true; m_capture.swvp = true; m_capture.textures.setAll(); m_capture.streams.setAll(); } m_textures.fill(nullptr); m_streams.fill(D3D8VBOP()); } // Construct a state block without a D3D9 object D3D8StateBlock::D3D8StateBlock(D3D8Device* pDevice) : D3D8StateBlock(pDevice, D3DSTATEBLOCKTYPE(0), nullptr) { } // Attach a D3D9 object to a state block that doesn't have one yet void D3D8StateBlock::SetD3D9(Com&& pStateBlock) { if (likely(m_stateBlock == nullptr)) { m_stateBlock = std::move(pStateBlock); } else { Logger::err("D3D8StateBlock::SetD3D9: m_stateBlock has already been initialized"); } } HRESULT D3D8StateBlock::Capture() { if (unlikely(m_stateBlock == nullptr)) return D3DERR_INVALIDCALL; if (m_capture.vs) m_device->GetVertexShader(&m_vertexShader); if (m_capture.ps) m_device->GetPixelShader(&m_pixelShader); for (DWORD stage = 0; stage < m_textures.size(); stage++) { if (m_capture.textures.get(stage)) m_textures[stage] = m_device->m_textures[stage].ptr(); } for (DWORD stream = 0; stream < m_streams.size(); stream++) { if (m_capture.streams.get(stream)) { m_streams[stream].buffer = m_device->m_streams[stream].buffer.ptr(); m_streams[stream].stride = m_device->m_streams[stream].stride; } } if (m_capture.indices) { m_baseVertexIndex = m_device->m_baseVertexIndex; m_indices = m_device->m_indices.ptr(); } if (m_capture.swvp) { DWORD swvpState; m_device->GetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, &swvpState); m_isSWVP = static_cast(swvpState); } return m_stateBlock->Capture(); } HRESULT D3D8StateBlock::Apply() { if (unlikely(m_stateBlock == nullptr)) return D3DERR_INVALIDCALL; HRESULT res = m_stateBlock->Apply(); if (m_capture.vs) m_device->SetVertexShader(m_vertexShader); if (m_capture.ps) m_device->SetPixelShader(m_pixelShader); for (DWORD stage = 0; stage < m_textures.size(); stage++) { if (m_capture.textures.get(stage)) m_device->SetTexture(stage, m_textures[stage]); } for (DWORD stream = 0; stream < m_streams.size(); stream++) { if (m_capture.streams.get(stream)) m_device->SetStreamSource(stream, m_streams[stream].buffer, m_streams[stream].stride); } if (m_capture.indices) m_device->SetIndices(m_indices, m_baseVertexIndex); // This was a very easy footgun for D3D8 applications. if (m_capture.swvp) m_device->SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, static_cast(m_isSWVP)); return res; } } dxvk-2.6.1/src/d3d8/d3d8_state_block.h000066400000000000000000000057521477473124000173340ustar00rootroot00000000000000#pragma once #include "d3d8_caps.h" #include "d3d8_include.h" #include "d3d8_device.h" #include "d3d8_device_child.h" #include "../util/util_bit.h" #include namespace dxvk { struct D3D8StateCapture { bool vs : 1; bool ps : 1; bool indices : 1; bool swvp : 1; bit::bitset textures; bit::bitset streams; D3D8StateCapture() : vs(false) , ps(false) , indices(false) , swvp(false) { // Ensure all bits are initialized to false textures.clearAll(); streams.clearAll(); } }; // Wrapper class for D3D9 state blocks. Captures D3D8-specific state. class D3D8StateBlock { public: D3D8StateBlock( D3D8Device* pDevice, D3DSTATEBLOCKTYPE Type, Com&& pStateBlock); D3D8StateBlock(D3D8Device* pDevice); void SetD3D9(Com&& pStateBlock); HRESULT Capture(); HRESULT Apply(); inline HRESULT SetVertexShader(DWORD Handle) { m_vertexShader = Handle; m_capture.vs = true; return D3D_OK; } inline HRESULT SetPixelShader(DWORD Handle) { m_pixelShader = Handle; m_capture.ps = true; return D3D_OK; } inline HRESULT SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) { m_textures[Stage] = pTexture; m_capture.textures.set(Stage, true); return D3D_OK; } inline HRESULT SetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer8* pStreamData, UINT Stride) { m_streams[StreamNumber].buffer = pStreamData; // The previous stride is preserved if pStreamData is NULL if (likely(pStreamData != nullptr)) m_streams[StreamNumber].stride = Stride; m_capture.streams.set(StreamNumber, true); return D3D_OK; } inline HRESULT SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex) { m_indices = pIndexData; m_baseVertexIndex = BaseVertexIndex; m_capture.indices = true; return D3D_OK; } inline HRESULT SetSoftwareVertexProcessing(bool value) { m_isSWVP = value; m_capture.swvp = true; return D3D_OK; } private: D3D8Device* m_device; Com m_stateBlock; D3DSTATEBLOCKTYPE m_type; struct D3D8VBOP { IDirect3DVertexBuffer8* buffer = nullptr; UINT stride = 0; }; // State Data // D3D8StateCapture m_capture; DWORD m_vertexShader = 0; DWORD m_pixelShader = 0; std::array m_textures; std::array m_streams; IDirect3DIndexBuffer8* m_indices = nullptr; UINT m_baseVertexIndex = 0; bool m_isSWVP; // D3DRS_SOFTWAREVERTEXPROCESSING }; }dxvk-2.6.1/src/d3d8/d3d8_subresource.h000066400000000000000000000027321477473124000173760ustar00rootroot00000000000000#pragma once #include "d3d8_resource.h" namespace dxvk { // Base class for Surfaces and Volumes, // which can be attached to Textures. template class D3D8Subresource : public D3D8Resource { using Resource = D3D8Resource; public: D3D8Subresource( D3D8Device* pDevice, const D3DPOOL Pool, Com&& Object, IDirect3DBaseTexture8* pBaseTexture) : Resource(pDevice, Pool, std::move(Object)), m_container(pBaseTexture) { } // Refing subresources implicitly refs the container texture, ULONG STDMETHODCALLTYPE AddRef() final { if (m_container != nullptr) return m_container->AddRef(); return Resource::AddRef(); } // and releasing them implicitly releases the texture. ULONG STDMETHODCALLTYPE Release() final { if (m_container != nullptr) return m_container->Release(); return Resource::Release(); } // Clients can grab the container if they want HRESULT STDMETHODCALLTYPE GetContainer(REFIID riid, void** ppContainer) final { if (m_container != nullptr) return m_container->QueryInterface(riid, ppContainer); return this->GetDevice()->QueryInterface(riid, ppContainer); } inline IDirect3DBaseTexture8* GetBaseTexture() { return m_container; } protected: IDirect3DBaseTexture8* m_container; }; }dxvk-2.6.1/src/d3d8/d3d8_surface.cpp000066400000000000000000000042551477473124000170220ustar00rootroot00000000000000#include "d3d8_surface.h" #include "d3d8_device.h" #include "d3d8_d3d9_util.h" namespace dxvk { D3D8Surface::D3D8Surface( D3D8Device* pDevice, const D3DPOOL Pool, IDirect3DBaseTexture8* pTexture, Com&& pSurface) : D3D8SurfaceBase (pDevice, Pool, std::move(pSurface), pTexture) { } // A surface does not need to be attached to a texture D3D8Surface::D3D8Surface( D3D8Device* pDevice, const D3DPOOL Pool, Com&& pSurface) : D3D8Surface (pDevice, Pool, nullptr, std::move(pSurface)) { } HRESULT STDMETHODCALLTYPE D3D8Surface::GetDesc(D3DSURFACE_DESC* pDesc) { if (unlikely(pDesc == nullptr)) return D3DERR_INVALIDCALL; d3d9::D3DSURFACE_DESC desc; HRESULT res = GetD3D9()->GetDesc(&desc); if (likely(SUCCEEDED(res))) ConvertSurfaceDesc8(&desc, pDesc); return res; } HRESULT STDMETHODCALLTYPE D3D8Surface::LockRect( D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) { return GetD3D9()->LockRect((d3d9::D3DLOCKED_RECT*)pLockedRect, pRect, Flags); } HRESULT STDMETHODCALLTYPE D3D8Surface::UnlockRect() { return GetD3D9()->UnlockRect(); } // TODO: Consider creating only one texture to // encompass all surface levels of a texture. Com D3D8Surface::GetBlitImage() { if (unlikely(m_blitImage == nullptr)) { m_blitImage = CreateBlitImage(); } return m_blitImage; } Com D3D8Surface::CreateBlitImage() { d3d9::D3DSURFACE_DESC desc; GetD3D9()->GetDesc(&desc); // NOTE: This adds a D3DPOOL_DEFAULT resource to the // device, which counts as losable during device reset Com image = nullptr; HRESULT res = GetParent()->GetD3D9()->CreateRenderTarget( desc.Width, desc.Height, desc.Format, d3d9::D3DMULTISAMPLE_NONE, 0, FALSE, &image, NULL); if (FAILED(res)) throw DxvkError("D3D8: Failed to create blit image"); return image; } }dxvk-2.6.1/src/d3d8/d3d8_surface.h000066400000000000000000000026551477473124000164710ustar00rootroot00000000000000#pragma once #include "d3d8_include.h" #include "d3d8_subresource.h" namespace dxvk { // Note: IDirect3DSurface8 does not actually inherit from IDirect3DResource8, // however it does expose serveral of the methods typically found part of // IDirect3DResource8, such as Set/Get/FreePrivateData, so model it as such. using D3D8SurfaceBase = D3D8Subresource; class D3D8Surface final : public D3D8SurfaceBase { public: D3D8Surface( D3D8Device* pDevice, const D3DPOOL Pool, IDirect3DBaseTexture8* pTexture, Com&& pSurface); D3D8Surface( D3D8Device* pDevice, const D3DPOOL Pool, Com&& pSurface); HRESULT STDMETHODCALLTYPE GetDesc(D3DSURFACE_DESC* pDesc) final; HRESULT STDMETHODCALLTYPE LockRect( D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) final; HRESULT STDMETHODCALLTYPE UnlockRect() final; /** * \brief Allocate or reuse an image of the same size * as this texture for performing blit into system mem. */ Com GetBlitImage(); private: Com CreateBlitImage(); Com m_blitImage; }; }dxvk-2.6.1/src/d3d8/d3d8_swapchain.cpp000066400000000000000000000026151477473124000173450ustar00rootroot00000000000000#include "d3d8_swapchain.h" namespace dxvk { D3D8SwapChain::D3D8SwapChain( D3D8Device* pDevice, D3DPRESENT_PARAMETERS* pPresentationParameters, Com&& pSwapChain) : D3D8SwapChainBase(pDevice, std::move(pSwapChain)) { m_backBuffers.resize(pPresentationParameters->BackBufferCount); } HRESULT STDMETHODCALLTYPE D3D8SwapChain::Present(const RECT *src, const RECT *dst, HWND hWnd, const RGNDATA *dirtyRegion) { return GetD3D9()->Present(src, dst, hWnd, dirtyRegion, 0); } HRESULT STDMETHODCALLTYPE D3D8SwapChain::GetBackBuffer( UINT BackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface8** ppBackBuffer) { if (unlikely(ppBackBuffer == nullptr)) return D3DERR_INVALIDCALL; // Same logic as in D3D8Device::GetBackBuffer if (BackBuffer >= m_backBuffers.size() || m_backBuffers[BackBuffer] == nullptr) { Com pSurface9; HRESULT res = GetD3D9()->GetBackBuffer(BackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9); if (likely(SUCCEEDED(res))) { m_backBuffers[BackBuffer] = new D3D8Surface(GetParent(), D3DPOOL_DEFAULT, std::move(pSurface9)); *ppBackBuffer = m_backBuffers[BackBuffer].ref(); } return res; } *ppBackBuffer = m_backBuffers[BackBuffer].ref(); return D3D_OK; } }dxvk-2.6.1/src/d3d8/d3d8_swapchain.h000066400000000000000000000014621477473124000170110ustar00rootroot00000000000000#pragma once #include "d3d8_device_child.h" #include "d3d8_surface.h" namespace dxvk { using D3D8SwapChainBase = D3D8DeviceChild; class D3D8SwapChain final : public D3D8SwapChainBase { public: D3D8SwapChain( D3D8Device* pDevice, D3DPRESENT_PARAMETERS* pPresentationParameters, Com&& pSwapChain); HRESULT STDMETHODCALLTYPE Present(const RECT *src, const RECT *dst, HWND hWnd, const RGNDATA *dirtyRegion) final; HRESULT STDMETHODCALLTYPE GetBackBuffer( UINT BackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface8** ppBackBuffer) final; private: std::vector> m_backBuffers; }; }dxvk-2.6.1/src/d3d8/d3d8_texture.cpp000066400000000000000000000113431477473124000170660ustar00rootroot00000000000000#include "d3d8_texture.h" #include "d3d8_d3d9_util.h" namespace dxvk { // D3D8Texture2D D3D8Texture2D::D3D8Texture2D( D3D8Device* pDevice, const D3DPOOL Pool, Com&& pTexture) : D3D8Texture2DBase(pDevice, Pool, std::move(pTexture), pTexture->GetLevelCount()) { } D3DRESOURCETYPE STDMETHODCALLTYPE D3D8Texture2D::GetType() { return D3DRTYPE_TEXTURE; } HRESULT STDMETHODCALLTYPE D3D8Texture2D::GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) { if (unlikely(pDesc == nullptr)) return D3DERR_INVALIDCALL; d3d9::D3DSURFACE_DESC surf; HRESULT res = GetD3D9()->GetLevelDesc(Level, &surf); if (likely(SUCCEEDED(res))) ConvertSurfaceDesc8(&surf, pDesc); return res; } HRESULT STDMETHODCALLTYPE D3D8Texture2D::GetSurfaceLevel(UINT Level, IDirect3DSurface8** ppSurfaceLevel) { return GetSubresource(Level, ppSurfaceLevel); } HRESULT STDMETHODCALLTYPE D3D8Texture2D::LockRect( UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) { return GetD3D9()->LockRect(Level, reinterpret_cast(pLockedRect), pRect, Flags); } HRESULT STDMETHODCALLTYPE D3D8Texture2D::UnlockRect(UINT Level) { return GetD3D9()->UnlockRect(Level); } HRESULT STDMETHODCALLTYPE D3D8Texture2D::AddDirtyRect(CONST RECT* pDirtyRect) { return GetD3D9()->AddDirtyRect(pDirtyRect); } // D3D8Texture3D D3D8Texture3D::D3D8Texture3D( D3D8Device* pDevice, const D3DPOOL Pool, Com&& pVolumeTexture) : D3D8Texture3DBase(pDevice, Pool, std::move(pVolumeTexture), pVolumeTexture->GetLevelCount()) {} D3DRESOURCETYPE STDMETHODCALLTYPE D3D8Texture3D::GetType() { return D3DRTYPE_VOLUMETEXTURE; } HRESULT STDMETHODCALLTYPE D3D8Texture3D::GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc) { if (unlikely(pDesc == nullptr)) return D3DERR_INVALIDCALL; d3d9::D3DVOLUME_DESC vol; HRESULT res = GetD3D9()->GetLevelDesc(Level, &vol); if (likely(SUCCEEDED(res))) ConvertVolumeDesc8(&vol, pDesc); return res; } HRESULT STDMETHODCALLTYPE D3D8Texture3D::GetVolumeLevel(UINT Level, IDirect3DVolume8** ppVolumeLevel) { return GetSubresource(Level, ppVolumeLevel); } HRESULT STDMETHODCALLTYPE D3D8Texture3D::LockBox( UINT Level, D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) { return GetD3D9()->LockBox( Level, reinterpret_cast(pLockedBox), reinterpret_cast(pBox), Flags ); } HRESULT STDMETHODCALLTYPE D3D8Texture3D::UnlockBox(UINT Level) { return GetD3D9()->UnlockBox(Level); } HRESULT STDMETHODCALLTYPE D3D8Texture3D::AddDirtyBox(CONST D3DBOX* pDirtyBox) { return GetD3D9()->AddDirtyBox(reinterpret_cast(pDirtyBox)); } // D3D8TextureCube D3D8TextureCube::D3D8TextureCube( D3D8Device* pDevice, const D3DPOOL Pool, Com&& pTexture) : D3D8TextureCubeBase(pDevice, Pool, std::move(pTexture), pTexture->GetLevelCount() * CUBE_FACES) { } D3DRESOURCETYPE STDMETHODCALLTYPE D3D8TextureCube::GetType() { return D3DRTYPE_CUBETEXTURE; } HRESULT STDMETHODCALLTYPE D3D8TextureCube::GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) { if (unlikely(pDesc == nullptr)) return D3DERR_INVALIDCALL; d3d9::D3DSURFACE_DESC surf; HRESULT res = GetD3D9()->GetLevelDesc(Level, &surf); if (likely(SUCCEEDED(res))) ConvertSurfaceDesc8(&surf, pDesc); return res; } HRESULT STDMETHODCALLTYPE D3D8TextureCube::GetCubeMapSurface( D3DCUBEMAP_FACES Face, UINT Level, IDirect3DSurface8** ppSurfaceLevel) { return GetSubresource((Level * CUBE_FACES) + Face, ppSurfaceLevel); } HRESULT STDMETHODCALLTYPE D3D8TextureCube::LockRect( D3DCUBEMAP_FACES Face, UINT Level, D3DLOCKED_RECT* pLockedRect, const RECT* pRect, DWORD Flags) { return GetD3D9()->LockRect( d3d9::D3DCUBEMAP_FACES(Face), Level, reinterpret_cast(pLockedRect), pRect, Flags); } HRESULT STDMETHODCALLTYPE D3D8TextureCube::UnlockRect(D3DCUBEMAP_FACES Face, UINT Level) { return GetD3D9()->UnlockRect(d3d9::D3DCUBEMAP_FACES(Face), Level); } HRESULT STDMETHODCALLTYPE D3D8TextureCube::AddDirtyRect(D3DCUBEMAP_FACES Face, const RECT* pDirtyRect) { return GetD3D9()->AddDirtyRect(d3d9::D3DCUBEMAP_FACES(Face), pDirtyRect); } }dxvk-2.6.1/src/d3d8/d3d8_texture.h000066400000000000000000000137261477473124000165420ustar00rootroot00000000000000#pragma once #include "d3d8_resource.h" #include "d3d8_surface.h" #include "d3d8_volume.h" #include #include namespace dxvk { template class D3D8BaseTexture : public D3D8Resource { public: constexpr static UINT CUBE_FACES = 6; using SubresourceType8 = typename SubresourceType::D3D8; using SubresourceType9 = typename SubresourceType::D3D9; D3D8BaseTexture( D3D8Device* pDevice, const D3DPOOL Pool, Com&& pBaseTexture, UINT SubresourceCount) : D3D8Resource ( pDevice, Pool, std::move(pBaseTexture) ) { m_subresources.resize(SubresourceCount, nullptr); } ~D3D8BaseTexture() { for (size_t i = 0; i < m_subresources.size(); i++) m_subresources[i] = nullptr; } virtual IUnknown* GetInterface(REFIID riid) final override try { return D3D8Resource::GetInterface(riid); } catch (const DxvkError& e) { if (riid == __uuidof(IDirect3DBaseTexture8)) return this; throw e; } void STDMETHODCALLTYPE PreLoad() final { this->GetD3D9()->PreLoad(); } DWORD STDMETHODCALLTYPE SetLOD(DWORD LODNew) final { return this->GetD3D9()->SetLOD(LODNew); } DWORD STDMETHODCALLTYPE GetLOD() final { return this->GetD3D9()->GetLOD(); } DWORD STDMETHODCALLTYPE GetLevelCount() final { return this->GetD3D9()->GetLevelCount(); } protected: HRESULT STDMETHODCALLTYPE GetSubresource(UINT Index, SubresourceType8** ppSubresource) { InitReturnPtr(ppSubresource); if (unlikely(ppSubresource == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(Index >= m_subresources.size())) return D3DERR_INVALIDCALL; if (m_subresources[Index] == nullptr) { try { Com subresource = LookupSubresource(Index); // Cache the subresource m_subresources[Index] = new SubresourceType(this->m_parent, this->m_pool, this, std::move(subresource)); } catch (const DxvkError& e) { Logger::warn(e.message()); return D3DERR_INVALIDCALL; } } *ppSubresource = m_subresources[Index].ref(); return D3D_OK; } private: Com LookupSubresource(UINT Index) { Com ptr = nullptr; HRESULT res = D3DERR_INVALIDCALL; if constexpr (std::is_same_v) { res = this->GetD3D9()->GetSurfaceLevel(Index, &ptr); } else if constexpr (std::is_same_v) { res = this->GetD3D9()->GetVolumeLevel(Index, &ptr); } else if constexpr (std::is_same_v) { res = this->GetD3D9()->GetCubeMapSurface(d3d9::D3DCUBEMAP_FACES(Index % CUBE_FACES), Index / CUBE_FACES, &ptr); } if (FAILED(res)) throw DxvkError(str::format("D3D8BaseTexture::GetSubresource: Failed to retrieve index ", Index)); return ptr; } std::vector> m_subresources; }; using D3D8Texture2DBase = D3D8BaseTexture; class D3D8Texture2D final : public D3D8Texture2DBase { public: D3D8Texture2D( D3D8Device* pDevice, const D3DPOOL Pool, Com&& pTexture); D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final; HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc); HRESULT STDMETHODCALLTYPE GetSurfaceLevel(UINT Level, IDirect3DSurface8** ppSurfaceLevel); HRESULT STDMETHODCALLTYPE LockRect( UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags); HRESULT STDMETHODCALLTYPE UnlockRect(UINT Level); HRESULT STDMETHODCALLTYPE AddDirtyRect(CONST RECT* pDirtyRect); }; using D3D8Texture3DBase = D3D8BaseTexture; class D3D8Texture3D final : public D3D8Texture3DBase { public: D3D8Texture3D( D3D8Device* pDevice, const D3DPOOL Pool, Com&& pVolumeTexture); D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final; HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc); HRESULT STDMETHODCALLTYPE GetVolumeLevel(UINT Level, IDirect3DVolume8** ppVolumeLevel); HRESULT STDMETHODCALLTYPE LockBox( UINT Level, D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags); HRESULT STDMETHODCALLTYPE UnlockBox(UINT Level); HRESULT STDMETHODCALLTYPE AddDirtyBox(CONST D3DBOX* pDirtyBox); }; using D3D8TextureCubeBase = D3D8BaseTexture; class D3D8TextureCube final : public D3D8TextureCubeBase { public: D3D8TextureCube( D3D8Device* pDevice, const D3DPOOL Pool, Com&& pTexture); D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final; HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc); HRESULT STDMETHODCALLTYPE GetCubeMapSurface( D3DCUBEMAP_FACES Face, UINT Level, IDirect3DSurface8** ppSurfaceLevel); HRESULT STDMETHODCALLTYPE LockRect( D3DCUBEMAP_FACES Face, UINT Level, D3DLOCKED_RECT* pLockedRect, const RECT* pRect, DWORD Flags); HRESULT STDMETHODCALLTYPE UnlockRect(D3DCUBEMAP_FACES Face, UINT Level); HRESULT STDMETHODCALLTYPE AddDirtyRect(D3DCUBEMAP_FACES Face, const RECT* pDirtyRect); }; }dxvk-2.6.1/src/d3d8/d3d8_volume.cpp000066400000000000000000000020311477473124000166670ustar00rootroot00000000000000#include "d3d8_volume.h" #include "d3d8_d3d9_util.h" namespace dxvk { D3D8Volume::D3D8Volume( D3D8Device* pDevice, const D3DPOOL Pool, IDirect3DVolumeTexture8* pTexture, Com&& pVolume) : D3D8VolumeBase(pDevice, Pool, std::move(pVolume), pTexture) { } HRESULT STDMETHODCALLTYPE D3D8Volume::GetDesc(D3DVOLUME_DESC* pDesc) { if (unlikely(pDesc == nullptr)) return D3DERR_INVALIDCALL; d3d9::D3DVOLUME_DESC desc; HRESULT res = GetD3D9()->GetDesc(&desc); if (likely(SUCCEEDED(res))) ConvertVolumeDesc8(&desc, pDesc); return res; } HRESULT STDMETHODCALLTYPE D3D8Volume::LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) { return GetD3D9()->LockBox( reinterpret_cast(pLockedBox), reinterpret_cast(pBox), Flags ); } HRESULT STDMETHODCALLTYPE D3D8Volume::UnlockBox() { return GetD3D9()->UnlockBox(); } }dxvk-2.6.1/src/d3d8/d3d8_volume.h000066400000000000000000000012201477473124000163330ustar00rootroot00000000000000#pragma once #include "d3d8_subresource.h" namespace dxvk { using D3D8VolumeBase = D3D8Subresource; class D3D8Volume final : public D3D8VolumeBase { public: D3D8Volume( D3D8Device* pDevice, const D3DPOOL Pool, IDirect3DVolumeTexture8* pTexture, Com&& pVolume); HRESULT STDMETHODCALLTYPE GetDesc(D3DVOLUME_DESC* pDesc); HRESULT STDMETHODCALLTYPE LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) final; HRESULT STDMETHODCALLTYPE UnlockBox() final; }; }dxvk-2.6.1/src/d3d8/d3d8_wrapped_object.h000066400000000000000000000026251477473124000200260ustar00rootroot00000000000000#pragma once #include "d3d8_include.h" namespace dxvk { template class D3D8WrappedObject : public ComObjectClamp { public: using D3D9 = D3D9Type; using D3D8 = D3D8Type; D3D8WrappedObject(Com&& object) : m_d3d9(std::move(object)) { } D3D9* GetD3D9() { return m_d3d9.ptr(); } // For cases where the object may be null. static D3D9* GetD3D9Nullable(D3D8WrappedObject* self) { if (unlikely(self == NULL)) { return NULL; } return self->m_d3d9.ptr(); } template static D3D9* GetD3D9Nullable(Com& self) { return GetD3D9Nullable(self.ptr()); } virtual IUnknown* GetInterface(REFIID riid) { if (riid == __uuidof(IUnknown)) return this; if (riid == __uuidof(D3D8)) return this; throw DxvkError("D3D8WrappedObject::QueryInterface: Unknown interface query"); } HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) final { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; try { *ppvObject = ref(this->GetInterface(riid)); return S_OK; } catch (const DxvkError& e) { Logger::warn(e.message()); Logger::warn(str::format(riid)); return E_NOINTERFACE; } } private: Com m_d3d9; }; }dxvk-2.6.1/src/d3d8/meson.build000066400000000000000000000022631477473124000162030ustar00rootroot00000000000000d3d8_res = wrc_generator.process('version.rc') d3d8_src = [ 'd3d8_buffer.cpp', 'd3d8_device.cpp', 'd3d8_interface.cpp', 'd3d8_main.cpp', 'd3d8_multithread.cpp', 'd3d8_options.cpp', 'd3d8_shader.cpp', 'd3d8_state_block.cpp', 'd3d8_surface.cpp', 'd3d8_swapchain.cpp', 'd3d8_texture.cpp', 'd3d8_volume.cpp' ] d3d8_ld_args = [] d3d8_link_depends = [] if platform != 'windows' lib_d3d9 = d3d9_dep d3d8_ld_args += [ '-Wl,--version-script', join_paths(meson.current_source_dir(), 'd3d8.sym') ] d3d8_link_depends += files('d3d8.sym') endif d3d8_dll = shared_library(dxvk_name_prefix+'d3d8', d3d8_src, d3d8_res, dependencies : [ lib_d3d9, util_dep, dxso_dep, dxvk_dep ], include_directories : dxvk_include_path, install : true, vs_module_defs : 'd3d8'+def_spec_ext, link_args : d3d8_ld_args, link_depends : [ d3d8_link_depends ], kwargs : dxvk_so_version, ) d3d8_dep = declare_dependency( link_with : [ d3d8_dll ], include_directories : [ dxvk_include_path ], ) if platform != 'windows' pkg.generate(d3d8_dll, filebase: dxvk_pkg_prefix + 'd3d8', subdirs: 'dxvk', ) endif dxvk-2.6.1/src/d3d8/version.rc000066400000000000000000000014661477473124000160600ustar00rootroot00000000000000#include // DLL version information. VS_VERSION_INFO VERSIONINFO FILEVERSION 10,0,17763,1 PRODUCTVERSION 10,0,17763,1 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS 0 FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "080904b0" BEGIN VALUE "CompanyName", "DXVK" VALUE "FileDescription", "Direct3D 8 Runtime" VALUE "FileVersion", "10.0.17763.1 (WinBuild.160101.0800)" VALUE "InternalName", "D3D8.dll" VALUE "LegalCopyright", "zlib/libpng license" VALUE "OriginalFilename", "D3D8.dll" VALUE "ProductName", "DXVK" VALUE "ProductVersion", "10.0.17763.1" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0809, 1200 END END dxvk-2.6.1/src/d3d9/000077500000000000000000000000001477473124000140375ustar00rootroot00000000000000dxvk-2.6.1/src/d3d9/d3d9.def000066400000000000000000000011411477473124000152570ustar00rootroot00000000000000LIBRARY D3D9.DLL EXPORTS Direct3DShaderValidatorCreate9 @24 PSGPError @25 PSGPSampleTexture @26 D3DPERF_BeginEvent @27 D3DPERF_EndEvent @28 D3DPERF_GetStatus @29 D3DPERF_QueryRepeatFrame @30 D3DPERF_SetMarker @31 D3DPERF_SetOptions @32 D3DPERF_SetRegion @33 DebugSetLevel @34 DebugSetMute @35 Direct3D9EnableMaximizedWindowedModeShim @36 Direct3DCreate9 @37 Direct3DCreate9Ex @38 DXVK_RegisterAnnotation @28257 NONAME DXVK_UnRegisterAnnotation @28258 NONAME Direct3D9ForceHybridEnumeration @16 NONAME PRIVATE Direct3DCreate9On12 @20 Direct3DCreate9On12Ex @21 dxvk-2.6.1/src/d3d9/d3d9.sym000066400000000000000000000010471477473124000153360ustar00rootroot00000000000000{ global: Direct3DCreate9; Direct3DCreate9Ex; D3DPERF_BeginEvent; D3DPERF_EndEvent; D3DPERF_SetMarker; D3DPERF_SetRegion; D3DPERF_QueryRepeatFrame; D3DPERF_SetOptions; D3DPERF_GetStatus; DebugSetMute; DebugSetLevel; PSGPError; PSGPSampleTexture; Direct3DShaderValidatorCreate9; Direct3D9EnableMaximizedWindowedModeShim; DXVK_RegisterAnnotation; DXVK_UnRegisterAnnotation; Direct3D9ForceHybridEnumeration; Direct3DCreate9On12; Direct3DCreate9On12Ex; local: *; }; dxvk-2.6.1/src/d3d9/d3d9_adapter.cpp000066400000000000000000001126761477473124000170230ustar00rootroot00000000000000#include "d3d9_adapter.h" #include "d3d9_interface.h" #include "d3d9_monitor.h" #include "d3d9_caps.h" #include "d3d9_util.h" #include "../util/util_bit.h" #include "../util/util_luid.h" #include "../util/util_ratio.h" #include "../util/util_string.h" #include "../wsi/wsi_monitor.h" #include namespace dxvk { const char* GetDriverDLL(DxvkGpuVendor vendor) { switch (vendor) { default: case DxvkGpuVendor::Nvidia: return "nvd3dum.dll"; #if defined(__x86_64__) || defined(_M_X64) case DxvkGpuVendor::Amd: return "aticfx64.dll"; case DxvkGpuVendor::Intel: return "igdumd64.dll"; #else case DxvkGpuVendor::Amd: return "aticfx32.dll"; case DxvkGpuVendor::Intel: return "igdumd32.dll"; #endif } } D3D9Adapter::D3D9Adapter( D3D9InterfaceEx* pParent, Rc Adapter, UINT Ordinal, UINT DisplayIndex) : m_parent (pParent), m_adapter (Adapter), m_ordinal (Ordinal), m_displayIndex (DisplayIndex), m_modeCacheFormat (D3D9Format::Unknown), m_d3d9Formats (Adapter, m_parent->GetOptions()) { m_adapter->logAdapterInfo(); } template static void copyToStringArray(char (&dst)[N], const char* src) { dxvk::str::strlcpy(dst, src, N); } HRESULT D3D9Adapter::GetAdapterIdentifier( DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier) { if (unlikely(pIdentifier == nullptr)) return D3DERR_INVALIDCALL; auto& options = m_parent->GetOptions(); const auto& props = m_adapter->deviceProperties(); WCHAR wideDisplayName[32] = { }; if (!wsi::getDisplayName(wsi::getDefaultMonitor(), wideDisplayName)) { Logger::err("D3D9Adapter::GetAdapterIdentifier: Failed to query monitor info"); return D3DERR_INVALIDCALL; } std::string displayName = str::fromws(wideDisplayName); GUID guid = bit::cast(m_adapter->devicePropertiesExt().vk11.deviceUUID); uint32_t vendorId = options.customVendorId == -1 ? props.vendorID : uint32_t(options.customVendorId); uint32_t deviceId = options.customDeviceId == -1 ? props.deviceID : uint32_t(options.customDeviceId); const char* desc = options.customDeviceDesc.empty() ? props.deviceName : options.customDeviceDesc.c_str(); const char* driver = GetDriverDLL(DxvkGpuVendor(vendorId)); copyToStringArray(pIdentifier->Description, desc); copyToStringArray(pIdentifier->DeviceName, displayName.c_str()); // The GDI device name. Not the actual device name. copyToStringArray(pIdentifier->Driver, driver); // This is the driver's dll. pIdentifier->DeviceIdentifier = guid; pIdentifier->DeviceId = deviceId; pIdentifier->VendorId = vendorId; pIdentifier->Revision = 0; pIdentifier->SubSysId = 0; pIdentifier->WHQLLevel = m_parent->IsExtended() ? 1 : 0; // This doesn't check with the driver on Direct3D9Ex and is always 1. pIdentifier->DriverVersion.QuadPart = INT64_MAX; return D3D_OK; } HRESULT D3D9Adapter::CheckDeviceType( D3DDEVTYPE DevType, D3D9Format AdapterFormat, D3D9Format BackBufferFormat, BOOL bWindowed) { if (!IsSupportedAdapterFormat(AdapterFormat)) return D3DERR_NOTAVAILABLE; if (!IsSupportedBackBufferFormat(AdapterFormat, BackBufferFormat, bWindowed)) return D3DERR_NOTAVAILABLE; return D3D_OK; } HRESULT D3D9Adapter::CheckDeviceFormat( D3DDEVTYPE DeviceType, D3D9Format AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3D9Format CheckFormat) { if (unlikely(AdapterFormat == D3D9Format::Unknown)) return D3DERR_INVALIDCALL; if (unlikely(RType == D3DRTYPE_VERTEXBUFFER || RType == D3DRTYPE_INDEXBUFFER)) return D3DERR_INVALIDCALL; if (!IsSupportedAdapterFormat(AdapterFormat)) return D3DERR_NOTAVAILABLE; const bool dmap = Usage & D3DUSAGE_DMAP; const bool rt = Usage & D3DUSAGE_RENDERTARGET; const bool ds = Usage & D3DUSAGE_DEPTHSTENCIL; const bool surface = RType == D3DRTYPE_SURFACE; const bool texture = RType == D3DRTYPE_TEXTURE; const bool twoDimensional = surface || texture; const bool srgb = (Usage & (D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE)) != 0; if (CheckFormat == D3D9Format::INST) return D3D_OK; if (rt && CheckFormat == D3D9Format::A8 && m_parent->GetOptions().disableA8RT) return D3DERR_NOTAVAILABLE; if (ds && !IsDepthStencilFormat(CheckFormat)) return D3DERR_NOTAVAILABLE; if (rt && CheckFormat == D3D9Format::NULL_FORMAT && twoDimensional) return D3D_OK; if (rt && CheckFormat == D3D9Format::RESZ && surface) return D3D_OK; if (CheckFormat == D3D9Format::ATOC && surface) return D3D_OK; if (CheckFormat == D3D9Format::NVDB && surface) return m_adapter->features().core.features.depthBounds ? D3D_OK : D3DERR_NOTAVAILABLE; // I really don't want to support this... if (dmap) return D3DERR_NOTAVAILABLE; auto mapping = m_d3d9Formats.GetFormatMapping(CheckFormat); if (mapping.FormatColor == VK_FORMAT_UNDEFINED) return D3DERR_NOTAVAILABLE; if (mapping.FormatSrgb == VK_FORMAT_UNDEFINED && srgb) return D3DERR_NOTAVAILABLE; if (RType == D3DRTYPE_CUBETEXTURE && mapping.Aspect != VK_IMAGE_ASPECT_COLOR_BIT) return D3DERR_NOTAVAILABLE; // Let's actually ask Vulkan now that we got some quirks out the way! VkFormat format = mapping.FormatColor; if (unlikely(mapping.ConversionFormatInfo.FormatColor != VK_FORMAT_UNDEFINED)) { format = mapping.ConversionFormatInfo.FormatColor; } return CheckDeviceVkFormat(format, Usage, RType); } HRESULT D3D9Adapter::CheckDeviceMultiSampleType( D3DDEVTYPE DeviceType, D3D9Format SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD* pQualityLevels) { if (pQualityLevels != nullptr) *pQualityLevels = 1; if (unlikely(MultiSampleType > D3DMULTISAMPLE_16_SAMPLES)) return D3DERR_INVALIDCALL; if (unlikely(SurfaceFormat == D3D9Format::Unknown)) return D3DERR_INVALIDCALL; auto dst = ConvertFormatUnfixed(SurfaceFormat); if (dst.FormatColor == VK_FORMAT_UNDEFINED) return D3DERR_NOTAVAILABLE; if (MultiSampleType != D3DMULTISAMPLE_NONE && (SurfaceFormat == D3D9Format::D32_LOCKABLE || SurfaceFormat == D3D9Format::D32F_LOCKABLE || SurfaceFormat == D3D9Format::D16_LOCKABLE || SurfaceFormat == D3D9Format::INTZ || SurfaceFormat == D3D9Format::DXT1 || SurfaceFormat == D3D9Format::DXT2 || SurfaceFormat == D3D9Format::DXT3 || SurfaceFormat == D3D9Format::DXT4 || SurfaceFormat == D3D9Format::DXT5)) return D3DERR_NOTAVAILABLE; uint32_t sampleCount = std::max(MultiSampleType, 1u); // Check if this is a power of two... if (sampleCount & (sampleCount - 1)) return D3DERR_NOTAVAILABLE; // Therefore... VkSampleCountFlags sampleFlags = VkSampleCountFlags(sampleCount); auto availableFlags = m_adapter->deviceProperties().limits.framebufferColorSampleCounts & m_adapter->deviceProperties().limits.framebufferDepthSampleCounts; if (!(availableFlags & sampleFlags)) return D3DERR_NOTAVAILABLE; if (pQualityLevels != nullptr) { if (MultiSampleType == D3DMULTISAMPLE_NONMASKABLE) *pQualityLevels = 32 - bit::lzcnt(availableFlags); else *pQualityLevels = 1; } return D3D_OK; } HRESULT D3D9Adapter::CheckDepthStencilMatch( D3DDEVTYPE DeviceType, D3D9Format AdapterFormat, D3D9Format RenderTargetFormat, D3D9Format DepthStencilFormat) { if (!IsDepthStencilFormat(DepthStencilFormat)) return D3DERR_NOTAVAILABLE; auto dsfMapping = GetFormatMapping(DepthStencilFormat); if (dsfMapping.FormatColor == VK_FORMAT_UNDEFINED) return D3DERR_NOTAVAILABLE; if (RenderTargetFormat == dxvk::D3D9Format::NULL_FORMAT) return D3D_OK; auto rtfMapping = GetFormatMapping(RenderTargetFormat); if (rtfMapping.FormatColor == VK_FORMAT_UNDEFINED) return D3DERR_NOTAVAILABLE; return D3D_OK; } HRESULT D3D9Adapter::CheckDeviceFormatConversion( D3DDEVTYPE DeviceType, D3D9Format SourceFormat, D3D9Format TargetFormat) { bool sourceSupported = SourceFormat != D3D9Format::Unknown && (IsSupportedBackBufferFormat(SourceFormat) || (IsFourCCFormat(SourceFormat) && !IsVendorFormat(SourceFormat))); bool targetSupported = TargetFormat == D3D9Format::X1R5G5B5 || TargetFormat == D3D9Format::A1R5G5B5 || TargetFormat == D3D9Format::R5G6B5 // || TargetFormat == D3D9Format::R8G8B8 <-- We don't support R8G8B8 || TargetFormat == D3D9Format::X8R8G8B8 || TargetFormat == D3D9Format::A8R8G8B8 || TargetFormat == D3D9Format::A2R10G10B10 || TargetFormat == D3D9Format::A16B16G16R16 || TargetFormat == D3D9Format::A2B10G10R10 || TargetFormat == D3D9Format::A8B8G8R8 || TargetFormat == D3D9Format::X8B8G8R8 || TargetFormat == D3D9Format::A16B16G16R16F || TargetFormat == D3D9Format::A32B32G32R32F; return (sourceSupported && targetSupported) ? D3D_OK : D3DERR_NOTAVAILABLE; } HRESULT D3D9Adapter::GetDeviceCaps( D3DDEVTYPE DeviceType, D3DCAPS9* pCaps) { using namespace dxvk::caps; if (unlikely(pCaps == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(DeviceType == D3DDEVTYPE_SW)) { if (m_parent->IsD3D8Compatible()) return D3DERR_INVALIDCALL; else return D3DERR_NOTAVAILABLE; } auto& options = m_parent->GetOptions(); const uint32_t maxShaderModel = m_parent->IsD3D8Compatible() ? std::min(1u, options.shaderModel) : options.shaderModel; const VkPhysicalDeviceLimits& limits = m_adapter->deviceProperties().limits; // TODO: Actually care about what the adapter supports here. // ^ For Intel and older cards most likely here. // Device Type pCaps->DeviceType = DeviceType; // Adapter Id pCaps->AdapterOrdinal = m_ordinal; // Caps 1 pCaps->Caps = D3DCAPS_READ_SCANLINE; // Caps 2 pCaps->Caps2 = D3DCAPS2_FULLSCREENGAMMA /* | D3DCAPS2_CANCALIBRATEGAMMA */ /* | D3DCAPS2_RESERVED */ /* | D3DCAPS2_CANMANAGERESOURCE */ | D3DCAPS2_DYNAMICTEXTURES | D3DCAPS2_CANAUTOGENMIPMAP /* | D3DCAPS2_CANSHARERESOURCE */; // Caps 3 pCaps->Caps3 = D3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD | D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION | D3DCAPS3_COPY_TO_VIDMEM | D3DCAPS3_COPY_TO_SYSTEMMEM /* | D3DCAPS3_DXVAHD */ /* | D3DCAPS3_DXVAHD_LIMITED */; // Presentation Intervals pCaps->PresentationIntervals = D3DPRESENT_INTERVAL_DEFAULT | D3DPRESENT_INTERVAL_ONE | D3DPRESENT_INTERVAL_TWO | D3DPRESENT_INTERVAL_THREE | D3DPRESENT_INTERVAL_FOUR | D3DPRESENT_INTERVAL_IMMEDIATE; // Cursor pCaps->CursorCaps = D3DCURSORCAPS_COLOR; // I do not support Cursor yet, but I don't want to say I don't support it for compatibility reasons. // Dev Caps pCaps->DevCaps = D3DDEVCAPS_EXECUTESYSTEMMEMORY | D3DDEVCAPS_EXECUTEVIDEOMEMORY | D3DDEVCAPS_TLVERTEXSYSTEMMEMORY | D3DDEVCAPS_TLVERTEXVIDEOMEMORY /* | D3DDEVCAPS_TEXTURESYSTEMMEMORY */ | D3DDEVCAPS_TEXTUREVIDEOMEMORY | D3DDEVCAPS_DRAWPRIMTLVERTEX | D3DDEVCAPS_CANRENDERAFTERFLIP | D3DDEVCAPS_TEXTURENONLOCALVIDMEM | D3DDEVCAPS_DRAWPRIMITIVES2 /* | D3DDEVCAPS_SEPARATETEXTUREMEMORIES */ | D3DDEVCAPS_DRAWPRIMITIVES2EX | D3DDEVCAPS_HWTRANSFORMANDLIGHT | D3DDEVCAPS_CANBLTSYSTONONLOCAL | D3DDEVCAPS_HWRASTERIZATION | D3DDEVCAPS_PUREDEVICE /* | D3DDEVCAPS_QUINTICRTPATCHES */ /* | D3DDEVCAPS_RTPATCHES */ /* | D3DDEVCAPS_RTPATCHHANDLEZERO */ /* | D3DDEVCAPS_NPATCHES */; // Primitive Misc. Caps pCaps->PrimitiveMiscCaps = D3DPMISCCAPS_MASKZ | D3DPMISCCAPS_CULLNONE | D3DPMISCCAPS_CULLCW | D3DPMISCCAPS_CULLCCW | D3DPMISCCAPS_COLORWRITEENABLE | D3DPMISCCAPS_CLIPPLANESCALEDPOINTS | D3DPMISCCAPS_CLIPTLVERTS | D3DPMISCCAPS_TSSARGTEMP | D3DPMISCCAPS_BLENDOP /* | D3DPMISCCAPS_NULLREFERENCE */ | D3DPMISCCAPS_INDEPENDENTWRITEMASKS | D3DPMISCCAPS_PERSTAGECONSTANT | D3DPMISCCAPS_FOGANDSPECULARALPHA | D3DPMISCCAPS_SEPARATEALPHABLEND | D3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS | D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING | D3DPMISCCAPS_FOGVERTEXCLAMPED | D3DPMISCCAPS_POSTBLENDSRGBCONVERT; // Raster Caps pCaps->RasterCaps = D3DPRASTERCAPS_DITHER | D3DPRASTERCAPS_ZTEST | D3DPRASTERCAPS_FOGVERTEX | D3DPRASTERCAPS_FOGTABLE | D3DPRASTERCAPS_MIPMAPLODBIAS /* | D3DPRASTERCAPS_ZBUFFERLESSHSR */ | D3DPRASTERCAPS_FOGRANGE | D3DPRASTERCAPS_ANISOTROPY /* | D3DPRASTERCAPS_WBUFFER */ | D3DPRASTERCAPS_WFOG | D3DPRASTERCAPS_ZFOG | D3DPRASTERCAPS_COLORPERSPECTIVE | D3DPRASTERCAPS_SCISSORTEST | D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS | D3DPRASTERCAPS_DEPTHBIAS | D3DPRASTERCAPS_MULTISAMPLE_TOGGLE; // <-- TODO! (but difficult in Vk) // Z Comparison Caps pCaps->ZCmpCaps = D3DPCMPCAPS_NEVER | D3DPCMPCAPS_LESS | D3DPCMPCAPS_EQUAL | D3DPCMPCAPS_LESSEQUAL | D3DPCMPCAPS_GREATER | D3DPCMPCAPS_NOTEQUAL | D3DPCMPCAPS_GREATEREQUAL | D3DPCMPCAPS_ALWAYS; // Source Blend Caps pCaps->SrcBlendCaps = D3DPBLENDCAPS_ZERO | D3DPBLENDCAPS_ONE | D3DPBLENDCAPS_SRCCOLOR | D3DPBLENDCAPS_INVSRCCOLOR | D3DPBLENDCAPS_SRCALPHA | D3DPBLENDCAPS_INVSRCALPHA | D3DPBLENDCAPS_DESTALPHA | D3DPBLENDCAPS_INVDESTALPHA | D3DPBLENDCAPS_DESTCOLOR | D3DPBLENDCAPS_INVDESTCOLOR | D3DPBLENDCAPS_SRCALPHASAT | D3DPBLENDCAPS_BOTHSRCALPHA | D3DPBLENDCAPS_BOTHINVSRCALPHA | D3DPBLENDCAPS_BLENDFACTOR; // Only 9Ex devices advertise D3DPBLENDCAPS_SRCCOLOR2 and D3DPBLENDCAPS_INVSRCCOLOR2 if (m_parent->IsExtended()) pCaps->SrcBlendCaps |= D3DPBLENDCAPS_SRCCOLOR2 | D3DPBLENDCAPS_INVSRCCOLOR2; // Destination Blend Caps pCaps->DestBlendCaps = pCaps->SrcBlendCaps; // Alpha Comparison Caps pCaps->AlphaCmpCaps = pCaps->ZCmpCaps; // Shade Caps pCaps->ShadeCaps = D3DPSHADECAPS_COLORGOURAUDRGB | D3DPSHADECAPS_SPECULARGOURAUDRGB | D3DPSHADECAPS_ALPHAGOURAUDBLEND | D3DPSHADECAPS_FOGGOURAUD; // Texture Caps pCaps->TextureCaps = D3DPTEXTURECAPS_PERSPECTIVE /* | D3DPTEXTURECAPS_POW2 */ | D3DPTEXTURECAPS_ALPHA /* | D3DPTEXTURECAPS_SQUAREONLY */ | D3DPTEXTURECAPS_TEXREPEATNOTSCALEDBYSIZE | D3DPTEXTURECAPS_ALPHAPALETTE /* | D3DPTEXTURECAPS_NONPOW2CONDITIONAL */ | D3DPTEXTURECAPS_PROJECTED | D3DPTEXTURECAPS_CUBEMAP | D3DPTEXTURECAPS_VOLUMEMAP | D3DPTEXTURECAPS_MIPMAP | D3DPTEXTURECAPS_MIPVOLUMEMAP | D3DPTEXTURECAPS_MIPCUBEMAP /* | D3DPTEXTURECAPS_CUBEMAP_POW2 */ /* | D3DPTEXTURECAPS_VOLUMEMAP_POW2 */ /* | D3DPTEXTURECAPS_NOPROJECTEDBUMPENV */; // Texture Filter Caps pCaps->TextureFilterCaps = D3DPTFILTERCAPS_MINFPOINT | D3DPTFILTERCAPS_MINFLINEAR | D3DPTFILTERCAPS_MINFANISOTROPIC /* | D3DPTFILTERCAPS_MINFPYRAMIDALQUAD */ /* | D3DPTFILTERCAPS_MINFGAUSSIANQUAD */ | D3DPTFILTERCAPS_MIPFPOINT | D3DPTFILTERCAPS_MIPFLINEAR /* | D3DPTFILTERCAPS_CONVOLUTIONMONO */ | D3DPTFILTERCAPS_MAGFPOINT | D3DPTFILTERCAPS_MAGFLINEAR | D3DPTFILTERCAPS_MAGFANISOTROPIC /* | D3DPTFILTERCAPS_MAGFPYRAMIDALQUAD */ /* | D3DPTFILTERCAPS_MAGFGAUSSIANQUAD */; // Cube Texture Filter Caps pCaps->CubeTextureFilterCaps = pCaps->TextureFilterCaps; // Volume Texture Filter Caps pCaps->VolumeTextureFilterCaps = pCaps->TextureFilterCaps; // Texture Address Caps pCaps->TextureAddressCaps = D3DPTADDRESSCAPS_WRAP | D3DPTADDRESSCAPS_MIRROR | D3DPTADDRESSCAPS_CLAMP | D3DPTADDRESSCAPS_BORDER | D3DPTADDRESSCAPS_INDEPENDENTUV | D3DPTADDRESSCAPS_MIRRORONCE; // Volume Texture Address Caps pCaps->VolumeTextureAddressCaps = pCaps->TextureAddressCaps; // Line Caps pCaps->LineCaps = D3DLINECAPS_TEXTURE | D3DLINECAPS_ZTEST | D3DLINECAPS_BLEND | D3DLINECAPS_ALPHACMP | D3DLINECAPS_FOG | D3DLINECAPS_ANTIALIAS; //<-- Lying about doing AA lines here, we don't *fully* support that. // Max Texture Width pCaps->MaxTextureWidth = MaxTextureDimension; // Max Texture Height pCaps->MaxTextureHeight = MaxTextureDimension; // Max Volume Extent pCaps->MaxVolumeExtent = 8192; // Max Texture Repeat pCaps->MaxTextureRepeat = 8192; // Max Texture Aspect Ratio pCaps->MaxTextureAspectRatio = 8192; // Max Anisotropy pCaps->MaxAnisotropy = 16; // Max Vertex W pCaps->MaxVertexW = 1e10f; // Guard Bands pCaps->GuardBandLeft = -32768.0f; pCaps->GuardBandTop = -32768.0f; pCaps->GuardBandRight = 32768.0f; pCaps->GuardBandBottom = 32768.0f; // Extents Adjust pCaps->ExtentsAdjust = 0.0f; // Stencil Caps pCaps->StencilCaps = D3DSTENCILCAPS_KEEP | D3DSTENCILCAPS_ZERO | D3DSTENCILCAPS_REPLACE | D3DSTENCILCAPS_INCRSAT | D3DSTENCILCAPS_DECRSAT | D3DSTENCILCAPS_INVERT | D3DSTENCILCAPS_INCR | D3DSTENCILCAPS_DECR | D3DSTENCILCAPS_TWOSIDED; // FVF Caps pCaps->FVFCaps = (MaxSimultaneousTextures & D3DFVFCAPS_TEXCOORDCOUNTMASK) /* | D3DFVFCAPS_DONOTSTRIPELEMENTS */ | D3DFVFCAPS_PSIZE; // Texture Op Caps pCaps->TextureOpCaps = D3DTEXOPCAPS_DISABLE | D3DTEXOPCAPS_SELECTARG1 | D3DTEXOPCAPS_SELECTARG2 | D3DTEXOPCAPS_MODULATE | D3DTEXOPCAPS_MODULATE2X | D3DTEXOPCAPS_MODULATE4X | D3DTEXOPCAPS_ADD | D3DTEXOPCAPS_ADDSIGNED | D3DTEXOPCAPS_ADDSIGNED2X | D3DTEXOPCAPS_SUBTRACT | D3DTEXOPCAPS_ADDSMOOTH | D3DTEXOPCAPS_BLENDDIFFUSEALPHA | D3DTEXOPCAPS_BLENDTEXTUREALPHA | D3DTEXOPCAPS_BLENDFACTORALPHA | D3DTEXOPCAPS_BLENDTEXTUREALPHAPM | D3DTEXOPCAPS_BLENDCURRENTALPHA | D3DTEXOPCAPS_PREMODULATE | D3DTEXOPCAPS_MODULATEALPHA_ADDCOLOR | D3DTEXOPCAPS_MODULATECOLOR_ADDALPHA | D3DTEXOPCAPS_MODULATEINVALPHA_ADDCOLOR | D3DTEXOPCAPS_MODULATEINVCOLOR_ADDALPHA | D3DTEXOPCAPS_BUMPENVMAP | D3DTEXOPCAPS_BUMPENVMAPLUMINANCE | D3DTEXOPCAPS_DOTPRODUCT3 | D3DTEXOPCAPS_MULTIPLYADD | D3DTEXOPCAPS_LERP; // Max Texture Blend Stages pCaps->MaxTextureBlendStages = MaxTextureBlendStages; // Max Simultaneous Textures pCaps->MaxSimultaneousTextures = MaxSimultaneousTextures; // Vertex Processing Caps pCaps->VertexProcessingCaps = D3DVTXPCAPS_TEXGEN | D3DVTXPCAPS_MATERIALSOURCE7 | D3DVTXPCAPS_DIRECTIONALLIGHTS | D3DVTXPCAPS_POSITIONALLIGHTS | D3DVTXPCAPS_LOCALVIEWER | D3DVTXPCAPS_TWEENING | D3DVTXPCAPS_TEXGEN_SPHEREMAP /* | D3DVTXPCAPS_NO_TEXGEN_NONLOCALVIEWER*/; // Max Active Lights pCaps->MaxActiveLights = caps::MaxEnabledLights; // Max User Clip Planes pCaps->MaxUserClipPlanes = MaxClipPlanes; // Max Vertex Blend Matrices pCaps->MaxVertexBlendMatrices = 4; // Max Vertex Blend Matrix Index pCaps->MaxVertexBlendMatrixIndex = 0; // Max Point Size pCaps->MaxPointSize = limits.pointSizeRange[1]; // Max Primitive Count pCaps->MaxPrimitiveCount = 0x00555555; // Max Vertex Index pCaps->MaxVertexIndex = 0x00ffffff; // Max Streams pCaps->MaxStreams = MaxStreams; // Max Stream Stride pCaps->MaxStreamStride = 508; // bytes // Late fixed-function capable cards, such as the GeForce 4 MX series, // expose support for VS 1.1, while not advertising any PS support const uint32_t majorVersionVS = maxShaderModel == 0 ? 1 : maxShaderModel; const uint32_t majorVersionPS = maxShaderModel; // Max supported SM1 is VS 1.1 and PS 1.4 const uint32_t minorVersionVS = majorVersionVS != 1 ? 0 : 1; const uint32_t minorVersionPS = majorVersionPS != 1 ? 0 : 4; // Shader Versions pCaps->VertexShaderVersion = D3DVS_VERSION(majorVersionVS, minorVersionVS); pCaps->PixelShaderVersion = D3DPS_VERSION(majorVersionPS, minorVersionPS); // Max Vertex Shader Const pCaps->MaxVertexShaderConst = MaxFloatConstantsVS; // Max PS1 Value pCaps->PixelShader1xMaxValue = maxShaderModel > 0 ? std::numeric_limits::max() : 0.0f; // Dev Caps 2 pCaps->DevCaps2 = D3DDEVCAPS2_STREAMOFFSET /* | D3DDEVCAPS2_DMAPNPATCH */ /* | D3DDEVCAPS2_ADAPTIVETESSRTPATCH */ /* | D3DDEVCAPS2_ADAPTIVETESSNPATCH */ | D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES /* | D3DDEVCAPS2_PRESAMPLEDDMAPNPATCH */ | D3DDEVCAPS2_VERTEXELEMENTSCANSHARESTREAMOFFSET; // Max N Patch Tesselation Level pCaps->MaxNpatchTessellationLevel = 0.0f; // Reserved for... something pCaps->Reserved5 = 0; // Master adapter for us is adapter 0, atm... pCaps->MasterAdapterOrdinal = 0; // The group of adapters this one is in pCaps->AdapterOrdinalInGroup = 0; // Number of adapters in current group pCaps->NumberOfAdaptersInGroup = 1; // Decl Type Caps pCaps->DeclTypes = D3DDTCAPS_UBYTE4 | D3DDTCAPS_UBYTE4N | D3DDTCAPS_SHORT2N | D3DDTCAPS_SHORT4N | D3DDTCAPS_USHORT2N | D3DDTCAPS_USHORT4N | D3DDTCAPS_UDEC3 | D3DDTCAPS_DEC3N | D3DDTCAPS_FLOAT16_2 | D3DDTCAPS_FLOAT16_4; // Number of simultaneous RTs pCaps->NumSimultaneousRTs = MaxSimultaneousRenderTargets; // Possible StretchRect filters pCaps->StretchRectFilterCaps = D3DPTFILTERCAPS_MINFPOINT | D3DPTFILTERCAPS_MINFLINEAR /* | D3DPTFILTERCAPS_MINFANISOTROPIC */ /* | D3DPTFILTERCAPS_MINFPYRAMIDALQUAD */ /* | D3DPTFILTERCAPS_MINFGAUSSIANQUAD */ /* | D3DPTFILTERCAPS_MIPFPOINT */ /* | D3DPTFILTERCAPS_MIPFLINEAR */ /* | D3DPTFILTERCAPS_CONVOLUTIONMONO */ | D3DPTFILTERCAPS_MAGFPOINT | D3DPTFILTERCAPS_MAGFLINEAR /* | D3DPTFILTERCAPS_MAGFANISOTROPIC */ /* | D3DPTFILTERCAPS_MAGFPYRAMIDALQUAD */ /* | D3DPTFILTERCAPS_MAGFGAUSSIANQUAD */; pCaps->VS20Caps.Caps = maxShaderModel >= 2 ? D3DVS20CAPS_PREDICATION : 0; pCaps->VS20Caps.DynamicFlowControlDepth = maxShaderModel >= 2 ? D3DVS20_MAX_DYNAMICFLOWCONTROLDEPTH : 0; pCaps->VS20Caps.NumTemps = maxShaderModel >= 2 ? D3DVS20_MAX_NUMTEMPS : 0; pCaps->VS20Caps.StaticFlowControlDepth = maxShaderModel >= 2 ? D3DVS20_MAX_STATICFLOWCONTROLDEPTH : 0; pCaps->PS20Caps.Caps = maxShaderModel >= 2 ? D3DPS20CAPS_ARBITRARYSWIZZLE | D3DPS20CAPS_GRADIENTINSTRUCTIONS | D3DPS20CAPS_PREDICATION | D3DPS20CAPS_NODEPENDENTREADLIMIT | D3DPS20CAPS_NOTEXINSTRUCTIONLIMIT : 0; pCaps->PS20Caps.DynamicFlowControlDepth = maxShaderModel >= 2 ? D3DPS20_MAX_DYNAMICFLOWCONTROLDEPTH : 0; pCaps->PS20Caps.NumTemps = maxShaderModel >= 2 ? D3DPS20_MAX_NUMTEMPS : 0; pCaps->PS20Caps.StaticFlowControlDepth = maxShaderModel >= 2 ? D3DPS20_MAX_STATICFLOWCONTROLDEPTH : 0; pCaps->PS20Caps.NumInstructionSlots = maxShaderModel >= 2 ? D3DPS20_MAX_NUMINSTRUCTIONSLOTS : 0; // Vertex texture samplers are only available as part of SM3, the caps are 0 otherwise. pCaps->VertexTextureFilterCaps = maxShaderModel == 3 ? D3DPTFILTERCAPS_MINFPOINT | D3DPTFILTERCAPS_MINFLINEAR /* | D3DPTFILTERCAPS_MINFANISOTROPIC */ /* | D3DPTFILTERCAPS_MINFPYRAMIDALQUAD */ /* | D3DPTFILTERCAPS_MINFGAUSSIANQUAD */ /* | D3DPTFILTERCAPS_MIPFPOINT */ /* | D3DPTFILTERCAPS_MIPFLINEAR */ /* | D3DPTFILTERCAPS_CONVOLUTIONMONO */ | D3DPTFILTERCAPS_MAGFPOINT | D3DPTFILTERCAPS_MAGFLINEAR /* | D3DPTFILTERCAPS_MAGFANISOTROPIC */ /* | D3DPTFILTERCAPS_MAGFPYRAMIDALQUAD */ /* | D3DPTFILTERCAPS_MAGFGAUSSIANQUAD */ : 0; pCaps->MaxVShaderInstructionsExecuted = maxShaderModel >= 2 ? 4294967295 : 0; pCaps->MaxPShaderInstructionsExecuted = maxShaderModel >= 2 ? 4294967295 : 0; pCaps->MaxVertexShader30InstructionSlots = maxShaderModel == 3 ? 32768 : 0; pCaps->MaxPixelShader30InstructionSlots = maxShaderModel == 3 ? 32768 : 0; return D3D_OK; } HMONITOR D3D9Adapter::GetMonitor() { return wsi::getDefaultMonitor(); } UINT D3D9Adapter::GetAdapterModeCountEx(const D3DDISPLAYMODEFILTER* pFilter) { if (pFilter == nullptr) return 0; // We don't offer any interlaced formats here so early out and avoid destroying mode cache. if (pFilter->ScanLineOrdering == D3DSCANLINEORDERING_INTERLACED) return 0; CacheModes(EnumerateFormat(pFilter->Format)); return m_modes.size(); } HRESULT D3D9Adapter::EnumAdapterModesEx( const D3DDISPLAYMODEFILTER* pFilter, UINT Mode, D3DDISPLAYMODEEX* pMode) { if (pMode == nullptr || pFilter == nullptr) return D3DERR_INVALIDCALL; const D3D9Format format = EnumerateFormat(pFilter->Format); if (FAILED(CheckDeviceFormat( D3DDEVTYPE_HAL, EnumerateFormat(pFilter->Format), D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, EnumerateFormat(pFilter->Format)))) return D3DERR_INVALIDCALL; CacheModes(format); // We don't return any scanline orderings that aren't progressive, // The format filtering is already handled for us by cache modes // So we can early out here and then just index. if (pFilter->ScanLineOrdering == D3DSCANLINEORDERING_INTERLACED) return D3DERR_INVALIDCALL; if (Mode >= m_modes.size()) return D3DERR_INVALIDCALL; *pMode = m_modes[Mode]; return D3D_OK; } HRESULT D3D9Adapter::GetAdapterDisplayModeEx( D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation) { if (pMode == nullptr) return D3DERR_INVALIDCALL; if (pRotation != nullptr) *pRotation = D3DDISPLAYROTATION_IDENTITY; wsi::WsiMode mode = { }; if (!wsi::getCurrentDisplayMode(wsi::getDefaultMonitor(), &mode)) { Logger::err("D3D9Adapter::GetAdapterDisplayModeEx: Failed to enum display settings"); return D3DERR_INVALIDCALL; } *pMode = ConvertDisplayMode(mode); return D3D_OK; } HRESULT D3D9Adapter::GetAdapterLUID(LUID* pLUID) { if (pLUID == nullptr) return D3DERR_INVALIDCALL; auto& vk11 = m_adapter->devicePropertiesExt().vk11; if (vk11.deviceLUIDValid) *pLUID = bit::cast(vk11.deviceLUID); else *pLUID = dxvk::GetAdapterLUID(m_ordinal); return D3D_OK; } HRESULT D3D9Adapter::CheckDeviceVkFormat( VkFormat Format, DWORD Usage, D3DRESOURCETYPE RType) { VkFormatFeatureFlags2 checkFlags = 0; if (RType != D3DRTYPE_SURFACE) checkFlags |= VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT; if (Usage & D3DUSAGE_RENDERTARGET) { checkFlags |= VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT; if (Usage & D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING) checkFlags |= VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT; } if (Usage & D3DUSAGE_DEPTHSTENCIL) checkFlags |= VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT; else checkFlags |= VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT; VkFormatFeatureFlags2 checkFlagsMipGen = checkFlags; if (Usage & D3DUSAGE_AUTOGENMIPMAP) { checkFlagsMipGen |= VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT; checkFlagsMipGen |= VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT; } DxvkFormatFeatures fmtSupport = m_adapter->getFormatFeatures(Format); VkFormatFeatureFlags2 imgFeatures = fmtSupport.optimal | fmtSupport.linear; if ((imgFeatures & checkFlags) != checkFlags) return D3DERR_NOTAVAILABLE; return ((imgFeatures & checkFlagsMipGen) != checkFlagsMipGen) ? D3DOK_NOAUTOGEN : D3D_OK; } void D3D9Adapter::CacheModes(D3D9Format Format) { if (!m_modes.empty() && m_modeCacheFormat == Format) return; // We already cached the modes for this format. No need to do it again. m_modes.clear(); m_modeCacheFormat = Format; // Skip unsupported formats if (!IsSupportedAdapterFormat(Format)) return; auto& options = m_parent->GetOptions(); // Walk over all modes that the display supports and // return those that match the requested format etc. wsi::WsiMode devMode = { }; uint32_t modeIndex = 0; const auto forcedRatio = Ratio(options.forceAspectRatio); while (wsi::getDisplayMode(wsi::getDefaultMonitor(), modeIndex++, &devMode)) { // Skip interlaced modes altogether if (devMode.interlaced) continue; // Skip modes with incompatible formats if (devMode.bitsPerPixel != GetMonitorFormatBpp(Format)) continue; if (!forcedRatio.undefined() && Ratio(devMode.width, devMode.height) != forcedRatio) continue; D3DDISPLAYMODEEX mode = ConvertDisplayMode(devMode); // Fix up the D3DFORMAT to match what we are enumerating mode.Format = static_cast(Format); if (std::count(m_modes.begin(), m_modes.end(), mode) == 0) m_modes.push_back(mode); } // Sort display modes by width, height and refresh rate (descending), in that order. // Some games rely on correct ordering, e.g. Prince of Persia (2008) expects the highest // refresh rate to be listed first for a particular resolution. std::sort(m_modes.begin(), m_modes.end(), [](const D3DDISPLAYMODEEX& a, const D3DDISPLAYMODEEX& b) { if (a.Width < b.Width) return true; if (a.Width > b.Width) return false; if (a.Height < b.Height) return true; if (a.Height > b.Height) return false; return b.RefreshRate < a.RefreshRate; }); } } dxvk-2.6.1/src/d3d9/d3d9_adapter.h000066400000000000000000000056151477473124000164620ustar00rootroot00000000000000#pragma once #include "d3d9_include.h" #include "d3d9_options.h" #include "d3d9_format.h" #include "../dxvk/dxvk_adapter.h" namespace dxvk { class D3D9InterfaceEx; class D3D9Adapter { public: D3D9Adapter( D3D9InterfaceEx* pParent, Rc Adapter, UINT Ordinal, UINT DisplayIndex); HRESULT GetAdapterIdentifier( DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier); HRESULT CheckDeviceType( D3DDEVTYPE DevType, D3D9Format AdapterFormat, D3D9Format BackBufferFormat, BOOL bWindowed); HRESULT CheckDeviceFormat( D3DDEVTYPE DeviceType, D3D9Format AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3D9Format CheckFormat); HRESULT CheckDeviceMultiSampleType( D3DDEVTYPE DeviceType, D3D9Format SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD* pQualityLevels); HRESULT CheckDepthStencilMatch( D3DDEVTYPE DeviceType, D3D9Format AdapterFormat, D3D9Format RenderTargetFormat, D3D9Format DepthStencilFormat); HRESULT CheckDeviceFormatConversion( D3DDEVTYPE DeviceType, D3D9Format SourceFormat, D3D9Format TargetFormat); HRESULT GetDeviceCaps( D3DDEVTYPE DeviceType, D3DCAPS9* pCaps); HMONITOR GetMonitor(); UINT GetAdapterModeCountEx(const D3DDISPLAYMODEFILTER* pFilter); HRESULT EnumAdapterModesEx( const D3DDISPLAYMODEFILTER* pFilter, UINT Mode, D3DDISPLAYMODEEX* pMode); HRESULT GetAdapterDisplayModeEx( D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation); HRESULT GetAdapterLUID(LUID* pLUID); UINT GetOrdinal() { return m_ordinal; } Rc GetDXVKAdapter() { return m_adapter; } D3D9_VK_FORMAT_MAPPING GetFormatMapping(D3D9Format Format) const { return m_d3d9Formats.GetFormatMapping(Format); } const DxvkFormatInfo* GetUnsupportedFormatInfo(D3D9Format Format) const { return m_d3d9Formats.GetUnsupportedFormatInfo(Format); } private: HRESULT CheckDeviceVkFormat( VkFormat Format, DWORD Usage, D3DRESOURCETYPE RType); void CacheModes(D3D9Format Format); D3D9InterfaceEx* m_parent; Rc m_adapter; UINT m_ordinal; UINT m_displayIndex; std::vector m_modes; D3D9Format m_modeCacheFormat; const D3D9VkFormatTable m_d3d9Formats; }; }dxvk-2.6.1/src/d3d9/d3d9_annotation.cpp000066400000000000000000000111441477473124000175410ustar00rootroot00000000000000#include "d3d9_annotation.h" namespace dxvk { //////////////////////////// // D3D9GlobalAnnotationList //////////////////////////// D3D9GlobalAnnotationList::D3D9GlobalAnnotationList() : m_shouldAnnotate(false) , m_eventDepth(0) {} D3D9GlobalAnnotationList::~D3D9GlobalAnnotationList() {} void D3D9GlobalAnnotationList::RegisterAnnotator(IDXVKUserDefinedAnnotation* annotation) { auto lock = std::unique_lock(m_mutex); m_shouldAnnotate = true; m_annotations.push_back(annotation); } void D3D9GlobalAnnotationList::UnregisterAnnotator(IDXVKUserDefinedAnnotation* annotation) { auto lock = std::unique_lock(m_mutex); auto iter = std::find(m_annotations.begin(), m_annotations.end(), annotation); if (iter != m_annotations.end()) m_annotations.erase(iter); } INT D3D9GlobalAnnotationList::BeginEvent(D3DCOLOR color, LPCWSTR name) { if (!m_shouldAnnotate) return 0; auto lock = std::unique_lock(m_mutex); for (auto* annotation : m_annotations) annotation->BeginEvent(color, name); return m_eventDepth++; } INT D3D9GlobalAnnotationList::EndEvent() { if (!m_shouldAnnotate) return 0; auto lock = std::unique_lock(m_mutex); for (auto* annotation : m_annotations) annotation->EndEvent(); return m_eventDepth--; } void D3D9GlobalAnnotationList::SetMarker(D3DCOLOR color, LPCWSTR name) { if (!m_shouldAnnotate) return; auto lock = std::unique_lock(m_mutex); for (auto* annotation : m_annotations) annotation->SetMarker(color, name); } void D3D9GlobalAnnotationList::SetRegion(D3DCOLOR color, LPCWSTR name) { // This, by the documentation, does nothing. } BOOL D3D9GlobalAnnotationList::QueryRepeatFrame() const { // This, by the documentation, does nothing. // It's meant to return TRUE if the profiler/debugger // wants a frame to be repeated, but we never need that. return FALSE; } void D3D9GlobalAnnotationList::SetOptions(DWORD options) { // This is used to say that the app should // not be debugged/profiled. } DWORD D3D9GlobalAnnotationList::GetStatus() const { // This returns whether the app is being // profiled / debugged. // Some apps may rely on this to emit // debug markers. return m_shouldAnnotate ? 1 : 0; } //////////////////////////// // D3D9UserDefinedAnnotation //////////////////////////// D3D9UserDefinedAnnotation::D3D9UserDefinedAnnotation(D3D9DeviceEx* device) : m_container(device) { D3D9GlobalAnnotationList::Instance().RegisterAnnotator(this); } D3D9UserDefinedAnnotation::~D3D9UserDefinedAnnotation() { D3D9GlobalAnnotationList::Instance().UnregisterAnnotator(this); } ULONG STDMETHODCALLTYPE D3D9UserDefinedAnnotation::AddRef() { return m_container->AddRef(); } ULONG STDMETHODCALLTYPE D3D9UserDefinedAnnotation::Release() { return m_container->Release(); } HRESULT STDMETHODCALLTYPE D3D9UserDefinedAnnotation::QueryInterface( REFIID riid, void** ppvObject) { return m_container->QueryInterface(riid, ppvObject); } INT STDMETHODCALLTYPE D3D9UserDefinedAnnotation::BeginEvent( D3DCOLOR Color, LPCWSTR Name) { D3D9DeviceLock lock = m_container->LockDevice(); m_container->EmitCs([color = Color, labelName = dxvk::str::fromws(Name)](DxvkContext *ctx) { VkDebugUtilsLabelEXT label; label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; label.pNext = nullptr; label.pLabelName = labelName.c_str(); DecodeD3DCOLOR(color, label.color); ctx->beginDebugLabel(label); }); // Handled by the global list. return 0; } INT STDMETHODCALLTYPE D3D9UserDefinedAnnotation::EndEvent() { D3D9DeviceLock lock = m_container->LockDevice(); m_container->EmitCs([](DxvkContext *ctx) { ctx->endDebugLabel(); }); // Handled by the global list. return 0; } void STDMETHODCALLTYPE D3D9UserDefinedAnnotation::SetMarker( D3DCOLOR Color, LPCWSTR Name) { D3D9DeviceLock lock = m_container->LockDevice(); m_container->EmitCs([color = Color, labelName = dxvk::str::fromws(Name)](DxvkContext *ctx) { VkDebugUtilsLabelEXT label; label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; label.pNext = nullptr; label.pLabelName = labelName.c_str(); DecodeD3DCOLOR(color, label.color); ctx->insertDebugLabel(label); }); } BOOL STDMETHODCALLTYPE D3D9UserDefinedAnnotation::GetStatus() { return TRUE; } } dxvk-2.6.1/src/d3d9/d3d9_annotation.h000066400000000000000000000035751477473124000172170ustar00rootroot00000000000000#pragma once #include "d3d9_device.h" #include "d3d9_include.h" #include "../dxvk/dxvk_annotation.h" #include "../util/thread.h" #include #include #include namespace dxvk { class D3D9GlobalAnnotationList { public: D3D9GlobalAnnotationList(); ~D3D9GlobalAnnotationList(); void RegisterAnnotator(IDXVKUserDefinedAnnotation* annotation); void UnregisterAnnotator(IDXVKUserDefinedAnnotation* annotation); INT BeginEvent(D3DCOLOR color, LPCWSTR name); INT EndEvent(); void SetMarker(D3DCOLOR color, LPCWSTR name); void SetRegion(D3DCOLOR color, LPCWSTR name); BOOL QueryRepeatFrame() const; void SetOptions(DWORD options); DWORD GetStatus() const; static D3D9GlobalAnnotationList& Instance() { return s_instance; } private: static D3D9GlobalAnnotationList s_instance; std::atomic m_shouldAnnotate; dxvk::mutex m_mutex; std::vector m_annotations; // Provide our own event depth as we // may have multiple annotators which could get out of sync. int32_t m_eventDepth; }; class D3D9UserDefinedAnnotation final : public IDXVKUserDefinedAnnotation { public: D3D9UserDefinedAnnotation(D3D9DeviceEx* device); ~D3D9UserDefinedAnnotation(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); INT STDMETHODCALLTYPE BeginEvent( D3DCOLOR Color, LPCWSTR Name); INT STDMETHODCALLTYPE EndEvent(); void STDMETHODCALLTYPE SetMarker( D3DCOLOR Color, LPCWSTR Name); BOOL STDMETHODCALLTYPE GetStatus(); private: D3D9DeviceEx* m_container; }; }dxvk-2.6.1/src/d3d9/d3d9_bridge.cpp000066400000000000000000000066701477473124000166330ustar00rootroot00000000000000 #include "d3d9_device.h" #include "d3d9_interface.h" #include "d3d9_bridge.h" #include "d3d9_swapchain.h" #include "d3d9_surface.h" namespace dxvk { DxvkD3D8Bridge::DxvkD3D8Bridge(D3D9DeviceEx* pDevice) : m_device(pDevice) { } DxvkD3D8Bridge::~DxvkD3D8Bridge() { } ULONG STDMETHODCALLTYPE DxvkD3D8Bridge::AddRef() { return m_device->AddRef(); } ULONG STDMETHODCALLTYPE DxvkD3D8Bridge::Release() { return m_device->Release(); } HRESULT STDMETHODCALLTYPE DxvkD3D8Bridge::QueryInterface( REFIID riid, void** ppvObject) { return m_device->QueryInterface(riid, ppvObject); } HRESULT DxvkD3D8Bridge::UpdateTextureFromBuffer( IDirect3DSurface9* pDestSurface, IDirect3DSurface9* pSrcSurface, const RECT* pSrcRect, const POINT* pDestPoint) { auto lock = m_device->LockDevice(); D3D9Surface* dst = static_cast(pDestSurface); D3D9Surface* src = static_cast(pSrcSurface); if (unlikely(dst == nullptr || src == nullptr)) return D3DERR_INVALIDCALL; // CopyRects will not pass a null pSrcRect, but check anyway if (unlikely(pSrcRect == nullptr)) return D3DERR_INVALIDCALL; // validate dimensions to ensure we calculate a meaningful srcOffset & extent if (unlikely(pSrcRect->left < 0 || pSrcRect->top < 0 || pSrcRect->right <= pSrcRect->left || pSrcRect->bottom <= pSrcRect->top)) return D3DERR_INVALIDCALL; // CopyRects will not pass a null pDestPoint, but check anyway if (unlikely(pDestPoint == nullptr)) return D3DERR_INVALIDCALL; // validate dimensions to ensure we caculate a meaningful dstOffset if (unlikely(pDestPoint->x < 0 || pDestPoint->y < 0)) return D3DERR_INVALIDCALL; D3D9CommonTexture* srcTextureInfo = src->GetCommonTexture(); D3D9CommonTexture* dstTextureInfo = dst->GetCommonTexture(); VkOffset3D srcOffset = { pSrcRect->left, pSrcRect->top, 0u }; VkExtent3D extent = { uint32_t(pSrcRect->right - pSrcRect->left), uint32_t(pSrcRect->bottom - pSrcRect->top), 1 }; VkOffset3D dstOffset = { pDestPoint->x, pDestPoint->y, 0u }; m_device->UpdateTextureFromBuffer( srcTextureInfo, dstTextureInfo, src->GetSubresource(), dst->GetSubresource(), srcOffset, extent, dstOffset ); dstTextureInfo->SetNeedsReadback(dst->GetSubresource(), true); if (dstTextureInfo->IsAutomaticMip()) m_device->MarkTextureMipsDirty(dstTextureInfo); return D3D_OK; } DxvkD3D8InterfaceBridge::DxvkD3D8InterfaceBridge(D3D9InterfaceEx* pObject) : m_interface(pObject) { } DxvkD3D8InterfaceBridge::~DxvkD3D8InterfaceBridge() { } ULONG STDMETHODCALLTYPE DxvkD3D8InterfaceBridge::AddRef() { return m_interface->AddRef(); } ULONG STDMETHODCALLTYPE DxvkD3D8InterfaceBridge::Release() { return m_interface->Release(); } HRESULT STDMETHODCALLTYPE DxvkD3D8InterfaceBridge::QueryInterface( REFIID riid, void** ppvObject) { return m_interface->QueryInterface(riid, ppvObject); } void DxvkD3D8InterfaceBridge::EnableD3D8CompatibilityMode() { m_interface->EnableD3D8CompatibilityMode(); } const Config* DxvkD3D8InterfaceBridge::GetConfig() const { return &m_interface->GetInstance()->config(); } } dxvk-2.6.1/src/d3d9/d3d9_bridge.h000066400000000000000000000056331477473124000162760ustar00rootroot00000000000000#pragma once #include #include "../util/config/config.h" /** * The D3D9 bridge allows D3D8 to access DXVK internals. * For Vulkan interop without needing DXVK internals, see d3d9_interop.h. * * NOTE: You must include "d3d9_include.h" or "d3d8_include.h" before this header. */ /** * \brief D3D9 device interface for D3D8 interop */ MIDL_INTERFACE("D3D9D3D8-42A9-4C1E-AA97-BEEFCAFE2000") IDxvkD3D8Bridge : public IUnknown { // D3D8 keeps D3D9 objects contained in a namespace. #ifdef DXVK_D3D9_NAMESPACE using IDirect3DSurface9 = d3d9::IDirect3DSurface9; #endif /** * \brief Updates a D3D9 surface from a D3D9 buffer * * \param [in] pDestSurface Destination surface (typically in VRAM) * \param [in] pSrcSurface Source surface (typically in system memory) * \param [in] pSrcRect Source rectangle * \param [in] pDestPoint Destination (top-left) point */ virtual HRESULT UpdateTextureFromBuffer( IDirect3DSurface9* pDestSurface, IDirect3DSurface9* pSrcSurface, const RECT* pSrcRect, const POINT* pDestPoint) = 0; }; /** * \brief D3D9 instance interface for D3D8 interop */ MIDL_INTERFACE("D3D9D3D8-A407-773E-18E9-CAFEBEEF3000") IDxvkD3D8InterfaceBridge : public IUnknown { /** * \brief Enforces D3D8-specific features and validations */ virtual void EnableD3D8CompatibilityMode() = 0; /** * \brief Retrieves the DXVK configuration * * \returns The DXVK Config object */ virtual const dxvk::Config* GetConfig() const = 0; }; #ifndef _MSC_VER __CRT_UUID_DECL(IDxvkD3D8Bridge, 0xD3D9D3D8, 0x42A9, 0x4C1E, 0xAA, 0x97, 0xBE, 0xEF, 0xCA, 0xFE, 0x20, 0x00); __CRT_UUID_DECL(IDxvkD3D8InterfaceBridge, 0xD3D9D3D8, 0xA407, 0x773E, 0x18, 0xE9, 0xCA, 0xFE, 0xBE, 0xEF, 0x30, 0x00); #endif namespace dxvk { class D3D9DeviceEx; class D3D9InterfaceEx; class DxvkD3D8Bridge : public IDxvkD3D8Bridge { public: DxvkD3D8Bridge(D3D9DeviceEx* pDevice); ~DxvkD3D8Bridge(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT UpdateTextureFromBuffer( IDirect3DSurface9* pDestSurface, IDirect3DSurface9* pSrcSurface, const RECT* pSrcRect, const POINT* pDestPoint); private: D3D9DeviceEx* m_device; }; class DxvkD3D8InterfaceBridge : public IDxvkD3D8InterfaceBridge { public: DxvkD3D8InterfaceBridge(D3D9InterfaceEx* pObject); ~DxvkD3D8InterfaceBridge(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); void EnableD3D8CompatibilityMode(); const Config* GetConfig() const; protected: D3D9InterfaceEx* m_interface; }; } dxvk-2.6.1/src/d3d9/d3d9_buffer.cpp000066400000000000000000000055621477473124000166470ustar00rootroot00000000000000#include "d3d9_buffer.h" namespace dxvk { //////////////////////// // D3D9VertexBuffer //////////////////////// D3D9VertexBuffer::D3D9VertexBuffer( D3D9DeviceEx* pDevice, const D3D9_BUFFER_DESC* pDesc, const bool Extended) : D3D9VertexBufferBase(pDevice, pDesc, Extended) { } HRESULT STDMETHODCALLTYPE D3D9VertexBuffer::QueryInterface( REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(IDirect3DResource9) || riid == __uuidof(IDirect3DVertexBuffer9)) { *ppvObject = ref(this); return S_OK; } if (logQueryInterfaceError(__uuidof(IDirect3DVertexBuffer9), riid)) { Logger::warn("D3D9VertexBuffer::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } D3DRESOURCETYPE STDMETHODCALLTYPE D3D9VertexBuffer::GetType() { return D3DRTYPE_VERTEXBUFFER; } HRESULT STDMETHODCALLTYPE D3D9VertexBuffer::GetDesc( D3DVERTEXBUFFER_DESC* pDesc) { if (pDesc == nullptr) return D3DERR_INVALIDCALL; const D3D9_BUFFER_DESC* desc = m_buffer.Desc(); pDesc->Format = static_cast(desc->Format); pDesc->Type = desc->Type; pDesc->Usage = desc->Usage; pDesc->Pool = desc->Pool; pDesc->Size = desc->Size; pDesc->FVF = desc->FVF; return D3D_OK; } ////////////////////// // D3D9IndexBuffer ////////////////////// D3D9IndexBuffer::D3D9IndexBuffer( D3D9DeviceEx* pDevice, const D3D9_BUFFER_DESC* pDesc, const bool Extended) : D3D9IndexBufferBase(pDevice, pDesc, Extended) { } HRESULT STDMETHODCALLTYPE D3D9IndexBuffer::QueryInterface( REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(IDirect3DResource9) || riid == __uuidof(IDirect3DIndexBuffer9)) { *ppvObject = ref(this); return S_OK; } if (logQueryInterfaceError(__uuidof(IDirect3DIndexBuffer9), riid)) { Logger::warn("D3D9IndexBuffer::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } D3DRESOURCETYPE STDMETHODCALLTYPE D3D9IndexBuffer::GetType() { return D3DRTYPE_INDEXBUFFER; } HRESULT STDMETHODCALLTYPE D3D9IndexBuffer::GetDesc( D3DINDEXBUFFER_DESC* pDesc) { if (pDesc == nullptr) return D3DERR_INVALIDCALL; const D3D9_BUFFER_DESC* desc = m_buffer.Desc(); pDesc->Format = static_cast(desc->Format); pDesc->Type = desc->Type; pDesc->Usage = desc->Usage; pDesc->Pool = desc->Pool; pDesc->Size = desc->Size; return D3D_OK; } }dxvk-2.6.1/src/d3d9/d3d9_buffer.h000066400000000000000000000042211477473124000163030ustar00rootroot00000000000000#pragma once #include "d3d9_resource.h" #include "d3d9_common_buffer.h" namespace dxvk { template class D3D9Buffer : public D3D9Resource { public: D3D9Buffer( D3D9DeviceEx* pDevice, const D3D9_BUFFER_DESC* pDesc, const bool Extended) : D3D9Resource (pDevice, pDesc->Pool, Extended ), m_buffer (pDevice, pDesc) { } HRESULT STDMETHODCALLTYPE Lock( UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags) final { return m_buffer.Lock( OffsetToLock, SizeToLock, ppbData, Flags); } HRESULT STDMETHODCALLTYPE Unlock() final { return m_buffer.Unlock(); } void STDMETHODCALLTYPE PreLoad() final { m_buffer.PreLoad(); } D3D9CommonBuffer* GetCommonBuffer() { return &m_buffer; } protected: D3D9CommonBuffer m_buffer; }; using D3D9VertexBufferBase = D3D9Buffer; class D3D9VertexBuffer final : public D3D9VertexBufferBase { public: D3D9VertexBuffer( D3D9DeviceEx* pDevice, const D3D9_BUFFER_DESC* pDesc, const bool Extended); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); D3DRESOURCETYPE STDMETHODCALLTYPE GetType(); HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc); }; using D3D9IndexBufferBase = D3D9Buffer; class D3D9IndexBuffer final : public D3D9IndexBufferBase { public: D3D9IndexBuffer( D3D9DeviceEx* pDevice, const D3D9_BUFFER_DESC* pDesc, const bool Extended); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); D3DRESOURCETYPE STDMETHODCALLTYPE GetType(); HRESULT STDMETHODCALLTYPE GetDesc(D3DINDEXBUFFER_DESC* pDesc); }; template inline D3D9CommonBuffer* GetCommonBuffer(const T& pResource) { return pResource != nullptr ? pResource->GetCommonBuffer() : nullptr; } }dxvk-2.6.1/src/d3d9/d3d9_caps.h000066400000000000000000000024021477473124000157570ustar00rootroot00000000000000#pragma once #include "d3d9_include.h" namespace dxvk::caps { constexpr uint32_t MaxClipPlanes = 6; constexpr uint32_t MaxSamplers = 16; constexpr uint32_t MaxStreams = 16; constexpr uint32_t MaxSimultaneousTextures = 8; constexpr uint32_t MaxTextureBlendStages = MaxSimultaneousTextures; constexpr uint32_t MaxSimultaneousRenderTargets = D3D_MAX_SIMULTANEOUS_RENDERTARGETS; constexpr uint32_t MaxFloatConstantsVS = 256; constexpr uint32_t MaxFloatConstantsPS = 224; constexpr uint32_t MaxOtherConstants = 16; constexpr uint32_t MaxFloatConstantsSoftware = 8192; constexpr uint32_t MaxOtherConstantsSoftware = 2048; constexpr uint32_t InputRegisterCount = 16; constexpr uint32_t MaxTextureDimension = 16384; constexpr uint32_t MaxMipLevels = 15; constexpr uint32_t MaxSubresources = 15 * 6; constexpr uint32_t MaxTransforms = 10 + 256; constexpr uint32_t TextureStageCount = MaxSimultaneousTextures; constexpr uint32_t MaxEnabledLights = 8; constexpr uint32_t MaxTexturesVS = 4; constexpr uint32_t MaxTexturesPS = 16; }dxvk-2.6.1/src/d3d9/d3d9_common_buffer.cpp000066400000000000000000000130331477473124000202070ustar00rootroot00000000000000#include "d3d9_common_buffer.h" #include "d3d9_device.h" #include "d3d9_util.h" namespace dxvk { D3D9CommonBuffer::D3D9CommonBuffer( D3D9DeviceEx* pDevice, const D3D9_BUFFER_DESC* pDesc) : m_parent ( pDevice ), m_desc ( *pDesc ), m_mapMode(DetermineMapMode(pDevice->GetOptions())) { m_buffer = CreateBuffer(); if (m_mapMode == D3D9_COMMON_BUFFER_MAP_MODE_BUFFER) m_stagingBuffer = CreateStagingBuffer(); m_allocation = GetMapBuffer()->storage(); if (m_desc.Pool != D3DPOOL_DEFAULT) m_dirtyRange = D3D9Range(0, m_desc.Size); } D3D9CommonBuffer::~D3D9CommonBuffer() { if (m_desc.Pool == D3DPOOL_DEFAULT) m_parent->DecrementLosableCounter(); } HRESULT D3D9CommonBuffer::Lock( UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags) { return m_parent->LockBuffer( this, OffsetToLock, SizeToLock, ppbData, Flags); } HRESULT D3D9CommonBuffer::Unlock() { return m_parent->UnlockBuffer(this); } HRESULT D3D9CommonBuffer::ValidateBufferProperties(const D3D9_BUFFER_DESC* pDesc) { if (unlikely(pDesc->Size == 0)) return D3DERR_INVALIDCALL; // Neither vertex nor index buffers can be created in D3DPOOL_SCRATCH // or in D3DPOOL_MANAGED with D3DUSAGE_DYNAMIC. if (unlikely(pDesc->Pool == D3DPOOL_SCRATCH || (pDesc->Pool == D3DPOOL_MANAGED && (pDesc->Usage & D3DUSAGE_DYNAMIC)))) return D3DERR_INVALIDCALL; // D3DUSAGE_AUTOGENMIPMAP, D3DUSAGE_DEPTHSTENCIL and D3DUSAGE_RENDERTARGET // are not permitted on index or vertex buffers. if (unlikely((pDesc->Usage & D3DUSAGE_AUTOGENMIPMAP) || (pDesc->Usage & D3DUSAGE_DEPTHSTENCIL) || (pDesc->Usage & D3DUSAGE_RENDERTARGET))) return D3DERR_INVALIDCALL; return D3D_OK; } void D3D9CommonBuffer::PreLoad() { if (IsPoolManaged(m_desc.Pool)) { auto lock = m_parent->LockDevice(); if (NeedsUpload()) m_parent->FlushBuffer(this); } } D3D9_COMMON_BUFFER_MAP_MODE D3D9CommonBuffer::DetermineMapMode(const D3D9Options* options) const { if (m_desc.Pool != D3DPOOL_DEFAULT) return D3D9_COMMON_BUFFER_MAP_MODE_BUFFER; // CSGO keeps vertex buffers locked across multiple frames and writes to it. It uses them for drawing without unlocking first. // Tests show that D3D9 DEFAULT + USAGE_DYNAMIC behaves like a directly mapped buffer even when unlocked. // DEFAULT + WRITEONLY does not behave like a directly mapped buffer EXCEPT if its locked at the moment. // That's annoying to implement so we just always directly map DEFAULT + WRITEONLY. if (!(m_desc.Usage & (D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY))) return D3D9_COMMON_BUFFER_MAP_MODE_BUFFER; if (!options->allowDirectBufferMapping) return D3D9_COMMON_BUFFER_MAP_MODE_BUFFER; return D3D9_COMMON_BUFFER_MAP_MODE_DIRECT; } Rc D3D9CommonBuffer::CreateBuffer() const { DxvkBufferCreateInfo info; info.size = m_desc.Size; info.usage = 0; info.stages = 0; info.access = 0; VkMemoryPropertyFlags memoryFlags = 0; if (m_desc.Type == D3DRTYPE_VERTEXBUFFER) { info.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; info.stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; info.access |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; if (m_parent->SupportsSWVP()) { info.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; info.stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; info.access |= VK_ACCESS_SHADER_WRITE_BIT; } } else if (m_desc.Type == D3DRTYPE_INDEXBUFFER) { info.usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; info.stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; info.access |= VK_ACCESS_INDEX_READ_BIT; } if (m_mapMode == D3D9_COMMON_BUFFER_MAP_MODE_DIRECT) { info.stages |= VK_PIPELINE_STAGE_HOST_BIT; info.access |= VK_ACCESS_HOST_WRITE_BIT; memoryFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; if ((m_desc.Usage & (D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC)) == 0 || DoPerDrawUpload() || m_parent->CanOnlySWVP() || m_parent->GetOptions()->cachedDynamicBuffers) { // Never use uncached memory on devices that support SWVP because we might end up reading from it. info.access |= VK_ACCESS_HOST_READ_BIT; memoryFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT; } else { memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; } } else { info.stages |= VK_PIPELINE_STAGE_TRANSFER_BIT; info.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; info.access |= VK_ACCESS_TRANSFER_WRITE_BIT; memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; } return m_parent->GetDXVKDevice()->createBuffer(info, memoryFlags); } Rc D3D9CommonBuffer::CreateStagingBuffer() const { DxvkBufferCreateInfo info; info.size = m_desc.Size; info.stages = VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; info.access = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT; if (!(m_desc.Usage & D3DUSAGE_WRITEONLY)) info.access |= VK_ACCESS_HOST_READ_BIT; VkMemoryPropertyFlags memoryFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; return m_parent->GetDXVKDevice()->createBuffer(info, memoryFlags); } }dxvk-2.6.1/src/d3d9/d3d9_common_buffer.h000066400000000000000000000147251477473124000176650ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_device.h" #include "../dxvk/dxvk_cs.h" #include "d3d9_device_child.h" #include "d3d9_format.h" namespace dxvk { /** * \brief Buffer map mode */ enum D3D9_COMMON_BUFFER_MAP_MODE { D3D9_COMMON_BUFFER_MAP_MODE_BUFFER, D3D9_COMMON_BUFFER_MAP_MODE_DIRECT }; /** * \brief Common buffer descriptor */ struct D3D9_BUFFER_DESC { D3DRESOURCETYPE Type; UINT Size; DWORD Usage; D3D9Format Format; D3DPOOL Pool; DWORD FVF; }; /** * \brief The type of buffer you want to use */ enum D3D9_COMMON_BUFFER_TYPE { D3D9_COMMON_BUFFER_TYPE_MAPPING, D3D9_COMMON_BUFFER_TYPE_STAGING, D3D9_COMMON_BUFFER_TYPE_REAL }; struct D3D9Range { D3D9Range() { Clear(); } D3D9Range(uint32_t min, uint32_t max) : min(min), max(max) { } inline bool IsDegenerate() const { return min == max; } inline void Conjoin(D3D9Range range) { if (IsDegenerate()) *this = range; else { min = std::min(range.min, min); max = std::max(range.max, max); } } inline bool Overlaps(D3D9Range range) { if (IsDegenerate()) return false; return range.max > min && range.min < max; } inline void Clear() { min = 0; max = 0; } uint32_t min = 0; uint32_t max = 0; }; class D3D9CommonBuffer { static constexpr VkDeviceSize BufferSliceAlignment = 64; public: D3D9CommonBuffer( D3D9DeviceEx* pDevice, const D3D9_BUFFER_DESC* pDesc); ~D3D9CommonBuffer(); HRESULT Lock( UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags); HRESULT Unlock(); /** * \brief Determine the mapping mode of the buffer, (ie. direct mapping or backed) */ D3D9_COMMON_BUFFER_MAP_MODE DetermineMapMode(const D3D9Options* options) const; /** * \brief Get the mapping mode of the buffer, (ie. direct mapping or backed) */ inline D3D9_COMMON_BUFFER_MAP_MODE GetMapMode() const { return m_mapMode; } /** * \brief Abstraction for getting a type of buffer (mapping/staging/the real buffer) across mapping modes. */ template inline const Rc& GetBuffer() const { if constexpr (Type == D3D9_COMMON_BUFFER_TYPE_MAPPING) return GetMapBuffer(); else if constexpr (Type == D3D9_COMMON_BUFFER_TYPE_STAGING) return GetStagingBuffer(); else //if constexpr (Type == D3D9_COMMON_BUFFER_TYPE_REAL) return GetRealBuffer(); } template inline DxvkBufferSlice GetBufferSlice() const { return GetBufferSlice(0, m_desc.Size); } template inline DxvkBufferSlice GetBufferSlice(VkDeviceSize offset) const { return GetBufferSlice(offset, m_desc.Size - offset); } template inline DxvkBufferSlice GetBufferSlice(VkDeviceSize offset, VkDeviceSize length) const { if (likely(length && offset < m_desc.Size)) { return DxvkBufferSlice(GetBuffer(), offset, std::min(m_desc.Size - offset, length)); } return DxvkBufferSlice(); } inline Rc DiscardMapSlice() { m_allocation = GetMapBuffer()->allocateStorage(); return m_allocation; } inline Rc GetMappedSlice() const { return m_allocation; } inline DWORD GetMapFlags() const { return m_mapFlags; } inline void SetMapFlags(DWORD Flags) { m_mapFlags = Flags; } inline const D3D9_BUFFER_DESC* Desc() const { return &m_desc; } static HRESULT ValidateBufferProperties(const D3D9_BUFFER_DESC* pDesc); /** * \brief The range of the buffer that was changed using Lock calls */ inline D3D9Range& DirtyRange() { return m_dirtyRange; } /** * \brief Whether or not the buffer was written to by the GPU (in IDirect3DDevice9::ProcessVertices) */ inline bool NeedsReadback() const { return m_needsReadback; } /** * \brief Sets whether or not the buffer was written to by the GPU */ inline void SetNeedsReadback(bool state) { m_needsReadback = state; } inline uint32_t IncrementLockCount() { return ++m_lockCount; } inline uint32_t DecrementLockCount() { if (m_lockCount == 0) return 0; return --m_lockCount; } inline uint32_t GetLockCount() const { return m_lockCount; } /** * \brief Whether or not the staging buffer needs to be copied to the actual buffer */ inline bool NeedsUpload() const { return m_desc.Pool != D3DPOOL_DEFAULT && !m_dirtyRange.IsDegenerate(); } void PreLoad(); bool HasSequenceNumber() const { return m_mapMode != D3D9_COMMON_BUFFER_MAP_MODE_DIRECT; } /** * \brief Tracks sequence number * * Stores which CS chunk the resource was last used on. * \param [in] Seq Sequence number */ void TrackMappingBufferSequenceNumber(uint64_t Seq) { m_seq = Seq; } /** * \brief Queries sequence number * * Returns which CS chunk the resource was last used on. * \returns Sequence number */ uint64_t GetMappingBufferSequenceNumber() const { return HasSequenceNumber() ? m_seq : DxvkCsThread::SynchronizeAll; } bool DoPerDrawUpload() const { return m_desc.Pool == D3DPOOL_SYSTEMMEM && (m_desc.Usage & D3DUSAGE_DYNAMIC) != 0; } private: Rc CreateBuffer() const; Rc CreateStagingBuffer() const; inline const Rc& GetMapBuffer() const { return m_stagingBuffer != nullptr ? m_stagingBuffer : m_buffer; } inline const Rc& GetStagingBuffer() const { return m_stagingBuffer; } inline const Rc& GetRealBuffer() const { return m_buffer; } D3D9DeviceEx* m_parent; const D3D9_BUFFER_DESC m_desc; DWORD m_mapFlags = 0; bool m_needsReadback = false; D3D9_COMMON_BUFFER_MAP_MODE m_mapMode; Rc m_buffer; Rc m_stagingBuffer; Rc m_allocation; D3D9Range m_dirtyRange; uint32_t m_lockCount = 0; uint64_t m_seq = 0ull; }; }dxvk-2.6.1/src/d3d9/d3d9_common_texture.cpp000066400000000000000000000667571477473124000204630ustar00rootroot00000000000000#include "d3d9_common_texture.h" #include "d3d9_util.h" #include "d3d9_device.h" #include "../util/util_shared_res.h" #include "../util/util_win32_compat.h" #include namespace dxvk { D3D9CommonTexture::D3D9CommonTexture( D3D9DeviceEx* pDevice, IUnknown* pInterface, const D3D9_COMMON_TEXTURE_DESC* pDesc, D3DRESOURCETYPE ResourceType, HANDLE* pSharedHandle) : m_device(pDevice), m_desc(*pDesc), m_type(ResourceType), m_d3d9Interop(pInterface, this) { if (m_desc.Format == D3D9Format::Unknown) m_desc.Format = (m_desc.Usage & D3DUSAGE_DEPTHSTENCIL) ? D3D9Format::D24X8 : D3D9Format::X8R8G8B8; m_exposedMipLevels = m_desc.MipLevels; if (m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP) m_exposedMipLevels = 1; for (uint32_t i = 0; i < m_dirtyBoxes.size(); i++) { AddDirtyBox(nullptr, i); } if (m_desc.Pool != D3DPOOL_DEFAULT && pSharedHandle) { throw DxvkError("D3D9: Incompatible pool type for texture sharing."); } if (IsPoolManaged(m_desc.Pool)) { SetAllNeedUpload(); } m_mapping = pDevice->LookupFormat(m_desc.Format); m_mapMode = DetermineMapMode(); m_shadow = DetermineShadowState(); m_upgradedToD32f = ConvertFormatUnfixed(m_desc.Format).FormatColor != m_mapping.FormatColor && (m_mapping.FormatColor == VK_FORMAT_D32_SFLOAT_S8_UINT || m_mapping.FormatColor == VK_FORMAT_D32_SFLOAT); m_supportsFetch4 = DetermineFetch4Compatibility(); const bool createImage = m_desc.Pool != D3DPOOL_SYSTEMMEM && m_desc.Pool != D3DPOOL_SCRATCH && m_desc.Format != D3D9Format::NULL_FORMAT; if (createImage) { bool plainSurface = m_type == D3DRTYPE_SURFACE && !(m_desc.Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)); try { m_image = CreatePrimaryImage(ResourceType, plainSurface, pSharedHandle); } catch (const DxvkError& e) { // D3DUSAGE_AUTOGENMIPMAP and offscreen plain is mutually exclusive // so we can combine their retry this way. if (m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP || plainSurface) { m_desc.Usage &= ~D3DUSAGE_AUTOGENMIPMAP; m_desc.MipLevels = 1; m_image = CreatePrimaryImage(ResourceType, false, pSharedHandle); } else throw e; } if (pSharedHandle && *pSharedHandle == nullptr) { *pSharedHandle = m_image->sharedHandle(); ExportImageInfo(); } if ((m_image->info().usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0) CreateSampleView(0); if (!IsManaged()) { m_size = m_image->getMemoryInfo().size; if (!m_device->ChangeReportedMemory(-m_size)) throw DxvkError("D3D9: Reporting out of memory from tracking."); } } for (uint32_t i = 0; i < CountSubresources(); i++) { m_memoryOffset[i] = m_totalSize; m_totalSize += GetMipSize(i); } // Initialization is handled by D3D9Initializer if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE) m_data = m_device->GetAllocator()->Alloc(m_totalSize); else if (m_mapMode != D3D9_COMMON_TEXTURE_MAP_MODE_NONE && m_desc.Pool != D3DPOOL_DEFAULT) CreateBuffer(false); } D3D9CommonTexture::~D3D9CommonTexture() { if (m_size != 0) m_device->ChangeReportedMemory(m_size); m_device->RemoveMappedTexture(this); if (m_desc.Pool == D3DPOOL_DEFAULT) m_device->DecrementLosableCounter(); } VkImageSubresource D3D9CommonTexture::GetSubresourceFromIndex( VkImageAspectFlags Aspect, UINT Subresource) const { VkImageSubresource result; result.aspectMask = Aspect; result.mipLevel = Subresource % m_desc.MipLevels; result.arrayLayer = Subresource / m_desc.MipLevels; return result; } HRESULT D3D9CommonTexture::NormalizeTextureProperties( D3D9DeviceEx* pDevice, D3DRESOURCETYPE ResourceType, D3D9_COMMON_TEXTURE_DESC* pDesc) { auto* options = pDevice->GetOptions(); ////////////////////// // Mapping Validation auto mapping = pDevice->LookupFormat(pDesc->Format); // Handle DisableA8RT hack for The Sims 2 if (pDesc->Format == D3D9Format::A8 && (pDesc->Usage & D3DUSAGE_RENDERTARGET) && options->disableA8RT) return D3DERR_INVALIDCALL; // Cube textures with depth formats are not supported on any native // driver, and allowing them triggers a broken code path in Gothic 3. if (ResourceType == D3DRTYPE_CUBETEXTURE && mapping.Aspect != VK_IMAGE_ASPECT_COLOR_BIT) return D3DERR_INVALIDCALL; // If the mapping is invalid then lets return invalid // Some edge cases: // NULL format does not map to anything, but should succeed // SCRATCH textures can still be made if the device does not support // the format at all. if (!mapping.IsValid() && pDesc->Format != D3D9Format::NULL_FORMAT) { auto info = pDevice->UnsupportedFormatInfo(pDesc->Format); if (pDesc->Pool != D3DPOOL_SCRATCH || info->elementSize == 0) return D3DERR_INVALIDCALL; } /////////////////// // Desc Validation if (pDesc->Width == 0 || pDesc->Height == 0 || pDesc->Depth == 0) return D3DERR_INVALIDCALL; // Native drivers won't allow the creation of DXT format // textures that aren't aligned to block dimensions. if (IsDXTFormat(pDesc->Format)) { D3D9_FORMAT_BLOCK_SIZE blockSize = GetFormatAlignedBlockSize(pDesc->Format); if ((blockSize.Width && (pDesc->Width & (blockSize.Width - 1))) || (blockSize.Height && (pDesc->Height & (blockSize.Height - 1)))) return D3DERR_INVALIDCALL; } if (FAILED(DecodeMultiSampleType(pDevice->GetDXVKDevice(), pDesc->MultiSample, pDesc->MultisampleQuality, nullptr))) return D3DERR_INVALIDCALL; // Using MANAGED pool with DYNAMIC usage is illegal if (IsPoolManaged(pDesc->Pool) && (pDesc->Usage & D3DUSAGE_DYNAMIC)) return D3DERR_INVALIDCALL; // D3DUSAGE_WRITEONLY doesn't apply to textures. if (pDesc->Usage & D3DUSAGE_WRITEONLY) return D3DERR_INVALIDCALL; constexpr DWORD usageRTOrDS = D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL; // RENDERTARGET and DEPTHSTENCIL must be default pool if (pDesc->Pool != D3DPOOL_DEFAULT && (pDesc->Usage & usageRTOrDS)) return D3DERR_INVALIDCALL; // RENDERTARGET and DEPTHSTENCIL in D3DPOOL_DEFAULT // can not also have DYNAMIC usage if (pDesc->Pool == D3DPOOL_DEFAULT && (pDesc->Usage & usageRTOrDS) && (pDesc->Usage & D3DUSAGE_DYNAMIC)) return D3DERR_INVALIDCALL; const bool isPlainSurface = ResourceType == D3DRTYPE_SURFACE && !(pDesc->Usage & usageRTOrDS); const bool isDepthStencilFormat = IsDepthStencilFormat(pDesc->Format); // With the exception of image surfaces (d3d8) // or plain offscreen surfaces (d3d9), depth stencil // formats can only be used in D3DPOOL_DEFAULT if (!isPlainSurface && pDesc->Pool != D3DPOOL_DEFAULT && isDepthStencilFormat) return D3DERR_INVALIDCALL; // Depth stencil formats can not have RENDERTARGET // usage, and nothing except depth stencil formats // can have DEPTHSTENCIL usage if (( isDepthStencilFormat && (pDesc->Usage & D3DUSAGE_RENDERTARGET)) || (!isDepthStencilFormat && (pDesc->Usage & D3DUSAGE_DEPTHSTENCIL))) return D3DERR_INVALIDCALL; // Volume textures can not be used as render targets if (ResourceType == D3DRTYPE_VOLUMETEXTURE && (pDesc->Usage & D3DUSAGE_RENDERTARGET)) return D3DERR_INVALIDCALL; // Volume textures in D3DPOOL_SCRATCH must not have DYNAMIC usage if (ResourceType == D3DRTYPE_VOLUMETEXTURE && pDesc->Pool == D3DPOOL_SCRATCH && (pDesc->Usage & D3DUSAGE_DYNAMIC)) return D3DERR_INVALIDCALL; // Use the maximum possible mip level count if the supplied // mip level count is either unspecified (0) or invalid const uint32_t maxMipLevelCount = pDesc->MultiSample <= D3DMULTISAMPLE_NONMASKABLE ? util::computeMipLevelCount({ pDesc->Width, pDesc->Height, pDesc->Depth }) : 1u; if (pDesc->Usage & D3DUSAGE_AUTOGENMIPMAP) pDesc->MipLevels = 0; if (pDesc->MipLevels == 0 || pDesc->MipLevels > maxMipLevelCount) pDesc->MipLevels = maxMipLevelCount; if (unlikely(pDesc->Discard)) { if (!isDepthStencilFormat) return D3DERR_INVALIDCALL; if (pDesc->Format == D3D9Format::D32_LOCKABLE || pDesc->Format == D3D9Format::D32F_LOCKABLE || pDesc->Format == D3D9Format::D16_LOCKABLE || pDesc->Format == D3D9Format::S8_LOCKABLE) return D3DERR_INVALIDCALL; } return D3D_OK; } void* D3D9CommonTexture::GetData(UINT Subresource) { if (unlikely(m_buffer != nullptr)) return m_buffer->mapPtr(m_memoryOffset[Subresource]); m_data.Map(); uint8_t* ptr = reinterpret_cast(m_data.Ptr()); if (ptr == nullptr) return nullptr; ptr += m_memoryOffset[Subresource]; return ptr; } void D3D9CommonTexture::CreateBuffer(bool Initialize) { if (likely(m_buffer != nullptr)) return; DxvkBufferCreateInfo info; info.size = m_totalSize; info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_HOST_BIT; info.access = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_HOST_READ_BIT; if (m_mapping.ConversionFormatInfo.FormatType != D3D9ConversionFormat_None) { info.usage |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; info.stages |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; } VkMemoryPropertyFlags memType = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; m_buffer = m_device->GetDXVKDevice()->createBuffer(info, memType); if (Initialize) { if (m_data) { m_data.Map(); std::memcpy(m_buffer->mapPtr(0), m_data.Ptr(), m_totalSize); } else { std::memset(m_buffer->mapPtr(0), 0, m_totalSize); } } m_data = {}; } VkDeviceSize D3D9CommonTexture::GetMipSize(UINT Subresource) const { const UINT MipLevel = Subresource % m_desc.MipLevels; const DxvkFormatInfo* formatInfo = m_mapping.FormatColor != VK_FORMAT_UNDEFINED ? lookupFormatInfo(m_mapping.FormatColor) : m_device->UnsupportedFormatInfo(m_desc.Format); const VkExtent3D mipExtent = util::computeMipLevelExtent( GetExtent(), MipLevel); VkExtent3D blockSize = formatInfo->blockSize; uint32_t elementSize = formatInfo->elementSize; if (unlikely(formatInfo->flags.test(DxvkFormatFlag::MultiPlane))) { // D3D9 doesn't allow specifying the plane when locking a texture. // So the subsampled planes inherit the pitch of the first plane. // That means the size is the size of plane 0 * plane count elementSize = formatInfo->planes[0].elementSize; blockSize = { formatInfo->planes[0].blockSize.width, formatInfo->planes[0].blockSize.height, 1u }; } const VkExtent3D blockCount = util::computeBlockCount( mipExtent, blockSize); return std::min(GetPlaneCount(), 2u) * align(elementSize * blockCount.width, 4) * blockCount.height * blockCount.depth; } Rc D3D9CommonTexture::CreatePrimaryImage(D3DRESOURCETYPE ResourceType, bool TryOffscreenRT, HANDLE* pSharedHandle) const { DxvkImageCreateInfo imageInfo; imageInfo.type = GetImageTypeFromResourceType(ResourceType); imageInfo.format = m_mapping.ConversionFormatInfo.FormatColor != VK_FORMAT_UNDEFINED ? m_mapping.ConversionFormatInfo.FormatColor : m_mapping.FormatColor; imageInfo.flags = 0; imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT; imageInfo.extent.width = m_desc.Width; imageInfo.extent.height = m_desc.Height; imageInfo.extent.depth = m_desc.Depth; imageInfo.numLayers = m_desc.ArraySize; imageInfo.mipLevels = m_desc.MipLevels; imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | m_desc.ImageUsage; imageInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT | m_device->GetEnabledShaderStages(); imageInfo.access = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.layout = VK_IMAGE_LAYOUT_GENERAL; imageInfo.shared = m_desc.IsBackBuffer; if (pSharedHandle) { imageInfo.sharing.type = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT; imageInfo.sharing.mode = (*pSharedHandle == INVALID_HANDLE_VALUE || *pSharedHandle == nullptr) ? DxvkSharedHandleMode::Export : DxvkSharedHandleMode::Import; imageInfo.sharing.handle = *pSharedHandle; imageInfo.shared = true; // TODO: validate metadata? } if (m_mapping.ConversionFormatInfo.FormatType != D3D9ConversionFormat_None) { imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT; imageInfo.stages |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; imageInfo.shared = true; } DecodeMultiSampleType(m_device->GetDXVKDevice(), m_desc.MultiSample, m_desc.MultisampleQuality, &imageInfo.sampleCount); if (!m_desc.IsAttachmentOnly) imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT; // The image must be marked as mutable if it can be reinterpreted // by a view with a different format. Depth-stencil formats cannot // be reinterpreted in Vulkan, so we'll ignore those. auto formatProperties = lookupFormatInfo(m_mapping.FormatColor); bool isMutable = m_mapping.FormatSrgb != VK_FORMAT_UNDEFINED; bool isColorFormat = (formatProperties->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0; if (isMutable && isColorFormat) { imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; imageInfo.viewFormatCount = 2; imageInfo.viewFormats = m_mapping.Formats; } const bool hasAttachmentFeedbackLoops = m_device->GetDXVKDevice()->features().extAttachmentFeedbackLoopLayout.attachmentFeedbackLoopLayout; const bool isRT = m_desc.Usage & D3DUSAGE_RENDERTARGET; const bool isDS = m_desc.Usage & D3DUSAGE_DEPTHSTENCIL; const bool isAutoGen = m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP; // Are we an RT, need to gen mips or an offscreen plain surface? if (isRT || isAutoGen || TryOffscreenRT) { imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; imageInfo.stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; imageInfo.access |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; } if (isDS) { imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; imageInfo.stages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; imageInfo.access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; } if (ResourceType == D3DRTYPE_TEXTURE && (isRT || isDS) && hasAttachmentFeedbackLoops) imageInfo.usage |= VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT; if (ResourceType == D3DRTYPE_CUBETEXTURE) imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; // Some image formats (i.e. the R32G32B32 ones) are // only supported with linear tiling on most GPUs if (!CheckImageSupport(&imageInfo, VK_IMAGE_TILING_OPTIMAL)) imageInfo.tiling = VK_IMAGE_TILING_LINEAR; // We must keep LINEAR images in GENERAL layout, but we // can choose a better layout for the image based on how // it is going to be used by the game. if (imageInfo.tiling == VK_IMAGE_TILING_OPTIMAL && imageInfo.sharing.mode == DxvkSharedHandleMode::None) imageInfo.layout = OptimizeLayout(imageInfo.usage); // Check if we can actually create the image if (!CheckImageSupport(&imageInfo, imageInfo.tiling)) { throw DxvkError(str::format( "D3D9: Cannot create texture:", "\n Type: 0x", std::hex, ResourceType, std::dec, "\n Format: ", m_desc.Format, "\n Extent: ", m_desc.Width, "x", m_desc.Height, "x", m_desc.Depth, "\n Samples: ", m_desc.MultiSample, "\n Layers: ", m_desc.ArraySize, "\n Levels: ", m_desc.MipLevels, "\n Usage: 0x", std::hex, m_desc.Usage, std::dec, "\n Pool: 0x", std::hex, m_desc.Pool, std::dec)); } return m_device->GetDXVKDevice()->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); } Rc D3D9CommonTexture::CreateResolveImage() const { DxvkImageCreateInfo imageInfo = m_image->info(); imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT; return m_device->GetDXVKDevice()->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); } BOOL D3D9CommonTexture::DetermineShadowState() const { constexpr std::array blacklist = { D3D9Format::INTZ, D3D9Format::DF16, D3D9Format::DF24 }; return IsDepthFormat(m_desc.Format) && std::find(blacklist.begin(), blacklist.end(), m_desc.Format) == blacklist.end(); } BOOL D3D9CommonTexture::DetermineFetch4Compatibility() const { constexpr std::array singleChannelFormats = { D3D9Format::INTZ, D3D9Format::DF16, D3D9Format::DF24, D3D9Format::R16F, D3D9Format::R32F, D3D9Format::A8, D3D9Format::L8, D3D9Format::L16 }; return std::find(singleChannelFormats.begin(), singleChannelFormats.end(), m_desc.Format) != singleChannelFormats.end(); } BOOL D3D9CommonTexture::CheckImageSupport( const DxvkImageCreateInfo* pImageInfo, VkImageTiling Tiling) const { DxvkFormatQuery formatQuery = { }; formatQuery.format = pImageInfo->format; formatQuery.type = pImageInfo->type; formatQuery.tiling = Tiling; formatQuery.usage = pImageInfo->usage; formatQuery.flags = pImageInfo->flags; auto properties = m_device->GetDXVKDevice()->getFormatLimits(formatQuery); if (!properties) return FALSE; return (pImageInfo->extent.width <= properties->maxExtent.width) && (pImageInfo->extent.height <= properties->maxExtent.height) && (pImageInfo->extent.depth <= properties->maxExtent.depth) && (pImageInfo->numLayers <= properties->maxArrayLayers) && (pImageInfo->mipLevels <= properties->maxMipLevels) && (pImageInfo->sampleCount & properties->sampleCounts); } VkImageType D3D9CommonTexture::GetImageTypeFromResourceType(D3DRESOURCETYPE Type) { switch (Type) { case D3DRTYPE_SURFACE: case D3DRTYPE_TEXTURE: return VK_IMAGE_TYPE_2D; case D3DRTYPE_VOLUMETEXTURE: return VK_IMAGE_TYPE_3D; case D3DRTYPE_CUBETEXTURE: return VK_IMAGE_TYPE_2D; default: throw DxvkError("D3D9CommonTexture: Unhandled resource type"); } } VkImageViewType D3D9CommonTexture::GetImageViewTypeFromResourceType( D3DRESOURCETYPE Dimension, UINT Layer) { switch (Dimension) { case D3DRTYPE_SURFACE: case D3DRTYPE_TEXTURE: return VK_IMAGE_VIEW_TYPE_2D; case D3DRTYPE_VOLUMETEXTURE: return VK_IMAGE_VIEW_TYPE_3D; case D3DRTYPE_CUBETEXTURE: return Layer == AllLayers ? VK_IMAGE_VIEW_TYPE_CUBE : VK_IMAGE_VIEW_TYPE_2D; default: throw DxvkError("D3D9CommonTexture: Unhandled resource type"); } } VkImageLayout D3D9CommonTexture::OptimizeLayout(VkImageUsageFlags Usage) const { // Filter out unnecessary flags. Transfer operations // are handled by the backend in a transparent manner. // Feedback loops are handled by hazard tracking. Usage &= ~(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT); // Storage images require GENERAL. if (Usage & VK_IMAGE_USAGE_STORAGE_BIT) return VK_IMAGE_LAYOUT_GENERAL; // Use GENERAL for non-renderable images to avoid layout transitions. if (Usage == VK_IMAGE_USAGE_SAMPLED_BIT) return VK_IMAGE_LAYOUT_GENERAL; // If the image is used only as an attachment, we never // have to transform the image back to a different layout. if (Usage == VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; if (Usage == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // Fall back to GENERAL if the image is not shader-readable if (!(Usage & VK_IMAGE_USAGE_SAMPLED_BIT)) return VK_IMAGE_LAYOUT_GENERAL; // Otherwise, pick a layout that can be used for reading. return (Usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } D3D9_COMMON_TEXTURE_MAP_MODE D3D9CommonTexture::DetermineMapMode() const { if (m_desc.Format == D3D9Format::NULL_FORMAT) return D3D9_COMMON_TEXTURE_MAP_MODE_NONE; #ifdef D3D9_ALLOW_UNMAPPING if (m_device->GetOptions()->textureMemory != 0 && m_desc.Pool != D3DPOOL_DEFAULT) return D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE; #endif if (m_desc.Pool == D3DPOOL_SYSTEMMEM || m_desc.Pool == D3DPOOL_SCRATCH) return D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM; return D3D9_COMMON_TEXTURE_MAP_MODE_BACKED; } void D3D9CommonTexture::ExportImageInfo() { /* From MSDN: Textures being shared from D3D9 to D3D11 have the following restrictions. - Textures must be 2D - Only 1 mip level is allowed - Texture must have default usage - Texture must be write only - MSAA textures are not allowed - Bind flags must have SHADER_RESOURCE and RENDER_TARGET set - Only R10G10B10A2_UNORM, R16G16B16A16_FLOAT and R8G8B8A8_UNORM formats are allowed */ DXGI_FORMAT dxgiFormat = DXGI_FORMAT_UNKNOWN; switch (m_desc.Format) { case D3D9Format::A2B10G10R10: dxgiFormat = DXGI_FORMAT_R10G10B10A2_UNORM; break; case D3D9Format::A16B16G16R16F: dxgiFormat = DXGI_FORMAT_R16G16B16A16_FLOAT; break; case D3D9Format::A8B8G8R8: dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM; break; case D3D9Format::X8B8G8R8: dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM; break; /* No RGBX in DXGI */ case D3D9Format::A8R8G8B8: dxgiFormat = DXGI_FORMAT_B8G8R8A8_UNORM; break; case D3D9Format::X8R8G8B8: dxgiFormat = DXGI_FORMAT_B8G8R8X8_UNORM; break; default: Logger::warn(str::format("D3D9: Unsupported format for shared textures: ", m_desc.Format)); return; } if (m_desc.Depth == 1 && m_desc.MipLevels == 1 && m_desc.MultiSample == D3DMULTISAMPLE_NONE && m_desc.Usage & D3DUSAGE_RENDERTARGET && dxgiFormat != DXGI_FORMAT_UNKNOWN) { HANDLE ntHandle = openKmtHandle(m_image->sharedHandle()); DxvkSharedTextureMetadata metadata; metadata.Width = m_desc.Width; metadata.Height = m_desc.Height; metadata.MipLevels = m_desc.MipLevels; metadata.ArraySize = m_desc.ArraySize; metadata.Format = dxgiFormat; metadata.SampleDesc.Count = 1; metadata.SampleDesc.Quality = 0; metadata.Usage = D3D11_USAGE_DEFAULT; metadata.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; metadata.CPUAccessFlags = 0; metadata.MiscFlags = D3D11_RESOURCE_MISC_SHARED; metadata.TextureLayout = D3D11_TEXTURE_LAYOUT_UNDEFINED; if (ntHandle == INVALID_HANDLE_VALUE || !setSharedMetadata(ntHandle, &metadata, sizeof(metadata))) Logger::warn("D3D9: Failed to write shared resource info for a texture"); if (ntHandle != INVALID_HANDLE_VALUE) ::CloseHandle(ntHandle); } } Rc D3D9CommonTexture::CreateView( UINT Layer, UINT Lod, VkImageUsageFlags UsageFlags, bool Srgb) { DxvkImageViewKey viewInfo; viewInfo.format = m_mapping.ConversionFormatInfo.FormatColor != VK_FORMAT_UNDEFINED ? PickSRGB(m_mapping.ConversionFormatInfo.FormatColor, m_mapping.ConversionFormatInfo.FormatSrgb, Srgb) : PickSRGB(m_mapping.FormatColor, m_mapping.FormatSrgb, Srgb); viewInfo.aspects = lookupFormatInfo(viewInfo.format)->aspectMask; viewInfo.usage = UsageFlags; viewInfo.viewType = GetImageViewTypeFromResourceType(m_type, Layer); viewInfo.mipIndex = Lod; viewInfo.mipCount = m_desc.MipLevels - Lod; viewInfo.layerIndex = Layer == AllLayers ? 0 : Layer; viewInfo.layerCount = Layer == AllLayers ? m_desc.ArraySize : 1; viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(m_mapping.Swizzle); // Remove the stencil aspect if we are trying to create a regular image // view of a depth stencil format if (UsageFlags != VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) viewInfo.aspects &= ~VK_IMAGE_ASPECT_STENCIL_BIT; if (UsageFlags == VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT || UsageFlags == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) viewInfo.mipCount = 1; // Remove swizzle on depth views. if (UsageFlags == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) viewInfo.packedSwizzle = 0u; // Create the underlying image view object return GetImage()->createView(viewInfo); } void D3D9CommonTexture::PreLoadAll() { if (!IsManaged()) return; auto lock = m_device->LockDevice(); m_device->UploadManagedTexture(this); m_device->MarkTextureUploaded(this); } void D3D9CommonTexture::PreLoadSubresource(UINT Subresource) { if (IsManaged()) { auto lock = m_device->LockDevice(); if (NeedsUpload(Subresource)) { m_device->FlushImage(this, Subresource); SetNeedsUpload(Subresource, false); if (!NeedsAnyUpload()) m_device->MarkTextureUploaded(this); } } } void D3D9CommonTexture::CreateSampleView(UINT Lod) { // This will be a no-op for SYSTEMMEM types given we // don't expose the cap to allow texturing with them. if (unlikely(m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM)) return; m_sampleView.Color = CreateView(AllLayers, Lod, VK_IMAGE_USAGE_SAMPLED_BIT, false); if (IsSrgbCompatible()) m_sampleView.Srgb = CreateView(AllLayers, Lod, VK_IMAGE_USAGE_SAMPLED_BIT, true); } const Rc& D3D9CommonTexture::GetBuffer() { return m_buffer; } DxvkBufferSlice D3D9CommonTexture::GetBufferSlice(UINT Subresource) { return DxvkBufferSlice(GetBuffer(), m_memoryOffset[Subresource], GetMipSize(Subresource)); } uint32_t D3D9CommonTexture::GetPlaneCount() const { const DxvkFormatInfo* formatInfo = m_mapping.FormatColor != VK_FORMAT_UNDEFINED ? lookupFormatInfo(m_mapping.FormatColor) : m_device->UnsupportedFormatInfo(m_desc.Format); return vk::getPlaneCount(formatInfo->aspectMask); } } dxvk-2.6.1/src/d3d9/d3d9_common_texture.h000066400000000000000000000401741477473124000201110ustar00rootroot00000000000000#pragma once #include "d3d9_format.h" #include "d3d9_util.h" #include "d3d9_caps.h" #include "d3d9_mem.h" #include "d3d9_interop.h" #include "../dxvk/dxvk_device.h" #include "../util/util_bit.h" namespace dxvk { class D3D9DeviceEx; /** * \brief Image memory mapping mode * * Determines how exactly \c LockBox will * behave when mapping an image. */ enum D3D9_COMMON_TEXTURE_MAP_MODE { D3D9_COMMON_TEXTURE_MAP_MODE_NONE, ///< No mapping available D3D9_COMMON_TEXTURE_MAP_MODE_BACKED, ///< Mapped image through buffer D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM, ///< Only a buffer - no image D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE, ///< Non-Vulkan memory that can be unmapped }; /** * \brief Common texture description * * Contains all members that can be * defined for 2D, Cube and 3D textures. */ struct D3D9_COMMON_TEXTURE_DESC { UINT Width; UINT Height; UINT Depth; UINT ArraySize; UINT MipLevels; DWORD Usage; D3D9Format Format; D3DPOOL Pool; D3DMULTISAMPLE_TYPE MultiSample; DWORD MultisampleQuality; bool Discard; bool IsBackBuffer; bool IsAttachmentOnly; bool IsLockable; // Additional parameters for ID3D9VkInteropDevice VkImageUsageFlags ImageUsage = 0; }; struct D3D9ColorView { inline Rc& Pick(bool Srgb) { return Srgb ? this->Srgb : this->Color; } inline const Rc& Pick(bool Srgb) const { return Srgb ? this->Srgb : this->Color; } Rc Color; Rc Srgb; }; template using D3D9SubresourceArray = std::array; using D3D9SubresourceBitset = bit::bitset; class D3D9CommonTexture { public: static constexpr UINT AllLayers = std::numeric_limits::max(); D3D9CommonTexture( D3D9DeviceEx* pDevice, IUnknown* pInterface, const D3D9_COMMON_TEXTURE_DESC* pDesc, D3DRESOURCETYPE ResourceType, HANDLE* pSharedHandle); ~D3D9CommonTexture(); /** * \brief Device * \returns The parent device */ D3D9DeviceEx* Device() const { return m_device; } /** * \brief Texture properties * * The returned data can be used to fill in * \c D3D11_TEXTURE2D_DESC and similar structs. * \returns Pointer to texture description */ const D3D9_COMMON_TEXTURE_DESC* Desc() const { return &m_desc; } /** * \brief Vulkan Format * \returns The Vulkan format of the resource */ const D3D9_VK_FORMAT_MAPPING& GetFormatMapping() const { return m_mapping; } /** * \brief Counts number of subresources * \returns Number of subresources */ UINT CountSubresources() const { return m_desc.ArraySize * m_desc.MipLevels; } /** * \brief Map mode * \returns Map mode */ D3D9_COMMON_TEXTURE_MAP_MODE GetMapMode() const { return m_mapMode; } /** * \brief The DXVK image * Note, this will be nullptr if the map mode is D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM * \returns The DXVK image */ const Rc& GetImage() const { return m_image; } /** * \brief Get a copy of the main image, but with a single sample * This function will allocate/reuse an image with the same info * as the main image * \returns An image with identical info, but 1 sample */ const Rc& GetResolveImage() { if (unlikely(m_resolveImage == nullptr)) m_resolveImage = CreateResolveImage(); return m_resolveImage; } /** * \brief Returns a pointer to the internal data used for LockRect/LockBox * * This works regardless of the map mode used by this texture * and will map the memory if necessary. * \param [in] Subresource Subresource index * @return Pointer to locking data */ void* GetData(UINT Subresource); const Rc& GetBuffer(); DxvkBufferSlice GetBufferSlice(UINT Subresource); /** * \brief Computes subresource from the subresource index * * Used by some functions that operate on only * one subresource, such as \c UpdateSurface. * \param [in] Aspect The image aspect * \param [in] Subresource Subresource index * \returns The Vulkan image subresource */ VkImageSubresource GetSubresourceFromIndex( VkImageAspectFlags Aspect, UINT Subresource) const; /** * \brief Normalizes and validates texture description * * Fills in undefined values and validates the texture * parameters. Any error returned by this method should * be forwarded to the application. * \param [in] pDevice D3D9 device * \param [in] ResourceType Resource type * \param [in,out] pDesc Texture description * \returns \c S_OK if the parameters are valid */ static HRESULT NormalizeTextureProperties( D3D9DeviceEx* pDevice, D3DRESOURCETYPE ResourceType, D3D9_COMMON_TEXTURE_DESC* pDesc); /** * \brief Shadow * \returns Whether the texture is to be depth compared */ bool IsShadow() const { return m_shadow; } /** * \brief Dref Clamp * \returns Whether the texture emulates an UNORM format with D32f */ bool IsUpgradedToD32f() const { return m_upgradedToD32f; } /** * \brief FETCH4 compatibility * \returns Whether the format of the texture supports the FETCH4 hack */ bool SupportsFetch4() const { return m_supportsFetch4; } /** * \brief Null * \returns Whether the texture is D3DFMT_NULL or not */ bool IsNull() const { return m_desc.Format == D3D9Format::NULL_FORMAT; } /** * \brief Subresource * \returns The subresource idx of a given face and mip level */ UINT CalcSubresource(UINT Face, UINT MipLevel) const { return Face * m_desc.MipLevels + MipLevel; } void UnmapData() { m_data.Unmap(); } /** * \brief Destroys a buffer * Destroys mapping and staging buffers for a given subresource */ void DestroyBuffer() { m_buffer = nullptr; MarkAllNeedReadback(); } bool IsDynamic() const { return m_desc.Usage & D3DUSAGE_DYNAMIC; } /** * \brief Managed * \returns Whether a resource is managed (pool) or not */ bool IsManaged() const { return IsPoolManaged(m_desc.Pool); } /** * \brief Render Target * \returns Whether a resource is a render target or not */ bool IsRenderTarget() const { return m_desc.Usage & D3DUSAGE_RENDERTARGET; } /** * \brief Depth stencil * \returns Whether a resource is a depth stencil or not */ bool IsDepthStencil() const { return m_desc.Usage & D3DUSAGE_DEPTHSTENCIL; } /** * \brief Autogen Mipmap * \returns Whether the texture is to have automatic mip generation */ bool IsAutomaticMip() const { return m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP; } /** * \brief Checks whether sRGB views can be created * \returns Whether the format is sRGB compatible. */ bool IsSrgbCompatible() const { return m_mapping.FormatSrgb; } /** * \brief Recreate main image view * Recreates the main view of the sampler w/ a specific LOD. * SetLOD only works on MANAGED textures so this is A-okay. */ void CreateSampleView(UINT Lod); /** * \brief Extent * \returns The extent of the top-level mip */ VkExtent3D GetExtent() const { return VkExtent3D{ m_desc.Width, m_desc.Height, m_desc.Depth }; } /** * \brief Mip Extent * \returns The extent of a mip or subresource */ VkExtent3D GetExtentMip(UINT Subresource) const { UINT MipLevel = Subresource % m_desc.MipLevels; return util::computeMipLevelExtent(GetExtent(), MipLevel); } bool MarkTransitionedToHazardLayout() { return std::exchange(m_transitionedToHazardLayout, true); } D3DRESOURCETYPE GetType() const { return m_type; } uint32_t GetPlaneCount() const; D3DPOOL GetPool() const { return m_desc.Pool; } const D3D9_VK_FORMAT_MAPPING& GetMapping() { return m_mapping; } void SetLocked(UINT Subresource, bool value) { m_locked.set(Subresource, value); } bool GetLocked(UINT Subresource) const { return m_locked.get(Subresource); } bool IsAnySubresourceLocked() const { return m_locked.any(); } void SetNeedsReadback(UINT Subresource, bool value) { m_needsReadback.set(Subresource, value); } bool NeedsReadback(UINT Subresource) const { return m_needsReadback.get(Subresource); } void MarkAllNeedReadback() { m_needsReadback.setAll(); } const Rc& GetSampleView(bool srgb) const { return m_sampleView.Pick(srgb && IsSrgbCompatible()); } VkImageLayout DetermineRenderTargetLayout(VkImageLayout hazardLayout) const { if (unlikely(m_transitionedToHazardLayout)) return hazardLayout; return m_image != nullptr && m_image->info().tiling == VK_IMAGE_TILING_OPTIMAL ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL; } VkImageLayout DetermineDepthStencilLayout(bool write, bool hazardous, VkImageLayout hazardLayout) const { if (unlikely(m_transitionedToHazardLayout)) return hazardLayout; if (unlikely(m_image->info().tiling != VK_IMAGE_TILING_OPTIMAL)) return VK_IMAGE_LAYOUT_GENERAL; if (unlikely(hazardous && !write)) return VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL; return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; } Rc CreateView( UINT Layer, UINT Lod, VkImageUsageFlags UsageFlags, bool Srgb); D3D9SubresourceBitset& GetUploadBitmask() { return m_needsUpload; } void SetAllNeedUpload() { if (likely(!IsAutomaticMip())) { m_needsUpload.setAll(); } else { for (uint32_t a = 0; a < m_desc.ArraySize; a++) { for (uint32_t m = 0; m < ExposedMipLevels(); m++) { SetNeedsUpload(CalcSubresource(a, m), true); } } } } void SetNeedsUpload(UINT Subresource, bool upload) { m_needsUpload.set(Subresource, upload); } bool NeedsUpload(UINT Subresource) const { return m_needsUpload.get(Subresource); } bool NeedsAnyUpload() { return m_needsUpload.any(); } void ClearNeedsUpload() { return m_needsUpload.clearAll(); } void SetNeedsMipGen(bool value) { m_needsMipGen = value; } bool NeedsMipGen() const { return m_needsMipGen; } DWORD ExposedMipLevels() const { return m_exposedMipLevels; } void SetMipFilter(D3DTEXTUREFILTERTYPE filter) { m_mipFilter = filter; } D3DTEXTUREFILTERTYPE GetMipFilter() const { return m_mipFilter; } void PreLoadAll(); void PreLoadSubresource(UINT Subresource); void AddDirtyBox(CONST D3DBOX* pDirtyBox, uint32_t layer) { if (pDirtyBox) { D3DBOX box = *pDirtyBox; if (box.Right <= box.Left || box.Bottom <= box.Top || box.Back <= box.Front) return; box.Right = std::min(box.Right, m_desc.Width); box.Bottom = std::min(box.Bottom, m_desc.Height); box.Back = std::min(box.Back, m_desc.Depth); D3DBOX& dirtyBox = m_dirtyBoxes[layer]; if (dirtyBox.Left == dirtyBox.Right) { dirtyBox = box; } else { dirtyBox.Left = std::min(dirtyBox.Left, box.Left); dirtyBox.Right = std::max(dirtyBox.Right, box.Right); dirtyBox.Top = std::min(dirtyBox.Top, box.Top); dirtyBox.Bottom = std::max(dirtyBox.Bottom, box.Bottom); dirtyBox.Front = std::min(dirtyBox.Front, box.Front); dirtyBox.Back = std::max(dirtyBox.Back, box.Back); } } else { m_dirtyBoxes[layer] = { 0, 0, m_desc.Width, m_desc.Height, 0, m_desc.Depth }; } } void ClearDirtyBoxes() { for (uint32_t i = 0; i < m_dirtyBoxes.size(); i++) { m_dirtyBoxes[i] = { 0, 0, 0, 0, 0, 0 }; } } const D3DBOX& GetDirtyBox(uint32_t layer) const { return m_dirtyBoxes[layer]; } static VkImageType GetImageTypeFromResourceType( D3DRESOURCETYPE Dimension); static VkImageViewType GetImageViewTypeFromResourceType( D3DRESOURCETYPE Dimension, UINT Layer); /** * \brief Tracks sequence number for a given subresource * * Stores which CS chunk the resource was last used on. * \param [in] Subresource Subresource index * \param [in] Seq Sequence number */ void TrackMappingBufferSequenceNumber(UINT Subresource, uint64_t Seq) { if (Subresource < m_seqs.size()) m_seqs[Subresource] = Seq; } /** * \brief Queries sequence number for a given subresource * * Returns which CS chunk the resource was last used on. * \param [in] Subresource Subresource index * \returns Sequence number for the given subresource */ uint64_t GetMappingBufferSequenceNumber(UINT Subresource) { return Subresource < m_seqs.size() ? m_seqs[Subresource] : 0ull; } /** * \brief Mip level * \returns Size of packed mip level in bytes */ VkDeviceSize GetMipSize(UINT Subresource) const; uint32_t GetTotalSize() const { return m_totalSize; } /** * \brief Creates a buffer * Creates the mapping buffer if necessary * \param [in] Initialize Whether to copy over existing data (or clear if there is no data) * \returns Whether an allocation happened */ void CreateBuffer(bool Initialize); ID3D9VkInteropTexture* GetVkInterop() { return &m_d3d9Interop; } private: D3D9DeviceEx* m_device; D3D9_COMMON_TEXTURE_DESC m_desc; D3DRESOURCETYPE m_type; D3D9_COMMON_TEXTURE_MAP_MODE m_mapMode; Rc m_image; Rc m_resolveImage; Rc m_buffer; D3D9Memory m_data = { }; D3D9SubresourceArray< uint64_t> m_seqs = { }; D3D9SubresourceArray< uint32_t> m_memoryOffset = { }; uint32_t m_totalSize = 0; D3D9_VK_FORMAT_MAPPING m_mapping; bool m_shadow; //< Depth Compare-ness bool m_upgradedToD32f; // Dref Clamp bool m_supportsFetch4; int64_t m_size = 0; bool m_transitionedToHazardLayout = false; D3D9ColorView m_sampleView; D3D9SubresourceBitset m_locked = { }; D3D9SubresourceBitset m_needsReadback = { }; D3D9SubresourceBitset m_needsUpload = { }; DWORD m_exposedMipLevels = 0; bool m_needsMipGen = false; D3DTEXTUREFILTERTYPE m_mipFilter = D3DTEXF_LINEAR; std::array m_dirtyBoxes; D3D9VkInteropTexture m_d3d9Interop; Rc CreatePrimaryImage(D3DRESOURCETYPE ResourceType, bool TryOffscreenRT, HANDLE* pSharedHandle) const; Rc CreateResolveImage() const; BOOL DetermineShadowState() const; BOOL DetermineFetch4Compatibility() const; BOOL CheckImageSupport( const DxvkImageCreateInfo* pImageInfo, VkImageTiling Tiling) const; D3D9_COMMON_TEXTURE_MAP_MODE DetermineMapMode() const; VkImageLayout OptimizeLayout( VkImageUsageFlags Usage) const; void ExportImageInfo(); }; } dxvk-2.6.1/src/d3d9/d3d9_constant_buffer.cpp000066400000000000000000000076001477473124000205530ustar00rootroot00000000000000#include "d3d9_constant_buffer.h" #include "d3d9_device.h" namespace dxvk { D3D9ConstantBuffer::D3D9ConstantBuffer() { } D3D9ConstantBuffer::D3D9ConstantBuffer( D3D9DeviceEx* pDevice, DxsoProgramType ShaderStage, DxsoConstantBuffers BufferType, VkDeviceSize Size) : D3D9ConstantBuffer(pDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, GetShaderStage(ShaderStage), computeResourceSlotId(ShaderStage, DxsoBindingType::ConstantBuffer, BufferType), Size) { } D3D9ConstantBuffer::D3D9ConstantBuffer( D3D9DeviceEx* pDevice, VkBufferUsageFlags Usage, VkShaderStageFlags Stages, uint32_t ResourceSlot, VkDeviceSize Size) : m_device (pDevice) , m_binding (ResourceSlot) , m_usage (Usage) , m_stages (Stages) , m_size (Size) , m_align (getAlignment(pDevice->GetDXVKDevice())) { } D3D9ConstantBuffer::~D3D9ConstantBuffer() { } void* D3D9ConstantBuffer::Alloc(VkDeviceSize size) { if (unlikely(m_buffer == nullptr)) m_slice = this->createBuffer(); size = align(size, m_align); if (unlikely(m_offset + size > m_size)) { m_slice = m_buffer->allocateStorage(); m_offset = 0; m_device->EmitCs([ cBuffer = m_buffer, cSlice = m_slice ] (DxvkContext* ctx) mutable { ctx->invalidateBuffer(cBuffer, std::move(cSlice)); }); } m_device->EmitCs([ cStages = m_stages, cBinding = m_binding, cOffset = m_offset, cLength = size ] (DxvkContext* ctx) { ctx->bindUniformBufferRange(cStages, cBinding, cOffset, cLength); }); void* mapPtr = reinterpret_cast(m_slice->mapPtr()) + m_offset; m_offset += size; return mapPtr; } void* D3D9ConstantBuffer::AllocSlice() { if (unlikely(m_buffer == nullptr)) m_slice = this->createBuffer(); else m_slice = m_buffer->allocateStorage(); m_device->EmitCs([ cBuffer = m_buffer, cSlice = m_slice ] (DxvkContext* ctx) mutable { ctx->invalidateBuffer(cBuffer, std::move(cSlice)); }); return m_slice->mapPtr(); } Rc D3D9ConstantBuffer::createBuffer() { auto options = m_device->GetOptions(); // Buffer usage and access flags don't make much of a difference // in the backend, so set both STORAGE and UNIFORM usage/access. DxvkBufferCreateInfo bufferInfo; bufferInfo.size = align(m_size, m_align); bufferInfo.usage = m_usage; bufferInfo.access = 0; bufferInfo.stages = util::pipelineStages(m_stages); bufferInfo.debugName = "Constant buffer"; if (m_usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) bufferInfo.access |= VK_ACCESS_UNIFORM_READ_BIT; if (m_usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) bufferInfo.access |= VK_ACCESS_SHADER_READ_BIT; VkMemoryPropertyFlags memoryFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; if (options->deviceLocalConstantBuffers) memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; m_buffer = m_device->GetDXVKDevice()->createBuffer(bufferInfo, memoryFlags); m_device->EmitCs([ cStages = m_stages, cBinding = m_binding, cSlice = DxvkBufferSlice(m_buffer) ] (DxvkContext* ctx) mutable { ctx->bindUniformBuffer(cStages, cBinding, std::move(cSlice)); }); return m_buffer->storage(); } VkDeviceSize D3D9ConstantBuffer::getAlignment(const Rc& device) const { return std::max(std::max( device->properties().core.properties.limits.minUniformBufferOffsetAlignment, device->properties().core.properties.limits.minStorageBufferOffsetAlignment), device->properties().extRobustness2.robustUniformBufferAccessSizeAlignment); } } dxvk-2.6.1/src/d3d9/d3d9_constant_buffer.h000066400000000000000000000035771477473124000202310ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_buffer.h" #include "../dxso/dxso_util.h" #include "../util/util_math.h" #include "d3d9_include.h" namespace dxvk { class D3D9DeviceEx; /** * \brief Constant buffer */ class D3D9ConstantBuffer { public: D3D9ConstantBuffer(); D3D9ConstantBuffer( D3D9DeviceEx* pDevice, DxsoProgramType ShaderStage, DxsoConstantBuffers BufferType, VkDeviceSize Size); D3D9ConstantBuffer( D3D9DeviceEx* pDevice, VkBufferUsageFlags Usage, VkShaderStageFlags Stages, uint32_t ResourceSlot, VkDeviceSize Size); ~D3D9ConstantBuffer(); /** * \brief Queries alignment * * Useful to pad copies with initialized data. * \returns Data alignment */ VkDeviceSize GetAlignment() const { return m_align; } /** * \brief Allocates a given amount of memory * * \param [in] size Number of bytes to allocate * \returns Map pointer of the allocated region */ void* Alloc(VkDeviceSize size); /** * \brief Allocates a full buffer slice * * This must not be called if \ref Alloc is used. * \returns Map pointer of the allocated region */ void* AllocSlice(); private: D3D9DeviceEx* m_device = nullptr; uint32_t m_binding = 0u; VkBufferUsageFlags m_usage = 0u; VkShaderStageFlags m_stages = 0u; VkDeviceSize m_size = 0ull; VkDeviceSize m_align = 0ull; VkDeviceSize m_offset = 0ull; Rc m_buffer = nullptr; Rc m_slice = nullptr; Rc createBuffer(); VkDeviceSize getAlignment(const Rc& device) const; }; }dxvk-2.6.1/src/d3d9/d3d9_constant_layout.h000066400000000000000000000015161477473124000202640ustar00rootroot00000000000000#pragma once #include #include "d3d9_caps.h" namespace dxvk { struct D3D9ConstantLayout { uint32_t floatCount; uint32_t intCount; uint32_t boolCount; uint32_t bitmaskCount; uint32_t floatSize() const { return floatCount * 4 * sizeof(float); } uint32_t intSize() const { return intCount * 4 * sizeof(int); } uint32_t bitmaskSize() const { // Account for SWVP (non SWVP uses a spec constant) return bitmaskCount != 1 ? bitmaskCount * 1 * sizeof(uint32_t) : 0; } uint32_t intOffset() const { return 0; } uint32_t floatOffset() const { return intOffset() + intSize(); } uint32_t bitmaskOffset() const { return floatOffset() + floatSize(); } uint32_t totalSize() const { return floatSize() + intSize() + bitmaskSize(); } }; }dxvk-2.6.1/src/d3d9/d3d9_constant_set.h000066400000000000000000000026131477473124000175410ustar00rootroot00000000000000#pragma once #include "d3d9_caps.h" #include "d3d9_constant_buffer.h" #include "d3d9_constant_layout.h" #include "../dxso/dxso_isgn.h" #include "../util/util_vector.h" #include namespace dxvk { enum class D3D9ConstantType { Float, Int, Bool }; // We make an assumption later based on the packing of this struct for copying. struct D3D9ShaderConstantsVSSoftware { Vector4i iConsts[caps::MaxOtherConstantsSoftware]; Vector4 fConsts[caps::MaxFloatConstantsSoftware]; uint32_t bConsts[caps::MaxOtherConstantsSoftware / 32]; }; struct D3D9ShaderConstantsVSHardware { Vector4i iConsts[caps::MaxOtherConstants]; Vector4 fConsts[caps::MaxFloatConstantsVS]; uint32_t bConsts[1]; }; struct D3D9ShaderConstantsPS { Vector4i iConsts[caps::MaxOtherConstants]; Vector4 fConsts[caps::MaxFloatConstantsPS]; uint32_t bConsts[1]; }; struct D3D9SwvpConstantBuffers { D3D9ConstantBuffer intBuffer; D3D9ConstantBuffer boolBuffer; }; struct D3D9ConstantSets { D3D9ConstantLayout layout; D3D9SwvpConstantBuffers swvp; D3D9ConstantBuffer buffer; DxsoShaderMetaInfo meta = {}; bool dirty = true; uint32_t maxChangedConstF = 0; uint32_t maxChangedConstI = 0; uint32_t maxChangedConstB = 0; }; } dxvk-2.6.1/src/d3d9/d3d9_cursor.cpp000066400000000000000000000074301477473124000167070ustar00rootroot00000000000000#include "d3d9_cursor.h" #include "d3d9_util.h" #include namespace dxvk { #ifdef _WIN32 void D3D9Cursor::ResetCursor() { ShowCursor(FALSE); if (m_hCursor != nullptr) ResetHardwareCursor(); else if (IsSoftwareCursor()) ResetSoftwareCursor(); } void D3D9Cursor::ResetHardwareCursor() { ::DestroyCursor(m_hCursor); m_hCursor = nullptr; } void D3D9Cursor::ResetSoftwareCursor() { m_sCursor.DrawCursor = false; m_sCursor.ResetCursor = true; } void D3D9Cursor::UpdateCursor(int X, int Y) { // SetCursorPosition is used to directly update the position of software cursors, // but keep track of the cursor position even when using hardware cursors, in order // to ensure a smooth transition/overlap from one type to the other. m_sCursor.X = X; m_sCursor.Y = Y; if (unlikely(m_sCursor.Width > 0 && m_sCursor.Height > 0)) return; POINT currentPos = { }; if (::GetCursorPos(¤tPos) && currentPos == POINT{ X, Y }) return; ::SetCursorPos(X, Y); } BOOL D3D9Cursor::ShowCursor(BOOL bShow) { // Cursor visibility remains unchanged (typically FALSE) if the cursor isn't set. if (unlikely(m_hCursor == nullptr && !IsSoftwareCursor())) return m_visible; if (m_hCursor != nullptr) ::SetCursor(bShow ? m_hCursor : nullptr); else if (likely(!m_sCursor.ResetCursor)) m_sCursor.DrawCursor = bShow; return std::exchange(m_visible, bShow); } HRESULT D3D9Cursor::SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap) { if (IsSoftwareCursor()) ResetSoftwareCursor(); CursorMask mask; std::memset(mask, ~0, sizeof(mask)); ICONINFO info; info.fIcon = FALSE; info.xHotspot = XHotSpot; info.yHotspot = YHotSpot; info.hbmMask = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 1, &mask[0]); info.hbmColor = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 32, &bitmap[0]); if (m_hCursor != nullptr) ::DestroyCursor(m_hCursor); m_hCursor = ::CreateIconIndirect(&info); ::DeleteObject(info.hbmMask); ::DeleteObject(info.hbmColor); ShowCursor(m_visible); return D3D_OK; } HRESULT D3D9Cursor::SetSoftwareCursor(UINT Width, UINT Height, UINT XHotSpot, UINT YHotSpot) { // Make sure to hide the win32 cursor ::SetCursor(nullptr); if (m_hCursor != nullptr) ResetHardwareCursor(); m_sCursor.Width = Width; m_sCursor.Height = Height; m_sCursor.XHotSpot = XHotSpot; m_sCursor.YHotSpot = YHotSpot; m_sCursor.ResetCursor = false; ShowCursor(m_visible); return D3D_OK; } #else void D3D9Cursor::ResetCursor() { Logger::warn("D3D9Cursor::ResetCursor: Not supported on current platform."); } void D3D9Cursor::ResetHardwareCursor() { Logger::warn("D3D9Cursor::ResetHardwareCursor: Not supported on current platform."); } void D3D9Cursor::ResetSoftwareCursor() { Logger::warn("D3D9Cursor::ResetSoftwareCursor: Not supported on current platform."); } void D3D9Cursor::UpdateCursor(int X, int Y) { Logger::warn("D3D9Cursor::UpdateCursor: Not supported on current platform."); } BOOL D3D9Cursor::ShowCursor(BOOL bShow) { Logger::warn("D3D9Cursor::ShowCursor: Not supported on current platform."); return std::exchange(m_visible, bShow); } HRESULT D3D9Cursor::SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap) { Logger::warn("D3D9Cursor::SetHardwareCursor: Not supported on current platform."); return D3D_OK; } HRESULT D3D9Cursor::SetSoftwareCursor(UINT Width, UINT Height, UINT XHotSpot, UINT YHotSpot) { Logger::warn("D3D9Cursor::SetSoftwareCursor: Not supported on current platform."); return D3D_OK; } #endif } dxvk-2.6.1/src/d3d9/d3d9_cursor.h000066400000000000000000000032631477473124000163540ustar00rootroot00000000000000#pragma once #include "d3d9_include.h" namespace dxvk { /** * \brief D3D9 Software Cursor */ struct D3D9_SOFTWARE_CURSOR { UINT Width = 0; UINT Height = 0; UINT XHotSpot = 0; UINT YHotSpot = 0; int32_t X = 0; int32_t Y = 0; bool DrawCursor = false; bool ResetCursor = false; }; constexpr uint32_t HardwareCursorWidth = 32u; constexpr uint32_t HardwareCursorHeight = 32u; constexpr uint32_t HardwareCursorFormatSize = 4u; constexpr uint32_t HardwareCursorPitch = HardwareCursorWidth * HardwareCursorFormatSize; // Format Size of 4 bytes (ARGB) using CursorBitmap = uint8_t[HardwareCursorHeight * HardwareCursorPitch]; // Monochrome mask (1 bit) using CursorMask = uint8_t[HardwareCursorHeight * HardwareCursorWidth / 8]; class D3D9Cursor { public: #ifdef _WIN32 ~D3D9Cursor() { if (m_hCursor != nullptr) ::DestroyCursor(m_hCursor); } #endif void ResetCursor(); void ResetHardwareCursor(); void ResetSoftwareCursor(); void UpdateCursor(int X, int Y); BOOL ShowCursor(BOOL bShow); HRESULT SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap); HRESULT SetSoftwareCursor(UINT Width, UINT Height, UINT XHotSpot, UINT YHotSpot); D3D9_SOFTWARE_CURSOR* GetSoftwareCursor() { return &m_sCursor; } BOOL IsSoftwareCursor() const { return m_sCursor.Width > 0 && m_sCursor.Height > 0; } BOOL IsCursorVisible() const { return m_visible; } private: BOOL m_visible = FALSE; D3D9_SOFTWARE_CURSOR m_sCursor; #ifdef _WIN32 HCURSOR m_hCursor = nullptr; #endif }; }dxvk-2.6.1/src/d3d9/d3d9_device.cpp000066400000000000000000011304461477473124000166360ustar00rootroot00000000000000#include "d3d9_device.h" #include "d3d9_annotation.h" #include "d3d9_common_texture.h" #include "d3d9_interface.h" #include "d3d9_swapchain.h" #include "d3d9_caps.h" #include "d3d9_util.h" #include "d3d9_texture.h" #include "d3d9_buffer.h" #include "d3d9_vertex_declaration.h" #include "d3d9_shader.h" #include "d3d9_query.h" #include "d3d9_stateblock.h" #include "d3d9_monitor.h" #include "d3d9_spec_constants.h" #include "d3d9_names.h" #include "d3d9_format_helpers.h" #include "../dxvk/dxvk_adapter.h" #include "../dxvk/dxvk_instance.h" #include "../util/util_bit.h" #include "../util/util_math.h" #include "d3d9_initializer.h" #include #include #ifdef MSC_VER #pragma fenv_access (on) #endif namespace dxvk { D3D9DeviceEx::D3D9DeviceEx( D3D9InterfaceEx* pParent, D3D9Adapter* pAdapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, Rc dxvkDevice) : m_parent ( pParent ) , m_deviceType ( DeviceType ) , m_window ( hFocusWindow ) , m_behaviorFlags ( BehaviorFlags ) , m_adapter ( pAdapter ) , m_dxvkDevice ( dxvkDevice ) , m_memoryAllocator ( ) , m_shaderAllocator ( ) , m_shaderModules ( new D3D9ShaderModuleSet ) , m_stagingBuffer ( dxvkDevice, StagingBufferSize ) , m_stagingBufferFence ( new sync::Fence() ) , m_d3d9Options ( dxvkDevice, pParent->GetInstance()->config() ) , m_multithread ( BehaviorFlags & D3DCREATE_MULTITHREADED ) , m_isSWVP ( (BehaviorFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING) ? true : false ) , m_isD3D8Compatible ( pParent->IsD3D8Compatible() ) , m_csThread ( dxvkDevice, dxvkDevice->createContext() ) , m_csChunk ( AllocCsChunk() ) , m_submissionFence ( new sync::Fence() ) , m_flushTracker ( GetMaxFlushType() ) , m_d3d9Interop ( this ) , m_d3d9On12 ( this ) , m_d3d8Bridge ( this ) { // If we can SWVP, then we use an extended constant set // as SWVP has many more slots available than HWVP. bool canSWVP = CanSWVP(); DetermineConstantLayouts(canSWVP); if (canSWVP) Logger::info("D3D9DeviceEx: Using extended constant set for software vertex processing."); if (m_dxvkDevice->debugFlags().test(DxvkDebugFlag::Markers)) m_annotation = new D3D9UserDefinedAnnotation(this); m_initializer = new D3D9Initializer(this); m_converter = new D3D9FormatHelper(m_dxvkDevice); EmitCs([ cDevice = m_dxvkDevice ] (DxvkContext* ctx) { ctx->beginRecording(cDevice->createCommandList()); // Disable logic op once and for all. DxvkLogicOpState loState = { }; ctx->setLogicOpState(loState); }); SynchronizeCsThread(DxvkCsThread::SynchronizeAll); if (!(BehaviorFlags & D3DCREATE_FPU_PRESERVE)) SetupFPU(); m_dxsoOptions = DxsoOptions(this, m_d3d9Options); // Check if VK_EXT_robustness2 is supported, so we can optimize the number of constants we need to copy. // Also check the required alignments. const bool supportsRobustness2 = m_dxvkDevice->features().extRobustness2.robustBufferAccess2; bool useRobustConstantAccess = supportsRobustness2; D3D9ConstantSets& vsConstSet = m_consts[DxsoProgramType::VertexShader]; D3D9ConstantSets& psConstSet = m_consts[DxsoProgramType::PixelShader]; if (useRobustConstantAccess) { m_robustSSBOAlignment = m_dxvkDevice->properties().extRobustness2.robustStorageBufferAccessSizeAlignment; m_robustUBOAlignment = m_dxvkDevice->properties().extRobustness2.robustUniformBufferAccessSizeAlignment; if (canSWVP) { const uint32_t floatBufferAlignment = m_dxsoOptions.vertexFloatConstantBufferAsSSBO ? m_robustSSBOAlignment : m_robustUBOAlignment; useRobustConstantAccess &= vsConstSet.layout.floatSize() % floatBufferAlignment == 0; useRobustConstantAccess &= vsConstSet.layout.intSize() % m_robustUBOAlignment == 0; useRobustConstantAccess &= vsConstSet.layout.bitmaskSize() % m_robustUBOAlignment == 0; } else { useRobustConstantAccess &= vsConstSet.layout.totalSize() % m_robustUBOAlignment == 0; } useRobustConstantAccess &= psConstSet.layout.totalSize() % m_robustUBOAlignment == 0; } if (!useRobustConstantAccess) { // Disable optimized constant copies, we always have to copy all constants. vsConstSet.maxChangedConstF = vsConstSet.layout.floatCount; vsConstSet.maxChangedConstI = vsConstSet.layout.intCount; vsConstSet.maxChangedConstB = vsConstSet.layout.boolCount; psConstSet.maxChangedConstF = psConstSet.layout.floatCount; if (supportsRobustness2) { Logger::warn("Disabling robust constant buffer access because of alignment."); } } // Check for VK_EXT_graphics_pipeline_libraries m_usingGraphicsPipelines = dxvkDevice->features().extGraphicsPipelineLibrary.graphicsPipelineLibrary; // Check for VK_EXT_depth_bias_control and set up initial state m_depthBiasRepresentation = { VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT, false }; if (dxvkDevice->features().extDepthBiasControl.depthBiasControl) { if (dxvkDevice->features().extDepthBiasControl.depthBiasExact) m_depthBiasRepresentation.depthBiasExact = true; if (dxvkDevice->features().extDepthBiasControl.floatRepresentation) { m_depthBiasRepresentation.depthBiasRepresentation = VK_DEPTH_BIAS_REPRESENTATION_FLOAT_EXT; m_depthBiasScale = 1.0f; } else if (dxvkDevice->features().extDepthBiasControl.leastRepresentableValueForceUnormRepresentation) m_depthBiasRepresentation.depthBiasRepresentation = VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT; } EmitCs([ cRepresentation = m_depthBiasRepresentation ] (DxvkContext* ctx) { ctx->setDepthBiasRepresentation(cRepresentation); }); CreateConstantBuffers(); m_availableMemory = DetermineInitialTextureMemory(); m_hazardLayout = dxvkDevice->features().extAttachmentFeedbackLoopLayout.attachmentFeedbackLoopLayout ? VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT : VK_IMAGE_LAYOUT_GENERAL; // Initially set all the dirty flags so we // always end up giving the backend *something* to work with. m_flags.set(D3D9DeviceFlag::DirtyFramebuffer); m_flags.set(D3D9DeviceFlag::DirtyClipPlanes); m_flags.set(D3D9DeviceFlag::DirtyDepthStencilState); m_flags.set(D3D9DeviceFlag::DirtyBlendState); m_flags.set(D3D9DeviceFlag::DirtyRasterizerState); m_flags.set(D3D9DeviceFlag::DirtyDepthBias); m_flags.set(D3D9DeviceFlag::DirtyAlphaTestState); m_flags.set(D3D9DeviceFlag::DirtyInputLayout); m_flags.set(D3D9DeviceFlag::DirtyViewportScissor); m_flags.set(D3D9DeviceFlag::DirtyMultiSampleState); m_flags.set(D3D9DeviceFlag::DirtyFogState); m_flags.set(D3D9DeviceFlag::DirtyFogColor); m_flags.set(D3D9DeviceFlag::DirtyFogDensity); m_flags.set(D3D9DeviceFlag::DirtyFogScale); m_flags.set(D3D9DeviceFlag::DirtyFogEnd); m_flags.set(D3D9DeviceFlag::DirtyFFVertexData); m_flags.set(D3D9DeviceFlag::DirtyFFVertexBlend); m_flags.set(D3D9DeviceFlag::DirtyFFVertexShader); m_flags.set(D3D9DeviceFlag::DirtyFFPixelShader); m_flags.set(D3D9DeviceFlag::DirtyFFViewport); m_flags.set(D3D9DeviceFlag::DirtyFFPixelData); m_flags.set(D3D9DeviceFlag::DirtyProgVertexShader); m_flags.set(D3D9DeviceFlag::DirtySharedPixelShaderData); m_flags.set(D3D9DeviceFlag::DirtyDepthBounds); m_flags.set(D3D9DeviceFlag::DirtyPointScale); m_flags.set(D3D9DeviceFlag::DirtySpecializationEntries); // Bitfields can't be initialized in header. m_activeRTsWhichAreTextures = 0; m_alphaSwizzleRTs = 0; m_lastHazardsRT = 0; } D3D9DeviceEx::~D3D9DeviceEx() { // Avoids hanging when in this state, see comment // in DxvkDevice::~DxvkDevice. if (this_thread::isInModuleDetachment()) return; Flush(); SynchronizeCsThread(DxvkCsThread::SynchronizeAll); if (m_annotation) delete m_annotation; delete m_initializer; delete m_converter; m_dxvkDevice->waitForIdle(); // Sync Device } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; bool extended = m_parent->IsExtended() && riid == __uuidof(IDirect3DDevice9Ex); if (riid == __uuidof(IUnknown) || riid == __uuidof(IDirect3DDevice9) || extended) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(IDxvkD3D8Bridge)) { *ppvObject = ref(&m_d3d8Bridge); return S_OK; } if (riid == __uuidof(ID3D9VkInteropDevice)) { *ppvObject = ref(&m_d3d9Interop); return S_OK; } if (riid == __uuidof(IDirect3DDevice9On12)) { *ppvObject = ref(&m_d3d9On12); return S_OK; } // We want to ignore this if the extended device is queried and we weren't made extended. if (riid == __uuidof(IDirect3DDevice9Ex)) return E_NOINTERFACE; if (logQueryInterfaceError(__uuidof(IDirect3DDevice9), riid)) { Logger::warn("D3D9DeviceEx::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::TestCooperativeLevel() { D3D9DeviceLock lock = LockDevice(); // Equivelant of D3D11/DXGI present tests. We can always present. if (likely(m_deviceLostState == D3D9DeviceLostState::Ok)) { return D3D_OK; } else if (m_deviceLostState == D3D9DeviceLostState::NotReset) { return D3DERR_DEVICENOTRESET; } else { return D3DERR_DEVICELOST; } } UINT STDMETHODCALLTYPE D3D9DeviceEx::GetAvailableTextureMem() { // This is not meant to be accurate. // The values are also wildly incorrect in d3d9... But some games rely // on this inaccurate value... // Clamp to megabyte range, as per spec. constexpr UINT range = 0xfff00000; // Can't have negative memory! int64_t memory = std::max(m_availableMemory.load(), 0); return UINT(memory) & range; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::EvictManagedResources() { return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetDirect3D(IDirect3D9** ppD3D9) { if (ppD3D9 == nullptr) return D3DERR_INVALIDCALL; *ppD3D9 = m_parent.ref(); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetDeviceCaps(D3DCAPS9* pCaps) { if (pCaps == nullptr) return D3DERR_INVALIDCALL; m_adapter->GetDeviceCaps(m_deviceType, pCaps); // When in SWVP mode, 256 matrices can be used for indexed vertex blending pCaps->MaxVertexBlendMatrixIndex = m_isSWVP ? 255 : 8; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetDisplayMode(UINT iSwapChain, D3DDISPLAYMODE* pMode) { if (unlikely(iSwapChain != 0)) return D3DERR_INVALIDCALL; return m_implicitSwapchain->GetDisplayMode(pMode); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS *pParameters) { if (pParameters == nullptr) return D3DERR_INVALIDCALL; pParameters->AdapterOrdinal = m_adapter->GetOrdinal(); pParameters->BehaviorFlags = m_behaviorFlags; pParameters->DeviceType = m_deviceType; pParameters->hFocusWindow = m_window; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetCursorProperties( UINT XHotSpot, UINT YHotSpot, IDirect3DSurface9* pCursorBitmap) { D3D9DeviceLock lock = LockDevice(); if (unlikely(pCursorBitmap == nullptr)) return D3DERR_INVALIDCALL; auto* cursorTex = GetCommonTexture(pCursorBitmap); if (unlikely(cursorTex->Desc()->Format != D3D9Format::A8R8G8B8)) return D3DERR_INVALIDCALL; uint32_t inputWidth = cursorTex->Desc()->Width; uint32_t inputHeight = cursorTex->Desc()->Height; // Check if surface dimensions are powers of two. if ((inputWidth && (inputWidth & (inputWidth - 1))) || (inputHeight && (inputHeight & (inputHeight - 1)))) return D3DERR_INVALIDCALL; // It makes no sense to have a hotspot outside of the bitmap. if ((inputWidth && (XHotSpot > inputWidth - 1)) || (inputHeight && (YHotSpot > inputHeight - 1))) return D3DERR_INVALIDCALL; D3DPRESENT_PARAMETERS params; m_implicitSwapchain->GetPresentParameters(¶ms); if (inputWidth > params.BackBufferWidth || inputHeight > params.BackBufferHeight) return D3DERR_INVALIDCALL; // Always use a hardware cursor when windowed. bool hwCursor = params.Windowed; // Always use a hardware cursor w/h <= 32 px hwCursor |= inputWidth <= HardwareCursorWidth || inputHeight <= HardwareCursorHeight; D3DLOCKED_BOX lockedBox; HRESULT hr = LockImage(cursorTex, 0, 0, &lockedBox, nullptr, D3DLOCK_READONLY); if (FAILED(hr)) return hr; const uint8_t* data = reinterpret_cast(lockedBox.pBits); if (hwCursor) { // Windows works with a stride of 128, lets respect that. // Copy data to the bitmap... CursorBitmap bitmap = { 0 }; size_t copyPitch = std::min( HardwareCursorPitch, inputWidth * inputHeight * HardwareCursorFormatSize); for (uint32_t h = 0; h < HardwareCursorHeight; h++) std::memcpy(&bitmap[h * HardwareCursorPitch], &data[h * lockedBox.RowPitch], copyPitch); UnlockImage(cursorTex, 0, 0); // Set this as our cursor. return m_cursor.SetHardwareCursor(XHotSpot, YHotSpot, bitmap); } else { size_t copyPitch = inputWidth * HardwareCursorFormatSize; std::vector bitmap(inputHeight * copyPitch, 0); for (uint32_t h = 0; h < inputHeight; h++) std::memcpy(&bitmap[h * copyPitch], &data[h * lockedBox.RowPitch], copyPitch); UnlockImage(cursorTex, 0, 0); m_implicitSwapchain->SetCursorTexture(inputWidth, inputHeight, &bitmap[0]); return m_cursor.SetSoftwareCursor(inputWidth, inputHeight, XHotSpot, YHotSpot); } return D3D_OK; } void STDMETHODCALLTYPE D3D9DeviceEx::SetCursorPosition(int X, int Y, DWORD Flags) { D3D9DeviceLock lock = LockDevice(); // I was not able to find an instance // where the cursor update was not immediate. // Fullscreen + Windowed seem to have the same // behaviour here. // Hence we ignore the flag D3DCURSOR_IMMEDIATE_UPDATE. m_cursor.UpdateCursor(X, Y); } BOOL STDMETHODCALLTYPE D3D9DeviceEx::ShowCursor(BOOL bShow) { D3D9DeviceLock lock = LockDevice(); return m_cursor.ShowCursor(bShow); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateAdditionalSwapChain( D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DSwapChain9** ppSwapChain) { return CreateAdditionalSwapChainEx(pPresentationParameters, nullptr, ppSwapChain); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetSwapChain(UINT iSwapChain, IDirect3DSwapChain9** pSwapChain) { D3D9DeviceLock lock = LockDevice(); InitReturnPtr(pSwapChain); if (unlikely(pSwapChain == nullptr)) return D3DERR_INVALIDCALL; // This only returns the implicit swapchain... if (unlikely(iSwapChain != 0)) return D3DERR_INVALIDCALL; *pSwapChain = static_cast(m_implicitSwapchain.ref()); return D3D_OK; } UINT STDMETHODCALLTYPE D3D9DeviceEx::GetNumberOfSwapChains() { // This only counts the implicit swapchain... return 1; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) { D3D9DeviceLock lock = LockDevice(); Logger::info("Device reset"); m_deviceLostState = D3D9DeviceLostState::Ok; HRESULT hr; // Black Desert creates a D3DDEVTYPE_NULLREF device and // expects reset to work despite passing invalid parameters. if (likely(m_deviceType != D3DDEVTYPE_NULLREF)) { hr = m_parent->ValidatePresentationParameters(pPresentationParameters); if (unlikely(FAILED(hr))) return hr; } if (!IsExtended()) { // The internal references are always cleared, regardless of whether the Reset call succeeds. ResetState(pPresentationParameters); m_implicitSwapchain->DestroyBackBuffers(); m_autoDepthStencil = nullptr; } else { // Extended devices only reset the bound render targets for (uint32_t i = 0; i < caps::MaxSimultaneousRenderTargets; i++) { SetRenderTargetInternal(i, nullptr); } SetDepthStencilSurface(nullptr); } m_flags.clr(D3D9DeviceFlag::InScene); m_cursor.ResetCursor(); /* * Before calling the IDirect3DDevice9::Reset method for a device, * an application should release any explicit render targets, * depth stencil surfaces, additional swap chains, state blocks, * and D3DPOOL_DEFAULT resources associated with the device. * * We have to check after ResetState clears the references held by SetTexture, etc. * This matches what Windows D3D9 does. */ if (unlikely(m_losableResourceCounter.load() != 0 && !IsExtended() && m_d3d9Options.countLosableResources)) { Logger::warn(str::format("Device reset failed because device still has alive losable resources: Device not reset. Remaining resources: ", m_losableResourceCounter.load())); m_deviceLostState = D3D9DeviceLostState::NotReset; // D3D8 returns D3DERR_DEVICELOST here, whereas D3D9 returns D3DERR_INVALIDCALL. return m_isD3D8Compatible ? D3DERR_DEVICELOST : D3DERR_INVALIDCALL; } hr = ResetSwapChain(pPresentationParameters, nullptr); if (unlikely(FAILED(hr))) { if (!IsExtended()) { Logger::warn("Device reset failed: Device not reset"); m_deviceLostState = D3D9DeviceLostState::NotReset; } return hr; } // Unbind all buffers that were still bound to the backend to avoid leaks. EmitCs([](DxvkContext* ctx) { ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32); for (uint32_t i = 0; i < caps::MaxStreams; i++) { ctx->bindVertexBuffer(i, DxvkBufferSlice(), 0); } }); Flush(); SynchronizeCsThread(DxvkCsThread::SynchronizeAll); if (m_d3d9Options.deferSurfaceCreation) m_resetCtr++; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::Present( const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion) { return PresentEx( pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, 0); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetBackBuffer( UINT iSwapChain, UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface9** ppBackBuffer) { InitReturnPtr(ppBackBuffer); if (unlikely(iSwapChain != 0)) return D3DERR_INVALIDCALL; return m_implicitSwapchain->GetBackBuffer(iBackBuffer, Type, ppBackBuffer); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetRasterStatus(UINT iSwapChain, D3DRASTER_STATUS* pRasterStatus) { if (unlikely(iSwapChain != 0)) return D3DERR_INVALIDCALL; return m_implicitSwapchain->GetRasterStatus(pRasterStatus); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetDialogBoxMode(BOOL bEnableDialogs) { return m_implicitSwapchain->SetDialogBoxMode(bEnableDialogs); } void STDMETHODCALLTYPE D3D9DeviceEx::SetGammaRamp( UINT iSwapChain, DWORD Flags, const D3DGAMMARAMP* pRamp) { if (unlikely(iSwapChain != 0)) return; m_implicitSwapchain->SetGammaRamp(Flags, pRamp); } void STDMETHODCALLTYPE D3D9DeviceEx::GetGammaRamp(UINT iSwapChain, D3DGAMMARAMP* pRamp) { if (unlikely(iSwapChain != 0)) return; m_implicitSwapchain->GetGammaRamp(pRamp); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateTexture( UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle) { InitReturnPtr(ppTexture); if (unlikely(ppTexture == nullptr)) return D3DERR_INVALIDCALL; D3D9_COMMON_TEXTURE_DESC desc; desc.Width = Width; desc.Height = Height; desc.Depth = 1; desc.ArraySize = 1; desc.MipLevels = Levels; desc.Usage = Usage; desc.Format = EnumerateFormat(Format); desc.Pool = Pool; desc.Discard = FALSE; desc.MultiSample = D3DMULTISAMPLE_NONE; desc.MultisampleQuality = 0; desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = FALSE; // Docs: // Textures placed in the D3DPOOL_DEFAULT pool cannot be locked // unless they are dynamic textures or they are private, FOURCC, driver formats. desc.IsLockable = Pool != D3DPOOL_DEFAULT || (Usage & D3DUSAGE_DYNAMIC) || IsVendorFormat(EnumerateFormat(Format)); if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc))) return D3DERR_INVALIDCALL; try { void* initialData = nullptr; // On Windows Vista (so most likely D3D9Ex), pSharedHandle can be used to pass initial data for a texture, // but only for a very specific type of texture. if (Pool == D3DPOOL_SYSTEMMEM && Levels == 1 && pSharedHandle != nullptr) { initialData = *(reinterpret_cast(pSharedHandle)); pSharedHandle = nullptr; } // Shared textures have to be in POOL_DEFAULT if (pSharedHandle != nullptr && Pool != D3DPOOL_DEFAULT) return D3DERR_INVALIDCALL; const Com texture = new D3D9Texture2D(this, &desc, IsExtended(), pSharedHandle); m_initializer->InitTexture(texture->GetCommonTexture(), initialData); *ppTexture = texture.ref(); if (desc.Pool == D3DPOOL_DEFAULT) m_losableResourceCounter++; return D3D_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return D3DERR_OUTOFVIDEOMEMORY; } } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateVolumeTexture( UINT Width, UINT Height, UINT Depth, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DVolumeTexture9** ppVolumeTexture, HANDLE* pSharedHandle) { InitReturnPtr(ppVolumeTexture); if (unlikely(ppVolumeTexture == nullptr)) return D3DERR_INVALIDCALL; if (pSharedHandle) Logger::err("CreateVolumeTexture: Shared volume textures not supported"); D3D9_COMMON_TEXTURE_DESC desc; desc.Width = Width; desc.Height = Height; desc.Depth = Depth; desc.ArraySize = 1; desc.MipLevels = Levels; desc.Usage = Usage; desc.Format = EnumerateFormat(Format); desc.Pool = Pool; desc.Discard = FALSE; desc.MultiSample = D3DMULTISAMPLE_NONE; desc.MultisampleQuality = 0; desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = FALSE; // Docs: // Textures placed in the D3DPOOL_DEFAULT pool cannot be locked // unless they are dynamic textures. Volume textures do not // exempt private, FOURCC, driver formats from these checks. desc.IsLockable = Pool != D3DPOOL_DEFAULT || (Usage & D3DUSAGE_DYNAMIC); if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_VOLUMETEXTURE, &desc))) return D3DERR_INVALIDCALL; try { const Com texture = new D3D9Texture3D(this, &desc, IsExtended()); m_initializer->InitTexture(texture->GetCommonTexture()); *ppVolumeTexture = texture.ref(); // The device cannot be reset if there's any remaining default resources if (desc.Pool == D3DPOOL_DEFAULT) m_losableResourceCounter++; return D3D_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return D3DERR_OUTOFVIDEOMEMORY; } } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateCubeTexture( UINT EdgeLength, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DCubeTexture9** ppCubeTexture, HANDLE* pSharedHandle) { InitReturnPtr(ppCubeTexture); if (unlikely(ppCubeTexture == nullptr)) return D3DERR_INVALIDCALL; if (pSharedHandle) Logger::err("CreateCubeTexture: Shared cube textures not supported"); D3D9_COMMON_TEXTURE_DESC desc; desc.Width = EdgeLength; desc.Height = EdgeLength; desc.Depth = 1; desc.ArraySize = 6; // A cube has 6 faces, wowwie! desc.MipLevels = Levels; desc.Usage = Usage; desc.Format = EnumerateFormat(Format); desc.Pool = Pool; desc.Discard = FALSE; desc.MultiSample = D3DMULTISAMPLE_NONE; desc.MultisampleQuality = 0; desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = FALSE; // Docs: // Textures placed in the D3DPOOL_DEFAULT pool cannot be locked // unless they are dynamic textures or they are private, FOURCC, driver formats. desc.IsLockable = Pool != D3DPOOL_DEFAULT || (Usage & D3DUSAGE_DYNAMIC) || IsVendorFormat(EnumerateFormat(Format)); if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_CUBETEXTURE, &desc))) return D3DERR_INVALIDCALL; try { const Com texture = new D3D9TextureCube(this, &desc, IsExtended()); m_initializer->InitTexture(texture->GetCommonTexture()); *ppCubeTexture = texture.ref(); // The device cannot be reset if there's any remaining default resources if (desc.Pool == D3DPOOL_DEFAULT) m_losableResourceCounter++; return D3D_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return D3DERR_OUTOFVIDEOMEMORY; } } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateVertexBuffer( UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9** ppVertexBuffer, HANDLE* pSharedHandle) { InitReturnPtr(ppVertexBuffer); if (unlikely(ppVertexBuffer == nullptr)) return D3DERR_INVALIDCALL; if (pSharedHandle) Logger::err("CreateVertexBuffer: Shared vertex buffers not supported"); D3D9_BUFFER_DESC desc; desc.Format = D3D9Format::VERTEXDATA; desc.FVF = FVF; desc.Pool = Pool; desc.Size = Length; desc.Type = D3DRTYPE_VERTEXBUFFER; desc.Usage = Usage; if (FAILED(D3D9CommonBuffer::ValidateBufferProperties(&desc))) return D3DERR_INVALIDCALL; try { const Com buffer = new D3D9VertexBuffer(this, &desc, IsExtended()); m_initializer->InitBuffer(buffer->GetCommonBuffer()); *ppVertexBuffer = buffer.ref(); // The device cannot be reset if there's any remaining default resources if (desc.Pool == D3DPOOL_DEFAULT) m_losableResourceCounter++; return D3D_OK; } catch (const DxvkError & e) { Logger::err(e.message()); return D3DERR_INVALIDCALL; } } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateIndexBuffer( UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer9** ppIndexBuffer, HANDLE* pSharedHandle) { InitReturnPtr(ppIndexBuffer); if (unlikely(ppIndexBuffer == nullptr)) return D3DERR_INVALIDCALL; if (pSharedHandle) Logger::err("CreateIndexBuffer: Shared index buffers not supported"); D3D9_BUFFER_DESC desc; desc.Format = EnumerateFormat(Format); desc.Pool = Pool; desc.Size = Length; desc.Type = D3DRTYPE_INDEXBUFFER; desc.Usage = Usage; if (FAILED(D3D9CommonBuffer::ValidateBufferProperties(&desc))) return D3DERR_INVALIDCALL; try { const Com buffer = new D3D9IndexBuffer(this, &desc, IsExtended()); m_initializer->InitBuffer(buffer->GetCommonBuffer()); *ppIndexBuffer = buffer.ref(); // The device cannot be reset if there's any remaining default resources if (desc.Pool == D3DPOOL_DEFAULT) m_losableResourceCounter++; return D3D_OK; } catch (const DxvkError & e) { Logger::err(e.message()); return D3DERR_INVALIDCALL; } } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateRenderTarget( UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) { return CreateRenderTargetEx( Width, Height, Format, MultiSample, MultisampleQuality, Lockable, ppSurface, pSharedHandle, 0); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateDepthStencilSurface( UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) { return CreateDepthStencilSurfaceEx( Width, Height, Format, MultiSample, MultisampleQuality, Discard, ppSurface, pSharedHandle, 0); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::UpdateSurface( IDirect3DSurface9* pSourceSurface, const RECT* pSourceRect, IDirect3DSurface9* pDestinationSurface, const POINT* pDestPoint) { D3D9DeviceLock lock = LockDevice(); D3D9Surface* src = static_cast(pSourceSurface); D3D9Surface* dst = static_cast(pDestinationSurface); if (unlikely(src == nullptr || dst == nullptr)) return D3DERR_INVALIDCALL; D3D9CommonTexture* srcTextureInfo = src->GetCommonTexture(); D3D9CommonTexture* dstTextureInfo = dst->GetCommonTexture(); if (unlikely(srcTextureInfo->Desc()->Pool != D3DPOOL_SYSTEMMEM || dstTextureInfo->Desc()->Pool != D3DPOOL_DEFAULT)) return D3DERR_INVALIDCALL; if (unlikely(srcTextureInfo->Desc()->Format != dstTextureInfo->Desc()->Format)) return D3DERR_INVALIDCALL; if (unlikely(srcTextureInfo->Desc()->MultiSample != D3DMULTISAMPLE_NONE)) return D3DERR_INVALIDCALL; if (unlikely(dstTextureInfo->Desc()->MultiSample != D3DMULTISAMPLE_NONE)) return D3DERR_INVALIDCALL; const DxvkFormatInfo* formatInfo = lookupFormatInfo(dstTextureInfo->GetFormatMapping().FormatColor); VkOffset3D srcOffset = { 0u, 0u, 0u }; VkOffset3D dstOffset = { 0u, 0u, 0u }; VkExtent3D texLevelExtent = srcTextureInfo->GetExtentMip(src->GetSubresource()); VkExtent3D extent = texLevelExtent; if (pSourceRect != nullptr) { srcOffset = { pSourceRect->left, pSourceRect->top, 0u }; extent = { uint32_t(pSourceRect->right - pSourceRect->left), uint32_t(pSourceRect->bottom - pSourceRect->top), 1 }; const bool extentAligned = extent.width % formatInfo->blockSize.width == 0 && extent.height % formatInfo->blockSize.height == 0; if (pSourceRect->left < 0 || pSourceRect->top < 0 || pSourceRect->right <= pSourceRect->left || pSourceRect->bottom <= pSourceRect->top || pSourceRect->left % formatInfo->blockSize.width != 0 || pSourceRect->top % formatInfo->blockSize.height != 0 || (extent != texLevelExtent && !extentAligned)) return D3DERR_INVALIDCALL; } if (pDestPoint != nullptr) { if (pDestPoint->x % formatInfo->blockSize.width != 0 || pDestPoint->y % formatInfo->blockSize.height != 0 || pDestPoint->x < 0 || pDestPoint->y < 0) return D3DERR_INVALIDCALL; dstOffset = { pDestPoint->x, pDestPoint->y, 0u }; } // The source surface must be in D3DPOOL_SYSTEMMEM so we just treat it as just another texture upload except with a different source. UpdateTextureFromBuffer(dstTextureInfo, srcTextureInfo, dst->GetSubresource(), src->GetSubresource(), srcOffset, extent, dstOffset); // The contents of the mapping no longer match the image. dstTextureInfo->SetNeedsReadback(dst->GetSubresource(), true); if (dstTextureInfo->IsAutomaticMip()) MarkTextureMipsDirty(dstTextureInfo); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::UpdateTexture( IDirect3DBaseTexture9* pSourceTexture, IDirect3DBaseTexture9* pDestinationTexture) { D3D9DeviceLock lock = LockDevice(); if (!pDestinationTexture || !pSourceTexture) return D3DERR_INVALIDCALL; D3D9CommonTexture* dstTexInfo = GetCommonTexture(pDestinationTexture); D3D9CommonTexture* srcTexInfo = GetCommonTexture(pSourceTexture); if (unlikely(srcTexInfo->Desc()->Pool != D3DPOOL_SYSTEMMEM || dstTexInfo->Desc()->Pool != D3DPOOL_DEFAULT)) return D3DERR_INVALIDCALL; if (unlikely(srcTexInfo->Desc()->MipLevels < dstTexInfo->Desc()->MipLevels && !dstTexInfo->IsAutomaticMip())) return D3DERR_INVALIDCALL; if (unlikely(dstTexInfo->Desc()->Format != srcTexInfo->Desc()->Format)) return D3DERR_INVALIDCALL; if (unlikely(srcTexInfo->IsAutomaticMip() && !dstTexInfo->IsAutomaticMip())) return D3DERR_INVALIDCALL; const Rc dstImage = dstTexInfo->GetImage(); uint32_t mipLevels = dstTexInfo->IsAutomaticMip() ? 1 : dstTexInfo->Desc()->MipLevels; uint32_t arraySlices = std::min(srcTexInfo->Desc()->ArraySize, dstTexInfo->Desc()->ArraySize); uint32_t srcMipOffset = 0; VkExtent3D srcFirstMipExtent = srcTexInfo->GetExtent(); VkExtent3D dstFirstMipExtent = dstTexInfo->GetExtent(); if (srcFirstMipExtent != dstFirstMipExtent) { // UpdateTexture can be used with textures that have different mip lengths. // It will either match the the top mips or the bottom ones. // If the largest mip maps don't match in size, we try to take the smallest ones // of the source. srcMipOffset = srcTexInfo->Desc()->MipLevels - mipLevels; srcFirstMipExtent = util::computeMipLevelExtent(srcTexInfo->GetExtent(), srcMipOffset); dstFirstMipExtent = dstTexInfo->GetExtent(); } if (srcFirstMipExtent != dstFirstMipExtent) return D3DERR_INVALIDCALL; for (uint32_t a = 0; a < arraySlices; a++) { // The docs claim that the dirty box is just a performance optimization, however in practice games rely on it. const D3DBOX& box = srcTexInfo->GetDirtyBox(a); if (box.Left >= box.Right || box.Top >= box.Bottom || box.Front >= box.Back) continue; // The dirty box is only tracked for mip level 0 VkExtent3D mip0Extent = { uint32_t(box.Right - box.Left), uint32_t(box.Bottom - box.Top), uint32_t(box.Back - box.Front) }; VkOffset3D mip0Offset = { int32_t(box.Left), int32_t(box.Top), int32_t(box.Front) }; for (uint32_t dstMip = 0; dstMip < mipLevels; dstMip++) { // Scale the dirty box for the respective mip level uint32_t srcMip = dstMip + srcMipOffset; uint32_t srcSubresource = srcTexInfo->CalcSubresource(a, srcMip); uint32_t dstSubresource = dstTexInfo->CalcSubresource(a, dstMip); VkExtent3D extent = util::computeMipLevelExtent(mip0Extent, srcMip); VkOffset3D offset = util::computeMipLevelOffset(mip0Offset, srcMip); // The source surface must be in D3DPOOL_SYSTEMMEM so we just treat it as just another texture upload except with a different source. UpdateTextureFromBuffer(dstTexInfo, srcTexInfo, dstSubresource, srcSubresource, offset, extent, offset); // The contents of the mapping no longer match the image. dstTexInfo->SetNeedsReadback(dstSubresource, true); } } srcTexInfo->ClearDirtyBoxes(); if (dstTexInfo->IsAutomaticMip() && mipLevels != dstTexInfo->Desc()->MipLevels) MarkTextureMipsDirty(dstTexInfo); ConsiderFlush(GpuFlushType::ImplicitWeakHint); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetRenderTargetData( IDirect3DSurface9* pRenderTarget, IDirect3DSurface9* pDestSurface) { D3D9DeviceLock lock = LockDevice(); if (unlikely(IsDeviceLost())) { return D3DERR_DEVICELOST; } D3D9Surface* src = static_cast(pRenderTarget); D3D9Surface* dst = static_cast(pDestSurface); if (unlikely(src == nullptr || dst == nullptr)) return D3DERR_INVALIDCALL; if (pRenderTarget == pDestSurface) return D3D_OK; D3D9CommonTexture* dstTexInfo = GetCommonTexture(dst); D3D9CommonTexture* srcTexInfo = GetCommonTexture(src); if (srcTexInfo->Desc()->Format != dstTexInfo->Desc()->Format) return D3DERR_INVALIDCALL; if (src->GetSurfaceExtent() != dst->GetSurfaceExtent()) return D3DERR_INVALIDCALL; if (dstTexInfo->Desc()->Pool == D3DPOOL_DEFAULT) return this->StretchRect(pRenderTarget, nullptr, pDestSurface, nullptr, D3DTEXF_NONE); VkExtent3D dstTexExtent = dstTexInfo->GetExtentMip(dst->GetMipLevel()); VkExtent3D srcTexExtent = srcTexInfo->GetExtentMip(src->GetMipLevel()); const bool clearDst = dstTexInfo->Desc()->MipLevels > 1 || dstTexExtent.width > srcTexExtent.width || dstTexExtent.height > srcTexExtent.height; dstTexInfo->CreateBuffer(clearDst); DxvkBufferSlice dstBufferSlice = dstTexInfo->GetBufferSlice(dst->GetSubresource()); Rc srcImage = srcTexInfo->GetImage(); const DxvkFormatInfo* srcFormatInfo = lookupFormatInfo(srcImage->info().format); const VkImageSubresource srcSubresource = srcTexInfo->GetSubresourceFromIndex(srcFormatInfo->aspectMask, src->GetSubresource()); VkImageSubresourceLayers srcSubresourceLayers = { srcSubresource.aspectMask, srcSubresource.mipLevel, srcSubresource.arrayLayer, 1 }; EmitCs([ cBufferSlice = std::move(dstBufferSlice), cImage = srcImage, cSubresources = srcSubresourceLayers, cLevelExtent = srcTexExtent ] (DxvkContext* ctx) { ctx->copyImageToBuffer(cBufferSlice.buffer(), cBufferSlice.offset(), 4, 0, VK_FORMAT_UNDEFINED, cImage, cSubresources, VkOffset3D { 0, 0, 0 }, cLevelExtent); }); dstTexInfo->SetNeedsReadback(dst->GetSubresource(), true); TrackTextureMappingBufferSequenceNumber(dstTexInfo, dst->GetSubresource()); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetFrontBufferData(UINT iSwapChain, IDirect3DSurface9* pDestSurface) { if (unlikely(iSwapChain != 0)) return D3DERR_INVALIDCALL; D3D9DeviceLock lock = LockDevice(); // In windowed mode, GetFrontBufferData takes a screenshot of the entire screen. // We use the last used swapchain as a workaround. // Total War: Medieval 2 relies on this. return m_mostRecentlyUsedSwapchain->GetFrontBufferData(pDestSurface); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::StretchRect( IDirect3DSurface9* pSourceSurface, const RECT* pSourceRect, IDirect3DSurface9* pDestSurface, const RECT* pDestRect, D3DTEXTUREFILTERTYPE Filter) { D3D9DeviceLock lock = LockDevice(); D3D9Surface* dst = static_cast(pDestSurface); D3D9Surface* src = static_cast(pSourceSurface); if (unlikely(src == nullptr || dst == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(src == dst)) return D3DERR_INVALIDCALL; bool fastPath = true; D3D9CommonTexture* dstTextureInfo = dst->GetCommonTexture(); D3D9CommonTexture* srcTextureInfo = src->GetCommonTexture(); if (unlikely(dstTextureInfo->Desc()->Pool != D3DPOOL_DEFAULT || srcTextureInfo->Desc()->Pool != D3DPOOL_DEFAULT)) return D3DERR_INVALIDCALL; Rc dstImage = dstTextureInfo->GetImage(); Rc srcImage = srcTextureInfo->GetImage(); if (dstImage == nullptr || srcImage == nullptr) return D3DERR_INVALIDCALL; const DxvkFormatInfo* dstFormatInfo = lookupFormatInfo(dstImage->info().format); const DxvkFormatInfo* srcFormatInfo = lookupFormatInfo(srcImage->info().format); const VkImageSubresource dstSubresource = dstTextureInfo->GetSubresourceFromIndex(dstFormatInfo->aspectMask, dst->GetSubresource()); const VkImageSubresource srcSubresource = srcTextureInfo->GetSubresourceFromIndex(srcFormatInfo->aspectMask, src->GetSubresource()); if (unlikely(Filter != D3DTEXF_NONE && Filter != D3DTEXF_LINEAR && Filter != D3DTEXF_POINT)) return D3DERR_INVALIDCALL; VkExtent3D srcExtent = srcImage->mipLevelExtent(srcSubresource.mipLevel); VkExtent3D dstExtent = dstImage->mipLevelExtent(dstSubresource.mipLevel); D3D9Format srcFormat = srcTextureInfo->Desc()->Format; D3D9Format dstFormat = dstTextureInfo->Desc()->Format; // We may only fast path copy non identicals one way! // We don't know what garbage could be in the X8 data. bool similar = AreFormatsSimilar(srcFormat, dstFormat); // Copies are only supported on similar formats. fastPath &= similar; // Copies are only supported if the sample count matches, // otherwise we need to resolve. auto needsResolve = false; if (srcImage->info().sampleCount != dstImage->info().sampleCount) { needsResolve = srcImage->info().sampleCount != VK_SAMPLE_COUNT_1_BIT; auto fbBlit = dstImage->info().sampleCount != VK_SAMPLE_COUNT_1_BIT; fastPath &= !fbBlit; } // Copies would only work if we are block aligned. if (pSourceRect != nullptr) { fastPath &= (pSourceRect->left % srcFormatInfo->blockSize.width == 0); fastPath &= (pSourceRect->right % srcFormatInfo->blockSize.width == 0); fastPath &= (pSourceRect->top % srcFormatInfo->blockSize.height == 0); fastPath &= (pSourceRect->bottom % srcFormatInfo->blockSize.height == 0); } if (pDestRect != nullptr) { fastPath &= (pDestRect->left % dstFormatInfo->blockSize.width == 0); fastPath &= (pDestRect->top % dstFormatInfo->blockSize.height == 0); } VkImageSubresourceLayers dstSubresourceLayers = { dstSubresource.aspectMask, dstSubresource.mipLevel, dstSubresource.arrayLayer, 1 }; VkImageSubresourceLayers srcSubresourceLayers = { srcSubresource.aspectMask, srcSubresource.mipLevel, srcSubresource.arrayLayer, 1 }; VkImageBlit blitInfo; blitInfo.dstSubresource = dstSubresourceLayers; blitInfo.srcSubresource = srcSubresourceLayers; blitInfo.dstOffsets[0] = pDestRect != nullptr ? VkOffset3D{ int32_t(pDestRect->left), int32_t(pDestRect->top), 0 } : VkOffset3D{ 0, 0, 0 }; blitInfo.dstOffsets[1] = pDestRect != nullptr ? VkOffset3D{ int32_t(pDestRect->right), int32_t(pDestRect->bottom), 1 } : VkOffset3D{ int32_t(dstExtent.width), int32_t(dstExtent.height), 1 }; blitInfo.srcOffsets[0] = pSourceRect != nullptr ? VkOffset3D{ int32_t(pSourceRect->left), int32_t(pSourceRect->top), 0 } : VkOffset3D{ 0, 0, 0 }; blitInfo.srcOffsets[1] = pSourceRect != nullptr ? VkOffset3D{ int32_t(pSourceRect->right), int32_t(pSourceRect->bottom), 1 } : VkOffset3D{ int32_t(srcExtent.width), int32_t(srcExtent.height), 1 }; if (unlikely(IsBlitRegionInvalid(blitInfo.srcOffsets, srcExtent))) return D3DERR_INVALIDCALL; if (unlikely(IsBlitRegionInvalid(blitInfo.dstOffsets, dstExtent))) return D3DERR_INVALIDCALL; VkExtent3D srcCopyExtent = { uint32_t(blitInfo.srcOffsets[1].x - blitInfo.srcOffsets[0].x), uint32_t(blitInfo.srcOffsets[1].y - blitInfo.srcOffsets[0].y), uint32_t(blitInfo.srcOffsets[1].z - blitInfo.srcOffsets[0].z) }; VkExtent3D dstCopyExtent = { uint32_t(blitInfo.dstOffsets[1].x - blitInfo.dstOffsets[0].x), uint32_t(blitInfo.dstOffsets[1].y - blitInfo.dstOffsets[0].y), uint32_t(blitInfo.dstOffsets[1].z - blitInfo.dstOffsets[0].z) }; bool srcIsDS = IsDepthStencilFormat(srcFormat); bool dstIsDS = IsDepthStencilFormat(dstFormat); if (unlikely(srcIsDS || dstIsDS)) { if (unlikely(!srcIsDS || !dstIsDS)) return D3DERR_INVALIDCALL; if (unlikely(srcTextureInfo->Desc()->Discard || dstTextureInfo->Desc()->Discard)) return D3DERR_INVALIDCALL; if (unlikely(srcCopyExtent.width != srcExtent.width || srcCopyExtent.height != srcExtent.height)) return D3DERR_INVALIDCALL; if (unlikely(m_flags.test(D3D9DeviceFlag::InScene))) return D3DERR_INVALIDCALL; } // Copies would only work if the extents match. (ie. no stretching) bool stretch = srcCopyExtent != dstCopyExtent; bool dstHasRTUsage = (dstTextureInfo->Desc()->Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)) != 0; bool dstIsSurface = dstTextureInfo->GetType() == D3DRTYPE_SURFACE; if (stretch) { if (unlikely(pSourceSurface == pDestSurface)) return D3DERR_INVALIDCALL; if (unlikely(dstIsDS)) return D3DERR_INVALIDCALL; // The docs say that stretching is only allowed if the destination is either a render target surface or a render target texture. // However in practice, using an offscreen plain surface in D3DPOOL_DEFAULT as the destination works fine. // Using a texture without USAGE_RENDERTARGET as destination however does not. if (unlikely(!dstIsSurface && !dstHasRTUsage)) return D3DERR_INVALIDCALL; } else { bool srcIsSurface = srcTextureInfo->GetType() == D3DRTYPE_SURFACE; bool srcHasRTUsage = (srcTextureInfo->Desc()->Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)) != 0; // Non-stretching copies are only allowed if: // - the destination is either a render target surface or a render target texture // - both destination and source are depth stencil surfaces // - both destination and source are offscreen plain surfaces. // The only way to get a surface with resource type D3DRTYPE_SURFACE without USAGE_RT or USAGE_DS is CreateOffscreenPlainSurface. if (unlikely((!dstHasRTUsage && (!dstIsSurface || !srcIsSurface || srcHasRTUsage)) && !m_isD3D8Compatible)) return D3DERR_INVALIDCALL; } fastPath &= !stretch; if (!fastPath || needsResolve) { // Compressed destination formats are forbidden for blits. if (dstFormatInfo->flags.test(DxvkFormatFlag::BlockCompressed)) return D3DERR_INVALIDCALL; } auto EmitResolveCS = [&](const Rc& resolveDst, bool intermediate) { VkImageResolve region; region.srcSubresource = blitInfo.srcSubresource; region.srcOffset = intermediate ? VkOffset3D { 0, 0, 0 } : blitInfo.srcOffsets[0]; region.dstSubresource = intermediate ? blitInfo.srcSubresource : blitInfo.dstSubresource; region.dstOffset = intermediate ? VkOffset3D { 0, 0, 0 } : blitInfo.dstOffsets[0]; region.extent = intermediate ? resolveDst->mipLevelExtent(blitInfo.srcSubresource.mipLevel) : srcCopyExtent; EmitCs([ cDstImage = resolveDst, cSrcImage = srcImage, cRegion = region ] (DxvkContext* ctx) { // Deliberately use AVERAGE even for depth resolves here ctx->resolveImage(cDstImage, cSrcImage, cRegion, cSrcImage->info().format, VK_RESOLVE_MODE_AVERAGE_BIT, VK_RESOLVE_MODE_SAMPLE_ZERO_BIT); }); }; if (fastPath) { if (needsResolve) { EmitResolveCS(dstImage, false); } else { EmitCs([ cDstImage = dstImage, cSrcImage = srcImage, cDstLayers = blitInfo.dstSubresource, cSrcLayers = blitInfo.srcSubresource, cDstOffset = blitInfo.dstOffsets[0], cSrcOffset = blitInfo.srcOffsets[0], cExtent = srcCopyExtent ] (DxvkContext* ctx) { ctx->copyImage( cDstImage, cDstLayers, cDstOffset, cSrcImage, cSrcLayers, cSrcOffset, cExtent); }); } } else { if (needsResolve) { auto resolveSrc = srcTextureInfo->GetResolveImage(); EmitResolveCS(resolveSrc, true); srcImage = resolveSrc; } DxvkImageViewKey dstViewInfo; dstViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; dstViewInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; dstViewInfo.format = dstImage->info().format; dstViewInfo.aspects = blitInfo.dstSubresource.aspectMask; dstViewInfo.mipIndex = blitInfo.dstSubresource.mipLevel; dstViewInfo.mipCount = 1; dstViewInfo.layerIndex = blitInfo.dstSubresource.baseArrayLayer; dstViewInfo.layerCount = blitInfo.dstSubresource.layerCount; dstViewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(dstTextureInfo->GetMapping().Swizzle); DxvkImageViewKey srcViewInfo; srcViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; srcViewInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; srcViewInfo.format = srcImage->info().format; srcViewInfo.aspects = blitInfo.srcSubresource.aspectMask; srcViewInfo.mipIndex = blitInfo.srcSubresource.mipLevel; srcViewInfo.mipCount = 1; srcViewInfo.layerIndex = blitInfo.srcSubresource.baseArrayLayer; srcViewInfo.layerCount = blitInfo.srcSubresource.layerCount; srcViewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(srcTextureInfo->GetMapping().Swizzle); EmitCs([ cDstView = dstImage->createView(dstViewInfo), cSrcView = srcImage->createView(srcViewInfo), cBlitInfo = blitInfo, cFilter = stretch ? DecodeFilter(Filter) : VK_FILTER_NEAREST ] (DxvkContext* ctx) { ctx->blitImageView( cDstView, cBlitInfo.dstOffsets, cSrcView, cBlitInfo.srcOffsets, cFilter); }); } dstTextureInfo->SetNeedsReadback(dst->GetSubresource(), true); if (dstTextureInfo->IsAutomaticMip()) MarkTextureMipsDirty(dstTextureInfo); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::ColorFill( IDirect3DSurface9* pSurface, const RECT* pRect, D3DCOLOR Color) { D3D9DeviceLock lock = LockDevice(); D3D9Surface* dst = static_cast(pSurface); if (unlikely(dst == nullptr)) return D3DERR_INVALIDCALL; D3D9CommonTexture* dstTextureInfo = dst->GetCommonTexture(); if (dstTextureInfo->IsNull()) return D3D_OK; if (unlikely(dstTextureInfo->Desc()->Pool != D3DPOOL_DEFAULT)) return D3DERR_INVALIDCALL; VkExtent3D mipExtent = dstTextureInfo->GetExtentMip(dst->GetSubresource()); VkOffset3D offset = VkOffset3D{ 0u, 0u, 0u }; VkExtent3D extent = mipExtent; if (pRect != nullptr) ConvertRect(*pRect, offset, extent); VkClearValue clearValue = { }; DecodeD3DCOLOR(Color, clearValue.color.float32); Rc image = dstTextureInfo->GetImage(); if (image->formatInfo()->aspectMask != VK_IMAGE_ASPECT_COLOR_BIT) return D3DERR_INVALIDCALL; VkImageSubresourceLayers subresource = { }; subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresource.mipLevel = dst->GetMipLevel(); if (dst->GetFace() == D3D9CommonTexture::AllLayers) { subresource.baseArrayLayer = 0u; subresource.layerCount = image->info().numLayers; } else { subresource.baseArrayLayer = dst->GetFace(); subresource.layerCount = 1u; } if (image->formatInfo()->flags.test(DxvkFormatFlag::BlockCompressed)) { EmitCs([ cImage = std::move(image), cSubresource = subresource, cOffset = offset, cExtent = extent, cClearValue = clearValue ] (DxvkContext* ctx) { auto formatInfo = cImage->formatInfo(); VkFormat blockFormat = formatInfo->elementSize == 16u ? VK_FORMAT_R32G32B32A32_UINT : VK_FORMAT_R32G32_UINT; DxvkImageUsageInfo usage = { }; usage.usage = VK_IMAGE_USAGE_STORAGE_BIT; usage.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT | VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT; usage.viewFormatCount = 1; usage.viewFormats = &blockFormat; usage.layout = VK_IMAGE_LAYOUT_GENERAL; ctx->ensureImageCompatibility(cImage, usage); DxvkImageViewKey viewKey = { }; viewKey.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; viewKey.format = blockFormat; viewKey.usage = VK_IMAGE_USAGE_STORAGE_BIT; viewKey.aspects = cSubresource.aspectMask; viewKey.mipIndex = cSubresource.mipLevel; viewKey.mipCount = 1u; viewKey.layerIndex = cSubresource.baseArrayLayer; viewKey.layerCount = cSubresource.layerCount; Rc view = cImage->createView(viewKey); VkClearValue clearBlock = { }; clearBlock.color = util::encodeClearBlockValue(cImage->info().format, cClearValue.color); VkOffset3D offset = util::computeBlockOffset(cOffset, formatInfo->blockSize); VkExtent3D extent = util::computeBlockExtent(cExtent, formatInfo->blockSize); ctx->clearImageView(view, offset, extent, VK_IMAGE_ASPECT_COLOR_BIT, clearBlock); }); } else { EmitCs([ cImage = std::move(image), cSubresource = subresource, cOffset = offset, cExtent = extent, cClearValue = clearValue ] (DxvkContext* ctx) { DxvkImageUsageInfo usage = { }; usage.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; ctx->ensureImageCompatibility(cImage, usage); DxvkImageViewKey viewKey = { }; viewKey.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; viewKey.format = cImage->info().format; viewKey.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; viewKey.aspects = cSubresource.aspectMask; viewKey.mipIndex = cSubresource.mipLevel; viewKey.mipCount = 1u; viewKey.layerIndex = cSubresource.baseArrayLayer; viewKey.layerCount = cSubresource.layerCount; Rc view = cImage->createView(viewKey); if (cOffset == VkOffset3D() && cExtent == cImage->mipLevelExtent(viewKey.mipIndex)) { ctx->clearRenderTarget(view, cSubresource.aspectMask, cClearValue, 0u); } else { ctx->clearImageView(view, cOffset, cExtent, cSubresource.aspectMask, cClearValue); } }); } dstTextureInfo->SetNeedsReadback(dst->GetSubresource(), true); if (dstTextureInfo->IsAutomaticMip()) MarkTextureMipsDirty(dstTextureInfo); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateOffscreenPlainSurface( UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) { return CreateOffscreenPlainSurfaceEx( Width, Height, Format, Pool, ppSurface, pSharedHandle, 0); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetRenderTarget( DWORD RenderTargetIndex, IDirect3DSurface9* pRenderTarget) { D3D9DeviceLock lock = LockDevice(); if (unlikely(pRenderTarget == nullptr && RenderTargetIndex == 0)) return D3DERR_INVALIDCALL; // We need to make sure the render target was created using this device. D3D9Surface* rt = static_cast(pRenderTarget); if (unlikely(rt != nullptr && rt->GetDevice() != this)) return D3DERR_INVALIDCALL; return SetRenderTargetInternal(RenderTargetIndex, pRenderTarget); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetRenderTargetInternal( DWORD RenderTargetIndex, IDirect3DSurface9* pRenderTarget) { if (unlikely(RenderTargetIndex >= caps::MaxSimultaneousRenderTargets)) return D3DERR_INVALIDCALL; D3D9Surface* rt = static_cast(pRenderTarget); D3D9CommonTexture* texInfo = rt != nullptr ? rt->GetCommonTexture() : nullptr; if (unlikely(rt != nullptr && !(texInfo->Desc()->Usage & D3DUSAGE_RENDERTARGET))) return D3DERR_INVALIDCALL; if (RenderTargetIndex == 0) { D3DVIEWPORT9 viewport; viewport.X = 0; viewport.Y = 0; viewport.MinZ = 0.0f; viewport.MaxZ = 1.0f; RECT scissorRect; scissorRect.left = 0; scissorRect.top = 0; if (likely(rt != nullptr)) { auto rtSize = rt->GetSurfaceExtent(); viewport.Width = rtSize.width; viewport.Height = rtSize.height; scissorRect.right = rtSize.width; scissorRect.bottom = rtSize.height; } else { viewport.Width = 0; viewport.Height = 0; scissorRect.right = 0; scissorRect.bottom = 0; } if (m_state.viewport != viewport) { m_flags.set(D3D9DeviceFlag::DirtyFFViewport); m_flags.set(D3D9DeviceFlag::DirtyPointScale); m_flags.set(D3D9DeviceFlag::DirtyViewportScissor); m_state.viewport = viewport; } if (m_state.scissorRect != scissorRect) { m_flags.set(D3D9DeviceFlag::DirtyViewportScissor); m_state.scissorRect = scissorRect; } } if (m_state.renderTargets[RenderTargetIndex] == rt) return D3D_OK; // Do a strong flush if the first render target is changed. ConsiderFlush(RenderTargetIndex == 0 ? GpuFlushType::ImplicitStrongHint : GpuFlushType::ImplicitWeakHint); m_flags.set(D3D9DeviceFlag::DirtyFramebuffer); m_state.renderTargets[RenderTargetIndex] = rt; // Update feedback loop tracking bitmasks UpdateActiveRTs(RenderTargetIndex); // Update render target alpha swizzle bitmask if we need to fix up the alpha channel // for XRGB formats uint32_t originalAlphaSwizzleRTs = m_alphaSwizzleRTs; m_alphaSwizzleRTs &= ~(1 << RenderTargetIndex); if (rt != nullptr) { if (texInfo->GetMapping().Swizzle.a == VK_COMPONENT_SWIZZLE_ONE) m_alphaSwizzleRTs |= 1 << RenderTargetIndex; if (texInfo->IsAutomaticMip()) texInfo->SetNeedsMipGen(true); } if (originalAlphaSwizzleRTs != m_alphaSwizzleRTs) m_flags.set(D3D9DeviceFlag::DirtyBlendState); if (RenderTargetIndex == 0) { if (likely(texInfo != nullptr)) { if (IsAlphaTestEnabled()) { // Need to recalculate the precision. m_flags.set(D3D9DeviceFlag::DirtyAlphaTestState); } bool validSampleMask = texInfo->Desc()->MultiSample > D3DMULTISAMPLE_NONMASKABLE; if (validSampleMask != m_flags.test(D3D9DeviceFlag::ValidSampleMask)) { m_flags.clr(D3D9DeviceFlag::ValidSampleMask); if (validSampleMask) m_flags.set(D3D9DeviceFlag::ValidSampleMask); m_flags.set(D3D9DeviceFlag::DirtyMultiSampleState); } } else { m_flags.clr(D3D9DeviceFlag::ValidSampleMask); m_flags.set(D3D9DeviceFlag::DirtyMultiSampleState); } } return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetRenderTarget( DWORD RenderTargetIndex, IDirect3DSurface9** ppRenderTarget) { D3D9DeviceLock lock = LockDevice(); InitReturnPtr(ppRenderTarget); if (unlikely(ppRenderTarget == nullptr || RenderTargetIndex > caps::MaxSimultaneousRenderTargets)) return D3DERR_INVALIDCALL; if (m_state.renderTargets[RenderTargetIndex] == nullptr) return D3DERR_NOTFOUND; *ppRenderTarget = m_state.renderTargets[RenderTargetIndex].ref(); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetDepthStencilSurface(IDirect3DSurface9* pNewZStencil) { D3D9DeviceLock lock = LockDevice(); D3D9Surface* ds = static_cast(pNewZStencil); if (unlikely(ds && !(ds->GetCommonTexture()->Desc()->Usage & D3DUSAGE_DEPTHSTENCIL))) return D3DERR_INVALIDCALL; if (m_state.depthStencil == ds) return D3D_OK; ConsiderFlush(GpuFlushType::ImplicitWeakHint); m_flags.set(D3D9DeviceFlag::DirtyFramebuffer); // Update depth bias if necessary if (ds != nullptr && m_depthBiasRepresentation.depthBiasRepresentation != VK_DEPTH_BIAS_REPRESENTATION_FLOAT_EXT) { const int32_t vendorId = m_dxvkDevice->adapter()->deviceProperties().vendorID; const bool exact = m_depthBiasRepresentation.depthBiasExact; const bool forceUnorm = m_depthBiasRepresentation.depthBiasRepresentation == VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT; const float rValue = GetDepthBufferRValue(ds->GetCommonTexture()->GetFormatMapping().FormatColor, vendorId, exact, forceUnorm); if (m_depthBiasScale != rValue) { m_depthBiasScale = rValue; m_flags.set(D3D9DeviceFlag::DirtyDepthBias); } } m_state.depthStencil = ds; UpdateActiveHazardsDS(std::numeric_limits::max()); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface) { D3D9DeviceLock lock = LockDevice(); InitReturnPtr(ppZStencilSurface); if (unlikely(ppZStencilSurface == nullptr)) return D3DERR_INVALIDCALL; if (m_state.depthStencil == nullptr) return D3DERR_NOTFOUND; *ppZStencilSurface = m_state.depthStencil.ref(); return D3D_OK; } // The Begin/EndScene functions actually do nothing. // Some games don't even call them. HRESULT STDMETHODCALLTYPE D3D9DeviceEx::BeginScene() { D3D9DeviceLock lock = LockDevice(); if (unlikely(m_flags.test(D3D9DeviceFlag::InScene))) return D3DERR_INVALIDCALL; m_flags.set(D3D9DeviceFlag::InScene); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::EndScene() { D3D9DeviceLock lock = LockDevice(); if (unlikely(!m_flags.test(D3D9DeviceFlag::InScene))) return D3DERR_INVALIDCALL; ConsiderFlush(GpuFlushType::ImplicitStrongHint); m_flags.clr(D3D9DeviceFlag::InScene); // D3D9 resets the internally bound vertex buffers and index buffer in EndScene if they were unbound in the meantime. // We have to ignore unbinding those buffers because of Operation Flashpoint Red River, // so we should also clear the bindings here, to avoid leaks. if (m_state.indices == nullptr) { EmitCs([](DxvkContext* ctx) { ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32); }); } for (uint32_t i : bit::BitMask(~m_activeVertexBuffers & ((1 << 16) - 1))) { if (m_state.vertexBuffers[i].vertexBuffer == nullptr) { EmitCs([cIndex = i](DxvkContext* ctx) { ctx->bindVertexBuffer(cIndex, DxvkBufferSlice(), 0); }); } } return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::Clear( DWORD Count, const D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil) { if (unlikely(!Count && pRects)) return D3D_OK; D3D9DeviceLock lock = LockDevice(); // D3DCLEAR_ZBUFFER and D3DCLEAR_STENCIL are invalid flags // if there is no currently bound DS (which can be the autoDS) if (unlikely(m_state.depthStencil == nullptr && (Flags & (D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL)))) return D3DERR_INVALIDCALL; const auto& vp = m_state.viewport; const auto& sc = m_state.scissorRect; bool srgb = m_state.renderStates[D3DRS_SRGBWRITEENABLE]; bool scissor = m_state.renderStates[D3DRS_SCISSORTESTENABLE]; VkOffset3D offset = { int32_t(vp.X), int32_t(vp.Y), 0 }; VkExtent3D extent = { vp.Width, vp.Height, 1u }; if (scissor) { offset.x = std::max (offset.x, sc.left); offset.y = std::max (offset.y, sc.top); extent.width = std::min(extent.width, sc.right - offset.x); extent.height = std::min(extent.height, sc.bottom - offset.y); } // This becomes pretty unreadable in one singular if statement... if (Count) { // If pRects is null, or our first rect encompasses the viewport: if (!pRects) Count = 0; else if (pRects[0].x1 <= offset.x && pRects[0].y1 <= offset.y && pRects[0].x2 >= offset.x + int32_t(extent.width) && pRects[0].y2 >= offset.y + int32_t(extent.height)) Count = 0; } // Here, Count of 0 will denote whether or not to care about user rects. VkClearValue clearValueDepth; clearValueDepth.depthStencil.depth = Z; clearValueDepth.depthStencil.stencil = Stencil; VkClearValue clearValueColor; DecodeD3DCOLOR(Color, clearValueColor.color.float32); VkImageAspectFlags depthAspectMask = 0; if (m_state.depthStencil != nullptr) { if (Flags & D3DCLEAR_ZBUFFER) depthAspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; if (Flags & D3DCLEAR_STENCIL) depthAspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; depthAspectMask &= lookupFormatInfo(m_state.depthStencil->GetCommonTexture()->GetFormatMapping().FormatColor)->aspectMask; } auto ClearImageView = [this]( uint32_t alignment, VkOffset3D offset, VkExtent3D extent, const Rc& imageView, VkImageAspectFlags aspectMask, VkClearValue clearValue) { VkExtent3D imageExtent = imageView->mipLevelExtent(0); extent.width = std::min(imageExtent.width, extent.width); extent.height = std::min(imageExtent.height, extent.height); if (unlikely(uint32_t(offset.x) >= imageExtent.width || uint32_t(offset.y) >= imageExtent.height)) return; const bool fullClear = align(extent.width, alignment) == align(imageExtent.width, alignment) && align(extent.height, alignment) == align(imageExtent.height, alignment) && offset.x == 0 && offset.y == 0; if (fullClear) { EmitCs([ cClearValue = clearValue, cAspectMask = aspectMask, cImageView = imageView ] (DxvkContext* ctx) { ctx->clearRenderTarget(cImageView, cAspectMask, cClearValue, 0u); }); } else { EmitCs([ cClearValue = clearValue, cAspectMask = aspectMask, cImageView = imageView, cOffset = offset, cExtent = extent ] (DxvkContext* ctx) { ctx->clearImageView( cImageView, cOffset, cExtent, cAspectMask, cClearValue); }); } }; auto ClearViewRect = [&]( uint32_t alignment, VkOffset3D offset, VkExtent3D extent) { // Clear depth if we need to. if (depthAspectMask != 0) ClearImageView(alignment, offset, extent, m_state.depthStencil->GetDepthStencilView(), depthAspectMask, clearValueDepth); // Clear render targets if we need to. if (Flags & D3DCLEAR_TARGET) { for (uint32_t rt = 0u; rt < m_state.renderTargets.size(); rt++) { if (!HasRenderTargetBound(rt)) continue; const auto& rts = m_state.renderTargets[rt]; const auto& rtv = rts->GetRenderTargetView(srgb); if (likely(rtv != nullptr)) { ClearImageView(alignment, offset, extent, rtv, VK_IMAGE_ASPECT_COLOR_BIT, clearValueColor); D3D9CommonTexture* dstTexture = rts->GetCommonTexture(); if (dstTexture->IsAutomaticMip()) MarkTextureMipsDirty(dstTexture); } } } }; // A Hat in Time and other UE3 games only gets partial clears here // because of an oversized rt height due to their weird alignment... // This works around that. uint32_t alignment = m_d3d9Options.lenientClear ? 8 : 1; if (extent.width == 0 || extent.height == 0) { return D3D_OK; } if (!Count) { // Clear our viewport & scissor minified region in this rendertarget. ClearViewRect(alignment, offset, extent); } else { // Clear the application provided rects. for (uint32_t i = 0; i < Count; i++) { VkOffset3D rectOffset = { std::max(pRects[i].x1, offset.x), std::max(pRects[i].y1, offset.y), 0 }; if (std::min(pRects[i].x2, offset.x + extent.width) <= rectOffset.x || std::min(pRects[i].y2, offset.y + extent.height) <= rectOffset.y) { continue; } VkExtent3D rectExtent = { std::min(pRects[i].x2, offset.x + extent.width) - rectOffset.x, std::min(pRects[i].y2, offset.y + extent.height) - rectOffset.y, 1u }; ClearViewRect(alignment, rectOffset, rectExtent); } } return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix) { return SetStateTransform(GetTransformIndex(State), pMatrix); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix) { D3D9DeviceLock lock = LockDevice(); if (unlikely(pMatrix == nullptr)) return D3DERR_INVALIDCALL; *pMatrix = bit::cast(m_state.transforms[GetTransformIndex(State)]); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::MultiplyTransform(D3DTRANSFORMSTATETYPE TransformState, const D3DMATRIX* pMatrix) { D3D9DeviceLock lock = LockDevice(); const uint32_t idx = GetTransformIndex(TransformState); m_state.transforms[idx] = m_state.transforms[idx] * ConvertMatrix(pMatrix); m_flags.set(D3D9DeviceFlag::DirtyFFVertexData); if (idx == GetTransformIndex(D3DTS_VIEW) || idx >= GetTransformIndex(D3DTS_WORLD)) m_flags.set(D3D9DeviceFlag::DirtyFFVertexBlend); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetViewport(const D3DVIEWPORT9* pViewport) { D3D9DeviceLock lock = LockDevice(); if (unlikely(ShouldRecord())) return m_recorder->SetViewport(pViewport); if (m_state.viewport == *pViewport) return D3D_OK; m_state.viewport = *pViewport; m_flags.set(D3D9DeviceFlag::DirtyViewportScissor); m_flags.set(D3D9DeviceFlag::DirtyFFViewport); m_flags.set(D3D9DeviceFlag::DirtyPointScale); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetViewport(D3DVIEWPORT9* pViewport) { D3D9DeviceLock lock = LockDevice(); if (pViewport == nullptr) return D3DERR_INVALIDCALL; *pViewport = m_state.viewport; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetMaterial(const D3DMATERIAL9* pMaterial) { D3D9DeviceLock lock = LockDevice(); if (unlikely(pMaterial == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(ShouldRecord())) return m_recorder->SetMaterial(pMaterial); m_state.material = *pMaterial; m_flags.set(D3D9DeviceFlag::DirtyFFVertexData); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetMaterial(D3DMATERIAL9* pMaterial) { D3D9DeviceLock lock = LockDevice(); if (unlikely(pMaterial == nullptr)) return D3DERR_INVALIDCALL; *pMaterial = m_state.material; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetLight(DWORD Index, const D3DLIGHT9* pLight) { D3D9DeviceLock lock = LockDevice(); if (unlikely(pLight == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(ShouldRecord())) { m_recorder->SetLight(Index, pLight); return D3D_OK; } if (Index >= m_state.lights.size()) m_state.lights.resize(Index + 1); m_state.lights[Index] = *pLight; if (m_state.IsLightEnabled(Index)) m_flags.set(D3D9DeviceFlag::DirtyFFVertexData); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetLight(DWORD Index, D3DLIGHT9* pLight) { D3D9DeviceLock lock = LockDevice(); if (unlikely(pLight == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(Index >= m_state.lights.size() || !m_state.lights[Index])) return D3DERR_INVALIDCALL; *pLight = m_state.lights[Index].value(); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::LightEnable(DWORD Index, BOOL Enable) { D3D9DeviceLock lock = LockDevice(); if (unlikely(ShouldRecord())) { m_recorder->LightEnable(Index, Enable); return D3D_OK; } if (unlikely(Index >= m_state.lights.size())) m_state.lights.resize(Index + 1); if (unlikely(!m_state.lights[Index])) m_state.lights[Index] = DefaultLight; if (m_state.IsLightEnabled(Index) == !!Enable) return D3D_OK; uint32_t searchIndex = std::numeric_limits::max(); uint32_t setIndex = Index; if (!Enable) std::swap(searchIndex, setIndex); for (auto& idx : m_state.enabledLightIndices) { if (idx == searchIndex) { idx = setIndex; m_flags.set(D3D9DeviceFlag::DirtyFFVertexData); m_flags.set(D3D9DeviceFlag::DirtyFFVertexShader); break; } } return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetLightEnable(DWORD Index, BOOL* pEnable) { D3D9DeviceLock lock = LockDevice(); if (unlikely(pEnable == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(Index >= m_state.lights.size() || !m_state.lights[Index])) return D3DERR_INVALIDCALL; *pEnable = m_state.IsLightEnabled(Index) ? 128 : 0; // Weird quirk but OK. return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetClipPlane(DWORD Index, const float* pPlane) { D3D9DeviceLock lock = LockDevice(); if (unlikely(!pPlane)) return D3DERR_INVALIDCALL; // Higher indexes will be capped to the last valid index if (unlikely(Index >= caps::MaxClipPlanes)) Index = caps::MaxClipPlanes - 1; if (unlikely(ShouldRecord())) return m_recorder->SetClipPlane(Index, pPlane); bool dirty = false; for (uint32_t i = 0; i < 4; i++) { dirty |= m_state.clipPlanes[Index].coeff[i] != pPlane[i]; m_state.clipPlanes[Index].coeff[i] = pPlane[i]; } bool enabled = m_state.renderStates[D3DRS_CLIPPLANEENABLE] & (1u << Index); dirty &= enabled; if (dirty) m_flags.set(D3D9DeviceFlag::DirtyClipPlanes); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetClipPlane(DWORD Index, float* pPlane) { D3D9DeviceLock lock = LockDevice(); if (unlikely(!pPlane)) return D3DERR_INVALIDCALL; // Higher indexes will be capped to the last valid index if (unlikely(Index >= caps::MaxClipPlanes)) Index = caps::MaxClipPlanes - 1; for (uint32_t i = 0; i < 4; i++) pPlane[i] = m_state.clipPlanes[Index].coeff[i]; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) { D3D9DeviceLock lock = LockDevice(); // D3D9 only allows reading for values 0 and 7-255 so we don't need to do anything but return OK if (unlikely(State > 255 || (State < D3DRS_ZENABLE && State != 0))) { return D3D_OK; } if (unlikely(ShouldRecord())) return m_recorder->SetRenderState(State, Value); auto& states = m_state.renderStates; DWORD old = states[State]; bool changed = old != Value; if (likely(changed)) { const bool oldClipPlaneEnabled = IsClipPlaneEnabled(); const bool oldDepthBiasEnabled = IsDepthBiasEnabled(); const bool oldATOC = IsAlphaToCoverageEnabled(); const bool oldNVDB = states[D3DRS_ADAPTIVETESS_X] == uint32_t(D3D9Format::NVDB); const bool oldAlphaTest = IsAlphaTestEnabled(); states[State] = Value; // AMD's driver hack for ATOC and RESZ if (unlikely(State == D3DRS_POINTSIZE)) { // ATOC constexpr uint32_t AlphaToCoverageEnable = uint32_t(D3D9Format::A2M1); constexpr uint32_t AlphaToCoverageDisable = uint32_t(D3D9Format::A2M0); if (Value == AlphaToCoverageEnable || Value == AlphaToCoverageDisable) { m_amdATOC = Value == AlphaToCoverageEnable; bool newATOC = IsAlphaToCoverageEnabled(); bool newAlphaTest = IsAlphaTestEnabled(); if (oldATOC != newATOC) m_flags.set(D3D9DeviceFlag::DirtyMultiSampleState); if (oldAlphaTest != newAlphaTest) m_flags.set(D3D9DeviceFlag::DirtyAlphaTestState); return D3D_OK; } // RESZ constexpr uint32_t RESZ = 0x7fa05000; if (Value == RESZ) { ResolveZ(); return D3D_OK; } } // NV's driver hack for ATOC. if (unlikely(State == D3DRS_ADAPTIVETESS_Y)) { constexpr uint32_t AlphaToCoverageEnable = uint32_t(D3D9Format::ATOC); constexpr uint32_t AlphaToCoverageDisable = 0; if (Value == AlphaToCoverageEnable || Value == AlphaToCoverageDisable) { m_nvATOC = Value == AlphaToCoverageEnable; bool newATOC = IsAlphaToCoverageEnabled(); bool newAlphaTest = IsAlphaTestEnabled(); if (oldATOC != newATOC) m_flags.set(D3D9DeviceFlag::DirtyMultiSampleState); if (oldAlphaTest != newAlphaTest) m_flags.set(D3D9DeviceFlag::DirtyAlphaTestState); return D3D_OK; } if (unlikely(Value == uint32_t(D3D9Format::COPM))) { // UE3 calls this MinimalNVIDIADriverShaderOptimization Logger::info("D3D9DeviceEx::SetRenderState: MinimalNVIDIADriverShaderOptimization is unsupported"); return D3D_OK; } } switch (State) { case D3DRS_SEPARATEALPHABLENDENABLE: case D3DRS_ALPHABLENDENABLE: case D3DRS_BLENDOP: case D3DRS_BLENDOPALPHA: case D3DRS_DESTBLEND: case D3DRS_DESTBLENDALPHA: case D3DRS_SRCBLEND: case D3DRS_SRCBLENDALPHA: m_flags.set(D3D9DeviceFlag::DirtyBlendState); break; case D3DRS_COLORWRITEENABLE: if (likely(!old != !Value)) UpdateAnyColorWrites<0>(); m_flags.set(D3D9DeviceFlag::DirtyBlendState); break; case D3DRS_COLORWRITEENABLE1: if (likely(!old != !Value)) UpdateAnyColorWrites<1>(); m_flags.set(D3D9DeviceFlag::DirtyBlendState); break; case D3DRS_COLORWRITEENABLE2: if (likely(!old != !Value)) UpdateAnyColorWrites<2>(); m_flags.set(D3D9DeviceFlag::DirtyBlendState); break; case D3DRS_COLORWRITEENABLE3: if (likely(!old != !Value)) UpdateAnyColorWrites<3>(); m_flags.set(D3D9DeviceFlag::DirtyBlendState); break; case D3DRS_ALPHATESTENABLE: { bool newATOC = IsAlphaToCoverageEnabled(); bool newAlphaTest = IsAlphaTestEnabled(); if (oldATOC != newATOC) m_flags.set(D3D9DeviceFlag::DirtyMultiSampleState); if (oldAlphaTest != newAlphaTest) m_flags.set(D3D9DeviceFlag::DirtyAlphaTestState); break; } case D3DRS_ALPHAFUNC: m_flags.set(D3D9DeviceFlag::DirtyAlphaTestState); break; case D3DRS_BLENDFACTOR: BindBlendFactor(); break; case D3DRS_MULTISAMPLEMASK: if (m_flags.test(D3D9DeviceFlag::ValidSampleMask)) m_flags.set(D3D9DeviceFlag::DirtyMultiSampleState); break; case D3DRS_ZWRITEENABLE: if (likely(!old != !Value)) UpdateActiveHazardsDS(std::numeric_limits::max()); [[fallthrough]]; case D3DRS_STENCILENABLE: case D3DRS_ZENABLE: if (likely(m_state.depthStencil != nullptr)) m_flags.set(D3D9DeviceFlag::DirtyFramebuffer); m_flags.set(D3D9DeviceFlag::DirtyDepthStencilState); break; case D3DRS_ZFUNC: case D3DRS_TWOSIDEDSTENCILMODE: case D3DRS_STENCILFAIL: case D3DRS_STENCILZFAIL: case D3DRS_STENCILPASS: case D3DRS_STENCILFUNC: case D3DRS_CCW_STENCILFAIL: case D3DRS_CCW_STENCILZFAIL: case D3DRS_CCW_STENCILPASS: case D3DRS_CCW_STENCILFUNC: case D3DRS_STENCILMASK: case D3DRS_STENCILWRITEMASK: m_flags.set(D3D9DeviceFlag::DirtyDepthStencilState); break; case D3DRS_STENCILREF: BindDepthStencilRefrence(); break; case D3DRS_SCISSORTESTENABLE: m_flags.set(D3D9DeviceFlag::DirtyViewportScissor); break; case D3DRS_SRGBWRITEENABLE: m_flags.set(D3D9DeviceFlag::DirtyFramebuffer); break; case D3DRS_DEPTHBIAS: case D3DRS_SLOPESCALEDEPTHBIAS: { const bool depthBiasEnabled = IsDepthBiasEnabled(); if (depthBiasEnabled != oldDepthBiasEnabled) m_flags.set(D3D9DeviceFlag::DirtyRasterizerState); if (depthBiasEnabled) m_flags.set(D3D9DeviceFlag::DirtyDepthBias); break; } case D3DRS_CULLMODE: case D3DRS_FILLMODE: m_flags.set(D3D9DeviceFlag::DirtyRasterizerState); break; case D3DRS_CLIPPLANEENABLE: { const bool clipPlaneEnabled = IsClipPlaneEnabled(); if (clipPlaneEnabled != oldClipPlaneEnabled) m_flags.set(D3D9DeviceFlag::DirtyFFVertexShader); m_flags.set(D3D9DeviceFlag::DirtyClipPlanes); break; } case D3DRS_ALPHAREF: UpdatePushConstant(); break; case D3DRS_TEXTUREFACTOR: m_flags.set(D3D9DeviceFlag::DirtyFFPixelData); break; case D3DRS_DIFFUSEMATERIALSOURCE: case D3DRS_AMBIENTMATERIALSOURCE: case D3DRS_SPECULARMATERIALSOURCE: case D3DRS_EMISSIVEMATERIALSOURCE: case D3DRS_COLORVERTEX: case D3DRS_LIGHTING: case D3DRS_NORMALIZENORMALS: case D3DRS_LOCALVIEWER: m_flags.set(D3D9DeviceFlag::DirtyFFVertexShader); break; case D3DRS_AMBIENT: m_flags.set(D3D9DeviceFlag::DirtyFFVertexData); break; case D3DRS_SPECULARENABLE: m_flags.set(D3D9DeviceFlag::DirtyFFPixelShader); break; case D3DRS_FOGENABLE: case D3DRS_FOGVERTEXMODE: case D3DRS_FOGTABLEMODE: m_flags.set(D3D9DeviceFlag::DirtyFogState); break; case D3DRS_RANGEFOGENABLE: m_flags.set(D3D9DeviceFlag::DirtyFFVertexShader); break; case D3DRS_FOGCOLOR: m_flags.set(D3D9DeviceFlag::DirtyFogColor); break; case D3DRS_FOGSTART: m_flags.set(D3D9DeviceFlag::DirtyFogScale); break; case D3DRS_FOGEND: m_flags.set(D3D9DeviceFlag::DirtyFogScale); m_flags.set(D3D9DeviceFlag::DirtyFogEnd); break; case D3DRS_FOGDENSITY: m_flags.set(D3D9DeviceFlag::DirtyFogDensity); break; case D3DRS_POINTSIZE: UpdatePushConstant(); break; case D3DRS_POINTSIZE_MIN: UpdatePushConstant(); break; case D3DRS_POINTSIZE_MAX: UpdatePushConstant(); break; case D3DRS_POINTSCALE_A: case D3DRS_POINTSCALE_B: case D3DRS_POINTSCALE_C: m_flags.set(D3D9DeviceFlag::DirtyPointScale); break; case D3DRS_POINTSCALEENABLE: case D3DRS_POINTSPRITEENABLE: // Nothing to do here! // This is handled in UpdatePointMode. break; case D3DRS_SHADEMODE: m_flags.set(D3D9DeviceFlag::DirtyRasterizerState); break; case D3DRS_TWEENFACTOR: m_flags.set(D3D9DeviceFlag::DirtyFFVertexData); break; case D3DRS_VERTEXBLEND: m_flags.set(D3D9DeviceFlag::DirtyFFVertexShader); break; case D3DRS_INDEXEDVERTEXBLENDENABLE: if (CanSWVP() && Value) m_flags.set(D3D9DeviceFlag::DirtyFFVertexBlend); m_flags.set(D3D9DeviceFlag::DirtyFFVertexShader); break; case D3DRS_ADAPTIVETESS_Y: break; case D3DRS_ADAPTIVETESS_X: case D3DRS_ADAPTIVETESS_Z: case D3DRS_ADAPTIVETESS_W: if (states[D3DRS_ADAPTIVETESS_X] == uint32_t(D3D9Format::NVDB) || oldNVDB) { m_flags.set(D3D9DeviceFlag::DirtyDepthBounds); if (m_state.depthStencil != nullptr && m_state.renderStates[D3DRS_ZENABLE]) m_flags.set(D3D9DeviceFlag::DirtyFramebuffer); break; } [[fallthrough]]; default: static bool s_errorShown[256]; if (!std::exchange(s_errorShown[State], true)) Logger::warn(str::format("D3D9DeviceEx::SetRenderState: Unhandled render state ", State)); break; } } return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue) { D3D9DeviceLock lock = LockDevice(); if (unlikely(pValue == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(State > 255 || (State < D3DRS_ZENABLE && State != 0))) { return D3DERR_INVALIDCALL; } if (State < D3DRS_ZENABLE || State > D3DRS_BLENDOPALPHA) *pValue = 0; else *pValue = m_state.renderStates[State]; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateStateBlock( D3DSTATEBLOCKTYPE Type, IDirect3DStateBlock9** ppSB) { D3D9DeviceLock lock = LockDevice(); // A state block can not be created while another is being recorded. if (unlikely(ShouldRecord())) return D3DERR_INVALIDCALL; InitReturnPtr(ppSB); if (unlikely(ppSB == nullptr)) return D3DERR_INVALIDCALL; try { const Com sb = new D3D9StateBlock(this, ConvertStateBlockType(Type)); *ppSB = sb.ref(); if (!m_isD3D8Compatible) m_losableResourceCounter++; return D3D_OK; } catch (const DxvkError & e) { Logger::err(e.message()); return D3DERR_INVALIDCALL; } } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::BeginStateBlock() { D3D9DeviceLock lock = LockDevice(); // Only one state block can be recorded at a given time. if (unlikely(ShouldRecord())) return D3DERR_INVALIDCALL; m_recorder = new D3D9StateBlock(this, D3D9StateBlockType::None); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::EndStateBlock(IDirect3DStateBlock9** ppSB) { D3D9DeviceLock lock = LockDevice(); // Recording a state block can't end if recording hasn't been started. if (unlikely(ppSB == nullptr || !ShouldRecord())) return D3DERR_INVALIDCALL; InitReturnPtr(ppSB); *ppSB = m_recorder.ref(); if (!m_isD3D8Compatible) m_losableResourceCounter++; m_recorder = nullptr; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetClipStatus(const D3DCLIPSTATUS9* pClipStatus) { Logger::warn("D3D9DeviceEx::SetClipStatus: Stub"); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetClipStatus(D3DCLIPSTATUS9* pClipStatus) { Logger::warn("D3D9DeviceEx::GetClipStatus: Stub"); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetTexture(DWORD Stage, IDirect3DBaseTexture9** ppTexture) { D3D9DeviceLock lock = LockDevice(); if (ppTexture == nullptr) return D3DERR_INVALIDCALL; *ppTexture = nullptr; if (unlikely(InvalidSampler(Stage))) return D3D_OK; DWORD stateSampler = RemapSamplerState(Stage); *ppTexture = ref(m_state.textures[stateSampler]); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetTexture(DWORD Stage, IDirect3DBaseTexture9* pTexture) { if (unlikely(InvalidSampler(Stage))) return D3D_OK; DWORD stateSampler = RemapSamplerState(Stage); return SetStateTexture(stateSampler, pTexture); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetTextureStageState( DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD* pValue) { auto dxvkType = RemapTextureStageStateType(Type); if (unlikely(pValue == nullptr)) return D3DERR_INVALIDCALL; Stage = std::min(Stage, DWORD(caps::TextureStageCount - 1)); dxvkType = std::min(dxvkType, D3D9TextureStageStateTypes(DXVK_TSS_COUNT - 1)); *pValue = m_state.textureStages[Stage][dxvkType]; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetTextureStageState( DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value) { return SetStateTextureStageState(Stage, RemapTextureStageStateType(Type), Value); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetSamplerState( DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD* pValue) { D3D9DeviceLock lock = LockDevice(); if (unlikely(pValue == nullptr)) return D3DERR_INVALIDCALL; *pValue = 0; if (unlikely(InvalidSampler(Sampler))) return D3D_OK; Sampler = RemapSamplerState(Sampler); *pValue = m_state.samplerStates[Sampler][Type]; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetSamplerState( DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value) { if (unlikely(InvalidSampler(Sampler))) return D3D_OK; uint32_t stateSampler = RemapSamplerState(Sampler); return SetStateSamplerState(stateSampler, Type, Value); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::ValidateDevice(DWORD* pNumPasses) { D3D9DeviceLock lock = LockDevice(); if (pNumPasses != nullptr) *pNumPasses = 1; return IsDeviceLost() ? D3DERR_DEVICELOST : D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries) { // This succeeds even though we don't advertise support. return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries) { // Don't advertise support for this... return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetCurrentTexturePalette(UINT PaletteNumber) { // This succeeds even though we don't advertise support. return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetCurrentTexturePalette(UINT *PaletteNumber) { // Don't advertise support for this... return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetScissorRect(const RECT* pRect) { D3D9DeviceLock lock = LockDevice(); if (unlikely(pRect == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(ShouldRecord())) return m_recorder->SetScissorRect(pRect); if (m_state.scissorRect == *pRect) return D3D_OK; m_state.scissorRect = *pRect; m_flags.set(D3D9DeviceFlag::DirtyViewportScissor); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetScissorRect(RECT* pRect) { D3D9DeviceLock lock = LockDevice(); if (unlikely(pRect == nullptr)) return D3DERR_INVALIDCALL; *pRect = m_state.scissorRect; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetSoftwareVertexProcessing(BOOL bSoftware) { auto lock = LockDevice(); if (bSoftware && !CanSWVP()) return D3DERR_INVALIDCALL; if (!bSoftware && (m_behaviorFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING)) return D3DERR_INVALIDCALL; m_isSWVP = bSoftware; return D3D_OK; } BOOL STDMETHODCALLTYPE D3D9DeviceEx::GetSoftwareVertexProcessing() { auto lock = LockDevice(); return m_isSWVP; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetNPatchMode(float nSegments) { return D3D_OK; } float STDMETHODCALLTYPE D3D9DeviceEx::GetNPatchMode() { return 0.0f; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawPrimitive( D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount) { D3D9DeviceLock lock = LockDevice(); if (unlikely(m_state.vertexDecl == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(!PrimitiveCount)) return S_OK; bool dynamicSysmemVBOs; uint32_t firstIndex = 0; int32_t baseVertexIndex = 0; uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount); UploadPerDrawData( StartVertex, vertexCount, firstIndex, 0, baseVertexIndex, &dynamicSysmemVBOs, nullptr ); PrepareDraw(PrimitiveType, !dynamicSysmemVBOs, false); EmitCs([this, cPrimType = PrimitiveType, cPrimCount = PrimitiveCount, cStartVertex = StartVertex ](DxvkContext* ctx) { uint32_t vertexCount = GetVertexCount(cPrimType, cPrimCount); ApplyPrimitiveType(ctx, cPrimType); // Tests on Windows show that D3D9 does not do non-indexed instanced draws. VkDrawIndirectCommand draw = { }; draw.vertexCount = vertexCount; draw.instanceCount = 1u; draw.firstVertex = cStartVertex; ctx->draw(1u, &draw); }); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawIndexedPrimitive( D3DPRIMITIVETYPE PrimitiveType, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT StartIndex, UINT PrimitiveCount) { D3D9DeviceLock lock = LockDevice(); if (unlikely(m_state.vertexDecl == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(!PrimitiveCount)) return S_OK; bool dynamicSysmemVBOs; bool dynamicSysmemIBO; uint32_t indexCount = GetVertexCount(PrimitiveType, PrimitiveCount); UploadPerDrawData( MinVertexIndex, NumVertices, StartIndex, indexCount, BaseVertexIndex, &dynamicSysmemVBOs, &dynamicSysmemIBO ); PrepareDraw(PrimitiveType, !dynamicSysmemVBOs, !dynamicSysmemIBO); EmitCs([this, cPrimType = PrimitiveType, cPrimCount = PrimitiveCount, cStartIndex = StartIndex, cBaseVertexIndex = BaseVertexIndex, cInstanceCount = GetInstanceCount() ](DxvkContext* ctx) { auto drawInfo = GenerateDrawInfo(cPrimType, cPrimCount, cInstanceCount); ApplyPrimitiveType(ctx, cPrimType); VkDrawIndexedIndirectCommand draw = { }; draw.indexCount = drawInfo.vertexCount; draw.instanceCount = drawInfo.instanceCount; draw.firstIndex = cStartIndex; draw.vertexOffset = cBaseVertexIndex; ctx->drawIndexed(1u, &draw); }); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawPrimitiveUP( D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride) { D3D9DeviceLock lock = LockDevice(); if (unlikely(m_state.vertexDecl == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(!PrimitiveCount)) return S_OK; PrepareDraw(PrimitiveType, false, false); uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount); const uint32_t dataSize = GetUPDataSize(vertexCount, VertexStreamZeroStride); const uint32_t bufferSize = GetUPBufferSize(vertexCount, VertexStreamZeroStride); auto upSlice = AllocUPBuffer(bufferSize); FillUPVertexBuffer(upSlice.mapPtr, pVertexStreamZeroData, dataSize, bufferSize); EmitCs([this, cBufferSlice = std::move(upSlice.slice), cPrimType = PrimitiveType, cStride = VertexStreamZeroStride, cVertexCount = vertexCount ](DxvkContext* ctx) mutable { ApplyPrimitiveType(ctx, cPrimType); // Tests on Windows show that D3D9 does not do non-indexed instanced draws. VkDrawIndirectCommand draw = { }; draw.vertexCount = cVertexCount; draw.instanceCount = 1u; ctx->bindVertexBuffer(0, std::move(cBufferSlice), cStride); ctx->draw(1u, &draw); ctx->bindVertexBuffer(0, DxvkBufferSlice(), 0); }); m_state.vertexBuffers[0].vertexBuffer = nullptr; m_state.vertexBuffers[0].offset = 0; m_state.vertexBuffers[0].stride = 0; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawIndexedPrimitiveUP( D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT PrimitiveCount, const void* pIndexData, D3DFORMAT IndexDataFormat, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride) { D3D9DeviceLock lock = LockDevice(); if (unlikely(m_state.vertexDecl == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(!PrimitiveCount)) return S_OK; PrepareDraw(PrimitiveType, false, false); uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount); const uint32_t vertexDataSize = GetUPDataSize(MinVertexIndex + NumVertices, VertexStreamZeroStride); const uint32_t vertexBufferSize = GetUPBufferSize(MinVertexIndex + NumVertices, VertexStreamZeroStride); const uint32_t indexSize = IndexDataFormat == D3DFMT_INDEX16 ? 2 : 4; const uint32_t indicesSize = vertexCount * indexSize; const uint32_t upSize = vertexBufferSize + indicesSize; auto upSlice = AllocUPBuffer(upSize); uint8_t* data = reinterpret_cast(upSlice.mapPtr); FillUPVertexBuffer(data, pVertexStreamZeroData, vertexDataSize, vertexBufferSize); std::memcpy(data + vertexBufferSize, pIndexData, indicesSize); EmitCs([this, cVertexSize = vertexBufferSize, cBufferSlice = std::move(upSlice.slice), cPrimType = PrimitiveType, cPrimCount = PrimitiveCount, cStride = VertexStreamZeroStride, cInstanceCount = GetInstanceCount(), cIndexType = DecodeIndexType( static_cast(IndexDataFormat)) ](DxvkContext* ctx) { auto drawInfo = GenerateDrawInfo(cPrimType, cPrimCount, cInstanceCount); ApplyPrimitiveType(ctx, cPrimType); VkDrawIndexedIndirectCommand draw = { }; draw.indexCount = drawInfo.vertexCount; draw.instanceCount = drawInfo.instanceCount; ctx->bindVertexBuffer(0, cBufferSlice.subSlice(0, cVertexSize), cStride); ctx->bindIndexBuffer(cBufferSlice.subSlice(cVertexSize, cBufferSlice.length() - cVertexSize), cIndexType); ctx->drawIndexed(1u, &draw); ctx->bindVertexBuffer(0, DxvkBufferSlice(), 0); ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32); }); m_state.vertexBuffers[0].vertexBuffer = nullptr; m_state.vertexBuffers[0].offset = 0; m_state.vertexBuffers[0].stride = 0; m_state.indices = nullptr; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::ProcessVertices( UINT SrcStartIndex, UINT DestIndex, UINT VertexCount, IDirect3DVertexBuffer9* pDestBuffer, IDirect3DVertexDeclaration9* pVertexDecl, DWORD Flags) { D3D9DeviceLock lock = LockDevice(); if (unlikely(pDestBuffer == nullptr)) return D3DERR_INVALIDCALL; // When vertex shader 3.0 or above is set as the current vertex shader, // the output vertex declaration must be present. if (UseProgrammableVS()) { const auto& programInfo = GetCommonShader(m_state.vertexShader)->GetInfo(); if (unlikely(programInfo.majorVersion() >= 3) && (pVertexDecl == nullptr)) return D3DERR_INVALIDCALL; } if (!SupportsSWVP()) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::err("D3D9DeviceEx::ProcessVertices: SWVP emu unsupported (vertexPipelineStoresAndAtomics)"); return D3D_OK; } if (!VertexCount) return D3D_OK; D3D9CommonBuffer* dst = static_cast(pDestBuffer)->GetCommonBuffer(); D3D9VertexDecl* decl = static_cast (pVertexDecl); bool dynamicSysmemVBOs; uint32_t firstIndex = 0; int32_t baseVertexIndex = 0; UploadPerDrawData( SrcStartIndex, VertexCount, firstIndex, 0, baseVertexIndex, &dynamicSysmemVBOs, nullptr ); PrepareDraw(D3DPT_FORCE_DWORD, !dynamicSysmemVBOs, false); if (decl == nullptr) { DWORD FVF = dst->Desc()->FVF; auto iter = m_fvfTable.find(FVF); if (iter == m_fvfTable.end()) { decl = new D3D9VertexDecl(this, FVF); m_fvfTable.insert(std::make_pair(FVF, decl)); } else decl = iter->second.ptr(); } uint32_t offset = DestIndex * decl->GetSize(0); auto slice = dst->GetBufferSlice(); slice = slice.subSlice(offset, slice.length() - offset); D3D9CompactVertexElements elements; for (const D3DVERTEXELEMENT9& element : decl->GetElements()) { elements.emplace_back(element); } EmitCs([this, cVertexElements = std::move(elements), cVertexCount = VertexCount, cStartIndex = SrcStartIndex, cInstanceCount = GetInstanceCount(), cBufferSlice = slice ](DxvkContext* ctx) mutable { Rc shader = m_swvpEmulator.GetShaderModule(this, std::move(cVertexElements)); auto drawInfo = GenerateDrawInfo(D3DPT_POINTLIST, cVertexCount, cInstanceCount); if (drawInfo.instanceCount != 1) { drawInfo.instanceCount = 1; Logger::warn("D3D9DeviceEx::ProcessVertices: instancing unsupported"); } ApplyPrimitiveType(ctx, D3DPT_POINTLIST); // Unbind the pixel shader, we aren't drawing // to avoid val errors / UB. ctx->bindShader(nullptr); VkDrawIndirectCommand draw = { }; draw.vertexCount = drawInfo.vertexCount; draw.instanceCount = drawInfo.instanceCount; draw.firstVertex = cStartIndex; ctx->bindShader(std::move(shader)); ctx->bindUniformBuffer(VK_SHADER_STAGE_GEOMETRY_BIT, getSWVPBufferSlot(), std::move(cBufferSlice)); ctx->draw(1u, &draw); ctx->bindUniformBuffer(VK_SHADER_STAGE_GEOMETRY_BIT, getSWVPBufferSlot(), DxvkBufferSlice()); ctx->bindShader(nullptr); }); // We unbound the pixel shader before, // let's make sure that gets rebound. m_flags.set(D3D9DeviceFlag::DirtyFFPixelShader); if (m_state.pixelShader != nullptr) { BindShader( GetCommonShader(m_state.pixelShader)); } if (dst->GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_BUFFER) { uint32_t copySize = VertexCount * decl->GetSize(0); EmitCs([ cSrcBuffer = dst->GetBuffer(), cDstBuffer = dst->GetBuffer(), cOffset = offset, cCopySize = copySize ](DxvkContext* ctx) { ctx->copyBuffer(cDstBuffer, cOffset, cSrcBuffer, cOffset, cCopySize); }); } dst->SetNeedsReadback(true); TrackBufferMappingBufferSequenceNumber(dst); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateVertexDeclaration( const D3DVERTEXELEMENT9* pVertexElements, IDirect3DVertexDeclaration9** ppDecl) { InitReturnPtr(ppDecl); if (unlikely(ppDecl == nullptr || pVertexElements == nullptr)) return D3DERR_INVALIDCALL; const D3DVERTEXELEMENT9* counter = pVertexElements; while (counter->Stream != 0xFF) counter++; const uint32_t declCount = uint32_t(counter - pVertexElements); try { const Com decl = new D3D9VertexDecl(this, pVertexElements, declCount); *ppDecl = decl.ref(); return D3D_OK; } catch (const DxvkError & e) { Logger::err(e.message()); return D3DERR_INVALIDCALL; } } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetVertexDeclaration(IDirect3DVertexDeclaration9* pDecl) { D3D9DeviceLock lock = LockDevice(); D3D9VertexDecl* decl = static_cast(pDecl); if (unlikely(ShouldRecord())) return m_recorder->SetVertexDeclaration(decl); if (decl == m_state.vertexDecl.ptr()) return D3D_OK; bool dirtyFFShader = decl == nullptr || m_state.vertexDecl == nullptr; if (!dirtyFFShader) dirtyFFShader |= decl->GetFlags() != m_state.vertexDecl->GetFlags() || decl->GetTexcoordMask() != m_state.vertexDecl->GetTexcoordMask(); if (dirtyFFShader) m_flags.set(D3D9DeviceFlag::DirtyFFVertexShader); m_state.vertexDecl = decl; m_flags.set(D3D9DeviceFlag::DirtyInputLayout); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetVertexDeclaration(IDirect3DVertexDeclaration9** ppDecl) { D3D9DeviceLock lock = LockDevice(); InitReturnPtr(ppDecl); if (ppDecl == nullptr) return D3D_OK; if (m_state.vertexDecl == nullptr) return D3D_OK; *ppDecl = m_state.vertexDecl.ref(); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetFVF(DWORD FVF) { D3D9DeviceLock lock = LockDevice(); if (FVF == 0) return D3D_OK; D3D9VertexDecl* decl = nullptr; auto iter = m_fvfTable.find(FVF); if (iter == m_fvfTable.end()) { decl = new D3D9VertexDecl(this, FVF); m_fvfTable.insert(std::make_pair(FVF, decl)); } else decl = iter->second.ptr(); return this->SetVertexDeclaration(decl); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetFVF(DWORD* pFVF) { D3D9DeviceLock lock = LockDevice(); if (pFVF == nullptr) return D3DERR_INVALIDCALL; *pFVF = m_state.vertexDecl != nullptr ? m_state.vertexDecl->GetFVF() : 0; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateVertexShader( const DWORD* pFunction, IDirect3DVertexShader9** ppShader) { // CreateVertexShader does not init the // return ptr unlike CreatePixelShader if (unlikely(ppShader == nullptr)) return D3DERR_INVALIDCALL; const uint32_t majorVersion = D3DSHADER_VERSION_MAJOR(pFunction[0]); const uint32_t minorVersion = D3DSHADER_VERSION_MINOR(pFunction[0]); // Late fixed-function capable hardware exposed support for VS 1.1 const uint32_t shaderModelVS = m_isD3D8Compatible ? 1u : std::max(1u, m_d3d9Options.shaderModel); if (unlikely(majorVersion > shaderModelVS || (majorVersion == 1 && minorVersion > 1) // Skip checking the SM2 minor version, as it has a 2_x mode apparently || (majorVersion == 3 && minorVersion != 0))) { Logger::err(str::format("D3D9DeviceEx::CreateVertexShader: Unsupported VS version ", majorVersion, ".", minorVersion)); return D3DERR_INVALIDCALL; } DxsoModuleInfo moduleInfo; moduleInfo.options = m_dxsoOptions; D3D9CommonShader module; uint32_t bytecodeLength; if (FAILED(this->CreateShaderModule(&module, &bytecodeLength, VK_SHADER_STAGE_VERTEX_BIT, pFunction, &moduleInfo))) return D3DERR_INVALIDCALL; if (m_isD3D8Compatible && !m_isSWVP) { const uint32_t maxVSConstantIndex = module.GetMaxDefinedConstant(); // D3D8 enforces the value advertised in pCaps->MaxVertexShaderConst for HWVP if (unlikely(maxVSConstantIndex > caps::MaxFloatConstantsVS - 1)) { Logger::err(str::format("D3D9DeviceEx::CreateVertexShader: Invalid constant index ", maxVSConstantIndex)); return D3DERR_INVALIDCALL; } } *ppShader = ref(new D3D9VertexShader(this, &m_shaderAllocator, module, pFunction, bytecodeLength)); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetVertexShader(IDirect3DVertexShader9* pShader) { D3D9DeviceLock lock = LockDevice(); D3D9VertexShader* shader = static_cast(pShader); if (unlikely(ShouldRecord())) return m_recorder->SetVertexShader(shader); if (shader == m_state.vertexShader.ptr()) return D3D_OK; auto* oldShader = GetCommonShader(m_state.vertexShader); auto* newShader = GetCommonShader(shader); bool oldCopies = oldShader && oldShader->GetMeta().needsConstantCopies; bool newCopies = newShader && newShader->GetMeta().needsConstantCopies; m_consts[DxsoProgramTypes::VertexShader].dirty |= oldCopies || newCopies || !oldShader; m_consts[DxsoProgramTypes::VertexShader].meta = newShader ? newShader->GetMeta() : DxsoShaderMetaInfo(); if (newShader && oldShader) { m_consts[DxsoProgramTypes::VertexShader].dirty |= newShader->GetMeta().maxConstIndexF > oldShader->GetMeta().maxConstIndexF || newShader->GetMeta().maxConstIndexI > oldShader->GetMeta().maxConstIndexI || newShader->GetMeta().maxConstIndexB > oldShader->GetMeta().maxConstIndexB; } m_state.vertexShader = shader; if (shader != nullptr) { m_flags.clr(D3D9DeviceFlag::DirtyProgVertexShader); m_flags.set(D3D9DeviceFlag::DirtyFFVertexShader); BindShader(GetCommonShader(shader)); m_vsShaderMasks = newShader->GetShaderMask(); UpdateTextureTypeMismatchesForShader(newShader, m_vsShaderMasks.samplerMask, FirstVSSamplerSlot); } else { m_vsShaderMasks = D3D9ShaderMasks(); // Fixed function vertex shaders don't support sampling textures. m_dirtyTextures |= m_vsShaderMasks.samplerMask & m_mismatchingTextureTypes; m_mismatchingTextureTypes &= ~m_vsShaderMasks.samplerMask; } m_flags.set(D3D9DeviceFlag::DirtyInputLayout); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetVertexShader(IDirect3DVertexShader9** ppShader) { D3D9DeviceLock lock = LockDevice(); InitReturnPtr(ppShader); if (unlikely(ppShader == nullptr)) return D3DERR_INVALIDCALL; *ppShader = m_state.vertexShader.ref(); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetVertexShaderConstantF( UINT StartRegister, const float* pConstantData, UINT Vector4fCount) { D3D9DeviceLock lock = LockDevice(); return SetShaderConstants< DxsoProgramTypes::VertexShader, D3D9ConstantType::Float>( StartRegister, pConstantData, Vector4fCount); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetVertexShaderConstantF( UINT StartRegister, float* pConstantData, UINT Vector4fCount) { D3D9DeviceLock lock = LockDevice(); return GetShaderConstants< DxsoProgramTypes::VertexShader, D3D9ConstantType::Float>( StartRegister, pConstantData, Vector4fCount); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetVertexShaderConstantI( UINT StartRegister, const int* pConstantData, UINT Vector4iCount) { D3D9DeviceLock lock = LockDevice(); return SetShaderConstants< DxsoProgramTypes::VertexShader, D3D9ConstantType::Int>( StartRegister, pConstantData, Vector4iCount); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetVertexShaderConstantI( UINT StartRegister, int* pConstantData, UINT Vector4iCount) { D3D9DeviceLock lock = LockDevice(); return GetShaderConstants< DxsoProgramTypes::VertexShader, D3D9ConstantType::Int>( StartRegister, pConstantData, Vector4iCount); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetVertexShaderConstantB( UINT StartRegister, const BOOL* pConstantData, UINT BoolCount) { D3D9DeviceLock lock = LockDevice(); return SetShaderConstants< DxsoProgramTypes::VertexShader, D3D9ConstantType::Bool>( StartRegister, pConstantData, BoolCount); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetVertexShaderConstantB( UINT StartRegister, BOOL* pConstantData, UINT BoolCount) { D3D9DeviceLock lock = LockDevice(); return GetShaderConstants< DxsoProgramTypes::VertexShader, D3D9ConstantType::Bool>( StartRegister, pConstantData, BoolCount); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetStreamSource( UINT StreamNumber, IDirect3DVertexBuffer9* pStreamData, UINT OffsetInBytes, UINT Stride) { D3D9DeviceLock lock = LockDevice(); if (unlikely(StreamNumber >= caps::MaxStreams)) return D3DERR_INVALIDCALL; D3D9VertexBuffer* buffer = static_cast(pStreamData); if (unlikely(ShouldRecord())) return m_recorder->SetStreamSource( StreamNumber, buffer, OffsetInBytes, Stride); auto& vbo = m_state.vertexBuffers[StreamNumber]; bool needsUpdate = vbo.vertexBuffer != buffer; if (needsUpdate) vbo.vertexBuffer = buffer; const uint32_t bit = 1u << StreamNumber; m_activeVertexBuffers &= ~bit; m_activeVertexBuffersToUploadPerDraw &= ~bit; m_activeVertexBuffersToUpload &= ~bit; if (buffer != nullptr) { needsUpdate |= vbo.offset != OffsetInBytes || vbo.stride != Stride; vbo.offset = OffsetInBytes; vbo.stride = Stride; const D3D9CommonBuffer* commonBuffer = GetCommonBuffer(buffer); m_activeVertexBuffers |= bit; if (commonBuffer->DoPerDrawUpload() || CanOnlySWVP()) m_activeVertexBuffersToUploadPerDraw |= bit; if (commonBuffer->NeedsUpload()) { m_activeVertexBuffersToUpload |= bit; } } else { // D3D9 doesn't actually unbind any vertex buffer when passing null. // Operation Flashpoint: Red River relies on this behavior. needsUpdate = false; vbo.offset = 0; } if (needsUpdate) BindVertexBuffer(StreamNumber, buffer, OffsetInBytes, Stride); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetStreamSource( UINT StreamNumber, IDirect3DVertexBuffer9** ppStreamData, UINT* pOffsetInBytes, UINT* pStride) { D3D9DeviceLock lock = LockDevice(); InitReturnPtr(ppStreamData); if (likely(pOffsetInBytes != nullptr)) *pOffsetInBytes = 0; if (likely(pStride != nullptr)) *pStride = 0; if (unlikely(ppStreamData == nullptr || pOffsetInBytes == nullptr || pStride == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(StreamNumber >= caps::MaxStreams)) return D3DERR_INVALIDCALL; const auto& vbo = m_state.vertexBuffers[StreamNumber]; *ppStreamData = vbo.vertexBuffer.ref(); *pOffsetInBytes = vbo.offset; *pStride = vbo.stride; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetStreamSourceFreq(UINT StreamNumber, UINT Setting) { D3D9DeviceLock lock = LockDevice(); if (unlikely(StreamNumber >= caps::MaxStreams)) return D3DERR_INVALIDCALL; const bool indexed = Setting & D3DSTREAMSOURCE_INDEXEDDATA; const bool instanced = Setting & D3DSTREAMSOURCE_INSTANCEDATA; if (unlikely(StreamNumber == 0 && instanced)) return D3DERR_INVALIDCALL; if (unlikely(instanced && indexed)) return D3DERR_INVALIDCALL; if (unlikely(Setting == 0)) return D3DERR_INVALIDCALL; if (unlikely(ShouldRecord())) return m_recorder->SetStreamSourceFreq(StreamNumber, Setting); if (m_state.streamFreq[StreamNumber] == Setting) return D3D_OK; m_state.streamFreq[StreamNumber] = Setting; if (instanced) m_instancedData |= 1u << StreamNumber; else m_instancedData &= ~(1u << StreamNumber); m_flags.set(D3D9DeviceFlag::DirtyInputLayout); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetStreamSourceFreq(UINT StreamNumber, UINT* pSetting) { D3D9DeviceLock lock = LockDevice(); if (unlikely(StreamNumber >= caps::MaxStreams)) return D3DERR_INVALIDCALL; if (unlikely(pSetting == nullptr)) return D3DERR_INVALIDCALL; *pSetting = m_state.streamFreq[StreamNumber]; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetIndices(IDirect3DIndexBuffer9* pIndexData) { D3D9DeviceLock lock = LockDevice(); D3D9IndexBuffer* buffer = static_cast(pIndexData); if (unlikely(ShouldRecord())) return m_recorder->SetIndices(buffer); if (buffer == m_state.indices.ptr()) return D3D_OK; m_state.indices = buffer; // Don't unbind the buffer if the game sets a nullptr here. // Operation Flashpoint Red River breaks if we do that. // EndScene will clean it up if necessary. if (buffer != nullptr) BindIndices(); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetIndices(IDirect3DIndexBuffer9** ppIndexData) { D3D9DeviceLock lock = LockDevice(); InitReturnPtr(ppIndexData); if (unlikely(ppIndexData == nullptr)) return D3DERR_INVALIDCALL; *ppIndexData = m_state.indices.ref(); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreatePixelShader( const DWORD* pFunction, IDirect3DPixelShader9** ppShader) { InitReturnPtr(ppShader); if (unlikely(ppShader == nullptr)) return D3DERR_INVALIDCALL; const uint32_t majorVersion = D3DSHADER_VERSION_MAJOR(pFunction[0]); const uint32_t minorVersion = D3DSHADER_VERSION_MINOR(pFunction[0]); const uint32_t shaderModelPS = m_isD3D8Compatible ? std::min(1u, m_d3d9Options.shaderModel) : m_d3d9Options.shaderModel; if (unlikely(majorVersion > shaderModelPS || (majorVersion == 1 && minorVersion > 4) // Skip checking the SM2 minor version, as it has a 2_x mode apparently || (majorVersion == 3 && minorVersion != 0))) { Logger::err(str::format("D3D9DeviceEx::CreatePixelShader: Unsupported PS version ", majorVersion, ".", minorVersion)); return D3DERR_INVALIDCALL; } DxsoModuleInfo moduleInfo; moduleInfo.options = m_dxsoOptions; D3D9CommonShader module; uint32_t bytecodeLength; if (FAILED(this->CreateShaderModule(&module, &bytecodeLength, VK_SHADER_STAGE_FRAGMENT_BIT, pFunction, &moduleInfo))) return D3DERR_INVALIDCALL; *ppShader = ref(new D3D9PixelShader(this, &m_shaderAllocator, module, pFunction, bytecodeLength)); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetPixelShader(IDirect3DPixelShader9* pShader) { D3D9DeviceLock lock = LockDevice(); D3D9PixelShader* shader = static_cast(pShader); if (unlikely(ShouldRecord())) return m_recorder->SetPixelShader(shader); if (shader == m_state.pixelShader.ptr()) return D3D_OK; auto* oldShader = GetCommonShader(m_state.pixelShader); auto* newShader = GetCommonShader(shader); bool oldCopies = oldShader && oldShader->GetMeta().needsConstantCopies; bool newCopies = newShader && newShader->GetMeta().needsConstantCopies; m_consts[DxsoProgramTypes::PixelShader].dirty |= oldCopies || newCopies || !oldShader; m_consts[DxsoProgramTypes::PixelShader].meta = newShader ? newShader->GetMeta() : DxsoShaderMetaInfo(); if (newShader && oldShader) { m_consts[DxsoProgramTypes::PixelShader].dirty |= newShader->GetMeta().maxConstIndexF > oldShader->GetMeta().maxConstIndexF || newShader->GetMeta().maxConstIndexI > oldShader->GetMeta().maxConstIndexI || newShader->GetMeta().maxConstIndexB > oldShader->GetMeta().maxConstIndexB; } m_state.pixelShader = shader; D3D9ShaderMasks newShaderMasks; if (shader != nullptr) { m_flags.set(D3D9DeviceFlag::DirtyFFPixelShader); BindShader(newShader); newShaderMasks = newShader->GetShaderMask(); UpdateTextureTypeMismatchesForShader(newShader, newShaderMasks.samplerMask, 0); } else { // TODO: What fixed function textures are in use? // Currently we are making all 8 of them as in use here. // The RT output is always 0 for fixed function. newShaderMasks = FixedFunctionMask; // Fixed function always uses spec constants to decide the texture type. m_dirtyTextures |= newShaderMasks.samplerMask & m_mismatchingTextureTypes; m_mismatchingTextureTypes &= ~newShaderMasks.samplerMask; } // If we have any RTs we would have bound to the the FB // not in the new shader mask, mark the framebuffer as dirty // so we unbind them. uint32_t boundMask = 0u; uint32_t anyColorWriteMask = 0u; for (uint32_t i = 0u; i < m_state.renderTargets.size(); i++) { boundMask |= HasRenderTargetBound(i) << i; anyColorWriteMask |= (m_state.renderStates[ColorWriteIndex(i)] != 0) << i; } uint32_t oldUseMask = boundMask & anyColorWriteMask & m_psShaderMasks.rtMask; uint32_t newUseMask = boundMask & anyColorWriteMask & newShaderMasks.rtMask; if (oldUseMask != newUseMask) m_flags.set(D3D9DeviceFlag::DirtyFramebuffer); if (m_psShaderMasks.samplerMask != newShaderMasks.samplerMask || m_psShaderMasks.rtMask != newShaderMasks.rtMask) { m_psShaderMasks = newShaderMasks; UpdateActiveHazardsRT(std::numeric_limits::max()); UpdateActiveHazardsDS(std::numeric_limits::max()); } return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetPixelShader(IDirect3DPixelShader9** ppShader) { D3D9DeviceLock lock = LockDevice(); InitReturnPtr(ppShader); if (unlikely(ppShader == nullptr)) return D3DERR_INVALIDCALL; *ppShader = m_state.pixelShader.ref(); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetPixelShaderConstantF( UINT StartRegister, const float* pConstantData, UINT Vector4fCount) { D3D9DeviceLock lock = LockDevice(); return SetShaderConstants < DxsoProgramTypes::PixelShader, D3D9ConstantType::Float>( StartRegister, pConstantData, Vector4fCount); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetPixelShaderConstantF( UINT StartRegister, float* pConstantData, UINT Vector4fCount) { D3D9DeviceLock lock = LockDevice(); return GetShaderConstants< DxsoProgramTypes::PixelShader, D3D9ConstantType::Float>( StartRegister, pConstantData, Vector4fCount); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetPixelShaderConstantI( UINT StartRegister, const int* pConstantData, UINT Vector4iCount) { D3D9DeviceLock lock = LockDevice(); return SetShaderConstants< DxsoProgramTypes::PixelShader, D3D9ConstantType::Int>( StartRegister, pConstantData, Vector4iCount); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetPixelShaderConstantI( UINT StartRegister, int* pConstantData, UINT Vector4iCount) { D3D9DeviceLock lock = LockDevice(); return GetShaderConstants< DxsoProgramTypes::PixelShader, D3D9ConstantType::Int>( StartRegister, pConstantData, Vector4iCount); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetPixelShaderConstantB( UINT StartRegister, const BOOL* pConstantData, UINT BoolCount) { D3D9DeviceLock lock = LockDevice(); return SetShaderConstants< DxsoProgramTypes::PixelShader, D3D9ConstantType::Bool>( StartRegister, pConstantData, BoolCount); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetPixelShaderConstantB( UINT StartRegister, BOOL* pConstantData, UINT BoolCount) { D3D9DeviceLock lock = LockDevice(); return GetShaderConstants< DxsoProgramTypes::PixelShader, D3D9ConstantType::Bool>( StartRegister, pConstantData, BoolCount); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawRectPatch( UINT Handle, const float* pNumSegs, const D3DRECTPATCH_INFO* pRectPatchInfo) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D9DeviceEx::DrawRectPatch: Stub"); return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawTriPatch( UINT Handle, const float* pNumSegs, const D3DTRIPATCH_INFO* pTriPatchInfo) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D9DeviceEx::DrawTriPatch: Stub"); return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DeletePatch(UINT Handle) { static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("D3D9DeviceEx::DeletePatch: Stub"); return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateQuery(D3DQUERYTYPE Type, IDirect3DQuery9** ppQuery) { HRESULT hr = D3D9Query::QuerySupported(this, Type); if (ppQuery == nullptr || hr != D3D_OK) return hr; try { *ppQuery = ref(new D3D9Query(this, Type)); return D3D_OK; } catch (const DxvkError & e) { Logger::err(e.message()); return D3DERR_NOTAVAILABLE; } } // Ex Methods HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetConvolutionMonoKernel( UINT width, UINT height, float* rows, float* columns) { // We don't advertise support for this. return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::ComposeRects( IDirect3DSurface9* pSrc, IDirect3DSurface9* pDst, IDirect3DVertexBuffer9* pSrcRectDescs, UINT NumRects, IDirect3DVertexBuffer9* pDstRectDescs, D3DCOMPOSERECTSOP Operation, int Xoffset, int Yoffset) { Logger::warn("D3D9DeviceEx::ComposeRects: Stub"); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetGPUThreadPriority(INT* pPriority) { Logger::warn("D3D9DeviceEx::GetGPUThreadPriority: Stub"); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetGPUThreadPriority(INT Priority) { Logger::warn("D3D9DeviceEx::SetGPUThreadPriority: Stub"); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::WaitForVBlank(UINT iSwapChain) { if (unlikely(iSwapChain != 0)) return D3DERR_INVALIDCALL; return m_implicitSwapchain->WaitForVBlank(); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CheckResourceResidency(IDirect3DResource9** pResourceArray, UINT32 NumResources) { Logger::warn("D3D9DeviceEx::CheckResourceResidency: Stub"); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetMaximumFrameLatency(UINT MaxLatency) { D3D9DeviceLock lock = LockDevice(); if (MaxLatency == 0) MaxLatency = DefaultFrameLatency; if (MaxLatency > MaxFrameLatency) MaxLatency = MaxFrameLatency; m_frameLatency = MaxLatency; m_implicitSwapchain->SyncFrameLatency(); return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetMaximumFrameLatency(UINT* pMaxLatency) { D3D9DeviceLock lock = LockDevice(); if (unlikely(pMaxLatency == nullptr)) return D3DERR_INVALIDCALL; *pMaxLatency = m_frameLatency; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CheckDeviceState(HWND hDestinationWindow) { return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::PresentEx( const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion, DWORD dwFlags) { if (m_cursor.IsSoftwareCursor()) { D3D9_SOFTWARE_CURSOR* pSoftwareCursor = m_cursor.GetSoftwareCursor(); UINT cursorWidth = pSoftwareCursor->DrawCursor ? pSoftwareCursor->Width : 0; UINT cursorHeight = pSoftwareCursor->DrawCursor ? pSoftwareCursor->Height : 0; m_implicitSwapchain->SetCursorPosition(pSoftwareCursor->X - pSoftwareCursor->XHotSpot, pSoftwareCursor->Y - pSoftwareCursor->YHotSpot, cursorWidth, cursorHeight); // Once a hardware cursor has been set or the device has been reset, // we need to ensure that we render a 0-sized rectangle first, and // only then fully clear the software cursor. if (unlikely(pSoftwareCursor->ResetCursor)) { pSoftwareCursor->Width = 0; pSoftwareCursor->Height = 0; pSoftwareCursor->XHotSpot = 0; pSoftwareCursor->YHotSpot = 0; pSoftwareCursor->ResetCursor = false; } } return m_implicitSwapchain->Present( pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateRenderTargetEx( UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage) { InitReturnPtr(ppSurface); if (unlikely(ppSurface == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(MultiSample > D3DMULTISAMPLE_16_SAMPLES)) return D3DERR_INVALIDCALL; uint32_t sampleCount = std::max(MultiSample, 1u); // Check if this is a power of two... if (sampleCount & (sampleCount - 1)) return D3DERR_NOTAVAILABLE; D3D9_COMMON_TEXTURE_DESC desc; desc.Width = Width; desc.Height = Height; desc.Depth = 1; desc.ArraySize = 1; desc.MipLevels = 1; desc.Usage = Usage | D3DUSAGE_RENDERTARGET; desc.Format = EnumerateFormat(Format); desc.Pool = D3DPOOL_DEFAULT; desc.Discard = FALSE; desc.MultiSample = MultiSample; desc.MultisampleQuality = MultisampleQuality; desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = TRUE; desc.IsLockable = Lockable; if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_SURFACE, &desc))) return D3DERR_INVALIDCALL; try { const Com surface = new D3D9Surface(this, &desc, IsExtended(), nullptr, pSharedHandle); m_initializer->InitTexture(surface->GetCommonTexture()); *ppSurface = surface.ref(); m_losableResourceCounter++; return D3D_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return D3DERR_OUTOFVIDEOMEMORY; } } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateOffscreenPlainSurfaceEx( UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage) { InitReturnPtr(ppSurface); if (unlikely(ppSurface == nullptr)) return D3DERR_INVALIDCALL; D3D9_COMMON_TEXTURE_DESC desc; desc.Width = Width; desc.Height = Height; desc.Depth = 1; desc.ArraySize = 1; desc.MipLevels = 1; desc.Usage = Usage; desc.Format = EnumerateFormat(Format); desc.Pool = Pool; desc.Discard = FALSE; desc.MultiSample = D3DMULTISAMPLE_NONE; desc.MultisampleQuality = 0; desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = TRUE; // Docs: Off-screen plain surfaces are always lockable, regardless of their pool types. desc.IsLockable = TRUE; if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_SURFACE, &desc))) return D3DERR_INVALIDCALL; if (pSharedHandle != nullptr && Pool != D3DPOOL_DEFAULT) return D3DERR_INVALIDCALL; try { const Com surface = new D3D9Surface(this, &desc, IsExtended(), nullptr, pSharedHandle); m_initializer->InitTexture(surface->GetCommonTexture()); *ppSurface = surface.ref(); if (desc.Pool == D3DPOOL_DEFAULT) m_losableResourceCounter++; return D3D_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return D3DERR_OUTOFVIDEOMEMORY; } } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateDepthStencilSurfaceEx( UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage) { InitReturnPtr(ppSurface); if (unlikely(ppSurface == nullptr)) return D3DERR_INVALIDCALL; D3D9_COMMON_TEXTURE_DESC desc; desc.Width = Width; desc.Height = Height; desc.Depth = 1; desc.ArraySize = 1; desc.MipLevels = 1; desc.Usage = Usage | D3DUSAGE_DEPTHSTENCIL; desc.Format = EnumerateFormat(Format); desc.Pool = D3DPOOL_DEFAULT; desc.Discard = Discard; desc.MultiSample = MultiSample; desc.MultisampleQuality = MultisampleQuality; desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = TRUE; desc.IsLockable = IsLockableDepthStencilFormat(desc.Format); if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_SURFACE, &desc))) return D3DERR_INVALIDCALL; try { const Com surface = new D3D9Surface(this, &desc, IsExtended(), nullptr, pSharedHandle); m_initializer->InitTexture(surface->GetCommonTexture()); *ppSurface = surface.ref(); m_losableResourceCounter++; return D3D_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return D3DERR_OUTOFVIDEOMEMORY; } } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::ResetEx( D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode) { D3D9DeviceLock lock = LockDevice(); HRESULT hr = ResetSwapChain(pPresentationParameters, pFullscreenDisplayMode); if (FAILED(hr)) return hr; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetDisplayModeEx( UINT iSwapChain, D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation) { if (unlikely(iSwapChain != 0)) return D3DERR_INVALIDCALL; return m_implicitSwapchain->GetDisplayModeEx(pMode, pRotation); } HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateAdditionalSwapChainEx( D3DPRESENT_PARAMETERS* pPresentationParameters, const D3DDISPLAYMODEEX* pFullscreenDisplayMode, IDirect3DSwapChain9** ppSwapChain) { D3D9DeviceLock lock = LockDevice(); InitReturnPtr(ppSwapChain); if (ppSwapChain == nullptr || pPresentationParameters == nullptr) return D3DERR_INVALIDCALL; // Additional fullscreen swapchains are forbidden. if (!pPresentationParameters->Windowed) return D3DERR_INVALIDCALL; // We can't make another swapchain if we are fullscreen. if (!m_implicitSwapchain->GetPresentParams()->Windowed) return D3DERR_INVALIDCALL; if (unlikely(IsDeviceLost())) { return D3DERR_DEVICELOST; } m_implicitSwapchain->Invalidate(pPresentationParameters->hDeviceWindow); try { auto* swapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode, false); *ppSwapChain = ref(swapchain); m_losableResourceCounter++; } catch (const DxvkError & e) { Logger::err(e.message()); return D3DERR_NOTAVAILABLE; } return D3D_OK; } HRESULT D3D9DeviceEx::SetStateSamplerState( DWORD StateSampler, D3DSAMPLERSTATETYPE Type, DWORD Value) { D3D9DeviceLock lock = LockDevice(); if (unlikely(ShouldRecord())) return m_recorder->SetStateSamplerState(StateSampler, Type, Value); auto& state = m_state.samplerStates; if (state[StateSampler][Type] == Value) return D3D_OK; state[StateSampler][Type] = Value; const uint32_t samplerBit = 1u << StateSampler; if (Type == D3DSAMP_ADDRESSU || Type == D3DSAMP_ADDRESSV || Type == D3DSAMP_ADDRESSW || Type == D3DSAMP_MAGFILTER || Type == D3DSAMP_MINFILTER || Type == D3DSAMP_MIPFILTER || Type == D3DSAMP_MAXANISOTROPY || Type == D3DSAMP_MIPMAPLODBIAS || Type == D3DSAMP_MAXMIPLEVEL || Type == D3DSAMP_BORDERCOLOR) m_dirtySamplerStates |= samplerBit; else if (Type == D3DSAMP_SRGBTEXTURE && (m_activeTextures & samplerBit)) m_dirtyTextures |= samplerBit; constexpr DWORD Fetch4Enabled = MAKEFOURCC('G', 'E', 'T', '4'); constexpr DWORD Fetch4Disabled = MAKEFOURCC('G', 'E', 'T', '1'); if (unlikely(Type == D3DSAMP_MIPMAPLODBIAS)) { if (unlikely(Value == Fetch4Enabled)) m_fetch4Enabled |= samplerBit; else if (unlikely(Value == Fetch4Disabled)) m_fetch4Enabled &= ~samplerBit; UpdateActiveFetch4(StateSampler); } if (unlikely(Type == D3DSAMP_MAGFILTER && (m_fetch4Enabled & samplerBit))) UpdateActiveFetch4(StateSampler); return D3D_OK; } HRESULT D3D9DeviceEx::SetStateTexture(DWORD StateSampler, IDirect3DBaseTexture9* pTexture) { D3D9DeviceLock lock = LockDevice(); if (unlikely(ShouldRecord())) return m_recorder->SetStateTexture(StateSampler, pTexture); if (m_state.textures[StateSampler] == pTexture) return D3D_OK; auto oldTexture = GetCommonTexture(m_state.textures[StateSampler]); auto newTexture = GetCommonTexture(pTexture); // We need to check our ops and disable respective stages. // Given we have transition from a null resource to // a valid resource or vice versa. const bool isPSSampler = StateSampler < caps::MaxTexturesPS; if (isPSSampler) { const uint32_t textureType = newTexture != nullptr ? uint32_t(newTexture->GetType() - D3DRTYPE_TEXTURE) : 0; // There are 3 texture types, so we need 2 bits. const uint32_t offset = StateSampler * 2; const uint32_t textureBitMask = 0b11u << offset; const uint32_t textureBits = textureType << offset; // In fixed function shaders and SM < 2 we put the type mask // into a spec constant to select the used sampler type. m_textureTypes &= ~textureBitMask; m_textureTypes |= textureBits; // If we either bind a new texture or unbind the old one, // we need to update the fixed function shader // because we generate a different shader based on whether each texture is bound. if (newTexture == nullptr || oldTexture == nullptr) m_flags.set(D3D9DeviceFlag::DirtyFFPixelShader); } DWORD oldUsage = oldTexture != nullptr ? oldTexture->Desc()->Usage : 0; DWORD newUsage = newTexture != nullptr ? newTexture->Desc()->Usage : 0; DWORD combinedUsage = oldUsage | newUsage; TextureChangePrivate(m_state.textures[StateSampler], pTexture); m_dirtyTextures |= 1u << StateSampler; UpdateTextureBitmasks(StateSampler, combinedUsage); return D3D_OK; } HRESULT D3D9DeviceEx::SetStateTransform(uint32_t idx, const D3DMATRIX* pMatrix) { D3D9DeviceLock lock = LockDevice(); if (unlikely(ShouldRecord())) return m_recorder->SetStateTransform(idx, pMatrix); m_state.transforms[idx] = ConvertMatrix(pMatrix); m_flags.set(D3D9DeviceFlag::DirtyFFVertexData); if (idx == GetTransformIndex(D3DTS_VIEW) || idx >= GetTransformIndex(D3DTS_WORLD)) m_flags.set(D3D9DeviceFlag::DirtyFFVertexBlend); return D3D_OK; } HRESULT D3D9DeviceEx::SetStateTextureStageState( DWORD Stage, D3D9TextureStageStateTypes Type, DWORD Value) { // Clamp values instead of checking and returning INVALID_CALL // Matches tests + Dawn of Magic 2 relies on it. Stage = std::min(Stage, DWORD(caps::TextureStageCount - 1)); Type = std::min(Type, D3D9TextureStageStateTypes(DXVK_TSS_COUNT - 1)); D3D9DeviceLock lock = LockDevice(); if (unlikely(ShouldRecord())) return m_recorder->SetStateTextureStageState(Stage, Type, Value); if (likely(m_state.textureStages[Stage][Type] != Value)) { m_state.textureStages[Stage][Type] = Value; switch (Type) { case DXVK_TSS_COLOROP: case DXVK_TSS_COLORARG0: case DXVK_TSS_COLORARG1: case DXVK_TSS_COLORARG2: case DXVK_TSS_ALPHAOP: case DXVK_TSS_ALPHAARG0: case DXVK_TSS_ALPHAARG1: case DXVK_TSS_ALPHAARG2: case DXVK_TSS_RESULTARG: m_flags.set(D3D9DeviceFlag::DirtyFFPixelShader); break; case DXVK_TSS_TEXCOORDINDEX: m_flags.set(D3D9DeviceFlag::DirtyFFVertexShader); break; case DXVK_TSS_TEXTURETRANSFORMFLAGS: m_projectionBitfield &= ~(1 << Stage); if (Value & D3DTTFF_PROJECTED) m_projectionBitfield |= 1 << Stage; m_flags.set(D3D9DeviceFlag::DirtyFFVertexShader); m_flags.set(D3D9DeviceFlag::DirtyFFPixelShader); break; case DXVK_TSS_BUMPENVMAT00: case DXVK_TSS_BUMPENVMAT01: case DXVK_TSS_BUMPENVMAT10: case DXVK_TSS_BUMPENVMAT11: case DXVK_TSS_BUMPENVLSCALE: case DXVK_TSS_BUMPENVLOFFSET: case DXVK_TSS_CONSTANT: m_flags.set(D3D9DeviceFlag::DirtySharedPixelShaderData); break; default: break; } } return D3D_OK; } bool D3D9DeviceEx::IsExtended() { return m_parent->IsExtended(); } bool D3D9DeviceEx::SupportsSWVP() { return m_dxvkDevice->features().core.features.vertexPipelineStoresAndAtomics && m_dxvkDevice->features().vk12.shaderInt8; } HWND D3D9DeviceEx::GetWindow() { return m_window; } DxvkDeviceFeatures D3D9DeviceEx::GetDeviceFeatures(const Rc& adapter) { DxvkDeviceFeatures supported = adapter->features(); DxvkDeviceFeatures enabled = {}; // Geometry shaders are used for some meta ops enabled.core.features.geometryShader = VK_TRUE; enabled.core.features.robustBufferAccess = VK_TRUE; enabled.vk12.samplerMirrorClampToEdge = VK_TRUE; enabled.vk13.shaderDemoteToHelperInvocation = VK_TRUE; enabled.extMemoryPriority.memoryPriority = supported.extMemoryPriority.memoryPriority; enabled.extVertexAttributeDivisor.vertexAttributeInstanceRateDivisor = supported.extVertexAttributeDivisor.vertexAttributeInstanceRateDivisor; enabled.extVertexAttributeDivisor.vertexAttributeInstanceRateZeroDivisor = supported.extVertexAttributeDivisor.vertexAttributeInstanceRateZeroDivisor; // ProcessVertices enabled.core.features.vertexPipelineStoresAndAtomics = supported.core.features.vertexPipelineStoresAndAtomics; enabled.vk12.shaderInt8 = supported.vk12.shaderInt8; // DXVK Meta enabled.core.features.imageCubeArray = VK_TRUE; // SM1 level hardware enabled.core.features.depthClamp = VK_TRUE; enabled.core.features.depthBiasClamp = VK_TRUE; enabled.core.features.fillModeNonSolid = VK_TRUE; enabled.core.features.pipelineStatisticsQuery = supported.core.features.pipelineStatisticsQuery; enabled.core.features.sampleRateShading = VK_TRUE; enabled.core.features.samplerAnisotropy = supported.core.features.samplerAnisotropy; enabled.core.features.shaderClipDistance = VK_TRUE; enabled.core.features.shaderCullDistance = VK_TRUE; // Ensure we support real BC formats and unofficial vendor ones. enabled.core.features.textureCompressionBC = VK_TRUE; // SM2 level hardware enabled.core.features.occlusionQueryPrecise = VK_TRUE; // SM3 level hardware enabled.core.features.multiViewport = VK_TRUE; enabled.core.features.independentBlend = VK_TRUE; // D3D10 level hardware supports this in D3D9 native. enabled.core.features.fullDrawIndexUint32 = VK_TRUE; // Enable depth bounds test if we support it. enabled.core.features.depthBounds = supported.core.features.depthBounds; if (supported.extCustomBorderColor.customBorderColorWithoutFormat) { enabled.extCustomBorderColor.customBorderColors = VK_TRUE; enabled.extCustomBorderColor.customBorderColorWithoutFormat = VK_TRUE; } if (supported.extAttachmentFeedbackLoopLayout.attachmentFeedbackLoopLayout) enabled.extAttachmentFeedbackLoopLayout.attachmentFeedbackLoopLayout = VK_TRUE; enabled.extNonSeamlessCubeMap.nonSeamlessCubeMap = supported.extNonSeamlessCubeMap.nonSeamlessCubeMap; enabled.extDepthBiasControl.depthBiasControl = supported.extDepthBiasControl.depthBiasControl; enabled.extDepthBiasControl.depthBiasExact = supported.extDepthBiasControl.depthBiasExact; if (supported.extDepthBiasControl.floatRepresentation) enabled.extDepthBiasControl.floatRepresentation = VK_TRUE; else if (supported.extDepthBiasControl.leastRepresentableValueForceUnormRepresentation) enabled.extDepthBiasControl.leastRepresentableValueForceUnormRepresentation = VK_TRUE; return enabled; } void D3D9DeviceEx::DetermineConstantLayouts(bool canSWVP) { D3D9ConstantSets& vsConstSet = m_consts[DxsoProgramType::VertexShader]; vsConstSet.layout.floatCount = canSWVP ? caps::MaxFloatConstantsSoftware : caps::MaxFloatConstantsVS; vsConstSet.layout.intCount = canSWVP ? caps::MaxOtherConstantsSoftware : caps::MaxOtherConstants; vsConstSet.layout.boolCount = canSWVP ? caps::MaxOtherConstantsSoftware : caps::MaxOtherConstants; vsConstSet.layout.bitmaskCount = align(vsConstSet.layout.boolCount, 32) / 32; D3D9ConstantSets& psConstSet = m_consts[DxsoProgramType::PixelShader]; psConstSet.layout.floatCount = caps::MaxFloatConstantsPS; psConstSet.layout.intCount = caps::MaxOtherConstants; psConstSet.layout.boolCount = caps::MaxOtherConstants; psConstSet.layout.bitmaskCount = align(psConstSet.layout.boolCount, 32) / 32; } D3D9BufferSlice D3D9DeviceEx::AllocUPBuffer(VkDeviceSize size) { constexpr VkDeviceSize UPBufferSize = 1 << 20; if (unlikely(m_upBuffer == nullptr || size > UPBufferSize)) { VkMemoryPropertyFlags memoryFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; DxvkBufferCreateInfo info; info.size = std::max(UPBufferSize, size); info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT; info.access = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDEX_READ_BIT; info.stages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; info.debugName = "UP buffer"; Rc buffer = m_dxvkDevice->createBuffer(info, memoryFlags); void* mapPtr = buffer->mapPtr(0); if (size <= UPBufferSize) { m_upBuffer = std::move(buffer); m_upBufferMapPtr = mapPtr; } else { // Temporary buffer D3D9BufferSlice result; result.slice = DxvkBufferSlice(std::move(buffer), 0, size); result.mapPtr = mapPtr; return result; } } VkDeviceSize alignedSize = align(size, CACHE_LINE_SIZE); if (unlikely(m_upBufferOffset + alignedSize > UPBufferSize)) { auto slice = m_upBuffer->allocateStorage(); m_upBufferOffset = 0; m_upBufferMapPtr = slice->mapPtr(); EmitCs([ cBuffer = m_upBuffer, cSlice = std::move(slice) ] (DxvkContext* ctx) mutable { ctx->invalidateBuffer(cBuffer, std::move(cSlice)); }); } D3D9BufferSlice result; result.slice = DxvkBufferSlice(m_upBuffer, m_upBufferOffset, size); result.mapPtr = reinterpret_cast(m_upBufferMapPtr) + m_upBufferOffset; m_upBufferOffset += alignedSize; return result; } D3D9BufferSlice D3D9DeviceEx::AllocStagingBuffer(VkDeviceSize size) { D3D9BufferSlice result; result.slice = m_stagingBuffer.alloc(size); result.mapPtr = result.slice.mapPtr(0); return result; } void D3D9DeviceEx::WaitStagingBuffer() { // Treshold for staging memory in flight. Since the staging buffer granularity // is somewhat coars, it is possible for one additional allocation to be in use, // but otherwise this is a hard upper bound. constexpr VkDeviceSize MaxStagingMemoryInFlight = env::is32BitHostPlatform() ? StagingBufferSize * 4 : StagingBufferSize * 16; // Threshold at which to submit eagerly. This is useful to ensure // that staging buffer memory gets recycled relatively soon. constexpr VkDeviceSize MaxStagingMemoryPerSubmission = MaxStagingMemoryInFlight / 3u; VkDeviceSize stagingBufferAllocated = m_stagingBuffer.getStatistics().allocatedTotal; if (stagingBufferAllocated > m_stagingMemorySignaled + MaxStagingMemoryPerSubmission) { // Perform submission. If the amount of staging memory allocated since the // last submission exceeds the hard limit, we need to submit to guarantee // forward progress. Ideally, this should not happen very often. GpuFlushType flushType = stagingBufferAllocated <= m_stagingMemorySignaled + MaxStagingMemoryInFlight ? GpuFlushType::ImplicitSynchronization : GpuFlushType::ExplicitFlush; ConsiderFlush(flushType); } // Wait for staging memory to get recycled. if (stagingBufferAllocated > MaxStagingMemoryInFlight) m_dxvkDevice->waitForFence(*m_stagingBufferFence, stagingBufferAllocated - MaxStagingMemoryInFlight); } D3D9_VK_FORMAT_MAPPING D3D9DeviceEx::LookupFormat( D3D9Format Format) const { return m_adapter->GetFormatMapping(Format); } const DxvkFormatInfo* D3D9DeviceEx::UnsupportedFormatInfo( D3D9Format Format) const { return m_adapter->GetUnsupportedFormatInfo(Format); } bool D3D9DeviceEx::WaitForResource( const DxvkPagedResource& Resource, uint64_t SequenceNumber, DWORD MapFlags) { // Wait for the any pending D3D9 command to be executed // on the CS thread so that we can determine whether the // resource is currently in use or not. // Determine access type to wait for based on map mode DxvkAccess access = (MapFlags & D3DLOCK_READONLY) ? DxvkAccess::Write : DxvkAccess::Read; if (!Resource.isInUse(access)) SynchronizeCsThread(SequenceNumber); if (Resource.isInUse(access)) { if (MapFlags & D3DLOCK_DONOTWAIT) { // We don't have to wait, but misbehaving games may // still try to spin on `Map` until the resource is // idle, so we should flush pending commands ConsiderFlush(GpuFlushType::ImplicitWeakHint); return false; } else { // Make sure pending commands using the resource get // executed on the the GPU if we have to wait for it Flush(); SynchronizeCsThread(SequenceNumber); m_dxvkDevice->waitForResource(Resource, access); } } return true; } uint32_t D3D9DeviceEx::CalcImageLockOffset( uint32_t SlicePitch, uint32_t RowPitch, const DxvkFormatInfo* FormatInfo, const D3DBOX* pBox) { if (pBox == nullptr) return 0; std::array offsets = { pBox->Front, pBox->Top, pBox->Left }; uint32_t elementSize = 1; if (FormatInfo != nullptr) { elementSize = FormatInfo->elementSize; VkExtent3D blockSize = FormatInfo->blockSize; if (unlikely(FormatInfo->flags.test(DxvkFormatFlag::MultiPlane))) { elementSize = FormatInfo->planes[0].elementSize; blockSize = { FormatInfo->planes[0].blockSize.width, FormatInfo->planes[0].blockSize.height, 1u }; } offsets[0] = offsets[0] / blockSize.depth; offsets[1] = offsets[1] / blockSize.height; offsets[2] = offsets[2] / blockSize.width; } return offsets[0] * SlicePitch + offsets[1] * RowPitch + offsets[2] * elementSize; } HRESULT D3D9DeviceEx::LockImage( D3D9CommonTexture* pResource, UINT Face, UINT MipLevel, D3DLOCKED_BOX* pLockedBox, const D3DBOX* pBox, DWORD Flags) { D3D9DeviceLock lock = LockDevice(); UINT Subresource = pResource->CalcSubresource(Face, MipLevel); // Don't allow multiple lockings. if (unlikely(pResource->GetLocked(Subresource))) return D3DERR_INVALIDCALL; if (unlikely((Flags & (D3DLOCK_DISCARD | D3DLOCK_READONLY)) == (D3DLOCK_DISCARD | D3DLOCK_READONLY))) return D3DERR_INVALIDCALL; // We only ever wait for textures that were used with GetRenderTargetData or GetFrontBufferData anyway. // Games like Beyond Good and Evil break if this doesn't succeed. Flags &= ~D3DLOCK_DONOTWAIT; if (unlikely((Flags & (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE)) == (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE))) Flags &= ~D3DLOCK_DISCARD; // Tests show that D3D9 drivers ignore DISCARD when the device is lost. if (unlikely(m_deviceLostState != D3D9DeviceLostState::Ok)) Flags &= ~D3DLOCK_DISCARD; auto& desc = *(pResource->Desc()); if (unlikely(!desc.IsLockable)) return D3DERR_INVALIDCALL; if (unlikely(pBox != nullptr)) { D3DRESOURCETYPE type = pResource->GetType(); D3D9_FORMAT_BLOCK_SIZE blockSize = GetFormatAlignedBlockSize(desc.Format); bool isBlockAlignedFormat = blockSize.Width > 0 && blockSize.Height > 0; bool isNotLeftAligned = pBox->Left && (pBox->Left & (blockSize.Width - 1)); bool isNotTopAligned = pBox->Top && (pBox->Top & (blockSize.Height - 1)); bool isNotRightAligned = pBox->Right && (pBox->Right & (blockSize.Width - 1)); bool isNotBottomAligned = pBox->Bottom && (pBox->Bottom & (blockSize.Height - 1)); // LockImage calls on D3DPOOL_DEFAULT surfaces and volume textures with formats // which need to be block aligned, must be validated for mip level 0. if (MipLevel == 0 && isBlockAlignedFormat && (type == D3DRTYPE_VOLUMETEXTURE || desc.Pool == D3DPOOL_DEFAULT) && (isNotLeftAligned || isNotTopAligned || isNotRightAligned || isNotBottomAligned)) return D3DERR_INVALIDCALL; } auto& formatMapping = pResource->GetFormatMapping(); const DxvkFormatInfo* formatInfo = formatMapping.IsValid() ? lookupFormatInfo(formatMapping.FormatColor) : UnsupportedFormatInfo(pResource->Desc()->Format); auto subresource = pResource->GetSubresourceFromIndex( formatInfo->aspectMask, Subresource); VkExtent3D levelExtent = pResource->GetExtentMip(MipLevel); VkExtent3D blockCount = util::computeBlockCount(levelExtent, formatInfo->blockSize); bool fullResource = pBox == nullptr; if (unlikely(!fullResource)) { // Check whether the box passed as argument matches or exceeds the entire texture. VkOffset3D lockOffset; VkExtent3D lockExtent; ConvertBox(*pBox, lockOffset, lockExtent); fullResource = lockOffset == VkOffset3D{ 0, 0, 0 } && lockExtent.width >= levelExtent.width && lockExtent.height >= levelExtent.height && lockExtent.depth >= levelExtent.depth; } // If we are not locking the entire image // a partial discard is meant to occur. // We can't really implement that, so just ignore discard // if we are not locking the full resource. // DISCARD is also ignored for MANAGED and SYSTEMEM. // DISCARD is not ignored for non-DYNAMIC unlike what the docs say. if (!fullResource || desc.Pool != D3DPOOL_DEFAULT) Flags &= ~D3DLOCK_DISCARD; if (desc.Usage & D3DUSAGE_WRITEONLY) Flags &= ~D3DLOCK_READONLY; // If we recently wrote to the texture on the gpu, // then we need to copy -> buffer // We are also always dirty if we are a render target, // a depth stencil, or auto generate mipmaps. bool renderable = desc.Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL); bool needsReadback = pResource->NeedsReadback(Subresource) || renderable; // Skip readback if we discard is specified. We can only do this for textures that have an associated Vulkan image. // Any other texture might write to the Vulkan staging buffer directly. (GetBackbufferData for example) needsReadback &= pResource->GetImage() != nullptr || !(Flags & D3DLOCK_DISCARD); pResource->SetNeedsReadback(Subresource, false); if (unlikely(pResource->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED || needsReadback)) { // Create mapping buffer if it doesn't exist yet. (POOL_DEFAULT) pResource->CreateBuffer(!needsReadback); } // Don't use MapTexture here to keep the mapped list small while the resource is still locked. void* mapPtr = pResource->GetData(Subresource); if (unlikely(needsReadback)) { // The texture was written to on the GPU. // This can be either the image (for D3DPOOL_DEFAULT) // or the buffer directly (for D3DPOOL_SYSTEMMEM). DxvkBufferSlice mappedBufferSlice = pResource->GetBufferSlice(Subresource); const Rc mappedBuffer = pResource->GetBuffer(); if (unlikely(pResource->GetFormatMapping().ConversionFormatInfo.FormatType != D3D9ConversionFormat_None)) { Logger::err(str::format("Reading back format", pResource->Desc()->Format, " is not supported. It is uploaded using the fomrat converter.")); } if (pResource->GetImage() != nullptr) { Rc resourceImage = pResource->GetImage(); Rc mappedImage; if (resourceImage->info().sampleCount != 1) { mappedImage = pResource->GetResolveImage(); } else { mappedImage = std::move(resourceImage); } // When using any map mode which requires the image contents // to be preserved, and if the GPU has write access to the // image, copy the current image contents into the buffer. auto subresourceLayers = vk::makeSubresourceLayers(subresource); // We need to resolve this, some games // lock MSAA render targets even though // that's entirely illegal and they explicitly // tell us that they do NOT want to lock them... // // resourceImage is null because the image reference was moved to mappedImage // for images that need to be resolved. if (resourceImage != nullptr) { EmitCs([ cMainImage = resourceImage, cResolveImage = mappedImage, cSubresource = subresourceLayers ] (DxvkContext* ctx) { VkFormat format = cMainImage->info().format; VkImageResolve region; region.srcSubresource = cSubresource; region.srcOffset = VkOffset3D { 0, 0, 0 }; region.dstSubresource = cSubresource; region.dstOffset = VkOffset3D { 0, 0, 0 }; region.extent = cMainImage->mipLevelExtent(cSubresource.mipLevel); ctx->resolveImage(cResolveImage, cMainImage, region, format, getDefaultResolveMode(format), VK_RESOLVE_MODE_SAMPLE_ZERO_BIT); }); } // if packedFormat is VK_FORMAT_UNDEFINED // DxvkContext::copyImageToBuffer will automatically take the format from the image VkFormat packedFormat = GetPackedDepthStencilFormat(desc.Format); EmitCs([ cImageBufferSlice = std::move(mappedBufferSlice), cImage = std::move(mappedImage), cSubresources = subresourceLayers, cLevelExtent = levelExtent, cPackedFormat = packedFormat ] (DxvkContext* ctx) { ctx->copyImageToBuffer(cImageBufferSlice.buffer(), cImageBufferSlice.offset(), 4, 0, cPackedFormat, cImage, cSubresources, VkOffset3D { 0, 0, 0 }, cLevelExtent); }); TrackTextureMappingBufferSequenceNumber(pResource, Subresource); } // Wait until the buffer is idle which may include the copy (and resolve) we just issued. if (!WaitForResource(*mappedBuffer, pResource->GetMappingBufferSequenceNumber(Subresource), Flags)) return D3DERR_WASSTILLDRAWING; } const bool atiHack = desc.Format == D3D9Format::ATI1 || desc.Format == D3D9Format::ATI2; // Set up map pointer. if (atiHack) { // The API didn't treat this as a block compressed format here. // So we need to lie here. The game is expected to use this info and do a workaround. // It's stupid. I know. pLockedBox->RowPitch = align(std::max(desc.Width >> MipLevel, 1u), 4); pLockedBox->SlicePitch = pLockedBox->RowPitch * std::max(desc.Height >> MipLevel, 1u); } else if (likely(!formatInfo->flags.test(DxvkFormatFlag::MultiPlane))) { pLockedBox->RowPitch = align(formatInfo->elementSize * blockCount.width, 4); pLockedBox->SlicePitch = pLockedBox->RowPitch * blockCount.height; } else { auto plane = &formatInfo->planes[0]; uint32_t planeElementSize = plane->elementSize; VkExtent3D planeBlockSize = { plane->blockSize.width, plane->blockSize.height, 1u }; VkExtent3D blockCount = util::computeBlockCount(levelExtent, planeBlockSize); pLockedBox->RowPitch = align(planeElementSize * blockCount.width, 4); pLockedBox->SlicePitch = pLockedBox->RowPitch * blockCount.height; } pResource->SetLocked(Subresource, true); // Make sure the amount of mapped texture memory stays below the threshold. UnmapTextures(); const bool readOnly = Flags & D3DLOCK_READONLY; const bool noDirtyUpdate = Flags & D3DLOCK_NO_DIRTY_UPDATE; if ((desc.Pool == D3DPOOL_DEFAULT || !noDirtyUpdate) && !readOnly) { if (pBox && MipLevel != 0) { D3DBOX scaledBox = *pBox; scaledBox.Left <<= MipLevel; scaledBox.Right = std::min(scaledBox.Right << MipLevel, pResource->Desc()->Width); scaledBox.Top <<= MipLevel; scaledBox.Bottom = std::min(scaledBox.Bottom << MipLevel, pResource->Desc()->Height); scaledBox.Back <<= MipLevel; scaledBox.Front = std::min(scaledBox.Front << MipLevel, pResource->Desc()->Depth); pResource->AddDirtyBox(&scaledBox, Face); } else { pResource->AddDirtyBox(pBox, Face); } } if (IsPoolManaged(desc.Pool) && !readOnly) { // Managed textures are uploaded at draw time. pResource->SetNeedsUpload(Subresource, true); for (uint32_t i : bit::BitMask(m_activeTextures)) { // Guaranteed to not be nullptr... auto texInfo = GetCommonTexture(m_state.textures[i]); if (texInfo == pResource) { m_activeTexturesToUpload |= 1 << i; } } } const uint32_t offset = CalcImageLockOffset( pLockedBox->SlicePitch, pLockedBox->RowPitch, (!atiHack) ? formatInfo : nullptr, pBox); uint8_t* data = reinterpret_cast(mapPtr); data += offset; pLockedBox->pBits = data; return D3D_OK; } HRESULT D3D9DeviceEx::UnlockImage( D3D9CommonTexture* pResource, UINT Face, UINT MipLevel) { D3D9DeviceLock lock = LockDevice(); UINT Subresource = pResource->CalcSubresource(Face, MipLevel); // Don't allow multiple unlockings, except for D3DRTYPE_TEXTURE if (unlikely(!pResource->GetLocked(Subresource))) { if (pResource->GetType() == D3DRTYPE_TEXTURE) return D3D_OK; else return D3DERR_INVALIDCALL; } MapTexture(pResource, Subresource); // Add it to the list of mapped resources pResource->SetLocked(Subresource, false); // Flush image contents from staging if we aren't read only // and we aren't deferring for managed. const D3DBOX& box = pResource->GetDirtyBox(Face); bool shouldFlush = pResource->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED; shouldFlush &= box.Left < box.Right && box.Top < box.Bottom && box.Front < box.Back; shouldFlush &= !pResource->IsManaged(); if (shouldFlush) { this->FlushImage(pResource, Subresource); if (!pResource->IsAnySubresourceLocked()) pResource->ClearDirtyBoxes(); } // Toss our staging buffer if we're not dynamic // and we aren't managed (for sysmem copy.) bool shouldToss = pResource->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED; shouldToss &= !pResource->IsDynamic(); shouldToss &= !pResource->IsManaged(); shouldToss &= !pResource->IsAnySubresourceLocked(); // The texture converter cannot handle converting back. So just keep textures in memory as a workaround. shouldToss &= pResource->GetFormatMapping().ConversionFormatInfo.FormatType == D3D9ConversionFormat_None; if (shouldToss) pResource->DestroyBuffer(); UnmapTextures(); return D3D_OK; } HRESULT D3D9DeviceEx::FlushImage( D3D9CommonTexture* pResource, UINT Subresource) { const Rc image = pResource->GetImage(); auto formatInfo = lookupFormatInfo(image->info().format); auto subresource = pResource->GetSubresourceFromIndex( formatInfo->aspectMask, Subresource); const D3DBOX& box = pResource->GetDirtyBox(subresource.arrayLayer); // The dirty box is only tracked for mip 0. Scale it for the mip level we're gonna upload. VkExtent3D mip0Extent = { box.Right - box.Left, box.Bottom - box.Top, box.Back - box.Front }; VkExtent3D extent = util::computeMipLevelExtent(mip0Extent, subresource.mipLevel); VkOffset3D mip0Offset = { int32_t(box.Left), int32_t(box.Top), int32_t(box.Front) }; VkOffset3D offset = util::computeMipLevelOffset(mip0Offset, subresource.mipLevel); UpdateTextureFromBuffer(pResource, pResource, Subresource, Subresource, offset, extent, offset); if (pResource->IsAutomaticMip()) MarkTextureMipsDirty(pResource); return D3D_OK; } void D3D9DeviceEx::UpdateTextureFromBuffer( D3D9CommonTexture* pDestTexture, D3D9CommonTexture* pSrcTexture, UINT DestSubresource, UINT SrcSubresource, VkOffset3D SrcOffset, VkExtent3D SrcExtent, VkOffset3D DestOffset) { // Wait until the amount of used staging memory is under a certain threshold to avoid using // too much memory and even more so to avoid using too much address space. WaitStagingBuffer(); const Rc image = pDestTexture->GetImage(); // Now that data has been written into the buffer, // we need to copy its contents into the image auto formatInfo = lookupFormatInfo(pDestTexture->GetFormatMapping().FormatColor); auto srcSubresource = pSrcTexture->GetSubresourceFromIndex( formatInfo->aspectMask, SrcSubresource); auto dstSubresource = pDestTexture->GetSubresourceFromIndex( formatInfo->aspectMask, DestSubresource); VkImageSubresourceLayers dstLayers = { dstSubresource.aspectMask, dstSubresource.mipLevel, dstSubresource.arrayLayer, 1 }; VkExtent3D dstTexLevelExtent = image->mipLevelExtent(dstSubresource.mipLevel); VkExtent3D srcTexLevelExtent = util::computeMipLevelExtent(pSrcTexture->GetExtent(), srcSubresource.mipLevel); auto convertFormat = pDestTexture->GetFormatMapping().ConversionFormatInfo; if (unlikely(pSrcTexture->NeedsReadback(SrcSubresource))) { // The src texutre has to be in POOL_SYSTEMEM, so it cannot use AUTOMIPGEN. // That means that NeedsReadback is only true if the texture has been used with GetRTData or GetFrontbufferData before. // Those functions create a buffer, so the buffer always exists here. const Rc& buffer = pSrcTexture->GetBuffer(); WaitForResource(*buffer, pSrcTexture->GetMappingBufferSequenceNumber(SrcSubresource), 0); pSrcTexture->SetNeedsReadback(SrcSubresource, false); } if (likely(convertFormat.FormatType == D3D9ConversionFormat_None)) { // The texture does not use a format that needs to be converted in a compute shader. // So we just need to make sure the passed size and offset are not out of range and properly aligned, // copy the data to a staging buffer and then copy that on the GPU to the actual image. VkOffset3D alignedDestOffset = { int32_t(alignDown(DestOffset.x, formatInfo->blockSize.width)), int32_t(alignDown(DestOffset.y, formatInfo->blockSize.height)), int32_t(alignDown(DestOffset.z, formatInfo->blockSize.depth)) }; VkOffset3D alignedSrcOffset = { int32_t(alignDown(SrcOffset.x, formatInfo->blockSize.width)), int32_t(alignDown(SrcOffset.y, formatInfo->blockSize.height)), int32_t(alignDown(SrcOffset.z, formatInfo->blockSize.depth)) }; SrcExtent.width += SrcOffset.x - alignedSrcOffset.x; SrcExtent.height += SrcOffset.y - alignedSrcOffset.y; SrcExtent.depth += SrcOffset.z - alignedSrcOffset.z; VkExtent3D extentBlockCount = util::computeBlockCount(SrcExtent, formatInfo->blockSize); VkExtent3D alignedExtent = util::computeBlockExtent(extentBlockCount, formatInfo->blockSize); alignedExtent = util::snapExtent3D(alignedDestOffset, alignedExtent, dstTexLevelExtent); alignedExtent = util::snapExtent3D(alignedSrcOffset, alignedExtent, srcTexLevelExtent); VkOffset3D srcOffsetBlockCount = util::computeBlockOffset(alignedSrcOffset, formatInfo->blockSize); VkExtent3D srcTexLevelExtentBlockCount = util::computeBlockCount(srcTexLevelExtent, formatInfo->blockSize); VkDeviceSize pitch = align(srcTexLevelExtentBlockCount.width * formatInfo->elementSize, 4); VkDeviceSize copySrcOffset = srcOffsetBlockCount.z * srcTexLevelExtentBlockCount.height * pitch + srcOffsetBlockCount.y * pitch + srcOffsetBlockCount.x * formatInfo->elementSize; // Get the mapping pointer from MapTexture to map the texture and keep track of that // in case it is unmappable. const void* mapPtr = MapTexture(pSrcTexture, SrcSubresource); VkDeviceSize dirtySize = extentBlockCount.width * extentBlockCount.height * extentBlockCount.depth * formatInfo->elementSize; D3D9BufferSlice slice = AllocStagingBuffer(dirtySize); const void* srcData = reinterpret_cast(mapPtr) + copySrcOffset; util::packImageData( slice.mapPtr, srcData, extentBlockCount, formatInfo->elementSize, pitch, pitch * srcTexLevelExtentBlockCount.height); VkFormat packedDSFormat = GetPackedDepthStencilFormat(pDestTexture->Desc()->Format); EmitCs([ cSrcSlice = slice.slice, cDstImage = image, cDstLayers = dstLayers, cDstLevelExtent = alignedExtent, cOffset = alignedDestOffset, cPackedDSFormat = packedDSFormat ] (DxvkContext* ctx) { ctx->copyBufferToImage( cDstImage, cDstLayers, cOffset, cDstLevelExtent, cSrcSlice.buffer(), cSrcSlice.offset(), 0, 0, cPackedDSFormat); }); TrackTextureMappingBufferSequenceNumber(pSrcTexture, SrcSubresource); } else { // The texture uses a format which gets converted by a compute shader. const void* mapPtr = MapTexture(pSrcTexture, SrcSubresource); // The compute shader does not support only converting a subrect of the texture if (unlikely(SrcOffset.x != 0 || SrcOffset.y != 0 || SrcOffset.z != 0 || DestOffset.x != 0 || DestOffset.y != 0 || DestOffset.z != 0 || SrcExtent != srcTexLevelExtent)) { Logger::warn("Offset and rect not supported with the texture converter."); } if (unlikely(srcTexLevelExtent != dstTexLevelExtent)) { Logger::err("Different extents are not supported with the texture converter."); return; } uint32_t formatElementSize = formatInfo->elementSize; VkExtent3D srcBlockSize = formatInfo->blockSize; if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) { formatElementSize = formatInfo->planes[0].elementSize; srcBlockSize = { formatInfo->planes[0].blockSize.width, formatInfo->planes[0].blockSize.height, 1u }; } VkExtent3D srcBlockCount = util::computeBlockCount(srcTexLevelExtent, srcBlockSize); srcBlockCount.height *= std::min(pSrcTexture->GetPlaneCount(), 2u); // the converter can not handle the 4 aligned pitch so we always repack into a staging buffer D3D9BufferSlice slice = AllocStagingBuffer(pSrcTexture->GetMipSize(SrcSubresource)); VkDeviceSize pitch = align(srcBlockCount.width * formatElementSize, 4); const DxvkFormatInfo* convertedFormatInfo = lookupFormatInfo(convertFormat.FormatColor); VkImageSubresourceLayers convertedDstLayers = { convertedFormatInfo->aspectMask, dstSubresource.mipLevel, dstSubresource.arrayLayer, 1 }; util::packImageData( slice.mapPtr, mapPtr, srcBlockCount, formatElementSize, pitch, std::min(pSrcTexture->GetPlaneCount(), 2u) * pitch * srcBlockCount.height); EmitCs([this, cConvertFormat = convertFormat, cDstImage = std::move(image), cDstLayers = convertedDstLayers, cSrcSlice = std::move(slice.slice) ] (DxvkContext* ctx) { auto contextObjects = ctx->beginExternalRendering(); m_converter->ConvertFormat(contextObjects, cConvertFormat, cDstImage, cDstLayers, cSrcSlice); }); } UnmapTextures(); ConsiderFlush(GpuFlushType::ImplicitWeakHint); } void D3D9DeviceEx::EmitGenerateMips( D3D9CommonTexture* pResource) { if (pResource->IsManaged()) UploadManagedTexture(pResource); EmitCs([ cImageView = pResource->GetSampleView(false), cFilter = pResource->GetMipFilter() ] (DxvkContext* ctx) { ctx->generateMipmaps(cImageView, DecodeFilter(cFilter)); }); } HRESULT D3D9DeviceEx::LockBuffer( D3D9CommonBuffer* pResource, UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags) { D3D9DeviceLock lock = LockDevice(); if (unlikely(ppbData == nullptr)) return D3DERR_INVALIDCALL; auto& desc = *pResource->Desc(); // Ignore DISCARD if NOOVERWRITE is set if (unlikely((Flags & (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE)) == (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE))) Flags &= ~D3DLOCK_DISCARD; // Ignore DISCARD and NOOVERWRITE if the buffer is not DEFAULT pool (tests + Halo 2) // The docs say DISCARD and NOOVERWRITE are ignored if the buffer is not DYNAMIC // but tests say otherwise! if (desc.Pool != D3DPOOL_DEFAULT || CanOnlySWVP()) Flags &= ~(D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE); // Ignore DONOTWAIT if we are DYNAMIC // Yes... D3D9 is a good API. if (desc.Usage & D3DUSAGE_DYNAMIC) Flags &= ~D3DLOCK_DONOTWAIT; // Tests show that D3D9 drivers ignore DISCARD when the device is lost. if (unlikely(m_deviceLostState != D3D9DeviceLostState::Ok)) Flags &= ~D3DLOCK_DISCARD; // In SWVP mode, we always use the per-draw upload path. // So the buffer will never be in use on the device. // FVF Buffers are the exception. Those can be used as a destination for ProcessVertices. if (unlikely(CanOnlySWVP() && !pResource->NeedsReadback())) Flags |= D3DLOCK_NOOVERWRITE; // We only bounds check for MANAGED. // (TODO: Apparently this is meant to happen for DYNAMIC too but I am not sure // how that works given it is meant to be a DIRECT access..?) const bool respectUserBounds = !(Flags & D3DLOCK_DISCARD) && SizeToLock != 0; // If we don't respect the bounds, encompass it all in our tests/checks // These values may be out of range and don't get clamped. uint32_t offset = respectUserBounds ? OffsetToLock : 0; uint32_t size = respectUserBounds ? std::min(SizeToLock, desc.Size - offset) : desc.Size; D3D9Range lockRange = D3D9Range(offset, offset + size); bool updateDirtyRange = (desc.Pool == D3DPOOL_DEFAULT || !(Flags & D3DLOCK_NO_DIRTY_UPDATE)) && !(Flags & D3DLOCK_READONLY); if (updateDirtyRange) { pResource->DirtyRange().Conjoin(lockRange); for (uint32_t i : bit::BitMask(m_activeVertexBuffers)) { auto commonBuffer = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer); if (commonBuffer == pResource) { m_activeVertexBuffersToUpload |= 1 << i; } } } const bool directMapping = pResource->GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_DIRECT; const bool needsReadback = pResource->NeedsReadback(); uint8_t* data = nullptr; if ((Flags & D3DLOCK_DISCARD) && (directMapping || needsReadback)) { // If we're not directly mapped and don't need readback, // the buffer is not currently getting used anyway // so there's no reason to waste memory by discarding. // Allocate a new backing slice for the buffer and set // it as the 'new' mapped slice. This assumes that the // only way to invalidate a buffer is by mapping it. Rc mappingBuffer = pResource->GetBuffer(); auto bufferSlice = pResource->DiscardMapSlice(); data = reinterpret_cast(bufferSlice->mapPtr()); EmitCs([ cBuffer = std::move(mappingBuffer), cBufferSlice = std::move(bufferSlice) ] (DxvkContext* ctx) mutable { ctx->invalidateBuffer(cBuffer, std::move(cBufferSlice)); }); pResource->SetNeedsReadback(false); } else { // The application either didn't specify DISCARD or the buffer is guaranteed to be idle anyway. // Use map pointer from previous map operation. This // way we don't have to synchronize with the CS thread // if the map mode is D3DLOCK_NOOVERWRITE. data = reinterpret_cast(pResource->GetMappedSlice()->mapPtr()); const bool readOnly = Flags & D3DLOCK_READONLY; // NOOVERWRITE promises that they will not write in a currently used area. const bool noOverwrite = Flags & D3DLOCK_NOOVERWRITE; const bool directMapping = pResource->GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_DIRECT; // If we're not directly mapped, we can rely on needsReadback to tell us if a sync is required. const bool skipWait = (!needsReadback && (readOnly || !directMapping)) || noOverwrite; if (!skipWait) { const Rc mappingBuffer = pResource->GetBuffer(); if (!WaitForResource(*mappingBuffer, pResource->GetMappingBufferSequenceNumber(), Flags)) return D3DERR_WASSTILLDRAWING; pResource->SetNeedsReadback(false); } } // The offset/size is not clamped to or affected by the desc size. data += OffsetToLock; *ppbData = reinterpret_cast(data); DWORD oldFlags = pResource->GetMapFlags(); // We need to remove the READONLY flags from the map flags // if there was ever a non-readonly upload. if (!(Flags & D3DLOCK_READONLY)) oldFlags &= ~D3DLOCK_READONLY; pResource->SetMapFlags(Flags | oldFlags); pResource->IncrementLockCount(); // We just mapped a buffer which may have come with an address space cost. // Unmap textures if the amount of mapped texture memory is exceeding the threshold. UnmapTextures(); return D3D_OK; } HRESULT D3D9DeviceEx::FlushBuffer( D3D9CommonBuffer* pResource) { // Wait until the amount of used staging memory is under a certain threshold to avoid using // too much memory and even more so to avoid using too much address space. WaitStagingBuffer(); auto dstBuffer = pResource->GetBufferSlice(); auto srcSlice = pResource->GetMappedSlice(); D3D9Range& range = pResource->DirtyRange(); D3D9BufferSlice slice = AllocStagingBuffer(range.max - range.min); void* srcData = reinterpret_cast(srcSlice->mapPtr()) + range.min; memcpy(slice.mapPtr, srcData, range.max - range.min); EmitCs([ cDstSlice = dstBuffer, cSrcSlice = slice.slice, cDstOffset = range.min, cLength = range.max - range.min ] (DxvkContext* ctx) { ctx->copyBuffer( cDstSlice.buffer(), cDstSlice.offset() + cDstOffset, cSrcSlice.buffer(), cSrcSlice.offset(), cLength); }); pResource->DirtyRange().Clear(); TrackBufferMappingBufferSequenceNumber(pResource); UnmapTextures(); ConsiderFlush(GpuFlushType::ImplicitWeakHint); return D3D_OK; } HRESULT D3D9DeviceEx::UnlockBuffer( D3D9CommonBuffer* pResource) { D3D9DeviceLock lock = LockDevice(); if (pResource->DecrementLockCount() != 0) return D3D_OK; // Nothing else to do for directly mapped buffers. Those were already written. if (pResource->GetMapMode() != D3D9_COMMON_BUFFER_MAP_MODE_BUFFER) return D3D_OK; // There is no part of the buffer that hasn't been uploaded yet. // This shouldn't happen. if (pResource->DirtyRange().IsDegenerate()) return D3D_OK; pResource->SetMapFlags(0); // Only D3DPOOL_DEFAULT buffers get uploaded in UnlockBuffer. // D3DPOOL_SYSTEMMEM and D3DPOOL_MANAGED get uploaded at draw time. if (pResource->Desc()->Pool != D3DPOOL_DEFAULT) return D3D_OK; FlushBuffer(pResource); return D3D_OK; } void D3D9DeviceEx::UploadPerDrawData( UINT& FirstVertexIndex, UINT NumVertices, UINT& FirstIndex, UINT NumIndices, INT& BaseVertexIndex, bool* pDynamicVBOs, bool* pDynamicIBO ) { const uint32_t usedBuffersMask = (m_state.vertexDecl != nullptr ? m_state.vertexDecl->GetStreamMask() : ~0u) & m_activeVertexBuffers; bool dynamicSysmemVBOs = usedBuffersMask == m_activeVertexBuffersToUploadPerDraw; D3D9CommonBuffer* ibo = GetCommonBuffer(m_state.indices); bool dynamicSysmemIBO = NumIndices != 0 && ibo != nullptr && (ibo->DoPerDrawUpload() || CanOnlySWVP()); *pDynamicVBOs = dynamicSysmemVBOs; if (unlikely(pDynamicIBO)) *pDynamicIBO = dynamicSysmemIBO; if (likely(!dynamicSysmemVBOs && !dynamicSysmemIBO)) return; uint32_t vertexBuffersToUpload; if (likely(dynamicSysmemVBOs)) vertexBuffersToUpload = m_activeVertexBuffersToUploadPerDraw & usedBuffersMask; else vertexBuffersToUpload = 0; // The UP buffer allocator will invalidate, // so we can only use 1 UP buffer slice per draw. // First we calculate the size of that UP buffer slice // and store all sizes and offsets into it. struct VBOCopy { uint32_t srcOffset; uint32_t dstOffset; uint32_t copyBufferLength; uint32_t copyElementCount; uint32_t copyElementSize; uint32_t copyElementStride; }; uint32_t totalUpBufferSize = 0; std::array vboCopies = {}; for (uint32_t i : bit::BitMask(vertexBuffersToUpload)) { auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer); if (likely(vbo == nullptr)) { continue; } if (unlikely(vbo->NeedsReadback())) { // There's only one way the GPU might write new data to a vertex buffer: // - Write to the primary buffer using ProcessVertices which gets copied over to the staging buffer at the end. // So it could end up writing to the buffer on the GPU while the same buffer gets read here on the CPU. // That is why we need to ensure the staging buffer is idle here. WaitForResource(*vbo->GetBuffer(), vbo->GetMappingBufferSequenceNumber(), D3DLOCK_READONLY); } const uint32_t vertexSize = m_state.vertexDecl->GetSize(i); const uint32_t vertexStride = m_state.vertexBuffers[i].stride; const uint32_t srcStride = vertexStride; const uint32_t dstStride = std::min(vertexStride, vertexSize); uint32_t elementCount = NumVertices; if (m_state.streamFreq[i] & D3DSTREAMSOURCE_INSTANCEDATA) { elementCount = GetInstanceCount(); } const uint32_t vboOffset = m_state.vertexBuffers[i].offset; const uint32_t vertexOffset = (FirstVertexIndex + BaseVertexIndex) * srcStride; const uint32_t vertexBufferSize = vbo->Desc()->Size; const uint32_t srcOffset = vboOffset + vertexOffset; if (unlikely(srcOffset > vertexBufferSize)) { // All vertices are out of bounds vboCopies[i].copyBufferLength = 0; } else if (unlikely(srcOffset + elementCount * srcStride > vertexBufferSize)) { // Some vertices are (partially) out of bounds uint32_t boundVertexBufferRange = vertexBufferSize - vboOffset; elementCount = boundVertexBufferRange / srcStride; // Copy all complete vertices vboCopies[i].copyBufferLength = elementCount * dstStride; // Copy the remaining partial vertex vboCopies[i].copyBufferLength += std::min(dstStride, boundVertexBufferRange % srcStride); } else { // No vertices are out of bounds vboCopies[i].copyBufferLength = elementCount * dstStride; } vboCopies[i].copyElementCount = elementCount; vboCopies[i].copyElementStride = srcStride; vboCopies[i].copyElementSize = dstStride; vboCopies[i].srcOffset = srcOffset; vboCopies[i].dstOffset = totalUpBufferSize; totalUpBufferSize += vboCopies[i].copyBufferLength; } uint32_t iboUPBufferSize = 0; uint32_t iboUPBufferOffset = 0; if (dynamicSysmemIBO) { auto* ibo = GetCommonBuffer(m_state.indices); if (likely(ibo != nullptr)) { uint32_t indexStride = ibo->Desc()->Format == D3D9Format::INDEX16 ? 2 : 4; uint32_t offset = indexStride * FirstIndex; uint32_t indexBufferSize = ibo->Desc()->Size; if (offset < indexBufferSize) { iboUPBufferSize = std::min(NumIndices * indexStride, indexBufferSize - offset); iboUPBufferOffset = totalUpBufferSize; totalUpBufferSize += iboUPBufferSize; } } } if (unlikely(totalUpBufferSize == 0)) { *pDynamicVBOs = false; if (pDynamicIBO) *pDynamicIBO = false; return; } auto upSlice = AllocUPBuffer(totalUpBufferSize); // Now copy the actual data and bind it. if (dynamicSysmemVBOs) { for (uint32_t i : bit::BitMask(vertexBuffersToUpload)) { const VBOCopy& copy = vboCopies[i]; if (likely(copy.copyBufferLength != 0)) { const auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer); uint8_t* data = reinterpret_cast(upSlice.mapPtr) + copy.dstOffset; const uint8_t* src = reinterpret_cast(vbo->GetMappedSlice()->mapPtr()) + copy.srcOffset; if (likely(copy.copyElementStride == copy.copyElementSize)) { std::memcpy(data, src, copy.copyBufferLength); } else { for (uint32_t j = 0; j < copy.copyElementCount; j++) { std::memcpy(data + j * copy.copyElementSize, src + j * copy.copyElementStride, copy.copyElementSize); } if (unlikely(copy.copyBufferLength > copy.copyElementCount * copy.copyElementSize)) { // Partial vertex at the end std::memcpy( data + copy.copyElementCount * copy.copyElementSize, src + copy.copyElementCount * copy.copyElementStride, copy.copyBufferLength - copy.copyElementCount * copy.copyElementSize); } } } auto vboSlice = upSlice.slice.subSlice(copy.dstOffset, copy.copyBufferLength); EmitCs([ cStream = i, cBufferSlice = std::move(vboSlice), cStride = copy.copyElementSize ](DxvkContext* ctx) mutable { ctx->bindVertexBuffer(cStream, std::move(cBufferSlice), cStride); }); m_flags.set(D3D9DeviceFlag::DirtyVertexBuffers); } // Change the draw call parameters to reflect the changed vertex buffers if (NumIndices != 0) { BaseVertexIndex = -FirstVertexIndex; } else { FirstVertexIndex = 0; } } if (dynamicSysmemIBO) { if (unlikely(iboUPBufferSize == 0)) { EmitCs([](DxvkContext* ctx) { ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32); }); m_flags.set(D3D9DeviceFlag::DirtyIndexBuffer); } else { auto* ibo = GetCommonBuffer(m_state.indices); uint32_t indexStride = ibo->Desc()->Format == D3D9Format::INDEX16 ? 2 : 4; VkIndexType indexType = DecodeIndexType(ibo->Desc()->Format); uint32_t offset = indexStride * FirstIndex; uint8_t* data = reinterpret_cast(upSlice.mapPtr) + iboUPBufferOffset; uint8_t* src = reinterpret_cast(ibo->GetMappedSlice()->mapPtr()) + offset; std::memcpy(data, src, iboUPBufferSize); auto iboSlice = upSlice.slice.subSlice(iboUPBufferOffset, iboUPBufferSize); EmitCs([ cBufferSlice = std::move(iboSlice), cIndexType = indexType ](DxvkContext* ctx) mutable { ctx->bindIndexBuffer(std::move(cBufferSlice), cIndexType); }); m_flags.set(D3D9DeviceFlag::DirtyIndexBuffer); } // Change the draw call parameters to reflect the changed index buffer FirstIndex = 0; } } void D3D9DeviceEx::InjectCsChunk( DxvkCsChunkRef&& Chunk, bool Synchronize) { m_csThread.injectChunk(DxvkCsQueue::HighPriority, std::move(Chunk), Synchronize); } void D3D9DeviceEx::EmitCsChunk(DxvkCsChunkRef&& chunk) { // Flush init commands so that the CS thread // can processe them before the first use. m_initializer->FlushCsChunk(); m_csSeqNum = m_csThread.dispatchChunk(std::move(chunk)); } void D3D9DeviceEx::ConsiderFlush(GpuFlushType FlushType) { uint64_t chunkId = GetCurrentSequenceNumber(); uint64_t submissionId = m_submissionFence->value(); if (m_flushTracker.considerFlush(FlushType, chunkId, submissionId)) Flush(); } void D3D9DeviceEx::SynchronizeCsThread(uint64_t SequenceNumber) { D3D9DeviceLock lock = LockDevice(); // Dispatch current chunk so that all commands // recorded prior to this function will be run if (SequenceNumber > m_csSeqNum) FlushCsChunk(); m_csThread.synchronize(SequenceNumber); } void D3D9DeviceEx::SetupFPU() { // Should match d3d9 float behaviour. #if defined(_MSC_VER) // For MSVC we can use these cross arch and platform funcs to set the FPU. // This will work on any platform, x86, x64, ARM, etc. // Clear exceptions. _clearfp(); // Disable exceptions _controlfp(_MCW_EM, _MCW_EM); #ifndef _WIN64 // Use 24 bit precision _controlfp(_PC_24, _MCW_PC); #endif // Round to nearest _controlfp(_RC_NEAR, _MCW_RC); #elif (defined(__GNUC__) || defined(__MINGW32__)) && (defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__)) || defined(__ia64)) // For GCC/MinGW we can use inline asm to set it. // This only works for x86 and x64 processors however. uint16_t control; // Get current control word. __asm__ __volatile__("fnstcw %0" : "=m" (*&control)); // Clear existing settings. control &= 0xF0C0; // Disable exceptions // Use 24 bit precision // Round to nearest control |= 0x003F; // Set new control word. __asm__ __volatile__("fldcw %0" : : "m" (*&control)); #else Logger::warn("D3D9DeviceEx::SetupFPU: not supported on this arch."); #endif } int64_t D3D9DeviceEx::DetermineInitialTextureMemory() { auto memoryProp = m_adapter->GetDXVKAdapter()->memoryProperties(); VkDeviceSize availableTextureMemory = 0; for (uint32_t i = 0; i < memoryProp.memoryHeapCount; i++) availableTextureMemory += memoryProp.memoryHeaps[i].size; constexpr VkDeviceSize Megabytes = 1024 * 1024; // The value returned is a 32-bit value, so we need to clamp it. VkDeviceSize maxMemory = (VkDeviceSize(m_d3d9Options.maxAvailableMemory) * Megabytes) - 1; availableTextureMemory = std::min(availableTextureMemory, maxMemory); return int64_t(availableTextureMemory); } void D3D9DeviceEx::CreateConstantBuffers() { constexpr VkDeviceSize DefaultConstantBufferSize = 1024ull << 10; constexpr VkDeviceSize SmallConstantBufferSize = 64ull << 10; m_consts[DxsoProgramTypes::VertexShader].buffer = D3D9ConstantBuffer(this, DxsoProgramType::VertexShader, DxsoConstantBuffers::VSConstantBuffer, DefaultConstantBufferSize); m_consts[DxsoProgramTypes::VertexShader].swvp.intBuffer = D3D9ConstantBuffer(this, DxsoProgramType::VertexShader, DxsoConstantBuffers::VSIntConstantBuffer, SmallConstantBufferSize); m_consts[DxsoProgramTypes::VertexShader].swvp.boolBuffer = D3D9ConstantBuffer(this, DxsoProgramType::VertexShader, DxsoConstantBuffers::VSBoolConstantBuffer, SmallConstantBufferSize); m_consts[DxsoProgramTypes::PixelShader].buffer = D3D9ConstantBuffer(this, DxsoProgramType::PixelShader, DxsoConstantBuffers::PSConstantBuffer, DefaultConstantBufferSize); m_vsClipPlanes = D3D9ConstantBuffer(this, DxsoProgramType::VertexShader, DxsoConstantBuffers::VSClipPlanes, caps::MaxClipPlanes * sizeof(D3D9ClipPlane)); m_vsFixedFunction = D3D9ConstantBuffer(this, DxsoProgramType::VertexShader, DxsoConstantBuffers::VSFixedFunction, sizeof(D3D9FixedFunctionVS)); m_psFixedFunction = D3D9ConstantBuffer(this, DxsoProgramType::PixelShader, DxsoConstantBuffers::PSFixedFunction, sizeof(D3D9FixedFunctionPS)); m_psShared = D3D9ConstantBuffer(this, DxsoProgramType::PixelShader, DxsoConstantBuffers::PSShared, sizeof(D3D9SharedPS)); m_vsVertexBlend = D3D9ConstantBuffer(this, DxsoProgramType::VertexShader, DxsoConstantBuffers::VSVertexBlendData, CanSWVP() ? sizeof(D3D9FixedFunctionVertexBlendDataSW) : sizeof(D3D9FixedFunctionVertexBlendDataHW)); // Allocate constant buffer for values that would otherwise get passed as spec constants for fast-linked pipelines to use. if (m_usingGraphicsPipelines) { m_specBuffer = D3D9ConstantBuffer(this, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, getSpecConstantBufferSlot(), D3D9SpecializationInfo::UBOSize); } } inline void D3D9DeviceEx::UploadSoftwareConstantSet(const D3D9ShaderConstantsVSSoftware& Src, const D3D9ConstantLayout& Layout) { /* * SWVP raises the amount of constants by a lot. * To avoid copying huge amounts of data for every draw call, * we track the highest set constant and only use a buffer big enough * to fit that. We rely on robustness to return 0 for OOB reads. */ D3D9ConstantSets& constSet = m_consts[DxsoProgramType::VertexShader]; if (!constSet.dirty) return; constSet.dirty = false; uint32_t floatCount = constSet.maxChangedConstF; if (constSet.meta.needsConstantCopies) { // If the shader requires us to preserve shader defined constants, // we copy those over. We need to adjust the amount of used floats accordingly. auto shader = GetCommonShader(m_state.vertexShader); floatCount = std::max(floatCount, shader->GetMaxDefinedConstant() + 1); } // If we statically know which is the last float constant accessed by the shader, we don't need to copy the rest. floatCount = std::min(floatCount, constSet.meta.maxConstIndexF); // Calculate data sizes for each constant type. const uint32_t floatDataSize = floatCount * sizeof(Vector4); const uint32_t intDataSize = std::min(constSet.meta.maxConstIndexI, constSet.maxChangedConstI) * sizeof(Vector4i); const uint32_t boolDataSize = divCeil(std::min(constSet.meta.maxConstIndexB, constSet.maxChangedConstB), 32u) * uint32_t(sizeof(uint32_t)); // Max copy source size is 8192 * 16 => always aligned to any plausible value // => we won't copy out of bounds if (likely(constSet.meta.maxConstIndexF != 0)) { auto mapPtr = CopySoftwareConstants(constSet.buffer, Src.fConsts, floatDataSize); if (constSet.meta.needsConstantCopies) { // Copy shader defined constants over so they can be accessed // with relative addressing. Vector4* data = reinterpret_cast(mapPtr); auto& shaderConsts = GetCommonShader(m_state.vertexShader)->GetConstants(); for (const auto& constant : shaderConsts) { if (constant.uboIdx < constSet.meta.maxConstIndexF) data[constant.uboIdx] = *reinterpret_cast(constant.float32); } } } // Max copy source size is 2048 * 16 => always aligned to any plausible value // => we won't copy out of bounds if (likely(constSet.meta.maxConstIndexI != 0)) CopySoftwareConstants(constSet.swvp.intBuffer, Src.iConsts, intDataSize); if (likely(constSet.meta.maxConstIndexB != 0)) CopySoftwareConstants(constSet.swvp.boolBuffer, Src.bConsts, boolDataSize); } inline void* D3D9DeviceEx::CopySoftwareConstants(D3D9ConstantBuffer& dstBuffer, const void* src, uint32_t size) { uint32_t alignment = dstBuffer.GetAlignment(); size = std::max(size, alignment); size = align(size, alignment); auto mapPtr = dstBuffer.Alloc(size); std::memcpy(mapPtr, src, size); return mapPtr; } template inline void D3D9DeviceEx::UploadConstantSet(const SoftwareLayoutType& Src, const D3D9ConstantLayout& Layout, const ShaderType& Shader) { /* * We just copy the float constants that have been set by the application and rely on robustness * to return 0 on OOB reads. */ D3D9ConstantSets& constSet = m_consts[ShaderStage]; if (!constSet.dirty) return; constSet.dirty = false; uint32_t floatCount = constSet.maxChangedConstF; if (constSet.meta.needsConstantCopies) { // If the shader requires us to preserve shader defined constants, // we copy those over. We need to adjust the amount of used floats accordingly. auto shader = GetCommonShader(Shader); floatCount = std::max(floatCount, shader->GetMaxDefinedConstant() + 1); } // If we statically know which is the last float constant accessed by the shader, we don't need to copy the rest. floatCount = std::min(constSet.meta.maxConstIndexF, floatCount); // There are very few int constants, so we put those into the same buffer at the start. // We always allocate memory for all possible int constants to make sure alignment works out. const uint32_t intRange = caps::MaxOtherConstants * sizeof(Vector4i); uint32_t floatDataSize = floatCount * sizeof(Vector4); // Determine amount of floats and buffer size based on highest used float constant and alignment const uint32_t alignment = constSet.buffer.GetAlignment(); const uint32_t bufferSize = align(std::max(floatDataSize + intRange, alignment), alignment); floatDataSize = bufferSize - intRange; void* mapPtr = constSet.buffer.Alloc(bufferSize); auto* dst = reinterpret_cast(mapPtr); const uint32_t intDataSize = constSet.meta.maxConstIndexI * sizeof(Vector4i); if (constSet.meta.maxConstIndexI != 0) std::memcpy(dst->iConsts, Src.iConsts, intDataSize); if (constSet.meta.maxConstIndexF != 0) std::memcpy(dst->fConsts, Src.fConsts, floatDataSize); if (constSet.meta.needsConstantCopies) { // Copy shader defined constants over so they can be accessed // with relative addressing. Vector4* data = reinterpret_cast(dst->fConsts); auto& shaderConsts = GetCommonShader(Shader)->GetConstants(); for (const auto& constant : shaderConsts) { if (constant.uboIdx < constSet.meta.maxConstIndexF) data[constant.uboIdx] = *reinterpret_cast(constant.float32); } } } template void D3D9DeviceEx::UploadConstants() { if constexpr (ShaderStage == DxsoProgramTypes::VertexShader) { if (CanSWVP()) return UploadSoftwareConstantSet(m_state.vsConsts.get(), m_consts[ShaderStage].layout); else return UploadConstantSet(m_state.vsConsts.get(), m_consts[ShaderStage].layout, m_state.vertexShader); } else { return UploadConstantSet (m_state.psConsts.get(), m_consts[ShaderStage].layout, m_state.pixelShader); } } void D3D9DeviceEx::UpdateClipPlanes() { m_flags.clr(D3D9DeviceFlag::DirtyClipPlanes); auto mapPtr = m_vsClipPlanes.AllocSlice(); auto dst = reinterpret_cast(mapPtr); uint32_t clipPlaneCount = 0u; for (uint32_t i = 0; i < caps::MaxClipPlanes; i++) { D3D9ClipPlane clipPlane = (m_state.renderStates[D3DRS_CLIPPLANEENABLE] & (1 << i)) ? m_state.clipPlanes[i] : D3D9ClipPlane(); if (clipPlane != D3D9ClipPlane()) dst[clipPlaneCount++] = clipPlane; } // Write the rest to 0 for GPL. for (uint32_t i = clipPlaneCount; i < caps::MaxClipPlanes; i++) dst[i] = D3D9ClipPlane(); if (m_specInfo.set(clipPlaneCount)) m_flags.set(D3D9DeviceFlag::DirtySpecializationEntries); } template void D3D9DeviceEx::UpdatePushConstant(const void* pData) { struct ConstantData { uint8_t Data[Length]; }; const ConstantData* constData = reinterpret_cast(pData); EmitCs([ cData = *constData ](DxvkContext* ctx) { ctx->pushConstants(Offset, Length, &cData); }); } template void D3D9DeviceEx::UpdatePushConstant() { auto& rs = m_state.renderStates; if constexpr (Item == D3D9RenderStateItem::AlphaRef) { uint32_t alpha = rs[D3DRS_ALPHAREF] & 0xFF; UpdatePushConstant(&alpha); } else if constexpr (Item == D3D9RenderStateItem::FogColor) { Vector4 color; DecodeD3DCOLOR(D3DCOLOR(rs[D3DRS_FOGCOLOR]), color.data); UpdatePushConstant(&color); } else if constexpr (Item == D3D9RenderStateItem::FogDensity) { float density = bit::cast(rs[D3DRS_FOGDENSITY]); UpdatePushConstant(&density); } else if constexpr (Item == D3D9RenderStateItem::FogEnd) { float end = bit::cast(rs[D3DRS_FOGEND]); UpdatePushConstant(&end); } else if constexpr (Item == D3D9RenderStateItem::FogScale) { float end = bit::cast(rs[D3DRS_FOGEND]); float start = bit::cast(rs[D3DRS_FOGSTART]); float scale = 1.0f / (end - start); UpdatePushConstant(&scale); } else if constexpr (Item == D3D9RenderStateItem::PointSize) { UpdatePushConstant(&rs[D3DRS_POINTSIZE]); } else if constexpr (Item == D3D9RenderStateItem::PointSizeMin) { UpdatePushConstant(&rs[D3DRS_POINTSIZE_MIN]); } else if constexpr (Item == D3D9RenderStateItem::PointSizeMax) { UpdatePushConstant(&rs[D3DRS_POINTSIZE_MAX]); } else if constexpr (Item == D3D9RenderStateItem::PointScaleA) { float scale = bit::cast(rs[D3DRS_POINTSCALE_A]); scale /= float(m_state.viewport.Height * m_state.viewport.Height); UpdatePushConstant(&scale); } else if constexpr (Item == D3D9RenderStateItem::PointScaleB) { float scale = bit::cast(rs[D3DRS_POINTSCALE_B]); scale /= float(m_state.viewport.Height * m_state.viewport.Height); UpdatePushConstant(&scale); } else if constexpr (Item == D3D9RenderStateItem::PointScaleC) { float scale = bit::cast(rs[D3DRS_POINTSCALE_C]); scale /= float(m_state.viewport.Height * m_state.viewport.Height); UpdatePushConstant(&scale); } else Logger::warn("D3D9: Invalid push constant set to update."); } template void D3D9DeviceEx::ExecuteFlush() { D3D9DeviceLock lock = LockDevice(); if constexpr (Synchronize9On12) m_submitStatus.result = VK_NOT_READY; // Update signaled staging buffer counter and signal the fence m_stagingMemorySignaled = m_stagingBuffer.getStatistics().allocatedTotal; // Add commands to flush the threaded // context, then flush the command list uint64_t submissionId = ++m_submissionId; EmitCs([ cSubmissionFence = m_submissionFence, cSubmissionId = submissionId, cSubmissionStatus = Synchronize9On12 ? &m_submitStatus : nullptr, cStagingBufferFence = m_stagingBufferFence, cStagingBufferAllocated = m_stagingMemorySignaled ] (DxvkContext* ctx) { ctx->signal(cSubmissionFence, cSubmissionId); ctx->signal(cStagingBufferFence, cStagingBufferAllocated); ctx->flushCommandList(nullptr, cSubmissionStatus); }); FlushCsChunk(); m_flushSeqNum = m_csSeqNum; m_flushTracker.notifyFlush(m_flushSeqNum, submissionId); // If necessary, block calling thread until the // Vulkan queue submission is performed. if constexpr (Synchronize9On12) m_dxvkDevice->waitForSubmission(&m_submitStatus); // Notify the device that the context has been flushed, // this resets some resource initialization heuristics. m_initializer->NotifyContextFlush(); } void D3D9DeviceEx::Flush() { ExecuteFlush(); } void D3D9DeviceEx::FlushAndSync9On12() { ExecuteFlush(); } void D3D9DeviceEx::BeginFrame(Rc LatencyTracker, uint64_t FrameId) { D3D9DeviceLock lock = LockDevice(); EmitCs([ cTracker = std::move(LatencyTracker), cFrameId = FrameId ] (DxvkContext* ctx) { if (cTracker && cTracker->needsAutoMarkers()) ctx->beginLatencyTracking(cTracker, cFrameId); }); } void D3D9DeviceEx::EndFrame(Rc LatencyTracker) { D3D9DeviceLock lock = LockDevice(); EmitCs([ cTracker = std::move(LatencyTracker) ] (DxvkContext* ctx) { ctx->endFrame(); if (cTracker && cTracker->needsAutoMarkers()) ctx->endLatencyTracking(cTracker); }); } inline void D3D9DeviceEx::UpdateActiveRTs(uint32_t index) { const uint32_t bit = 1 << index; m_activeRTsWhichAreTextures &= ~bit; if (HasRenderTargetBound(index) && m_state.renderTargets[index]->GetBaseTexture() != nullptr && m_state.renderStates[ColorWriteIndex(index)] != 0) m_activeRTsWhichAreTextures |= bit; UpdateActiveHazardsRT(bit); } template inline void D3D9DeviceEx::UpdateAnyColorWrites() { // The 0th RT is always bound. bool bound = HasRenderTargetBound(Index); if (Index == 0 || bound) { if (bound) { m_flags.set(D3D9DeviceFlag::DirtyFramebuffer); } UpdateActiveRTs(Index); } } inline void D3D9DeviceEx::UpdateTextureBitmasks(uint32_t index, DWORD combinedUsage) { const uint32_t bit = 1 << index; m_activeTextureRTs &= ~bit; m_activeTextureDSs &= ~bit; m_activeTextures &= ~bit; m_activeTexturesToUpload &= ~bit; m_activeTexturesToGen &= ~bit; m_mismatchingTextureTypes &= ~bit; auto tex = GetCommonTexture(m_state.textures[index]); if (tex != nullptr) { m_activeTextures |= bit; if (unlikely(tex->IsRenderTarget())) m_activeTextureRTs |= bit; if (unlikely(tex->IsDepthStencil())) m_activeTextureDSs |= bit; if (unlikely(tex->NeedsAnyUpload())) m_activeTexturesToUpload |= bit; if (unlikely(tex->NeedsMipGen())) m_activeTexturesToGen |= bit; // Update shadow sampler mask const bool oldDepth = m_depthTextures & bit; const bool newDepth = tex->IsShadow(); if (oldDepth != newDepth) { m_depthTextures ^= bit; m_dirtySamplerStates |= bit; } // Update dref clamp mask m_drefClamp &= ~bit; m_drefClamp |= uint32_t(tex->IsUpgradedToD32f()) << index; // Update non-seamless cubemap mask const bool oldCube = m_cubeTextures & bit; const bool newCube = tex->GetType() == D3DRTYPE_CUBETEXTURE; if (oldCube != newCube) { m_cubeTextures ^= bit; m_dirtySamplerStates |= bit; } if (unlikely(m_fetch4Enabled & bit)) UpdateActiveFetch4(index); UpdateTextureTypeMismatchesForTexture(index); } else { if (unlikely(m_fetch4 & bit)) UpdateActiveFetch4(index); } if (unlikely(combinedUsage & D3DUSAGE_RENDERTARGET)) UpdateActiveHazardsRT(bit); if (unlikely(combinedUsage & D3DUSAGE_DEPTHSTENCIL)) UpdateActiveHazardsDS(bit); } inline void D3D9DeviceEx::UpdateActiveHazardsRT(uint32_t texMask) { auto masks = m_psShaderMasks; masks.rtMask &= m_activeRTsWhichAreTextures; masks.samplerMask &= m_activeTextureRTs & texMask; m_activeHazardsRT = m_activeHazardsRT & (~texMask); for (uint32_t rtIdx : bit::BitMask(masks.rtMask)) { for (uint32_t samplerIdx : bit::BitMask(masks.samplerMask)) { D3D9Surface* rtSurf = m_state.renderTargets[rtIdx].ptr(); IDirect3DBaseTexture9* rtBase = rtSurf->GetBaseTexture(); IDirect3DBaseTexture9* texBase = m_state.textures[samplerIdx]; // HACK: Don't mark for hazards if we aren't rendering to mip 0! // Some games use screenspace passes like this for blurring // Sampling from mip 0 (texture) -> mip 1 (rt) // and we'd trigger the hazard path otherwise which is unnecessary, // and would shove us into GENERAL and emitting readback barriers. if (likely(rtSurf->GetMipLevel() != 0 || rtBase != texBase)) continue; m_activeHazardsRT |= 1 << samplerIdx; } } } inline void D3D9DeviceEx::UpdateActiveHazardsDS(uint32_t texMask) { auto masks = m_psShaderMasks; masks.samplerMask &= m_activeTextureDSs & texMask; m_activeHazardsDS = m_activeHazardsDS & (~texMask); if (m_state.depthStencil != nullptr && m_state.depthStencil->GetBaseTexture() != nullptr) { for (uint32_t samplerIdx : bit::BitMask(masks.samplerMask)) { IDirect3DBaseTexture9* dsBase = m_state.depthStencil->GetBaseTexture(); IDirect3DBaseTexture9* texBase = m_state.textures[samplerIdx]; if (likely(dsBase != texBase)) continue; m_activeHazardsDS |= 1 << samplerIdx; } } } void D3D9DeviceEx::MarkRenderHazards() { struct { uint8_t RT : 1; uint8_t DS : 1; } hazardState; hazardState.RT = m_activeHazardsRT != 0; hazardState.DS = m_activeHazardsDS != 0; EmitCs([ cHazardState = hazardState ](DxvkContext* ctx) { VkPipelineStageFlags srcStages = 0; VkAccessFlags srcAccess = 0; if (cHazardState.RT != 0) { srcStages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; srcAccess |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; } if (cHazardState.DS != 0) { srcStages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; srcAccess |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; } ctx->emitGraphicsBarrier( srcStages, srcAccess, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT); }); for (uint32_t samplerIdx : bit::BitMask(m_activeHazardsRT)) { // Guaranteed to not be nullptr... auto tex = GetCommonTexture(m_state.textures[samplerIdx]); if (unlikely(!tex->MarkTransitionedToHazardLayout())) { TransitionImage(tex, m_hazardLayout); m_flags.set(D3D9DeviceFlag::DirtyFramebuffer); } } bool zWriteEnabled = m_state.renderStates[D3DRS_ZWRITEENABLE]; if (m_activeHazardsDS != 0 && zWriteEnabled) { // Guaranteed to not be nullptr... auto tex = m_state.depthStencil->GetCommonTexture(); if (unlikely(!tex->MarkTransitionedToHazardLayout())) { TransitionImage(tex, m_hazardLayout); m_flags.set(D3D9DeviceFlag::DirtyFramebuffer); } } } void D3D9DeviceEx::UpdateActiveFetch4(uint32_t stateSampler) { auto& state = m_state.samplerStates; const uint32_t samplerBit = 1u << stateSampler; auto texture = GetCommonTexture(m_state.textures[stateSampler]); const bool textureSupportsFetch4 = texture != nullptr && texture->SupportsFetch4(); const bool fetch4Enabled = m_fetch4Enabled & samplerBit; const bool pointSampled = state[stateSampler][D3DSAMP_MAGFILTER] == D3DTEXF_POINT; const bool shouldFetch4 = fetch4Enabled && textureSupportsFetch4 && pointSampled; if (unlikely(shouldFetch4 != !!(m_fetch4 & samplerBit))) { if (shouldFetch4) m_fetch4 |= samplerBit; else m_fetch4 &= ~samplerBit; } } void D3D9DeviceEx::UploadManagedTexture(D3D9CommonTexture* pResource) { for (uint32_t subresource = 0; subresource < pResource->CountSubresources(); subresource++) { if (!pResource->NeedsUpload(subresource)) continue; this->FlushImage(pResource, subresource); } pResource->ClearDirtyBoxes(); pResource->ClearNeedsUpload(); } void D3D9DeviceEx::UploadManagedTextures(uint32_t mask) { // Guaranteed to not be nullptr... for (uint32_t texIdx : bit::BitMask(mask)) UploadManagedTexture(GetCommonTexture(m_state.textures[texIdx])); m_activeTexturesToUpload &= ~mask; } void D3D9DeviceEx::UpdateTextureTypeMismatchesForShader(const D3D9CommonShader* shader, uint32_t shaderSamplerMask, uint32_t shaderSamplerOffset) { const uint32_t stageCorrectedShaderSamplerMask = shaderSamplerMask << shaderSamplerOffset; if (unlikely(shader->GetInfo().majorVersion() < 2 || m_d3d9Options.forceSamplerTypeSpecConstants)) { // SM 1 shaders don't define the texture type in the shader. // We always use spec constants for those. m_dirtyTextures |= stageCorrectedShaderSamplerMask & m_mismatchingTextureTypes; m_mismatchingTextureTypes &= ~stageCorrectedShaderSamplerMask; return; } for (const uint32_t i : bit::BitMask(stageCorrectedShaderSamplerMask)) { const D3D9CommonTexture* texture = GetCommonTexture(m_state.textures[i]); if (unlikely(texture == nullptr)) { // Unbound textures are not mismatching texture types m_dirtyTextures |= m_mismatchingTextureTypes & (1 << i); m_mismatchingTextureTypes &= ~(1 << i); continue; } VkImageViewType boundViewType = D3D9CommonTexture::GetImageViewTypeFromResourceType(texture->GetType(), D3D9CommonTexture::AllLayers); VkImageViewType shaderViewType = shader->GetImageViewType(i - shaderSamplerOffset); if (unlikely(boundViewType != shaderViewType)) { m_dirtyTextures |= 1 << i; m_mismatchingTextureTypes |= 1 << i; } else { // The texture type is no longer mismatching, make sure we bind the texture now. m_dirtyTextures |= m_mismatchingTextureTypes & (1 << i); m_mismatchingTextureTypes &= ~(1 << i); } } } void D3D9DeviceEx::UpdateTextureTypeMismatchesForTexture(uint32_t stateSampler) { uint32_t shaderTextureIndex; const D3D9CommonShader* shader; if (likely(IsPSSampler(stateSampler))) { shader = GetCommonShader(m_state.pixelShader); shaderTextureIndex = stateSampler; } else if (unlikely(IsVSSampler(stateSampler))) { shader = GetCommonShader(m_state.vertexShader); shaderTextureIndex = stateSampler - caps::MaxTexturesPS - 1; } else { // Do not type check the fixed function displacement map texture. return; } if (unlikely(shader == nullptr || shader->GetInfo().majorVersion() < 2 || m_d3d9Options.forceSamplerTypeSpecConstants)) { // This function only gets called by UpdateTextureBitmasks // which clears the dirty and mismatching bits for the texture before anyway. return; } const D3D9CommonTexture* tex = GetCommonTexture(m_state.textures[stateSampler]); VkImageViewType boundViewType = D3D9CommonTexture::GetImageViewTypeFromResourceType(tex->GetType(), D3D9CommonTexture::AllLayers); VkImageViewType shaderViewType = shader->GetImageViewType(shaderTextureIndex); // D3D9 does not have 1D textures. The value of VIEW_TYPE_1D is 0 // which is the default when there is no declaration for the type. bool shaderUsesTexture = shaderViewType != VkImageViewType(0); if (unlikely(boundViewType != shaderViewType && shaderUsesTexture)) { const uint32_t samplerBit = 1u << stateSampler; m_mismatchingTextureTypes |= samplerBit; } } void D3D9DeviceEx::GenerateTextureMips(uint32_t mask) { for (uint32_t texIdx : bit::BitMask(mask)) { // Guaranteed to not be nullptr... auto texInfo = GetCommonTexture(m_state.textures[texIdx]); if (likely(texInfo->NeedsMipGen())) { this->EmitGenerateMips(texInfo); if (likely(!IsTextureBoundAsAttachment(texInfo))) { texInfo->SetNeedsMipGen(false); } } } m_activeTexturesToGen &= ~mask; } void D3D9DeviceEx::MarkTextureMipsDirty(D3D9CommonTexture* pResource) { pResource->SetNeedsMipGen(true); for (uint32_t i : bit::BitMask(m_activeTextures)) { // Guaranteed to not be nullptr... auto texInfo = GetCommonTexture(m_state.textures[i]); if (texInfo == pResource) { m_activeTexturesToGen |= 1 << i; // We can early out here, no need to add another index for this. break; } } } void D3D9DeviceEx::MarkTextureMipsUnDirty(D3D9CommonTexture* pResource) { if (likely(!IsTextureBoundAsAttachment(pResource))) { // We need to keep the texture marked as needing mipmap generation because we don't set that when rendering. pResource->SetNeedsMipGen(false); for (uint32_t i : bit::BitMask(m_activeTextures)) { // Guaranteed to not be nullptr... auto texInfo = GetCommonTexture(m_state.textures[i]); if (unlikely(texInfo == pResource)) { m_activeTexturesToGen &= ~(1 << i); } } } } void D3D9DeviceEx::MarkTextureUploaded(D3D9CommonTexture* pResource) { for (uint32_t i : bit::BitMask(m_activeTextures)) { // Guaranteed to not be nullptr... auto texInfo = GetCommonTexture(m_state.textures[i]); if (texInfo == pResource) m_activeTexturesToUpload &= ~(1 << i); } } void D3D9DeviceEx::UpdatePointMode(bool pointList) { if (!pointList) { UpdatePointModeSpec(0); return; } auto& rs = m_state.renderStates; const bool scale = rs[D3DRS_POINTSCALEENABLE] && !UseProgrammableVS(); const bool sprite = rs[D3DRS_POINTSPRITEENABLE]; const uint32_t scaleBit = scale ? 1u : 0u; const uint32_t spriteBit = sprite ? 2u : 0u; uint32_t mode = scaleBit | spriteBit; if (rs[D3DRS_POINTSCALEENABLE] && m_flags.test(D3D9DeviceFlag::DirtyPointScale)) { m_flags.clr(D3D9DeviceFlag::DirtyPointScale); UpdatePushConstant(); UpdatePushConstant(); UpdatePushConstant(); } UpdatePointModeSpec(mode); } void D3D9DeviceEx::UpdateFog() { auto& rs = m_state.renderStates; bool fogEnabled = rs[D3DRS_FOGENABLE]; bool pixelFog = rs[D3DRS_FOGTABLEMODE] != D3DFOG_NONE && fogEnabled; bool vertexFog = rs[D3DRS_FOGVERTEXMODE] != D3DFOG_NONE && fogEnabled && !pixelFog; auto UpdateFogConstants = [&](D3DFOGMODE FogMode) { if (m_flags.test(D3D9DeviceFlag::DirtyFogColor)) { m_flags.clr(D3D9DeviceFlag::DirtyFogColor); UpdatePushConstant(); } if (FogMode == D3DFOG_LINEAR) { if (m_flags.test(D3D9DeviceFlag::DirtyFogScale)) { m_flags.clr(D3D9DeviceFlag::DirtyFogScale); UpdatePushConstant(); } if (m_flags.test(D3D9DeviceFlag::DirtyFogEnd)) { m_flags.clr(D3D9DeviceFlag::DirtyFogEnd); UpdatePushConstant(); } } else if (FogMode == D3DFOG_EXP || FogMode == D3DFOG_EXP2) { if (m_flags.test(D3D9DeviceFlag::DirtyFogDensity)) { m_flags.clr(D3D9DeviceFlag::DirtyFogDensity); UpdatePushConstant(); } } }; if (vertexFog) { D3DFOGMODE mode = D3DFOGMODE(rs[D3DRS_FOGVERTEXMODE]); UpdateFogConstants(mode); if (m_flags.test(D3D9DeviceFlag::DirtyFogState)) { m_flags.clr(D3D9DeviceFlag::DirtyFogState); UpdateFogModeSpec(true, mode, D3DFOG_NONE); } } else if (pixelFog) { D3DFOGMODE mode = D3DFOGMODE(rs[D3DRS_FOGTABLEMODE]); UpdateFogConstants(mode); if (m_flags.test(D3D9DeviceFlag::DirtyFogState)) { m_flags.clr(D3D9DeviceFlag::DirtyFogState); UpdateFogModeSpec(true, D3DFOG_NONE, mode); } } else { if (fogEnabled) UpdateFogConstants(D3DFOG_NONE); if (m_flags.test(D3D9DeviceFlag::DirtyFogState)) { m_flags.clr(D3D9DeviceFlag::DirtyFogState); UpdateFogModeSpec(fogEnabled, D3DFOG_NONE, D3DFOG_NONE); } } } void D3D9DeviceEx::BindFramebuffer() { m_flags.clr(D3D9DeviceFlag::DirtyFramebuffer); DxvkRenderTargets attachments; bool srgb = m_state.renderStates[D3DRS_SRGBWRITEENABLE]; // D3D9 doesn't have the concept of a framebuffer object, // so we'll just create a new one every time the render // target bindings are updated. Set up the attachments. VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM; // Some games break if render targets that get disabled using the color write mask // end up shrinking the render area. So we don't bind those. // (This impacted Dead Space 1.) // But we want to minimize frame buffer changes because those // break up the current render pass. So we dont unbind for disabled color write masks // if the RT has the same size or is bigger than the smallest active RT. uint32_t boundMask = 0u; uint32_t anyColorWriteMask = 0u; uint32_t limitsRenderAreaMask = 0u; VkExtent2D renderArea = { ~0u, ~0u }; for (uint32_t i = 0u; i < m_state.renderTargets.size(); i++) { if (!HasRenderTargetBound(i)) continue; const DxvkImageCreateInfo& rtImageInfo = m_state.renderTargets[i]->GetCommonTexture()->GetImage()->info(); // Dont bind it if the sample count doesnt match if (likely(sampleCount == VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM)) sampleCount = rtImageInfo.sampleCount; else if (unlikely(sampleCount != rtImageInfo.sampleCount)) continue; // Dont bind it if the pixel shader doesnt write to it if (!(m_psShaderMasks.rtMask & (1 << i))) continue; boundMask |= 1 << i; VkExtent2D rtExtent = m_state.renderTargets[i]->GetSurfaceExtent(); bool rtLimitsRenderArea = rtExtent.width < renderArea.width || rtExtent.height < renderArea.height; limitsRenderAreaMask |= rtLimitsRenderArea << i; // It will only get bound if its not smaller than the others. // So RTs with a disabled color write mask will never impact the render area. if (m_state.renderStates[ColorWriteIndex(i)] == 0) continue; anyColorWriteMask |= 1 << i; if (rtExtent.width < renderArea.width && rtExtent.height < renderArea.height) { // It's smaller on both axis, so the previous RTs no longer limit the size limitsRenderAreaMask = 1 << i; } renderArea.width = std::min(renderArea.width, rtExtent.width); renderArea.height = std::min(renderArea.height, rtExtent.height); } bool dsvBound = false; if (m_state.depthStencil != nullptr) { // We only need to skip binding the DSV if it would shrink the render area // despite not being used, otherwise we might end up with unnecessary render pass spills bool anyDSStateEnabled = m_state.renderStates[D3DRS_ZENABLE] || m_state.renderStates[D3DRS_ZWRITEENABLE] || m_state.renderStates[D3DRS_STENCILENABLE] || m_state.renderStates[D3DRS_ADAPTIVETESS_X] == uint32_t(D3D9Format::NVDB); VkExtent2D dsvExtent = m_state.depthStencil->GetSurfaceExtent(); bool dsvLimitsRenderArea = dsvExtent.width < renderArea.width || dsvExtent.height < renderArea.height; const DxvkImageCreateInfo& dsImageInfo = m_state.depthStencil->GetCommonTexture()->GetImage()->info(); const bool sampleCountMatches = sampleCount == VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM || sampleCount == dsImageInfo.sampleCount; dsvBound = sampleCountMatches && (anyDSStateEnabled || !dsvLimitsRenderArea); if (sampleCountMatches && anyDSStateEnabled && dsvExtent.width < renderArea.width && dsvExtent.height < renderArea.height) { // It's smaller on both axis, so the previous RTs no longer limit the size limitsRenderAreaMask = 0u; } } // We only need to skip binding the RT if it would shrink the render area // despite not having color writes enabled, // otherwise we might end up with unnecessary render pass spills boundMask &= (anyColorWriteMask | ~limitsRenderAreaMask); for (uint32_t i : bit::BitMask(boundMask)) { attachments.color[i] = { m_state.renderTargets[i]->GetRenderTargetView(srgb), m_state.renderTargets[i]->GetRenderTargetLayout(m_hazardLayout) }; } if (dsvBound) { const bool depthWrite = m_state.renderStates[D3DRS_ZWRITEENABLE]; attachments.depth = { m_state.depthStencil->GetDepthStencilView(), m_state.depthStencil->GetDepthStencilLayout(depthWrite, m_activeHazardsDS != 0, m_hazardLayout) }; } VkImageAspectFlags feedbackLoopAspects = 0u; if (m_hazardLayout == VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT) { if (m_activeHazardsRT != 0) feedbackLoopAspects |= VK_IMAGE_ASPECT_COLOR_BIT; if (m_activeHazardsDS != 0 && attachments.depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL) feedbackLoopAspects |= VK_IMAGE_ASPECT_DEPTH_BIT; } // Create and bind the framebuffer object to the context EmitCs([ cAttachments = std::move(attachments), cFeedbackLoopAspects = feedbackLoopAspects ] (DxvkContext* ctx) mutable { ctx->bindRenderTargets(std::move(cAttachments), cFeedbackLoopAspects); }); } void D3D9DeviceEx::BindViewportAndScissor() { m_flags.clr(D3D9DeviceFlag::DirtyViewportScissor); // D3D9's coordinate system has its origin in the bottom left, // but the viewport coordinates are aligned to the top-left // corner so we can get away with flipping the viewport. const D3DVIEWPORT9& vp = m_state.viewport; // Correctness Factor for 1/2 texel offset constexpr float cf = 0.5f; // How much to bias MinZ by to avoid a depth // degenerate viewport. // Tests show that the bias is only applied below minZ values of 0.5 float zBias; if (vp.MinZ >= 0.5f) { zBias = 0.0f; } else { zBias = 0.001f; } DxvkViewport state = { }; state.viewport = VkViewport{ float(vp.X) + cf, float(vp.Height + vp.Y) + cf, float(vp.Width), -float(vp.Height), std::clamp(vp.MinZ, 0.0f, 1.0f), std::clamp(std::max(vp.MaxZ, vp.MinZ + zBias), 0.0f, 1.0f), }; // Scissor rectangles. Vulkan does not provide an easy way // to disable the scissor test, so we'll have to set scissor // rects that are at least as large as the framebuffer. bool enableScissorTest = m_state.renderStates[D3DRS_SCISSORTESTENABLE]; if (enableScissorTest) { RECT sr = m_state.scissorRect; VkOffset2D srPosA; srPosA.x = std::max(0, sr.left); srPosA.x = std::max(vp.X, srPosA.x); srPosA.y = std::max(0, sr.top); srPosA.y = std::max(vp.Y, srPosA.y); VkOffset2D srPosB; srPosB.x = std::max(srPosA.x, sr.right); srPosB.x = std::min(vp.X + vp.Width, srPosB.x); srPosB.y = std::max(srPosA.y, sr.bottom); srPosB.y = std::min(vp.Y + vp.Height, srPosB.y); VkExtent2D srSize; srSize.width = uint32_t(srPosB.x - srPosA.x); srSize.height = uint32_t(srPosB.y - srPosA.y); state.scissor = VkRect2D{ srPosA, srSize }; } else { state.scissor = VkRect2D{ VkOffset2D { int32_t(vp.X), int32_t(vp.Y) }, VkExtent2D { vp.Width, vp.Height }}; } EmitCs([ cViewport = state ] (DxvkContext* ctx) { ctx->setViewports(1, &cViewport); }); } void D3D9DeviceEx::BindMultiSampleState() { m_flags.clr(D3D9DeviceFlag::DirtyMultiSampleState); DxvkMultisampleState msState = { }; msState.setSampleMask(m_flags.test(D3D9DeviceFlag::ValidSampleMask) ? uint16_t(m_state.renderStates[D3DRS_MULTISAMPLEMASK]) : uint16_t(0xffffu)); msState.setAlphaToCoverage(IsAlphaToCoverageEnabled()); EmitCs([ cState = msState ] (DxvkContext* ctx) { ctx->setMultisampleState(cState); }); } void D3D9DeviceEx::BindBlendState() { m_flags.clr(D3D9DeviceFlag::DirtyBlendState); auto& state = m_state.renderStates; DxvkBlendMode mode = { }; mode.setBlendEnable(state[D3DRS_ALPHABLENDENABLE]); D3D9BlendState color = { }; color.Src = D3DBLEND(state[D3DRS_SRCBLEND]); color.Dst = D3DBLEND(state[D3DRS_DESTBLEND]); color.Op = D3DBLENDOP(state[D3DRS_BLENDOP]); FixupBlendState(color); D3D9BlendState alpha = color; if (state[D3DRS_SEPARATEALPHABLENDENABLE]) { alpha.Src = D3DBLEND(state[D3DRS_SRCBLENDALPHA]); alpha.Dst = D3DBLEND(state[D3DRS_DESTBLENDALPHA]); alpha.Op = D3DBLENDOP(state[D3DRS_BLENDOPALPHA]); FixupBlendState(alpha); } mode.setColorOp(DecodeBlendFactor(color.Src, false), DecodeBlendFactor(color.Dst, false), DecodeBlendOp(color.Op)); mode.setAlphaOp(DecodeBlendFactor(alpha.Src, true), DecodeBlendFactor(alpha.Dst, true), DecodeBlendOp(alpha.Op)); uint16_t writeMasks = 0; for (uint32_t i = 0; i < 4; i++) writeMasks |= (state[ColorWriteIndex(i)] & 0xfu) << (4u * i); EmitCs([ cMode = mode, cWriteMasks = writeMasks, cAlphaMasks = m_alphaSwizzleRTs ](DxvkContext* ctx) { for (uint32_t i = 0; i < 4; i++) { DxvkBlendMode mode = cMode; mode.setWriteMask(cWriteMasks >> (4u * i)); // Adjust the blend factor based on the render target alpha swizzle bit mask. // Specific formats such as the XRGB ones require a ONE swizzle for alpha // which cannot be directly applied with the image view of the attachment. if (cAlphaMasks & (1 << i)) { auto NormalizeFactor = [] (VkBlendFactor Factor) { if (Factor == VK_BLEND_FACTOR_DST_ALPHA) return VK_BLEND_FACTOR_ONE; else if (Factor == VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA) return VK_BLEND_FACTOR_ZERO; return Factor; }; mode.setColorOp(NormalizeFactor(mode.colorSrcFactor()), NormalizeFactor(mode.colorDstFactor()), mode.colorBlendOp()); mode.setAlphaOp(NormalizeFactor(mode.alphaSrcFactor()), NormalizeFactor(mode.alphaDstFactor()), mode.alphaBlendOp()); } mode.normalize(); ctx->setBlendMode(i, mode); } }); } void D3D9DeviceEx::BindBlendFactor() { DxvkBlendConstants blendConstants; DecodeD3DCOLOR( D3DCOLOR(m_state.renderStates[D3DRS_BLENDFACTOR]), reinterpret_cast(&blendConstants)); EmitCs([ cBlendConstants = blendConstants ](DxvkContext* ctx) { ctx->setBlendConstants(cBlendConstants); }); } void D3D9DeviceEx::BindDepthStencilState() { m_flags.clr(D3D9DeviceFlag::DirtyDepthStencilState); auto& rs = m_state.renderStates; bool stencil = rs[D3DRS_STENCILENABLE]; bool twoSidedStencil = stencil && rs[D3DRS_TWOSIDEDSTENCILMODE]; DxvkDepthStencilState state = { }; state.setDepthTest(rs[D3DRS_ZENABLE]); state.setDepthWrite(rs[D3DRS_ZWRITEENABLE]); state.setStencilTest(stencil); state.setDepthCompareOp(DecodeCompareOp(D3DCMPFUNC(rs[D3DRS_ZFUNC]))); DxvkStencilOp frontOp = { }; if (stencil) { frontOp.setFailOp(DecodeStencilOp(D3DSTENCILOP(rs[D3DRS_STENCILFAIL]))); frontOp.setPassOp(DecodeStencilOp(D3DSTENCILOP(rs[D3DRS_STENCILPASS]))); frontOp.setDepthFailOp(DecodeStencilOp(D3DSTENCILOP(rs[D3DRS_STENCILZFAIL]))); frontOp.setCompareOp(DecodeCompareOp(D3DCMPFUNC(rs[D3DRS_STENCILFUNC]))); frontOp.setCompareMask(rs[D3DRS_STENCILMASK]); frontOp.setWriteMask(rs[D3DRS_STENCILWRITEMASK]); } DxvkStencilOp backOp = frontOp; if (twoSidedStencil) { backOp.setFailOp(DecodeStencilOp(D3DSTENCILOP(rs[D3DRS_CCW_STENCILFAIL]))); backOp.setPassOp(DecodeStencilOp(D3DSTENCILOP(rs[D3DRS_CCW_STENCILPASS]))); backOp.setDepthFailOp(DecodeStencilOp(D3DSTENCILOP(rs[D3DRS_CCW_STENCILZFAIL]))); backOp.setCompareOp(DecodeCompareOp(D3DCMPFUNC(rs[D3DRS_CCW_STENCILFUNC]))); backOp.setCompareMask(rs[D3DRS_STENCILMASK]); backOp.setWriteMask(rs[D3DRS_STENCILWRITEMASK]); } state.setStencilOpFront(frontOp); state.setStencilOpBack(backOp); EmitCs([ cState = state ] (DxvkContext* ctx) mutable { cState.normalize(); ctx->setDepthStencilState(cState); }); } void D3D9DeviceEx::BindRasterizerState() { m_flags.clr(D3D9DeviceFlag::DirtyRasterizerState); auto& rs = m_state.renderStates; DxvkRasterizerState state = { }; state.setCullMode(DecodeCullMode(D3DCULL(rs[D3DRS_CULLMODE]))); state.setDepthBias(IsDepthBiasEnabled()); state.setDepthClip(true); state.setFrontFace(VK_FRONT_FACE_CLOCKWISE); state.setPolygonMode(DecodeFillMode(D3DFILLMODE(rs[D3DRS_FILLMODE]))); state.setFlatShading(m_state.renderStates[D3DRS_SHADEMODE] == D3DSHADE_FLAT); EmitCs([ cState = state ](DxvkContext* ctx) { ctx->setRasterizerState(cState); }); } void D3D9DeviceEx::BindDepthBias() { m_flags.clr(D3D9DeviceFlag::DirtyDepthBias); auto& rs = m_state.renderStates; float depthBias = bit::cast(rs[D3DRS_DEPTHBIAS]) * m_depthBiasScale; float slopeScaledDepthBias = bit::cast(rs[D3DRS_SLOPESCALEDEPTHBIAS]); DxvkDepthBias biases; biases.depthBiasConstant = depthBias; biases.depthBiasSlope = slopeScaledDepthBias; biases.depthBiasClamp = 0.0f; EmitCs([ cBiases = biases ](DxvkContext* ctx) { ctx->setDepthBias(cBiases); }); } uint32_t D3D9DeviceEx::GetAlphaTestPrecision() { if (m_state.renderTargets[0] == nullptr) return 0; D3D9Format format = m_state.renderTargets[0]->GetCommonTexture()->Desc()->Format; switch (format) { case D3D9Format::A2B10G10R10: case D3D9Format::A2R10G10B10: case D3D9Format::A2W10V10U10: case D3D9Format::A2B10G10R10_XR_BIAS: return 0x2; /* 10 bit */ case D3D9Format::R16F: case D3D9Format::G16R16F: case D3D9Format::A16B16G16R16F: return 0x7; /* 15 bit */ case D3D9Format::G16R16: case D3D9Format::A16B16G16R16: case D3D9Format::V16U16: case D3D9Format::L16: case D3D9Format::Q16W16V16U16: return 0x8; /* 16 bit */ case D3D9Format::R32F: case D3D9Format::G32R32F: case D3D9Format::A32B32G32R32F: return 0xF; /* float */ default: return 0x0; /* 8 bit */ } } void D3D9DeviceEx::BindAlphaTestState() { m_flags.clr(D3D9DeviceFlag::DirtyAlphaTestState); auto& rs = m_state.renderStates; VkCompareOp alphaOp = IsAlphaTestEnabled() ? DecodeCompareOp(D3DCMPFUNC(rs[D3DRS_ALPHAFUNC])) : VK_COMPARE_OP_ALWAYS; uint32_t precision = alphaOp != VK_COMPARE_OP_ALWAYS ? GetAlphaTestPrecision() : 0u; UpdateAlphaTestSpec(alphaOp, precision); } void D3D9DeviceEx::BindDepthStencilRefrence() { auto& rs = m_state.renderStates; uint32_t ref = uint32_t(rs[D3DRS_STENCILREF]) & 0xff; EmitCs([cRef = ref] (DxvkContext* ctx) { ctx->setStencilReference(cRef); }); } void D3D9DeviceEx::BindSampler(DWORD Sampler) { auto samplerInfo = RemapStateSamplerShader(Sampler); const uint32_t slot = computeResourceSlotId( samplerInfo.first, DxsoBindingType::Image, samplerInfo.second); m_samplerBindCount++; EmitCs([this, cSlot = slot, cState = D3D9SamplerInfo(m_state.samplerStates[Sampler]), cIsCube = bool(m_cubeTextures & (1u << Sampler)), cIsDepth = bool(m_depthTextures & (1u << Sampler)), cBindId = m_samplerBindCount ] (DxvkContext* ctx) { DxvkSamplerKey key = { }; key.setFilter( DecodeFilter(cState.minFilter), DecodeFilter(cState.magFilter), DecodeMipFilter(cState.mipFilter)); if (cIsCube) { key.setAddressModes( VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE); key.setLegacyCubeFilter(!m_d3d9Options.seamlessCubes); } else { key.setAddressModes( DecodeAddressMode(cState.addressU), DecodeAddressMode(cState.addressV), DecodeAddressMode(cState.addressW)); } key.setDepthCompare(cIsDepth, VK_COMPARE_OP_LESS_OR_EQUAL); if (cState.mipFilter) { // Anisotropic filtering doesn't make any sense with only one mip uint32_t anisotropy = cState.maxAnisotropy; if (cState.minFilter != D3DTEXF_ANISOTROPIC) anisotropy = 0u; if (m_d3d9Options.samplerAnisotropy != -1 && cState.minFilter > D3DTEXF_POINT) anisotropy = m_d3d9Options.samplerAnisotropy; key.setAniso(anisotropy); float lodBias = cState.mipLodBias; lodBias += m_d3d9Options.samplerLodBias; if (m_d3d9Options.clampNegativeLodBias) lodBias = std::max(lodBias, 0.0f); key.setLodRange(float(cState.maxMipLevel), 16.0f, lodBias); } if (key.u.p.hasBorder) DecodeD3DCOLOR(cState.borderColor, key.borderColor.float32); VkShaderStageFlags stage = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; ctx->bindResourceSampler(stage, cSlot, m_dxvkDevice->createSampler(key)); // Let the main thread know about current sampler stats uint64_t liveCount = m_dxvkDevice->getSamplerStats().liveCount; m_lastSamplerStats.store(liveCount | (cBindId << SamplerCountBits), std::memory_order_relaxed); }); } void D3D9DeviceEx::BindTexture(DWORD StateSampler) { auto shaderSampler = RemapStateSamplerShader(StateSampler); uint32_t slot = computeResourceSlotId(shaderSampler.first, DxsoBindingType::Image, uint32_t(shaderSampler.second)); const bool srgb = m_state.samplerStates[StateSampler][D3DSAMP_SRGBTEXTURE] & 0x1; D3D9CommonTexture* commonTex = GetCommonTexture(m_state.textures[StateSampler]); EmitCs([ cSlot = slot, cImageView = commonTex->GetSampleView(srgb) ](DxvkContext* ctx) mutable { VkShaderStageFlags stage = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; ctx->bindResourceImageView(stage, cSlot, std::move(cImageView)); }); } void D3D9DeviceEx::UnbindTextures(uint32_t mask) { EmitCs([ cMask = mask ](DxvkContext* ctx) { for (uint32_t i : bit::BitMask(cMask)) { auto shaderSampler = RemapStateSamplerShader(i); uint32_t slot = computeResourceSlotId(shaderSampler.first, DxsoBindingType::Image, uint32_t(shaderSampler.second)); VkShaderStageFlags stage = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; ctx->bindResourceImageView(stage, slot, nullptr); } }); } void D3D9DeviceEx::UndirtySamplers(uint32_t mask) { EnsureSamplerLimit(); for (uint32_t i : bit::BitMask(mask)) BindSampler(i); m_dirtySamplerStates &= ~mask; } void D3D9DeviceEx::UndirtyTextures(uint32_t usedMask) { const uint32_t activeMask = usedMask & (m_activeTextures & ~m_mismatchingTextureTypes); const uint32_t inactiveMask = usedMask & (~m_activeTextures | m_mismatchingTextureTypes); for (uint32_t i : bit::BitMask(activeMask)) BindTexture(i); if (inactiveMask) UnbindTextures(inactiveMask); m_dirtyTextures &= ~usedMask; } void D3D9DeviceEx::MarkTextureBindingDirty(IDirect3DBaseTexture9* texture) { D3D9DeviceLock lock = LockDevice(); for (uint32_t i : bit::BitMask(m_activeTextures)) { if (m_state.textures[i] == texture) m_dirtyTextures |= 1u << i; } } D3D9DrawInfo D3D9DeviceEx::GenerateDrawInfo( D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, UINT InstanceCount) { D3D9DrawInfo drawInfo; drawInfo.vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount); drawInfo.instanceCount = (m_iaState.streamsInstanced & m_iaState.streamsUsed) ? InstanceCount : 1u; return drawInfo; } uint32_t D3D9DeviceEx::GetInstanceCount() const { return std::max(m_state.streamFreq[0] & 0x7FFFFFu, 1u); } void D3D9DeviceEx::PrepareDraw(D3DPRIMITIVETYPE PrimitiveType, bool UploadVBOs, bool UploadIBO) { if (unlikely(m_activeHazardsRT != 0 || m_activeHazardsDS != 0)) MarkRenderHazards(); if (unlikely((!m_lastHazardsDS) != (!m_activeHazardsDS)) || unlikely((!m_lastHazardsRT) != (!m_activeHazardsRT))) { m_flags.set(D3D9DeviceFlag::DirtyFramebuffer); m_lastHazardsDS = m_activeHazardsDS; m_lastHazardsRT = m_activeHazardsRT; } if (likely(UploadVBOs)) { const uint32_t usedBuffersMask = m_state.vertexDecl != nullptr ? m_state.vertexDecl->GetStreamMask() : ~0u; const uint32_t buffersToUpload = m_activeVertexBuffersToUpload & usedBuffersMask; for (uint32_t bufferIdx : bit::BitMask(buffersToUpload)) { auto* vbo = GetCommonBuffer(m_state.vertexBuffers[bufferIdx].vertexBuffer); if (likely(vbo != nullptr && vbo->NeedsUpload())) FlushBuffer(vbo); } m_activeVertexBuffersToUpload &= ~buffersToUpload; } const uint32_t usedSamplerMask = m_psShaderMasks.samplerMask | m_vsShaderMasks.samplerMask; const uint32_t usedTextureMask = m_activeTextures & usedSamplerMask; const uint32_t texturesToUpload = m_activeTexturesToUpload & usedTextureMask; if (unlikely(texturesToUpload != 0)) UploadManagedTextures(texturesToUpload); const uint32_t texturesToGen = m_activeTexturesToGen & usedTextureMask; if (unlikely(texturesToGen != 0)) GenerateTextureMips(texturesToGen); auto* ibo = GetCommonBuffer(m_state.indices); if (unlikely(UploadIBO && ibo != nullptr && ibo->NeedsUpload())) FlushBuffer(ibo); UpdateFog(); if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyFramebuffer))) BindFramebuffer(); if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyViewportScissor))) BindViewportAndScissor(); const uint32_t activeDirtySamplers = m_dirtySamplerStates & usedTextureMask; if (unlikely(activeDirtySamplers)) UndirtySamplers(activeDirtySamplers); const uint32_t usedDirtyTextures = m_dirtyTextures & usedSamplerMask; if (likely(usedDirtyTextures)) UndirtyTextures(usedDirtyTextures); if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyBlendState))) BindBlendState(); if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyDepthStencilState))) BindDepthStencilState(); if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyRasterizerState))) BindRasterizerState(); if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyDepthBias))) BindDepthBias(); if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyMultiSampleState))) BindMultiSampleState(); if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyAlphaTestState))) BindAlphaTestState(); if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyClipPlanes))) UpdateClipPlanes(); UpdatePointMode(PrimitiveType == D3DPT_POINTLIST); if (likely(UseProgrammableVS())) { if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyProgVertexShader))) { m_flags.set(D3D9DeviceFlag::DirtyInputLayout); BindShader( GetCommonShader(m_state.vertexShader)); } UploadConstants(); if (likely(!CanSWVP())) { UpdateVertexBoolSpec( m_state.vsConsts->bConsts[0] & m_consts[DxsoProgramType::VertexShader].meta.boolConstantMask); } else UpdateVertexBoolSpec(0); } else { UpdateVertexBoolSpec(0); UpdateFixedFunctionVS(); } if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyInputLayout))) BindInputLayout(); if (likely(UseProgrammablePS())) { UploadConstants(); const uint32_t psTextureMask = usedTextureMask & ((1u << caps::MaxTexturesPS) - 1u); const uint32_t fetch4 = m_fetch4 & psTextureMask; const uint32_t projected = m_projectionBitfield & psTextureMask; const auto& programInfo = GetCommonShader(m_state.pixelShader)->GetInfo(); if (programInfo.majorVersion() >= 2) UpdatePixelShaderSamplerSpec(m_d3d9Options.forceSamplerTypeSpecConstants ? m_textureTypes : 0u, 0u, fetch4); else UpdatePixelShaderSamplerSpec(m_textureTypes, programInfo.minorVersion() >= 4 ? 0u : projected, fetch4); // For implicit samplers... UpdatePixelBoolSpec( m_state.psConsts->bConsts[0] & m_consts[DxsoProgramType::PixelShader].meta.boolConstantMask); } else { UpdatePixelBoolSpec(0); UpdatePixelShaderSamplerSpec(0u, 0u, 0u); UpdateFixedFunctionPS(); } const uint32_t nullTextureMask = usedSamplerMask & ~usedTextureMask; const uint32_t depthTextureMask = m_depthTextures & usedTextureMask; const uint32_t drefClampMask = m_drefClamp & depthTextureMask; UpdateCommonSamplerSpec(nullTextureMask, depthTextureMask, drefClampMask); if (unlikely(m_flags.test(D3D9DeviceFlag::DirtySharedPixelShaderData))) { m_flags.clr(D3D9DeviceFlag::DirtySharedPixelShaderData); auto mapPtr = m_psShared.AllocSlice(); D3D9SharedPS* data = reinterpret_cast(mapPtr); for (uint32_t i = 0; i < caps::TextureStageCount; i++) { DecodeD3DCOLOR(D3DCOLOR(m_state.textureStages[i][DXVK_TSS_CONSTANT]), data->Stages[i].Constant); // Flip major-ness so we can get away with a nice easy // dot in the shader without complex access data->Stages[i].BumpEnvMat[0][0] = bit::cast(m_state.textureStages[i][DXVK_TSS_BUMPENVMAT00]); data->Stages[i].BumpEnvMat[1][0] = bit::cast(m_state.textureStages[i][DXVK_TSS_BUMPENVMAT01]); data->Stages[i].BumpEnvMat[0][1] = bit::cast(m_state.textureStages[i][DXVK_TSS_BUMPENVMAT10]); data->Stages[i].BumpEnvMat[1][1] = bit::cast(m_state.textureStages[i][DXVK_TSS_BUMPENVMAT11]); data->Stages[i].BumpEnvLScale = bit::cast(m_state.textureStages[i][DXVK_TSS_BUMPENVLSCALE]); data->Stages[i].BumpEnvLOffset = bit::cast(m_state.textureStages[i][DXVK_TSS_BUMPENVLOFFSET]); } } if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyDepthBounds))) { m_flags.clr(D3D9DeviceFlag::DirtyDepthBounds); DxvkDepthBounds db = { }; db.enableDepthBounds = (m_state.renderStates[D3DRS_ADAPTIVETESS_X] == uint32_t(D3D9Format::NVDB)); if (db.enableDepthBounds) { db.minDepthBounds = std::clamp(bit::cast(m_state.renderStates[D3DRS_ADAPTIVETESS_Z]), 0.0f, 1.0f); db.maxDepthBounds = std::clamp(bit::cast(m_state.renderStates[D3DRS_ADAPTIVETESS_W]), 0.0f, 1.0f); } EmitCs([ cDepthBounds = db ] (DxvkContext* ctx) { ctx->setDepthBounds(cDepthBounds); }); } BindSpecConstants(); if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyVertexBuffers) && UploadVBOs)) { for (uint32_t i = 0; i < caps::MaxStreams; i++) { const D3D9VBO& vbo = m_state.vertexBuffers[i]; BindVertexBuffer(i, vbo.vertexBuffer.ptr(), vbo.offset, vbo.stride); } m_flags.clr(D3D9DeviceFlag::DirtyVertexBuffers); } if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyIndexBuffer) && UploadIBO)) { BindIndices(); m_flags.clr(D3D9DeviceFlag::DirtyIndexBuffer); } } void D3D9DeviceEx::EnsureSamplerLimit() { constexpr uint32_t MaxSamplerCount = DxvkSamplerPool::MaxSamplerCount - SamplerCount; // Maximum possible number of live samplers we can have // since last reading back from the CS thread. if (likely(m_lastSamplerLiveCount + m_samplerBindCount - m_lastSamplerBindCount <= MaxSamplerCount)) return; // Update current stats from CS thread and check again. We // don't want to do this every time due to potential cache // thrashing. uint64_t lastStats = m_lastSamplerStats.load(std::memory_order_relaxed); m_lastSamplerLiveCount = lastStats & SamplerCountMask; m_lastSamplerBindCount = lastStats >> SamplerCountBits; if (likely(m_lastSamplerLiveCount + m_samplerBindCount - m_lastSamplerBindCount <= MaxSamplerCount)) return; // If we have a large number of sampler updates in flight, wait for // the CS thread to complete some and re-evaluate. We should not hit // this path under normal gameplay conditions. ConsiderFlush(GpuFlushType::ImplicitSynchronization); uint64_t sequenceNumber = m_csThread.lastSequenceNumber(); while (++sequenceNumber <= GetCurrentSequenceNumber()) { SynchronizeCsThread(sequenceNumber); uint64_t lastStats = m_lastSamplerStats.load(std::memory_order_relaxed); m_lastSamplerLiveCount = lastStats & SamplerCountMask; m_lastSamplerBindCount = lastStats >> SamplerCountBits; if (m_lastSamplerLiveCount + m_samplerBindCount - m_lastSamplerBindCount <= MaxSamplerCount) return; } // If we end up here, the game somehow managed to queue up so // many samplers that we need to wait for the GPU to free some. // We should absolutely never hit this path in the real world. Logger::warn("Sampler pool exhausted, synchronizing with GPU."); Flush(); SynchronizeCsThread(DxvkCsThread::SynchronizeAll); uint64_t submissionId = m_submissionFence->value(); while (++submissionId <= m_submissionId) { m_submissionFence->wait(submissionId); // Need to manually update sampler stats here since we // might otherwise hit this path again the next time auto samplerStats = m_dxvkDevice->getSamplerStats(); m_lastSamplerStats = samplerStats.liveCount | (m_samplerBindCount << SamplerCountBits); if (samplerStats.liveCount <= MaxSamplerCount) return; } // If we end up *here*, good luck. Logger::warn("Sampler pool exhausted, cannot create any new samplers."); } template void D3D9DeviceEx::BindShader( const D3D9CommonShader* pShaderModule) { auto shader = pShaderModule->GetShader(); if (unlikely(shader->needsLibraryCompile())) m_dxvkDevice->requestCompileShader(shader); EmitCs([ cShader = std::move(shader) ] (DxvkContext* ctx) mutable { constexpr VkShaderStageFlagBits stage = GetShaderStage(ShaderStage); ctx->bindShader(std::move(cShader)); }); } void D3D9DeviceEx::BindInputLayout() { m_flags.clr(D3D9DeviceFlag::DirtyInputLayout); if (m_state.vertexDecl == nullptr) { EmitCs([&cIaState = m_iaState] (DxvkContext* ctx) { cIaState.streamsUsed = 0; ctx->setInputLayout(0, nullptr, 0, nullptr); }); } else { std::array streamFreq; for (uint32_t i = 0; i < caps::MaxStreams; i++) streamFreq[i] = m_state.streamFreq[i]; Com vertexDecl = m_state.vertexDecl; Com vertexShader; if (UseProgrammableVS()) vertexShader = m_state.vertexShader; EmitCs([ &cIaState = m_iaState, cVertexDecl = std::move(vertexDecl), cVertexShader = std::move(vertexShader), cStreamsInstanced = m_instancedData, cStreamFreq = streamFreq ] (DxvkContext* ctx) { cIaState.streamsInstanced = cStreamsInstanced; cIaState.streamsUsed = 0; const auto& elements = cVertexDecl->GetElements(); std::array attrList = { }; std::array bindList = { }; std::array vertexSizes = { }; uint32_t attrMask = 0; uint32_t bindMask = 0; const auto& isgn = cVertexShader != nullptr ? GetCommonShader(cVertexShader)->GetIsgn() : GetFixedFunctionIsgn(); for (uint32_t i = 0; i < isgn.elemCount; i++) { const auto& decl = isgn.elems[i]; DxvkVertexAttribute attrib = { }; attrib.location = i; attrib.binding = NullStreamIdx; attrib.format = VK_FORMAT_R32G32B32A32_SFLOAT; attrib.offset = 0; for (const auto& element : elements) { DxsoSemantic elementSemantic = { static_cast(element.Usage), element.UsageIndex }; if (elementSemantic.usage == DxsoUsage::PositionT) elementSemantic.usage = DxsoUsage::Position; if (elementSemantic == decl.semantic) { attrib.binding = uint32_t(element.Stream); attrib.format = DecodeDecltype(D3DDECLTYPE(element.Type)); attrib.offset = element.Offset; cIaState.streamsUsed |= 1u << attrib.binding; break; } } attrList[i] = DxvkVertexInput(attrib); vertexSizes[attrib.binding] = std::max(vertexSizes[attrib.binding], uint32_t(attrib.offset + lookupFormatInfo(attrib.format)->elementSize)); DxvkVertexBinding binding = { }; binding.binding = attrib.binding; binding.extent = vertexSizes[attrib.binding]; uint32_t instanceData = cStreamFreq[binding.binding % caps::MaxStreams]; if (instanceData & D3DSTREAMSOURCE_INSTANCEDATA) { binding.divisor = instanceData & 0x7FFFFF; // Remove instance packed-in flags in the data. binding.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE; } else { binding.divisor = 0u; binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; } bindList[binding.binding] = DxvkVertexInput(binding); attrMask |= 1u << i; bindMask |= 1u << binding.binding; } // Compact the attribute and binding lists to filter // out attributes and bindings not used by the shader uint32_t attrCount = CompactSparseList(attrList.data(), attrMask); uint32_t bindCount = CompactSparseList(bindList.data(), bindMask); ctx->setInputLayout( attrCount, attrList.data(), bindCount, bindList.data()); }); } } void D3D9DeviceEx::BindVertexBuffer( UINT Slot, D3D9VertexBuffer* pBuffer, UINT Offset, UINT Stride) { EmitCs([ cSlotId = Slot, cBufferSlice = pBuffer != nullptr ? pBuffer->GetCommonBuffer()->GetBufferSlice(Offset) : DxvkBufferSlice(), cStride = pBuffer != nullptr ? Stride : 0 ] (DxvkContext* ctx) mutable { ctx->bindVertexBuffer(cSlotId, std::move(cBufferSlice), cStride); }); } void D3D9DeviceEx::BindIndices() { D3D9CommonBuffer* buffer = GetCommonBuffer(m_state.indices); D3D9Format format = buffer != nullptr ? buffer->Desc()->Format : D3D9Format::INDEX32; const VkIndexType indexType = DecodeIndexType(format); EmitCs([ cBufferSlice = buffer != nullptr ? buffer->GetBufferSlice() : DxvkBufferSlice(), cIndexType = indexType ](DxvkContext* ctx) mutable { ctx->bindIndexBuffer(std::move(cBufferSlice), cIndexType); }); } void D3D9DeviceEx::Begin(D3D9Query* pQuery) { D3D9DeviceLock lock = LockDevice(); EmitCs([cQuery = Com(pQuery)](DxvkContext* ctx) { cQuery->Begin(ctx); }); } void D3D9DeviceEx::End(D3D9Query* pQuery) { D3D9DeviceLock lock = LockDevice(); EmitCs([cQuery = Com(pQuery)](DxvkContext* ctx) { cQuery->End(ctx); }); pQuery->NotifyEnd(); if (unlikely(pQuery->IsEvent())) { pQuery->IsStalling() ? Flush() : ConsiderFlush(GpuFlushType::ImplicitStrongHint); } else if (pQuery->IsStalling()) { ConsiderFlush(GpuFlushType::ImplicitWeakHint); } } void D3D9DeviceEx::SetVertexBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits) { m_state.vsConsts->bConsts[idx] &= ~mask; m_state.vsConsts->bConsts[idx] |= bits & mask; m_consts[DxsoProgramTypes::VertexShader].dirty = true; } void D3D9DeviceEx::SetPixelBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits) { m_state.psConsts->bConsts[idx] &= ~mask; m_state.psConsts->bConsts[idx] |= bits & mask; m_consts[DxsoProgramTypes::PixelShader].dirty = true; } HRESULT D3D9DeviceEx::CreateShaderModule( D3D9CommonShader* pShaderModule, uint32_t* pLength, VkShaderStageFlagBits ShaderStage, const DWORD* pShaderBytecode, const DxsoModuleInfo* pModuleInfo) { try { m_shaderModules->GetShaderModule(this, pShaderModule, pLength, ShaderStage, pModuleInfo, pShaderBytecode); return D3D_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return D3DERR_INVALIDCALL; } } template < DxsoProgramType ProgramType, D3D9ConstantType ConstantType, typename T> HRESULT D3D9DeviceEx::SetShaderConstants( UINT StartRegister, const T* pConstantData, UINT Count) { const uint32_t regCountHardware = DetermineHardwareRegCount(); constexpr uint32_t regCountSoftware = DetermineSoftwareRegCount(); // Error out in case of StartRegister + Count overflow if (unlikely(StartRegister > std::numeric_limits::max() - Count)) return D3DERR_INVALIDCALL; if (unlikely(StartRegister + Count > regCountSoftware)) return D3DERR_INVALIDCALL; Count = UINT( std::max( std::clamp(Count + StartRegister, 0, regCountHardware) - INT(StartRegister), 0)); if (unlikely(Count == 0)) return D3D_OK; if (unlikely(pConstantData == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(ShouldRecord())) return m_recorder->SetShaderConstants( StartRegister, pConstantData, Count); D3D9ConstantSets& constSet = m_consts[ProgramType]; if constexpr (ConstantType == D3D9ConstantType::Float) { constSet.maxChangedConstF = std::max(constSet.maxChangedConstF, StartRegister + Count); } else if constexpr (ConstantType == D3D9ConstantType::Int && ProgramType == DxsoProgramType::VertexShader) { // We only track changed int constants for vertex shaders (and it's only used when the device uses the SWVP UBO layout). // Pixel shaders (and vertex shaders on HWVP devices) always copy all int constants into the same UBO as the float constants constSet.maxChangedConstI = std::max(constSet.maxChangedConstI, StartRegister + Count); } else if constexpr (ConstantType == D3D9ConstantType::Bool && ProgramType == DxsoProgramType::VertexShader) { // We only track changed bool constants for vertex shaders (and it's only used when the device uses the SWVP UBO layout). // Pixel shaders (and vertex shaders on HWVP devices) always put all bool constants into a single spec constant. constSet.maxChangedConstB = std::max(constSet.maxChangedConstB, StartRegister + Count); } if constexpr (ConstantType != D3D9ConstantType::Bool) { uint32_t maxCount = ConstantType == D3D9ConstantType::Float ? constSet.meta.maxConstIndexF : constSet.meta.maxConstIndexI; constSet.dirty |= StartRegister < maxCount; } else if constexpr (ProgramType == DxsoProgramType::VertexShader) { if (unlikely(CanSWVP())) { constSet.dirty |= StartRegister < constSet.meta.maxConstIndexB; } } UpdateStateConstants( &m_state, StartRegister, pConstantData, Count, m_d3d9Options.d3d9FloatEmulation == D3D9FloatEmulation::Enabled); return D3D_OK; } void D3D9DeviceEx::UpdateFixedFunctionVS() { // Shader... bool hasPositionT = m_state.vertexDecl != nullptr ? m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasPositionT) : false; bool hasBlendWeight = m_state.vertexDecl != nullptr ? m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasBlendWeight) : false; bool hasBlendIndices = m_state.vertexDecl != nullptr ? m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasBlendIndices) : false; bool indexedVertexBlend = hasBlendIndices && m_state.renderStates[D3DRS_INDEXEDVERTEXBLENDENABLE]; D3D9FF_VertexBlendMode vertexBlendMode = D3D9FF_VertexBlendMode_Disabled; if (m_state.renderStates[D3DRS_VERTEXBLEND] != D3DVBF_DISABLE && !hasPositionT) { vertexBlendMode = m_state.renderStates[D3DRS_VERTEXBLEND] == D3DVBF_TWEENING ? D3D9FF_VertexBlendMode_Tween : D3D9FF_VertexBlendMode_Normal; if (m_state.renderStates[D3DRS_VERTEXBLEND] != D3DVBF_0WEIGHTS) { if (!hasBlendWeight) vertexBlendMode = D3D9FF_VertexBlendMode_Disabled; } else if (!indexedVertexBlend) vertexBlendMode = D3D9FF_VertexBlendMode_Disabled; } if (unlikely(hasPositionT && m_state.vertexShader != nullptr && !m_flags.test(D3D9DeviceFlag::DirtyProgVertexShader))) { m_flags.set(D3D9DeviceFlag::DirtyInputLayout); m_flags.set(D3D9DeviceFlag::DirtyFFVertexShader); m_flags.set(D3D9DeviceFlag::DirtyProgVertexShader); } if (m_flags.test(D3D9DeviceFlag::DirtyFFVertexShader)) { m_flags.clr(D3D9DeviceFlag::DirtyFFVertexShader); D3D9FFShaderKeyVS key; key.Data.Contents.HasPositionT = hasPositionT; key.Data.Contents.HasColor0 = m_state.vertexDecl != nullptr ? m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasColor0) : false; key.Data.Contents.HasColor1 = m_state.vertexDecl != nullptr ? m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasColor1) : false; key.Data.Contents.HasPointSize = m_state.vertexDecl != nullptr ? m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasPointSize) : false; key.Data.Contents.HasFog = m_state.vertexDecl != nullptr ? m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasFog) : false; bool lighting = m_state.renderStates[D3DRS_LIGHTING] != 0 && !key.Data.Contents.HasPositionT; bool colorVertex = m_state.renderStates[D3DRS_COLORVERTEX] != 0; uint32_t mask = (lighting && colorVertex) ? (key.Data.Contents.HasColor0 ? D3DMCS_COLOR1 : D3DMCS_MATERIAL) | (key.Data.Contents.HasColor1 ? D3DMCS_COLOR2 : D3DMCS_MATERIAL) : 0; key.Data.Contents.UseLighting = lighting; key.Data.Contents.NormalizeNormals = m_state.renderStates[D3DRS_NORMALIZENORMALS]; key.Data.Contents.LocalViewer = m_state.renderStates[D3DRS_LOCALVIEWER] && lighting; key.Data.Contents.RangeFog = m_state.renderStates[D3DRS_RANGEFOGENABLE]; key.Data.Contents.DiffuseSource = m_state.renderStates[D3DRS_DIFFUSEMATERIALSOURCE] & mask; key.Data.Contents.AmbientSource = m_state.renderStates[D3DRS_AMBIENTMATERIALSOURCE] & mask; key.Data.Contents.SpecularSource = m_state.renderStates[D3DRS_SPECULARMATERIALSOURCE] & mask; key.Data.Contents.EmissiveSource = m_state.renderStates[D3DRS_EMISSIVEMATERIALSOURCE] & mask; uint32_t lightCount = 0; if (key.Data.Contents.UseLighting) { for (uint32_t i = 0; i < caps::MaxEnabledLights; i++) { if (m_state.enabledLightIndices[i] != std::numeric_limits::max()) lightCount++; } } key.Data.Contents.LightCount = lightCount; for (uint32_t i = 0; i < caps::MaxTextureBlendStages; i++) { uint32_t transformFlags = m_state.textureStages[i][DXVK_TSS_TEXTURETRANSFORMFLAGS] & ~(D3DTTFF_PROJECTED); uint32_t index = m_state.textureStages[i][DXVK_TSS_TEXCOORDINDEX]; uint32_t indexFlags = (index & TCIMask) >> TCIOffset; transformFlags &= 0b111; index &= 0b111; key.Data.Contents.TransformFlags |= transformFlags << (i * 3); key.Data.Contents.TexcoordFlags |= indexFlags << (i * 3); key.Data.Contents.TexcoordIndices |= index << (i * 3); key.Data.Contents.Projected |= ((m_state.textureStages[i][DXVK_TSS_TEXTURETRANSFORMFLAGS] & D3DTTFF_PROJECTED) == D3DTTFF_PROJECTED) << i; } key.Data.Contents.TexcoordDeclMask = m_state.vertexDecl != nullptr ? m_state.vertexDecl->GetTexcoordMask() : 0; key.Data.Contents.VertexBlendMode = uint32_t(vertexBlendMode); if (vertexBlendMode == D3D9FF_VertexBlendMode_Normal) { key.Data.Contents.VertexBlendIndexed = indexedVertexBlend; key.Data.Contents.VertexBlendCount = m_state.renderStates[D3DRS_VERTEXBLEND] & 0xff; } key.Data.Contents.VertexClipping = IsClipPlaneEnabled(); EmitCs([ this, cKey = key, &cShaders = m_ffModules ](DxvkContext* ctx) { auto shader = cShaders.GetShaderModule(this, cKey); ctx->bindShader(shader.GetShader()); }); } if (hasPositionT && (m_flags.test(D3D9DeviceFlag::DirtyFFViewport) || m_ffZTest != IsZTestEnabled())) { m_flags.clr(D3D9DeviceFlag::DirtyFFViewport); m_flags.set(D3D9DeviceFlag::DirtyFFVertexData); const auto& vp = m_state.viewport; // For us to account for the Vulkan viewport rules // when translating Window Coords -> Real Coords: // We need to negate the inverse extent we multiply by, // this follows through to the offset when that gets // timesed by it. // The 1.0f additional offset however does not, // so we account for that there manually. m_ffZTest = IsZTestEnabled(); m_viewportInfo.inverseExtent = Vector4( 2.0f / float(vp.Width), -2.0f / float(vp.Height), m_ffZTest ? 1.0f : 0.0f, 1.0f); m_viewportInfo.inverseOffset = Vector4( -float(vp.X), -float(vp.Y), 0.0f, 0.0f); m_viewportInfo.inverseOffset = m_viewportInfo.inverseOffset * m_viewportInfo.inverseExtent; m_viewportInfo.inverseOffset = m_viewportInfo.inverseOffset + Vector4(-1.0f, 1.0f, 0.0f, 0.0f); } // Constants... if (m_flags.test(D3D9DeviceFlag::DirtyFFVertexData)) { m_flags.clr(D3D9DeviceFlag::DirtyFFVertexData); auto mapPtr = m_vsFixedFunction.AllocSlice(); auto WorldView = m_state.transforms[GetTransformIndex(D3DTS_VIEW)] * m_state.transforms[GetTransformIndex(D3DTS_WORLD)]; auto NormalMatrix = inverse(WorldView); D3D9FixedFunctionVS* data = reinterpret_cast(mapPtr); data->WorldView = WorldView; data->NormalMatrix = NormalMatrix; data->InverseView = transpose(inverse(m_state.transforms[GetTransformIndex(D3DTS_VIEW)])); data->Projection = m_state.transforms[GetTransformIndex(D3DTS_PROJECTION)]; for (uint32_t i = 0; i < data->TexcoordMatrices.size(); i++) data->TexcoordMatrices[i] = m_state.transforms[GetTransformIndex(D3DTS_TEXTURE0) + i]; data->ViewportInfo = m_viewportInfo; DecodeD3DCOLOR(m_state.renderStates[D3DRS_AMBIENT], data->GlobalAmbient.data); uint32_t lightIdx = 0; for (uint32_t i = 0; i < caps::MaxEnabledLights; i++) { auto idx = m_state.enabledLightIndices[i]; if (idx == std::numeric_limits::max()) continue; data->Lights[lightIdx++] = D3D9Light(m_state.lights[idx].value(), m_state.transforms[GetTransformIndex(D3DTS_VIEW)]); } data->Material = m_state.material; data->TweenFactor = bit::cast(m_state.renderStates[D3DRS_TWEENFACTOR]); } if (m_flags.test(D3D9DeviceFlag::DirtyFFVertexBlend) && vertexBlendMode == D3D9FF_VertexBlendMode_Normal) { m_flags.clr(D3D9DeviceFlag::DirtyFFVertexBlend); auto mapPtr = m_vsVertexBlend.AllocSlice(); auto UploadVertexBlendData = [&](auto data) { for (uint32_t i = 0; i < std::size(data->WorldView); i++) data->WorldView[i] = m_state.transforms[GetTransformIndex(D3DTS_VIEW)] * m_state.transforms[GetTransformIndex(D3DTS_WORLDMATRIX(i))]; }; (m_isSWVP && indexedVertexBlend) ? UploadVertexBlendData(reinterpret_cast(mapPtr)) : UploadVertexBlendData(reinterpret_cast(mapPtr)); } } void D3D9DeviceEx::UpdateFixedFunctionPS() { // Shader... if (m_flags.test(D3D9DeviceFlag::DirtyFFPixelShader) || m_lastSamplerTypesFF != m_textureTypes) { m_flags.clr(D3D9DeviceFlag::DirtyFFPixelShader); m_lastSamplerTypesFF = m_textureTypes; // Used args for a given operation. auto ArgsMask = [](DWORD Op) { switch (Op) { case D3DTOP_DISABLE: return 0b000u; // No Args case D3DTOP_SELECTARG1: case D3DTOP_PREMODULATE: return 0b010u; // Arg 1 case D3DTOP_SELECTARG2: return 0b100u; // Arg 2 case D3DTOP_MULTIPLYADD: case D3DTOP_LERP: return 0b111u; // Arg 0, 1, 2 default: return 0b110u; // Arg 1, 2 } }; D3D9FFShaderKeyFS key; uint32_t idx; for (idx = 0; idx < caps::TextureStageCount; idx++) { auto& stage = key.Stages[idx].Contents; auto& data = m_state.textureStages[idx]; // Subsequent stages do not occur if this is true. if (data[DXVK_TSS_COLOROP] == D3DTOP_DISABLE) break; // If the stage is invalid (ie. no texture bound), // this and all subsequent stages get disabled. if (m_state.textures[idx] == nullptr) { if (((data[DXVK_TSS_COLORARG0] & D3DTA_SELECTMASK) == D3DTA_TEXTURE && (ArgsMask(data[DXVK_TSS_COLOROP]) & (1 << 0u))) || ((data[DXVK_TSS_COLORARG1] & D3DTA_SELECTMASK) == D3DTA_TEXTURE && (ArgsMask(data[DXVK_TSS_COLOROP]) & (1 << 1u))) || ((data[DXVK_TSS_COLORARG2] & D3DTA_SELECTMASK) == D3DTA_TEXTURE && (ArgsMask(data[DXVK_TSS_COLOROP]) & (1 << 2u)))) break; } stage.TextureBound = m_state.textures[idx] != nullptr ? 1 : 0; stage.ColorOp = data[DXVK_TSS_COLOROP]; stage.AlphaOp = data[DXVK_TSS_ALPHAOP]; stage.ColorArg0 = data[DXVK_TSS_COLORARG0]; stage.ColorArg1 = data[DXVK_TSS_COLORARG1]; stage.ColorArg2 = data[DXVK_TSS_COLORARG2]; stage.AlphaArg0 = data[DXVK_TSS_ALPHAARG0]; stage.AlphaArg1 = data[DXVK_TSS_ALPHAARG1]; stage.AlphaArg2 = data[DXVK_TSS_ALPHAARG2]; const uint32_t samplerOffset = idx * 2; stage.Type = (m_textureTypes >> samplerOffset) & 0xffu; stage.ResultIsTemp = data[DXVK_TSS_RESULTARG] == D3DTA_TEMP; uint32_t ttff = data[DXVK_TSS_TEXTURETRANSFORMFLAGS]; uint32_t count = ttff & ~D3DTTFF_PROJECTED; stage.Projected = (ttff & D3DTTFF_PROJECTED) ? 1 : 0; stage.ProjectedCount = (ttff & D3DTTFF_PROJECTED) ? count : 0; stage.SampleDref = (m_depthTextures & (1 << idx)) != 0; } auto& stage0 = key.Stages[0].Contents; if (stage0.ResultIsTemp && stage0.ColorOp != D3DTOP_DISABLE && stage0.AlphaOp == D3DTOP_DISABLE) { stage0.AlphaOp = D3DTOP_SELECTARG1; stage0.AlphaArg1 = D3DTA_DIFFUSE; } stage0.GlobalSpecularEnable = m_state.renderStates[D3DRS_SPECULARENABLE]; // The last stage *always* writes to current. if (idx >= 1) key.Stages[idx - 1].Contents.ResultIsTemp = false; EmitCs([ this, cKey = key, &cShaders = m_ffModules ](DxvkContext* ctx) { auto shader = cShaders.GetShaderModule(this, cKey); ctx->bindShader(shader.GetShader()); }); } // Constants if (m_flags.test(D3D9DeviceFlag::DirtyFFPixelData)) { m_flags.clr(D3D9DeviceFlag::DirtyFFPixelData); auto mapPtr = m_psFixedFunction.AllocSlice(); auto& rs = m_state.renderStates; D3D9FixedFunctionPS* data = reinterpret_cast(mapPtr); DecodeD3DCOLOR((D3DCOLOR)rs[D3DRS_TEXTUREFACTOR], data->textureFactor.data); } } bool D3D9DeviceEx::UseProgrammableVS() { return m_state.vertexShader != nullptr && m_state.vertexDecl != nullptr && !m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasPositionT); } bool D3D9DeviceEx::UseProgrammablePS() { return m_state.pixelShader != nullptr; } void D3D9DeviceEx::ApplyPrimitiveType( DxvkContext* pContext, D3DPRIMITIVETYPE PrimType) { if (m_iaState.primitiveType != PrimType) { m_iaState.primitiveType = PrimType; auto iaState = DecodeInputAssemblyState(PrimType); pContext->setInputAssemblyState(iaState); } } void D3D9DeviceEx::ResolveZ() { D3D9Surface* src = m_state.depthStencil.ptr(); IDirect3DBaseTexture9* dst = m_state.textures[0]; if (unlikely(!src || !dst)) return; D3D9CommonTexture* srcTextureInfo = GetCommonTexture(src); D3D9CommonTexture* dstTextureInfo = GetCommonTexture(dst); const D3D9_COMMON_TEXTURE_DESC* srcDesc = srcTextureInfo->Desc(); const D3D9_COMMON_TEXTURE_DESC* dstDesc = dstTextureInfo->Desc(); VkSampleCountFlagBits dstSampleCount; DecodeMultiSampleType(m_dxvkDevice, dstDesc->MultiSample, dstDesc->MultisampleQuality, &dstSampleCount); if (unlikely(dstSampleCount != VK_SAMPLE_COUNT_1_BIT)) { Logger::warn("D3D9DeviceEx::ResolveZ: dstSampleCount != 1. Discarding."); return; } const D3D9_VK_FORMAT_MAPPING srcFormatInfo = LookupFormat(srcDesc->Format); const D3D9_VK_FORMAT_MAPPING dstFormatInfo = LookupFormat(dstDesc->Format); VkImageSubresource dstSubresource = dstTextureInfo->GetSubresourceFromIndex( dstFormatInfo.Aspect, 0); VkImageSubresource srcSubresource = srcTextureInfo->GetSubresourceFromIndex( srcFormatInfo.Aspect, src->GetSubresource()); if ((dstSubresource.aspectMask & srcSubresource.aspectMask) != 0) { // for depthStencil -> depth or depthStencil -> stencil copies, only copy the aspect that both images support dstSubresource.aspectMask = dstSubresource.aspectMask & srcSubresource.aspectMask; srcSubresource.aspectMask = dstSubresource.aspectMask & srcSubresource.aspectMask; } else if (unlikely(dstSubresource.aspectMask != VK_IMAGE_ASPECT_COLOR_BIT && srcSubresource.aspectMask != VK_IMAGE_ASPECT_COLOR_BIT)) { Logger::err(str::format("D3D9DeviceEx::ResolveZ: Trying to blit from ", srcFormatInfo.FormatColor, " (aspect ", srcSubresource.aspectMask, ")", " to ", dstFormatInfo.FormatColor, " (aspect ", dstSubresource.aspectMask, ")" )); return; } const VkImageSubresourceLayers dstSubresourceLayers = { dstSubresource.aspectMask, dstSubresource.mipLevel, dstSubresource.arrayLayer, 1 }; const VkImageSubresourceLayers srcSubresourceLayers = { srcSubresource.aspectMask, srcSubresource.mipLevel, srcSubresource.arrayLayer, 1 }; VkSampleCountFlagBits srcSampleCount; DecodeMultiSampleType(m_dxvkDevice, srcDesc->MultiSample, srcDesc->MultisampleQuality, &srcSampleCount); if (srcSampleCount == VK_SAMPLE_COUNT_1_BIT) { EmitCs([ cDstImage = dstTextureInfo->GetImage(), cSrcImage = srcTextureInfo->GetImage(), cDstLayers = dstSubresourceLayers, cSrcLayers = srcSubresourceLayers ] (DxvkContext* ctx) { ctx->copyImage( cDstImage, cDstLayers, VkOffset3D { 0, 0, 0 }, cSrcImage, cSrcLayers, VkOffset3D { 0, 0, 0 }, cDstImage->mipLevelExtent(cDstLayers.mipLevel)); }); } else { EmitCs([ cDstImage = dstTextureInfo->GetImage(), cSrcImage = srcTextureInfo->GetImage(), cDstSubres = dstSubresourceLayers, cSrcSubres = srcSubresourceLayers ] (DxvkContext* ctx) { // We should resolve using the first sample according to // http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/Advanced-DX9-Capabilities-for-ATI-Radeon-Cards_v2.pdf // "The resolve operation copies the depth value from the *first sample only* into the resolved depth stencil texture." VkImageResolve region; region.srcSubresource = cSrcSubres; region.srcOffset = VkOffset3D { 0, 0, 0 }; region.dstSubresource = cDstSubres; region.dstOffset = VkOffset3D { 0, 0, 0 }; region.extent = cDstImage->mipLevelExtent(cDstSubres.mipLevel); ctx->resolveImage(cDstImage, cSrcImage, region, cSrcImage->info().format, VK_RESOLVE_MODE_SAMPLE_ZERO_BIT, VK_RESOLVE_MODE_SAMPLE_ZERO_BIT); }); } dstTextureInfo->MarkAllNeedReadback(); } void D3D9DeviceEx::TransitionImage(D3D9CommonTexture* pResource, VkImageLayout NewLayout) { EmitCs([ cImage = pResource->GetImage(), cNewLayout = NewLayout ] (DxvkContext* ctx) { ctx->changeImageLayout( cImage, cNewLayout); }); } void D3D9DeviceEx::TransformImage( D3D9CommonTexture* pResource, const VkImageSubresourceRange* pSubresources, VkImageLayout OldLayout, VkImageLayout NewLayout) { EmitCs([ cImage = pResource->GetImage(), cSubresources = *pSubresources, cOldLayout = OldLayout, cNewLayout = NewLayout ] (DxvkContext* ctx) { ctx->transformImage( cImage, cSubresources, cOldLayout, cNewLayout); }); } void D3D9DeviceEx::ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters) { SetDepthStencilSurface(nullptr); for (uint32_t i = 0; i < caps::MaxSimultaneousRenderTargets; i++) SetRenderTargetInternal(i, nullptr); auto& rs = m_state.renderStates; rs[D3DRS_SEPARATEALPHABLENDENABLE] = FALSE; rs[D3DRS_ALPHABLENDENABLE] = FALSE; rs[D3DRS_BLENDOP] = D3DBLENDOP_ADD; rs[D3DRS_BLENDOPALPHA] = D3DBLENDOP_ADD; rs[D3DRS_DESTBLEND] = D3DBLEND_ZERO; rs[D3DRS_DESTBLENDALPHA] = D3DBLEND_ZERO; rs[D3DRS_COLORWRITEENABLE] = 0x0000000f; rs[D3DRS_COLORWRITEENABLE1] = 0x0000000f; rs[D3DRS_COLORWRITEENABLE2] = 0x0000000f; rs[D3DRS_COLORWRITEENABLE3] = 0x0000000f; rs[D3DRS_SRCBLEND] = D3DBLEND_ONE; rs[D3DRS_SRCBLENDALPHA] = D3DBLEND_ONE; BindBlendState(); rs[D3DRS_BLENDFACTOR] = 0xffffffff; BindBlendFactor(); rs[D3DRS_ZENABLE] = pPresentationParameters->EnableAutoDepthStencil ? D3DZB_TRUE : D3DZB_FALSE; rs[D3DRS_ZFUNC] = D3DCMP_LESSEQUAL; rs[D3DRS_TWOSIDEDSTENCILMODE] = FALSE; rs[D3DRS_ZWRITEENABLE] = TRUE; rs[D3DRS_STENCILENABLE] = FALSE; rs[D3DRS_STENCILFAIL] = D3DSTENCILOP_KEEP; rs[D3DRS_STENCILZFAIL] = D3DSTENCILOP_KEEP; rs[D3DRS_STENCILPASS] = D3DSTENCILOP_KEEP; rs[D3DRS_STENCILFUNC] = D3DCMP_ALWAYS; rs[D3DRS_CCW_STENCILFAIL] = D3DSTENCILOP_KEEP; rs[D3DRS_CCW_STENCILZFAIL] = D3DSTENCILOP_KEEP; rs[D3DRS_CCW_STENCILPASS] = D3DSTENCILOP_KEEP; rs[D3DRS_CCW_STENCILFUNC] = D3DCMP_ALWAYS; rs[D3DRS_STENCILMASK] = 0xFFFFFFFF; rs[D3DRS_STENCILWRITEMASK] = 0xFFFFFFFF; BindDepthStencilState(); rs[D3DRS_STENCILREF] = 0; BindDepthStencilRefrence(); rs[D3DRS_FILLMODE] = D3DFILL_SOLID; rs[D3DRS_CULLMODE] = D3DCULL_CCW; rs[D3DRS_DEPTHBIAS] = bit::cast(0.0f); rs[D3DRS_SLOPESCALEDEPTHBIAS] = bit::cast(0.0f); BindRasterizerState(); BindDepthBias(); rs[D3DRS_SCISSORTESTENABLE] = FALSE; rs[D3DRS_ALPHATESTENABLE] = FALSE; rs[D3DRS_ALPHAFUNC] = D3DCMP_ALWAYS; BindAlphaTestState(); rs[D3DRS_ALPHAREF] = 0; UpdatePushConstant(); rs[D3DRS_MULTISAMPLEMASK] = 0xffffffff; BindMultiSampleState(); rs[D3DRS_TEXTUREFACTOR] = 0xffffffff; m_flags.set(D3D9DeviceFlag::DirtyFFPixelData); rs[D3DRS_DIFFUSEMATERIALSOURCE] = D3DMCS_COLOR1; rs[D3DRS_SPECULARMATERIALSOURCE] = D3DMCS_COLOR2; rs[D3DRS_AMBIENTMATERIALSOURCE] = D3DMCS_MATERIAL; rs[D3DRS_EMISSIVEMATERIALSOURCE] = D3DMCS_MATERIAL; rs[D3DRS_LIGHTING] = TRUE; rs[D3DRS_COLORVERTEX] = TRUE; rs[D3DRS_LOCALVIEWER] = TRUE; rs[D3DRS_RANGEFOGENABLE] = FALSE; rs[D3DRS_NORMALIZENORMALS] = FALSE; m_flags.set(D3D9DeviceFlag::DirtyFFVertexShader); // PS rs[D3DRS_SPECULARENABLE] = FALSE; rs[D3DRS_AMBIENT] = 0; m_flags.set(D3D9DeviceFlag::DirtyFFVertexData); rs[D3DRS_FOGENABLE] = FALSE; rs[D3DRS_FOGCOLOR] = 0; rs[D3DRS_FOGTABLEMODE] = D3DFOG_NONE; rs[D3DRS_FOGSTART] = bit::cast(0.0f); rs[D3DRS_FOGEND] = bit::cast(1.0f); rs[D3DRS_FOGDENSITY] = bit::cast(1.0f); rs[D3DRS_FOGVERTEXMODE] = D3DFOG_NONE; m_flags.set(D3D9DeviceFlag::DirtyFogColor); m_flags.set(D3D9DeviceFlag::DirtyFogDensity); m_flags.set(D3D9DeviceFlag::DirtyFogEnd); m_flags.set(D3D9DeviceFlag::DirtyFogScale); m_flags.set(D3D9DeviceFlag::DirtyFogState); rs[D3DRS_CLIPPLANEENABLE] = 0; m_flags.set(D3D9DeviceFlag::DirtyClipPlanes); const VkPhysicalDeviceLimits& limits = m_dxvkDevice->adapter()->deviceProperties().limits; rs[D3DRS_POINTSPRITEENABLE] = FALSE; rs[D3DRS_POINTSCALEENABLE] = FALSE; rs[D3DRS_POINTSCALE_A] = bit::cast(1.0f); rs[D3DRS_POINTSCALE_B] = bit::cast(0.0f); rs[D3DRS_POINTSCALE_C] = bit::cast(0.0f); rs[D3DRS_POINTSIZE] = bit::cast(1.0f); rs[D3DRS_POINTSIZE_MIN] = m_isD3D8Compatible ? bit::cast(0.0f) : bit::cast(1.0f); rs[D3DRS_POINTSIZE_MAX] = bit::cast(limits.pointSizeRange[1]); UpdatePushConstant(); UpdatePushConstant(); UpdatePushConstant(); m_flags.set(D3D9DeviceFlag::DirtyPointScale); UpdatePointMode(false); rs[D3DRS_SRGBWRITEENABLE] = 0; rs[D3DRS_SHADEMODE] = D3DSHADE_GOURAUD; rs[D3DRS_VERTEXBLEND] = D3DVBF_DISABLE; rs[D3DRS_INDEXEDVERTEXBLENDENABLE] = FALSE; rs[D3DRS_TWEENFACTOR] = bit::cast(0.0f); m_flags.set(D3D9DeviceFlag::DirtyFFVertexBlend); // Render States not implemented beyond this point. rs[D3DRS_LASTPIXEL] = TRUE; rs[D3DRS_DITHERENABLE] = FALSE; rs[D3DRS_WRAP0] = 0; rs[D3DRS_WRAP1] = 0; rs[D3DRS_WRAP2] = 0; rs[D3DRS_WRAP3] = 0; rs[D3DRS_WRAP4] = 0; rs[D3DRS_WRAP5] = 0; rs[D3DRS_WRAP6] = 0; rs[D3DRS_WRAP7] = 0; rs[D3DRS_CLIPPING] = TRUE; rs[D3DRS_MULTISAMPLEANTIALIAS] = TRUE; rs[D3DRS_PATCHEDGESTYLE] = D3DPATCHEDGE_DISCRETE; rs[D3DRS_DEBUGMONITORTOKEN] = D3DDMT_ENABLE; rs[D3DRS_POSITIONDEGREE] = D3DDEGREE_CUBIC; rs[D3DRS_NORMALDEGREE] = D3DDEGREE_LINEAR; rs[D3DRS_ANTIALIASEDLINEENABLE] = FALSE; rs[D3DRS_MINTESSELLATIONLEVEL] = bit::cast(1.0f); rs[D3DRS_MAXTESSELLATIONLEVEL] = bit::cast(1.0f); rs[D3DRS_ADAPTIVETESS_X] = bit::cast(0.0f); rs[D3DRS_ADAPTIVETESS_Y] = bit::cast(0.0f); rs[D3DRS_ADAPTIVETESS_Z] = bit::cast(1.0f); rs[D3DRS_ADAPTIVETESS_W] = bit::cast(0.0f); rs[D3DRS_ENABLEADAPTIVETESSELLATION] = FALSE; rs[D3DRS_WRAP8] = 0; rs[D3DRS_WRAP9] = 0; rs[D3DRS_WRAP10] = 0; rs[D3DRS_WRAP11] = 0; rs[D3DRS_WRAP12] = 0; rs[D3DRS_WRAP13] = 0; rs[D3DRS_WRAP14] = 0; rs[D3DRS_WRAP15] = 0; // End Unimplemented Render States for (uint32_t i = 0; i < caps::TextureStageCount; i++) { auto& stage = m_state.textureStages[i]; stage[DXVK_TSS_COLOROP] = i == 0 ? D3DTOP_MODULATE : D3DTOP_DISABLE; stage[DXVK_TSS_COLORARG1] = D3DTA_TEXTURE; stage[DXVK_TSS_COLORARG2] = D3DTA_CURRENT; stage[DXVK_TSS_ALPHAOP] = i == 0 ? D3DTOP_SELECTARG1 : D3DTOP_DISABLE; stage[DXVK_TSS_ALPHAARG1] = D3DTA_TEXTURE; stage[DXVK_TSS_ALPHAARG2] = D3DTA_CURRENT; stage[DXVK_TSS_BUMPENVMAT00] = bit::cast(0.0f); stage[DXVK_TSS_BUMPENVMAT01] = bit::cast(0.0f); stage[DXVK_TSS_BUMPENVMAT10] = bit::cast(0.0f); stage[DXVK_TSS_BUMPENVMAT11] = bit::cast(0.0f); stage[DXVK_TSS_TEXCOORDINDEX] = i; stage[DXVK_TSS_BUMPENVLSCALE] = bit::cast(0.0f); stage[DXVK_TSS_BUMPENVLOFFSET] = bit::cast(0.0f); stage[DXVK_TSS_TEXTURETRANSFORMFLAGS] = D3DTTFF_DISABLE; stage[DXVK_TSS_COLORARG0] = D3DTA_CURRENT; stage[DXVK_TSS_ALPHAARG0] = D3DTA_CURRENT; stage[DXVK_TSS_RESULTARG] = D3DTA_CURRENT; stage[DXVK_TSS_CONSTANT] = 0x00000000; } m_flags.set(D3D9DeviceFlag::DirtySharedPixelShaderData); m_flags.set(D3D9DeviceFlag::DirtyFFPixelShader); for (uint32_t i = 0; i < caps::MaxStreams; i++) m_state.streamFreq[i] = 1; for (uint32_t i = 0; i < m_state.textures->size(); i++) { SetStateTexture(i, nullptr); } EmitCs([ cSize = m_state.textures->size() ](DxvkContext* ctx) { VkShaderStageFlags stage = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; for (uint32_t i = 0; i < cSize; i++) { auto samplerInfo = RemapStateSamplerShader(DWORD(i)); uint32_t slot = computeResourceSlotId(samplerInfo.first, DxsoBindingType::Image, uint32_t(samplerInfo.second)); ctx->bindResourceImageView(stage, slot, nullptr); } }); m_dirtyTextures = 0; m_depthTextures = 0; m_cubeTextures = 0; auto& ss = m_state.samplerStates.get(); for (uint32_t i = 0; i < ss.size(); i++) { auto& state = ss[i]; state[D3DSAMP_ADDRESSU] = D3DTADDRESS_WRAP; state[D3DSAMP_ADDRESSV] = D3DTADDRESS_WRAP; state[D3DSAMP_ADDRESSW] = D3DTADDRESS_WRAP; state[D3DSAMP_BORDERCOLOR] = 0x00000000; state[D3DSAMP_MAGFILTER] = D3DTEXF_POINT; state[D3DSAMP_MINFILTER] = D3DTEXF_POINT; state[D3DSAMP_MIPFILTER] = D3DTEXF_NONE; state[D3DSAMP_MIPMAPLODBIAS] = bit::cast(0.0f); state[D3DSAMP_MAXMIPLEVEL] = 0; state[D3DSAMP_MAXANISOTROPY] = 1; state[D3DSAMP_SRGBTEXTURE] = 0; state[D3DSAMP_ELEMENTINDEX] = 0; state[D3DSAMP_DMAPOFFSET] = 0; BindSampler(i); } m_dirtySamplerStates = 0; for (uint32_t i = 0; i < caps::MaxClipPlanes; i++) { float plane[4] = { 0, 0, 0, 0 }; SetClipPlane(i, plane); } // We should do this... m_flags.set(D3D9DeviceFlag::DirtyInputLayout); UpdatePixelShaderSamplerSpec(0u, 0u, 0u); UpdateVertexBoolSpec(0u); UpdatePixelBoolSpec(0u); UpdateCommonSamplerSpec(0u, 0u, 0u); UpdateAnyColorWrites<0>(); UpdateAnyColorWrites<1>(); UpdateAnyColorWrites<2>(); UpdateAnyColorWrites<3>(); SetIndices(nullptr); for (uint32_t i = 0; i < caps::MaxStreams; i++) { SetStreamSource(i, nullptr, 0, 0); } } HRESULT D3D9DeviceEx::ResetSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode) { D3D9Format backBufferFmt = EnumerateFormat(pPresentationParameters->BackBufferFormat); bool unlockedFormats = m_implicitSwapchain != nullptr && m_implicitSwapchain->HasFormatsUnlocked(); Logger::info(str::format( "D3D9DeviceEx::ResetSwapChain:\n", " Requested Presentation Parameters\n", " - Width: ", pPresentationParameters->BackBufferWidth, "\n", " - Height: ", pPresentationParameters->BackBufferHeight, "\n", " - Format: ", backBufferFmt, "\n" " - Auto Depth Stencil: ", pPresentationParameters->EnableAutoDepthStencil ? "true" : "false", "\n", " ^ Format: ", EnumerateFormat(pPresentationParameters->AutoDepthStencilFormat), "\n", " - Windowed: ", pPresentationParameters->Windowed ? "true" : "false", "\n", " - Swap effect: ", pPresentationParameters->SwapEffect, "\n")); // Black Desert creates a D3DDEVTYPE_NULLREF device and // expects this validation to not prevent a swapchain reset. if (likely(m_deviceType != D3DDEVTYPE_NULLREF) && unlikely(!pPresentationParameters->Windowed && (pPresentationParameters->BackBufferWidth == 0 || pPresentationParameters->BackBufferHeight == 0))) { return D3DERR_INVALIDCALL; } if (backBufferFmt != D3D9Format::Unknown && !unlockedFormats) { if (!IsSupportedBackBufferFormat(backBufferFmt)) { Logger::err(str::format("D3D9DeviceEx::ResetSwapChain: Unsupported backbuffer format: ", EnumerateFormat(pPresentationParameters->BackBufferFormat))); return D3DERR_INVALIDCALL; } } if (m_implicitSwapchain != nullptr) { HRESULT hr = m_implicitSwapchain->Reset(pPresentationParameters, pFullscreenDisplayMode); if (FAILED(hr)) return hr; } else { m_implicitSwapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode, true); m_mostRecentlyUsedSwapchain = m_implicitSwapchain.ptr(); } if (pPresentationParameters->EnableAutoDepthStencil) { D3D9_COMMON_TEXTURE_DESC desc; desc.Width = pPresentationParameters->BackBufferWidth; desc.Height = pPresentationParameters->BackBufferHeight; desc.Depth = 1; desc.ArraySize = 1; desc.MipLevels = 1; desc.Usage = D3DUSAGE_DEPTHSTENCIL; desc.Format = EnumerateFormat(pPresentationParameters->AutoDepthStencilFormat); desc.Pool = D3DPOOL_DEFAULT; desc.Discard = (pPresentationParameters->Flags & D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL) != 0; desc.MultiSample = pPresentationParameters->MultiSampleType; desc.MultisampleQuality = pPresentationParameters->MultiSampleQuality; desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = TRUE; desc.IsLockable = IsLockableDepthStencilFormat(desc.Format); if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_SURFACE, &desc))) return D3DERR_NOTAVAILABLE; m_autoDepthStencil = new D3D9Surface(this, &desc, IsExtended(), nullptr, nullptr); m_initializer->InitTexture(m_autoDepthStencil->GetCommonTexture()); SetDepthStencilSurface(m_autoDepthStencil.ptr()); m_losableResourceCounter++; } SetRenderTarget(0, m_implicitSwapchain->GetBackBuffer(0)); // Force this if we end up binding the same RT to make scissor change go into effect. BindViewportAndScissor(); return D3D_OK; } HRESULT D3D9DeviceEx::InitialReset(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode) { ResetState(pPresentationParameters); HRESULT hr = ResetSwapChain(pPresentationParameters, pFullscreenDisplayMode); if (FAILED(hr)) return hr; Flush(); SynchronizeCsThread(DxvkCsThread::SynchronizeAll); return D3D_OK; } void D3D9DeviceEx::TrackBufferMappingBufferSequenceNumber( D3D9CommonBuffer* pResource) { uint64_t sequenceNumber = GetCurrentSequenceNumber(); pResource->TrackMappingBufferSequenceNumber(sequenceNumber); } void D3D9DeviceEx::TrackTextureMappingBufferSequenceNumber( D3D9CommonTexture* pResource, UINT Subresource) { uint64_t sequenceNumber = GetCurrentSequenceNumber(); pResource->TrackMappingBufferSequenceNumber(Subresource, sequenceNumber); } uint64_t D3D9DeviceEx::GetCurrentSequenceNumber() { // We do not flush empty chunks, so if we are tracking a resource // immediately after a flush, we need to use the sequence number // of the previously submitted chunk to prevent deadlocks. return m_csChunk->empty() ? m_csSeqNum : m_csSeqNum + 1; } void* D3D9DeviceEx::MapTexture(D3D9CommonTexture* pTexture, UINT Subresource) { // Will only be called inside the device lock void *ptr = pTexture->GetData(Subresource); #ifdef D3D9_ALLOW_UNMAPPING if (likely(pTexture->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE)) { m_mappedTextures.insert(pTexture); } #endif return ptr; } void D3D9DeviceEx::TouchMappedTexture(D3D9CommonTexture* pTexture) { #ifdef D3D9_ALLOW_UNMAPPING if (pTexture->GetMapMode() != D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE) return; D3D9DeviceLock lock = LockDevice(); m_mappedTextures.touch(pTexture); #endif } void D3D9DeviceEx::RemoveMappedTexture(D3D9CommonTexture* pTexture) { #ifdef D3D9_ALLOW_UNMAPPING if (pTexture->GetMapMode() != D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE) return; D3D9DeviceLock lock = LockDevice(); m_mappedTextures.remove(pTexture); #endif } void D3D9DeviceEx::UnmapTextures() { // Will only be called inside the device lock #ifdef D3D9_ALLOW_UNMAPPING uint32_t mappedMemory = m_memoryAllocator.MappedMemory(); if (likely(mappedMemory < uint32_t(m_d3d9Options.textureMemory))) return; uint32_t threshold = (m_d3d9Options.textureMemory / 4) * 3; auto iter = m_mappedTextures.leastRecentlyUsedIter(); while (m_memoryAllocator.MappedMemory() >= threshold && iter != m_mappedTextures.leastRecentlyUsedEndIter()) { if (unlikely((*iter)->IsAnySubresourceLocked() != 0)) { iter++; continue; } (*iter)->UnmapData(); iter = m_mappedTextures.remove(iter); } #endif } //////////////////////////////////// // D3D9 Device Lost //////////////////////////////////// void D3D9DeviceEx::NotifyFullscreen(HWND window, bool fullscreen) { D3D9DeviceLock lock = LockDevice(); if (fullscreen) { if (unlikely(window != m_fullscreenWindow && m_fullscreenWindow != NULL)) { Logger::warn("Multiple fullscreen windows detected."); } m_fullscreenWindow = window; } else { if (unlikely(m_fullscreenWindow != window)) { Logger::warn("Window was not fullscreen in the first place."); } else { m_fullscreenWindow = 0; } } } void D3D9DeviceEx::NotifyWindowActivated(HWND window, bool activated) { D3D9DeviceLock lock = LockDevice(); if (likely(!m_d3d9Options.deviceLossOnFocusLoss || IsExtended())) return; if (activated && m_deviceLostState == D3D9DeviceLostState::Lost) { Logger::info("Device not reset"); m_deviceLostState = D3D9DeviceLostState::NotReset; } else if (!activated && m_deviceLostState != D3D9DeviceLostState::Lost && m_fullscreenWindow == window) { Logger::info("Device lost"); m_deviceLostState = D3D9DeviceLostState::Lost; m_fullscreenWindow = NULL; } } //////////////////////////////////// // D3D9 Device Specialization State //////////////////////////////////// void D3D9DeviceEx::UpdateAlphaTestSpec(VkCompareOp alphaOp, uint32_t precision) { bool dirty = m_specInfo.set(uint32_t(alphaOp)); dirty |= m_specInfo.set(precision); if (dirty) m_flags.set(D3D9DeviceFlag::DirtySpecializationEntries); } void D3D9DeviceEx::UpdateVertexBoolSpec(uint32_t value) { if (m_specInfo.set(value)) m_flags.set(D3D9DeviceFlag::DirtySpecializationEntries); } void D3D9DeviceEx::UpdatePixelBoolSpec(uint32_t value) { if (m_specInfo.set(value)) m_flags.set(D3D9DeviceFlag::DirtySpecializationEntries); } void D3D9DeviceEx::UpdatePixelShaderSamplerSpec(uint32_t types, uint32_t projections, uint32_t fetch4) { bool dirty = m_specInfo.set(types); dirty |= m_specInfo.set(projections); dirty |= m_specInfo.set(fetch4); if (dirty) m_flags.set(D3D9DeviceFlag::DirtySpecializationEntries); } void D3D9DeviceEx::UpdateCommonSamplerSpec(uint32_t nullMask, uint32_t depthMask, uint32_t drefMask) { bool dirty = m_specInfo.set(depthMask); dirty |= m_specInfo.set(nullMask); dirty |= m_specInfo.set(drefMask); if (dirty) m_flags.set(D3D9DeviceFlag::DirtySpecializationEntries); } void D3D9DeviceEx::UpdatePointModeSpec(uint32_t mode) { if (m_specInfo.set(mode)) m_flags.set(D3D9DeviceFlag::DirtySpecializationEntries); } void D3D9DeviceEx::UpdateFogModeSpec(bool fogEnabled, D3DFOGMODE vertexFogMode, D3DFOGMODE pixelFogMode) { bool dirty = m_specInfo.set(fogEnabled); dirty |= m_specInfo.set(vertexFogMode); dirty |= m_specInfo.set(pixelFogMode); if (dirty) m_flags.set(D3D9DeviceFlag::DirtySpecializationEntries); } void D3D9DeviceEx::BindSpecConstants() { if (!m_flags.test(D3D9DeviceFlag::DirtySpecializationEntries)) return; EmitCs([cSpecInfo = m_specInfo](DxvkContext* ctx) { for (size_t i = 0; i < cSpecInfo.data.size(); i++) ctx->setSpecConstant(VK_PIPELINE_BIND_POINT_GRAPHICS, i, cSpecInfo.data[i]); }); // Write spec constants into buffer for fast-linked pipelines to use it. if (m_usingGraphicsPipelines) { // TODO: Make uploading specialization information less naive. auto mapPtr = m_specBuffer.AllocSlice(); memcpy(mapPtr, m_specInfo.data.data(), D3D9SpecializationInfo::UBOSize); } m_flags.clr(D3D9DeviceFlag::DirtySpecializationEntries); } GpuFlushType D3D9DeviceEx::GetMaxFlushType() const { if (m_d3d9Options.reproducibleCommandStream) return GpuFlushType::ExplicitFlush; else if (m_dxvkDevice->perfHints().preferRenderPassOps) return GpuFlushType::ImplicitStrongHint; else return GpuFlushType::ImplicitWeakHint; } } dxvk-2.6.1/src/d3d9/d3d9_device.h000066400000000000000000001554351477473124000163070ustar00rootroot00000000000000#pragma once #include "../dxvk/dxvk_device.h" #include "../dxvk/dxvk_cs.h" #include "../dxvk/dxvk_staging.h" #include "d3d9_include.h" #include "d3d9_cursor.h" #include "d3d9_format.h" #include "d3d9_multithread.h" #include "d3d9_adapter.h" #include "d3d9_constant_buffer.h" #include "d3d9_constant_set.h" #include "d3d9_mem.h" #include "d3d9_state.h" #include "d3d9_options.h" #include "../dxso/dxso_module.h" #include "../dxso/dxso_util.h" #include "../dxso/dxso_options.h" #include "../dxso/dxso_modinfo.h" #include "d3d9_fixed_function.h" #include "d3d9_swvp_emu.h" #include "d3d9_spec_constants.h" #include "d3d9_interop.h" #include "d3d9_on_12.h" #include #include #include "d3d9_bridge.h" #include #include #include #include "../util/util_flush.h" #include "../util/util_lru.h" namespace dxvk { class D3D9InterfaceEx; class D3D9SwapChainEx; class D3D9CommonTexture; class D3D9CommonBuffer; class D3D9CommonShader; class D3D9ShaderModuleSet; class D3D9Initializer; class D3D9Query; class D3D9StateBlock; class D3D9FormatHelper; class D3D9UserDefinedAnnotation; enum class D3D9DeviceFlag : uint32_t { DirtyFramebuffer, DirtyClipPlanes, DirtyDepthStencilState, DirtyBlendState, DirtyRasterizerState, DirtyDepthBias, DirtyAlphaTestState, DirtyInputLayout, DirtyViewportScissor, DirtyMultiSampleState, DirtyVertexBuffers, DirtyIndexBuffer, DirtyFogState, DirtyFogColor, DirtyFogDensity, DirtyFogScale, DirtyFogEnd, DirtyFFVertexData, DirtyFFVertexBlend, DirtyFFVertexShader, DirtyFFPixelShader, DirtyFFViewport, DirtyFFPixelData, DirtyProgVertexShader, DirtySharedPixelShaderData, ValidSampleMask, DirtyDepthBounds, DirtyPointScale, InScene, DirtySpecializationEntries, }; using D3D9DeviceFlags = Flags; enum class D3D9DeviceLostState { Ok = 0, Lost = 1, NotReset = 2, }; struct D3D9DrawInfo { uint32_t vertexCount; uint32_t instanceCount; }; struct D3D9BufferSlice { DxvkBufferSlice slice = {}; void* mapPtr = nullptr; }; class D3D9DeviceEx final : public ComObjectClamp { constexpr static uint32_t DefaultFrameLatency = 3; constexpr static uint32_t MaxFrameLatency = 20; constexpr static uint32_t MinFlushIntervalUs = 750; constexpr static uint32_t IncFlushIntervalUs = 250; constexpr static uint32_t MaxPendingSubmits = 6; constexpr static uint32_t NullStreamIdx = caps::MaxStreams; constexpr static VkDeviceSize StagingBufferSize = 4ull << 20; friend class D3D9SwapChainEx; friend struct D3D9WindowContext; friend class D3D9ConstantBuffer; friend class D3D9UserDefinedAnnotation; friend class DxvkD3D8Bridge; friend D3D9VkInteropDevice; public: D3D9DeviceEx( D3D9InterfaceEx* pParent, D3D9Adapter* pAdapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, Rc dxvkDevice); ~D3D9DeviceEx(); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE TestCooperativeLevel(); UINT STDMETHODCALLTYPE GetAvailableTextureMem(); HRESULT STDMETHODCALLTYPE EvictManagedResources(); HRESULT STDMETHODCALLTYPE GetDirect3D(IDirect3D9** ppD3D9); HRESULT STDMETHODCALLTYPE GetDeviceCaps(D3DCAPS9* pCaps); HRESULT STDMETHODCALLTYPE GetDisplayMode(UINT iSwapChain, D3DDISPLAYMODE* pMode); HRESULT STDMETHODCALLTYPE GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS *pParameters); HRESULT STDMETHODCALLTYPE SetCursorProperties( UINT XHotSpot, UINT YHotSpot, IDirect3DSurface9* pCursorBitmap); void STDMETHODCALLTYPE SetCursorPosition(int X, int Y, DWORD Flags); BOOL STDMETHODCALLTYPE ShowCursor(BOOL bShow); HRESULT STDMETHODCALLTYPE CreateAdditionalSwapChain( D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DSwapChain9** ppSwapChain); HRESULT STDMETHODCALLTYPE GetSwapChain(UINT iSwapChain, IDirect3DSwapChain9** pSwapChain); UINT STDMETHODCALLTYPE GetNumberOfSwapChains(); HRESULT STDMETHODCALLTYPE Reset(D3DPRESENT_PARAMETERS* pPresentationParameters); HRESULT STDMETHODCALLTYPE Present( const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion); HRESULT STDMETHODCALLTYPE GetBackBuffer( UINT iSwapChain, UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface9** ppBackBuffer); HRESULT STDMETHODCALLTYPE GetRasterStatus(UINT iSwapChain, D3DRASTER_STATUS* pRasterStatus); HRESULT STDMETHODCALLTYPE SetDialogBoxMode(BOOL bEnableDialogs); void STDMETHODCALLTYPE SetGammaRamp( UINT iSwapChain, DWORD Flags, const D3DGAMMARAMP* pRamp); void STDMETHODCALLTYPE GetGammaRamp(UINT iSwapChain, D3DGAMMARAMP* pRamp); HRESULT STDMETHODCALLTYPE CreateTexture( UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle); HRESULT STDMETHODCALLTYPE CreateVolumeTexture( UINT Width, UINT Height, UINT Depth, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DVolumeTexture9** ppVolumeTexture, HANDLE* pSharedHandle); HRESULT STDMETHODCALLTYPE CreateCubeTexture( UINT EdgeLength, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DCubeTexture9** ppCubeTexture, HANDLE* pSharedHandle); HRESULT STDMETHODCALLTYPE CreateVertexBuffer( UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9** ppVertexBuffer, HANDLE* pSharedHandle); HRESULT STDMETHODCALLTYPE CreateIndexBuffer( UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer9** ppIndexBuffer, HANDLE* pSharedHandle); HRESULT STDMETHODCALLTYPE CreateRenderTarget( UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle); HRESULT STDMETHODCALLTYPE CreateDepthStencilSurface( UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle); HRESULT STDMETHODCALLTYPE UpdateSurface( IDirect3DSurface9* pSourceSurface, const RECT* pSourceRect, IDirect3DSurface9* pDestinationSurface, const POINT* pDestPoint); HRESULT STDMETHODCALLTYPE UpdateTexture( IDirect3DBaseTexture9* pSourceTexture, IDirect3DBaseTexture9* pDestinationTexture); HRESULT STDMETHODCALLTYPE GetRenderTargetData( IDirect3DSurface9* pRenderTarget, IDirect3DSurface9* pDestSurface); HRESULT STDMETHODCALLTYPE GetFrontBufferData(UINT iSwapChain, IDirect3DSurface9* pDestSurface); HRESULT STDMETHODCALLTYPE StretchRect( IDirect3DSurface9* pSourceSurface, const RECT* pSourceRect, IDirect3DSurface9* pDestSurface, const RECT* pDestRect, D3DTEXTUREFILTERTYPE Filter); HRESULT STDMETHODCALLTYPE ColorFill( IDirect3DSurface9* pSurface, const RECT* pRect, D3DCOLOR Color); HRESULT STDMETHODCALLTYPE CreateOffscreenPlainSurface( UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle); HRESULT STDMETHODCALLTYPE SetRenderTarget( DWORD RenderTargetIndex, IDirect3DSurface9* pRenderTarget); HRESULT STDMETHODCALLTYPE GetRenderTarget( DWORD RenderTargetIndex, IDirect3DSurface9** ppRenderTarget); HRESULT STDMETHODCALLTYPE SetDepthStencilSurface(IDirect3DSurface9* pNewZStencil); HRESULT STDMETHODCALLTYPE GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface); HRESULT STDMETHODCALLTYPE BeginScene(); HRESULT STDMETHODCALLTYPE EndScene(); HRESULT STDMETHODCALLTYPE Clear( DWORD Count, const D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil); HRESULT STDMETHODCALLTYPE SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix); HRESULT STDMETHODCALLTYPE GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix); HRESULT STDMETHODCALLTYPE MultiplyTransform(D3DTRANSFORMSTATETYPE TransformState, const D3DMATRIX* pMatrix); HRESULT STDMETHODCALLTYPE SetViewport(const D3DVIEWPORT9* pViewport); HRESULT STDMETHODCALLTYPE GetViewport(D3DVIEWPORT9* pViewport); HRESULT STDMETHODCALLTYPE SetMaterial(const D3DMATERIAL9* pMaterial); HRESULT STDMETHODCALLTYPE GetMaterial(D3DMATERIAL9* pMaterial); HRESULT STDMETHODCALLTYPE SetLight(DWORD Index, const D3DLIGHT9* pLight); HRESULT STDMETHODCALLTYPE GetLight(DWORD Index, D3DLIGHT9* pLight); HRESULT STDMETHODCALLTYPE LightEnable(DWORD Index, BOOL Enable); HRESULT STDMETHODCALLTYPE GetLightEnable(DWORD Index, BOOL* pEnable); HRESULT STDMETHODCALLTYPE SetClipPlane(DWORD Index, const float* pPlane); HRESULT STDMETHODCALLTYPE GetClipPlane(DWORD Index, float* pPlane); HRESULT STDMETHODCALLTYPE SetRenderState(D3DRENDERSTATETYPE State, DWORD Value); HRESULT STDMETHODCALLTYPE GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue); HRESULT STDMETHODCALLTYPE CreateStateBlock( D3DSTATEBLOCKTYPE Type, IDirect3DStateBlock9** ppSB); HRESULT STDMETHODCALLTYPE BeginStateBlock(); HRESULT STDMETHODCALLTYPE EndStateBlock(IDirect3DStateBlock9** ppSB); HRESULT STDMETHODCALLTYPE SetClipStatus(const D3DCLIPSTATUS9* pClipStatus); HRESULT STDMETHODCALLTYPE GetClipStatus(D3DCLIPSTATUS9* pClipStatus); HRESULT STDMETHODCALLTYPE GetTexture(DWORD Stage, IDirect3DBaseTexture9** ppTexture); HRESULT STDMETHODCALLTYPE SetTexture(DWORD Stage, IDirect3DBaseTexture9* pTexture); HRESULT STDMETHODCALLTYPE GetTextureStageState( DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD* pValue); HRESULT STDMETHODCALLTYPE SetTextureStageState( DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value); HRESULT STDMETHODCALLTYPE GetSamplerState( DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD* pValue); HRESULT STDMETHODCALLTYPE SetSamplerState( DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value); HRESULT STDMETHODCALLTYPE ValidateDevice(DWORD* pNumPasses); HRESULT STDMETHODCALLTYPE SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries); HRESULT STDMETHODCALLTYPE GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries); HRESULT STDMETHODCALLTYPE SetCurrentTexturePalette(UINT PaletteNumber); HRESULT STDMETHODCALLTYPE GetCurrentTexturePalette(UINT *PaletteNumber); HRESULT STDMETHODCALLTYPE SetScissorRect(const RECT* pRect); HRESULT STDMETHODCALLTYPE GetScissorRect(RECT* pRect); HRESULT STDMETHODCALLTYPE SetSoftwareVertexProcessing(BOOL bSoftware); BOOL STDMETHODCALLTYPE GetSoftwareVertexProcessing(); HRESULT STDMETHODCALLTYPE SetNPatchMode(float nSegments); float STDMETHODCALLTYPE GetNPatchMode(); HRESULT STDMETHODCALLTYPE DrawPrimitive( D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount); HRESULT STDMETHODCALLTYPE DrawIndexedPrimitive( D3DPRIMITIVETYPE PrimitiveType, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT StartIndex, UINT PrimitiveCount); HRESULT STDMETHODCALLTYPE DrawPrimitiveUP( D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride); HRESULT STDMETHODCALLTYPE DrawIndexedPrimitiveUP( D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT PrimitiveCount, const void* pIndexData, D3DFORMAT IndexDataFormat, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride); HRESULT STDMETHODCALLTYPE ProcessVertices( UINT SrcStartIndex, UINT DestIndex, UINT VertexCount, IDirect3DVertexBuffer9* pDestBuffer, IDirect3DVertexDeclaration9* pVertexDecl, DWORD Flags); HRESULT STDMETHODCALLTYPE CreateVertexDeclaration( const D3DVERTEXELEMENT9* pVertexElements, IDirect3DVertexDeclaration9** ppDecl); HRESULT STDMETHODCALLTYPE SetVertexDeclaration(IDirect3DVertexDeclaration9* pDecl); HRESULT STDMETHODCALLTYPE GetVertexDeclaration(IDirect3DVertexDeclaration9** ppDecl); HRESULT STDMETHODCALLTYPE SetFVF(DWORD FVF); HRESULT STDMETHODCALLTYPE GetFVF(DWORD* pFVF); HRESULT STDMETHODCALLTYPE CreateVertexShader( const DWORD* pFunction, IDirect3DVertexShader9** ppShader); HRESULT STDMETHODCALLTYPE SetVertexShader(IDirect3DVertexShader9* pShader); HRESULT STDMETHODCALLTYPE GetVertexShader(IDirect3DVertexShader9** ppShader); HRESULT STDMETHODCALLTYPE SetVertexShaderConstantF( UINT StartRegister, const float* pConstantData, UINT Vector4fCount); HRESULT STDMETHODCALLTYPE GetVertexShaderConstantF( UINT StartRegister, float* pConstantData, UINT Vector4fCount); HRESULT STDMETHODCALLTYPE SetVertexShaderConstantI( UINT StartRegister, const int* pConstantData, UINT Vector4iCount); HRESULT STDMETHODCALLTYPE GetVertexShaderConstantI( UINT StartRegister, int* pConstantData, UINT Vector4iCount); HRESULT STDMETHODCALLTYPE SetVertexShaderConstantB( UINT StartRegister, const BOOL* pConstantData, UINT BoolCount); HRESULT STDMETHODCALLTYPE GetVertexShaderConstantB( UINT StartRegister, BOOL* pConstantData, UINT BoolCount); HRESULT STDMETHODCALLTYPE SetStreamSource( UINT StreamNumber, IDirect3DVertexBuffer9* pStreamData, UINT OffsetInBytes, UINT Stride); HRESULT STDMETHODCALLTYPE GetStreamSource( UINT StreamNumber, IDirect3DVertexBuffer9** ppStreamData, UINT* pOffsetInBytes, UINT* pStride); HRESULT STDMETHODCALLTYPE SetStreamSourceFreq(UINT StreamNumber, UINT Setting); HRESULT STDMETHODCALLTYPE GetStreamSourceFreq(UINT StreamNumber, UINT* pSetting); HRESULT STDMETHODCALLTYPE SetIndices(IDirect3DIndexBuffer9* pIndexData); HRESULT STDMETHODCALLTYPE GetIndices(IDirect3DIndexBuffer9** ppIndexData); HRESULT STDMETHODCALLTYPE CreatePixelShader( const DWORD* pFunction, IDirect3DPixelShader9** ppShader); HRESULT STDMETHODCALLTYPE SetPixelShader(IDirect3DPixelShader9* pShader); HRESULT STDMETHODCALLTYPE GetPixelShader(IDirect3DPixelShader9** ppShader); HRESULT STDMETHODCALLTYPE SetPixelShaderConstantF( UINT StartRegister, const float* pConstantData, UINT Vector4fCount); HRESULT STDMETHODCALLTYPE GetPixelShaderConstantF( UINT StartRegister, float* pConstantData, UINT Vector4fCount); HRESULT STDMETHODCALLTYPE SetPixelShaderConstantI( UINT StartRegister, const int* pConstantData, UINT Vector4iCount); HRESULT STDMETHODCALLTYPE GetPixelShaderConstantI( UINT StartRegister, int* pConstantData, UINT Vector4iCount); HRESULT STDMETHODCALLTYPE SetPixelShaderConstantB( UINT StartRegister, const BOOL* pConstantData, UINT BoolCount); HRESULT STDMETHODCALLTYPE GetPixelShaderConstantB( UINT StartRegister, BOOL* pConstantData, UINT BoolCount); HRESULT STDMETHODCALLTYPE DrawRectPatch( UINT Handle, const float* pNumSegs, const D3DRECTPATCH_INFO* pRectPatchInfo); HRESULT STDMETHODCALLTYPE DrawTriPatch( UINT Handle, const float* pNumSegs, const D3DTRIPATCH_INFO* pTriPatchInfo); HRESULT STDMETHODCALLTYPE DeletePatch(UINT Handle); HRESULT STDMETHODCALLTYPE CreateQuery(D3DQUERYTYPE Type, IDirect3DQuery9** ppQuery); // Ex Methods HRESULT STDMETHODCALLTYPE SetConvolutionMonoKernel( UINT width, UINT height, float* rows, float* columns); HRESULT STDMETHODCALLTYPE ComposeRects( IDirect3DSurface9* pSrc, IDirect3DSurface9* pDst, IDirect3DVertexBuffer9* pSrcRectDescs, UINT NumRects, IDirect3DVertexBuffer9* pDstRectDescs, D3DCOMPOSERECTSOP Operation, int Xoffset, int Yoffset); HRESULT STDMETHODCALLTYPE GetGPUThreadPriority(INT* pPriority); HRESULT STDMETHODCALLTYPE SetGPUThreadPriority(INT Priority); HRESULT STDMETHODCALLTYPE WaitForVBlank(UINT iSwapChain); HRESULT STDMETHODCALLTYPE CheckResourceResidency(IDirect3DResource9** pResourceArray, UINT32 NumResources); HRESULT STDMETHODCALLTYPE SetMaximumFrameLatency(UINT MaxLatency); HRESULT STDMETHODCALLTYPE GetMaximumFrameLatency(UINT* pMaxLatency); HRESULT STDMETHODCALLTYPE CheckDeviceState(HWND hDestinationWindow); HRESULT STDMETHODCALLTYPE PresentEx( const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion, DWORD dwFlags); HRESULT STDMETHODCALLTYPE CreateRenderTargetEx( UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage); HRESULT STDMETHODCALLTYPE CreateOffscreenPlainSurfaceEx( UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage); HRESULT STDMETHODCALLTYPE CreateDepthStencilSurfaceEx( UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage); HRESULT STDMETHODCALLTYPE ResetEx( D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode); HRESULT STDMETHODCALLTYPE GetDisplayModeEx( UINT iSwapChain, D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation); HRESULT STDMETHODCALLTYPE CreateAdditionalSwapChainEx( D3DPRESENT_PARAMETERS* pPresentationParameters, const D3DDISPLAYMODEEX* pFullscreenDisplayMode, IDirect3DSwapChain9** ppSwapChain); /** * @brief Sets the given sampler state * * @param StateSampler Sampler index (according to our internal way of storing samplers) * @param Type Sampler state type to change * @param Value State value */ HRESULT SetStateSamplerState( DWORD StateSampler, D3DSAMPLERSTATETYPE Type, DWORD Value); /** * @brief Sets the given sampler texture * * @param StateSampler Sampler index (according to our internal way of storing samplers) * @param pTexture Texture to use */ HRESULT SetStateTexture(DWORD StateSampler, IDirect3DBaseTexture9* pTexture); /** * @brief Sets the transform for the given sampler * * @param idx Sampler index (according to our internal way of storing samplers) * @param pMatrix Transform matrix */ HRESULT SetStateTransform(uint32_t idx, const D3DMATRIX* pMatrix); /** * @brief Sets the fixed function texture processing state * * @param Stage Sampler index (according to our internal way of storing samplers) * @param Type Fixed function texture stage type * @param Value Value for the state */ HRESULT SetStateTextureStageState( DWORD Stage, D3D9TextureStageStateTypes Type, DWORD Value); VkPipelineStageFlags GetEnabledShaderStages() const { return m_dxvkDevice->getShaderPipelineStages(); } static DxvkDeviceFeatures GetDeviceFeatures(const Rc& adapter); /** * \brief Returns whether the Vulkan device supports the required features for ProcessVertices */ bool SupportsSWVP(); bool IsExtended(); HWND GetWindow(); const Rc& GetDXVKDevice() { return m_dxvkDevice; } D3D9_VK_FORMAT_MAPPING LookupFormat( D3D9Format Format) const; const DxvkFormatInfo* UnsupportedFormatInfo( D3D9Format Format) const; bool WaitForResource( const DxvkPagedResource& Resource, uint64_t SequenceNumber, DWORD MapFlags); /** * \brief Locks a subresource of an image * * \param [in] Subresource The subresource of the image to lock * \param [out] pLockedBox The returned locked box of the image, containing data ptr and strides * \param [in] pBox The region of the subresource to lock. This offsets the returned data ptr * \param [in] Flags The D3DLOCK_* flags to lock the image with * \returns \c D3D_OK if the parameters are valid or D3DERR_INVALIDCALL if it fails. */ HRESULT LockImage( D3D9CommonTexture* pResource, UINT Face, UINT Mip, D3DLOCKED_BOX* pLockedBox, const D3DBOX* pBox, DWORD Flags); uint32_t CalcImageLockOffset( uint32_t SlicePitch, uint32_t RowPitch, const DxvkFormatInfo* FormatInfo, const D3DBOX* pBox); /** * \brief Unlocks a subresource of an image * * Passthrough to device unlock. * \param [in] Subresource The subresource of the image to unlock * \returns \c D3D_OK if the parameters are valid or D3DERR_INVALIDCALL if it fails. */ HRESULT UnlockImage( D3D9CommonTexture* pResource, UINT Face, UINT MipLevel); /** * \brief Uploads the given texture subresource from its local system memory copy. */ HRESULT FlushImage( D3D9CommonTexture* pResource, UINT Subresource); /** * \brief Copies the given part of a texture from the local system memory copy of the source texture * to the image of the destination texture. */ void UpdateTextureFromBuffer( D3D9CommonTexture* pDestTexture, D3D9CommonTexture* pSrcTexture, UINT DestSubresource, UINT SrcSubresource, VkOffset3D SrcOffset, VkExtent3D SrcExtent, VkOffset3D DestOffset); void EmitGenerateMips( D3D9CommonTexture* pResource); HRESULT LockBuffer( D3D9CommonBuffer* pResource, UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags); /** * \brief Uploads the given buffer from its local system memory copy. */ HRESULT FlushBuffer( D3D9CommonBuffer* pResource); HRESULT UnlockBuffer( D3D9CommonBuffer* pResource); /** * @brief Uploads data from D3DPOOL_SYSMEM + D3DUSAGE_DYNAMIC buffers and binds the temporary buffers. * * @param FirstVertexIndex The first vertex * @param NumVertices The number of vertices that are accessed. If this is 0, the vertex buffer binding will not be modified. * @param FirstIndex The first index * @param NumIndices The number of indices that will be drawn. If this is 0, the index buffer binding will not be modified. */ void UploadPerDrawData( UINT& FirstVertexIndex, UINT NumVertices, UINT& FirstIndex, UINT NumIndices, INT& BaseVertexIndex, bool* pDynamicVBOs, bool* pDynamicIBO); void SetupFPU(); int64_t DetermineInitialTextureMemory(); void CreateConstantBuffers(); void SynchronizeCsThread(uint64_t SequenceNumber); void Flush(); void FlushAndSync9On12(); void BeginFrame(Rc LatencyTracker, uint64_t FrameId); void EndFrame(Rc LatencyTracker); void UpdateActiveRTs(uint32_t index); template void UpdateAnyColorWrites(); void UpdateTextureBitmasks(uint32_t index, DWORD combinedUsage); void UpdateActiveHazardsRT(uint32_t rtMask); void UpdateActiveHazardsDS(uint32_t texMask); void MarkRenderHazards(); void UpdateActiveFetch4(uint32_t stateSampler); /** * @brief Sets the mismatching texture type bits for all samplers if necessary. * * 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 * match the texture type expected by the respective shader. * * It will *not* unset the bit if the texture type does match. * * @param stateSampler Sampler index (according to our internal way of storing samplers) */ /** * @brief Sets the mismatching texture type bits for all samplers if necessary. * * 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 * match the texture type expected by the shader. * * @param shader The shader * @param shaderSamplerMask Mask of all samplers that the shader uses (according to our internal way of storing samplers) * @param shaderSamplerOffset First index of the shader's samplers according to our internal way of storing samplers. * Used to transform the sampler indices that are relative to the entire pipeline to ones relative to the shader. */ void UpdateTextureTypeMismatchesForShader(const D3D9CommonShader* shader, uint32_t shaderSamplerMask, uint32_t shaderSamplerOffset); /** * @brief Sets the mismatching texture type bit for the given sampler. * * This function will set the mismatching texture type bit for the given sampler if it does not * match the texture type expected by the respective shader. * * It will *not* unset the bit if the texture type does match. * * @param stateSampler Sampler index (according to our internal way of storing samplers) */ void UpdateTextureTypeMismatchesForTexture(uint32_t stateSampler); void UploadManagedTexture(D3D9CommonTexture* pResource); void UploadManagedTextures(uint32_t mask); void GenerateTextureMips(uint32_t mask); void MarkTextureMipsDirty(D3D9CommonTexture* pResource); void MarkTextureMipsUnDirty(D3D9CommonTexture* pResource); void MarkTextureUploaded(D3D9CommonTexture* pResource); void UpdatePointMode(bool pointList); void UpdateFog(); void BindFramebuffer(); void BindViewportAndScissor(); inline bool IsAlphaToCoverageEnabled() { const bool alphaTest = m_state.renderStates[D3DRS_ALPHATESTENABLE] != 0; return (m_amdATOC || (m_nvATOC && alphaTest)) && m_flags.test(D3D9DeviceFlag::ValidSampleMask); } inline bool IsDepthBiasEnabled() { const auto& rs = m_state.renderStates; float depthBias = bit::cast(rs[D3DRS_DEPTHBIAS]); float slopeScaledDepthBias = bit::cast(rs[D3DRS_SLOPESCALEDEPTHBIAS]); return depthBias != 0.0f || slopeScaledDepthBias != 0.0f; } inline bool IsAlphaTestEnabled() { return m_state.renderStates[D3DRS_ALPHATESTENABLE] && !IsAlphaToCoverageEnabled(); } inline bool IsZTestEnabled() { return m_state.renderStates[D3DRS_ZENABLE] && m_state.depthStencil != nullptr; } inline bool IsClipPlaneEnabled() { return m_state.renderStates[D3DRS_CLIPPLANEENABLE] != 0; } void BindMultiSampleState(); void BindBlendState(); void BindBlendFactor(); void BindDepthStencilState(); void BindDepthStencilRefrence(); void BindRasterizerState(); void BindDepthBias(); inline void UploadSoftwareConstantSet(const D3D9ShaderConstantsVSSoftware& Src, const D3D9ConstantLayout& Layout); inline void* CopySoftwareConstants(D3D9ConstantBuffer& dstBuffer, const void* src, uint32_t size); template inline void UploadConstantSet(const SoftwareLayoutType& Src, const D3D9ConstantLayout& Layout, const ShaderType& Shader); template void UploadConstants(); void UpdateClipPlanes(); /** * \brief Updates the push constant data at the given offset with data from the specified pointer. * * \param Offset Offset at which the push constant data gets written. * \param Length Length of the push constant data to write. * \param pData Push constant data */ template void UpdatePushConstant(const void* pData); /** * \brief Updates the specified push constant based on the device state. * * \param Item Render state push constant to update */ template void UpdatePushConstant(); void BindSampler(DWORD Sampler); void BindTexture(DWORD SamplerSampler); void UnbindTextures(uint32_t mask); void UndirtySamplers(uint32_t mask); void UndirtyTextures(uint32_t usedMask); void MarkTextureBindingDirty(IDirect3DBaseTexture9* texture); HRESULT STDMETHODCALLTYPE SetRenderTargetInternal( DWORD RenderTargetIndex, IDirect3DSurface9* pRenderTarget); D3D9DrawInfo GenerateDrawInfo( D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, UINT InstanceCount); uint32_t GetInstanceCount() const; void PrepareDraw(D3DPRIMITIVETYPE PrimitiveType, bool UploadVBOs, bool UploadIBOs); void EnsureSamplerLimit(); template void BindShader( const D3D9CommonShader* pShaderModule); void BindInputLayout(); void BindVertexBuffer( UINT Slot, D3D9VertexBuffer* pBuffer, UINT Offset, UINT Stride); void BindIndices(); D3D9DeviceLock LockDevice() { return m_multithread.AcquireLock(); } const D3D9Options* GetOptions() const { return &m_d3d9Options; } Direct3DState9* GetRawState() { return &m_state; } void Begin(D3D9Query* pQuery); void End(D3D9Query* pQuery); void SetVertexBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits); void SetPixelBoolBitfield (uint32_t idx, uint32_t mask, uint32_t bits); void ConsiderFlush(GpuFlushType FlushType); bool ChangeReportedMemory(int64_t delta) { if (IsExtended()) return true; int64_t availableMemory = m_availableMemory.fetch_add(delta); return !m_d3d9Options.memoryTrackTest || availableMemory >= delta; } void ResolveZ(); void TransitionImage(D3D9CommonTexture* pResource, VkImageLayout NewLayout); void TransformImage( D3D9CommonTexture* pResource, const VkImageSubresourceRange* pSubresources, VkImageLayout OldLayout, VkImageLayout NewLayout); const D3D9ConstantLayout& GetVertexConstantLayout() { return m_consts[DxsoProgramType::VertexShader].layout; } const D3D9ConstantLayout& GetPixelConstantLayout() { return m_consts[DxsoProgramType::PixelShader].layout; } void ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters); HRESULT ResetSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode); HRESULT InitialReset(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode); /** * \brief Returns the allocator used for unmappable system memory texture data */ D3D9MemoryAllocator* GetAllocator() { return &m_memoryAllocator; } /** * \brief Gets the pointer of the system memory copy of the texture * * Also tracks the texture if it is unmappable. */ void* MapTexture(D3D9CommonTexture* pTexture, UINT Subresource); /** * \brief Moves the texture to the front of the LRU list of mapped textures */ void TouchMappedTexture(D3D9CommonTexture* pTexture); /** * \brief Removes the texture from the LRU list of mapped textures */ void RemoveMappedTexture(D3D9CommonTexture* pTexture); /** * \brief Returns whether the device is currently recording a StateBlock */ bool ShouldRecord() const { return m_recorder != nullptr; } bool IsD3D8Compatible() const { return m_isD3D8Compatible; } // Device Lost bool IsDeviceLost() const { return m_deviceLostState != D3D9DeviceLostState::Ok; } void NotifyFullscreen(HWND window, bool fullscreen); void NotifyWindowActivated(HWND window, bool activated); /** * \brief Increases the amount of D3DPOOL_DEFAULT resources that block a device reset */ void IncrementLosableCounter() { m_losableResourceCounter++; } /** * \brief Decreases the amount of D3DPOOL_DEFAULT resources that block a device reset */ void DecrementLosableCounter() { m_losableResourceCounter--; } /** * \brief Returns whether the device is configured to only support vertex processing. */ bool CanOnlySWVP() const { return m_behaviorFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING; } /** * \brief Returns whether the device can be set to do software vertex processing. * It may also be set up to only support software vertex processing. */ bool CanSWVP() const { return m_behaviorFlags & (D3DCREATE_MIXED_VERTEXPROCESSING | D3DCREATE_SOFTWARE_VERTEXPROCESSING); } /** * \brief Returns whether or not the device is currently set to do software vertex processing. */ bool IsSWVP() const { return m_isSWVP; } /** * \brief Returns the number of vertex shader modules generated for fixed function state. */ UINT GetFixedFunctionVSCount() const { return m_ffModules.GetVSCount(); } /** * \brief Returns the number of fragment shader modules generated for fixed function state. */ UINT GetFixedFunctionFSCount() const { return m_ffModules.GetFSCount(); } /** * \brief Returns the number of shader modules generated for ProcessVertices. */ UINT GetSWVPShaderCount() const { return m_swvpEmulator.GetShaderCount(); } void InjectCsChunk( DxvkCsChunkRef&& Chunk, bool Synchronize); template void InjectCs( Fn&& Command) { auto chunk = AllocCsChunk(); chunk->push(std::move(Command)); InjectCsChunk(std::move(chunk), false); } DxvkCsChunkRef AllocCsChunk() { DxvkCsChunk* chunk = m_csChunkPool.allocChunk(DxvkCsChunkFlag::SingleUse); return DxvkCsChunkRef(chunk, &m_csChunkPool); } private: template void EmitCs(Cmd&& command) { if (unlikely(!m_csChunk->push(command))) { EmitCsChunk(std::move(m_csChunk)); m_csChunk = AllocCsChunk(); if constexpr (AllowFlush) ConsiderFlush(GpuFlushType::ImplicitWeakHint); m_csChunk->push(command); } } void EmitCsChunk(DxvkCsChunkRef&& chunk); void FlushCsChunk() { if (likely(!m_csChunk->empty())) { EmitCsChunk(std::move(m_csChunk)); m_csChunk = AllocCsChunk(); } } /** * \brief Queries current reset counter * Used for the deferred surface creation workaround. * (Device Reset detection for D3D9SwapChainEx::Present) */ uint32_t GetResetCounter() { return m_resetCtr; } template void ExecuteFlush(); void DetermineConstantLayouts(bool canSWVP); /** * \brief Allocates buffer memory for DrawPrimitiveUp draws */ D3D9BufferSlice AllocUPBuffer(VkDeviceSize size); /** * \brief Allocates buffer memory for resource uploads */ D3D9BufferSlice AllocStagingBuffer(VkDeviceSize size); /** * \brief Waits until the amount of used staging memory is below a certain threshold. */ void WaitStagingBuffer(); HRESULT CreateShaderModule( D3D9CommonShader* pShaderModule, uint32_t* pLength, VkShaderStageFlagBits ShaderStage, const DWORD* pShaderBytecode, const DxsoModuleInfo* pModuleInfo); inline uint32_t GetUPDataSize(uint32_t vertexCount, uint32_t stride) { return vertexCount * stride; } inline uint32_t GetUPBufferSize(uint32_t vertexCount, uint32_t stride) { return (vertexCount - 1) * stride + std::max(m_state.vertexDecl->GetSize(0), stride); } /** * \brief Writes data to the given pointer and zeroes any access buffer space */ inline void FillUPVertexBuffer(void* buffer, const void* userData, uint32_t dataSize, uint32_t bufferSize) { uint8_t* data = reinterpret_cast(buffer); // Don't copy excess data if we don't end up needing it. dataSize = std::min(dataSize, bufferSize); std::memcpy(data, userData, dataSize); // Pad out with 0 to make buffer range checks happy // Some games have components out of range in the vertex decl // that they don't read from the shader. // My tests show that these are read back as 0 always if out of range of // the dataSize. // // So... make the actual buffer the range that satisfies the range of the vertex // declaration and pad with 0s outside of it. if (dataSize < bufferSize) std::memset(data + dataSize, 0, bufferSize - dataSize); } // So we don't do OOB. template inline static constexpr uint32_t DetermineSoftwareRegCount() { constexpr bool isVS = ProgramType == DxsoProgramType::VertexShader; switch (ConstantType) { default: case D3D9ConstantType::Float: return isVS ? caps::MaxFloatConstantsSoftware : caps::MaxFloatConstantsPS; case D3D9ConstantType::Int: return isVS ? caps::MaxOtherConstantsSoftware : caps::MaxOtherConstants; case D3D9ConstantType::Bool: return isVS ? caps::MaxOtherConstantsSoftware : caps::MaxOtherConstants; } } // So we don't copy more than we need. template inline uint32_t DetermineHardwareRegCount() const { const auto& layout = m_consts[ProgramType].layout; switch (ConstantType) { default: case D3D9ConstantType::Float: return layout.floatCount; case D3D9ConstantType::Int: return layout.intCount; case D3D9ConstantType::Bool: return layout.boolCount; } } inline uint32_t GetFrameLatency() { return m_frameLatency; } template < DxsoProgramType ProgramType, D3D9ConstantType ConstantType, typename T> HRESULT SetShaderConstants( UINT StartRegister, const T* pConstantData, UINT Count); template < DxsoProgramType ProgramType, D3D9ConstantType ConstantType, typename T> HRESULT GetShaderConstants( UINT StartRegister, T* pConstantData, UINT Count) { auto GetHelper = [&] (const auto& set) { const uint32_t regCountHardware = DetermineHardwareRegCount(); constexpr uint32_t regCountSoftware = DetermineSoftwareRegCount(); if (StartRegister + Count > regCountSoftware) return D3DERR_INVALIDCALL; Count = UINT( std::max( std::clamp(Count + StartRegister, 0, regCountHardware) - INT(StartRegister), 0)); if (Count == 0) return D3D_OK; if (pConstantData == nullptr) return D3DERR_INVALIDCALL; if constexpr (ConstantType == D3D9ConstantType::Float) { const float* source = set->fConsts[StartRegister].data; const size_t size = Count * sizeof(Vector4); std::memcpy(pConstantData, source, size); } else if constexpr (ConstantType == D3D9ConstantType::Int) { const int* source = set->iConsts[StartRegister].data; const size_t size = Count * sizeof(Vector4i); std::memcpy(pConstantData, source, size); } else { for (uint32_t i = 0; i < Count; i++) { const uint32_t constantIdx = StartRegister + i; const uint32_t arrayIdx = constantIdx / 32; const uint32_t bitIdx = constantIdx % 32; const uint32_t bit = (1u << bitIdx); bool constValue = set->bConsts[arrayIdx] & bit; pConstantData[i] = constValue ? TRUE : FALSE; } } return D3D_OK; }; return ProgramType == DxsoProgramTypes::VertexShader ? GetHelper(m_state.vsConsts) : GetHelper(m_state.psConsts); } void UpdateFixedFunctionVS(); void UpdateFixedFunctionPS(); void ApplyPrimitiveType( DxvkContext* pContext, D3DPRIMITIVETYPE PrimType); bool UseProgrammableVS(); bool UseProgrammablePS(); uint32_t GetAlphaTestPrecision(); void BindAlphaTestState(); void UpdateAlphaTestSpec(VkCompareOp alphaOp, uint32_t precision); void UpdateVertexBoolSpec(uint32_t value); void UpdatePixelBoolSpec(uint32_t value); void UpdatePixelShaderSamplerSpec(uint32_t types, uint32_t projections, uint32_t fetch4); void UpdateCommonSamplerSpec(uint32_t boundMask, uint32_t depthMask, uint32_t drefMask); void UpdatePointModeSpec(uint32_t mode); void UpdateFogModeSpec(bool fogEnabled, D3DFOGMODE vertexFogMode, D3DFOGMODE pixelFogMode); void BindSpecConstants(); void TrackBufferMappingBufferSequenceNumber( D3D9CommonBuffer* pResource); void TrackTextureMappingBufferSequenceNumber( D3D9CommonTexture* pResource, UINT Subresource); uint64_t GetCurrentSequenceNumber(); /** * \brief Will unmap the least recently used textures if the amount of mapped texture memory exceeds a threshold. */ void UnmapTextures(); /** * \brief Get the swapchain that was used the most recently for presenting * Has to be externally synchronized. */ D3D9SwapChainEx* GetMostRecentlyUsedSwapchain() { return m_mostRecentlyUsedSwapchain; } /** * \brief Set the swapchain that was used the most recently for presenting * Has to be externally synchronized. */ void SetMostRecentlyUsedSwapchain(D3D9SwapChainEx* swapchain) { m_mostRecentlyUsedSwapchain = swapchain; } /** * @brief Reset the most recently swapchain back to the implicit one * Has to be externally synchronized. */ void ResetMostRecentlyUsedSwapchain() { m_mostRecentlyUsedSwapchain = m_implicitSwapchain.ptr(); } bool IsTextureBoundAsAttachment(const D3D9CommonTexture* pTexture) const { if (unlikely(pTexture->IsRenderTarget())) { for (uint32_t i = 0u; i < m_state.renderTargets.size(); i++) { if (m_state.renderTargets[i] == nullptr) continue; auto texInfo = m_state.renderTargets[i]->GetCommonTexture(); if (unlikely(texInfo == pTexture)) { return true; } } } else if (unlikely(pTexture->IsDepthStencil() && m_state.depthStencil != nullptr)) { auto texInfo = m_state.depthStencil->GetCommonTexture(); if (unlikely(texInfo == pTexture)) { return true; } } return false; } inline bool HasRenderTargetBound(uint32_t Index) const { return m_state.renderTargets[Index] != nullptr && !m_state.renderTargets[Index]->IsNull(); } GpuFlushType GetMaxFlushType() const; Com m_parent; D3DDEVTYPE m_deviceType; HWND m_window; WORD m_behaviorFlags; D3D9Adapter* m_adapter; Rc m_dxvkDevice; D3D9MemoryAllocator m_memoryAllocator; // Second memory allocator used for D3D9 shader bytecode. // Most games never access the stored bytecode, so putting that // into the same chunks as texture memory would waste address space. D3D9MemoryAllocator m_shaderAllocator; uint32_t m_frameLatency = DefaultFrameLatency; D3D9Initializer* m_initializer = nullptr; D3D9FormatHelper* m_converter = nullptr; D3D9FFShaderModuleSet m_ffModules; D3D9SWVPEmulator m_swvpEmulator; Com m_recorder; Rc m_shaderModules; D3D9ConstantBuffer m_vsClipPlanes; D3D9ConstantBuffer m_vsFixedFunction; D3D9ConstantBuffer m_vsVertexBlend; D3D9ConstantBuffer m_psFixedFunction; D3D9ConstantBuffer m_psShared; D3D9ConstantBuffer m_specBuffer; Rc m_upBuffer; VkDeviceSize m_upBufferOffset = 0ull; void* m_upBufferMapPtr = nullptr; DxvkStagingBuffer m_stagingBuffer; Rc m_stagingBufferFence; VkDeviceSize m_stagingMemorySignaled = 0ull; D3D9Cursor m_cursor; Com m_autoDepthStencil; Com m_implicitSwapchain; const D3D9Options m_d3d9Options; DxsoOptions m_dxsoOptions; std::unordered_map< DWORD, Com> m_fvfTable; D3D9Multithread m_multithread; D3D9InputAssemblyState m_iaState; D3D9DeviceFlags m_flags; // Last state of depth textures. Doesn't update when NULL is bound. // & with m_activeTextures to normalize. uint32_t m_instancedData = 0; uint32_t m_depthTextures = 0; uint32_t m_drefClamp = 0; uint32_t m_cubeTextures = 0; uint32_t m_textureTypes = 0; uint32_t m_mismatchingTextureTypes = 0; uint32_t m_projectionBitfield = 0; uint32_t m_dirtySamplerStates = 0; uint32_t m_dirtyTextures = 0; uint32_t m_activeRTsWhichAreTextures : 4; uint32_t m_alphaSwizzleRTs : 4; uint32_t m_lastHazardsRT : 4; uint32_t m_activeTextureRTs = 0; uint32_t m_activeTextureDSs = 0; uint32_t m_activeHazardsRT = 0; uint32_t m_activeHazardsDS = 0; uint32_t m_activeTextures = 0; uint32_t m_activeTexturesToUpload = 0; uint32_t m_activeTexturesToGen = 0; uint32_t m_activeVertexBuffers = 0; uint32_t m_activeVertexBuffersToUpload = 0; uint32_t m_activeVertexBuffersToUploadPerDraw = 0; // m_fetch4Enabled is whether fetch4 is currently enabled // from the application. // // m_fetch4 is whether it should be enabled in the shader // ie. are we in a correct state to use it // (enabled + texture supports it + point sampled) uint32_t m_fetch4Enabled = 0; uint32_t m_fetch4 = 0; uint32_t m_lastHazardsDS = 0; uint32_t m_lastSamplerTypesFF = 0; D3D9SpecializationInfo m_specInfo = D3D9SpecializationInfo(); D3D9ShaderMasks m_vsShaderMasks = D3D9ShaderMasks(); D3D9ShaderMasks m_psShaderMasks = FixedFunctionMask; bool m_isSWVP; bool m_isD3D8Compatible; bool m_amdATOC = false; bool m_nvATOC = false; bool m_ffZTest = false; VkImageLayout m_hazardLayout = VK_IMAGE_LAYOUT_GENERAL; bool m_usingGraphicsPipelines = false; uint32_t m_resetCtr = 0u; DxvkDepthBiasRepresentation m_depthBiasRepresentation = { VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT, false }; float m_depthBiasScale = 0.0f; uint32_t m_robustSSBOAlignment = 1; uint32_t m_robustUBOAlignment = 1; D3D9ConstantSets m_consts[DxsoProgramTypes::Count]; D3D9UserDefinedAnnotation* m_annotation = nullptr; D3D9ViewportInfo m_viewportInfo; DxvkCsChunkPool m_csChunkPool; DxvkCsThread m_csThread; DxvkCsChunkRef m_csChunk; uint64_t m_csSeqNum = 0ull; Rc m_submissionFence; uint64_t m_submissionId = 0ull; DxvkSubmitStatus m_submitStatus; uint64_t m_flushSeqNum = 0ull; GpuFlushTracker m_flushTracker; std::atomic m_availableMemory = { 0 }; D3D9DeviceLostState m_deviceLostState = D3D9DeviceLostState::Ok; HWND m_fullscreenWindow = NULL; std::atomic m_losableResourceCounter = { 0 }; D3D9SwapChainEx* m_mostRecentlyUsedSwapchain = nullptr; #ifdef D3D9_ALLOW_UNMAPPING lru_list m_mappedTextures; #endif // m_state should be declared last (i.e. freed first), because it // references objects that can call back into the device when freed. Direct3DState9 m_state; D3D9VkInteropDevice m_d3d9Interop; D3D9On12 m_d3d9On12; DxvkD3D8Bridge m_d3d8Bridge; // Sampler statistics constexpr static uint32_t SamplerCountBits = 12u; constexpr static uint64_t SamplerCountMask = (1u << SamplerCountBits) - 1u; uint64_t m_samplerBindCount = 0u; uint64_t m_lastSamplerLiveCount = 0u; uint64_t m_lastSamplerBindCount = 0u; // Written by CS thread alignas(CACHE_LINE_SIZE) std::atomic m_lastSamplerStats = { 0u }; }; } dxvk-2.6.1/src/d3d9/d3d9_device_child.h000066400000000000000000000032541477473124000174410ustar00rootroot00000000000000#pragma once #include "d3d9_include.h" namespace dxvk { class D3D9DeviceEx; template class D3D9DeviceChild : public ComObjectClamp { public: D3D9DeviceChild(D3D9DeviceEx* pDevice) : m_parent( pDevice ) { } ULONG STDMETHODCALLTYPE AddRef() { uint32_t refCount = this->m_refCount++; if (unlikely(!refCount)) { this->AddRefPrivate(); GetDevice()->AddRef(); } return refCount + 1; } ULONG STDMETHODCALLTYPE Release() { uint32_t oldRefCount, refCount; do { oldRefCount = this->m_refCount.load(std::memory_order_acquire); // clamp value to 0 to prevent underruns if (unlikely(!oldRefCount)) return 0; refCount = oldRefCount - 1; } while (!this->m_refCount.compare_exchange_weak(oldRefCount, refCount, std::memory_order_release, std::memory_order_acquire)); if (unlikely(!refCount)) { auto* pDevice = GetDevice(); this->ReleasePrivate(); pDevice->Release(); } return refCount; } HRESULT STDMETHODCALLTYPE GetDevice(IDirect3DDevice9** ppDevice) { InitReturnPtr(ppDevice); if (ppDevice == nullptr) return D3DERR_INVALIDCALL; *ppDevice = ref(GetDevice()); return D3D_OK; } IDirect3DDevice9Ex* GetDevice() { return reinterpret_cast(m_parent); } D3D9DeviceEx* GetParent() { return m_parent; } protected: D3D9DeviceEx* m_parent; }; }dxvk-2.6.1/src/d3d9/d3d9_fixed_function.cpp000066400000000000000000003106221477473124000203760ustar00rootroot00000000000000#include "d3d9_fixed_function.h" #include "d3d9_device.h" #include "d3d9_util.h" #include "d3d9_spec_constants.h" #include "../dxvk/dxvk_hash.h" #include "../spirv/spirv_module.h" #include namespace dxvk { D3D9FixedFunctionOptions::D3D9FixedFunctionOptions(const D3D9Options* options) { invariantPosition = options->invariantPosition; forceSampleRateShading = options->forceSampleRateShading; drefScaling = options->drefScaling; } uint32_t DoFixedFunctionFog(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, const D3D9FogContext& fogCtx) { uint32_t floatType = spvModule.defFloatType(32); uint32_t vec3Type = spvModule.defVectorType(floatType, 3); uint32_t vec4Type = spvModule.defVectorType(floatType, 4); uint32_t floatPtr = spvModule.defPointerType(floatType, spv::StorageClassPushConstant); uint32_t vec3Ptr = spvModule.defPointerType(vec3Type, spv::StorageClassPushConstant); uint32_t fogColorMember = spvModule.constu32(uint32_t(D3D9RenderStateItem::FogColor)); uint32_t fogColor = spvModule.opLoad(vec3Type, spvModule.opAccessChain(vec3Ptr, fogCtx.RenderState, 1, &fogColorMember)); uint32_t fogScaleMember = spvModule.constu32(uint32_t(D3D9RenderStateItem::FogScale)); uint32_t fogScale = spvModule.opLoad(floatType, spvModule.opAccessChain(floatPtr, fogCtx.RenderState, 1, &fogScaleMember)); uint32_t fogEndMember = spvModule.constu32(uint32_t(D3D9RenderStateItem::FogEnd)); uint32_t fogEnd = spvModule.opLoad(floatType, spvModule.opAccessChain(floatPtr, fogCtx.RenderState, 1, &fogEndMember)); uint32_t fogDensityMember = spvModule.constu32(uint32_t(D3D9RenderStateItem::FogDensity)); uint32_t fogDensity = spvModule.opLoad(floatType, spvModule.opAccessChain(floatPtr, fogCtx.RenderState, 1, &fogDensityMember)); uint32_t fogMode = spec.get( spvModule, fogCtx.SpecUBO, fogCtx.IsPixel ? SpecPixelFogMode : SpecVertexFogMode); uint32_t fogEnabled = spec.get(spvModule, fogCtx.SpecUBO, SpecFogEnabled); fogEnabled = spvModule.opINotEqual(spvModule.defBoolType(), fogEnabled, spvModule.constu32(0)); uint32_t doFog = spvModule.allocateId(); uint32_t skipFog = spvModule.allocateId(); uint32_t returnType = fogCtx.IsPixel ? vec4Type : floatType; uint32_t returnTypePtr = spvModule.defPointerType(returnType, spv::StorageClassPrivate); uint32_t returnValuePtr = spvModule.newVar(returnTypePtr, spv::StorageClassPrivate); spvModule.opStore(returnValuePtr, fogCtx.IsPixel ? fogCtx.oColor : spvModule.constf32(0.0f)); // Actually do the fog now we have all the vars in-place. spvModule.opSelectionMerge(skipFog, spv::SelectionControlMaskNone); spvModule.opBranchConditional(fogEnabled, doFog, skipFog); spvModule.opLabel(doFog); uint32_t wIndex = 3; uint32_t zIndex = 2; uint32_t w = spvModule.opCompositeExtract(floatType, fogCtx.vPos, 1, &wIndex); uint32_t z = spvModule.opCompositeExtract(floatType, fogCtx.vPos, 1, &zIndex); uint32_t depth = 0; if (fogCtx.IsPixel) depth = spvModule.opFMul(floatType, z, spvModule.opFDiv(floatType, spvModule.constf32(1.0f), w)); else { if (fogCtx.RangeFog) { std::array indices = { 0, 1, 2 }; uint32_t pos3 = spvModule.opVectorShuffle(vec3Type, fogCtx.vPos, fogCtx.vPos, indices.size(), indices.data()); depth = spvModule.opLength(floatType, pos3); } else depth = fogCtx.HasFogInput ? fogCtx.vFog : spvModule.opFAbs(floatType, z); } uint32_t fogFactor; if (!fogCtx.IsPixel && fogCtx.IsFixedFunction && fogCtx.IsPositionT) { fogFactor = fogCtx.HasSpecular ? spvModule.opCompositeExtract(floatType, fogCtx.Specular, 1, &wIndex) : spvModule.constf32(1.0f); } else { uint32_t applyFogFactor = spvModule.allocateId(); std::array fogVariables; std::array fogCaseLabels = { { { uint32_t(D3DFOG_NONE), spvModule.allocateId() }, { uint32_t(D3DFOG_EXP), spvModule.allocateId() }, { uint32_t(D3DFOG_EXP2), spvModule.allocateId() }, { uint32_t(D3DFOG_LINEAR), spvModule.allocateId() }, } }; spvModule.opSelectionMerge(applyFogFactor, spv::SelectionControlMaskNone); spvModule.opSwitch(fogMode, fogCaseLabels[D3DFOG_NONE].labelId, fogCaseLabels.size(), fogCaseLabels.data()); for (uint32_t i = 0; i < fogCaseLabels.size(); i++) { spvModule.opLabel(fogCaseLabels[i].labelId); fogVariables[i].labelId = fogCaseLabels[i].labelId; fogVariables[i].varId = [&] { auto mode = D3DFOGMODE(fogCaseLabels[i].literal); switch (mode) { default: // vFog case D3DFOG_NONE: { if (fogCtx.IsPixel) return fogCtx.vFog; if (fogCtx.IsFixedFunction && fogCtx.HasSpecular) return spvModule.opCompositeExtract(floatType, fogCtx.Specular, 1, &wIndex); return spvModule.constf32(1.0f); } // (end - d) / (end - start) case D3DFOG_LINEAR: { uint32_t fogFactor = spvModule.opFSub(floatType, fogEnd, depth); fogFactor = spvModule.opFMul(floatType, fogFactor, fogScale); fogFactor = spvModule.opNClamp(floatType, fogFactor, spvModule.constf32(0.0f), spvModule.constf32(1.0f)); return fogFactor; } // 1 / (e^[d * density])^2 case D3DFOG_EXP2: // 1 / (e^[d * density]) case D3DFOG_EXP: { uint32_t fogFactor = spvModule.opFMul(floatType, depth, fogDensity); if (mode == D3DFOG_EXP2) fogFactor = spvModule.opFMul(floatType, fogFactor, fogFactor); // Provides the rcp. fogFactor = spvModule.opFNegate(floatType, fogFactor); fogFactor = spvModule.opExp(floatType, fogFactor); return fogFactor; } } }(); spvModule.opBranch(applyFogFactor); } spvModule.opLabel(applyFogFactor); fogFactor = spvModule.opPhi(floatType, fogVariables.size(), fogVariables.data()); } uint32_t fogRetValue = 0; // Return the new color if we are doing this in PS // or just the fog factor for oFog in VS if (fogCtx.IsPixel) { std::array indices = { 0, 1, 2, 6 }; uint32_t color = fogCtx.oColor; uint32_t color3 = spvModule.opVectorShuffle(vec3Type, color, color, 3, indices.data()); std::array fogFacIndices = { fogFactor, fogFactor, fogFactor }; uint32_t fogFact3 = spvModule.opCompositeConstruct(vec3Type, fogFacIndices.size(), fogFacIndices.data()); uint32_t lerpedFrog = spvModule.opFMix(vec3Type, fogColor, color3, fogFact3); fogRetValue = spvModule.opVectorShuffle(vec4Type, lerpedFrog, color, indices.size(), indices.data()); } else fogRetValue = fogFactor; spvModule.opStore(returnValuePtr, fogRetValue); spvModule.opBranch(skipFog); spvModule.opLabel(skipFog); return spvModule.opLoad(returnType, returnValuePtr); } void DoFixedFunctionAlphaTest(SpirvModule& spvModule, const D3D9AlphaTestContext& ctx) { // Labels for the alpha test std::array atestCaseLabels = {{ { uint32_t(VK_COMPARE_OP_NEVER), spvModule.allocateId() }, { uint32_t(VK_COMPARE_OP_LESS), spvModule.allocateId() }, { uint32_t(VK_COMPARE_OP_EQUAL), spvModule.allocateId() }, { uint32_t(VK_COMPARE_OP_LESS_OR_EQUAL), spvModule.allocateId() }, { uint32_t(VK_COMPARE_OP_GREATER), spvModule.allocateId() }, { uint32_t(VK_COMPARE_OP_NOT_EQUAL), spvModule.allocateId() }, { uint32_t(VK_COMPARE_OP_GREATER_OR_EQUAL), spvModule.allocateId() }, { uint32_t(VK_COMPARE_OP_ALWAYS), spvModule.allocateId() }, }}; uint32_t atestBeginLabel = spvModule.allocateId(); uint32_t atestTestLabel = spvModule.allocateId(); uint32_t atestDiscardLabel = spvModule.allocateId(); uint32_t atestKeepLabel = spvModule.allocateId(); uint32_t atestSkipLabel = spvModule.allocateId(); // if (alpha_func != ALWAYS) { ... } uint32_t boolType = spvModule.defBoolType(); uint32_t isNotAlways = spvModule.opINotEqual(boolType, ctx.alphaFuncId, spvModule.constu32(VK_COMPARE_OP_ALWAYS)); spvModule.opSelectionMerge(atestSkipLabel, spv::SelectionControlMaskNone); spvModule.opBranchConditional(isNotAlways, atestBeginLabel, atestSkipLabel); spvModule.opLabel(atestBeginLabel); // The lower 8 bits of the alpha ref contain the actual reference value // from the API, the upper bits store the accuracy bit count minus 8. // So if we want 12 bits of accuracy (i.e. 0-4095), that value will be 4. uint32_t uintType = spvModule.defIntType(32, 0); // Check if the given bit precision is supported uint32_t precisionIntLabel = spvModule.allocateId(); uint32_t precisionFloatLabel = spvModule.allocateId(); uint32_t precisionEndLabel = spvModule.allocateId(); uint32_t useIntPrecision = spvModule.opULessThanEqual(boolType, ctx.alphaPrecisionId, spvModule.constu32(8)); spvModule.opSelectionMerge(precisionEndLabel, spv::SelectionControlMaskNone); spvModule.opBranchConditional(useIntPrecision, precisionIntLabel, precisionFloatLabel); spvModule.opLabel(precisionIntLabel); // Adjust alpha ref to the given range uint32_t alphaRefIdInt = spvModule.opBitwiseOr(uintType, spvModule.opShiftLeftLogical(uintType, ctx.alphaRefId, ctx.alphaPrecisionId), spvModule.opShiftRightLogical(uintType, ctx.alphaRefId, spvModule.opISub(uintType, spvModule.constu32(8), ctx.alphaPrecisionId))); // Convert alpha ref to float since we'll do the comparison based on that uint32_t floatType = spvModule.defFloatType(32); alphaRefIdInt = spvModule.opConvertUtoF(floatType, alphaRefIdInt); // Adjust alpha to the given range and round uint32_t alphaFactorId = spvModule.opISub(uintType, spvModule.opShiftLeftLogical(uintType, spvModule.constu32(256), ctx.alphaPrecisionId), spvModule.constu32(1)); alphaFactorId = spvModule.opConvertUtoF(floatType, alphaFactorId); uint32_t alphaIdInt = spvModule.opRoundEven(floatType, spvModule.opFMul(floatType, ctx.alphaId, alphaFactorId)); spvModule.opBranch(precisionEndLabel); spvModule.opLabel(precisionFloatLabel); // If we're not using integer precision, normalize the alpha ref uint32_t alphaRefIdFloat = spvModule.opFDiv(floatType, spvModule.opConvertUtoF(floatType, ctx.alphaRefId), spvModule.constf32(255.0f)); spvModule.opBranch(precisionEndLabel); spvModule.opLabel(precisionEndLabel); std::array alphaRefLabels = { SpirvPhiLabel { alphaRefIdInt, precisionIntLabel }, SpirvPhiLabel { alphaRefIdFloat, precisionFloatLabel }, }; uint32_t alphaRefId = spvModule.opPhi(floatType, alphaRefLabels.size(), alphaRefLabels.data()); std::array alphaIdLabels = { SpirvPhiLabel { alphaIdInt, precisionIntLabel }, SpirvPhiLabel { ctx.alphaId, precisionFloatLabel }, }; uint32_t alphaId = spvModule.opPhi(floatType, alphaIdLabels.size(), alphaIdLabels.data()); // switch (alpha_func) { ... } spvModule.opSelectionMerge(atestTestLabel, spv::SelectionControlMaskNone); spvModule.opSwitch(ctx.alphaFuncId, atestCaseLabels[uint32_t(VK_COMPARE_OP_ALWAYS)].labelId, atestCaseLabels.size(), atestCaseLabels.data()); std::array atestVariables; for (uint32_t i = 0; i < atestCaseLabels.size(); i++) { spvModule.opLabel(atestCaseLabels[i].labelId); atestVariables[i].labelId = atestCaseLabels[i].labelId; atestVariables[i].varId = [&] { switch (VkCompareOp(atestCaseLabels[i].literal)) { case VK_COMPARE_OP_NEVER: return spvModule.constBool(false); case VK_COMPARE_OP_LESS: return spvModule.opFOrdLessThan (boolType, alphaId, alphaRefId); case VK_COMPARE_OP_EQUAL: return spvModule.opFOrdEqual (boolType, alphaId, alphaRefId); case VK_COMPARE_OP_LESS_OR_EQUAL: return spvModule.opFOrdLessThanEqual (boolType, alphaId, alphaRefId); case VK_COMPARE_OP_GREATER: return spvModule.opFOrdGreaterThan (boolType, alphaId, alphaRefId); case VK_COMPARE_OP_NOT_EQUAL: return spvModule.opFUnordNotEqual (boolType, alphaId, alphaRefId); case VK_COMPARE_OP_GREATER_OR_EQUAL: return spvModule.opFOrdGreaterThanEqual(boolType, alphaId, alphaRefId); default: case VK_COMPARE_OP_ALWAYS: return spvModule.constBool(true); } }(); spvModule.opBranch(atestTestLabel); } // end switch spvModule.opLabel(atestTestLabel); uint32_t atestResult = spvModule.opPhi(boolType, atestVariables.size(), atestVariables.data()); uint32_t atestDiscard = spvModule.opLogicalNot(boolType, atestResult); // if (do_discard) { ... } spvModule.opSelectionMerge(atestKeepLabel, spv::SelectionControlMaskNone); spvModule.opBranchConditional(atestDiscard, atestDiscardLabel, atestKeepLabel); spvModule.opLabel(atestDiscardLabel); spvModule.opDemoteToHelperInvocation(); spvModule.opBranch(atestKeepLabel); // end if (do_discard) spvModule.opLabel(atestKeepLabel); spvModule.opBranch(atestSkipLabel); // end if (alpha_test) spvModule.opLabel(atestSkipLabel); } uint32_t SetupRenderStateBlock(SpirvModule& spvModule) { uint32_t floatType = spvModule.defFloatType(32); uint32_t uintType = spvModule.defIntType(32, 0); uint32_t vec3Type = spvModule.defVectorType(floatType, 3); std::array rsMembers = {{ vec3Type, floatType, floatType, floatType, uintType, floatType, floatType, floatType, floatType, floatType, floatType, }}; uint32_t rsStruct = spvModule.defStructTypeUnique(rsMembers.size(), rsMembers.data()); uint32_t rsBlock = spvModule.newVar( spvModule.defPointerType(rsStruct, spv::StorageClassPushConstant), spv::StorageClassPushConstant); spvModule.setDebugName (rsBlock, "render_state"); spvModule.setDebugName (rsStruct, "render_state_t"); spvModule.decorate (rsStruct, spv::DecorationBlock); uint32_t memberIdx = 0; auto SetMemberName = [&](const char* name, uint32_t offset) { spvModule.setDebugMemberName (rsStruct, memberIdx, name); spvModule.memberDecorateOffset (rsStruct, memberIdx, offset); memberIdx++; }; SetMemberName("fog_color", offsetof(D3D9RenderStateInfo, fogColor)); SetMemberName("fog_scale", offsetof(D3D9RenderStateInfo, fogScale)); SetMemberName("fog_end", offsetof(D3D9RenderStateInfo, fogEnd)); SetMemberName("fog_density", offsetof(D3D9RenderStateInfo, fogDensity)); SetMemberName("alpha_ref", offsetof(D3D9RenderStateInfo, alphaRef)); SetMemberName("point_size", offsetof(D3D9RenderStateInfo, pointSize)); SetMemberName("point_size_min", offsetof(D3D9RenderStateInfo, pointSizeMin)); SetMemberName("point_size_max", offsetof(D3D9RenderStateInfo, pointSizeMax)); SetMemberName("point_scale_a", offsetof(D3D9RenderStateInfo, pointScaleA)); SetMemberName("point_scale_b", offsetof(D3D9RenderStateInfo, pointScaleB)); SetMemberName("point_scale_c", offsetof(D3D9RenderStateInfo, pointScaleC)); return rsBlock; } uint32_t SetupSpecUBO(SpirvModule& spvModule, std::vector& bindings) { uint32_t uintType = spvModule.defIntType(32, 0); std::array specMembers; for (auto& x : specMembers) x = uintType; uint32_t specStruct = spvModule.defStructTypeUnique(uint32_t(specMembers.size()), specMembers.data()); spvModule.setDebugName (specStruct, "spec_state_t"); spvModule.decorate (specStruct, spv::DecorationBlock); for (uint32_t i = 0; i < SpecConstantCount; i++) { std::string name = str::format("dword", i); spvModule.setDebugMemberName (specStruct, i, name.c_str()); spvModule.memberDecorateOffset (specStruct, i, sizeof(uint32_t) * i); } uint32_t specBlock = spvModule.newVar( spvModule.defPointerType(specStruct, spv::StorageClassUniform), spv::StorageClassUniform); spvModule.setDebugName (specBlock, "spec_state"); spvModule.decorateDescriptorSet(specBlock, 0); spvModule.decorateBinding (specBlock, getSpecConstantBufferSlot()); DxvkBindingInfo binding = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }; binding.resourceBinding = getSpecConstantBufferSlot(); binding.viewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM; binding.access = VK_ACCESS_UNIFORM_READ_BIT; binding.uboSet = VK_TRUE; bindings.push_back(binding); return specBlock; } D3D9PointSizeInfoVS GetPointSizeInfoVS(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, uint32_t vPos, uint32_t vtx, uint32_t perVertPointSize, uint32_t rsBlock, uint32_t specUbo, bool isFixedFunction) { uint32_t floatType = spvModule.defFloatType(32); uint32_t floatPtr = spvModule.defPointerType(floatType, spv::StorageClassPushConstant); uint32_t vec3Type = spvModule.defVectorType(floatType, 3); uint32_t vec4Type = spvModule.defVectorType(floatType, 4); uint32_t uint32Type = spvModule.defIntType(32, 0); uint32_t boolType = spvModule.defBoolType(); auto LoadFloat = [&](D3D9RenderStateItem item) { uint32_t index = spvModule.constu32(uint32_t(item)); return spvModule.opLoad(floatType, spvModule.opAccessChain(floatPtr, rsBlock, 1, &index)); }; uint32_t value = perVertPointSize != 0 ? perVertPointSize : LoadFloat(D3D9RenderStateItem::PointSize); if (isFixedFunction) { uint32_t pointMode = spec.get(spvModule, specUbo, SpecPointMode); uint32_t scaleBit = spvModule.opBitFieldUExtract(uint32Type, pointMode, spvModule.consti32(0), spvModule.consti32(1)); uint32_t isScale = spvModule.opIEqual(boolType, scaleBit, spvModule.constu32(1)); uint32_t scaleC = LoadFloat(D3D9RenderStateItem::PointScaleC); uint32_t scaleB = LoadFloat(D3D9RenderStateItem::PointScaleB); uint32_t scaleA = LoadFloat(D3D9RenderStateItem::PointScaleA); std::array indices = { 0, 1, 2, 3 }; uint32_t vtx3; if (vPos != 0) { vPos = spvModule.opLoad(vec4Type, vPos); uint32_t rhw = spvModule.opCompositeExtract(floatType, vPos, 1, &indices[3]); rhw = spvModule.opFDiv(floatType, spvModule.constf32(1.0f), rhw); uint32_t pos3 = spvModule.opVectorShuffle(vec3Type, vPos, vPos, 3, indices.data()); vtx3 = spvModule.opVectorTimesScalar(vec3Type, pos3, rhw); } else { vtx3 = spvModule.opVectorShuffle(vec3Type, vtx, vtx, 3, indices.data()); } uint32_t DeSqr = spvModule.opDot (floatType, vtx3, vtx3); uint32_t De = spvModule.opSqrt(floatType, DeSqr); uint32_t scaleValue = spvModule.opFMul(floatType, scaleC, DeSqr); scaleValue = spvModule.opFFma(floatType, scaleB, De, scaleValue); scaleValue = spvModule.opFAdd(floatType, scaleA, scaleValue); scaleValue = spvModule.opSqrt(floatType, scaleValue); scaleValue = spvModule.opFDiv(floatType, value, scaleValue); value = spvModule.opSelect(floatType, isScale, scaleValue, value); } uint32_t min = LoadFloat(D3D9RenderStateItem::PointSizeMin); uint32_t max = LoadFloat(D3D9RenderStateItem::PointSizeMax); D3D9PointSizeInfoVS info; info.defaultValue = value; info.min = min; info.max = max; return info; } D3D9PointSizeInfoPS GetPointSizeInfoPS(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, uint32_t rsBlock, uint32_t specUbo) { uint32_t uint32Type = spvModule.defIntType(32, 0); uint32_t boolType = spvModule.defBoolType(); uint32_t boolVec4 = spvModule.defVectorType(boolType, 4); uint32_t pointMode = spec.get(spvModule, specUbo, SpecPointMode); uint32_t spriteBit = spvModule.opBitFieldUExtract(uint32Type, pointMode, spvModule.consti32(1), spvModule.consti32(1)); uint32_t isSprite = spvModule.opIEqual(boolType, spriteBit, spvModule.constu32(1)); std::array isSpriteIndices; for (uint32_t i = 0; i < isSpriteIndices.size(); i++) isSpriteIndices[i] = isSprite; isSprite = spvModule.opCompositeConstruct(boolVec4, isSpriteIndices.size(), isSpriteIndices.data()); D3D9PointSizeInfoPS info; info.isSprite = isSprite; return info; } uint32_t GetPointCoord(SpirvModule& spvModule) { uint32_t floatType = spvModule.defFloatType(32); uint32_t vec2Type = spvModule.defVectorType(floatType, 2); uint32_t vec4Type = spvModule.defVectorType(floatType, 4); uint32_t vec2Ptr = spvModule.defPointerType(vec2Type, spv::StorageClassInput); uint32_t pointCoordPtr = spvModule.newVar(vec2Ptr, spv::StorageClassInput); spvModule.decorateBuiltIn(pointCoordPtr, spv::BuiltInPointCoord); uint32_t pointCoord = spvModule.opLoad(vec2Type, pointCoordPtr); std::array indices = { 0, 1, 2, 3 }; std::array pointCoordIndices = { spvModule.opCompositeExtract(floatType, pointCoord, 1, &indices[0]), spvModule.opCompositeExtract(floatType, pointCoord, 1, &indices[1]), spvModule.constf32(0.0f), spvModule.constf32(0.0f) }; return spvModule.opCompositeConstruct(vec4Type, pointCoordIndices.size(), pointCoordIndices.data()); } uint32_t GetSharedConstants(SpirvModule& spvModule) { uint32_t float_t = spvModule.defFloatType(32); uint32_t vec2_t = spvModule.defVectorType(float_t, 2); uint32_t vec4_t = spvModule.defVectorType(float_t, 4); std::array stageMembers = { vec4_t, vec2_t, vec2_t, float_t, float_t, }; std::array members; for (auto& member : members) member = stageMembers; const uint32_t structType = spvModule.defStructType(members.size() * stageMembers.size(), members[0].data()); spvModule.decorateBlock(structType); uint32_t offset = 0; for (uint32_t stage = 0; stage < caps::TextureStageCount; stage++) { spvModule.memberDecorateOffset(structType, stage * D3D9SharedPSStages_Count + D3D9SharedPSStages_Constant, offset); offset += sizeof(float) * 4; spvModule.memberDecorateOffset(structType, stage * D3D9SharedPSStages_Count + D3D9SharedPSStages_BumpEnvMat0, offset); offset += sizeof(float) * 2; spvModule.memberDecorateOffset(structType, stage * D3D9SharedPSStages_Count + D3D9SharedPSStages_BumpEnvMat1, offset); offset += sizeof(float) * 2; spvModule.memberDecorateOffset(structType, stage * D3D9SharedPSStages_Count + D3D9SharedPSStages_BumpEnvLScale, offset); offset += sizeof(float); spvModule.memberDecorateOffset(structType, stage * D3D9SharedPSStages_Count + D3D9SharedPSStages_BumpEnvLOffset, offset); offset += sizeof(float); // Padding... offset += sizeof(float) * 2; } uint32_t sharedState = spvModule.newVar( spvModule.defPointerType(structType, spv::StorageClassUniform), spv::StorageClassUniform); spvModule.setDebugName(sharedState, "D3D9SharedPS"); return sharedState; } enum class D3D9FFVSMembers { WorldViewMatrix, NormalMatrix, InverseViewMatrix, ProjMatrix, Texcoord0, Texcoord1, Texcoord2, Texcoord3, Texcoord4, Texcoord5, Texcoord6, Texcoord7, InverseOffset, InverseExtent, GlobalAmbient, Light0, Light1, Light2, Light3, Light4, Light5, Light6, Light7, MaterialDiffuse, MaterialAmbient, MaterialSpecular, MaterialEmissive, MaterialPower, TweenFactor, MemberCount }; struct D3D9FFVertexData { uint32_t constantBuffer; uint32_t vertexBlendData; uint32_t lightType; struct { uint32_t worldview; uint32_t normal; uint32_t inverseView; uint32_t proj; uint32_t texcoord[8]; uint32_t invOffset; uint32_t invExtent; uint32_t globalAmbient; uint32_t materialDiffuse; uint32_t materialSpecular; uint32_t materialAmbient; uint32_t materialEmissive; uint32_t materialPower; uint32_t tweenFactor; } constants; struct { uint32_t POSITION; uint32_t POSITION1; uint32_t POINTSIZE; uint32_t NORMAL; uint32_t NORMAL1; uint32_t TEXCOORD[8]; uint32_t COLOR[2]; uint32_t FOG; uint32_t BLENDWEIGHT; uint32_t BLENDINDICES; } in; struct { uint32_t POSITION; uint32_t POINTSIZE; uint32_t NORMAL; uint32_t TEXCOORD[8]; uint32_t COLOR[2]; uint32_t FOG; } out; }; enum D3D9FFPSMembers { TextureFactor = 0, MemberCount }; struct D3D9FFPixelData { uint32_t constantBuffer; uint32_t sharedState; struct { uint32_t textureFactor; } constants; struct { uint32_t TEXCOORD[8]; uint32_t COLOR[2]; uint32_t FOG; uint32_t POS; } in; struct { uint32_t texcoordCnt; uint32_t typeId; uint32_t varId; } samplers[8]; struct { uint32_t COLOR; } out; }; class D3D9FFShaderCompiler { public: D3D9FFShaderCompiler( Rc Device, const D3D9FFShaderKeyVS& Key, const std::string& Name, D3D9FixedFunctionOptions Options); D3D9FFShaderCompiler( Rc Device, const D3D9FFShaderKeyFS& Key, const std::string& Name, D3D9FixedFunctionOptions Options); Rc compile(); DxsoIsgn isgn() { return m_isgn; } private: // Returns value for inputs // Returns ptr for outputs uint32_t declareIO(bool input, DxsoSemantic semantic, spv::BuiltIn builtin = spv::BuiltInMax); void compileVS(); void setupRenderStateInfo(); void emitLightTypeDecl(); void emitBaseBufferDecl(); void emitVertexBlendDecl(); void setupVS(); void compilePS(); void setupPS(); void emitPsSharedConstants(); void emitVsClipping(uint32_t vtx); void alphaTestPS(); uint32_t emitMatrixTimesVector(uint32_t rowCount, uint32_t colCount, uint32_t matrix, uint32_t vector); uint32_t emitVectorTimesMatrix(uint32_t rowCount, uint32_t colCount, uint32_t vector, uint32_t matrix); bool isVS() { return m_programType == DxsoProgramType::VertexShader; } bool isPS() { return !isVS(); } std::string m_filename; SpirvModule m_module; std::vector m_bindings; uint32_t m_inputMask = 0u; uint32_t m_outputMask = 0u; uint32_t m_flatShadingMask = 0u; DxsoProgramType m_programType; D3D9FFShaderKeyVS m_vsKey; D3D9FFShaderKeyFS m_fsKey; D3D9FFVertexData m_vs = { }; D3D9FFPixelData m_ps = { }; DxsoIsgn m_isgn; DxsoIsgn m_osgn; uint32_t m_floatType = 0u; uint32_t m_uint32Type = 0u; uint32_t m_vec4Type = 0u; uint32_t m_vec3Type = 0u; uint32_t m_vec2Type = 0u; uint32_t m_mat3Type = 0u; uint32_t m_mat4Type = 0u; uint32_t m_entryPointId = 0u; uint32_t m_rsBlock = 0u; uint32_t m_specUbo = 0u; uint32_t m_mainFuncLabel = 0u; D3D9FixedFunctionOptions m_options; D3D9ShaderSpecConstantManager m_spec; }; D3D9FFShaderCompiler::D3D9FFShaderCompiler( Rc Device, const D3D9FFShaderKeyVS& Key, const std::string& Name, D3D9FixedFunctionOptions Options) : m_filename ( Name ) , m_module ( spvVersion(1, 3) ) , m_programType ( DxsoProgramTypes::VertexShader ) , m_vsKey ( Key ) , m_options ( Options ) { } D3D9FFShaderCompiler::D3D9FFShaderCompiler( Rc Device, const D3D9FFShaderKeyFS& Key, const std::string& Name, D3D9FixedFunctionOptions Options) : m_filename ( Name ) , m_module ( spvVersion(1, 3) ) , m_programType ( DxsoProgramTypes::PixelShader ) , m_fsKey ( Key ) , m_options ( Options ) { } Rc D3D9FFShaderCompiler::compile() { m_floatType = m_module.defFloatType(32); m_uint32Type = m_module.defIntType(32, 0); m_vec4Type = m_module.defVectorType(m_floatType, 4); m_vec3Type = m_module.defVectorType(m_floatType, 3); m_vec2Type = m_module.defVectorType(m_floatType, 2); m_mat3Type = m_module.defMatrixType(m_vec3Type, 3); m_mat4Type = m_module.defMatrixType(m_vec4Type, 4); m_entryPointId = m_module.allocateId(); // Set the shader name so that we recognize it in renderdoc m_module.setDebugSource( spv::SourceLanguageUnknown, 0, m_module.addDebugString(m_filename.c_str()), nullptr); // Set the memory model. This is the same for all shaders. m_module.setMemoryModel( spv::AddressingModelLogical, spv::MemoryModelGLSL450); m_module.enableCapability(spv::CapabilityShader); m_module.enableCapability(spv::CapabilityImageQuery); m_module.functionBegin( m_module.defVoidType(), m_entryPointId, m_module.defFunctionType( m_module.defVoidType(), 0, nullptr), spv::FunctionControlMaskNone); m_module.setDebugName(m_entryPointId, "main"); m_mainFuncLabel = m_module.allocateId(); m_module.opLabel(m_mainFuncLabel); if (isVS()) compileVS(); else compilePS(); m_module.opReturn(); m_module.functionEnd(); // Declare the entry point, we now have all the // information we need, including the interfaces m_module.addEntryPoint(m_entryPointId, isVS() ? spv::ExecutionModelVertex : spv::ExecutionModelFragment, "main"); // Create the shader module object DxvkShaderCreateInfo info; info.stage = isVS() ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT; info.bindingCount = m_bindings.size(); info.bindings = m_bindings.data(); info.inputMask = m_inputMask; info.outputMask = m_outputMask; info.flatShadingInputs = m_flatShadingMask; info.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; info.pushConstSize = sizeof(D3D9RenderStateInfo); return new DxvkShader(info, m_module.compile()); } uint32_t D3D9FFShaderCompiler::declareIO(bool input, DxsoSemantic semantic, spv::BuiltIn builtin) { // Declare in ISGN and do linkage auto& sgn = input ? m_isgn : m_osgn; uint32_t& slots = input ? m_inputMask : m_outputMask; uint32_t i = sgn.elemCount++; uint32_t slot = i; if (builtin == spv::BuiltInMax) { if (input != isVS()) { slot = RegisterLinkerSlot(semantic); // Requires linkage... } slots |= 1u << slot; } auto& elem = sgn.elems[i]; elem.slot = slot; elem.semantic = semantic; // Declare variable spv::StorageClass storageClass = input ? spv::StorageClassInput : spv::StorageClassOutput; const bool scalar = semantic.usage == DxsoUsage::Fog || semantic.usage == DxsoUsage::PointSize; uint32_t type = scalar ? m_floatType : m_vec4Type; uint32_t ptrType = m_module.defPointerType(type, storageClass); uint32_t ptr = m_module.newVar(ptrType, storageClass); if (builtin == spv::BuiltInMax) { m_module.decorateLocation(ptr, slot); if (isPS() && input && m_options.forceSampleRateShading) { m_module.enableCapability(spv::CapabilitySampleRateShading); m_module.decorate(ptr, spv::DecorationSample); } } else { m_module.decorateBuiltIn(ptr, builtin); } bool diffuseOrSpec = semantic == DxsoSemantic{ DxsoUsage::Color, 0 } || semantic == DxsoSemantic{ DxsoUsage::Color, 1 }; if (diffuseOrSpec && input) m_flatShadingMask |= 1u << slot; std::string name = str::format(input ? "in_" : "out_", semantic.usage, semantic.usageIndex); m_module.setDebugName(ptr, name.c_str()); if (input) return m_module.opLoad(type, ptr); return ptr; } void D3D9FFShaderCompiler::compileVS() { setupVS(); std::array indices = { 0, 1, 2, 3 }; uint32_t gl_Position = m_vs.in.POSITION; uint32_t vtx = m_vs.in.POSITION; uint32_t normal = m_module.opVectorShuffle(m_vec3Type, m_vs.in.NORMAL, m_vs.in.NORMAL, 3, indices.data()); if (m_vsKey.Data.Contents.VertexBlendMode == D3D9FF_VertexBlendMode_Tween) { uint32_t vtx1 = m_vs.in.POSITION1; uint32_t normal1 = m_module.opVectorShuffle(m_vec3Type, m_vs.in.NORMAL1, m_vs.in.NORMAL1, 3, indices.data()); vtx = m_module.opFMix(m_vec3Type, vtx, vtx1, m_vs.constants.tweenFactor); normal = m_module.opFMix(m_vec3Type, normal, normal1, m_vs.constants.tweenFactor); } const uint32_t wIndex = 3; if (!m_vsKey.Data.Contents.HasPositionT) { if (m_vsKey.Data.Contents.VertexBlendMode == D3D9FF_VertexBlendMode_Normal) { uint32_t blendWeightRemaining = m_module.constf32(1); uint32_t vtxSum = 0; uint32_t nrmSum = 0; for (uint32_t i = 0; i <= m_vsKey.Data.Contents.VertexBlendCount; i++) { std::array arrayIndices; if (m_vsKey.Data.Contents.VertexBlendIndexed) { uint32_t index = m_module.opCompositeExtract(m_floatType, m_vs.in.BLENDINDICES, 1, &i); index = m_module.opConvertFtoU(m_uint32Type, m_module.opRound(m_floatType, index)); arrayIndices = { m_module.constu32(0), index }; } else arrayIndices = { m_module.constu32(0), m_module.constu32(i) }; uint32_t worldview = m_module.opLoad(m_mat4Type, m_module.opAccessChain( m_module.defPointerType(m_mat4Type, spv::StorageClassUniform), m_vs.vertexBlendData, arrayIndices.size(), arrayIndices.data())); uint32_t nrmMtx = worldview; std::array mtxIndices; for (uint32_t i = 0; i < 3; i++) { mtxIndices[i] = m_module.opCompositeExtract(m_vec4Type, nrmMtx, 1, &i); mtxIndices[i] = m_module.opVectorShuffle(m_vec3Type, mtxIndices[i], mtxIndices[i], 3, indices.data()); } nrmMtx = m_module.opCompositeConstruct(m_mat3Type, mtxIndices.size(), mtxIndices.data()); uint32_t vtxResult = emitVectorTimesMatrix(4, 4, vtx, worldview); uint32_t nrmResult = m_module.opVectorTimesMatrix(m_vec3Type, normal, nrmMtx); uint32_t weight; if (i != m_vsKey.Data.Contents.VertexBlendCount) { weight = m_module.opCompositeExtract(m_floatType, m_vs.in.BLENDWEIGHT, 1, &i); blendWeightRemaining = m_module.opFSub(m_floatType, blendWeightRemaining, weight); } else weight = blendWeightRemaining; std::array weightIds = { weight, weight, weight, weight }; uint32_t weightVec4 = m_module.opCompositeConstruct(m_vec4Type, 4, weightIds.data()); uint32_t weightVec3 = m_module.opCompositeConstruct(m_vec3Type, 3, weightIds.data()); vtxSum = vtxSum ? m_module.opFFma(m_vec4Type, vtxResult, weightVec4, vtxSum) : m_module.opFMul(m_vec4Type, vtxResult, weightVec4); nrmSum = nrmSum ? m_module.opFFma(m_vec3Type, nrmResult, weightVec3, nrmSum) : m_module.opFMul(m_vec3Type, nrmResult, weightVec3); m_module.decorate(vtxSum, spv::DecorationNoContraction); } vtx = vtxSum; normal = nrmSum; } else { vtx = emitVectorTimesMatrix(4, 4, vtx, m_vs.constants.worldview); uint32_t nrmMtx = m_vs.constants.normal; std::array mtxIndices; for (uint32_t i = 0; i < 3; i++) { mtxIndices[i] = m_module.opCompositeExtract(m_vec4Type, nrmMtx, 1, &i); mtxIndices[i] = m_module.opVectorShuffle(m_vec3Type, mtxIndices[i], mtxIndices[i], 3, indices.data()); } nrmMtx = m_module.opCompositeConstruct(m_mat3Type, mtxIndices.size(), mtxIndices.data()); normal = m_module.opMatrixTimesVector(m_vec3Type, nrmMtx, normal); } // Some games rely no normals not being normal. if (m_vsKey.Data.Contents.NormalizeNormals) { uint32_t bool_t = m_module.defBoolType(); uint32_t bool3_t = m_module.defVectorType(bool_t, 3); uint32_t isZeroNormal = m_module.opAll(bool_t, m_module.opFOrdEqual(bool3_t, normal, m_module.constvec3f32(0.0f, 0.0f, 0.0f))); std::array members = { isZeroNormal, isZeroNormal, isZeroNormal }; uint32_t isZeroNormal3 = m_module.opCompositeConstruct(bool3_t, members.size(), members.data()); normal = m_module.opNormalize(m_vec3Type, normal); normal = m_module.opSelect(m_vec3Type, isZeroNormal3, m_module.constvec3f32(0.0f, 0.0f, 0.0f), normal); } gl_Position = emitVectorTimesMatrix(4, 4, vtx, m_vs.constants.proj); } else { gl_Position = m_module.opFMul(m_vec4Type, gl_Position, m_vs.constants.invExtent); gl_Position = m_module.opFAdd(m_vec4Type, gl_Position, m_vs.constants.invOffset); // We still need to account for perspective correction here... // gl_Position.w = 1.0f / gl_Position.w // gl_Position.xyz *= gl_Position.w; uint32_t bool_t = m_module.defBoolType(); uint32_t w = m_module.opCompositeExtract (m_floatType, gl_Position, 1, &wIndex); // w = gl_Position.w uint32_t is0 = m_module.opFOrdEqual (bool_t, w, m_module.constf32(0)); // is0 = w == 0 uint32_t rhw = m_module.opFDiv (m_floatType, m_module.constf32(1.0f), w); // rhw = 1.0f / w rhw = m_module.opSelect (m_floatType, is0, m_module.constf32(1.0), rhw); // rhw = w == 0 ? 1.0 : rhw gl_Position = m_module.opVectorTimesScalar(m_vec4Type, gl_Position, rhw); // gl_Position.xyz *= rhw gl_Position = m_module.opCompositeInsert (m_vec4Type, rhw, gl_Position, 1, &wIndex); // gl_Position.w = rhw } m_module.opStore(m_vs.out.POSITION, gl_Position); std::array outNrmIndices; for (uint32_t i = 0; i < 3; i++) outNrmIndices[i] = m_module.opCompositeExtract(m_floatType, normal, 1, &i); outNrmIndices[3] = m_module.constf32(1.0f); uint32_t outNrm = m_module.opCompositeConstruct(m_vec4Type, outNrmIndices.size(), outNrmIndices.data()); m_module.opStore(m_vs.out.NORMAL, outNrm); for (uint32_t i = 0; i < caps::TextureStageCount; i++) { uint32_t inputIndex = (m_vsKey.Data.Contents.TexcoordIndices >> (i * 3)) & 0b111; uint32_t inputFlags = (m_vsKey.Data.Contents.TexcoordFlags >> (i * 3)) & 0b111; uint32_t texcoordCount = (m_vsKey.Data.Contents.TexcoordDeclMask >> (inputIndex * 3)) & 0b111; uint32_t transformed; const uint32_t wIndex = 3; uint32_t flags = (m_vsKey.Data.Contents.TransformFlags >> (i * 3)) & 0b111; // Passing 0xffffffff results in it getting clamped to the dimensions of the texture coords and getting treated as PROJECTED // but D3D9 does not apply the transformation matrix. bool applyTransform = flags > D3DTTFF_COUNT1 && flags <= D3DTTFF_COUNT4; uint32_t count = std::min(flags, 4u); // A projection component index of 4 means we won't do projection uint32_t projIndex = count != 0 ? count - 1 : 4; switch (inputFlags) { default: case (DXVK_TSS_TCI_PASSTHRU >> TCIOffset): transformed = m_vs.in.TEXCOORD[inputIndex & 0xFF]; if (texcoordCount < 4) { // Vulkan sets the w component to 1.0 if that's not provided by the vertex buffer, D3D9 expects 0 here transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(0), transformed, 1, &wIndex); } if (applyTransform && !m_vsKey.Data.Contents.HasPositionT) { /*This doesn't happen every time and I cannot figure out the difference between when it does and doesn't. Keep it disabled for now, it's more likely that games rely on the zero texcoord than the weird 1 here. if (texcoordCount <= 1) { // y gets padded to 1 for some reason uint32_t idx = 1; transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(1), transformed, 1, &idx); }*/ if (texcoordCount >= 1 && texcoordCount < 4) { // The first component after the last one thats backed by a vertex buffer gets padded to 1 for some reason. uint32_t idx = texcoordCount; transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(1), transformed, 1, &idx); } } else if (texcoordCount != 0 && !applyTransform) { // COUNT0, COUNT1, COUNT > 4 => take count from vertex decl if that's not zero count = texcoordCount; } projIndex = count != 0 ? count - 1 : 4; break; case (DXVK_TSS_TCI_CAMERASPACENORMAL >> TCIOffset): transformed = outNrm; if (!applyTransform) { count = 3; projIndex = 4; } break; case (DXVK_TSS_TCI_CAMERASPACEPOSITION >> TCIOffset): transformed = vtx; if (!applyTransform) { count = 3; projIndex = 4; } break; case (DXVK_TSS_TCI_CAMERASPACEREFLECTIONVECTOR >> TCIOffset): { uint32_t vtx3 = m_module.opVectorShuffle(m_vec3Type, vtx, vtx, 3, indices.data()); vtx3 = m_module.opNormalize(m_vec3Type, vtx3); uint32_t reflection = m_module.opReflect(m_vec3Type, vtx3, normal); std::array transformIndices; for (uint32_t i = 0; i < 3; i++) transformIndices[i] = m_module.opCompositeExtract(m_floatType, reflection, 1, &i); transformIndices[3] = m_module.constf32(1.0f); transformed = m_module.opCompositeConstruct(m_vec4Type, transformIndices.size(), transformIndices.data()); if (!applyTransform) { count = 3; projIndex = 4; } break; } case (DXVK_TSS_TCI_SPHEREMAP >> TCIOffset): { uint32_t vtx3 = m_module.opVectorShuffle(m_vec3Type, vtx, vtx, 3, indices.data()); vtx3 = m_module.opNormalize(m_vec3Type, vtx3); uint32_t reflection = m_module.opReflect(m_vec3Type, vtx3, normal); uint32_t m = m_module.opFAdd(m_vec3Type, reflection, m_module.constvec3f32(0, 0, 1)); m = m_module.opLength(m_floatType, m); m = m_module.opFMul(m_floatType, m, m_module.constf32(2.0f)); std::array transformIndices; for (uint32_t i = 0; i < 2; i++) { transformIndices[i] = m_module.opCompositeExtract(m_floatType, reflection, 1, &i); transformIndices[i] = m_module.opFDiv(m_floatType, transformIndices[i], m); transformIndices[i] = m_module.opFAdd(m_floatType, transformIndices[i], m_module.constf32(0.5f)); } transformIndices[2] = m_module.constf32(0.0f); transformIndices[3] = m_module.constf32(1.0f); transformed = m_module.opCompositeConstruct(m_vec4Type, transformIndices.size(), transformIndices.data()); break; } } if (applyTransform && !m_vsKey.Data.Contents.HasPositionT) { transformed = m_module.opVectorTimesMatrix(m_vec4Type, transformed, m_vs.constants.texcoord[i]); } if (m_vsKey.Data.Contents.Projected && projIndex < 4) { // The projection idx is always based on the flags, even when the input mode is not DXVK_TSS_TCI_PASSTHRU. uint32_t projValue = m_module.opCompositeExtract(m_floatType, transformed, 1, &projIndex); // The w component is only used for projection or unused, so always insert the component that's supposed to be divided by there. // The fragment shader will then decide whether to project or not. transformed = m_module.opCompositeInsert(m_vec4Type, projValue, transformed, 1, &wIndex); } uint32_t totalComponents = (m_vsKey.Data.Contents.Projected && projIndex < 4) ? 3 : 4; for (uint32_t i = count; i < totalComponents; i++) { // Discard the components that exceed the specified D3DTTFF_COUNT transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(0), transformed, 1, &i); } m_module.opStore(m_vs.out.TEXCOORD[i], transformed); } if (m_vsKey.Data.Contents.UseLighting) { auto PickSource = [&](uint32_t Source, uint32_t Material) { if (Source == D3DMCS_MATERIAL) return Material; else if (Source == D3DMCS_COLOR1) return m_vs.in.COLOR[0]; else return m_vs.in.COLOR[1]; }; uint32_t diffuseValue = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f); uint32_t specularValue = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f); uint32_t ambientValue = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f); for (uint32_t i = 0; i < m_vsKey.Data.Contents.LightCount; i++) { uint32_t light_ptr_t = m_module.defPointerType(m_vs.lightType, spv::StorageClassUniform); uint32_t indexVal = m_module.constu32(uint32_t(D3D9FFVSMembers::Light0) + i); uint32_t lightPtr = m_module.opAccessChain(light_ptr_t, m_vs.constantBuffer, 1, &indexVal); auto LoadLightItem = [&](uint32_t type, uint32_t idx) { uint32_t typePtr = m_module.defPointerType(type, spv::StorageClassUniform); idx = m_module.constu32(idx); return m_module.opLoad(type, m_module.opAccessChain(typePtr, lightPtr, 1, &idx)); }; uint32_t diffuse = LoadLightItem(m_vec4Type, 0); uint32_t specular = LoadLightItem(m_vec4Type, 1); uint32_t ambient = LoadLightItem(m_vec4Type, 2); uint32_t position = LoadLightItem(m_vec4Type, 3); uint32_t direction = LoadLightItem(m_vec4Type, 4); uint32_t type = LoadLightItem(m_uint32Type, 5); uint32_t range = LoadLightItem(m_floatType, 6); uint32_t falloff = LoadLightItem(m_floatType, 7); uint32_t atten0 = LoadLightItem(m_floatType, 8); uint32_t atten1 = LoadLightItem(m_floatType, 9); uint32_t atten2 = LoadLightItem(m_floatType, 10); uint32_t theta = LoadLightItem(m_floatType, 11); uint32_t phi = LoadLightItem(m_floatType, 12); uint32_t bool_t = m_module.defBoolType(); uint32_t bool3_t = m_module.defVectorType(bool_t, 3); uint32_t isSpot = m_module.opIEqual(bool_t, type, m_module.constu32(D3DLIGHT_SPOT)); uint32_t isDirectional = m_module.opIEqual(bool_t, type, m_module.constu32(D3DLIGHT_DIRECTIONAL)); std::array members = { isDirectional, isDirectional, isDirectional }; uint32_t isDirectional3 = m_module.opCompositeConstruct(bool3_t, members.size(), members.data()); uint32_t vtx3 = m_module.opVectorShuffle(m_vec3Type, vtx, vtx, 3, indices.data()); position = m_module.opVectorShuffle(m_vec3Type, position, position, 3, indices.data()); direction = m_module.opVectorShuffle(m_vec3Type, direction, direction, 3, indices.data()); uint32_t delta = m_module.opFSub(m_vec3Type, position, vtx3); uint32_t d = m_module.opLength(m_floatType, delta); uint32_t hitDir = m_module.opFNegate(m_vec3Type, direction); hitDir = m_module.opSelect(m_vec3Type, isDirectional3, hitDir, delta); hitDir = m_module.opNormalize(m_vec3Type, hitDir); uint32_t atten = m_module.opFFma (m_floatType, d, atten2, atten1); atten = m_module.opFFma (m_floatType, d, atten, atten0); atten = m_module.opFDiv (m_floatType, m_module.constf32(1.0f), atten); atten = m_module.opNMin (m_floatType, atten, m_module.constf32(std::numeric_limits::max())); atten = m_module.opSelect(m_floatType, m_module.opFOrdGreaterThan(bool_t, d, range), m_module.constf32(0.0f), atten); atten = m_module.opSelect(m_floatType, isDirectional, m_module.constf32(1.0f), atten); // Spot Lighting { uint32_t rho = m_module.opDot (m_floatType, m_module.opFNegate(m_vec3Type, hitDir), direction); uint32_t spotAtten = m_module.opFSub(m_floatType, rho, phi); spotAtten = m_module.opFDiv(m_floatType, spotAtten, m_module.opFSub(m_floatType, theta, phi)); spotAtten = m_module.opPow (m_floatType, spotAtten, falloff); uint32_t insideThetaAndPhi = m_module.opFOrdLessThanEqual(bool_t, rho, theta); uint32_t insidePhi = m_module.opFOrdGreaterThan(bool_t, rho, phi); spotAtten = m_module.opSelect(m_floatType, insidePhi, spotAtten, m_module.constf32(0.0f)); spotAtten = m_module.opSelect(m_floatType, insideThetaAndPhi, spotAtten, m_module.constf32(1.0f)); spotAtten = m_module.opFClamp(m_floatType, spotAtten, m_module.constf32(0.0f), m_module.constf32(1.0f)); spotAtten = m_module.opFMul(m_floatType, atten, spotAtten); atten = m_module.opSelect(m_floatType, isSpot, spotAtten, atten); } uint32_t hitDot = m_module.opDot(m_floatType, normal, hitDir); hitDot = m_module.opFClamp(m_floatType, hitDot, m_module.constf32(0.0f), m_module.constf32(1.0f)); uint32_t diffuseness = m_module.opFMul(m_floatType, hitDot, atten); uint32_t mid; if (m_vsKey.Data.Contents.LocalViewer) { mid = m_module.opNormalize(m_vec3Type, vtx3); mid = m_module.opFSub(m_vec3Type, hitDir, mid); } else mid = m_module.opFSub(m_vec3Type, hitDir, m_module.constvec3f32(0.0f, 0.0f, 1.0f)); mid = m_module.opNormalize(m_vec3Type, mid); uint32_t midDot = m_module.opDot(m_floatType, normal, mid); midDot = m_module.opFClamp(m_floatType, midDot, m_module.constf32(0.0f), m_module.constf32(1.0f)); uint32_t doSpec = m_module.opFOrdGreaterThan(bool_t, midDot, m_module.constf32(0.0f)); doSpec = m_module.opLogicalAnd(bool_t, doSpec, m_module.opFOrdGreaterThan(bool_t, hitDot, m_module.constf32(0.0f))); uint32_t specularness = m_module.opPow(m_floatType, midDot, m_vs.constants.materialPower); specularness = m_module.opFMul(m_floatType, specularness, atten); specularness = m_module.opSelect(m_floatType, doSpec, specularness, m_module.constf32(0.0f)); uint32_t lightAmbient = m_module.opVectorTimesScalar(m_vec4Type, ambient, atten); uint32_t lightDiffuse = m_module.opVectorTimesScalar(m_vec4Type, diffuse, diffuseness); uint32_t lightSpecular = m_module.opVectorTimesScalar(m_vec4Type, specular, specularness); ambientValue = m_module.opFAdd(m_vec4Type, ambientValue, lightAmbient); diffuseValue = m_module.opFAdd(m_vec4Type, diffuseValue, lightDiffuse); specularValue = m_module.opFAdd(m_vec4Type, specularValue, lightSpecular); } uint32_t mat_diffuse = PickSource(m_vsKey.Data.Contents.DiffuseSource, m_vs.constants.materialDiffuse); uint32_t mat_ambient = PickSource(m_vsKey.Data.Contents.AmbientSource, m_vs.constants.materialAmbient); uint32_t mat_emissive = PickSource(m_vsKey.Data.Contents.EmissiveSource, m_vs.constants.materialEmissive); uint32_t mat_specular = PickSource(m_vsKey.Data.Contents.SpecularSource, m_vs.constants.materialSpecular); std::array alphaSwizzle = {0, 1, 2, 7}; uint32_t finalColor0 = m_module.opFFma(m_vec4Type, mat_ambient, m_vs.constants.globalAmbient, mat_emissive); finalColor0 = m_module.opFFma(m_vec4Type, mat_ambient, ambientValue, finalColor0); finalColor0 = m_module.opFFma(m_vec4Type, mat_diffuse, diffuseValue, finalColor0); finalColor0 = m_module.opVectorShuffle(m_vec4Type, finalColor0, mat_diffuse, alphaSwizzle.size(), alphaSwizzle.data()); uint32_t finalColor1 = m_module.opFMul(m_vec4Type, mat_specular, specularValue); // Saturate finalColor0 = m_module.opFClamp(m_vec4Type, finalColor0, m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f), m_module.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f)); finalColor1 = m_module.opFClamp(m_vec4Type, finalColor1, m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f), m_module.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f)); m_module.opStore(m_vs.out.COLOR[0], finalColor0); m_module.opStore(m_vs.out.COLOR[1], finalColor1); } else { m_module.opStore(m_vs.out.COLOR[0], m_vs.in.COLOR[0]); m_module.opStore(m_vs.out.COLOR[1], m_vs.in.COLOR[1]); } D3D9FogContext fogCtx; fogCtx.IsPixel = false; fogCtx.RangeFog = m_vsKey.Data.Contents.RangeFog; fogCtx.RenderState = m_rsBlock; fogCtx.vPos = vtx; fogCtx.HasFogInput = m_vsKey.Data.Contents.HasFog; fogCtx.vFog = m_vs.in.FOG; fogCtx.oColor = 0; fogCtx.IsFixedFunction = true; fogCtx.IsPositionT = m_vsKey.Data.Contents.HasPositionT; fogCtx.HasSpecular = m_vsKey.Data.Contents.HasColor1; fogCtx.Specular = m_vs.in.COLOR[1]; fogCtx.SpecUBO = m_specUbo; m_module.opStore(m_vs.out.FOG, DoFixedFunctionFog(m_spec, m_module, fogCtx)); auto pointInfo = GetPointSizeInfoVS(m_spec, m_module, 0, vtx, m_vs.in.POINTSIZE, m_rsBlock, m_specUbo, true); uint32_t pointSize = m_module.opFClamp(m_floatType, pointInfo.defaultValue, pointInfo.min, pointInfo.max); m_module.opStore(m_vs.out.POINTSIZE, pointSize); if (m_vsKey.Data.Contents.VertexClipping) emitVsClipping(vtx); } void D3D9FFShaderCompiler::setupRenderStateInfo() { m_rsBlock = SetupRenderStateBlock(m_module); } void D3D9FFShaderCompiler::emitLightTypeDecl() { std::array light_members = { m_vec4Type, // Diffuse m_vec4Type, // Specular m_vec4Type, // Ambient m_vec4Type, // Position m_vec4Type, // Direction m_uint32Type, // Type m_floatType, // Range m_floatType, // Falloff m_floatType, // Attenuation0 m_floatType, // Attenuation1 m_floatType, // Attenuation2 m_floatType, // Theta m_floatType, // Phi }; m_vs.lightType = m_module.defStructType(light_members.size(), light_members.data()); m_module.setDebugName(m_vs.lightType, "light_t"); uint32_t offset = 0; m_module.memberDecorateOffset(m_vs.lightType, 0, offset); offset += 4 * sizeof(float); m_module.setDebugMemberName (m_vs.lightType, 0, "Diffuse"); m_module.memberDecorateOffset(m_vs.lightType, 1, offset); offset += 4 * sizeof(float); m_module.setDebugMemberName (m_vs.lightType, 1, "Specular"); m_module.memberDecorateOffset(m_vs.lightType, 2, offset); offset += 4 * sizeof(float); m_module.setDebugMemberName (m_vs.lightType, 2, "Ambient"); m_module.memberDecorateOffset(m_vs.lightType, 3, offset); offset += 4 * sizeof(float); m_module.setDebugMemberName (m_vs.lightType, 3, "Position"); m_module.memberDecorateOffset(m_vs.lightType, 4, offset); offset += 4 * sizeof(float); m_module.setDebugMemberName (m_vs.lightType, 4, "Direction"); m_module.memberDecorateOffset(m_vs.lightType, 5, offset); offset += 1 * sizeof(uint32_t); m_module.setDebugMemberName (m_vs.lightType, 5, "Type"); m_module.memberDecorateOffset(m_vs.lightType, 6, offset); offset += 1 * sizeof(float); m_module.setDebugMemberName (m_vs.lightType, 6, "Range"); m_module.memberDecorateOffset(m_vs.lightType, 7, offset); offset += 1 * sizeof(float); m_module.setDebugMemberName (m_vs.lightType, 7, "Falloff"); m_module.memberDecorateOffset(m_vs.lightType, 8, offset); offset += 1 * sizeof(float); m_module.setDebugMemberName (m_vs.lightType, 8, "Attenuation0"); m_module.memberDecorateOffset(m_vs.lightType, 9, offset); offset += 1 * sizeof(float); m_module.setDebugMemberName (m_vs.lightType, 9, "Attenuation1"); m_module.memberDecorateOffset(m_vs.lightType, 10, offset); offset += 1 * sizeof(float); m_module.setDebugMemberName (m_vs.lightType, 10, "Attenuation2"); m_module.memberDecorateOffset(m_vs.lightType, 11, offset); offset += 1 * sizeof(float); m_module.setDebugMemberName (m_vs.lightType, 11, "Theta"); m_module.memberDecorateOffset(m_vs.lightType, 12, offset); offset += 1 * sizeof(float); m_module.setDebugMemberName (m_vs.lightType, 12, "Phi"); } void D3D9FFShaderCompiler::emitBaseBufferDecl() { // Constant Buffer for VS. std::array members = { m_mat4Type, // World m_mat4Type, // View m_mat4Type, // InverseView m_mat4Type, // Proj m_mat4Type, // Texture0 m_mat4Type, // Texture1 m_mat4Type, // Texture2 m_mat4Type, // Texture3 m_mat4Type, // Texture4 m_mat4Type, // Texture5 m_mat4Type, // Texture6 m_mat4Type, // Texture7 m_vec4Type, // Inverse Offset m_vec4Type, // Inverse Extent m_vec4Type, // Global Ambient m_vs.lightType, // Light0 m_vs.lightType, // Light1 m_vs.lightType, // Light2 m_vs.lightType, // Light3 m_vs.lightType, // Light4 m_vs.lightType, // Light5 m_vs.lightType, // Light6 m_vs.lightType, // Light7 m_vec4Type, // Material Diffuse m_vec4Type, // Material Ambient m_vec4Type, // Material Specular m_vec4Type, // Material Emissive m_floatType, // Material Power m_floatType, // Tween Factor }; const uint32_t structType = m_module.defStructType(members.size(), members.data()); m_module.decorateBlock(structType); uint32_t offset = 0; for (uint32_t i = 0; i < uint32_t(D3D9FFVSMembers::InverseOffset); i++) { m_module.memberDecorateOffset(structType, i, offset); offset += sizeof(Matrix4); m_module.memberDecorateMatrixStride(structType, i, 16); m_module.memberDecorate(structType, i, spv::DecorationRowMajor); } for (uint32_t i = uint32_t(D3D9FFVSMembers::InverseOffset); i < uint32_t(D3D9FFVSMembers::Light0); i++) { m_module.memberDecorateOffset(structType, i, offset); offset += sizeof(Vector4); } for (uint32_t i = 0; i < caps::MaxEnabledLights; i++) { m_module.memberDecorateOffset(structType, uint32_t(D3D9FFVSMembers::Light0) + i, offset); offset += sizeof(D3D9Light); } for (uint32_t i = uint32_t(D3D9FFVSMembers::MaterialDiffuse); i < uint32_t(D3D9FFVSMembers::MaterialPower); i++) { m_module.memberDecorateOffset(structType, i, offset); offset += sizeof(Vector4); } m_module.memberDecorateOffset(structType, uint32_t(D3D9FFVSMembers::MaterialPower), offset); offset += sizeof(float); m_module.memberDecorateOffset(structType, uint32_t(D3D9FFVSMembers::TweenFactor), offset); offset += sizeof(float); m_module.setDebugName(structType, "D3D9FixedFunctionVS"); uint32_t member = 0; m_module.setDebugMemberName(structType, member++, "WorldView"); m_module.setDebugMemberName(structType, member++, "Normal"); m_module.setDebugMemberName(structType, member++, "InverseView"); m_module.setDebugMemberName(structType, member++, "Projection"); m_module.setDebugMemberName(structType, member++, "TexcoordTransform0"); m_module.setDebugMemberName(structType, member++, "TexcoordTransform1"); m_module.setDebugMemberName(structType, member++, "TexcoordTransform2"); m_module.setDebugMemberName(structType, member++, "TexcoordTransform3"); m_module.setDebugMemberName(structType, member++, "TexcoordTransform4"); m_module.setDebugMemberName(structType, member++, "TexcoordTransform5"); m_module.setDebugMemberName(structType, member++, "TexcoordTransform6"); m_module.setDebugMemberName(structType, member++, "TexcoordTransform7"); m_module.setDebugMemberName(structType, member++, "ViewportInfo_InverseOffset"); m_module.setDebugMemberName(structType, member++, "ViewportInfo_InverseExtent"); m_module.setDebugMemberName(structType, member++, "GlobalAmbient"); m_module.setDebugMemberName(structType, member++, "Light0"); m_module.setDebugMemberName(structType, member++, "Light1"); m_module.setDebugMemberName(structType, member++, "Light2"); m_module.setDebugMemberName(structType, member++, "Light3"); m_module.setDebugMemberName(structType, member++, "Light4"); m_module.setDebugMemberName(structType, member++, "Light5"); m_module.setDebugMemberName(structType, member++, "Light6"); m_module.setDebugMemberName(structType, member++, "Light7"); m_module.setDebugMemberName(structType, member++, "Material_Diffuse"); m_module.setDebugMemberName(structType, member++, "Material_Ambient"); m_module.setDebugMemberName(structType, member++, "Material_Specular"); m_module.setDebugMemberName(structType, member++, "Material_Emissive"); m_module.setDebugMemberName(structType, member++, "Material_Power"); m_module.setDebugMemberName(structType, member++, "TweenFactor"); m_vs.constantBuffer = m_module.newVar( m_module.defPointerType(structType, spv::StorageClassUniform), spv::StorageClassUniform); m_module.setDebugName(m_vs.constantBuffer, "consts"); const uint32_t bindingId = computeResourceSlotId( DxsoProgramType::VertexShader, DxsoBindingType::ConstantBuffer, DxsoConstantBuffers::VSFixedFunction); m_module.decorateDescriptorSet(m_vs.constantBuffer, 0); m_module.decorateBinding(m_vs.constantBuffer, bindingId); DxvkBindingInfo binding = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }; binding.resourceBinding = bindingId; binding.viewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM; binding.access = VK_ACCESS_UNIFORM_READ_BIT; binding.uboSet = VK_TRUE; m_bindings.push_back(binding); } void D3D9FFShaderCompiler::emitVertexBlendDecl() { const uint32_t arrayType = m_module.defRuntimeArrayTypeUnique(m_mat4Type); m_module.decorateArrayStride(arrayType, sizeof(Matrix4)); const uint32_t structType = m_module.defStructTypeUnique(1, &arrayType); m_module.memberDecorateMatrixStride(structType, 0, 16); m_module.memberDecorate(structType, 0, spv::DecorationRowMajor); m_module.decorate(structType, spv::DecorationBufferBlock); m_module.memberDecorateOffset(structType, 0, 0); m_module.setDebugName(structType, "D3D9FF_VertexBlendData"); m_module.setDebugMemberName(structType, 0, "WorldViewArray"); m_vs.vertexBlendData = m_module.newVar( m_module.defPointerType(structType, spv::StorageClassUniform), spv::StorageClassUniform); m_module.setDebugName(m_vs.vertexBlendData, "VertexBlendData"); const uint32_t bindingId = computeResourceSlotId( DxsoProgramType::VertexShader, DxsoBindingType::ConstantBuffer, DxsoConstantBuffers::VSVertexBlendData); m_module.decorateDescriptorSet(m_vs.vertexBlendData, 0); m_module.decorateBinding(m_vs.vertexBlendData, bindingId); m_module.decorate(m_vs.vertexBlendData, spv::DecorationNonWritable); DxvkBindingInfo binding = { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER }; binding.resourceBinding = bindingId; binding.viewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM; binding.access = VK_ACCESS_SHADER_READ_BIT; binding.uboSet = VK_TRUE; m_bindings.push_back(binding); } void D3D9FFShaderCompiler::setupVS() { setupRenderStateInfo(); m_specUbo = SetupSpecUBO(m_module, m_bindings); // VS Caps m_module.enableCapability(spv::CapabilityClipDistance); emitLightTypeDecl(); emitBaseBufferDecl(); if (m_vsKey.Data.Contents.VertexBlendMode == D3D9FF_VertexBlendMode_Normal) emitVertexBlendDecl(); // Load constants auto LoadConstant = [&](uint32_t type, uint32_t idx) { uint32_t offset = m_module.constu32(idx); uint32_t typePtr = m_module.defPointerType(type, spv::StorageClassUniform); return m_module.opLoad(type, m_module.opAccessChain(typePtr, m_vs.constantBuffer, 1, &offset)); }; m_vs.constants.worldview = LoadConstant(m_mat4Type, uint32_t(D3D9FFVSMembers::WorldViewMatrix)); m_vs.constants.normal = LoadConstant(m_mat4Type, uint32_t(D3D9FFVSMembers::NormalMatrix)); m_vs.constants.inverseView = LoadConstant(m_mat4Type, uint32_t(D3D9FFVSMembers::InverseViewMatrix)); m_vs.constants.proj = LoadConstant(m_mat4Type, uint32_t(D3D9FFVSMembers::ProjMatrix)); for (uint32_t i = 0; i < caps::TextureStageCount; i++) m_vs.constants.texcoord[i] = LoadConstant(m_mat4Type, uint32_t(D3D9FFVSMembers::Texcoord0) + i); m_vs.constants.invOffset = LoadConstant(m_vec4Type, uint32_t(D3D9FFVSMembers::InverseOffset)); m_vs.constants.invExtent = LoadConstant(m_vec4Type, uint32_t(D3D9FFVSMembers::InverseExtent)); m_vs.constants.globalAmbient = LoadConstant(m_vec4Type, uint32_t(D3D9FFVSMembers::GlobalAmbient)); m_vs.constants.materialDiffuse = LoadConstant(m_vec4Type, uint32_t(D3D9FFVSMembers::MaterialDiffuse)); m_vs.constants.materialAmbient = LoadConstant(m_vec4Type, uint32_t(D3D9FFVSMembers::MaterialAmbient)); m_vs.constants.materialSpecular = LoadConstant(m_vec4Type, uint32_t(D3D9FFVSMembers::MaterialSpecular)); m_vs.constants.materialEmissive = LoadConstant(m_vec4Type, uint32_t(D3D9FFVSMembers::MaterialEmissive)); m_vs.constants.materialPower = LoadConstant(m_floatType, uint32_t(D3D9FFVSMembers::MaterialPower)); m_vs.constants.tweenFactor = LoadConstant(m_floatType, uint32_t(D3D9FFVSMembers::TweenFactor)); // Do IO m_vs.in.POSITION = declareIO(true, DxsoSemantic{ DxsoUsage::Position, 0 }); m_vs.in.NORMAL = declareIO(true, DxsoSemantic{ DxsoUsage::Normal, 0 }); if (m_vsKey.Data.Contents.VertexBlendMode == D3D9FF_VertexBlendMode_Tween) { m_vs.in.POSITION1 = declareIO(true, DxsoSemantic{ DxsoUsage::Position, 1 }); m_vs.in.NORMAL1 = declareIO(true, DxsoSemantic{ DxsoUsage::Normal, 1 }); } else { m_isgn.elemCount++; m_isgn.elemCount++; } for (uint32_t i = 0; i < caps::TextureStageCount; i++) m_vs.in.TEXCOORD[i] = declareIO(true, DxsoSemantic{ DxsoUsage::Texcoord, i }); if (m_vsKey.Data.Contents.HasColor0) m_vs.in.COLOR[0] = declareIO(true, DxsoSemantic{ DxsoUsage::Color, 0 }); else { m_vs.in.COLOR[0] = m_module.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f); m_isgn.elemCount++; } if (m_vsKey.Data.Contents.HasColor1) m_vs.in.COLOR[1] = declareIO(true, DxsoSemantic{ DxsoUsage::Color, 1 }); else { m_vs.in.COLOR[1] = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f); m_isgn.elemCount++; } if (m_vsKey.Data.Contents.HasFog) m_vs.in.FOG = declareIO(true, DxsoSemantic{ DxsoUsage::Fog, 0 }); else m_isgn.elemCount++; if (m_vsKey.Data.Contents.HasPointSize) m_vs.in.POINTSIZE = declareIO(true, DxsoSemantic{ DxsoUsage::PointSize, 0 }); else m_isgn.elemCount++; if (m_vsKey.Data.Contents.VertexBlendMode == D3D9FF_VertexBlendMode_Normal) { m_vs.in.BLENDWEIGHT = declareIO(true, DxsoSemantic{ DxsoUsage::BlendWeight, 0 }); m_vs.in.BLENDINDICES = declareIO(true, DxsoSemantic{ DxsoUsage::BlendIndices, 0 }); } else { m_isgn.elemCount++; m_isgn.elemCount++; } // Declare Outputs m_vs.out.POSITION = declareIO(false, DxsoSemantic{ DxsoUsage::Position, 0 }, spv::BuiltInPosition); if (m_options.invariantPosition) m_module.decorate(m_vs.out.POSITION, spv::DecorationInvariant); m_vs.out.POINTSIZE = declareIO(false, DxsoSemantic{ DxsoUsage::PointSize, 0 }, spv::BuiltInPointSize); m_vs.out.NORMAL = declareIO(false, DxsoSemantic{ DxsoUsage::Normal, 0 }); for (uint32_t i = 0; i < caps::TextureStageCount; i++) m_vs.out.TEXCOORD[i] = declareIO(false, DxsoSemantic{ DxsoUsage::Texcoord, i }); m_vs.out.COLOR[0] = declareIO(false, DxsoSemantic{ DxsoUsage::Color, 0 }); m_vs.out.COLOR[1] = declareIO(false, DxsoSemantic{ DxsoUsage::Color, 1 }); m_vs.out.FOG = declareIO(false, DxsoSemantic{ DxsoUsage::Fog, 0 }); } void D3D9FFShaderCompiler::compilePS() { setupPS(); uint32_t diffuse = m_ps.in.COLOR[0]; uint32_t specular = m_ps.in.COLOR[1]; // Current starts of as equal to diffuse. uint32_t current = diffuse; // Temp starts off as equal to vec4(0) uint32_t temp = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f); uint32_t texture = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 1.0f); uint32_t unboundTextureConstId = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 1.0f); for (uint32_t i = 0; i < caps::TextureStageCount; i++) { const auto& stage = m_fsKey.Stages[i].Contents; bool processedTexture = false; auto DoBumpmapCoords = [&](uint32_t typeId, uint32_t baseCoords) { uint32_t stage = i - 1; uint32_t coords = baseCoords; for (uint32_t i = 0; i < 2; i++) { std::array indices = { 0, 1, 2, 3 }; uint32_t tc_m_n = m_module.opCompositeExtract(m_floatType, coords, 1, &i); uint32_t offset = m_module.constu32(D3D9SharedPSStages_Count * stage + D3D9SharedPSStages_BumpEnvMat0 + i); uint32_t bm = m_module.opAccessChain(m_module.defPointerType(m_vec2Type, spv::StorageClassUniform), m_ps.sharedState, 1, &offset); bm = m_module.opLoad(m_vec2Type, bm); uint32_t t = m_module.opVectorShuffle(m_vec2Type, texture, texture, 2, indices.data()); uint32_t dot = m_module.opDot(m_floatType, bm, t); uint32_t result = m_module.opFAdd(m_floatType, tc_m_n, dot); coords = m_module.opCompositeInsert(typeId, result, coords, 1, &i); } return coords; }; auto ScalarReplicate = [&](uint32_t reg) { std::array replicant = { reg, reg, reg, reg }; return m_module.opCompositeConstruct(m_vec4Type, replicant.size(), replicant.data()); }; auto GetTexture = [&]() { if (!processedTexture) { SpirvImageOperands imageOperands; uint32_t imageVarId = m_module.opLoad(m_ps.samplers[i].typeId, m_ps.samplers[i].varId); uint32_t texcoordCnt = m_ps.samplers[i].texcoordCnt; // Add one for the texcoord count // if we need to include the divider if (m_fsKey.Stages[i].Contents.Projected) texcoordCnt++; std::array indices = { 0, 1, 2, 3 }; uint32_t texcoord = m_ps.in.TEXCOORD[i]; uint32_t texcoord_t = m_module.defVectorType(m_floatType, texcoordCnt); texcoord = m_module.opVectorShuffle(texcoord_t, texcoord, texcoord, texcoordCnt, indices.data()); bool shouldProject = m_fsKey.Stages[i].Contents.Projected; uint32_t projValue = 0; if (shouldProject) { // Always use w, the vertex shader puts the correct value there. const uint32_t projIdx = 3; projValue = m_module.opCompositeExtract(m_floatType, m_ps.in.TEXCOORD[i], 1, &projIdx); uint32_t insertIdx = texcoordCnt - 1; texcoord = m_module.opCompositeInsert(texcoord_t, projValue, texcoord, 1, &insertIdx); } if (i != 0 && ( m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAP || m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAPLUMINANCE)) { if (shouldProject) { uint32_t projRcp = m_module.opFDiv(m_floatType, m_module.constf32(1.0), projValue); texcoord = m_module.opVectorTimesScalar(texcoord_t, texcoord, projRcp); } texcoord = DoBumpmapCoords(texcoord_t, texcoord); shouldProject = false; } if (unlikely(stage.SampleDref)) { uint32_t component = 2; uint32_t reference = m_module.opCompositeExtract(m_floatType, texcoord, 1, &component); // [D3D8] Scale Dref to [0..(2^N - 1)] for D24S8 and D16 if Dref scaling is enabled if (m_options.drefScaling) { uint32_t maxDref = m_module.constf32(1.0f / (float(1 << m_options.drefScaling) - 1.0f)); reference = m_module.opFMul(m_floatType, reference, maxDref); } texture = m_module.opImageSampleDrefImplicitLod(m_floatType, imageVarId, texcoord, reference, imageOperands); texture = ScalarReplicate(texture); } else if (shouldProject) { texture = m_module.opImageSampleProjImplicitLod(m_vec4Type, imageVarId, texcoord, imageOperands); } else { texture = m_module.opImageSampleImplicitLod(m_vec4Type, imageVarId, texcoord, imageOperands); } if (i != 0 && m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAPLUMINANCE) { uint32_t index = m_module.constu32(D3D9SharedPSStages_Count * (i - 1) + D3D9SharedPSStages_BumpEnvLScale); uint32_t lScale = m_module.opAccessChain(m_module.defPointerType(m_floatType, spv::StorageClassUniform), m_ps.sharedState, 1, &index); lScale = m_module.opLoad(m_floatType, lScale); index = m_module.constu32(D3D9SharedPSStages_Count * (i - 1) + D3D9SharedPSStages_BumpEnvLOffset); uint32_t lOffset = m_module.opAccessChain(m_module.defPointerType(m_floatType, spv::StorageClassUniform), m_ps.sharedState, 1, &index); lOffset = m_module.opLoad(m_floatType, lOffset); uint32_t zIndex = 2; uint32_t scale = m_module.opCompositeExtract(m_floatType, texture, 1, &zIndex); scale = m_module.opFMul(m_floatType, scale, lScale); scale = m_module.opFAdd(m_floatType, scale, lOffset); scale = m_module.opFClamp(m_floatType, scale, m_module.constf32(0.0f), m_module.constf32(1.0)); texture = m_module.opVectorTimesScalar(m_vec4Type, texture, scale); } } processedTexture = true; return texture; }; auto AlphaReplicate = [&](uint32_t reg) { uint32_t alphaComponentId = 3; uint32_t alpha = m_module.opCompositeExtract(m_floatType, reg, 1, &alphaComponentId); return ScalarReplicate(alpha); }; auto Complement = [&](uint32_t reg) { return m_module.opFSub(m_vec4Type, m_module.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f), reg); }; auto Saturate = [&](uint32_t reg) { return m_module.opFClamp(m_vec4Type, reg, m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f), m_module.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f)); }; auto GetArg = [&] (uint32_t arg) { uint32_t reg = m_module.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f); switch (arg & D3DTA_SELECTMASK) { case D3DTA_CONSTANT: { uint32_t offset = m_module.constu32(D3D9SharedPSStages_Count * i + D3D9SharedPSStages_Constant); uint32_t ptr = m_module.opAccessChain(m_module.defPointerType(m_vec4Type, spv::StorageClassUniform), m_ps.sharedState, 1, &offset); reg = m_module.opLoad(m_vec4Type, ptr); break; } case D3DTA_CURRENT: reg = current; break; case D3DTA_DIFFUSE: reg = diffuse; break; case D3DTA_SPECULAR: reg = specular; break; case D3DTA_TEMP: reg = temp; break; case D3DTA_TEXTURE: if (stage.TextureBound != 0) { reg = GetTexture(); } else { reg = unboundTextureConstId; } break; case D3DTA_TFACTOR: reg = m_ps.constants.textureFactor; break; default: break; } // reg = 1 - reg if (arg & D3DTA_COMPLEMENT) reg = Complement(reg); // reg = reg.wwww if (arg & D3DTA_ALPHAREPLICATE) reg = AlphaReplicate(reg); return reg; }; auto DoOp = [&](D3DTEXTUREOP op, uint32_t dst, std::array arg) { switch (op) { case D3DTOP_SELECTARG1: dst = arg[1]; break; case D3DTOP_SELECTARG2: dst = arg[2]; break; case D3DTOP_MODULATE4X: dst = m_module.opFMul(m_vec4Type, arg[1], arg[2]); dst = m_module.opVectorTimesScalar(m_vec4Type, dst, m_module.constf32(4.0f)); dst = Saturate(dst); break; case D3DTOP_MODULATE2X: dst = m_module.opFMul(m_vec4Type, arg[1], arg[2]); dst = m_module.opVectorTimesScalar(m_vec4Type, dst, m_module.constf32(2.0f)); dst = Saturate(dst); break; case D3DTOP_MODULATE: dst = m_module.opFMul(m_vec4Type, arg[1], arg[2]); break; case D3DTOP_ADDSIGNED2X: arg[2] = m_module.opFSub(m_vec4Type, arg[2], m_module.constvec4f32(0.5f, 0.5f, 0.5f, 0.5f)); dst = m_module.opFAdd(m_vec4Type, arg[1], arg[2]); dst = m_module.opVectorTimesScalar(m_vec4Type, dst, m_module.constf32(2.0f)); dst = Saturate(dst); break; case D3DTOP_ADDSIGNED: arg[2] = m_module.opFSub(m_vec4Type, arg[2], m_module.constvec4f32(0.5f, 0.5f, 0.5f, 0.5f)); dst = m_module.opFAdd(m_vec4Type, arg[1], arg[2]); dst = Saturate(dst); break; case D3DTOP_ADD: dst = m_module.opFAdd(m_vec4Type, arg[1], arg[2]); dst = Saturate(dst); break; case D3DTOP_SUBTRACT: dst = m_module.opFSub(m_vec4Type, arg[1], arg[2]); dst = Saturate(dst); break; case D3DTOP_ADDSMOOTH: dst = m_module.opFFma(m_vec4Type, Complement(arg[1]), arg[2], arg[1]); dst = Saturate(dst); break; case D3DTOP_BLENDDIFFUSEALPHA: dst = m_module.opFMix(m_vec4Type, arg[2], arg[1], AlphaReplicate(diffuse)); break; case D3DTOP_BLENDTEXTUREALPHA: dst = m_module.opFMix(m_vec4Type, arg[2], arg[1], AlphaReplicate(GetTexture())); break; case D3DTOP_BLENDFACTORALPHA: dst = m_module.opFMix(m_vec4Type, arg[2], arg[1], AlphaReplicate(m_ps.constants.textureFactor)); break; case D3DTOP_BLENDTEXTUREALPHAPM: dst = m_module.opFFma(m_vec4Type, arg[2], Complement(AlphaReplicate(GetTexture())), arg[1]); dst = Saturate(dst); break; case D3DTOP_BLENDCURRENTALPHA: dst = m_module.opFMix(m_vec4Type, arg[2], arg[1], AlphaReplicate(current)); break; case D3DTOP_PREMODULATE: Logger::warn("D3DTOP_PREMODULATE: not implemented"); break; case D3DTOP_MODULATEALPHA_ADDCOLOR: dst = m_module.opFFma(m_vec4Type, AlphaReplicate(arg[1]), arg[2], arg[1]); dst = Saturate(dst); break; case D3DTOP_MODULATECOLOR_ADDALPHA: dst = m_module.opFFma(m_vec4Type, arg[1], arg[2], AlphaReplicate(arg[1])); dst = Saturate(dst); break; case D3DTOP_MODULATEINVALPHA_ADDCOLOR: dst = m_module.opFFma(m_vec4Type, Complement(AlphaReplicate(arg[1])), arg[2], arg[1]); dst = Saturate(dst); break; case D3DTOP_MODULATEINVCOLOR_ADDALPHA: dst = m_module.opFFma(m_vec4Type, Complement(arg[1]), arg[2], AlphaReplicate(arg[1])); dst = Saturate(dst); break; case D3DTOP_BUMPENVMAPLUMINANCE: case D3DTOP_BUMPENVMAP: // Load texture for the next stage... texture = GetTexture(); break; case D3DTOP_DOTPRODUCT3: { // Get vec3 of arg1 & 2 uint32_t vec3Type = m_module.defVectorType(m_floatType, 3); std::array indices = { 0, 1, 2 }; arg[1] = m_module.opVectorShuffle(vec3Type, arg[1], arg[1], indices.size(), indices.data()); arg[2] = m_module.opVectorShuffle(vec3Type, arg[2], arg[2], indices.size(), indices.data()); // Bias according to spec. arg[1] = m_module.opFSub(vec3Type, arg[1], m_module.constvec3f32(0.5f, 0.5f, 0.5f)); arg[2] = m_module.opFSub(vec3Type, arg[2], m_module.constvec3f32(0.5f, 0.5f, 0.5f)); // Do the dotting! dst = m_module.opDot(m_floatType, arg[1], arg[2]); // Multiply by 4 and replicate -> vec4 dst = m_module.opFMul(m_floatType, dst, m_module.constf32(4.0f)); dst = ScalarReplicate(dst); // Saturate dst = Saturate(dst); break; } case D3DTOP_MULTIPLYADD: dst = m_module.opFFma(m_vec4Type, arg[1], arg[2], arg[0]); dst = Saturate(dst); break; case D3DTOP_LERP: dst = m_module.opFMix(m_vec4Type, arg[2], arg[1], arg[0]); break; default: Logger::warn("Unhandled texture op!"); break; } return dst; }; uint32_t& dst = stage.ResultIsTemp ? temp : current; D3DTEXTUREOP colorOp = (D3DTEXTUREOP)stage.ColorOp; // This cancels all subsequent stages. if (colorOp == D3DTOP_DISABLE) break; std::array colorArgs = { stage.ColorArg0, stage.ColorArg1, stage.ColorArg2}; D3DTEXTUREOP alphaOp = (D3DTEXTUREOP)stage.AlphaOp; std::array alphaArgs = { stage.AlphaArg0, stage.AlphaArg1, stage.AlphaArg2}; auto ProcessArgs = [&](auto op, auto& args) { for (uint32_t& arg : args) arg = GetArg(arg); }; // Fast path if alpha/color path is identical. // D3DTOP_DOTPRODUCT3 also has special quirky behaviour here. const bool fastPath = colorOp == alphaOp && colorArgs == alphaArgs; if (fastPath || colorOp == D3DTOP_DOTPRODUCT3) { if (colorOp != D3DTOP_DISABLE) { ProcessArgs(colorOp, colorArgs); dst = DoOp(colorOp, dst, colorArgs); } } else { std::array indices = { 0, 1, 2, 4 + 3 }; uint32_t colorResult = dst; uint32_t alphaResult = dst; if (colorOp != D3DTOP_DISABLE) { ProcessArgs(colorOp, colorArgs); colorResult = DoOp(colorOp, dst, colorArgs); } if (alphaOp != D3DTOP_DISABLE) { ProcessArgs(alphaOp, alphaArgs); alphaResult = DoOp(alphaOp, dst, alphaArgs); } // src0.x, src0.y, src0.z src1.w if (colorResult != dst) dst = m_module.opVectorShuffle(m_vec4Type, colorResult, dst, indices.size(), indices.data()); // src0.x, src0.y, src0.z src1.w // But we flip src0, src1 to be inverse of color. if (alphaResult != dst) dst = m_module.opVectorShuffle(m_vec4Type, dst, alphaResult, indices.size(), indices.data()); } } if (m_fsKey.Stages[0].Contents.GlobalSpecularEnable) { uint32_t specular = m_module.opFMul(m_vec4Type, m_ps.in.COLOR[1], m_module.constvec4f32(1.0f, 1.0f, 1.0f, 0.0f)); current = m_module.opFAdd(m_vec4Type, current, specular); } D3D9FogContext fogCtx; fogCtx.IsPixel = true; fogCtx.RangeFog = false; fogCtx.RenderState = m_rsBlock; fogCtx.vPos = m_ps.in.POS; fogCtx.vFog = m_ps.in.FOG; fogCtx.oColor = current; fogCtx.IsFixedFunction = true; fogCtx.IsPositionT = false; fogCtx.HasSpecular = false; fogCtx.Specular = 0; fogCtx.SpecUBO = m_specUbo; current = DoFixedFunctionFog(m_spec, m_module, fogCtx); m_module.opStore(m_ps.out.COLOR, current); alphaTestPS(); } void D3D9FFShaderCompiler::setupPS() { setupRenderStateInfo(); m_specUbo = SetupSpecUBO(m_module, m_bindings); // PS Caps m_module.enableExtension("SPV_EXT_demote_to_helper_invocation"); m_module.enableCapability(spv::CapabilityDemoteToHelperInvocationEXT); m_module.enableCapability(spv::CapabilityDerivativeControl); m_module.setExecutionMode(m_entryPointId, spv::ExecutionModeOriginUpperLeft); uint32_t pointCoord = GetPointCoord(m_module); auto pointInfo = GetPointSizeInfoPS(m_spec, m_module, m_rsBlock, m_specUbo); // We need to replace TEXCOORD inputs with gl_PointCoord // if D3DRS_POINTSPRITEENABLE is set. for (uint32_t i = 0; i < caps::TextureStageCount; i++) { m_ps.in.TEXCOORD[i] = declareIO(true, DxsoSemantic{ DxsoUsage::Texcoord, i }); m_ps.in.TEXCOORD[i] = m_module.opSelect(m_vec4Type, pointInfo.isSprite, pointCoord, m_ps.in.TEXCOORD[i]); } m_ps.in.COLOR[0] = declareIO(true, DxsoSemantic{ DxsoUsage::Color, 0 }); m_ps.in.COLOR[1] = declareIO(true, DxsoSemantic{ DxsoUsage::Color, 1 }); m_ps.in.FOG = declareIO(true, DxsoSemantic{ DxsoUsage::Fog, 0 }); m_ps.in.POS = declareIO(true, DxsoSemantic{ DxsoUsage::Position, 0 }, spv::BuiltInFragCoord); m_ps.out.COLOR = declareIO(false, DxsoSemantic{ DxsoUsage::Color, 0 }); // Constant Buffer for PS. std::array members = { m_vec4Type // Texture Factor }; const uint32_t structType = m_module.defStructType(members.size(), members.data()); m_module.decorateBlock(structType); uint32_t offset = 0; for (uint32_t i = 0; i < uint32_t(D3D9FFPSMembers::MemberCount); i++) { m_module.memberDecorateOffset(structType, i, offset); offset += sizeof(Vector4); } m_module.setDebugName(structType, "D3D9FixedFunctionPS"); m_module.setDebugMemberName(structType, 0, "textureFactor"); m_ps.constantBuffer = m_module.newVar( m_module.defPointerType(structType, spv::StorageClassUniform), spv::StorageClassUniform); m_module.setDebugName(m_ps.constantBuffer, "consts"); const uint32_t bindingId = computeResourceSlotId( DxsoProgramType::PixelShader, DxsoBindingType::ConstantBuffer, DxsoConstantBuffers::PSFixedFunction); m_module.decorateDescriptorSet(m_ps.constantBuffer, 0); m_module.decorateBinding(m_ps.constantBuffer, bindingId); DxvkBindingInfo binding = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }; binding.resourceBinding = bindingId; binding.viewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM; binding.access = VK_ACCESS_UNIFORM_READ_BIT; binding.uboSet = VK_TRUE; m_bindings.push_back(binding); // Load constants auto LoadConstant = [&](uint32_t type, uint32_t idx) { uint32_t offset = m_module.constu32(idx); uint32_t typePtr = m_module.defPointerType(type, spv::StorageClassUniform); return m_module.opLoad(type, m_module.opAccessChain(typePtr, m_ps.constantBuffer, 1, &offset)); }; m_ps.constants.textureFactor = LoadConstant(m_vec4Type, uint32_t(D3D9FFPSMembers::TextureFactor)); // Samplers for (uint32_t i = 0; i < caps::TextureStageCount; i++) { auto& sampler = m_ps.samplers[i]; D3DRESOURCETYPE type = D3DRESOURCETYPE(m_fsKey.Stages[i].Contents.Type + D3DRTYPE_TEXTURE); spv::Dim dimensionality; VkImageViewType viewType; switch (type) { default: case D3DRTYPE_TEXTURE: dimensionality = spv::Dim2D; sampler.texcoordCnt = 2; viewType = VK_IMAGE_VIEW_TYPE_2D; // Z coordinate for Dref sampling if (m_fsKey.Stages[i].Contents.SampleDref) sampler.texcoordCnt++; break; case D3DRTYPE_CUBETEXTURE: dimensionality = spv::DimCube; sampler.texcoordCnt = 3; viewType = VK_IMAGE_VIEW_TYPE_CUBE; break; case D3DRTYPE_VOLUMETEXTURE: dimensionality = spv::Dim3D; sampler.texcoordCnt = 3; viewType = VK_IMAGE_VIEW_TYPE_3D; break; } sampler.typeId = m_module.defImageType( m_module.defFloatType(32), dimensionality, 0, 0, 0, 1, spv::ImageFormatUnknown); sampler.typeId = m_module.defSampledImageType(sampler.typeId); sampler.varId = m_module.newVar( m_module.defPointerType( sampler.typeId, spv::StorageClassUniformConstant), spv::StorageClassUniformConstant); std::string name = str::format("s", i); m_module.setDebugName(sampler.varId, name.c_str()); const uint32_t bindingId = computeResourceSlotId(DxsoProgramType::PixelShader, DxsoBindingType::Image, i); m_module.decorateDescriptorSet(sampler.varId, 0); m_module.decorateBinding(sampler.varId, bindingId); // Store descriptor info for the shader interface DxvkBindingInfo binding = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER }; binding.resourceBinding = bindingId; binding.viewType = viewType; binding.access = VK_ACCESS_SHADER_READ_BIT; m_bindings.push_back(binding); } emitPsSharedConstants(); } void D3D9FFShaderCompiler::emitPsSharedConstants() { m_ps.sharedState = GetSharedConstants(m_module); const uint32_t bindingId = computeResourceSlotId( m_programType, DxsoBindingType::ConstantBuffer, PSShared); m_module.decorateDescriptorSet(m_ps.sharedState, 0); m_module.decorateBinding(m_ps.sharedState, bindingId); DxvkBindingInfo binding = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }; binding.resourceBinding = bindingId; binding.viewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM; binding.access = VK_ACCESS_UNIFORM_READ_BIT; binding.uboSet = VK_TRUE; m_bindings.push_back(binding); } void D3D9FFShaderCompiler::emitVsClipping(uint32_t vtx) { uint32_t worldPos = emitMatrixTimesVector(4, 4, m_vs.constants.inverseView, vtx); uint32_t clipPlaneCountId = m_module.constu32(caps::MaxClipPlanes); uint32_t floatType = m_module.defFloatType(32); uint32_t vec4Type = m_module.defVectorType(floatType, 4); uint32_t boolType = m_module.defBoolType(); // Declare uniform buffer containing clip planes uint32_t clipPlaneArray = m_module.defArrayTypeUnique(vec4Type, clipPlaneCountId); uint32_t clipPlaneStruct = m_module.defStructTypeUnique(1, &clipPlaneArray); uint32_t clipPlaneBlock = m_module.newVar( m_module.defPointerType(clipPlaneStruct, spv::StorageClassUniform), spv::StorageClassUniform); m_module.decorateArrayStride (clipPlaneArray, 16); m_module.setDebugName (clipPlaneStruct, "clip_info_t"); m_module.setDebugMemberName (clipPlaneStruct, 0, "clip_planes"); m_module.decorate (clipPlaneStruct, spv::DecorationBlock); m_module.memberDecorateOffset (clipPlaneStruct, 0, 0); uint32_t bindingId = computeResourceSlotId( DxsoProgramType::VertexShader, DxsoBindingType::ConstantBuffer, DxsoConstantBuffers::VSClipPlanes); m_module.setDebugName (clipPlaneBlock, "clip_info"); m_module.decorateDescriptorSet(clipPlaneBlock, 0); m_module.decorateBinding (clipPlaneBlock, bindingId); DxvkBindingInfo binding = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }; binding.resourceBinding = bindingId; binding.viewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM; binding.access = VK_ACCESS_UNIFORM_READ_BIT; binding.uboSet = VK_TRUE; m_bindings.push_back(binding); // Declare output array for clip distances uint32_t clipDistArray = m_module.newVar( m_module.defPointerType( m_module.defArrayType(floatType, clipPlaneCountId), spv::StorageClassOutput), spv::StorageClassOutput); m_module.decorateBuiltIn(clipDistArray, spv::BuiltInClipDistance); // Always consider clip planes enabled when doing GPL by forcing 6 for the quick value. uint32_t clipPlaneCount = m_spec.get(m_module, m_specUbo, SpecClipPlaneCount, 0, 32, m_module.constu32(caps::MaxClipPlanes)); // Compute clip distances for (uint32_t i = 0; i < caps::MaxClipPlanes; i++) { std::array blockMembers = {{ m_module.constu32(0), m_module.constu32(i), }}; uint32_t planeId = m_module.opLoad(vec4Type, m_module.opAccessChain( m_module.defPointerType(vec4Type, spv::StorageClassUniform), clipPlaneBlock, blockMembers.size(), blockMembers.data())); uint32_t distId = m_module.opDot(floatType, worldPos, planeId); uint32_t clipPlaneEnabled = m_module.opULessThan(boolType, m_module.constu32(i), clipPlaneCount); uint32_t value = m_module.opSelect(floatType, clipPlaneEnabled, distId, m_module.constf32(0.0f)); m_module.opStore(m_module.opAccessChain( m_module.defPointerType(floatType, spv::StorageClassOutput), clipDistArray, 1, &blockMembers[1]), value); } } void D3D9FFShaderCompiler::alphaTestPS() { uint32_t uintPtr = m_module.defPointerType(m_uint32Type, spv::StorageClassPushConstant); auto oC0 = m_ps.out.COLOR; uint32_t alphaComponentId = 3; uint32_t alphaRefMember = m_module.constu32(uint32_t(D3D9RenderStateItem::AlphaRef)); D3D9AlphaTestContext alphaTestContext; alphaTestContext.alphaFuncId = m_spec.get(m_module, m_specUbo, SpecAlphaCompareOp); alphaTestContext.alphaPrecisionId = m_spec.get(m_module, m_specUbo, SpecAlphaPrecisionBits); alphaTestContext.alphaRefId = m_module.opLoad(m_uint32Type, m_module.opAccessChain(uintPtr, m_rsBlock, 1, &alphaRefMember)); alphaTestContext.alphaId = m_module.opCompositeExtract(m_floatType, m_module.opLoad(m_vec4Type, oC0), 1, &alphaComponentId); DoFixedFunctionAlphaTest(m_module, alphaTestContext); } uint32_t D3D9FFShaderCompiler::emitMatrixTimesVector(uint32_t rowCount, uint32_t colCount, uint32_t matrix, uint32_t vector) { uint32_t f32Type = m_module.defFloatType(32); uint32_t vecType = m_module.defVectorType(f32Type, rowCount); uint32_t accum = 0; for (uint32_t i = 0; i < colCount; i++) { std::array indices = { i, i, i, i }; uint32_t a = m_module.opVectorShuffle(vecType, vector, vector, rowCount, indices.data()); uint32_t b = m_module.opCompositeExtract(vecType, matrix, 1, &i); accum = accum ? m_module.opFFma(vecType, a, b, accum) : m_module.opFMul(vecType, a, b); m_module.decorate(accum, spv::DecorationNoContraction); } return accum; } uint32_t D3D9FFShaderCompiler::emitVectorTimesMatrix(uint32_t rowCount, uint32_t colCount, uint32_t vector, uint32_t matrix) { uint32_t f32Type = m_module.defFloatType(32); uint32_t vecType = m_module.defVectorType(f32Type, colCount); uint32_t matType = m_module.defMatrixType(vecType, rowCount); matrix = m_module.opTranspose(matType, matrix); return emitMatrixTimesVector(colCount, rowCount, matrix, vector); } D3D9FFShader::D3D9FFShader( D3D9DeviceEx* pDevice, const D3D9FFShaderKeyVS& Key) { Sha1Hash hash = Sha1Hash::compute(&Key, sizeof(Key)); DxvkShaderKey shaderKey = { VK_SHADER_STAGE_VERTEX_BIT, hash }; std::string name = str::format("FF_", shaderKey.toString()); D3D9FFShaderCompiler compiler( pDevice->GetDXVKDevice(), Key, name, pDevice->GetOptions()); m_shader = compiler.compile(); m_isgn = compiler.isgn(); Dump(pDevice, Key, name); m_shader->setShaderKey(shaderKey); pDevice->GetDXVKDevice()->registerShader(m_shader); } D3D9FFShader::D3D9FFShader( D3D9DeviceEx* pDevice, const D3D9FFShaderKeyFS& Key) { Sha1Hash hash = Sha1Hash::compute(&Key, sizeof(Key)); DxvkShaderKey shaderKey = { VK_SHADER_STAGE_FRAGMENT_BIT, hash }; std::string name = str::format("FF_", shaderKey.toString()); D3D9FFShaderCompiler compiler( pDevice->GetDXVKDevice(), Key, name, pDevice->GetOptions()); m_shader = compiler.compile(); m_isgn = compiler.isgn(); Dump(pDevice, Key, name); m_shader->setShaderKey(shaderKey); pDevice->GetDXVKDevice()->registerShader(m_shader); } template void D3D9FFShader::Dump(D3D9DeviceEx* pDevice, const T& Key, const std::string& Name) { const std::string& dumpPath = pDevice->GetOptions()->shaderDumpPath; if (dumpPath.size() != 0) { std::ofstream dumpStream( str::topath(str::format(dumpPath, "/", Name, ".spv").c_str()).c_str(), std::ios_base::binary | std::ios_base::trunc); m_shader->dump(dumpStream); } } D3D9FFShader D3D9FFShaderModuleSet::GetShaderModule( D3D9DeviceEx* pDevice, const D3D9FFShaderKeyVS& ShaderKey) { // Use the shader's unique key for the lookup auto entry = m_vsModules.find(ShaderKey); if (entry != m_vsModules.end()) return entry->second; D3D9FFShader shader( pDevice, ShaderKey); m_vsModules.insert({ShaderKey, shader}); return shader; } D3D9FFShader D3D9FFShaderModuleSet::GetShaderModule( D3D9DeviceEx* pDevice, const D3D9FFShaderKeyFS& ShaderKey) { // Use the shader's unique key for the lookup auto entry = m_fsModules.find(ShaderKey); if (entry != m_fsModules.end()) return entry->second; D3D9FFShader shader( pDevice, ShaderKey); m_fsModules.insert({ShaderKey, shader}); return shader; } size_t D3D9FFShaderKeyHash::operator () (const D3D9FFShaderKeyVS& key) const { DxvkHashState state; std::hash uint32hash; for (uint32_t i = 0; i < std::size(key.Data.Primitive); i++) state.add(uint32hash(key.Data.Primitive[i])); return state; } size_t D3D9FFShaderKeyHash::operator () (const D3D9FFShaderKeyFS& key) const { DxvkHashState state; std::hash uint32hash; for (uint32_t i = 0; i < caps::TextureStageCount; i++) { for (uint32_t j = 0; j < std::size(key.Stages[i].Primitive); j++) state.add(uint32hash(key.Stages[i].Primitive[j])); } return state; } bool operator == (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b) { return std::memcmp(&a, &b, sizeof(D3D9FFShaderKeyVS)) == 0; } bool operator == (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b) { return std::memcmp(&a, &b, sizeof(D3D9FFShaderKeyFS)) == 0; } bool operator != (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b) { return !(a == b); } bool operator != (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b) { return !(a == b); } bool D3D9FFShaderKeyEq::operator () (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b) const { return a == b; } bool D3D9FFShaderKeyEq::operator () (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b) const { return a == b; } static inline DxsoIsgn CreateFixedFunctionIsgn() { DxsoIsgn ffIsgn; ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Position, 0 }; ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Normal, 0 }; ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Position, 1 }; ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Normal, 1 }; for (uint32_t i = 0; i < 8; i++) ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Texcoord, i }; ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Color, 0 }; ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Color, 1 }; ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Fog, 0 }; ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::PointSize, 0 }; ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::BlendWeight, 0 }; ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::BlendIndices, 0 }; return ffIsgn; } DxsoIsgn g_ffIsgn = CreateFixedFunctionIsgn(); } dxvk-2.6.1/src/d3d9/d3d9_fixed_function.h000066400000000000000000000152531477473124000200450ustar00rootroot00000000000000#pragma once #include "d3d9_include.h" #include "d3d9_caps.h" #include "../dxvk/dxvk_shader.h" #include "../dxso/dxso_isgn.h" #include namespace dxvk { class D3D9DeviceEx; class SpirvModule; struct D3D9Options; class D3D9ShaderSpecConstantManager; struct D3D9FogContext { // General inputs... bool IsPixel; bool RangeFog; uint32_t RenderState; uint32_t vPos; uint32_t vFog; uint32_t oColor; bool HasFogInput; bool IsFixedFunction; bool IsPositionT; bool HasSpecular; uint32_t Specular; uint32_t SpecUBO; }; struct D3D9AlphaTestContext { uint32_t alphaId; uint32_t alphaPrecisionId; uint32_t alphaFuncId; uint32_t alphaRefId; }; struct D3D9FixedFunctionOptions { D3D9FixedFunctionOptions(const D3D9Options* options); bool invariantPosition; bool forceSampleRateShading; int32_t drefScaling; }; constexpr float GetDrefScaleFactor(int32_t bitDepth) { return 1.0f / (float(1 << bitDepth) - 1.0f); } // Returns new oFog if VS // Returns new oColor if PS uint32_t DoFixedFunctionFog(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, const D3D9FogContext& fogCtx); void DoFixedFunctionAlphaTest(SpirvModule& spvModule, const D3D9AlphaTestContext& ctx); // Returns a render state block uint32_t SetupRenderStateBlock(SpirvModule& spvModule); struct D3D9PointSizeInfoVS { uint32_t defaultValue; uint32_t min; uint32_t max; }; // Default point size and point scale magic! D3D9PointSizeInfoVS GetPointSizeInfoVS(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, uint32_t vPos, uint32_t vtx, uint32_t perVertPointSize, uint32_t rsBlock, uint32_t specUbo, bool isFixedFunction); struct D3D9PointSizeInfoPS { uint32_t isSprite; }; D3D9PointSizeInfoPS GetPointSizeInfoPS(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, uint32_t rsBlock, uint32_t specUbo); uint32_t GetPointCoord(SpirvModule& spvModule); uint32_t GetSharedConstants(SpirvModule& spvModule); uint32_t SetupSpecUBO(SpirvModule& spvModule, std::vector& bindings); constexpr uint32_t TCIOffset = 16; constexpr uint32_t TCIMask = 0b111 << TCIOffset; enum D3D9FF_VertexBlendMode { D3D9FF_VertexBlendMode_Disabled, D3D9FF_VertexBlendMode_Normal, D3D9FF_VertexBlendMode_Tween, }; struct D3D9FFShaderKeyVSData { union { struct { uint32_t TexcoordIndices : 24; uint32_t HasPositionT : 1; uint32_t HasColor0 : 1; // Diffuse uint32_t HasColor1 : 1; // Specular uint32_t HasPointSize : 1; uint32_t UseLighting : 1; uint32_t NormalizeNormals : 1; uint32_t LocalViewer : 1; uint32_t RangeFog : 1; uint32_t TexcoordFlags : 24; uint32_t DiffuseSource : 2; uint32_t AmbientSource : 2; uint32_t SpecularSource : 2; uint32_t EmissiveSource : 2; uint32_t TransformFlags : 24; uint32_t LightCount : 4; uint32_t TexcoordDeclMask : 24; uint32_t HasFog : 1; uint32_t VertexBlendMode : 2; uint32_t VertexBlendIndexed : 1; uint32_t VertexBlendCount : 3; uint32_t VertexClipping : 1; uint32_t Projected : 8; } Contents; uint32_t Primitive[5]; }; }; struct D3D9FFShaderKeyVS { D3D9FFShaderKeyVS() { // memcmp safety std::memset(&Data, 0, sizeof(Data)); } D3D9FFShaderKeyVSData Data; }; constexpr uint32_t TextureArgCount = 3; struct D3D9FFShaderStage { union { struct { uint32_t ColorOp : 5; uint32_t ColorArg0 : 6; uint32_t ColorArg1 : 6; uint32_t ColorArg2 : 6; uint32_t AlphaOp : 5; uint32_t AlphaArg0 : 6; uint32_t AlphaArg1 : 6; uint32_t AlphaArg2 : 6; uint32_t Type : 2; uint32_t ResultIsTemp : 1; uint32_t Projected : 1; uint32_t ProjectedCount : 3; uint32_t SampleDref : 1; uint32_t TextureBound : 1; // Included in here, read from Stage 0 for packing reasons // Affects all stages. uint32_t GlobalSpecularEnable : 1; } Contents; uint32_t Primitive[2]; }; }; struct D3D9FFShaderKeyFS { D3D9FFShaderKeyFS() { // memcmp safety std::memset(Stages, 0, sizeof(Stages)); // Normalize this. DISABLE != 0. for (uint32_t i = 0; i < caps::TextureStageCount; i++) { Stages[i].Contents.ColorOp = D3DTOP_DISABLE; Stages[i].Contents.AlphaOp = D3DTOP_DISABLE; } } D3D9FFShaderStage Stages[caps::TextureStageCount]; }; struct D3D9FFShaderKeyHash { size_t operator () (const D3D9FFShaderKeyVS& key) const; size_t operator () (const D3D9FFShaderKeyFS& key) const; }; bool operator == (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b); bool operator != (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b); bool operator == (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b); bool operator != (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b); struct D3D9FFShaderKeyEq { bool operator () (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b) const; bool operator () (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b) const; }; class D3D9FFShader { public: D3D9FFShader( D3D9DeviceEx* pDevice, const D3D9FFShaderKeyVS& Key); D3D9FFShader( D3D9DeviceEx* pDevice, const D3D9FFShaderKeyFS& Key); template void Dump(D3D9DeviceEx* pDevice, const T& Key, const std::string& Name); Rc GetShader() const { return m_shader; } private: Rc m_shader; DxsoIsgn m_isgn; }; class D3D9FFShaderModuleSet : public RcObject { public: D3D9FFShader GetShaderModule( D3D9DeviceEx* pDevice, const D3D9FFShaderKeyVS& ShaderKey); D3D9FFShader GetShaderModule( D3D9DeviceEx* pDevice, const D3D9FFShaderKeyFS& ShaderKey); UINT GetVSCount() const { return m_vsModules.size(); } UINT GetFSCount() const { return m_fsModules.size(); } private: std::unordered_map< D3D9FFShaderKeyVS, D3D9FFShader, D3D9FFShaderKeyHash, D3D9FFShaderKeyEq> m_vsModules; std::unordered_map< D3D9FFShaderKeyFS, D3D9FFShader, D3D9FFShaderKeyHash, D3D9FFShaderKeyEq> m_fsModules; }; inline const DxsoIsgn& GetFixedFunctionIsgn() { extern DxsoIsgn g_ffIsgn; return g_ffIsgn; } }dxvk-2.6.1/src/d3d9/d3d9_format.cpp000066400000000000000000000470771477473124000166750ustar00rootroot00000000000000#include "d3d9_format.h" namespace dxvk { // It is also worth noting that the msb/lsb-ness is flipped between VK and D3D9. D3D9_VK_FORMAT_MAPPING ConvertFormatUnfixed(D3D9Format Format) { switch (Format) { case D3D9Format::Unknown: return {}; case D3D9Format::R8G8B8: return {}; // Unsupported case D3D9Format::A8R8G8B8: return { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::X8R8G8B8: return { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::R5G6B5: return { VK_FORMAT_R5G6B5_UNORM_PACK16, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT}; case D3D9Format::X1R5G5B5: return { VK_FORMAT_A1R5G5B5_UNORM_PACK16, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::A1R5G5B5: return { VK_FORMAT_A1R5G5B5_UNORM_PACK16, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::A4R4G4B4: return { VK_FORMAT_A4R4G4B4_UNORM_PACK16, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::R3G3B2: return {}; // Unsupported case D3D9Format::A8: return { VK_FORMAT_R8_UNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_R }}; case D3D9Format::A8R3G3B2: return {}; // Unsupported case D3D9Format::X4R4G4B4: return { VK_FORMAT_A4R4G4B4_UNORM_PACK16, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::A2B10G10R10: return { VK_FORMAT_A2B10G10R10_UNORM_PACK32, // The A2 is out of place here. This should be investigated. VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::A8B8G8R8: return { VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::X8B8G8R8: return { VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::G16R16: return { VK_FORMAT_R16G16_UNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::A2R10G10B10: return { VK_FORMAT_A2R10G10B10_UNORM_PACK32, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::A16B16G16R16: return { VK_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::A8P8: return {}; // Unsupported case D3D9Format::P8: return {}; // Unsupported case D3D9Format::L8: return { VK_FORMAT_R8_UNORM, VK_FORMAT_R8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::A8L8: return { VK_FORMAT_R8G8_UNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G }}; case D3D9Format::A4L4: return { VK_FORMAT_R4G4_UNORM_PACK8, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R }}; case D3D9Format::V8U8: return { VK_FORMAT_R8G8_SNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::L6V5U5: return { // Any PACK16 format will do... VK_FORMAT_B5G6R5_UNORM_PACK16, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }, { D3D9ConversionFormat_L6V5U5, // Convert -> float (this is a mixed snorm and unorm type) VK_FORMAT_R16G16B16A16_SFLOAT } }; case D3D9Format::X8L8V8U8: return { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE }, { D3D9ConversionFormat_X8L8V8U8, // Convert -> float (this is a mixed snorm and unorm type) VK_FORMAT_R16G16B16A16_SFLOAT } }; case D3D9Format::Q8W8V8U8: return { VK_FORMAT_R8G8B8A8_SNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::V16U16: return { VK_FORMAT_R16G16_SNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::A2W10V10U10: return { VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }, { D3D9ConversionFormat_A2W10V10U10, // Convert -> float (this is a mixed snorm and unorm type) VK_FORMAT_R16G16B16A16_SFLOAT } }; case D3D9Format::W11V11U10: return { VK_FORMAT_B10G11R11_UFLOAT_PACK32, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE }, { D3D9ConversionFormat_W11V11U10, // can't use B10G11R11 bc this is a snorm type VK_FORMAT_R16G16B16A16_SNORM } }; case D3D9Format::UYVY: return { VK_FORMAT_G8B8G8R8_422_UNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }, { D3D9ConversionFormat_UYVY, VK_FORMAT_B8G8R8A8_UNORM } }; case D3D9Format::R8G8_B8G8: return { VK_FORMAT_G8B8G8R8_422_UNORM, // This format may have been _SCALED in DX9. VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::YUY2: return { VK_FORMAT_G8B8G8R8_422_UNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }, { D3D9ConversionFormat_YUY2, VK_FORMAT_B8G8R8A8_UNORM } }; case D3D9Format::G8R8_G8B8: return { VK_FORMAT_B8G8R8G8_422_UNORM, // This format may have been _SCALED in DX9. VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::DXT1: return { VK_FORMAT_BC1_RGBA_UNORM_BLOCK, VK_FORMAT_BC1_RGBA_SRGB_BLOCK, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::DXT2: return { VK_FORMAT_BC2_UNORM_BLOCK, VK_FORMAT_BC2_SRGB_BLOCK, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::DXT3: return { VK_FORMAT_BC2_UNORM_BLOCK, VK_FORMAT_BC2_SRGB_BLOCK, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::DXT4: return { VK_FORMAT_BC3_UNORM_BLOCK, VK_FORMAT_BC3_SRGB_BLOCK, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::DXT5: return { VK_FORMAT_BC3_UNORM_BLOCK, VK_FORMAT_BC3_SRGB_BLOCK, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::D16_LOCKABLE: return { VK_FORMAT_D16_UNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_DEPTH_BIT }; case D3D9Format::D32: return {}; // Unsupported (everywhere) case D3D9Format::D15S1: return {}; // Unsupported (everywhere) case D3D9Format::D24S8: return { VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT }; case D3D9Format::D24X8: return { VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_DEPTH_BIT }; case D3D9Format::D24X4S4: return {}; // Unsupported (everywhere) case D3D9Format::D16: return { VK_FORMAT_D16_UNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_DEPTH_BIT }; case D3D9Format::D32F_LOCKABLE: return { VK_FORMAT_D32_SFLOAT, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_DEPTH_BIT }; case D3D9Format::D24FS8: return { VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT }; case D3D9Format::D32_LOCKABLE: return { VK_FORMAT_D32_SFLOAT, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_DEPTH_BIT }; case D3D9Format::S8_LOCKABLE: return { VK_FORMAT_S8_UINT, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_STENCIL_BIT }; case D3D9Format::L16: return { VK_FORMAT_R16_UNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::VERTEXDATA: return { VK_FORMAT_R8_UINT, VK_FORMAT_UNDEFINED, 0 }; case D3D9Format::INDEX16: return { VK_FORMAT_R16_UINT, VK_FORMAT_UNDEFINED, 0 }; case D3D9Format::INDEX32: return { VK_FORMAT_R32_UINT, VK_FORMAT_UNDEFINED, 0 }; case D3D9Format::Q16W16V16U16: return { VK_FORMAT_R16G16B16A16_SNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::MULTI2_ARGB8: return {}; // Unsupported case D3D9Format::R16F: return { VK_FORMAT_R16_SFLOAT, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::G16R16F: return { VK_FORMAT_R16G16_SFLOAT, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::A16B16G16R16F: return { VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::R32F: return { VK_FORMAT_R32_SFLOAT, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::G32R32F: return { VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::A32B32G32R32F: return { VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::CxV8U8: return {}; // Unsupported case D3D9Format::A1: return {}; // Unsupported case D3D9Format::A2B10G10R10_XR_BIAS: return { VK_FORMAT_A2B10G10R10_SNORM_PACK32, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT }; case D3D9Format::BINARYBUFFER: return { VK_FORMAT_R8_UINT, VK_FORMAT_UNDEFINED, 0 }; case D3D9Format::ATI1: return { VK_FORMAT_BC4_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::ATI2: return { VK_FORMAT_BC5_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::INST: return {}; // Driver hack, handled elsewhere case D3D9Format::DF24: return { VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_DEPTH_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::DF16: return { VK_FORMAT_D16_UNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_DEPTH_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE }}; case D3D9Format::NULL_FORMAT: return {}; // Driver hack, handled elsewhere case D3D9Format::GET4: return {}; // Unsupported case D3D9Format::GET1: return {}; // Unsupported case D3D9Format::NVDB: return {}; // Driver hack, handled elsewhere case D3D9Format::A2M1: return {}; // Driver hack, handled elsewhere case D3D9Format::A2M0: return {}; // Driver hack, handled elsewhere case D3D9Format::ATOC: return {}; // Driver hack, handled elsewhere case D3D9Format::INTZ: return { VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R }}; case D3D9Format::NV12: return { VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }, { D3D9ConversionFormat_NV12, VK_FORMAT_B8G8R8A8_UNORM } }; case D3D9Format::YV12: return { VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }, { D3D9ConversionFormat_YV12, VK_FORMAT_B8G8R8A8_UNORM } }; case D3D9Format::RAWZ: return {}; // Unsupported case D3D9Format::R16: return {}; // Unsupported case D3D9Format::AL16: return {}; // Unsupported default: Logger::warn(str::format("ConvertFormat: Unknown format encountered: ", Format)); return {}; // Unsupported } } // Block size of formats that require some form of alignment D3D9_FORMAT_BLOCK_SIZE GetFormatAlignedBlockSize(D3D9Format Format) { switch (Format) { case D3D9Format::DXT1: case D3D9Format::DXT2: case D3D9Format::DXT3: case D3D9Format::DXT4: case D3D9Format::DXT5: case D3D9Format::ATI1: case D3D9Format::ATI2: return { 4, 4, 1 }; case D3D9Format::YUY2: case D3D9Format::UYVY: return { 2, 1, 1 }; default: return {}; // Irrelevant or unknown block size } } D3D9VkFormatTable::D3D9VkFormatTable( const Rc& adapter, const D3D9Options& options) { const auto& props = adapter->deviceProperties(); uint32_t vendorId = options.customVendorId == -1 ? props.vendorID : uint32_t(options.customVendorId); // NVIDIA does not natively support any DF formats m_dfSupport = vendorId == uint32_t(DxvkGpuVendor::Nvidia) ? false : options.supportDFFormats; m_x4r4g4b4Support = options.supportX4R4G4B4; // Only AMD supports D16_LOCKABLE natively m_d16lockableSupport = vendorId == uint32_t(DxvkGpuVendor::Amd) ? true : options.supportD16Lockable; // AMD do not support 24-bit depth buffers on Vulkan, // so we have to fall back to a 32-bit depth format. m_d24s8Support = !options.useD32forD24 && CheckImageFormatSupport(adapter, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT); // NVIDIA do not support 16-bit depth buffers with stencil on Vulkan, // so we have to fall back to a 32-bit depth format. m_d16s8Support = CheckImageFormatSupport(adapter, VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT); if (!m_d24s8Support) Logger::info("D3D9: VK_FORMAT_D24_UNORM_S8_UINT -> VK_FORMAT_D32_SFLOAT_S8_UINT"); if (!m_d16s8Support) { if (m_d24s8Support) Logger::info("D3D9: VK_FORMAT_D16_UNORM_S8_UINT -> VK_FORMAT_D24_UNORM_S8_UINT"); else Logger::info("D3D9: VK_FORMAT_D16_UNORM_S8_UINT -> VK_FORMAT_D32_SFLOAT_S8_UINT"); } } D3D9_VK_FORMAT_MAPPING D3D9VkFormatTable::GetFormatMapping( D3D9Format Format) const { D3D9_VK_FORMAT_MAPPING mapping = ConvertFormatUnfixed(Format); if (Format == D3D9Format::X4R4G4B4 && !m_x4r4g4b4Support) return D3D9_VK_FORMAT_MAPPING(); if (Format == D3D9Format::D16_LOCKABLE && !m_d16lockableSupport) return D3D9_VK_FORMAT_MAPPING(); if (Format == D3D9Format::DF16 && !m_dfSupport) return D3D9_VK_FORMAT_MAPPING(); if (Format == D3D9Format::DF24 && !m_dfSupport) return D3D9_VK_FORMAT_MAPPING(); if (!m_d24s8Support && mapping.FormatColor == VK_FORMAT_D24_UNORM_S8_UINT) mapping.FormatColor = (mapping.Aspect & VK_IMAGE_ASPECT_STENCIL_BIT) ? VK_FORMAT_D32_SFLOAT_S8_UINT : VK_FORMAT_D32_SFLOAT; if (!m_d16s8Support && mapping.FormatColor == VK_FORMAT_D16_UNORM_S8_UINT) mapping.FormatColor = m_d24s8Support ? VK_FORMAT_D24_UNORM_S8_UINT : VK_FORMAT_D32_SFLOAT_S8_UINT; return mapping; } const DxvkFormatInfo* D3D9VkFormatTable::GetUnsupportedFormatInfo( D3D9Format Format) const { static const DxvkFormatInfo r8b8g8 = { 3, VK_IMAGE_ASPECT_COLOR_BIT }; static const DxvkFormatInfo r3g3b2 = { 1, VK_IMAGE_ASPECT_COLOR_BIT }; static const DxvkFormatInfo a8r3g3b2 = { 2, VK_IMAGE_ASPECT_COLOR_BIT }; static const DxvkFormatInfo a8p8 = { 2, VK_IMAGE_ASPECT_COLOR_BIT }; static const DxvkFormatInfo p8 = { 1, VK_IMAGE_ASPECT_COLOR_BIT }; static const DxvkFormatInfo cxv8u8 = { 2, VK_IMAGE_ASPECT_COLOR_BIT }; static const DxvkFormatInfo unknown = {}; switch (Format) { case D3D9Format::R8G8B8: return &r8b8g8; case D3D9Format::R3G3B2: return &r3g3b2; case D3D9Format::A8R3G3B2: return &a8r3g3b2; case D3D9Format::A8P8: return &a8p8; case D3D9Format::P8: return &p8; // MULTI2_ARGB8 -> Don't have a clue what this is. case D3D9Format::CxV8U8: return &cxv8u8; // A1 -> Doesn't map nicely here cause it's not byte aligned. // Gonna just pretend that doesn't exist until something // depends on that. default: return &unknown; } } bool D3D9VkFormatTable::CheckImageFormatSupport( const Rc& Adapter, VkFormat Format, VkFormatFeatureFlags2 Features) const { DxvkFormatFeatures supported = Adapter->getFormatFeatures(Format); return (supported.linear & Features) == Features || (supported.optimal & Features) == Features; } }dxvk-2.6.1/src/d3d9/d3d9_format.h000066400000000000000000000174031477473124000163300ustar00rootroot00000000000000#pragma once #include #include "d3d9_options.h" #include "../dxvk/dxvk_adapter.h" #include "../dxvk/dxvk_format.h" #include namespace dxvk { enum class D3D9Format : uint32_t { Unknown = 0, R8G8B8 = 20, A8R8G8B8 = 21, X8R8G8B8 = 22, R5G6B5 = 23, X1R5G5B5 = 24, A1R5G5B5 = 25, A4R4G4B4 = 26, R3G3B2 = 27, A8 = 28, A8R3G3B2 = 29, X4R4G4B4 = 30, A2B10G10R10 = 31, A8B8G8R8 = 32, X8B8G8R8 = 33, G16R16 = 34, A2R10G10B10 = 35, A16B16G16R16 = 36, A8P8 = 40, P8 = 41, L8 = 50, A8L8 = 51, A4L4 = 52, V8U8 = 60, L6V5U5 = 61, X8L8V8U8 = 62, Q8W8V8U8 = 63, V16U16 = 64, W11V11U10 = 65, A2W10V10U10 = 67, UYVY = MAKEFOURCC('U', 'Y', 'V', 'Y'), R8G8_B8G8 = MAKEFOURCC('R', 'G', 'B', 'G'), YUY2 = MAKEFOURCC('Y', 'U', 'Y', '2'), G8R8_G8B8 = MAKEFOURCC('G', 'R', 'G', 'B'), DXT1 = MAKEFOURCC('D', 'X', 'T', '1'), DXT2 = MAKEFOURCC('D', 'X', 'T', '2'), DXT3 = MAKEFOURCC('D', 'X', 'T', '3'), DXT4 = MAKEFOURCC('D', 'X', 'T', '4'), DXT5 = MAKEFOURCC('D', 'X', 'T', '5'), D16_LOCKABLE = 70, D32 = 71, D15S1 = 73, D24S8 = 75, D24X8 = 77, D24X4S4 = 79, D16 = 80, D32F_LOCKABLE = 82, D24FS8 = 83, D32_LOCKABLE = 84, S8_LOCKABLE = 85, L16 = 81, VERTEXDATA = 100, INDEX16 = 101, INDEX32 = 102, Q16W16V16U16 = 110, MULTI2_ARGB8 = MAKEFOURCC('M', 'E', 'T', '1'), R16F = 111, G16R16F = 112, A16B16G16R16F = 113, R32F = 114, G32R32F = 115, A32B32G32R32F = 116, CxV8U8 = 117, A1 = 118, A2B10G10R10_XR_BIAS = 119, BINARYBUFFER = 199, // Driver Hacks / Unofficial Formats ATI1 = MAKEFOURCC('A', 'T', 'I', '1'), ATI2 = MAKEFOURCC('A', 'T', 'I', '2'), INST = MAKEFOURCC('I', 'N', 'S', 'T'), DF24 = MAKEFOURCC('D', 'F', '2', '4'), DF16 = MAKEFOURCC('D', 'F', '1', '6'), NULL_FORMAT = MAKEFOURCC('N', 'U', 'L', 'L'), GET4 = MAKEFOURCC('G', 'E', 'T', '4'), GET1 = MAKEFOURCC('G', 'E', 'T', '1'), NVDB = MAKEFOURCC('N', 'V', 'D', 'B'), A2M1 = MAKEFOURCC('A', '2', 'M', '1'), A2M0 = MAKEFOURCC('A', '2', 'M', '0'), ATOC = MAKEFOURCC('A', 'T', 'O', 'C'), INTZ = MAKEFOURCC('I', 'N', 'T', 'Z'), RAWZ = MAKEFOURCC('R', 'A', 'W', 'Z'), RESZ = MAKEFOURCC('R', 'E', 'S', 'Z'), NV11 = MAKEFOURCC('N', 'V', '1', '1'), NV12 = MAKEFOURCC('N', 'V', '1', '2'), P010 = MAKEFOURCC('P', '0', '1', '0'), // Same as NV12 but 10 bit P016 = MAKEFOURCC('P', '0', '1', '6'), // Same as NV12 but 16 bit Y210 = MAKEFOURCC('Y', '2', '1', '0'), Y216 = MAKEFOURCC('Y', '2', '1', '6'), Y410 = MAKEFOURCC('Y', '4', '1', '0'), AYUV = MAKEFOURCC('A', 'Y', 'U', 'V'), YV12 = MAKEFOURCC('Y', 'V', '1', '2'), OPAQUE_420 = MAKEFOURCC('4', '2', '0', 'O'), // Not supported but exist AI44 = MAKEFOURCC('A', 'I', '4', '4'), IA44 = MAKEFOURCC('I', 'A', '4', '4'), R2VB = MAKEFOURCC('R', '2', 'V', 'B'), COPM = MAKEFOURCC('C', 'O', 'P', 'M'), SSAA = MAKEFOURCC('S', 'S', 'A', 'A'), AL16 = MAKEFOURCC('A', 'L', '1', '6'), R16 = MAKEFOURCC(' ', 'R', '1', '6'), EXT1 = MAKEFOURCC('E', 'X', 'T', '1'), FXT1 = MAKEFOURCC('F', 'X', 'T', '1'), GXT1 = MAKEFOURCC('G', 'X', 'T', '1'), HXT1 = MAKEFOURCC('H', 'X', 'T', '1'), }; inline D3D9Format EnumerateFormat(D3DFORMAT format) { return static_cast(format); } std::ostream& operator << (std::ostream& os, D3D9Format format); enum D3D9ConversionFormat : uint32_t { D3D9ConversionFormat_None = 0, D3D9ConversionFormat_YUY2 = 1, D3D9ConversionFormat_UYVY, D3D9ConversionFormat_L6V5U5, D3D9ConversionFormat_X8L8V8U8, D3D9ConversionFormat_A2W10V10U10, D3D9ConversionFormat_W11V11U10, D3D9ConversionFormat_NV12, D3D9ConversionFormat_YV12, D3D9ConversionFormat_Count }; struct D3D9_CONVERSION_FORMAT_INFO { D3D9ConversionFormat FormatType = D3D9ConversionFormat_None; VkFormat FormatColor = VK_FORMAT_UNDEFINED; VkFormat FormatSrgb = VK_FORMAT_UNDEFINED; }; struct D3D9_FORMAT_BLOCK_SIZE { uint8_t Width = 0; uint8_t Height = 0; uint8_t Depth = 0; }; /** * \brief Format mapping * * Maps a D3D9 format to a set of Vulkan formats. */ struct D3D9_VK_FORMAT_MAPPING { union { struct { VkFormat FormatColor; ///< Corresponding color format VkFormat FormatSrgb; ///< Corresponding color format }; VkFormat Formats[2]; }; VkImageAspectFlags Aspect = 0; ///< Defined aspects for the color format VkComponentMapping Swizzle = { ///< Color component swizzle VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; D3D9_CONVERSION_FORMAT_INFO ConversionFormatInfo = { }; bool IsValid() const { return FormatColor != VK_FORMAT_UNDEFINED; } }; D3D9_VK_FORMAT_MAPPING ConvertFormatUnfixed(D3D9Format Format); D3D9_FORMAT_BLOCK_SIZE GetFormatAlignedBlockSize(D3D9Format Format); /** * \brief Format table * * Initializes a format table for a specific * device and provides methods to look up * formats. */ class D3D9VkFormatTable { public: D3D9VkFormatTable( const Rc& adapter, const D3D9Options& options); /** * \brief Retrieves info for a given D3D9 format * * \param [in] Format The D3D9 format to look up * \param [in] Mode the format lookup mode * \returns Format info */ D3D9_VK_FORMAT_MAPPING GetFormatMapping( D3D9Format Format) const; /** * \brief Retrieves format info for unsupported * formats. * * \param [in] Format The D3D9 format to look up */ const DxvkFormatInfo* GetUnsupportedFormatInfo( D3D9Format Format) const; private: bool CheckImageFormatSupport( const Rc& Adapter, VkFormat Format, VkFormatFeatureFlags2 Features) const; bool m_d24s8Support; bool m_d16s8Support; bool m_dfSupport; bool m_x4r4g4b4Support; bool m_d16lockableSupport; }; inline bool IsFourCCFormat(D3D9Format format) { // BINARYBUFFER is the largest non-fourcc format return format > D3D9Format::BINARYBUFFER; } inline bool IsVendorFormat(D3D9Format format) { return IsFourCCFormat(format) && format != D3D9Format::MULTI2_ARGB8 && format != D3D9Format::UYVY && format != D3D9Format::R8G8_B8G8 && format != D3D9Format::YUY2 && format != D3D9Format::G8R8_G8B8 && format != D3D9Format::DXT1 && format != D3D9Format::DXT2 && format != D3D9Format::DXT3 && format != D3D9Format::DXT4 && format != D3D9Format::DXT5; } inline bool IsDXTFormat(D3D9Format format) { return format == D3D9Format::DXT1 || format == D3D9Format::DXT2 || format == D3D9Format::DXT3 || format == D3D9Format::DXT4 || format == D3D9Format::DXT5; } // D3D9 documentation says: IDirect3DSurface9::GetDC is valid on the following formats only: // D3DFMT_R5G6B5, D3DFMT_X1R5G5B5, D3DFMT_R8G8B8, and D3DFMT_X8R8G8B8. However, // the equivalent formats of D3DFMT_A1R5G5B5 and D3DFMT_A8R8G8B8 are also supported. inline bool IsSurfaceGetDCCompatibleFormat(D3D9Format format) { return format == D3D9Format::R5G6B5 || format == D3D9Format::X1R5G5B5 || format == D3D9Format::A1R5G5B5 || format == D3D9Format::R8G8B8 || format == D3D9Format::X8R8G8B8 || format == D3D9Format::A8R8G8B8; } } dxvk-2.6.1/src/d3d9/d3d9_format_helpers.cpp000066400000000000000000000246221477473124000204060ustar00rootroot00000000000000#include "d3d9_format_helpers.h" #include #include #include #include #include #include #include namespace dxvk { D3D9FormatHelper::D3D9FormatHelper(const Rc& device) : m_device (device) , m_setLayout (CreateSetLayout()) , m_pipelineLayout (CreatePipelineLayout()) { InitPipelines(); } D3D9FormatHelper::~D3D9FormatHelper() { auto vk = m_device->vkd(); for (auto& p : m_pipelines) vk->vkDestroyPipeline(vk->device(), p, nullptr); vk->vkDestroyDescriptorSetLayout(vk->device(), m_setLayout, nullptr); vk->vkDestroyPipelineLayout(vk->device(), m_pipelineLayout, nullptr); } void D3D9FormatHelper::ConvertFormat( const DxvkContextObjects& ctx, D3D9_CONVERSION_FORMAT_INFO conversionFormat, const Rc& dstImage, VkImageSubresourceLayers dstSubresource, const DxvkBufferSlice& srcSlice) { switch (conversionFormat.FormatType) { case D3D9ConversionFormat_YUY2: case D3D9ConversionFormat_UYVY: { ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R32_UINT, { 2u, 1u }); break; } case D3D9ConversionFormat_NV12: ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R16_UINT, { 2u, 1u }); break; case D3D9ConversionFormat_YV12: ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R8_UINT, { 1u, 1u }); break; case D3D9ConversionFormat_L6V5U5: ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R16_UINT, { 1u, 1u }); break; case D3D9ConversionFormat_X8L8V8U8: ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R32_UINT, { 1u, 1u }); break; case D3D9ConversionFormat_A2W10V10U10: ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R32_UINT, { 1u, 1u }); break; case D3D9ConversionFormat_W11V11U10: ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R32_UINT, { 1u, 1u }); break; default: Logger::warn("Unimplemented format conversion"); } } void D3D9FormatHelper::ConvertGenericFormat( const DxvkContextObjects& ctx, D3D9_CONVERSION_FORMAT_INFO videoFormat, const Rc& dstImage, VkImageSubresourceLayers dstSubresource, const DxvkBufferSlice& srcSlice, VkFormat bufferFormat, VkExtent2D macroPixelRun) { DxvkImageViewKey imageViewInfo; imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; imageViewInfo.format = dstImage->info().format; imageViewInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT; imageViewInfo.aspects = dstSubresource.aspectMask; imageViewInfo.mipIndex = dstSubresource.mipLevel; imageViewInfo.mipCount = 1; imageViewInfo.layerIndex = dstSubresource.baseArrayLayer; imageViewInfo.layerCount = dstSubresource.layerCount; auto tmpImageView = dstImage->createView(imageViewInfo); VkExtent3D imageExtent = dstImage->mipLevelExtent(dstSubresource.mipLevel); imageExtent = VkExtent3D{ imageExtent.width / macroPixelRun.width, imageExtent.height / macroPixelRun.height, 1 }; DxvkBufferViewKey bufferViewInfo; bufferViewInfo.format = bufferFormat; bufferViewInfo.offset = srcSlice.offset(); bufferViewInfo.size = srcSlice.length(); bufferViewInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; auto tmpBufferView = srcSlice.buffer()->createView(bufferViewInfo); VkDescriptorSet set = ctx.descriptorPool->alloc(m_setLayout); VkDescriptorImageInfo imageDescriptor = { }; imageDescriptor.imageLayout = VK_IMAGE_LAYOUT_GENERAL; imageDescriptor.imageView = tmpImageView->handle(); VkBufferView bufferViewHandle = tmpBufferView->handle(); std::array descriptorWrites = {{ { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, set, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &imageDescriptor }, { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, set, 1, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, nullptr, nullptr, &bufferViewHandle }, }}; ctx.cmd->updateDescriptorSets( descriptorWrites.size(), descriptorWrites.data()); ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipelines[videoFormat.FormatType]); ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipelineLayout, set, 0, nullptr); ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(VkExtent2D), &imageExtent); ctx.cmd->cmdDispatch(DxvkCmdBuffer::ExecBuffer, ((imageExtent.width + 7u) / 8u), ((imageExtent.height + 7u) / 8u), 1u); // We can reasonably assume that the image is in GENERAL layout anyway VkMemoryBarrier2 memoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 }; memoryBarrier.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; memoryBarrier.srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT; memoryBarrier.dstStageMask = dstImage->info().stages | srcSlice.buffer()->info().stages; memoryBarrier.dstAccessMask = dstImage->info().access | srcSlice.buffer()->info().access; VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO }; depInfo.memoryBarrierCount = 1u; depInfo.pMemoryBarriers = &memoryBarrier; ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo); ctx.cmd->track(tmpImageView->image(), DxvkAccess::Write); ctx.cmd->track(tmpBufferView->buffer(), DxvkAccess::Read); } void D3D9FormatHelper::InitPipelines() { m_pipelines[D3D9ConversionFormat_YUY2] = CreatePipeline(sizeof(d3d9_convert_yuy2_uyvy), d3d9_convert_yuy2_uyvy, 0); m_pipelines[D3D9ConversionFormat_UYVY] = CreatePipeline(sizeof(d3d9_convert_yuy2_uyvy), d3d9_convert_yuy2_uyvy, 1); m_pipelines[D3D9ConversionFormat_L6V5U5] = CreatePipeline(sizeof(d3d9_convert_l6v5u5), d3d9_convert_l6v5u5, 0); m_pipelines[D3D9ConversionFormat_X8L8V8U8] = CreatePipeline(sizeof(d3d9_convert_x8l8v8u8), d3d9_convert_x8l8v8u8, 0); m_pipelines[D3D9ConversionFormat_A2W10V10U10] = CreatePipeline(sizeof(d3d9_convert_a2w10v10u10), d3d9_convert_a2w10v10u10, 0); m_pipelines[D3D9ConversionFormat_W11V11U10] = CreatePipeline(sizeof(d3d9_convert_w11v11u10), d3d9_convert_w11v11u10, 0); m_pipelines[D3D9ConversionFormat_NV12] = CreatePipeline(sizeof(d3d9_convert_nv12), d3d9_convert_nv12, 0); m_pipelines[D3D9ConversionFormat_YV12] = CreatePipeline(sizeof(d3d9_convert_yv12), d3d9_convert_yv12, 0); } VkDescriptorSetLayout D3D9FormatHelper::CreateSetLayout() { auto vk = m_device->vkd(); static const std::array bindings = {{ { 0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT }, { 1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT }, }}; VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; info.bindingCount = bindings.size(); info.pBindings = bindings.data(); VkDescriptorSetLayout layout = VK_NULL_HANDLE; VkResult vr = vk->vkCreateDescriptorSetLayout(vk->device(), &info, nullptr, &layout); if (vr != VK_SUCCESS) throw DxvkError(str::format("Failed to create format conversion descriptor set layout: ", vr)); return layout; } VkPipelineLayout D3D9FormatHelper::CreatePipelineLayout() { auto vk = m_device->vkd(); VkPushConstantRange pushConstants = { }; pushConstants.size = sizeof(VkExtent2D); pushConstants.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; info.setLayoutCount = 1u; info.pSetLayouts = &m_setLayout; info.pushConstantRangeCount = 1u; info.pPushConstantRanges = &pushConstants; VkPipelineLayout layout = VK_NULL_HANDLE; VkResult vr = vk->vkCreatePipelineLayout(vk->device(), &info, nullptr, &layout); if (vr != VK_SUCCESS) throw DxvkError(str::format("Failed to create format conversion pipeline layout: ", vr)); return layout; } VkPipeline D3D9FormatHelper::CreatePipeline(size_t size, const uint32_t* code, uint32_t specConstant) { auto vk = m_device->vkd(); VkSpecializationMapEntry specEntry = { }; specEntry.size = sizeof(specConstant); VkSpecializationInfo specInfo = { }; specInfo.mapEntryCount = 1; specInfo.pMapEntries = &specEntry; specInfo.dataSize = sizeof(specConstant); specInfo.pData = &specConstant; VkComputePipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; pipelineInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; pipelineInfo.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; pipelineInfo.stage.pName = "main"; pipelineInfo.stage.pSpecializationInfo = &specInfo; pipelineInfo.layout = m_pipelineLayout; pipelineInfo.basePipelineIndex = -1; VkShaderModuleCreateInfo moduleInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; moduleInfo.codeSize = size; moduleInfo.pCode = code; if (m_device->features().khrMaintenance5.maintenance5 || m_device->features().extGraphicsPipelineLibrary.graphicsPipelineLibrary) { pipelineInfo.stage.pNext = &moduleInfo; } else { VkResult vr = vk->vkCreateShaderModule(vk->device(), &moduleInfo, nullptr, &pipelineInfo.stage.module); if (vr != VK_SUCCESS) throw DxvkError(str::format("Failed to create format conversion shader module: ", vr)); } VkPipeline pipeline = VK_NULL_HANDLE; VkResult vr = vk->vkCreateComputePipelines(vk->device(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline); vk->vkDestroyShaderModule(vk->device(), pipelineInfo.stage.module, nullptr); if (vr != VK_SUCCESS) throw DxvkError(str::format("Failed to create format conversion pipeline: ", vr)); return pipeline; } }dxvk-2.6.1/src/d3d9/d3d9_format_helpers.h000066400000000000000000000030011477473124000200370ustar00rootroot00000000000000#pragma once #include "d3d9_include.h" #include "d3d9_format.h" #include "../dxvk/dxvk_device.h" #include "../dxvk/dxvk_context.h" namespace dxvk { class D3D9FormatHelper { public: D3D9FormatHelper(const Rc& device); ~D3D9FormatHelper(); void Flush(); void ConvertFormat( const DxvkContextObjects& ctx, D3D9_CONVERSION_FORMAT_INFO conversionFormat, const Rc& dstImage, VkImageSubresourceLayers dstSubresource, const DxvkBufferSlice& srcSlice); private: void ConvertGenericFormat( const DxvkContextObjects& ctx, D3D9_CONVERSION_FORMAT_INFO videoFormat, const Rc& dstImage, VkImageSubresourceLayers dstSubresource, const DxvkBufferSlice& srcSlice, VkFormat bufferFormat, VkExtent2D macroPixelRun); enum BindingIds : uint32_t { Image = 0, Buffer = 1, }; void InitPipelines(); VkDescriptorSetLayout CreateSetLayout(); VkPipelineLayout CreatePipelineLayout(); VkPipeline CreatePipeline(size_t size, const uint32_t* code, uint32_t specConstant); Rc m_device; VkDescriptorSetLayout m_setLayout = VK_NULL_HANDLE; VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; std::array m_pipelines = { }; }; }dxvk-2.6.1/src/d3d9/d3d9_hud.cpp000066400000000000000000000101601477473124000161440ustar00rootroot00000000000000#include "d3d9_hud.h" namespace dxvk::hud { HudSamplerCount::HudSamplerCount(D3D9DeviceEx* device) : m_device (device) , m_samplerCount ("0"){ } void HudSamplerCount::update(dxvk::high_resolution_clock::time_point time) { DxvkSamplerStats stats = m_device->GetDXVKDevice()->getSamplerStats(); m_samplerCount = str::format(stats.totalCount); } HudPos HudSamplerCount::render( const DxvkContextObjects& ctx, const HudPipelineKey& key, const HudOptions& options, HudRenderer& renderer, HudPos position) { position.y += 16; renderer.drawText(16, position, 0xffc0ff00u, "Samplers:"); renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_samplerCount); position.y += 8; return position; } HudTextureMemory::HudTextureMemory(D3D9DeviceEx* device) : m_device (device) , m_allocatedString ("") , m_mappedString ("") { } void HudTextureMemory::update(dxvk::high_resolution_clock::time_point time) { D3D9MemoryAllocator* allocator = m_device->GetAllocator(); m_maxAllocated = std::max(m_maxAllocated, allocator->AllocatedMemory()); m_maxUsed = std::max(m_maxUsed, allocator->UsedMemory()); m_maxMapped = std::max(m_maxMapped, allocator->MappedMemory()); auto elapsed = std::chrono::duration_cast(time - m_lastUpdate); if (elapsed.count() < UpdateInterval) return; m_allocatedString = str::format(m_maxAllocated >> 20, " MB (Used: ", m_maxUsed >> 20, " MB)"); m_mappedString = str::format(m_maxMapped >> 20, " MB"); m_maxAllocated = 0; m_maxUsed = 0; m_maxMapped = 0; m_lastUpdate = time; } HudPos HudTextureMemory::render( const DxvkContextObjects& ctx, const HudPipelineKey& key, const HudOptions& options, HudRenderer& renderer, HudPos position) { position.y += 16; renderer.drawText(16, position, 0xffc0ff00u, "Mappable:"); renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_allocatedString); position.y += 20; renderer.drawText(16, position, 0xffc0ff00u, "Mapped:"); renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_mappedString); position.y += 8; return position; } HudFixedFunctionShaders::HudFixedFunctionShaders(D3D9DeviceEx* device) : m_device (device) , m_ffShaderCount ("") {} void HudFixedFunctionShaders::update(dxvk::high_resolution_clock::time_point time) { m_ffShaderCount = str::format( "VS: ", m_device->GetFixedFunctionVSCount(), " FS: ", m_device->GetFixedFunctionFSCount(), " SWVP: ", m_device->GetSWVPShaderCount() ); } HudPos HudFixedFunctionShaders::render( const DxvkContextObjects& ctx, const HudPipelineKey& key, const HudOptions& options, HudRenderer& renderer, HudPos position) { position.y += 16; renderer.drawText(16, position, 0xffc0ff00u, "FF Shaders:"); renderer.drawText(16, { position.x + 155, position.y }, 0xffffffffu, m_ffShaderCount); position.y += 8; return position; } HudSWVPState::HudSWVPState(D3D9DeviceEx* device) : m_device (device) , m_isSWVPText ("") {} void HudSWVPState::update(dxvk::high_resolution_clock::time_point time) { if (m_device->IsSWVP()) { if (m_device->CanOnlySWVP()) { m_isSWVPText = "SWVP"; } else { m_isSWVPText = "SWVP (Mixed)"; } } else { if (m_device->CanSWVP()) { m_isSWVPText = "HWVP (Mixed)"; } else { m_isSWVPText = "HWVP"; } } } HudPos HudSWVPState::render( const DxvkContextObjects& ctx, const HudPipelineKey& key, const HudOptions& options, HudRenderer& renderer, HudPos position) { position.y += 16; renderer.drawText(16, position, 0xffc0ff00u, "Vertex Processing:"); renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, m_isSWVPText); position.y += 8; return position; } } dxvk-2.6.1/src/d3d9/d3d9_hud.h000066400000000000000000000047501477473124000156210ustar00rootroot00000000000000#pragma once #include "d3d9_device.h" #include "../dxvk/hud/dxvk_hud_item.h" namespace dxvk::hud { /** * \brief HUD item to display sampler count */ class HudSamplerCount : public HudItem { public: HudSamplerCount(D3D9DeviceEx* device); void update(dxvk::high_resolution_clock::time_point time); HudPos render( const DxvkContextObjects& ctx, const HudPipelineKey& key, const HudOptions& options, HudRenderer& renderer, HudPos position); private: D3D9DeviceEx* m_device; std::string m_samplerCount; }; /** * \brief HUD item to display unmappable memory */ class HudTextureMemory : public HudItem { constexpr static int64_t UpdateInterval = 500'000; public: HudTextureMemory(D3D9DeviceEx* device); void update(dxvk::high_resolution_clock::time_point time); HudPos render( const DxvkContextObjects& ctx, const HudPipelineKey& key, const HudOptions& options, HudRenderer& renderer, HudPos position); private: D3D9DeviceEx* m_device; uint32_t m_maxAllocated = 0; uint32_t m_maxUsed = 0; uint32_t m_maxMapped = 0; dxvk::high_resolution_clock::time_point m_lastUpdate = dxvk::high_resolution_clock::now(); std::string m_allocatedString; std::string m_mappedString; }; /** * \brief HUD item to display amount of generated fixed function shaders */ class HudFixedFunctionShaders : public HudItem { public: HudFixedFunctionShaders(D3D9DeviceEx* device); void update(dxvk::high_resolution_clock::time_point time); HudPos render( const DxvkContextObjects& ctx, const HudPipelineKey& key, const HudOptions& options, HudRenderer& renderer, HudPos position); private: D3D9DeviceEx* m_device; std::string m_ffShaderCount; }; /** * \brief HUD item to whether or not we're in SWVP mode */ class HudSWVPState : public HudItem { public: HudSWVPState(D3D9DeviceEx* device); void update(dxvk::high_resolution_clock::time_point time); HudPos render( const DxvkContextObjects& ctx, const HudPipelineKey& key, const HudOptions& options, HudRenderer& renderer, HudPos position); private: D3D9DeviceEx* m_device; std::string m_isSWVPText; }; }dxvk-2.6.1/src/d3d9/d3d9_include.h000066400000000000000000000057611477473124000164670ustar00rootroot00000000000000#pragma once #ifndef _MSC_VER #ifdef _WIN32_WINNT #undef _WIN32_WINNT #endif #define _WIN32_WINNT 0x0A00 #endif #include #include //for some reason we need to specify __declspec(dllexport) for MinGW #if defined(__WINE__) || !defined(_WIN32) #define DLLEXPORT __attribute__((visibility("default"))) #else #define DLLEXPORT #endif #include "../util/com/com_guid.h" #include "../util/com/com_object.h" #include "../util/com/com_pointer.h" #include "../util/log/log.h" #include "../util/log/log_debug.h" #include "../util/rc/util_rc.h" #include "../util/rc/util_rc_ptr.h" #include "../util/sync/sync_recursive.h" #include "../util/util_env.h" #include "../util/util_enum.h" #include "../util/util_error.h" #include "../util/util_flags.h" #include "../util/util_likely.h" #include "../util/util_math.h" #include "../util/util_string.h" // Missed definitions in Wine/MinGW. #ifndef D3DPRESENT_FORCEIMMEDIATE #define D3DPRESENT_FORCEIMMEDIATE 0x00000100L #endif // Missing in some versions of mingw headers #ifndef _MSC_VER namespace dxvk { typedef struct _D3DDEVINFO_RESOURCEMANAGER { char dummy; } D3DDEVINFO_RESOURCEMANAGER, * LPD3DDEVINFO_RESOURCEMANAGER; } #endif // This is the managed pool on D3D9Ex, it's just hidden! #define D3DPOOL_MANAGED_EX D3DPOOL(6) using D3D9VertexElements = std::vector; ///////////////////// // D3D9On12 content ///////////////////// #include #define MAX_D3D9ON12_QUEUES 2 struct D3D9ON12_ARGS { BOOL Enable9On12; IUnknown* pD3D12Device; IUnknown* ppD3D12Queues[MAX_D3D9ON12_QUEUES]; UINT NumQueues; UINT NodeMask; }; extern "C" { // Ordinal 20 typedef IDirect3D9* (WINAPI* PFN_Direct3DCreate9On12)(UINT sdk_version, D3D9ON12_ARGS* override_list, UINT override_entry_count); IDirect3D9* WINAPI Direct3DCreate9On12(UINT sdk_version, D3D9ON12_ARGS* override_list, UINT override_entry_count); // Ordinal 21 typedef HRESULT(WINAPI* PFN_Direct3DCreate9On12Ex)(UINT sdk_version, D3D9ON12_ARGS* override_list, UINT override_entry_count, IDirect3D9Ex** output); HRESULT WINAPI Direct3DCreate9On12Ex(UINT sdk_version, D3D9ON12_ARGS* override_list, UINT override_entry_count, IDirect3D9Ex** output); } MIDL_INTERFACE("e7fda234-b589-4049-940d-8878977531c8") IDirect3DDevice9On12 : public IUnknown { virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** object) = 0; virtual ULONG STDMETHODCALLTYPE AddRef() = 0; virtual ULONG STDMETHODCALLTYPE Release() = 0; virtual HRESULT STDMETHODCALLTYPE GetD3D12Device(REFIID riid, void** object) = 0; virtual HRESULT STDMETHODCALLTYPE UnwrapUnderlyingResource(IDirect3DResource9* resource, ID3D12CommandQueue* command_queue, REFIID riid, void** object) = 0; virtual HRESULT STDMETHODCALLTYPE ReturnUnderlyingResource(IDirect3DResource9* resource, UINT num_sync, UINT64* signal_values, ID3D12Fence** fences) = 0; }; #ifndef _MSC_VER __CRT_UUID_DECL(IDirect3DDevice9On12, 0xe7fda234,0xb589,0x4049,0x94,0x0d,0x88,0x78,0x97,0x75,0x31,0xc8); #endif dxvk-2.6.1/src/d3d9/d3d9_initializer.cpp000066400000000000000000000121771477473124000177210ustar00rootroot00000000000000#include #include "d3d9_initializer.h" #include "d3d9_device.h" namespace dxvk { D3D9Initializer::D3D9Initializer( D3D9DeviceEx* pParent) : m_parent(pParent), m_device(pParent->GetDXVKDevice()), m_csChunk(m_parent->AllocCsChunk()) { } D3D9Initializer::~D3D9Initializer() { } void D3D9Initializer::NotifyContextFlush() { std::lock_guard lock(m_mutex); m_transferCommands = 0; } void D3D9Initializer::InitBuffer( D3D9CommonBuffer* pBuffer) { VkMemoryPropertyFlags memFlags = pBuffer->GetBuffer()->memFlags(); (memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ? InitHostVisibleBuffer(pBuffer->GetBufferSlice()) : InitDeviceLocalBuffer(pBuffer->GetBufferSlice()); if (pBuffer->GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_BUFFER) InitHostVisibleBuffer(pBuffer->GetBufferSlice()); } void D3D9Initializer::InitTexture( D3D9CommonTexture* pTexture, void* pInitialData) { if (pTexture->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_NONE) return; void* mapPtr = nullptr; if (pTexture->Desc()->Pool != D3DPOOL_DEFAULT) { mapPtr = pTexture->GetData(0); if (mapPtr == nullptr) throw DxvkError("D3D9: InitTexture: map failed"); } if (pTexture->GetImage() != nullptr) InitDeviceLocalTexture(pTexture); if (mapPtr != nullptr) { InitHostVisibleTexture(pTexture, pInitialData, mapPtr); pTexture->UnmapData(); } SyncSharedTexture(pTexture); } void D3D9Initializer::InitDeviceLocalBuffer( DxvkBufferSlice Slice) { std::lock_guard lock(m_mutex); m_transferCommands += 1; EmitCs([ cBuffer = Slice.buffer() ] (DxvkContext* ctx) { ctx->initBuffer( cBuffer); }); ThrottleAllocationLocked(); } void D3D9Initializer::InitHostVisibleBuffer( DxvkBufferSlice Slice) { // If the buffer is mapped, we can write data directly // to the mapped memory region instead of doing it on // the GPU. Same goes for zero-initialization. std::memset( Slice.mapPtr(0), 0, Slice.length()); } void D3D9Initializer::InitDeviceLocalTexture( D3D9CommonTexture* pTexture) { std::lock_guard lock(m_mutex); Rc image = pTexture->GetImage(); EmitCs([ cImage = std::move(image) ] (DxvkContext* ctx) { ctx->initImage(cImage, VK_IMAGE_LAYOUT_UNDEFINED); }); ThrottleAllocationLocked(); } void D3D9Initializer::InitHostVisibleTexture( D3D9CommonTexture* pTexture, void* pInitialData, void* mapPtr) { // If the buffer is mapped, we can write data directly // to the mapped memory region instead of doing it on // the GPU. Same goes for zero-initialization. if (pInitialData) { // Initial data is only supported for textures with 1 subresource VkExtent3D mipExtent = pTexture->GetExtentMip(0); const DxvkFormatInfo* formatInfo = lookupFormatInfo(pTexture->GetFormatMapping().FormatColor); VkExtent3D blockCount = util::computeBlockCount(mipExtent, formatInfo->blockSize); uint32_t pitch = blockCount.width * formatInfo->elementSize; uint32_t alignedPitch = align(pitch, 4); util::packImageData( mapPtr, pInitialData, pitch, pitch * blockCount.height, alignedPitch, alignedPitch * blockCount.height, D3D9CommonTexture::GetImageTypeFromResourceType(pTexture->GetType()), mipExtent, pTexture->Desc()->ArraySize, formatInfo, VK_IMAGE_ASPECT_COLOR_BIT); } else { // All subresources are allocated in one chunk of memory. // So we can just get the pointer for subresource 0 and memset all of them at once. std::memset( mapPtr, 0, pTexture->GetTotalSize()); } } void D3D9Initializer::ThrottleAllocationLocked() { if (m_transferCommands > MaxTransferCommands) ExecuteFlushLocked(); } void D3D9Initializer::ExecuteFlush() { std::lock_guard lock(m_mutex); ExecuteFlushLocked(); } void D3D9Initializer::ExecuteFlushLocked() { EmitCs([] (DxvkContext* ctx) { ctx->flushCommandList(nullptr, nullptr); }); FlushCsChunk(); m_transferCommands = 0; } void D3D9Initializer::SyncSharedTexture(D3D9CommonTexture* pResource) { if (pResource->GetImage() == nullptr || pResource->GetImage()->info().sharing.mode == DxvkSharedHandleMode::None) return; // Ensure that initialization commands are submitted and waited on before // returning control to the application in order to avoid race conditions // in case the texture is used immediately on a secondary device. ExecuteFlush(); m_device->waitForResource(*pResource->GetImage(), DxvkAccess::Write); } void D3D9Initializer::FlushCsChunkLocked() { m_parent->InjectCsChunk(std::move(m_csChunk), false); m_csChunk = m_parent->AllocCsChunk(); } } dxvk-2.6.1/src/d3d9/d3d9_initializer.h000066400000000000000000000037451477473124000173670ustar00rootroot00000000000000#pragma once #include "d3d9_common_buffer.h" #include "d3d9_common_texture.h" namespace dxvk { class D3D9DeviceEx; /** * \brief Resource initialization context * * Manages a context which is used for resource * initialization. This includes * zero-initialization for buffers and images. */ class D3D9Initializer { constexpr static size_t MaxTransferMemory = 32 * 1024 * 1024; constexpr static size_t MaxTransferCommands = 512; public: D3D9Initializer( D3D9DeviceEx* pParent); ~D3D9Initializer(); void FlushCsChunk() { std::lock_guard lock(m_csMutex); if (!m_csChunk->empty()) FlushCsChunkLocked(); } void NotifyContextFlush(); void InitBuffer( D3D9CommonBuffer* pBuffer); void InitTexture( D3D9CommonTexture* pTexture, void* pInitialData = nullptr); private: dxvk::mutex m_mutex; D3D9DeviceEx* m_parent; Rc m_device; size_t m_transferCommands = 0; dxvk::mutex m_csMutex; DxvkCsChunkRef m_csChunk; void InitDeviceLocalBuffer( DxvkBufferSlice Slice); void InitHostVisibleBuffer( DxvkBufferSlice Slice); void InitDeviceLocalTexture( D3D9CommonTexture* pTexture); void InitHostVisibleTexture( D3D9CommonTexture* pTexture, void* pInitialData, void* mapPtr); void ThrottleAllocationLocked(); void ExecuteFlush(); void ExecuteFlushLocked(); void SyncSharedTexture( D3D9CommonTexture* pResource); void FlushCsChunkLocked(); void NotifyContextFlushLocked(); template void EmitCs(Cmd&& command) { std::lock_guard lock(m_csMutex); if (unlikely(!m_csChunk->push(command))) { FlushCsChunkLocked(); m_csChunk->push(command); } } }; } dxvk-2.6.1/src/d3d9/d3d9_interface.cpp000066400000000000000000000365501477473124000173370ustar00rootroot00000000000000#include "d3d9_interface.h" #include "d3d9_monitor.h" #include "d3d9_caps.h" #include "d3d9_device.h" #include "d3d9_bridge.h" #include "../util/util_singleton.h" #include namespace dxvk { Singleton g_dxvkInstance; D3D9InterfaceEx::D3D9InterfaceEx(bool bExtended) : m_instance ( g_dxvkInstance.acquire(DxvkInstanceFlag::ClientApiIsD3D9) ) , m_d3d8Bridge ( this ) , m_extended ( bExtended ) , m_d3d9Options ( nullptr, m_instance->config() ) , m_d3d9Interop ( this ) { // D3D9 doesn't enumerate adapters like physical adapters... // only as connected displays. // Let's create some "adapters" for the amount of displays we have. // We'll go through and match up displays -> our adapters in order. // If we run out of adapters, then we'll just make repeats of the first one. // We can't match up by names on Linux/Wine as they don't match at all // like on Windows, so this is our best option. #ifdef _WIN32 if (m_d3d9Options.enumerateByDisplays) { DISPLAY_DEVICEA device = { }; device.cb = sizeof(device); uint32_t adapterOrdinal = 0; uint32_t i = 0; while (::EnumDisplayDevicesA(nullptr, i++, &device, 0)) { // If we aren't attached, skip over. if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) continue; // If we are a mirror, skip over this device. if (device.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) continue; Rc adapter = adapterOrdinal >= m_instance->adapterCount() ? m_instance->enumAdapters(0) : m_instance->enumAdapters(adapterOrdinal); if (adapter != nullptr) m_adapters.emplace_back(this, adapter, adapterOrdinal++, i - 1); } } else #endif { const uint32_t adapterCount = m_instance->adapterCount(); m_adapters.reserve(adapterCount); for (uint32_t i = 0; i < adapterCount; i++) m_adapters.emplace_back(this, m_instance->enumAdapters(i), i, 0); } #ifdef _WIN32 if (m_d3d9Options.dpiAware) { Logger::info("Process set as DPI aware"); SetProcessDPIAware(); } #endif if (unlikely(m_d3d9Options.shaderModel == 0)) Logger::warn("D3D9InterfaceEx: WARNING! Fixed-function exclusive mode is enabled."); } D3D9InterfaceEx::~D3D9InterfaceEx() { g_dxvkInstance.release(); } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(IDirect3D9) || (m_extended && riid == __uuidof(IDirect3D9Ex))) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(IDxvkD3D8InterfaceBridge)) { *ppvObject = ref(&m_d3d8Bridge); return S_OK; } if (riid == __uuidof(ID3D9VkInteropInterface) || riid == __uuidof(ID3D9VkInteropInterface1)) { *ppvObject = ref(&m_d3d9Interop); return S_OK; } if (logQueryInterfaceError(__uuidof(IDirect3D9), riid)) { Logger::warn("D3D9InterfaceEx::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::RegisterSoftwareDevice(void* pInitializeFunction) { Logger::warn("D3D9InterfaceEx::RegisterSoftwareDevice: Stub"); return D3D_OK; } UINT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterCount() { return UINT(m_adapters.size()); } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterIdentifier( UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier) { if (auto* adapter = GetAdapter(Adapter)) return adapter->GetAdapterIdentifier(Flags, pIdentifier); return D3DERR_INVALIDCALL; } UINT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterModeCount(UINT Adapter, D3DFORMAT Format) { D3DDISPLAYMODEFILTER filter; filter.Size = sizeof(D3DDISPLAYMODEFILTER); filter.Format = Format; filter.ScanLineOrdering = D3DSCANLINEORDERING_PROGRESSIVE; return this->GetAdapterModeCountEx(Adapter, &filter); } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode) { if (auto* adapter = GetAdapter(Adapter)) { D3DDISPLAYMODEEX modeEx = { }; modeEx.Size = sizeof(D3DDISPLAYMODEEX); HRESULT hr = adapter->GetAdapterDisplayModeEx(&modeEx, nullptr); if (FAILED(hr)) return hr; pMode->Width = modeEx.Width; pMode->Height = modeEx.Height; pMode->RefreshRate = modeEx.RefreshRate; pMode->Format = modeEx.Format; return D3D_OK; } return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDeviceType( UINT Adapter, D3DDEVTYPE DevType, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, BOOL bWindowed) { if (auto* adapter = GetAdapter(Adapter)) return adapter->CheckDeviceType( DevType, EnumerateFormat(AdapterFormat), EnumerateFormat(BackBufferFormat), bWindowed); return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDeviceFormat( UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat) { if (auto* adapter = GetAdapter(Adapter)) return adapter->CheckDeviceFormat( DeviceType, EnumerateFormat(AdapterFormat), Usage, RType, EnumerateFormat(CheckFormat)); return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDeviceMultiSampleType( UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD* pQualityLevels) { if (auto* adapter = GetAdapter(Adapter)) return adapter->CheckDeviceMultiSampleType( DeviceType, EnumerateFormat(SurfaceFormat), Windowed, MultiSampleType, pQualityLevels); return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDepthStencilMatch( UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat) { if (auto* adapter = GetAdapter(Adapter)) return adapter->CheckDepthStencilMatch( DeviceType, EnumerateFormat(AdapterFormat), EnumerateFormat(RenderTargetFormat), EnumerateFormat(DepthStencilFormat)); return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDeviceFormatConversion( UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SourceFormat, D3DFORMAT TargetFormat) { if (auto* adapter = GetAdapter(Adapter)) return adapter->CheckDeviceFormatConversion( DeviceType, EnumerateFormat(SourceFormat), EnumerateFormat(TargetFormat)); return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetDeviceCaps( UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps) { if (auto* adapter = GetAdapter(Adapter)) return adapter->GetDeviceCaps( DeviceType, pCaps); return D3DERR_INVALIDCALL; } HMONITOR STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterMonitor(UINT Adapter) { if (auto* adapter = GetAdapter(Adapter)) return adapter->GetMonitor(); return nullptr; } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CreateDevice( UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface) { return this->CreateDeviceEx( Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, nullptr, // <-- pFullscreenDisplayMode reinterpret_cast(ppReturnedDeviceInterface)); } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::EnumAdapterModes( UINT Adapter, D3DFORMAT Format, UINT Mode, D3DDISPLAYMODE* pMode) { if (pMode == nullptr) return D3DERR_INVALIDCALL; D3DDISPLAYMODEFILTER filter; filter.Format = Format; filter.ScanLineOrdering = D3DSCANLINEORDERING_PROGRESSIVE; filter.Size = sizeof(D3DDISPLAYMODEFILTER); D3DDISPLAYMODEEX modeEx = { }; modeEx.Size = sizeof(D3DDISPLAYMODEEX); HRESULT hr = this->EnumAdapterModesEx(Adapter, &filter, Mode, &modeEx); if (FAILED(hr)) return hr; pMode->Width = modeEx.Width; pMode->Height = modeEx.Height; pMode->RefreshRate = modeEx.RefreshRate; pMode->Format = modeEx.Format; return D3D_OK; } // Ex Methods UINT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterModeCountEx(UINT Adapter, CONST D3DDISPLAYMODEFILTER* pFilter) { if (auto* adapter = GetAdapter(Adapter)) return adapter->GetAdapterModeCountEx(pFilter); return 0; } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::EnumAdapterModesEx( UINT Adapter, const D3DDISPLAYMODEFILTER* pFilter, UINT Mode, D3DDISPLAYMODEEX* pMode) { if (auto* adapter = GetAdapter(Adapter)) return adapter->EnumAdapterModesEx(pFilter, Mode, pMode); return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterDisplayModeEx( UINT Adapter, D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation) { if (auto* adapter = GetAdapter(Adapter)) return adapter->GetAdapterDisplayModeEx(pMode, pRotation); return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CreateDeviceEx( UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode, IDirect3DDevice9Ex** ppReturnedDeviceInterface) { InitReturnPtr(ppReturnedDeviceInterface); if (unlikely(ppReturnedDeviceInterface == nullptr || pPresentationParameters == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(DeviceType == D3DDEVTYPE_SW)) return D3DERR_INVALIDCALL; // D3DDEVTYPE_REF devices can be created with D3D8, but not // with D3D9, unless the Windows SDK 8.0 or later is installed. // Report it unavailable, as it would be on most end-user systems. if (unlikely(DeviceType == D3DDEVTYPE_REF && !m_isD3D8Compatible)) return D3DERR_NOTAVAILABLE; // Creating a device with D3DCREATE_PUREDEVICE only works in conjunction // with D3DCREATE_HARDWARE_VERTEXPROCESSING on native drivers. if (unlikely(BehaviorFlags & D3DCREATE_PUREDEVICE && !(BehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING))) return D3DERR_INVALIDCALL; HRESULT hr; // Black Desert creates a D3DDEVTYPE_NULLREF device and // expects it be created despite passing invalid parameters. if (likely(DeviceType != D3DDEVTYPE_NULLREF)) { hr = ValidatePresentationParameters(pPresentationParameters); if (unlikely(FAILED(hr))) return hr; } auto* adapter = GetAdapter(Adapter); if (adapter == nullptr) return D3DERR_INVALIDCALL; auto dxvkAdapter = adapter->GetDXVKAdapter(); try { auto dxvkDevice = dxvkAdapter->createDevice(m_instance, D3D9DeviceEx::GetDeviceFeatures(dxvkAdapter)); auto* device = new D3D9DeviceEx( this, adapter, DeviceType, hFocusWindow, BehaviorFlags, dxvkDevice); hr = device->InitialReset(pPresentationParameters, pFullscreenDisplayMode); if (unlikely(FAILED(hr))) return hr; *ppReturnedDeviceInterface = ref(device); } catch (const DxvkError& e) { Logger::err(e.message()); return D3DERR_NOTAVAILABLE; } return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterLUID(UINT Adapter, LUID* pLUID) { if (auto* adapter = GetAdapter(Adapter)) return adapter->GetAdapterLUID(pLUID); return D3DERR_INVALIDCALL; } HRESULT D3D9InterfaceEx::ValidatePresentationParameters(D3DPRESENT_PARAMETERS* pPresentationParameters) { if (m_extended) { // The swap effect value on a D3D9Ex device // can not be higher than D3DSWAPEFFECT_FLIPEX. if (unlikely(pPresentationParameters->SwapEffect > D3DSWAPEFFECT_FLIPEX)) return D3DERR_INVALIDCALL; // 30 is the highest supported back buffer count for Ex devices. if (unlikely(pPresentationParameters->BackBufferCount > D3DPRESENT_BACK_BUFFERS_MAX_EX)) return D3DERR_INVALIDCALL; } else { // The swap effect value on a non-Ex D3D9 device // can not be higher than D3DSWAPEFFECT_COPY. if (unlikely(pPresentationParameters->SwapEffect > D3DSWAPEFFECT_COPY)) return D3DERR_INVALIDCALL; // 3 is the highest supported back buffer count for non-Ex devices. if (unlikely(pPresentationParameters->BackBufferCount > D3DPRESENT_BACK_BUFFERS_MAX)) return D3DERR_INVALIDCALL; } // The swap effect value can not be 0. if (unlikely(!pPresentationParameters->SwapEffect)) return D3DERR_INVALIDCALL; // D3DSWAPEFFECT_COPY can not be used with more than one back buffer. // Allow D3DSWAPEFFECT_COPY to bypass this restriction in D3D8 compatibility // mode, since it may be a remapping of D3DSWAPEFFECT_COPY_VSYNC and RC Cars // depends on it not being validated. if (unlikely(!IsD3D8Compatible() && pPresentationParameters->SwapEffect == D3DSWAPEFFECT_COPY && pPresentationParameters->BackBufferCount > 1)) return D3DERR_INVALIDCALL; // Valid fullscreen presentation intervals must be known values. if (unlikely(!pPresentationParameters->Windowed && !(pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_DEFAULT || pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_ONE || pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_TWO || pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_THREE || pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_FOUR || pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE))) return D3DERR_INVALIDCALL; // In windowed mode, only a subset of the presentation interval flags can be used. if (unlikely(pPresentationParameters->Windowed && !(pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_DEFAULT || pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_ONE || pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE))) return D3DERR_INVALIDCALL; return D3D_OK; } }dxvk-2.6.1/src/d3d9/d3d9_interface.h000066400000000000000000000121101477473124000167660ustar00rootroot00000000000000#pragma once #include "d3d9_adapter.h" #include "d3d9_bridge.h" #include "d3d9_interop.h" #include "../dxvk/dxvk_instance.h" namespace dxvk { /** * \brief D3D9 interface implementation * * Implements the IDirect3DDevice9Ex interfaces * which provides the way to get adapters and create other objects such as \ref IDirect3DDevice9Ex. * similar to \ref DxgiFactory but for D3D9. */ class D3D9InterfaceEx final : public ComObjectClamp { public: D3D9InterfaceEx(bool bExtended); ~D3D9InterfaceEx(); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE RegisterSoftwareDevice(void* pInitializeFunction); UINT STDMETHODCALLTYPE GetAdapterCount(); HRESULT STDMETHODCALLTYPE GetAdapterIdentifier( UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier); UINT STDMETHODCALLTYPE GetAdapterModeCount(UINT Adapter, D3DFORMAT Format); HRESULT STDMETHODCALLTYPE GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode); HRESULT STDMETHODCALLTYPE CheckDeviceType( UINT Adapter, D3DDEVTYPE DevType, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, BOOL bWindowed); HRESULT STDMETHODCALLTYPE CheckDeviceFormat( UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat); HRESULT STDMETHODCALLTYPE CheckDeviceMultiSampleType( UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD* pQualityLevels); HRESULT STDMETHODCALLTYPE CheckDepthStencilMatch( UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat); HRESULT STDMETHODCALLTYPE CheckDeviceFormatConversion( UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SourceFormat, D3DFORMAT TargetFormat); HRESULT STDMETHODCALLTYPE GetDeviceCaps( UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps); HMONITOR STDMETHODCALLTYPE GetAdapterMonitor(UINT Adapter); HRESULT STDMETHODCALLTYPE CreateDevice( UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface); HRESULT STDMETHODCALLTYPE EnumAdapterModes( UINT Adapter, D3DFORMAT Format, UINT Mode, D3DDISPLAYMODE* pMode); // Ex Methods UINT STDMETHODCALLTYPE GetAdapterModeCountEx(UINT Adapter, CONST D3DDISPLAYMODEFILTER* pFilter); HRESULT STDMETHODCALLTYPE EnumAdapterModesEx( UINT Adapter, const D3DDISPLAYMODEFILTER* pFilter, UINT Mode, D3DDISPLAYMODEEX* pMode); HRESULT STDMETHODCALLTYPE GetAdapterDisplayModeEx( UINT Adapter, D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation); HRESULT STDMETHODCALLTYPE CreateDeviceEx( UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode, IDirect3DDevice9Ex** ppReturnedDeviceInterface); HRESULT STDMETHODCALLTYPE GetAdapterLUID(UINT Adapter, LUID* pLUID); HRESULT ValidatePresentationParameters(D3DPRESENT_PARAMETERS* pPresentationParameters); const D3D9Options& GetOptions() { return m_d3d9Options; } D3D9Adapter* GetAdapter(UINT Ordinal) { return Ordinal < m_adapters.size() ? &m_adapters[Ordinal] : nullptr; } bool IsExtended() { return m_extended; } bool IsD3D8Compatible() const { return m_isD3D8Compatible; } void EnableD3D8CompatibilityMode() { m_isD3D8Compatible = true; Logger::info("The D3D9 interface is now operating in D3D8 compatibility mode."); } Rc GetInstance() { return m_instance; } private: Rc m_instance; DxvkD3D8InterfaceBridge m_d3d8Bridge; bool m_extended; bool m_isD3D8Compatible = false; D3D9Options m_d3d9Options; std::vector m_adapters; D3D9VkInteropInterface m_d3d9Interop; }; }dxvk-2.6.1/src/d3d9/d3d9_interfaces.h000066400000000000000000000236571477473124000171730ustar00rootroot00000000000000#pragma once #include "d3d9_include.h" #include "../vulkan/vulkan_loader.h" /** * \brief D3D9 interface for Vulkan interop * * Provides access to the instance and physical device * handles for the given D3D9 interface and adapter ordinals. */ MIDL_INTERFACE("3461a81b-ce41-485b-b6b5-fcf08ba6a6bd") ID3D9VkInteropInterface : public IUnknown { /** * \brief Queries Vulkan handles used by DXVK * * \param [out] pInstance The Vulkan instance */ virtual void STDMETHODCALLTYPE GetInstanceHandle( VkInstance* pInstance) = 0; /** * \brief Queries Vulkan handles used by DXVK * * \param [in] Adapter Adapter ordinal * \param [out] pInstance The Vulkan instance */ virtual void STDMETHODCALLTYPE GetPhysicalDeviceHandle( UINT Adapter, VkPhysicalDevice* pPhysicalDevice) = 0; }; /** * \brief D3D9 interface for Vulkan interop - extended * * Provides access to the instance extension lists * and everything provided by ID3D9VkInteropInterface */ MIDL_INTERFACE("d6589ed4-7a37-4096-bac2-223b25ae31d2") ID3D9VkInteropInterface1 : public ID3D9VkInteropInterface { /** * \brief Gets a list of enabled instance extensions * * \param [out] pExtensionCount Number of extensions * \param [out] ppExtensions List of extension names * \returns D3DERR_MOREDATA if the list was truncated */ virtual HRESULT STDMETHODCALLTYPE GetInstanceExtensions( UINT* pExtensionCount, const char** ppExtensions) = 0; }; /** * \brief D3D9 texture interface for Vulkan interop * * Provides access to the backing image of a * D3D9 texture, surface, or volume. */ MIDL_INTERFACE("d56344f5-8d35-46fd-806d-94c351b472c1") ID3D9VkInteropTexture : public IUnknown { /** * \brief Retrieves Vulkan image info * * Retrieves both the image handle as well as the image's * properties. Any of the given pointers may be \c nullptr. * * If \c pInfo is not \c nullptr, the following rules apply: * - \c pInfo->sType \e must be \c VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO * - \c pInfo->pNext \e must be \c nullptr or point to a supported * extension-specific structure (currently none) * - \c pInfo->queueFamilyIndexCount must be the length of the * \c pInfo->pQueueFamilyIndices array, in \c uint32_t units. * - \c pInfo->pQueueFamilyIndices must point to a pre-allocated * array of \c uint32_t of size \c pInfo->pQueueFamilyIndices. * * \note As of now, the sharing mode will always be * \c VK_SHARING_MODE_EXCLUSIVE and no queue * family indices will be written to the array. * * After the call, the structure pointed to by \c pInfo can * be used to create an image with identical properties. * * If \c pLayout is not \c nullptr, it will receive the * layout that the image will be in after flushing any * outstanding commands on the device. * \param [out] pHandle The image handle * \param [out] pLayout Image layout * \param [out] pInfo Image properties * \returns \c S_OK on success, or \c D3DERR_INVALIDCALL */ virtual HRESULT STDMETHODCALLTYPE GetVulkanImageInfo( VkImage* pHandle, VkImageLayout* pLayout, VkImageCreateInfo* pInfo) = 0; }; /** * \brief D3D9 image description */ struct D3D9VkExtImageDesc { D3DRESOURCETYPE Type; // Can be SURFACE, TEXTURE, CUBETEXTURE, VOLUMETEXTURE UINT Width; UINT Height; UINT Depth; // Can be > 1 for VOLUMETEXTURE UINT MipLevels; // Can be > 1 for TEXTURE, CUBETEXTURE, VOLUMETEXTURE DWORD Usage; D3DFORMAT Format; D3DPOOL Pool; D3DMULTISAMPLE_TYPE MultiSample; // Must be NONE unless Type is SURFACE DWORD MultiSampleQuality; bool Discard; // Depth stencils only bool IsAttachmentOnly; // If false, then VK_IMAGE_USAGE_SAMPLED_BIT will be added bool IsLockable; VkImageUsageFlags ImageUsage; // Additional image usage flags }; /** * \brief D3D9 device interface for Vulkan interop * * Provides access to the device and instance handles * as well as the queue that is used for rendering. */ MIDL_INTERFACE("2eaa4b89-0107-4bdb-87f7-0f541c493ce0") ID3D9VkInteropDevice : public IUnknown { /** * \brief Queries Vulkan handles used by DXVK * * \param [out] pInstance The Vulkan instance * \param [out] pPhysDev The physical device * \param [out] pDevide The device handle */ virtual void STDMETHODCALLTYPE GetVulkanHandles( VkInstance* pInstance, VkPhysicalDevice* pPhysDev, VkDevice* pDevice) = 0; /** * \brief Queries the rendering queue used by DXVK * * \param [out] pQueue The Vulkan queue handle * \param [out] pQueueIndex Queue index * \param [out] pQueueFamilyIndex Queue family index */ virtual void STDMETHODCALLTYPE GetSubmissionQueue( VkQueue* pQueue, uint32_t* pQueueIndex, uint32_t* pQueueFamilyIndex) = 0; /** * \brief Transitions a Texture to a given layout * * Executes an explicit image layout transition on the * D3D device. Note that the image subresources \e must * be transitioned back to its original layout before * using it again from D3D9. * Synchronization is left up to the caller. * This function merely emits a call to transition the * texture on the DXVK internal command stream. * \param [in] pTexture The image to transform * \param [in] pSubresources Subresources to transform * \param [in] OldLayout Current image layout * \param [in] NewLayout Desired image layout */ virtual void STDMETHODCALLTYPE TransitionTextureLayout( ID3D9VkInteropTexture* pTexture, const VkImageSubresourceRange* pSubresources, VkImageLayout OldLayout, VkImageLayout NewLayout) = 0; /** * \brief Flushes outstanding D3D rendering commands * * Must be called before submitting Vulkan commands * to the rendering queue if those commands use the * backing resource of a D3D9 object. */ virtual void STDMETHODCALLTYPE FlushRenderingCommands() = 0; /** * \brief Locks submission queue * * Should be called immediately before submitting * Vulkan commands to the rendering queue in order * to prevent DXVK from using the queue. * * While the submission queue is locked, no D3D9 * methods must be called from the locking thread, * or otherwise a deadlock might occur. */ virtual void STDMETHODCALLTYPE LockSubmissionQueue() = 0; /** * \brief Releases submission queue * * Should be called immediately after submitting * Vulkan commands to the rendering queue in order * to allow DXVK to submit new commands. */ virtual void STDMETHODCALLTYPE ReleaseSubmissionQueue() = 0; /** * \brief Locks the device * * Can be called to ensure no D3D9 device methods * can be executed until UnlockDevice has been called. * * This will do nothing if the D3DCREATE_MULTITHREADED * is not set. */ virtual void STDMETHODCALLTYPE LockDevice() = 0; /** * \brief Unlocks the device * * Must only be called after a call to LockDevice. */ virtual void STDMETHODCALLTYPE UnlockDevice() = 0; /** * \brief Wait for a resource to finish being used * * Waits for the GPU resource associated with the * resource to finish being used by the GPU. * * Valid D3DLOCK flags: * - D3DLOCK_READONLY: Only waits for writes * - D3DLOCK_DONOTWAIT: Does not wait for the resource (may flush) * * \param [in] pResource Resource to be waited upon * \param [in] MapFlags D3DLOCK flags * \returns true if the resource is ready to use, * false if the resource is till in use */ virtual bool STDMETHODCALLTYPE WaitForResource( IDirect3DResource9* pResource, DWORD MapFlags) = 0; /** * \brief Creates a custom image/surface/texture * * \param [in] desc Image description * \param [out, retval] ppResult Pointer to a resource of the D3DRESOURCETYPE given by desc.Type * \returns D3D_OK, D3DERR_INVALIDCALL, or D3DERR_OUTOFVIDEOMEMORY */ virtual HRESULT STDMETHODCALLTYPE CreateImage( const D3D9VkExtImageDesc* desc, IDirect3DResource9** ppResult) = 0; }; /** * \brief D3D9 current output metadata */ struct D3D9VkExtOutputMetadata { float RedPrimary[2]; float GreenPrimary[2]; float BluePrimary[2]; float WhitePoint[2]; float MinLuminance; float MaxLuminance; float MaxFullFrameLuminance; }; /** * \brief D3D9 extended swapchain */ MIDL_INTERFACE("13776e93-4aa9-430a-a4ec-fe9e281181d5") ID3D9VkExtSwapchain : public IUnknown { virtual BOOL STDMETHODCALLTYPE CheckColorSpaceSupport( VkColorSpaceKHR ColorSpace) = 0; virtual HRESULT STDMETHODCALLTYPE SetColorSpace( VkColorSpaceKHR ColorSpace) = 0; virtual HRESULT STDMETHODCALLTYPE SetHDRMetaData( const VkHdrMetadataEXT *pHDRMetadata) = 0; virtual HRESULT STDMETHODCALLTYPE GetCurrentOutputDesc( D3D9VkExtOutputMetadata *pOutputDesc) = 0; virtual void STDMETHODCALLTYPE UnlockAdditionalFormats() = 0; }; #ifndef _MSC_VER __CRT_UUID_DECL(ID3D9VkInteropInterface, 0x3461a81b,0xce41,0x485b,0xb6,0xb5,0xfc,0xf0,0x8b,0xa6,0xa6,0xbd); __CRT_UUID_DECL(ID3D9VkInteropInterface1, 0xd6589ed4,0x7a37,0x4096,0xba,0xc2,0x22,0x3b,0x25,0xae,0x31,0xd2); __CRT_UUID_DECL(ID3D9VkInteropTexture, 0xd56344f5,0x8d35,0x46fd,0x80,0x6d,0x94,0xc3,0x51,0xb4,0x72,0xc1); __CRT_UUID_DECL(ID3D9VkInteropDevice, 0x2eaa4b89,0x0107,0x4bdb,0x87,0xf7,0x0f,0x54,0x1c,0x49,0x3c,0xe0); __CRT_UUID_DECL(ID3D9VkExtSwapchain, 0x13776e93,0x4aa9,0x430a,0xa4,0xec,0xfe,0x9e,0x28,0x11,0x81,0xd5); #endif dxvk-2.6.1/src/d3d9/d3d9_interop.cpp000066400000000000000000000274441477473124000170610ustar00rootroot00000000000000#include "d3d9_interop.h" #include "d3d9_interface.h" #include "d3d9_common_texture.h" #include "d3d9_device.h" #include "d3d9_texture.h" #include "d3d9_buffer.h" #include "d3d9_initializer.h" namespace dxvk { //////////////////////////////// // Interface Interop /////////////////////////////// D3D9VkInteropInterface::D3D9VkInteropInterface( D3D9InterfaceEx* pInterface) : m_interface(pInterface) { } D3D9VkInteropInterface::~D3D9VkInteropInterface() { } ULONG STDMETHODCALLTYPE D3D9VkInteropInterface::AddRef() { return m_interface->AddRef(); } ULONG STDMETHODCALLTYPE D3D9VkInteropInterface::Release() { return m_interface->Release(); } HRESULT STDMETHODCALLTYPE D3D9VkInteropInterface::QueryInterface( REFIID riid, void** ppvObject) { return m_interface->QueryInterface(riid, ppvObject); } void STDMETHODCALLTYPE D3D9VkInteropInterface::GetInstanceHandle( VkInstance* pInstance) { if (pInstance != nullptr) *pInstance = m_interface->GetInstance()->handle(); } void STDMETHODCALLTYPE D3D9VkInteropInterface::GetPhysicalDeviceHandle( UINT Adapter, VkPhysicalDevice* pPhysicalDevice) { if (pPhysicalDevice != nullptr) { D3D9Adapter* adapter = m_interface->GetAdapter(Adapter); *pPhysicalDevice = adapter ? adapter->GetDXVKAdapter()->handle() : nullptr; } } HRESULT STDMETHODCALLTYPE D3D9VkInteropInterface::GetInstanceExtensions( UINT* pExtensionCount, const char** ppExtensions) { if (pExtensionCount == nullptr) return D3DERR_INVALIDCALL; const DxvkNameList& extensions = m_interface->GetInstance()->extensionNameList(); if (ppExtensions == nullptr) { *pExtensionCount = extensions.count(); return D3D_OK; } // Write UINT count = 0; UINT maxCount = *pExtensionCount; for (uint32_t i = 0; i < extensions.count() && i < maxCount; i++) { ppExtensions[i] = extensions.name(i); count++; } *pExtensionCount = count; return (count < maxCount) ? D3DERR_MOREDATA : D3D_OK; } //////////////////////////////// // Texture Interop /////////////////////////////// D3D9VkInteropTexture::D3D9VkInteropTexture( IUnknown* pInterface, D3D9CommonTexture* pTexture) : m_interface(pInterface) , m_texture (pTexture) { } D3D9VkInteropTexture::~D3D9VkInteropTexture() { } ULONG STDMETHODCALLTYPE D3D9VkInteropTexture::AddRef() { return m_interface->AddRef(); } ULONG STDMETHODCALLTYPE D3D9VkInteropTexture::Release() { return m_interface->Release(); } HRESULT STDMETHODCALLTYPE D3D9VkInteropTexture::QueryInterface( REFIID riid, void** ppvObject) { return m_interface->QueryInterface(riid, ppvObject); } HRESULT STDMETHODCALLTYPE D3D9VkInteropTexture::GetVulkanImageInfo( VkImage* pHandle, VkImageLayout* pLayout, VkImageCreateInfo* pInfo) { const Rc image = m_texture->GetImage(); const DxvkImageCreateInfo& info = image->info(); if (pHandle != nullptr) *pHandle = image->handle(); if (pLayout != nullptr) *pLayout = info.layout; if (pInfo != nullptr) { // We currently don't support any extended structures if (pInfo->sType != VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO || pInfo->pNext != nullptr) return D3DERR_INVALIDCALL; pInfo->flags = 0; pInfo->imageType = info.type; pInfo->format = info.format; pInfo->extent = info.extent; pInfo->mipLevels = info.mipLevels; pInfo->arrayLayers = info.numLayers; pInfo->samples = info.sampleCount; pInfo->tiling = info.tiling; pInfo->usage = info.usage; pInfo->sharingMode = VK_SHARING_MODE_EXCLUSIVE; pInfo->queueFamilyIndexCount = 0; pInfo->initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; } return S_OK; } //////////////////////////////// // Device Interop /////////////////////////////// D3D9VkInteropDevice::D3D9VkInteropDevice( D3D9DeviceEx* pInterface) : m_device(pInterface) { } D3D9VkInteropDevice::~D3D9VkInteropDevice() { } ULONG STDMETHODCALLTYPE D3D9VkInteropDevice::AddRef() { return m_device->AddRef(); } ULONG STDMETHODCALLTYPE D3D9VkInteropDevice::Release() { return m_device->Release(); } HRESULT STDMETHODCALLTYPE D3D9VkInteropDevice::QueryInterface( REFIID riid, void** ppvObject) { return m_device->QueryInterface(riid, ppvObject); } void STDMETHODCALLTYPE D3D9VkInteropDevice::GetVulkanHandles( VkInstance* pInstance, VkPhysicalDevice* pPhysDev, VkDevice* pDevice) { auto device = m_device->GetDXVKDevice(); auto adapter = device->adapter(); auto instance = device->instance(); if (pDevice != nullptr) *pDevice = device->handle(); if (pPhysDev != nullptr) *pPhysDev = adapter->handle(); if (pInstance != nullptr) *pInstance = instance->handle(); } void STDMETHODCALLTYPE D3D9VkInteropDevice::GetSubmissionQueue( VkQueue* pQueue, uint32_t* pQueueIndex, uint32_t* pQueueFamilyIndex) { auto device = m_device->GetDXVKDevice(); DxvkDeviceQueue queue = device->queues().graphics; if (pQueue != nullptr) *pQueue = queue.queueHandle; if (pQueueIndex != nullptr) *pQueueIndex = queue.queueIndex; if (pQueueFamilyIndex != nullptr) *pQueueFamilyIndex = queue.queueFamily; } void STDMETHODCALLTYPE D3D9VkInteropDevice::TransitionTextureLayout( ID3D9VkInteropTexture* pTexture, const VkImageSubresourceRange* pSubresources, VkImageLayout OldLayout, VkImageLayout NewLayout) { auto texture = static_cast(pTexture)->GetCommonTexture(); m_device->EmitCs([ cImage = texture->GetImage(), cSubresources = *pSubresources, cOldLayout = OldLayout, cNewLayout = NewLayout ] (DxvkContext* ctx) { ctx->transformImage( cImage, cSubresources, cOldLayout, cNewLayout); }); } void STDMETHODCALLTYPE D3D9VkInteropDevice::FlushRenderingCommands() { m_device->Flush(); m_device->SynchronizeCsThread(DxvkCsThread::SynchronizeAll); } void STDMETHODCALLTYPE D3D9VkInteropDevice::LockSubmissionQueue() { m_device->GetDXVKDevice()->lockSubmission(); } void STDMETHODCALLTYPE D3D9VkInteropDevice::ReleaseSubmissionQueue() { m_device->GetDXVKDevice()->unlockSubmission(); } void STDMETHODCALLTYPE D3D9VkInteropDevice::LockDevice() { m_lock = m_device->LockDevice(); } void STDMETHODCALLTYPE D3D9VkInteropDevice::UnlockDevice() { m_lock = D3D9DeviceLock(); } static Rc GetDxvkResource(IDirect3DResource9 *pResource) { switch (pResource->GetType()) { case D3DRTYPE_SURFACE: return static_cast (pResource)->GetCommonTexture()->GetImage(); // Does not inherit from IDirect3DResource9... lol. //case D3DRTYPE_VOLUME: return static_cast (pResource)->GetCommonTexture()->GetImage(); case D3DRTYPE_TEXTURE: return static_cast (pResource)->GetCommonTexture()->GetImage(); case D3DRTYPE_VOLUMETEXTURE: return static_cast (pResource)->GetCommonTexture()->GetImage(); case D3DRTYPE_CUBETEXTURE: return static_cast (pResource)->GetCommonTexture()->GetImage(); case D3DRTYPE_VERTEXBUFFER: return static_cast(pResource)->GetCommonBuffer()->GetBuffer(); case D3DRTYPE_INDEXBUFFER: return static_cast (pResource)->GetCommonBuffer()->GetBuffer(); default: return nullptr; } } bool STDMETHODCALLTYPE D3D9VkInteropDevice::WaitForResource( IDirect3DResource9* pResource, DWORD MapFlags) { return m_device->WaitForResource(*GetDxvkResource(pResource), DxvkCsThread::SynchronizeAll, MapFlags); } HRESULT STDMETHODCALLTYPE D3D9VkInteropDevice::CreateImage( const D3D9VkExtImageDesc* params, IDirect3DResource9** ppResult) { InitReturnPtr(ppResult); if (unlikely(ppResult == nullptr)) return D3DERR_INVALIDCALL; if (unlikely(params == nullptr)) return D3DERR_INVALIDCALL; ///////////////////////////// // Image desc validation // Cannot create a volume by itself, use D3DRTYPE_VOLUMETEXTURE if (unlikely(params->Type == D3DRTYPE_VOLUME)) return D3DERR_INVALIDCALL; // Only allowed: SURFACE, TEXTURE, CUBETEXTURE, VOLUMETEXTURE if (unlikely(params->Type < D3DRTYPE_SURFACE || params->Type > D3DRTYPE_CUBETEXTURE)) return D3DERR_INVALIDCALL; // Only volume textures can have depth > 1 if (unlikely(params->Type != D3DRTYPE_VOLUMETEXTURE && params->Depth > 1)) return D3DERR_INVALIDCALL; if (params->Type == D3DRTYPE_SURFACE) { // Surfaces can only have 1 mip level if (unlikely(params->MipLevels > 1)) return D3DERR_INVALIDCALL; if (unlikely(params->MultiSample > D3DMULTISAMPLE_16_SAMPLES)) return D3DERR_INVALIDCALL; } else { // Textures can't be multisampled if (unlikely(params->MultiSample != D3DMULTISAMPLE_NONE)) return D3DERR_INVALIDCALL; } D3D9_COMMON_TEXTURE_DESC desc; desc.Width = params->Width; desc.Height = params->Height; desc.Depth = params->Depth; desc.ArraySize = params->Type == D3DRTYPE_CUBETEXTURE ? 6 : 1; desc.MipLevels = params->MipLevels; desc.Usage = params->Usage; desc.Format = EnumerateFormat(params->Format); desc.Pool = params->Pool; desc.Discard = params->Discard; desc.MultiSample = params->MultiSample; desc.MultisampleQuality = params->MultiSampleQuality; desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = params->IsAttachmentOnly; desc.IsLockable = params->IsLockable; desc.ImageUsage = params->ImageUsage; D3DRESOURCETYPE textureType = params->Type == D3DRTYPE_SURFACE ? D3DRTYPE_TEXTURE : params->Type; if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(m_device, textureType, &desc))) return D3DERR_INVALIDCALL; switch (params->Type) { case D3DRTYPE_SURFACE: return CreateTextureResource(desc, ppResult); case D3DRTYPE_TEXTURE: return CreateTextureResource(desc, ppResult); case D3DRTYPE_VOLUMETEXTURE: return CreateTextureResource(desc, ppResult); case D3DRTYPE_CUBETEXTURE: return CreateTextureResource(desc, ppResult); default: return D3DERR_INVALIDCALL; } } template HRESULT D3D9VkInteropDevice::CreateTextureResource( const D3D9_COMMON_TEXTURE_DESC& desc, IDirect3DResource9** ppResult) { try { const Com texture = new ResourceType(m_device, &desc, m_device->IsExtended()); m_device->m_initializer->InitTexture(texture->GetCommonTexture()); *ppResult = texture.ref(); if (desc.Pool == D3DPOOL_DEFAULT) m_device->m_losableResourceCounter++; return D3D_OK; } catch (const DxvkError& e) { Logger::err(e.message()); return D3DERR_OUTOFVIDEOMEMORY; } } } dxvk-2.6.1/src/d3d9/d3d9_interop.h000066400000000000000000000070711477473124000165200ustar00rootroot00000000000000#pragma once #include "d3d9_interfaces.h" #include "d3d9_multithread.h" namespace dxvk { class D3D9InterfaceEx; class D3D9CommonTexture; class D3D9DeviceEx; struct D3D9_COMMON_TEXTURE_DESC; class D3D9VkInteropInterface final : public ID3D9VkInteropInterface1 { public: D3D9VkInteropInterface( D3D9InterfaceEx* pInterface); ~D3D9VkInteropInterface(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); void STDMETHODCALLTYPE GetInstanceHandle( VkInstance* pInstance); void STDMETHODCALLTYPE GetPhysicalDeviceHandle( UINT Adapter, VkPhysicalDevice* pPhysicalDevice); HRESULT STDMETHODCALLTYPE GetInstanceExtensions( UINT* pExtensionCount, const char** ppExtensions); private: D3D9InterfaceEx* m_interface; }; class D3D9VkInteropTexture final : public ID3D9VkInteropTexture { public: D3D9VkInteropTexture( IUnknown* pInterface, D3D9CommonTexture* pTexture); ~D3D9VkInteropTexture(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE GetVulkanImageInfo( VkImage* pHandle, VkImageLayout* pLayout, VkImageCreateInfo* pInfo); D3D9CommonTexture* GetCommonTexture() { return m_texture; } private: IUnknown* m_interface; D3D9CommonTexture* m_texture; }; class D3D9VkInteropDevice final : public ID3D9VkInteropDevice { public: D3D9VkInteropDevice( D3D9DeviceEx* pInterface); ~D3D9VkInteropDevice(); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject); void STDMETHODCALLTYPE GetVulkanHandles( VkInstance* pInstance, VkPhysicalDevice* pPhysDev, VkDevice* pDevice); void STDMETHODCALLTYPE GetSubmissionQueue( VkQueue* pQueue, uint32_t* pQueueIndex, uint32_t* pQueueFamilyIndex); void STDMETHODCALLTYPE TransitionTextureLayout( ID3D9VkInteropTexture* pTexture, const VkImageSubresourceRange* pSubresources, VkImageLayout OldLayout, VkImageLayout NewLayout); void STDMETHODCALLTYPE FlushRenderingCommands(); void STDMETHODCALLTYPE LockSubmissionQueue(); void STDMETHODCALLTYPE ReleaseSubmissionQueue(); void STDMETHODCALLTYPE LockDevice(); void STDMETHODCALLTYPE UnlockDevice(); bool STDMETHODCALLTYPE WaitForResource( IDirect3DResource9* pResource, DWORD MapFlags); HRESULT STDMETHODCALLTYPE CreateImage( const D3D9VkExtImageDesc* desc, IDirect3DResource9** ppResult); private: template HRESULT CreateTextureResource( const D3D9_COMMON_TEXTURE_DESC& desc, IDirect3DResource9** ppResult); D3D9DeviceEx* m_device; D3D9DeviceLock m_lock; }; } dxvk-2.6.1/src/d3d9/d3d9_main.cpp000066400000000000000000000067401477473124000163210ustar00rootroot00000000000000#include "../dxvk/dxvk_instance.h" #include "d3d9_interface.h" #include "d3d9_shader_validator.h" #include "d3d9_annotation.h" class D3DFE_PROCESSVERTICES; using PSGPERRORID = UINT; namespace dxvk { Logger Logger::s_instance("d3d9.log"); D3D9GlobalAnnotationList D3D9GlobalAnnotationList::s_instance; HRESULT CreateD3D9( bool Extended, IDirect3D9Ex** ppDirect3D9Ex) { if (!ppDirect3D9Ex) return D3DERR_INVALIDCALL; *ppDirect3D9Ex = ref(new D3D9InterfaceEx( Extended )); return D3D_OK; } } extern "C" { DLLEXPORT IDirect3D9* __stdcall Direct3DCreate9(UINT nSDKVersion) { IDirect3D9Ex* pDirect3D = nullptr; dxvk::CreateD3D9(false, &pDirect3D); return pDirect3D; } DLLEXPORT HRESULT __stdcall Direct3DCreate9Ex(UINT nSDKVersion, IDirect3D9Ex** ppDirect3D9Ex) { return dxvk::CreateD3D9(true, ppDirect3D9Ex); } DLLEXPORT int __stdcall D3DPERF_BeginEvent(D3DCOLOR col, LPCWSTR wszName) { return dxvk::D3D9GlobalAnnotationList::Instance().BeginEvent(col, wszName); } DLLEXPORT int __stdcall D3DPERF_EndEvent(void) { return dxvk::D3D9GlobalAnnotationList::Instance().EndEvent(); } DLLEXPORT void __stdcall D3DPERF_SetMarker(D3DCOLOR col, LPCWSTR wszName) { dxvk::D3D9GlobalAnnotationList::Instance().SetMarker(col, wszName); } DLLEXPORT void __stdcall D3DPERF_SetRegion(D3DCOLOR col, LPCWSTR wszName) { dxvk::D3D9GlobalAnnotationList::Instance().SetRegion(col, wszName); } DLLEXPORT BOOL __stdcall D3DPERF_QueryRepeatFrame(void) { return dxvk::D3D9GlobalAnnotationList::Instance().QueryRepeatFrame(); } DLLEXPORT void __stdcall D3DPERF_SetOptions(DWORD dwOptions) { dxvk::D3D9GlobalAnnotationList::Instance().SetOptions(dwOptions); } DLLEXPORT DWORD __stdcall D3DPERF_GetStatus(void) { return dxvk::D3D9GlobalAnnotationList::Instance().GetStatus(); } DLLEXPORT void __stdcall DebugSetMute(void) { } DLLEXPORT int __stdcall DebugSetLevel(void) { return 0; } // Processor Specific Geometry Pipeline // for P3 SIMD/AMD 3DNow. DLLEXPORT void __stdcall PSGPError(D3DFE_PROCESSVERTICES* a, PSGPERRORID b, UINT c) { } DLLEXPORT void __stdcall PSGPSampleTexture(D3DFE_PROCESSVERTICES* a, UINT b, float(*const c)[4], UINT d, float(*const e)[4]) { } DLLEXPORT dxvk::D3D9ShaderValidator* __stdcall Direct3DShaderValidatorCreate9(void) { return ref(new dxvk::D3D9ShaderValidator()); } DLLEXPORT int __stdcall Direct3D9EnableMaximizedWindowedModeShim(UINT a) { return 0; } DLLEXPORT void __stdcall DXVK_RegisterAnnotation(IDXVKUserDefinedAnnotation* annotation) { dxvk::D3D9GlobalAnnotationList::Instance().RegisterAnnotator(annotation); } DLLEXPORT void __stdcall DXVK_UnRegisterAnnotation(IDXVKUserDefinedAnnotation* annotation) { dxvk::D3D9GlobalAnnotationList::Instance().UnregisterAnnotator(annotation); } DLLEXPORT void __stdcall Direct3D9ForceHybridEnumeration(UINT uHybrid) { } DLLEXPORT IDirect3D9* __stdcall Direct3DCreate9On12(UINT sdk_version, D3D9ON12_ARGS* override_list, UINT override_entry_count) { dxvk::Logger::warn("Direct3DCreate9On12: 9On12 functionality is unimplemented."); return Direct3DCreate9(sdk_version); } DLLEXPORT HRESULT __stdcall Direct3DCreate9On12Ex(UINT sdk_version, D3D9ON12_ARGS* override_list, UINT override_entry_count, IDirect3D9Ex** output) { dxvk::Logger::warn("Direct3DCreate9On12Ex: 9On12 functionality is unimplemented."); return Direct3DCreate9Ex(sdk_version, output); } } dxvk-2.6.1/src/d3d9/d3d9_mem.cpp000066400000000000000000000253261477473124000161540ustar00rootroot00000000000000#include "d3d9_mem.h" #include "../util/util_string.h" #include "../util/util_math.h" #include "../util/log/log.h" #include "../util/util_likely.h" #include #include #ifdef D3D9_ALLOW_UNMAPPING #include #else #include #endif namespace dxvk { #ifdef D3D9_ALLOW_UNMAPPING D3D9MemoryAllocator::D3D9MemoryAllocator() { SYSTEM_INFO sysInfo; GetSystemInfo(&sysInfo); m_allocationGranularity = sysInfo.dwAllocationGranularity; m_mappingGranularity = m_allocationGranularity * 16; } D3D9Memory D3D9MemoryAllocator::Alloc(uint32_t Size) { std::lock_guard lock(m_mutex); uint32_t alignedSize = align(Size, CACHE_LINE_SIZE); for (auto& chunk : m_chunks) { D3D9Memory memory = chunk->AllocLocked(alignedSize); if (memory) { m_usedMemory += memory.GetSize(); return memory; } } uint32_t chunkSize = std::max(D3D9ChunkSize, alignedSize); m_allocatedMemory += chunkSize; D3D9MemoryChunk* chunk = new D3D9MemoryChunk(this, chunkSize); std::unique_ptr uniqueChunk(chunk); D3D9Memory memory = uniqueChunk->AllocLocked(alignedSize); m_usedMemory += memory.GetSize(); m_chunks.push_back(std::move(uniqueChunk)); return memory; } void D3D9MemoryAllocator::Free(D3D9Memory *Memory) { std::lock_guard lock(m_mutex); D3D9MemoryChunk* chunk = Memory->GetChunk(); chunk->FreeLocked(Memory); m_usedMemory -= Memory->GetSize(); if (chunk->IsEmpty()) FreeChunk(chunk); } void D3D9MemoryAllocator::FreeChunk(D3D9MemoryChunk *Chunk) { // Has to be called in the lock m_allocatedMemory -= Chunk->Size(); m_chunks.erase(std::remove_if(m_chunks.begin(), m_chunks.end(), [&](auto& item) { return item.get() == Chunk; }), m_chunks.end()); } void* D3D9MemoryAllocator::Map(D3D9Memory* Memory) { std::lock_guard lock(m_mutex); D3D9MemoryChunk* chunk = Memory->GetChunk(); uint32_t memoryMapped; void* ptr = chunk->MapLocked(Memory, memoryMapped); m_mappedMemory += memoryMapped; return ptr; } void D3D9MemoryAllocator::Unmap(D3D9Memory* Memory) { std::lock_guard lock(m_mutex); D3D9MemoryChunk* chunk = Memory->GetChunk(); m_mappedMemory -= chunk->UnmapLocked(Memory); } uint32_t D3D9MemoryAllocator::MappedMemory() const { return m_mappedMemory.load(); } uint32_t D3D9MemoryAllocator::UsedMemory() const { return m_usedMemory.load(); } uint32_t D3D9MemoryAllocator::AllocatedMemory() const { return m_allocatedMemory.load(); } D3D9MemoryChunk::D3D9MemoryChunk(D3D9MemoryAllocator* Allocator, uint32_t Size) : m_allocator(Allocator), m_size(Size) { m_mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE | SEC_COMMIT, 0, Size, nullptr); m_freeRanges.push_back({ 0, Size }); uint32_t mappingGranularity = Allocator->MappingGranularity(); m_mappingRanges.resize(((Size + mappingGranularity - 1) / mappingGranularity)); } D3D9MemoryChunk::~D3D9MemoryChunk() { // Has to be protected by the allocator lock CloseHandle(m_mapping); } void* D3D9MemoryChunk::MapLocked(D3D9Memory* Memory, uint32_t& mappedSize) { // Has to be protected by the allocator lock mappedSize = 0; uint32_t mappingGranularity = m_allocator->MappingGranularity(); uint32_t alignedOffset = alignDown(Memory->GetOffset(), mappingGranularity); uint32_t alignmentDelta = Memory->GetOffset() - alignedOffset; uint32_t alignedSize = Memory->GetSize() + alignmentDelta; if (alignedSize > mappingGranularity) { // The allocation crosses the boundary of the internal mapping page it's a part of // so we map it on it's own. alignedOffset = alignDown(Memory->GetOffset(), m_allocator->AllocationGranularity()); alignmentDelta = Memory->GetOffset() - alignedOffset; alignedSize = Memory->GetSize() + alignmentDelta; mappedSize = alignedSize; uint8_t* basePtr = static_cast(MapViewOfFile(m_mapping, FILE_MAP_ALL_ACCESS, 0, alignedOffset, alignedSize)); if (unlikely(basePtr == nullptr)) { DWORD error = GetLastError(); Logger::err(str::format("Mapping non-persisted file failed: ", error, ", Mapped memory: ", m_allocator->MappedMemory())); return nullptr; } return basePtr + alignmentDelta; } // For small allocations we map the entire mapping page to minimize the overhead from having the align the offset to 65k bytes. // This should hopefully also reduce the amount of MapViewOfFile calls we do for tiny allocations. auto& mappingRange = m_mappingRanges[Memory->GetOffset() / mappingGranularity]; if (unlikely(mappingRange.refCount == 0)) { mappedSize = mappingGranularity; mappingRange.ptr = static_cast(MapViewOfFile(m_mapping, FILE_MAP_ALL_ACCESS, 0, alignedOffset, m_allocator->MappingGranularity())); if (unlikely(mappingRange.ptr == nullptr)) { DWORD error = GetLastError(); LPTSTR buffer = nullptr; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPTSTR)&buffer, 0, nullptr); Logger::err(str::format("Mapping non-persisted file failed: ", error, ", Mapped memory: ", m_allocator->MappedMemory(), ", Msg: ", buffer)); if (buffer) { LocalFree(buffer); } } } mappingRange.refCount++; uint8_t* basePtr = static_cast(mappingRange.ptr); return basePtr + alignmentDelta; } uint32_t D3D9MemoryChunk::UnmapLocked(D3D9Memory* Memory) { // Has to be protected by the allocator lock uint32_t mappingGranularity = m_allocator->MappingGranularity(); uint32_t alignedOffset = alignDown(Memory->GetOffset(), mappingGranularity); uint32_t alignmentDelta = Memory->GetOffset() - alignedOffset; uint32_t alignedSize = Memory->GetSize() + alignmentDelta; if (alignedSize > mappingGranularity) { // Single use mapping alignedOffset = alignDown(Memory->GetOffset(), m_allocator->AllocationGranularity()); alignmentDelta = Memory->GetOffset() - alignedOffset; alignedSize = Memory->GetSize() + alignmentDelta; uint8_t* basePtr = static_cast(Memory->Ptr()) - alignmentDelta; UnmapViewOfFile(basePtr); return alignedSize; } auto& mappingRange = m_mappingRanges[Memory->GetOffset() / mappingGranularity]; mappingRange.refCount--; if (unlikely(mappingRange.refCount == 0)) { UnmapViewOfFile(mappingRange.ptr); mappingRange.ptr = nullptr; return mappingGranularity; } return 0; } D3D9Memory D3D9MemoryChunk::AllocLocked(uint32_t Size) { // Has to be protected by the allocator lock uint32_t offset = 0; uint32_t size = 0; for (auto range = m_freeRanges.begin(); range != m_freeRanges.end(); range++) { if (range->length >= Size) { offset = range->offset; size = Size; range->offset += Size; range->length -= Size; if (range->length < (4 << 10)) { size += range->length; m_freeRanges.erase(range); } break; } } if (size != 0) return D3D9Memory(this, offset, Size); return {}; } void D3D9MemoryChunk::FreeLocked(D3D9Memory *Memory) { // Has to be protected by the allocator lock uint32_t offset = Memory->GetOffset(); uint32_t size = Memory->GetSize(); auto curr = m_freeRanges.begin(); // shamelessly stolen from dxvk_memory.cpp while (curr != m_freeRanges.end()) { if (curr->offset == offset + size) { size += curr->length; curr = m_freeRanges.erase(curr); } else if (curr->offset + curr->length == offset) { offset -= curr->length; size += curr->length; curr = m_freeRanges.erase(curr); } else { curr++; } } m_freeRanges.push_back({ offset, size }); } bool D3D9MemoryChunk::IsEmpty() const { // Has to be protected by the allocator lock return m_freeRanges.size() == 1 && m_freeRanges[0].length == m_size; } D3D9MemoryAllocator* D3D9MemoryChunk::Allocator() const { return m_allocator; } D3D9Memory::D3D9Memory(D3D9MemoryChunk* Chunk, size_t Offset, size_t Size) : m_chunk(Chunk), m_offset(Offset), m_size(Size) {} D3D9Memory::D3D9Memory(D3D9Memory&& other) : m_chunk(std::exchange(other.m_chunk, nullptr)), m_ptr(std::exchange(other.m_ptr, nullptr)), m_offset(std::exchange(other.m_offset, 0)), m_size(std::exchange(other.m_size, 0)) {} D3D9Memory::~D3D9Memory() { this->Free(); } D3D9Memory& D3D9Memory::operator = (D3D9Memory&& other) { this->Free(); m_chunk = std::exchange(other.m_chunk, nullptr); m_ptr = std::exchange(other.m_ptr, nullptr); m_offset = std::exchange(other.m_offset, 0); m_size = std::exchange(other.m_size, 0); return *this; } void D3D9Memory::Free() { if (unlikely(m_chunk == nullptr)) return; if (m_ptr != nullptr) Unmap(); m_chunk->Allocator()->Free(this); m_chunk = nullptr; } void D3D9Memory::Map() { if (unlikely(m_ptr != nullptr)) return; if (unlikely(m_chunk == nullptr)) return; m_ptr = m_chunk->Allocator()->Map(this); } void D3D9Memory::Unmap() { if (unlikely(m_ptr == nullptr)) return; m_chunk->Allocator()->Unmap(this); m_ptr = nullptr; } void* D3D9Memory::Ptr() { return m_ptr; } #else D3D9Memory D3D9MemoryAllocator::Alloc(uint32_t Size) { D3D9Memory memory(this, Size); m_allocatedMemory += Size; return memory; } uint32_t D3D9MemoryAllocator::MappedMemory() const { return m_allocatedMemory.load(); } uint32_t D3D9MemoryAllocator::UsedMemory() const { return m_allocatedMemory.load(); } uint32_t D3D9MemoryAllocator::AllocatedMemory() const { return m_allocatedMemory.load(); } D3D9Memory::D3D9Memory(D3D9MemoryAllocator* pAllocator, size_t Size) : m_allocator (pAllocator), m_ptr (malloc(Size)), m_size (Size) {} D3D9Memory::D3D9Memory(D3D9Memory&& other) : m_allocator(std::exchange(other.m_allocator, nullptr)), m_ptr(std::exchange(other.m_ptr, nullptr)), m_size(std::exchange(other.m_size, 0)) {} D3D9Memory::~D3D9Memory() { this->Free(); } D3D9Memory& D3D9Memory::operator = (D3D9Memory&& other) { this->Free(); m_allocator = std::exchange(other.m_allocator, nullptr); m_ptr = std::exchange(other.m_ptr, nullptr); m_size = std::exchange(other.m_size, 0); return *this; } void D3D9Memory::Free() { if (m_ptr == nullptr) return; free(m_ptr); m_ptr = nullptr; m_allocator->NotifyFreed(m_size); } #endif } dxvk-2.6.1/src/d3d9/d3d9_mem.h000066400000000000000000000107111477473124000156110ustar00rootroot00000000000000 #pragma once #include "../util/thread.h" #if defined(_WIN32) && !defined(_WIN64) #define D3D9_ALLOW_UNMAPPING #endif #ifdef D3D9_ALLOW_UNMAPPING #define WIN32_LEAN_AND_MEAN #include #endif #include namespace dxvk { class D3D9MemoryAllocator; class D3D9Memory; #ifdef D3D9_ALLOW_UNMAPPING class D3D9MemoryChunk; constexpr uint32_t D3D9ChunkSize = 64 << 20; struct D3D9MemoryRange { uint32_t offset; uint32_t length; }; struct D3D9MappingRange { uint32_t refCount = 0; void* ptr = nullptr; }; class D3D9MemoryChunk { friend D3D9MemoryAllocator; public: D3D9MemoryChunk(D3D9MemoryAllocator* Allocator, uint32_t Size); ~D3D9MemoryChunk(); D3D9MemoryChunk (const D3D9MemoryChunk&) = delete; D3D9MemoryChunk& operator = (const D3D9MemoryChunk&) = delete; D3D9MemoryChunk (D3D9MemoryChunk&& other) = delete; D3D9MemoryChunk& operator = (D3D9MemoryChunk&& other) = delete; D3D9MemoryAllocator* Allocator() const; private: bool IsEmpty() const; uint32_t Size() const { return m_size; } D3D9Memory AllocLocked(uint32_t Size); void FreeLocked(D3D9Memory* Memory); void* MapLocked(D3D9Memory* memory, uint32_t& mappedSize); uint32_t UnmapLocked(D3D9Memory* memory); D3D9MemoryAllocator* m_allocator; HANDLE m_mapping; uint32_t m_size; std::vector m_freeRanges; std::vector m_mappingRanges; }; class D3D9Memory { friend D3D9MemoryChunk; friend D3D9MemoryAllocator; public: D3D9Memory() {} ~D3D9Memory(); D3D9Memory (const D3D9Memory&) = delete; D3D9Memory& operator = (const D3D9Memory&) = delete; D3D9Memory (D3D9Memory&& other); D3D9Memory& operator = (D3D9Memory&& other); explicit operator bool() const { return m_chunk != nullptr; } void Map(); void Unmap(); void* Ptr(); private: D3D9Memory(D3D9MemoryChunk* Chunk, size_t Offset, size_t Size); void Free(); D3D9MemoryChunk* GetChunk() const { return m_chunk; } size_t GetOffset() const { return m_offset; } size_t GetSize() const { return m_size; } D3D9MemoryChunk* m_chunk = nullptr; void* m_ptr = nullptr; size_t m_offset = 0; size_t m_size = 0; }; class D3D9MemoryAllocator { friend D3D9MemoryChunk; public: D3D9MemoryAllocator(); ~D3D9MemoryAllocator() = default; D3D9Memory Alloc(uint32_t Size); D3D9Memory AllocFromChunk(D3D9MemoryChunk* Chunk, uint32_t Size); void Free(D3D9Memory* Memory); void* Map(D3D9Memory* Memory); void Unmap(D3D9Memory* Memory); uint32_t MappedMemory() const; uint32_t UsedMemory() const; uint32_t AllocatedMemory() const; uint32_t AllocationGranularity() const { return m_allocationGranularity; } uint32_t MappingGranularity() const { return m_mappingGranularity; } private: void FreeChunk(D3D9MemoryChunk* Chunk); dxvk::mutex m_mutex; std::vector> m_chunks; std::atomic m_mappedMemory = 0; std::atomic m_allocatedMemory = 0; std::atomic m_usedMemory = 0; uint32_t m_allocationGranularity; uint32_t m_mappingGranularity; }; #else class D3D9Memory { friend D3D9MemoryAllocator; public: D3D9Memory() {} ~D3D9Memory(); D3D9Memory (const D3D9Memory&) = delete; D3D9Memory& operator = (const D3D9Memory&) = delete; D3D9Memory (D3D9Memory&& other); D3D9Memory& operator = (D3D9Memory&& other); explicit operator bool() const { return m_ptr != nullptr; } void Map() {} void Unmap() {} void* Ptr() { return m_ptr; } private: D3D9Memory(D3D9MemoryAllocator* pAllocator, size_t Size); void Free(); D3D9MemoryAllocator* m_allocator = nullptr; void* m_ptr = nullptr; size_t m_size = 0; }; class D3D9MemoryAllocator { public: D3D9Memory Alloc(uint32_t Size); uint32_t MappedMemory() const; uint32_t UsedMemory() const; uint32_t AllocatedMemory() const; void NotifyFreed(uint32_t Size) { m_allocatedMemory -= Size; } private: std::atomic m_allocatedMemory = 0; }; #endif } dxvk-2.6.1/src/d3d9/d3d9_monitor.cpp000066400000000000000000000035651477473124000170660ustar00rootroot00000000000000#include "d3d9_monitor.h" #include "d3d9_format.h" namespace dxvk { uint32_t GetMonitorFormatBpp(D3D9Format Format) { switch (Format) { case D3D9Format::A8R8G8B8: case D3D9Format::X8R8G8B8: // This is still 32 bit even though the alpha is unspecified. case D3D9Format::A2R10G10B10: return 32; case D3D9Format::A1R5G5B5: case D3D9Format::X1R5G5B5: case D3D9Format::R5G6B5: return 16; default: Logger::warn(str::format( "GetMonitorFormatBpp: Unknown format: ", Format)); return 32; } } bool IsSupportedAdapterFormat( D3D9Format Format) { // D3D9Format::X1R5G5B5 is unsupported by native drivers and some apps, // such as the BGE SettingsApplication, rely on it not being exposed. return Format == D3D9Format::A2R10G10B10 || Format == D3D9Format::X8R8G8B8 || Format == D3D9Format::R5G6B5; } bool IsSupportedBackBufferFormat( D3D9Format AdapterFormat, D3D9Format BackBufferFormat, BOOL Windowed) { if (!Windowed) { return (AdapterFormat == D3D9Format::A2R10G10B10 && BackBufferFormat == D3D9Format::A2R10G10B10) || (AdapterFormat == D3D9Format::X8R8G8B8 && BackBufferFormat == D3D9Format::X8R8G8B8) || (AdapterFormat == D3D9Format::X8R8G8B8 && BackBufferFormat == D3D9Format::A8R8G8B8) || (AdapterFormat == D3D9Format::R5G6B5 && BackBufferFormat == D3D9Format::R5G6B5); } return IsSupportedBackBufferFormat(BackBufferFormat); } bool IsSupportedBackBufferFormat( D3D9Format BackBufferFormat) { return BackBufferFormat == D3D9Format::A2R10G10B10 || BackBufferFormat == D3D9Format::A8R8G8B8 || BackBufferFormat == D3D9Format::X8R8G8B8 || BackBufferFormat == D3D9Format::R5G6B5 || BackBufferFormat == D3D9Format::Unknown; } } dxvk-2.6.1/src/d3d9/d3d9_monitor.h000066400000000000000000000036041477473124000165250ustar00rootroot00000000000000#pragma once #include "d3d9_include.h" #include "d3d9_format.h" #include "../wsi/wsi_window.h" #include "../wsi/wsi_monitor.h" namespace dxvk { /** * \brief Queries bits per pixel for a format * * The format must be a valid swap chain format. * \param [in] Format The D3D9 format to query * \returns Bits per pixel for this format */ uint32_t GetMonitorFormatBpp( D3D9Format Format); /** * \brief Returns if a format is supported for a backbuffer/swapchain. * * \param [in] Format The D3D9 format to query * \returns If it is supported as a swapchain/backbuffer format. */ bool IsSupportedAdapterFormat( D3D9Format Format); bool IsSupportedBackBufferFormat( D3D9Format AdapterFormat, D3D9Format BackBufferFormat, BOOL Windowed); bool IsSupportedBackBufferFormat( D3D9Format BackBufferFormat); inline wsi::WsiMode ConvertDisplayMode(const D3DDISPLAYMODEEX& mode) { wsi::WsiMode wsiMode = { }; wsiMode.width = mode.Width; wsiMode.height = mode.Height; wsiMode.refreshRate = wsi::WsiRational{ mode.RefreshRate, 1 }; wsiMode.bitsPerPixel = GetMonitorFormatBpp(EnumerateFormat(mode.Format)); wsiMode.interlaced = mode.ScanLineOrdering == D3DSCANLINEORDERING_INTERLACED; return wsiMode; } inline D3DDISPLAYMODEEX ConvertDisplayMode(const wsi::WsiMode& wsiMode) { D3DDISPLAYMODEEX d3d9Mode = { }; d3d9Mode.Size = sizeof(D3DDISPLAYMODEEX); d3d9Mode.Width = wsiMode.width; d3d9Mode.Height = wsiMode.height; d3d9Mode.RefreshRate = wsiMode.refreshRate.numerator / wsiMode.refreshRate.denominator; d3d9Mode.Format = D3DFMT_X8R8G8B8; d3d9Mode.ScanLineOrdering = wsiMode.interlaced ? D3DSCANLINEORDERING_INTERLACED : D3DSCANLINEORDERING_PROGRESSIVE; return d3d9Mode; } } dxvk-2.6.1/src/d3d9/d3d9_multithread.cpp000066400000000000000000000002401477473124000177040ustar00rootroot00000000000000#include "d3d9_device.h" namespace dxvk { D3D9Multithread::D3D9Multithread( BOOL Protected) : m_protected( Protected ) { } }dxvk-2.6.1/src/d3d9/d3d9_multithread.h000066400000000000000000000024111477473124000173530ustar00rootroot00000000000000#pragma once #include "d3d9_include.h" namespace dxvk { /** * \brief Device lock * * Lightweight RAII wrapper that implements * a subset of the functionality provided by * \c std::unique_lock, with the goal of being * cheaper to construct and destroy. */ class D3D9DeviceLock { public: D3D9DeviceLock() : m_mutex(nullptr) { } D3D9DeviceLock(sync::RecursiveSpinlock& mutex) : m_mutex(&mutex) { mutex.lock(); } D3D9DeviceLock(D3D9DeviceLock&& other) : m_mutex(other.m_mutex) { other.m_mutex = nullptr; } D3D9DeviceLock& operator = (D3D9DeviceLock&& other) { if (m_mutex) m_mutex->unlock(); m_mutex = other.m_mutex; other.m_mutex = nullptr; return *this; } ~D3D9DeviceLock() { if (m_mutex != nullptr) m_mutex->unlock(); } private: sync::RecursiveSpinlock* m_mutex; }; /** * \brief D3D9 context lock */ class D3D9Multithread { public: D3D9Multithread( BOOL Protected); D3D9DeviceLock AcquireLock() { return m_protected ? D3D9DeviceLock(m_mutex) : D3D9DeviceLock(); } private: BOOL m_protected; sync::RecursiveSpinlock m_mutex; }; }dxvk-2.6.1/src/d3d9/d3d9_names.cpp000066400000000000000000000173541477473124000165030ustar00rootroot00000000000000#include "d3d9_format.h" namespace dxvk { std::ostream& operator << (std::ostream& os, D3D9Format e) { switch (e) { ENUM_NAME(D3D9Format::Unknown); ENUM_NAME(D3D9Format::R8G8B8); ENUM_NAME(D3D9Format::A8R8G8B8); ENUM_NAME(D3D9Format::X8R8G8B8); ENUM_NAME(D3D9Format::R5G6B5); ENUM_NAME(D3D9Format::X1R5G5B5); ENUM_NAME(D3D9Format::A1R5G5B5); ENUM_NAME(D3D9Format::A4R4G4B4); ENUM_NAME(D3D9Format::R3G3B2); ENUM_NAME(D3D9Format::A8); ENUM_NAME(D3D9Format::A8R3G3B2); ENUM_NAME(D3D9Format::X4R4G4B4); ENUM_NAME(D3D9Format::A2B10G10R10); ENUM_NAME(D3D9Format::A8B8G8R8); ENUM_NAME(D3D9Format::X8B8G8R8); ENUM_NAME(D3D9Format::G16R16); ENUM_NAME(D3D9Format::A2R10G10B10); ENUM_NAME(D3D9Format::A16B16G16R16); ENUM_NAME(D3D9Format::A8P8); ENUM_NAME(D3D9Format::P8); ENUM_NAME(D3D9Format::L8); ENUM_NAME(D3D9Format::A8L8); ENUM_NAME(D3D9Format::A4L4); ENUM_NAME(D3D9Format::V8U8); ENUM_NAME(D3D9Format::L6V5U5); ENUM_NAME(D3D9Format::X8L8V8U8); ENUM_NAME(D3D9Format::Q8W8V8U8); ENUM_NAME(D3D9Format::V16U16); ENUM_NAME(D3D9Format::A2W10V10U10); ENUM_NAME(D3D9Format::UYVY); ENUM_NAME(D3D9Format::R8G8_B8G8); ENUM_NAME(D3D9Format::YUY2); ENUM_NAME(D3D9Format::G8R8_G8B8); ENUM_NAME(D3D9Format::DXT1); ENUM_NAME(D3D9Format::DXT2); ENUM_NAME(D3D9Format::DXT3); ENUM_NAME(D3D9Format::DXT4); ENUM_NAME(D3D9Format::DXT5); ENUM_NAME(D3D9Format::D16_LOCKABLE); ENUM_NAME(D3D9Format::D32); ENUM_NAME(D3D9Format::D15S1); ENUM_NAME(D3D9Format::D24S8); ENUM_NAME(D3D9Format::D24X8); ENUM_NAME(D3D9Format::D24X4S4); ENUM_NAME(D3D9Format::D16); ENUM_NAME(D3D9Format::D32F_LOCKABLE); ENUM_NAME(D3D9Format::D24FS8); ENUM_NAME(D3D9Format::D32_LOCKABLE); ENUM_NAME(D3D9Format::S8_LOCKABLE); ENUM_NAME(D3D9Format::L16); ENUM_NAME(D3D9Format::VERTEXDATA); ENUM_NAME(D3D9Format::INDEX16); ENUM_NAME(D3D9Format::INDEX32); ENUM_NAME(D3D9Format::Q16W16V16U16); ENUM_NAME(D3D9Format::MULTI2_ARGB8); ENUM_NAME(D3D9Format::R16F); ENUM_NAME(D3D9Format::G16R16F); ENUM_NAME(D3D9Format::A16B16G16R16F); ENUM_NAME(D3D9Format::R32F); ENUM_NAME(D3D9Format::G32R32F); ENUM_NAME(D3D9Format::A32B32G32R32F); ENUM_NAME(D3D9Format::CxV8U8); ENUM_NAME(D3D9Format::A1); ENUM_NAME(D3D9Format::A2B10G10R10_XR_BIAS); ENUM_NAME(D3D9Format::BINARYBUFFER); // Driver Hacks / Unofficial Formats ENUM_NAME(D3D9Format::ATI1); ENUM_NAME(D3D9Format::ATI2); ENUM_NAME(D3D9Format::INST); ENUM_NAME(D3D9Format::DF24); ENUM_NAME(D3D9Format::DF16); ENUM_NAME(D3D9Format::NULL_FORMAT); ENUM_NAME(D3D9Format::GET4); ENUM_NAME(D3D9Format::GET1); ENUM_NAME(D3D9Format::NVDB); ENUM_NAME(D3D9Format::A2M1); ENUM_NAME(D3D9Format::A2M0); ENUM_NAME(D3D9Format::ATOC); ENUM_NAME(D3D9Format::INTZ); ENUM_NAME(D3D9Format::RAWZ); ENUM_NAME(D3D9Format::RESZ); ENUM_NAME(D3D9Format::NV11); ENUM_NAME(D3D9Format::NV12); ENUM_NAME(D3D9Format::P010); ENUM_NAME(D3D9Format::P016); ENUM_NAME(D3D9Format::Y210); ENUM_NAME(D3D9Format::Y216); ENUM_NAME(D3D9Format::Y410); ENUM_NAME(D3D9Format::AYUV); ENUM_NAME(D3D9Format::YV12); ENUM_NAME(D3D9Format::OPAQUE_420); ENUM_NAME(D3D9Format::AI44); ENUM_NAME(D3D9Format::IA44); ENUM_NAME(D3D9Format::R2VB); ENUM_NAME(D3D9Format::COPM); ENUM_NAME(D3D9Format::SSAA); ENUM_NAME(D3D9Format::AL16); ENUM_NAME(D3D9Format::R16); ENUM_NAME(D3D9Format::EXT1); ENUM_NAME(D3D9Format::FXT1); ENUM_NAME(D3D9Format::GXT1); ENUM_NAME(D3D9Format::HXT1); ENUM_DEFAULT(e); } } std::ostream& operator << (std::ostream& os, D3DRENDERSTATETYPE e) { switch (e) { ENUM_NAME(D3DRS_ZENABLE); ENUM_NAME(D3DRS_FILLMODE); ENUM_NAME(D3DRS_SHADEMODE); ENUM_NAME(D3DRS_ZWRITEENABLE); ENUM_NAME(D3DRS_ALPHATESTENABLE); ENUM_NAME(D3DRS_LASTPIXEL); ENUM_NAME(D3DRS_SRCBLEND); ENUM_NAME(D3DRS_DESTBLEND); ENUM_NAME(D3DRS_CULLMODE); ENUM_NAME(D3DRS_ZFUNC); ENUM_NAME(D3DRS_ALPHAREF); ENUM_NAME(D3DRS_ALPHAFUNC); ENUM_NAME(D3DRS_DITHERENABLE); ENUM_NAME(D3DRS_ALPHABLENDENABLE); ENUM_NAME(D3DRS_FOGENABLE); ENUM_NAME(D3DRS_SPECULARENABLE); ENUM_NAME(D3DRS_FOGCOLOR); ENUM_NAME(D3DRS_FOGTABLEMODE); ENUM_NAME(D3DRS_FOGSTART); ENUM_NAME(D3DRS_FOGEND); ENUM_NAME(D3DRS_FOGDENSITY); ENUM_NAME(D3DRS_RANGEFOGENABLE); ENUM_NAME(D3DRS_STENCILENABLE); ENUM_NAME(D3DRS_STENCILFAIL); ENUM_NAME(D3DRS_STENCILZFAIL); ENUM_NAME(D3DRS_STENCILPASS); ENUM_NAME(D3DRS_STENCILFUNC); ENUM_NAME(D3DRS_STENCILREF); ENUM_NAME(D3DRS_STENCILMASK); ENUM_NAME(D3DRS_STENCILWRITEMASK); ENUM_NAME(D3DRS_TEXTUREFACTOR); ENUM_NAME(D3DRS_WRAP0); ENUM_NAME(D3DRS_WRAP1); ENUM_NAME(D3DRS_WRAP2); ENUM_NAME(D3DRS_WRAP3); ENUM_NAME(D3DRS_WRAP4); ENUM_NAME(D3DRS_WRAP5); ENUM_NAME(D3DRS_WRAP6); ENUM_NAME(D3DRS_WRAP7); ENUM_NAME(D3DRS_CLIPPING); ENUM_NAME(D3DRS_LIGHTING); ENUM_NAME(D3DRS_AMBIENT); ENUM_NAME(D3DRS_FOGVERTEXMODE); ENUM_NAME(D3DRS_COLORVERTEX); ENUM_NAME(D3DRS_LOCALVIEWER); ENUM_NAME(D3DRS_NORMALIZENORMALS); ENUM_NAME(D3DRS_DIFFUSEMATERIALSOURCE); ENUM_NAME(D3DRS_SPECULARMATERIALSOURCE); ENUM_NAME(D3DRS_AMBIENTMATERIALSOURCE); ENUM_NAME(D3DRS_EMISSIVEMATERIALSOURCE); ENUM_NAME(D3DRS_VERTEXBLEND); ENUM_NAME(D3DRS_CLIPPLANEENABLE); ENUM_NAME(D3DRS_POINTSIZE); ENUM_NAME(D3DRS_POINTSIZE_MIN); ENUM_NAME(D3DRS_POINTSPRITEENABLE); ENUM_NAME(D3DRS_POINTSCALEENABLE); ENUM_NAME(D3DRS_POINTSCALE_A); ENUM_NAME(D3DRS_POINTSCALE_B); ENUM_NAME(D3DRS_POINTSCALE_C); ENUM_NAME(D3DRS_MULTISAMPLEANTIALIAS); ENUM_NAME(D3DRS_MULTISAMPLEMASK); ENUM_NAME(D3DRS_PATCHEDGESTYLE); ENUM_NAME(D3DRS_DEBUGMONITORTOKEN); ENUM_NAME(D3DRS_POINTSIZE_MAX); ENUM_NAME(D3DRS_INDEXEDVERTEXBLENDENABLE); ENUM_NAME(D3DRS_COLORWRITEENABLE); ENUM_NAME(D3DRS_TWEENFACTOR); ENUM_NAME(D3DRS_BLENDOP); ENUM_NAME(D3DRS_POSITIONDEGREE); ENUM_NAME(D3DRS_NORMALDEGREE); ENUM_NAME(D3DRS_SCISSORTESTENABLE); ENUM_NAME(D3DRS_SLOPESCALEDEPTHBIAS); ENUM_NAME(D3DRS_ANTIALIASEDLINEENABLE); ENUM_NAME(D3DRS_MINTESSELLATIONLEVEL); ENUM_NAME(D3DRS_MAXTESSELLATIONLEVEL); ENUM_NAME(D3DRS_ADAPTIVETESS_X); ENUM_NAME(D3DRS_ADAPTIVETESS_Y); ENUM_NAME(D3DRS_ADAPTIVETESS_Z); ENUM_NAME(D3DRS_ADAPTIVETESS_W); ENUM_NAME(D3DRS_ENABLEADAPTIVETESSELLATION); ENUM_NAME(D3DRS_TWOSIDEDSTENCILMODE); ENUM_NAME(D3DRS_CCW_STENCILFAIL); ENUM_NAME(D3DRS_CCW_STENCILZFAIL); ENUM_NAME(D3DRS_CCW_STENCILPASS); ENUM_NAME(D3DRS_CCW_STENCILFUNC); ENUM_NAME(D3DRS_COLORWRITEENABLE1); ENUM_NAME(D3DRS_COLORWRITEENABLE2); ENUM_NAME(D3DRS_COLORWRITEENABLE3); ENUM_NAME(D3DRS_BLENDFACTOR); ENUM_NAME(D3DRS_SRGBWRITEENABLE); ENUM_NAME(D3DRS_DEPTHBIAS); ENUM_NAME(D3DRS_WRAP8); ENUM_NAME(D3DRS_WRAP9); ENUM_NAME(D3DRS_WRAP10); ENUM_NAME(D3DRS_WRAP11); ENUM_NAME(D3DRS_WRAP12); ENUM_NAME(D3DRS_WRAP13); ENUM_NAME(D3DRS_WRAP14); ENUM_NAME(D3DRS_WRAP15); ENUM_NAME(D3DRS_SEPARATEALPHABLENDENABLE); ENUM_NAME(D3DRS_SRCBLENDALPHA); ENUM_NAME(D3DRS_DESTBLENDALPHA); ENUM_NAME(D3DRS_BLENDOPALPHA); ENUM_DEFAULT(e); } } }dxvk-2.6.1/src/d3d9/d3d9_names.h000066400000000000000000000001651477473124000161400ustar00rootroot00000000000000#include "d3d9_include.h" namespace dxvk { std::ostream& operator << (std::ostream& os, D3DRENDERSTATETYPE e); }dxvk-2.6.1/src/d3d9/d3d9_on_12.cpp000066400000000000000000000023011477473124000163000ustar00rootroot00000000000000#include "d3d9_on_12.h" #include "d3d9_device.h" namespace dxvk { D3D9On12::D3D9On12(D3D9DeviceEx* device) : m_device(device) { } HRESULT STDMETHODCALLTYPE D3D9On12::QueryInterface(REFIID riid, void** object) { return m_device->QueryInterface(riid, object); } ULONG STDMETHODCALLTYPE D3D9On12::AddRef() { return m_device->AddRef(); } ULONG STDMETHODCALLTYPE D3D9On12::Release() { return m_device->Release(); } HRESULT STDMETHODCALLTYPE D3D9On12::GetD3D12Device(REFIID riid, void** object) { InitReturnPtr(object); Logger::err("D3D9On12::GetD3D12Device: Stub"); return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE D3D9On12::UnwrapUnderlyingResource(IDirect3DResource9* resource, ID3D12CommandQueue* command_queue, REFIID riid, void** object) { Logger::err("D3D9On12::GetD3D12Device: UnwrapUnderlyingResource: Stub"); return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE D3D9On12::ReturnUnderlyingResource(IDirect3DResource9* resource, UINT num_sync, UINT64* signal_values, ID3D12Fence** fences) { if (num_sync) Logger::err("D3D9On12::GetD3D12Device: ReturnUnderlyingResource: Stub"); m_device->FlushAndSync9On12(); return S_OK; } } dxvk-2.6.1/src/d3d9/d3d9_on_12.h000066400000000000000000000013711477473124000157530ustar00rootroot00000000000000#pragma once #include "d3d9_include.h" namespace dxvk { class D3D9DeviceEx; class D3D9On12 final : public IDirect3DDevice9On12 { public: D3D9On12(D3D9DeviceEx* device); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** object); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE GetD3D12Device(REFIID riid, void** object); HRESULT STDMETHODCALLTYPE UnwrapUnderlyingResource(IDirect3DResource9* resource, ID3D12CommandQueue* command_queue, REFIID riid, void** object); HRESULT STDMETHODCALLTYPE ReturnUnderlyingResource(IDirect3DResource9* resource, UINT num_sync, UINT64* signal_values, ID3D12Fence** fences); private: D3D9DeviceEx* m_device; }; } dxvk-2.6.1/src/d3d9/d3d9_options.cpp000066400000000000000000000153041477473124000170640ustar00rootroot00000000000000#include "../util/util_math.h" #include "d3d9_options.h" #include "d3d9_caps.h" namespace dxvk { static int32_t parsePciId(const std::string& str) { if (str.size() != 4) return -1; int32_t id = 0; for (size_t i = 0; i < str.size(); i++) { id *= 16; if (str[i] >= '0' && str[i] <= '9') id += str[i] - '0'; else if (str[i] >= 'A' && str[i] <= 'F') id += str[i] - 'A' + 10; else if (str[i] >= 'a' && str[i] <= 'f') id += str[i] - 'a' + 10; else return -1; } return id; } D3D9Options::D3D9Options(const Rc& device, const Config& config) { const Rc adapter = device != nullptr ? device->adapter() : nullptr; // Fetch these as a string representing a hexadecimal number and parse it. this->customVendorId = parsePciId(config.getOption("d3d9.customVendorId")); this->customDeviceId = parsePciId(config.getOption("d3d9.customDeviceId")); this->customDeviceDesc = config.getOption("d3d9.customDeviceDesc"); const uint32_t vendorId = this->customVendorId != -1 ? this->customVendorId : (adapter != nullptr ? adapter->deviceProperties().vendorID : 0); this->maxFrameLatency = config.getOption ("d3d9.maxFrameLatency", 0); this->maxFrameRate = config.getOption ("d3d9.maxFrameRate", 0); this->presentInterval = config.getOption ("d3d9.presentInterval", -1); this->shaderModel = config.getOption ("d3d9.shaderModel", 3u); this->dpiAware = config.getOption ("d3d9.dpiAware", true); this->strictConstantCopies = config.getOption ("d3d9.strictConstantCopies", false); this->strictPow = config.getOption ("d3d9.strictPow", true); this->lenientClear = config.getOption ("d3d9.lenientClear", false); this->deferSurfaceCreation = config.getOption ("d3d9.deferSurfaceCreation", false); this->samplerAnisotropy = config.getOption ("d3d9.samplerAnisotropy", -1); this->maxAvailableMemory = config.getOption ("d3d9.maxAvailableMemory", 4096); this->supportDFFormats = config.getOption ("d3d9.supportDFFormats", true); this->supportX4R4G4B4 = config.getOption ("d3d9.supportX4R4G4B4", true); this->supportD16Lockable = config.getOption ("d3d9.supportD16Lockable", false); this->useD32forD24 = config.getOption ("d3d9.useD32forD24", false); this->disableA8RT = config.getOption ("d3d9.disableA8RT", false); this->invariantPosition = config.getOption ("d3d9.invariantPosition", true); this->memoryTrackTest = config.getOption ("d3d9.memoryTrackTest", false); this->supportVCache = config.getOption ("d3d9.supportVCache", vendorId == uint32_t(DxvkGpuVendor::Nvidia)); this->forceSamplerTypeSpecConstants = config.getOption ("d3d9.forceSamplerTypeSpecConstants", false); this->forceSwapchainMSAA = config.getOption ("d3d9.forceSwapchainMSAA", -1); this->forceSampleRateShading = config.getOption ("d3d9.forceSampleRateShading", false); this->forceAspectRatio = config.getOption ("d3d9.forceAspectRatio", ""); this->enumerateByDisplays = config.getOption ("d3d9.enumerateByDisplays", true); this->cachedDynamicBuffers = config.getOption ("d3d9.cachedDynamicBuffers", false); this->deviceLocalConstantBuffers = config.getOption ("d3d9.deviceLocalConstantBuffers", false); this->allowDirectBufferMapping = config.getOption ("d3d9.allowDirectBufferMapping", true); this->seamlessCubes = config.getOption ("d3d9.seamlessCubes", false); this->textureMemory = config.getOption ("d3d9.textureMemory", 100) << 20; this->deviceLossOnFocusLoss = config.getOption ("d3d9.deviceLossOnFocusLoss", false); this->samplerLodBias = config.getOption ("d3d9.samplerLodBias", 0.0f); this->clampNegativeLodBias = config.getOption ("d3d9.clampNegativeLodBias", false); this->countLosableResources = config.getOption ("d3d9.countLosableResources", true); this->reproducibleCommandStream = config.getOption ("d3d9.reproducibleCommandStream", false); this->extraFrontbuffer = config.getOption ("d3d9.extraFrontbuffer", false); // D3D8 options this->drefScaling = config.getOption ("d3d8.scaleDref", 0); // Clamp the shader model value between 0 and 3 this->shaderModel = dxvk::clamp(this->shaderModel, 0u, 3u); // Clamp LOD bias so that people don't abuse this in unintended ways this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f); std::string floatEmulation = Config::toLower(config.getOption("d3d9.floatEmulation", "auto")); if (floatEmulation == "strict") { d3d9FloatEmulation = D3D9FloatEmulation::Strict; } else if (floatEmulation == "false") { d3d9FloatEmulation = D3D9FloatEmulation::Disabled; } else if (floatEmulation == "true") { d3d9FloatEmulation = D3D9FloatEmulation::Enabled; } else { bool hasMulz = adapter != nullptr && (adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV) || adapter->matchesDriver(VK_DRIVER_ID_MESA_NVK) || adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE, Version(2, 0, 316), Version()) || adapter->matchesDriver(VK_DRIVER_ID_NVIDIA_PROPRIETARY, Version(565, 57, 1), Version())); d3d9FloatEmulation = hasMulz ? D3D9FloatEmulation::Strict : D3D9FloatEmulation::Enabled; } this->shaderDumpPath = env::getEnvVar("DXVK_SHADER_DUMP_PATH"); } } dxvk-2.6.1/src/d3d9/d3d9_options.h000066400000000000000000000111271477473124000165300ustar00rootroot00000000000000#pragma once #include "../util/config/config.h" #include "../dxvk/dxvk_device.h" namespace dxvk { enum class D3D9FloatEmulation { Disabled, Enabled, Strict }; struct D3D9Options { D3D9Options(const Rc& device, const Config& config); /// Override PCI vendor and device IDs reported to the /// application. This may make apps think they are running /// on a different GPU than they do and behave differently. int32_t customVendorId; int32_t customDeviceId; std::string customDeviceDesc; /// Present interval. Overrides the value /// in D3DPRESENT_PARAMS used in swapchain present. int32_t presentInterval; /// Override maximum frame latency if the app specifies /// a higher value. May help with frame timing issues. int32_t maxFrameLatency; /// Limit frame rate int32_t maxFrameRate; /// Set the max shader model the device can support in the caps. uint32_t shaderModel; /// Whether or not to set the process as DPI aware in Windows when the API interface is created. bool dpiAware; /// True: Copy our constant set into UBO if we are relative indexing ever. /// False: Copy our constant set into UBO if we are relative indexing at the start of a defined constant /// Why?: In theory, FXC should never generate code where this would be an issue. bool strictConstantCopies; /// Whether or not we should care about pow(0, 0) = 1 bool strictPow; /// Whether or not to do a fast path clear if we're close enough to the whole render target. bool lenientClear; /// Defer surface creation bool deferSurfaceCreation; /// Anisotropic filter override /// /// Enforces anisotropic filtering with the /// given anisotropy value for all samplers. int32_t samplerAnisotropy; /// Max available memory override /// /// Changes the max initial value used in /// tracking and GetAvailableTextureMem uint32_t maxAvailableMemory; /// D3D9 Floating Point Emulation (anything * 0 = 0) D3D9FloatEmulation d3d9FloatEmulation; /// Support the DF16 & DF24 texture format bool supportDFFormats; /// Support X4R4G4B4 bool supportX4R4G4B4; /// Support D16_LOCKABLE bool supportD16Lockable; /// Use D32f for D24 bool useD32forD24; /// Disable D3DFMT_A8 for render targets. /// Specifically to work around a game /// bug in The Sims 2 that happens on native too! bool disableA8RT; /// Work around a NV driver quirk /// Fixes flickering/z-fighting in some games. bool invariantPosition; /// Whether or not to respect memory tracking for /// failing resource allocation. bool memoryTrackTest; /// Support VCACHE query bool supportVCache; /// Forced aspect ratio, disable other modes std::string forceAspectRatio; /// Always use a spec constant to determine sampler type (instead of just in PS 1.x) /// Works around a game bug in Halo CE where it gives cube textures to 2d/volume samplers bool forceSamplerTypeSpecConstants; /// Forces an MSAA level on the swapchain int32_t forceSwapchainMSAA; /// Forces sample rate shading bool forceSampleRateShading; /// Enumerate adapters by displays bool enumerateByDisplays; /// Cached dynamic buffers: Maps all buffers in cached memory. bool cachedDynamicBuffers; /// Use device local memory for constant buffers. bool deviceLocalConstantBuffers; /// Disable direct buffer mapping bool allowDirectBufferMapping; /// Don't use non seamless cube maps bool seamlessCubes; /// Mipmap LOD bias /// /// Enforces the given LOD bias for all samplers. float samplerLodBias; /// Clamps negative LOD bias bool clampNegativeLodBias; /// How much virtual memory will be used for textures (in MB). int32_t textureMemory; /// Shader dump path std::string shaderDumpPath; /// Enable emulation of device loss when a fullscreen app loses focus bool deviceLossOnFocusLoss; /// Disable counting losable resources and rejecting calls to Reset() if any are still alive bool countLosableResources; /// Ensure that for the same D3D commands the output VK commands /// don't change between runs. Useful for comparative benchmarking, /// can negatively affect performance. bool reproducibleCommandStream; /// Enable depth texcoord Z (Dref) scaling (D3D8 quirk) int32_t drefScaling; /// Add an extra front buffer to make GetFrontBufferData() work correctly when the swapchain only has a single buffer bool extraFrontbuffer; }; } dxvk-2.6.1/src/d3d9/d3d9_query.cpp000066400000000000000000000225511477473124000165400ustar00rootroot00000000000000#include "d3d9_query.h" #include "d3d9_device.h" namespace dxvk { D3D9Query::D3D9Query( D3D9DeviceEx* pDevice, D3DQUERYTYPE QueryType) : D3D9DeviceChild(pDevice) , m_queryType (QueryType) , m_state (D3D9_VK_QUERY_INITIAL) { Rc dxvkDevice = m_parent->GetDXVKDevice(); switch (m_queryType) { case D3DQUERYTYPE_VCACHE: break; case D3DQUERYTYPE_EVENT: m_event[0] = dxvkDevice->createGpuEvent(); break; case D3DQUERYTYPE_OCCLUSION: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_OCCLUSION, VK_QUERY_CONTROL_PRECISE_BIT, 0); break; case D3DQUERYTYPE_TIMESTAMP: m_query[0] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_TIMESTAMP, 0, 0); break; case D3DQUERYTYPE_TIMESTAMPDISJOINT: for (uint32_t i = 0; i < 2; i++) { m_query[i] = dxvkDevice->createGpuQuery( VK_QUERY_TYPE_TIMESTAMP, 0, 0); } break; case D3DQUERYTYPE_TIMESTAMPFREQ: break; default: throw DxvkError(str::format("D3D9Query: Unsupported query type ", m_queryType)); } } HRESULT STDMETHODCALLTYPE D3D9Query::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(IDirect3DQuery9)) { *ppvObject = ref(this); return S_OK; } if (logQueryInterfaceError(__uuidof(IDirect3DQuery9), riid)) { Logger::warn("D3D9Query::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } D3DQUERYTYPE STDMETHODCALLTYPE D3D9Query::GetType() { return m_queryType; } DWORD STDMETHODCALLTYPE D3D9Query::GetDataSize() { switch (m_queryType) { case D3DQUERYTYPE_VCACHE: return sizeof(D3DDEVINFO_VCACHE); case D3DQUERYTYPE_RESOURCEMANAGER: return sizeof(D3DDEVINFO_RESOURCEMANAGER); case D3DQUERYTYPE_VERTEXSTATS: return sizeof(D3DDEVINFO_D3DVERTEXSTATS); case D3DQUERYTYPE_EVENT: return sizeof(BOOL); case D3DQUERYTYPE_OCCLUSION: return sizeof(DWORD); case D3DQUERYTYPE_TIMESTAMP: return sizeof(UINT64); case D3DQUERYTYPE_TIMESTAMPDISJOINT: return sizeof(BOOL); case D3DQUERYTYPE_TIMESTAMPFREQ: return sizeof(UINT64); case D3DQUERYTYPE_PIPELINETIMINGS: return sizeof(D3DDEVINFO_D3D9PIPELINETIMINGS); case D3DQUERYTYPE_INTERFACETIMINGS: return sizeof(D3DDEVINFO_D3D9INTERFACETIMINGS); case D3DQUERYTYPE_VERTEXTIMINGS: return sizeof(D3DDEVINFO_D3D9STAGETIMINGS); case D3DQUERYTYPE_PIXELTIMINGS: return sizeof(D3DDEVINFO_D3D9PIPELINETIMINGS); case D3DQUERYTYPE_BANDWIDTHTIMINGS: return sizeof(D3DDEVINFO_D3D9BANDWIDTHTIMINGS); case D3DQUERYTYPE_CACHEUTILIZATION: return sizeof(D3DDEVINFO_D3D9CACHEUTILIZATION); default: return 0; } } HRESULT STDMETHODCALLTYPE D3D9Query::Issue(DWORD dwIssueFlags) { // Note: No need to submit to CS if we don't do anything! if (dwIssueFlags == D3DISSUE_BEGIN) { if (QueryBeginnable(m_queryType)) { if (m_state == D3D9_VK_QUERY_BEGUN && QueryEndable(m_queryType)) { m_resetCtr.fetch_add(1, std::memory_order_acquire); m_parent->End(this); } m_parent->Begin(this); m_state = D3D9_VK_QUERY_BEGUN; } } else { if (QueryEndable(m_queryType)) { if (m_state != D3D9_VK_QUERY_BEGUN && QueryBeginnable(m_queryType)) m_parent->Begin(this); m_resetCtr.fetch_add(1, std::memory_order_acquire); m_parent->End(this); } m_state = D3D9_VK_QUERY_ENDED; } return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9Query::GetData(void* pData, DWORD dwSize, DWORD dwGetDataFlags) { D3D9DeviceLock lock = m_parent->LockDevice(); bool flush = dwGetDataFlags & D3DGETDATA_FLUSH; if (unlikely(m_parent->IsDeviceLost())) { return flush ? D3DERR_DEVICELOST : S_FALSE; } if (m_state == D3D9_VK_QUERY_CACHED) { // Query data was already retrieved once. // Use cached query data to prevent having to check the VK event // and having to iterate over the VK queries again if (likely(pData && dwSize)) { if (m_queryType != D3DQUERYTYPE_EVENT) { memcpy(pData, &m_dataCache, dwSize); } else { *static_cast(pData) = true; } } return D3D_OK; } HRESULT hr = this->GetQueryData(pData, dwSize); // If we get S_FALSE and it's not from the fact // they didn't call end, do some flushy stuff... if (flush && hr == S_FALSE && m_state != D3D9_VK_QUERY_BEGUN) { this->NotifyStall(); m_parent->ConsiderFlush(GpuFlushType::ImplicitSynchronization); } return hr; } HRESULT D3D9Query::GetQueryData(void* pData, DWORD dwSize) { // Let the game know that calling end might be a good idea... if (m_state == D3D9_VK_QUERY_BEGUN) return S_FALSE; if (unlikely(!pData && dwSize)) return D3DERR_INVALIDCALL; // The game forgot to even issue the query! // Let's do it for them... // This will issue both the begin, and the end. if (m_state == D3D9_VK_QUERY_INITIAL) this->Issue(D3DISSUE_END); if (m_resetCtr != 0u) return S_FALSE; if (m_queryType == D3DQUERYTYPE_EVENT) { DxvkGpuEventStatus status = m_event[0]->test(); if (status == DxvkGpuEventStatus::Invalid) return D3DERR_INVALIDCALL; bool signaled = status == DxvkGpuEventStatus::Signaled; if (pData != nullptr) *static_cast(pData) = signaled; if (signaled) { m_state = D3D9_VK_QUERY_CACHED; return D3D_OK; } else { return S_FALSE; } } else { std::array queryData = { }; for (uint32_t i = 0; i < MaxGpuQueries && m_query[i] != nullptr; i++) { DxvkGpuQueryStatus status = m_query[i]->getData(queryData[i]); if (status == DxvkGpuQueryStatus::Invalid || status == DxvkGpuQueryStatus::Failed) return D3DERR_INVALIDCALL; if (status == DxvkGpuQueryStatus::Pending) return S_FALSE; } if (pData == nullptr) return D3D_OK; switch (m_queryType) { case D3DQUERYTYPE_VCACHE: m_dataCache.VCache.Pattern = MAKEFOURCC('C', 'A', 'C', 'H'); m_dataCache.VCache.OptMethod = 1; m_dataCache.VCache.CacheSize = 16; m_dataCache.VCache.MagicNumber = 7; break; case D3DQUERYTYPE_OCCLUSION: m_dataCache.Occlusion = DWORD(queryData[0].occlusion.samplesPassed); break; case D3DQUERYTYPE_TIMESTAMP: m_dataCache.Timestamp = queryData[0].timestamp.time; break; case D3DQUERYTYPE_TIMESTAMPDISJOINT: m_dataCache.TimestampDisjoint = queryData[0].timestamp.time < queryData[1].timestamp.time; break; case D3DQUERYTYPE_TIMESTAMPFREQ: m_dataCache.TimestampFreq = GetTimestampQueryFrequency(); break; default: break; } if (likely(pData && dwSize)) memcpy(pData, &m_dataCache, dwSize); m_state = D3D9_VK_QUERY_CACHED; return D3D_OK; } } UINT64 D3D9Query::GetTimestampQueryFrequency() const { Rc device = m_parent->GetDXVKDevice(); Rc adapter = device->adapter(); VkPhysicalDeviceLimits limits = adapter->deviceProperties().limits; return uint64_t(1'000'000'000.0f / limits.timestampPeriod); } void D3D9Query::Begin(DxvkContext* ctx) { switch (m_queryType) { case D3DQUERYTYPE_OCCLUSION: ctx->beginQuery(m_query[0]); break; case D3DQUERYTYPE_TIMESTAMPDISJOINT: ctx->writeTimestamp(m_query[1]); break; default: break; } } void D3D9Query::End(DxvkContext* ctx) { switch (m_queryType) { case D3DQUERYTYPE_TIMESTAMP: case D3DQUERYTYPE_TIMESTAMPDISJOINT: ctx->writeTimestamp(m_query[0]); break; case D3DQUERYTYPE_OCCLUSION: ctx->endQuery(m_query[0]); break; case D3DQUERYTYPE_EVENT: ctx->signalGpuEvent(m_event[0]); break; default: break; } m_resetCtr.fetch_sub(1, std::memory_order_release); } bool D3D9Query::QueryBeginnable(D3DQUERYTYPE QueryType) { return QueryType == D3DQUERYTYPE_OCCLUSION || QueryType == D3DQUERYTYPE_TIMESTAMPDISJOINT; } bool D3D9Query::QueryEndable(D3DQUERYTYPE QueryType) { return QueryBeginnable(QueryType) || QueryType == D3DQUERYTYPE_TIMESTAMP || QueryType == D3DQUERYTYPE_EVENT; } HRESULT D3D9Query::QuerySupported(D3D9DeviceEx* pDevice, D3DQUERYTYPE QueryType) { switch (QueryType) { case D3DQUERYTYPE_VCACHE: if (!pDevice->GetOptions()->supportVCache) return D3DERR_NOTAVAILABLE; return D3D_OK; case D3DQUERYTYPE_EVENT: case D3DQUERYTYPE_OCCLUSION: case D3DQUERYTYPE_TIMESTAMP: case D3DQUERYTYPE_TIMESTAMPDISJOINT: case D3DQUERYTYPE_TIMESTAMPFREQ: return D3D_OK; default: return D3DERR_NOTAVAILABLE; } } } dxvk-2.6.1/src/d3d9/d3d9_query.h000066400000000000000000000041641477473124000162050ustar00rootroot00000000000000#pragma once #include "d3d9_device_child.h" #include "../dxvk/dxvk_context.h" namespace dxvk { enum D3D9_VK_QUERY_STATE : uint32_t { D3D9_VK_QUERY_INITIAL, D3D9_VK_QUERY_BEGUN, D3D9_VK_QUERY_ENDED, D3D9_VK_QUERY_CACHED }; union D3D9_QUERY_DATA { D3DDEVINFO_VCACHE VCache; DWORD Occlusion; UINT64 Timestamp; BOOL TimestampDisjoint; UINT64 TimestampFreq; D3DDEVINFO_D3DVERTEXSTATS VertexStats; }; class D3D9Query : public D3D9DeviceChild { constexpr static uint32_t MaxGpuQueries = 2; constexpr static uint32_t MaxGpuEvents = 1; public: D3D9Query( D3D9DeviceEx* pDevice, D3DQUERYTYPE QueryType); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); D3DQUERYTYPE STDMETHODCALLTYPE GetType() final; DWORD STDMETHODCALLTYPE GetDataSize() final; HRESULT STDMETHODCALLTYPE Issue(DWORD dwIssueFlags) final; HRESULT STDMETHODCALLTYPE GetData(void* pData, DWORD dwSize, DWORD dwGetDataFlags) final; HRESULT GetQueryData(void* pData, DWORD dwSize); void Begin(DxvkContext* ctx); void End(DxvkContext* ctx); static bool QueryBeginnable(D3DQUERYTYPE QueryType); static bool QueryEndable(D3DQUERYTYPE QueryType); static HRESULT QuerySupported(D3D9DeviceEx* pDevice, D3DQUERYTYPE QueryType); bool IsEvent() const { return m_queryType == D3DQUERYTYPE_EVENT; } bool IsStalling() const { return m_stallFlag; } void NotifyEnd() { m_stallMask <<= 1; } void NotifyStall() { m_stallMask |= 1; m_stallFlag |= bit::popcnt(m_stallMask) >= 16; } private: D3DQUERYTYPE m_queryType; D3D9_VK_QUERY_STATE m_state; std::array, MaxGpuQueries> m_query; std::array, MaxGpuEvents> m_event; uint32_t m_stallMask = 0; bool m_stallFlag = false; std::atomic m_resetCtr = { 0u }; D3D9_QUERY_DATA m_dataCache; UINT64 GetTimestampQueryFrequency() const; }; }dxvk-2.6.1/src/d3d9/d3d9_resource.h000066400000000000000000000052731477473124000166710ustar00rootroot00000000000000#pragma once #include "d3d9_device_child.h" #include "../util/com/com_private_data.h" namespace dxvk { template class D3D9Resource : public D3D9DeviceChild { public: D3D9Resource(D3D9DeviceEx* pDevice, D3DPOOL Pool, bool Extended) : D3D9DeviceChild(pDevice) , m_pool ( Pool ) , m_priority ( 0 ) , m_isExtended ( Extended ) { } HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID refguid, const void* pData, DWORD SizeOfData, DWORD Flags) final { HRESULT hr; if (Flags & D3DSPD_IUNKNOWN) { if(unlikely(SizeOfData != sizeof(IUnknown*))) return D3DERR_INVALIDCALL; IUnknown* unknown = const_cast( reinterpret_cast(pData)); hr = m_privateData.setInterface( refguid, unknown); } else hr = m_privateData.setData( refguid, SizeOfData, pData); if (unlikely(FAILED(hr))) return D3DERR_INVALIDCALL; return D3D_OK; } HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID refguid, void* pData, DWORD* pSizeOfData) final { if (unlikely(pData == nullptr && pSizeOfData == nullptr)) return D3DERR_NOTFOUND; HRESULT hr = m_privateData.getData( refguid, reinterpret_cast(pSizeOfData), pData); if (unlikely(FAILED(hr))) { if(hr == DXGI_ERROR_MORE_DATA) return D3DERR_MOREDATA; else if (hr == DXGI_ERROR_NOT_FOUND) return D3DERR_NOTFOUND; else return D3DERR_INVALIDCALL; } return D3D_OK; } HRESULT STDMETHODCALLTYPE FreePrivateData(REFGUID refguid) final { HRESULT hr = m_privateData.setData(refguid, 0, nullptr); if (unlikely(FAILED(hr))) return D3DERR_INVALIDCALL; return D3D_OK; } DWORD STDMETHODCALLTYPE SetPriority(DWORD PriorityNew) { // Priority can only be set for D3DPOOL_MANAGED resources on // D3D9 interfaces, and for D3DPOOL_DEFAULT on D3D9Ex interfaces if (likely((m_pool == D3DPOOL_MANAGED && !m_isExtended) || (m_pool == D3DPOOL_DEFAULT && m_isExtended))) { DWORD oldPriority = m_priority; m_priority = PriorityNew; return oldPriority; } return m_priority; } DWORD STDMETHODCALLTYPE GetPriority() { return m_priority; } protected: const D3DPOOL m_pool; DWORD m_priority; private: const bool m_isExtended; ComPrivateData m_privateData; }; }dxvk-2.6.1/src/d3d9/d3d9_shader.cpp000066400000000000000000000112671477473124000166430ustar00rootroot00000000000000#include "d3d9_shader.h" #include "d3d9_caps.h" #include "d3d9_device.h" #include "d3d9_util.h" namespace dxvk { D3D9CommonShader::D3D9CommonShader() {} D3D9CommonShader::D3D9CommonShader( D3D9DeviceEx* pDevice, VkShaderStageFlagBits ShaderStage, const DxvkShaderKey& Key, const DxsoModuleInfo* pDxsoModuleInfo, const void* pShaderBytecode, const DxsoAnalysisInfo& AnalysisInfo, DxsoModule* pModule) { const uint32_t bytecodeLength = AnalysisInfo.bytecodeByteLength; const std::string name = Key.toString(); Logger::debug(str::format("Compiling shader ", name)); // If requested by the user, dump both the raw DXBC // shader and the compiled SPIR-V module to a file. const std::string& dumpPath = pDevice->GetOptions()->shaderDumpPath; if (dumpPath.size() != 0) { DxsoReader reader( reinterpret_cast(pShaderBytecode)); reader.store(std::ofstream(str::topath(str::format(dumpPath, "/", name, ".dxso").c_str()).c_str(), std::ios_base::binary | std::ios_base::trunc), bytecodeLength); char comment[2048]; Com blob; HRESULT hr = DisassembleShader( pShaderBytecode, TRUE, comment, &blob); if (SUCCEEDED(hr)) { std::ofstream disassembledOut(str::topath(str::format(dumpPath, "/", name, ".dxso.dis").c_str()).c_str(), std::ios_base::binary | std::ios_base::trunc); disassembledOut.write( reinterpret_cast(blob->GetBufferPointer()), blob->GetBufferSize()); } } // Decide whether we need to create a pass-through // geometry shader for vertex shader stream output const D3D9ConstantLayout& constantLayout = ShaderStage == VK_SHADER_STAGE_VERTEX_BIT ? pDevice->GetVertexConstantLayout() : pDevice->GetPixelConstantLayout(); m_shader = pModule->compile(*pDxsoModuleInfo, name, AnalysisInfo, constantLayout); m_isgn = pModule->isgn(); m_usedSamplers = pModule->usedSamplers(); m_textureTypes = pModule->textureTypes(); // Shift up these sampler bits so we can just // do an or per-draw in the device. // We shift by 17 because 16 ps samplers + 1 dmap (tess) if (ShaderStage == VK_SHADER_STAGE_VERTEX_BIT) m_usedSamplers <<= FirstVSSamplerSlot; m_usedRTs = pModule->usedRTs(); m_info = pModule->info(); m_meta = pModule->meta(); m_constants = pModule->constants(); m_maxDefinedConst = pModule->maxDefinedConstant(); m_shader->setShaderKey(Key); if (dumpPath.size() != 0) { std::ofstream dumpStream( str::topath(str::format(dumpPath, "/", name, ".spv").c_str()).c_str(), std::ios_base::binary | std::ios_base::trunc); m_shader->dump(dumpStream); } pDevice->GetDXVKDevice()->registerShader(m_shader); } void D3D9ShaderModuleSet::GetShaderModule( D3D9DeviceEx* pDevice, D3D9CommonShader* pShaderModule, uint32_t* pLength, VkShaderStageFlagBits ShaderStage, const DxsoModuleInfo* pDxbcModuleInfo, const void* pShaderBytecode) { DxsoReader reader( reinterpret_cast(pShaderBytecode)); DxsoModule module(reader); if (module.info().shaderStage() != ShaderStage) throw DxvkError("GetShaderModule: Bytecode does not match shader stage"); DxsoAnalysisInfo info = module.analyze(); *pLength = info.bytecodeByteLength; DxvkShaderKey lookupKey = DxvkShaderKey( ShaderStage, Sha1Hash::compute(pShaderBytecode, info.bytecodeByteLength)); // Use the shader's unique key for the lookup { std::unique_lock lock(m_mutex); auto entry = m_modules.find(lookupKey); if (entry != m_modules.end()) { *pShaderModule = entry->second; return; } } // This shader has not been compiled yet, so we have to create a // new module. This takes a while, so we won't lock the structure. *pShaderModule = D3D9CommonShader( pDevice, ShaderStage, lookupKey, pDxbcModuleInfo, pShaderBytecode, info, &module); // Insert the new module into the lookup table. If another thread // has compiled the same shader in the meantime, we should return // that object instead and discard the newly created module. { std::unique_lock lock(m_mutex); auto status = m_modules.insert({ lookupKey, *pShaderModule }); if (!status.second) { *pShaderModule = status.first->second; return; } } } } dxvk-2.6.1/src/d3d9/d3d9_shader.h000066400000000000000000000134321477473124000163040ustar00rootroot00000000000000#pragma once #include "d3d9_resource.h" #include "../dxso/dxso_module.h" #include "d3d9_util.h" #include "d3d9_mem.h" #include namespace dxvk { /** * \brief Common shader object * * Stores the compiled SPIR-V shader and the SHA-1 * hash of the original DXBC shader, which can be * used to identify the shader. */ class D3D9CommonShader { public: D3D9CommonShader(); D3D9CommonShader( D3D9DeviceEx* pDevice, VkShaderStageFlagBits ShaderStage, const DxvkShaderKey& Key, const DxsoModuleInfo* pDxbcModuleInfo, const void* pShaderBytecode, const DxsoAnalysisInfo& AnalysisInfo, DxsoModule* pModule); Rc GetShader() const { return m_shader; } std::string GetName() const { return m_shader->debugName(); } const DxsoIsgn& GetIsgn() const { return m_isgn; } const DxsoShaderMetaInfo& GetMeta() const { return m_meta; } const DxsoDefinedConstants& GetConstants() const { return m_constants; } D3D9ShaderMasks GetShaderMask() const { return D3D9ShaderMasks{ m_usedSamplers, m_usedRTs }; } const DxsoProgramInfo& GetInfo() const { return m_info; } uint32_t GetMaxDefinedConstant() const { return m_maxDefinedConst; } VkImageViewType GetImageViewType(uint32_t samplerSlot) const { const uint32_t offset = samplerSlot * 2; const uint32_t mask = 0b11; return static_cast((m_textureTypes >> offset) & mask); } private: DxsoIsgn m_isgn; uint32_t m_usedSamplers; uint32_t m_usedRTs; uint32_t m_textureTypes; DxsoProgramInfo m_info; DxsoShaderMetaInfo m_meta; DxsoDefinedConstants m_constants; uint32_t m_maxDefinedConst; Rc m_shader; }; /** * \brief Common shader interface * * Implements methods for all D3D11*Shader * interfaces and stores the actual shader * module object. */ template class D3D9Shader : public D3D9DeviceChild { public: D3D9Shader( D3D9DeviceEx* pDevice, D3D9MemoryAllocator* pAllocator, const D3D9CommonShader& CommonShader, const void* pShaderBytecode, uint32_t BytecodeLength) : D3D9DeviceChild( pDevice ) , m_shader ( CommonShader ) , m_bytecode ( pAllocator->Alloc(BytecodeLength) ) , m_bytecodeLength ( BytecodeLength ) { m_bytecode.Map(); std::memcpy(m_bytecode.Ptr(), pShaderBytecode, BytecodeLength); m_bytecode.Unmap(); } HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(Base)) { *ppvObject = ref(this); return S_OK; } if (logQueryInterfaceError(__uuidof(Base), riid)) { Logger::warn("D3D9Shader::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE GetFunction(void* pOut, UINT* pSizeOfData) { if (pSizeOfData == nullptr) return D3DERR_INVALIDCALL; if (pOut == nullptr) { *pSizeOfData = m_bytecodeLength; return D3D_OK; } m_bytecode.Map(); uint32_t copyAmount = std::min(*pSizeOfData, m_bytecodeLength); std::memcpy(pOut, m_bytecode.Ptr(), copyAmount); m_bytecode.Unmap(); return D3D_OK; } const D3D9CommonShader* GetCommonShader() const { return &m_shader; } private: D3D9CommonShader m_shader; D3D9Memory m_bytecode; uint32_t m_bytecodeLength; }; // Needs their own classes and not usings for forward decl. class D3D9VertexShader final : public D3D9Shader { public: D3D9VertexShader( D3D9DeviceEx* pDevice, D3D9MemoryAllocator* pAllocator, const D3D9CommonShader& CommonShader, const void* pShaderBytecode, uint32_t BytecodeLength) : D3D9Shader( pDevice, pAllocator, CommonShader, pShaderBytecode, BytecodeLength ) { } }; class D3D9PixelShader final : public D3D9Shader { public: D3D9PixelShader( D3D9DeviceEx* pDevice, D3D9MemoryAllocator* pAllocator, const D3D9CommonShader& CommonShader, const void* pShaderBytecode, uint32_t BytecodeLength) : D3D9Shader( pDevice, pAllocator, CommonShader, pShaderBytecode, BytecodeLength ) { } }; /** * \brief Shader module set * * Some applications may compile the same shader multiple * times, so we should cache the resulting shader modules * and reuse them rather than creating new ones. This * class is thread-safe. */ class D3D9ShaderModuleSet : public RcObject { public: void GetShaderModule( D3D9DeviceEx* pDevice, D3D9CommonShader* pShaderModule, uint32_t* pLength, VkShaderStageFlagBits ShaderStage, const DxsoModuleInfo* pDxbcModuleInfo, const void* pShaderBytecode); private: dxvk::mutex m_mutex; std::unordered_map< DxvkShaderKey, D3D9CommonShader, DxvkHash, DxvkEq> m_modules; }; template const D3D9CommonShader* GetCommonShader(const T& pShader) { return pShader != nullptr ? pShader->GetCommonShader() : nullptr; } }dxvk-2.6.1/src/d3d9/d3d9_shader_validator.cpp000066400000000000000000000216271477473124000207110ustar00rootroot00000000000000#include "d3d9_shader_validator.h" namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9ShaderValidator::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = ref(this); return S_OK; } HRESULT STDMETHODCALLTYPE D3D9ShaderValidator::Begin( D3D9ShaderValidatorCallback pCallback, void* pUserData, DWORD Unknown) { if (unlikely(m_state != D3D9ShaderValidatorState::Begin)) { return ErrorCallback(nullptr, -1, 0, nullptr, 0, D3D9ShaderValidatorMessage::BeginOutOfOrder, "IDirect3DShaderValidator9::Begin called out of order. ::End must be called first."); } m_callback = pCallback; m_userData = pUserData; m_state = D3D9ShaderValidatorState::ValidatingHeader; return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9ShaderValidator::Instruction( const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw) { if (unlikely(pdwInst == nullptr || !cdw)) { return ErrorCallback(pFile, Line, 0, pdwInst, cdw, D3D9ShaderValidatorMessage::InstructionNullArgs, "IDirect3DShaderValidator9::Instruction called with NULL == pdwInst or 0 == cdw."); } if (unlikely(m_state == D3D9ShaderValidatorState::Begin)) { return ErrorCallback(pFile, Line, 0, pdwInst, cdw, D3D9ShaderValidatorMessage::InstructionOutOfOrder, "IDirect3DShaderValidator9::Instruction called out of order. ::Begin must be called first."); } else if (unlikely(m_state == D3D9ShaderValidatorState::EndOfShader)) { return ErrorCallback(pFile, Line, 0, pdwInst, cdw, D3D9ShaderValidatorMessage::InstructionEndOfShader, "IDirect3DShaderValidator9::Instruction called out of order. After end token there should be no more instructions. Call ::End next."); } else if (unlikely(m_state == D3D9ShaderValidatorState::Error)) { return E_FAIL; } if (m_state == D3D9ShaderValidatorState::ValidatingHeader) return ValidateHeader(pFile, Line, pdwInst, cdw); DxsoCodeIter pdwInstIter{ reinterpret_cast(pdwInst) }; bool isEndToken = !m_ctx->decodeInstruction(pdwInstIter); const DxsoInstructionContext instContext = m_ctx->getInstructionContext(); if (isEndToken) return ValidateEndToken(pFile, Line, pdwInst, cdw); // TODO: DxsoDecodeContext::decodeInstructionLength() does not currently appear // to return the correct token length in many cases, and as such dwordLength // will not be equal to cdw in many situations that are expected to pass validation // /*Logger::debug(str::format("IDirect3DShaderValidator9::Instruction: opcode ", instContext.instruction.opcode)); // + 1 to account for the opcode... uint32_t dwordLength = instContext.instruction.tokenLength + 1; Logger::debug(str::format("IDirect3DShaderValidator9::Instruction: cdw ", cdw)); Logger::debug(str::format("IDirect3DShaderValidator9::Instruction: dwordLength ", dwordLength)); if (unlikely(cdw != dwordLength)) { return ErrorCallback(pFile, Line, 0x2, D3D9ShaderValidatorMessage::BadInstructionLength, str::format("Instruction length specified for instruction (", cdw, ") does not match the token count encountered (", dwordLength, "). Aborting validation.")); }*/ // a maximum of 10 inputs are supported with PS 3.0 (validation required by The Void) if (m_isPixelShader && m_majorVersion == 3) { switch (instContext.instruction.opcode) { case DxsoOpcode::Comment: case DxsoOpcode::Def: case DxsoOpcode::DefB: case DxsoOpcode::DefI: break; default: // Iterate over register tokens. Bit 31 of register tokens is always 1. for (uint32_t instNum = 1; instNum < cdw && (pdwInst[instNum] >> 31); instNum++) { DWORD regType = ((pdwInst[instNum] & D3DSP_REGTYPE_MASK) >> D3DSP_REGTYPE_SHIFT) | ((pdwInst[instNum] & D3DSP_REGTYPE_MASK2) >> D3DSP_REGTYPE_SHIFT2); DWORD regIndex = pdwInst[instNum] & D3DSP_REGNUM_MASK; if (unlikely(regType == static_cast(DxsoRegisterType::Input) && regIndex >= 10)) { return ErrorCallback(pFile, Line, 0x2, pdwInst, cdw, instContext.instruction.opcode == DxsoOpcode::Dcl ? D3D9ShaderValidatorMessage::BadInputRegisterDeclaration : D3D9ShaderValidatorMessage::BadInputRegister, str::format("IDirect3DShaderValidator9::Instruction: PS input registers index #", regIndex, " not valid for operand ", instNum, ".")); } } } } return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D9ShaderValidator::End() { if (unlikely(m_state == D3D9ShaderValidatorState::Error)) { return E_FAIL; } else if (unlikely(m_state == D3D9ShaderValidatorState::Begin)) { return ErrorCallback(nullptr, 0, 0, nullptr, 0, D3D9ShaderValidatorMessage::EndOutOfOrder, "IDirect3DShaderValidator9::End called out of order. Call to ::Begin, followed by calls to ::Instruction must occur first."); } else if (unlikely(m_state != D3D9ShaderValidatorState::EndOfShader)) { return ErrorCallback(nullptr, 0, 0, nullptr, 0, D3D9ShaderValidatorMessage::MissingEndToken, "IDirect3DShaderValidator9::End: Shader missing end token."); } m_state = D3D9ShaderValidatorState::Begin; m_isPixelShader = false; m_majorVersion = 0; m_minorVersion = 0; m_callback = nullptr; m_userData = nullptr; m_ctx = nullptr; return D3D_OK; } HRESULT D3D9ShaderValidator::ValidateHeader(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw) { if (unlikely(cdw != 1)) { return ErrorCallback(pFile, Line, 0x6, pdwInst, cdw, D3D9ShaderValidatorMessage::BadVersionTokenLength, "IDirect3DShaderValidator9::Instruction: Bad version token. DWORD count > 1 given. Expected DWORD count to be 1 for version token."); } DxsoReader reader = { reinterpret_cast(pdwInst) }; uint32_t headerToken = reader.readu32(); uint32_t shaderType = headerToken & 0xffff0000; DxsoProgramType programType; if (shaderType == 0xffff0000) { // Pixel Shader programType = DxsoProgramTypes::PixelShader; m_isPixelShader = true; } else if (shaderType == 0xfffe0000) { // Vertex Shader programType = DxsoProgramTypes::VertexShader; m_isPixelShader = false; } else { return ErrorCallback(pFile, Line, 0x6, pdwInst, cdw, D3D9ShaderValidatorMessage::BadVersionTokenType, "IDirect3DShaderValidator9::Instruction: Bad version token. It indicates neither a pixel shader nor a vertex shader."); } m_majorVersion = D3DSHADER_VERSION_MAJOR(headerToken); m_minorVersion = D3DSHADER_VERSION_MINOR(headerToken); m_ctx = std::make_unique(DxsoProgramInfo{ programType, m_minorVersion, m_majorVersion }); m_state = D3D9ShaderValidatorState::ValidatingInstructions; const char* shaderTypeOutput = m_isPixelShader ? "PS" : "VS"; Logger::debug(str::format("IDirect3DShaderValidator9::Instruction: Validating ", shaderTypeOutput, " version ", m_majorVersion, ".", m_minorVersion)); return D3D_OK; } HRESULT D3D9ShaderValidator::ValidateEndToken(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw) { // Reached the end token. if (unlikely(cdw != 1)) { return ErrorCallback(pFile, Line, 0x6, pdwInst, cdw, D3D9ShaderValidatorMessage::BadEndToken, "IDirect3DShaderValidator9::Instruction: Bad end token. DWORD count > 1 given. Expected DWORD count to be 1 for end token."); } m_state = D3D9ShaderValidatorState::EndOfShader; return D3D_OK; } HRESULT D3D9ShaderValidator::ErrorCallback( const char* pFile, UINT Line, DWORD Unknown, const DWORD* pInstr, DWORD InstrLength, D3D9ShaderValidatorMessage MessageID, const std::string& Message) { if (m_callback) m_callback(pFile, Line, Unknown, MessageID, Message.c_str(), m_userData); // TODO: Consider switching this to debug, once we're // confident the implementation doesn't cause any issues Logger::warn(Message); // Log instruction that caused the error as raw bytecode if (Logger::logLevel() <= LogLevel::Debug && pInstr && InstrLength) { std::stringstream instMsg; for (uint32_t i = 0; i < InstrLength; i++) { instMsg << (i ? "," : " ["); instMsg << std::hex << std::setfill('0') << std::setw(8) << pInstr[i]; instMsg << (i + 1 == InstrLength ? "]" : ""); } Logger::debug(instMsg.str()); } m_state = D3D9ShaderValidatorState::Error; return E_FAIL; } }dxvk-2.6.1/src/d3d9/d3d9_shader_validator.h000066400000000000000000000060031477473124000203450ustar00rootroot00000000000000#pragma once #include "d3d9_include.h" #include "../dxso/dxso_header.h" #include "../dxso/dxso_decoder.h" namespace dxvk { enum class D3D9ShaderValidatorMessage : uint32_t { BeginOutOfOrder = 0xeb, InstructionOutOfOrder = 0xec, InstructionEndOfShader = 0xed, InstructionNullArgs = 0xee, BadVersionTokenLength = 0xef, BadVersionTokenType = 0xf0, BadEndToken = 0xf1, EndOutOfOrder = 0xf2, MissingEndToken = 0xf3, BadInputRegisterDeclaration = 0x12c, BadInputRegister = 0x167, BadInstructionLength = 0x21e, }; enum class D3D9ShaderValidatorState { Begin, ValidatingHeader, ValidatingInstructions, EndOfShader, Error, }; using D3D9ShaderValidatorCallback = HRESULT(STDMETHODCALLTYPE *)( const char* pFile, UINT Line, DWORD Unknown, D3D9ShaderValidatorMessage MessageID, const char* pMessage, void* pUserData); class IDirect3DShaderValidator9 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Begin( D3D9ShaderValidatorCallback pCallback, void* pUserParam, DWORD Unknown) = 0; virtual HRESULT STDMETHODCALLTYPE Instruction( const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw) = 0; virtual HRESULT STDMETHODCALLTYPE End() = 0; }; class D3D9ShaderValidator final : public ComObjectClamp { public: HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE Begin( D3D9ShaderValidatorCallback pCallback, void* pUserData, DWORD Unknown); HRESULT STDMETHODCALLTYPE Instruction( const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw); HRESULT STDMETHODCALLTYPE End(); private: HRESULT ValidateHeader(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw); HRESULT ValidateEndToken(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw); HRESULT ErrorCallback( const char* pFile, UINT Line, DWORD Unknown, const DWORD* pInstr, DWORD InstrLength, D3D9ShaderValidatorMessage MessageID, const std::string& Message); bool m_isPixelShader = false; uint32_t m_majorVersion = 0; uint32_t m_minorVersion = 0; D3D9ShaderValidatorState m_state = D3D9ShaderValidatorState::Begin; D3D9ShaderValidatorCallback m_callback = nullptr; void* m_userData = nullptr; std::unique_ptr m_ctx; }; }dxvk-2.6.1/src/d3d9/d3d9_spec_constants.h000066400000000000000000000122021477473124000200560ustar00rootroot00000000000000#pragma once #include #include #include "../spirv/spirv_module.h" class D3D9DeviceEx; namespace dxvk { enum D3D9SpecConstantId : uint32_t { SpecSamplerType, // 2 bits for 16 PS samplers | Bits: 32 SpecSamplerDepthMode, // 1 bit for 21 VS + PS samplers | Bits: 21 SpecAlphaCompareOp, // Range: 0 -> 7 | Bits: 3 SpecPointMode, // Range: 0 -> 3 | Bits: 2 SpecVertexFogMode, // Range: 0 -> 3 | Bits: 2 SpecPixelFogMode, // Range: 0 -> 3 | Bits: 2 SpecFogEnabled, // Range: 0 -> 1 | Bits: 1 SpecSamplerNull, // 1 bit for 21 samplers | Bits: 21 SpecProjectionType, // 1 bit for 6 PS 1.x samplers | Bits: 6 SpecAlphaPrecisionBits, // Range: 0 -> 8 or 0xF | Bits: 4 SpecVertexShaderBools, // 16 bools | Bits: 16 SpecPixelShaderBools, // 16 bools | Bits: 16 SpecDrefClamp, // 1 bit for 16 PS samplers | Bits: 16 SpecFetch4, // 1 bit for 16 PS samplers | Bits: 16 SpecClipPlaneCount, // 3 bits for 6 clip planes | Bits : 3 SpecConstantCount, }; struct BitfieldPosition { constexpr uint32_t mask() const { return uint32_t((1ull << sizeInBits) - 1) << bitOffset; } uint32_t dwordOffset; uint32_t bitOffset; uint32_t sizeInBits; }; struct D3D9SpecializationInfo { static constexpr uint32_t MaxSpecDwords = 6; static constexpr uint32_t MaxUBODwords = 5; static constexpr size_t UBOSize = MaxUBODwords * sizeof(uint32_t); static constexpr std::array Layout{{ { 0, 0, 32 }, // SamplerType { 1, 0, 21 }, // SamplerDepthMode { 1, 21, 3 }, // AlphaCompareOp { 1, 24, 2 }, // PointMode { 1, 26, 2 }, // VertexFogMode { 1, 28, 2 }, // PixelFogMode { 1, 30, 1 }, // FogEnabled { 2, 0, 21 }, // SamplerNull { 2, 21, 6 }, // ProjectionType { 2, 27, 4 }, // AlphaPrecisionBits { 3, 0, 16 }, // VertexShaderBools { 3, 16, 16 }, // PixelShaderBools { 4, 0, 16 }, // DrefClamp { 4, 16, 16 }, // Fetch4 { 5, 0, 3 }, // ClipPlaneCount }}; template bool set(const T& value) { const uint32_t x = uint32_t(value); if (get() == x) return false; constexpr auto& layout = Layout[Id]; data[layout.dwordOffset] &= ~layout.mask(); data[layout.dwordOffset] |= (x << layout.bitOffset) & layout.mask(); return true; } template uint32_t get() const { constexpr auto& layout = Layout[Id]; return (data[layout.dwordOffset] & layout.mask()) >> layout.bitOffset; } std::array data = {}; }; class D3D9ShaderSpecConstantManager { public: uint32_t get(SpirvModule &module, uint32_t specUbo, D3D9SpecConstantId id) { return get(module, specUbo, id, 0, 32); } uint32_t get(SpirvModule &module, uint32_t specUbo, D3D9SpecConstantId id, uint32_t bitOffset, uint32_t bitCount, uint32_t uboOverride = 0) { const auto &layout = D3D9SpecializationInfo::Layout[id]; uint32_t uintType = module.defIntType(32, 0); uint32_t optimized = getOptimizedBool(module); uint32_t quickValue = uboOverride ? uboOverride : getSpecUBODword(module, specUbo, layout.dwordOffset); uint32_t optimizedValue = getSpecConstDword(module, layout.dwordOffset); uint32_t val = module.opSelect(uintType, optimized, optimizedValue, quickValue); bitCount = std::min(bitCount, layout.sizeInBits - bitOffset); if (bitCount == 32) return val; return module.opBitFieldUExtract( module.defIntType(32, 0), val, module.consti32(bitOffset + layout.bitOffset), module.consti32(bitCount)); } private: uint32_t getSpecConstDword(SpirvModule &module, uint32_t idx) { if (!m_specConstantIds[idx]) { m_specConstantIds[idx] = module.specConst32(module.defIntType(32, 0), 0); module.decorateSpecId(m_specConstantIds[idx], idx); } return m_specConstantIds[idx]; } uint32_t getSpecUBODword(SpirvModule& module, uint32_t specUbo, uint32_t idx) { uint32_t uintType = module.defIntType(32, 0); uint32_t uintPtr = module.defPointerType(uintType, spv::StorageClassUniform); uint32_t member = module.constu32(idx); uint32_t dword = module.opLoad(uintType, module.opAccessChain(uintPtr, specUbo, 1, &member)); return dword; } uint32_t getOptimizedBool(SpirvModule& module) { uint32_t boolType = module.defBoolType(); // The spec constant at MaxNumSpecConstants is set to True // when this is an optimized pipeline. uint32_t optimized = getSpecConstDword(module, MaxNumSpecConstants); optimized = module.opINotEqual(boolType, optimized, module.constu32(0)); return optimized; } std::array m_specConstantIds = {}; }; }dxvk-2.6.1/src/d3d9/d3d9_state.cpp000066400000000000000000000013231477473124000165050ustar00rootroot00000000000000#include "d3d9_state.h" #include "d3d9_texture.h" namespace dxvk { template