mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-02-27 04:54:15 +01:00
Merge branch 'master' into filter-uuid
This commit is contained in:
commit
1e5555374b
37
.github/workflows/artifacts.yml
vendored
37
.github/workflows/artifacts.yml
vendored
@ -9,13 +9,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
id: checkout-code
|
id: checkout-code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup problem matcher
|
- name: Setup problem matcher
|
||||||
uses: Joshua-Ashton/gcc-problem-matcher@v2
|
uses: Joshua-Ashton/gcc-problem-matcher@v3
|
||||||
|
|
||||||
- name: Build release
|
- name: Build release
|
||||||
id: build-release
|
id: build-release
|
||||||
@ -28,9 +28,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
id: upload-artifacts
|
id: upload-artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: dxvk-${{ env.VERSION_NAME }}
|
name: dxvk-win-${{ env.VERSION_NAME }}
|
||||||
path: build/dxvk-${{ env.VERSION_NAME }}
|
path: build/dxvk-${{ env.VERSION_NAME }}
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
@ -41,26 +41,43 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
id: checkout-code
|
id: checkout-code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup problem matcher
|
- name: Setup problem matcher
|
||||||
uses: Joshua-Ashton/gcc-problem-matcher@v2
|
uses: Joshua-Ashton/gcc-problem-matcher@v3
|
||||||
|
|
||||||
- name: Build release
|
- name: Build release
|
||||||
id: build-release
|
id: build-release
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
export VERSION_NAME="${GITHUB_REF##*/}-${GITHUB_SHA##*/}"
|
export VERSION_NAME="${GITHUB_REF##*/}-${GITHUB_SHA##*/}"
|
||||||
./package-native.sh ${VERSION_NAME} build --no-package
|
./package-native.sh ${VERSION_NAME} build
|
||||||
echo "VERSION_NAME=${VERSION_NAME}" >> $GITHUB_ENV
|
echo "VERSION_NAME=${VERSION_NAME}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
id: upload-artifacts
|
id: upload-artifacts
|
||||||
uses: actions/upload-artifact@v3
|
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-20.04
|
||||||
|
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:
|
with:
|
||||||
name: dxvk-${{ env.VERSION_NAME }}
|
name: dxvk-${{ env.VERSION_NAME }}
|
||||||
path: build/dxvk-native-${{ env.VERSION_NAME }}
|
pattern: dxvk*
|
||||||
if-no-files-found: error
|
delete-merged: true
|
||||||
|
9
.github/workflows/test-build-windows.yml
vendored
9
.github/workflows/test-build-windows.yml
vendored
@ -9,7 +9,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
id: checkout-code
|
id: checkout-code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
@ -32,6 +32,13 @@ jobs:
|
|||||||
Write-Output "VSDEVCMD=${installationPath}\Common7\Tools\VsDevCmd.bat" `
|
Write-Output "VSDEVCMD=${installationPath}\Common7\Tools\VsDevCmd.bat" `
|
||||||
| Out-File -FilePath "${Env:GITHUB_ENV}" -Append
|
| 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: Build MSVC x86
|
- name: Build MSVC x86
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,5 +1,7 @@
|
|||||||
Copyright (c) 2017 Philip Rebohle
|
Copyright (c) 2017 Philip Rebohle
|
||||||
Copyright (c) 2019 Joshua Ashton
|
Copyright (c) 2019 Joshua Ashton
|
||||||
|
Copyright (c) 2019 Robin Kertels
|
||||||
|
Copyright (c) 2023 Jeffrey Ellison
|
||||||
|
|
||||||
zlib/libpng license
|
zlib/libpng license
|
||||||
|
|
||||||
|
171
README.md
171
README.md
@ -1,6 +1,6 @@
|
|||||||
# DXVK
|
# DXVK
|
||||||
|
|
||||||
A Vulkan-based translation layer for Direct3D 9/10/11 which allows running 3D applications on Linux using Wine.
|
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).
|
For the current status of the project, please refer to the [project wiki](https://github.com/doitsujin/dxvk/wiki).
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ The most recent development builds can be found [here](https://github.com/doitsu
|
|||||||
Release builds can be found [here](https://github.com/doitsujin/dxvk/releases).
|
Release builds can be found [here](https://github.com/doitsujin/dxvk/releases).
|
||||||
|
|
||||||
## How to use
|
## 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 DLL overrides for `d3d11`, `d3d10core`, `dxgi`, and `d3d9`.
|
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 DLL overrides for `d3d8`, `d3d9`, `d3d10core`, `d3d11` and `dxgi`.
|
||||||
|
|
||||||
In a default Wine prefix that would be as follows:
|
In a default Wine prefix that would be as follows:
|
||||||
```
|
```
|
||||||
@ -32,9 +32,84 @@ In order to remove DXVK from a prefix, remove the DLLs and DLL overrides, and ru
|
|||||||
|
|
||||||
Tools such as Steam Play, Lutris, Bottles, Heroic Launcher, etc will automatically handle setup of dxvk on their own when enabled.
|
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
|
### 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.
|
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.
|
||||||
|
- `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]*
|
||||||
|
- `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
|
## Build instructions
|
||||||
|
|
||||||
In order to pull in all submodules that are needed for building, clone the repository using the following command:
|
In order to pull in all submodules that are needed for building, clone the repository using the following command:
|
||||||
@ -42,8 +117,6 @@ In order to pull in all submodules that are needed for building, clone the repos
|
|||||||
git clone --recursive https://github.com/doitsujin/dxvk.git
|
git clone --recursive https://github.com/doitsujin/dxvk.git
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Requirements:
|
### Requirements:
|
||||||
- [wine 7.1](https://www.winehq.org/) or newer
|
- [wine 7.1](https://www.winehq.org/) or newer
|
||||||
- [Meson](https://mesonbuild.com/) build system (at least version 0.49)
|
- [Meson](https://mesonbuild.com/) build system (at least version 0.49)
|
||||||
@ -76,76 +149,9 @@ cd build.w64
|
|||||||
ninja install
|
ninja install
|
||||||
```
|
```
|
||||||
|
|
||||||
The D3D9, D3D10, D3D11 and DXGI DLLs will be located in `/your/dxvk/directory/bin`. Setup has to be done manually in this case.
|
The D3D8, D3D9, D3D10, D3D11 and DXGI DLLs will be located in `/your/dxvk/directory/bin`.
|
||||||
|
|
||||||
### Online multi-player games
|
### Build troubleshooting
|
||||||
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.**
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
- `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]*
|
|
||||||
- `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.
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
### 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`
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
DXVK requires threading support from your mingw-w64 build environment. If you
|
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"
|
are missing this, you may see "error: ‘std::cv_status’ has not been declared"
|
||||||
or similar threading related errors.
|
or similar threading related errors.
|
||||||
@ -163,3 +169,22 @@ 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
|
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
|
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.
|
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 `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.
|
||||||
|
98
dxvk.conf
98
dxvk.conf
@ -1,3 +1,10 @@
|
|||||||
|
# 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)
|
# Expose the HDR10 ColorSpace (DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020)
|
||||||
# to the application by default.
|
# to the application by default.
|
||||||
# This shows to the game that the global Windows 'HDR Mode' is enabled.
|
# This shows to the game that the global Windows 'HDR Mode' is enabled.
|
||||||
@ -10,6 +17,7 @@
|
|||||||
|
|
||||||
# dxgi.enableHDR = True
|
# dxgi.enableHDR = True
|
||||||
|
|
||||||
|
|
||||||
# Create the VkSurface on the first call to IDXGISwapChain::Present,
|
# Create the VkSurface on the first call to IDXGISwapChain::Present,
|
||||||
# rather than when creating the swap chain. Some games that start
|
# rather than when creating the swap chain. Some games that start
|
||||||
# rendering with a different graphics API may require this option,
|
# rendering with a different graphics API may require this option,
|
||||||
@ -35,7 +43,12 @@
|
|||||||
# bugs in games that have physics or other simulation tied to their frame
|
# bugs in games that have physics or other simulation tied to their frame
|
||||||
# rate, but do not provide their own limiter.
|
# rate, but do not provide their own limiter.
|
||||||
#
|
#
|
||||||
# Supported values : Any non-negative integer
|
# 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
|
# dxgi.maxFrameRate = 0
|
||||||
# d3d9.maxFrameRate = 0
|
# d3d9.maxFrameRate = 0
|
||||||
@ -62,7 +75,7 @@
|
|||||||
|
|
||||||
|
|
||||||
# Report Nvidia GPUs as AMD GPUs. Unless NVAPI support is explicitly
|
# Report Nvidia GPUs as AMD GPUs. Unless NVAPI support is explicitly
|
||||||
# enabled through proton, this is done by default in order to work
|
# enabled through Proton, this is done by default in order to work
|
||||||
# around crashes or low performance with Nvidia-speciic code paths
|
# around crashes or low performance with Nvidia-speciic code paths
|
||||||
# in games, especially Unreal Engine.
|
# in games, especially Unreal Engine.
|
||||||
#
|
#
|
||||||
@ -71,6 +84,13 @@
|
|||||||
# dxgi.hideNvidiaGpu = Auto
|
# 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
|
# 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.
|
# known to have issues with AMDAGS or other AMD-specific code paths.
|
||||||
#
|
#
|
||||||
@ -90,6 +110,7 @@
|
|||||||
# Override maximum amount of device memory and shared system memory
|
# Override maximum amount of device memory and shared system memory
|
||||||
# reported to the application. This may fix texture streaming issues
|
# reported to the application. This may fix texture streaming issues
|
||||||
# in games that do not support cards with large amounts of VRAM.
|
# 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.
|
# Supported values: Any number in Megabytes.
|
||||||
|
|
||||||
@ -292,6 +313,32 @@
|
|||||||
# d3d11.enableContextLock = 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.
|
# Sets number of pipeline compiler threads.
|
||||||
#
|
#
|
||||||
# If the graphics pipeline library feature is enabled, the given
|
# If the graphics pipeline library feature is enabled, the given
|
||||||
@ -479,6 +526,7 @@
|
|||||||
# Supported values:
|
# Supported values:
|
||||||
# - True/False
|
# - True/False
|
||||||
|
|
||||||
|
# d3d11.longMad = False
|
||||||
# d3d9.longMad = False
|
# d3d9.longMad = False
|
||||||
|
|
||||||
# Device Local Constant Buffers
|
# Device Local Constant Buffers
|
||||||
@ -494,7 +542,9 @@
|
|||||||
|
|
||||||
# Support DF formats
|
# Support DF formats
|
||||||
#
|
#
|
||||||
# Support the vendor extension DF floating point depth 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:
|
# Supported values:
|
||||||
# - True/False
|
# - True/False
|
||||||
@ -520,14 +570,16 @@
|
|||||||
|
|
||||||
# d3d9.supportX4R4G4B4 = True
|
# d3d9.supportX4R4G4B4 = True
|
||||||
|
|
||||||
# Support D32
|
# Support D16_LOCKABLE
|
||||||
#
|
#
|
||||||
# Support the D32 format.
|
# 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:
|
# Supported values:
|
||||||
# - True/False
|
# - True/False
|
||||||
|
|
||||||
# d3d9.supportD32 = True
|
# d3d9.supportD16Lockable = False
|
||||||
|
|
||||||
# Disable A8 as a Render Target
|
# Disable A8 as a Render Target
|
||||||
#
|
#
|
||||||
@ -623,3 +675,37 @@
|
|||||||
# DO NOT CHANGE THIS UNLESS YOU HAVE A VERY GOOD REASON.
|
# DO NOT CHANGE THIS UNLESS YOU HAVE A VERY GOOD REASON.
|
||||||
|
|
||||||
# d3d9.textureMemory = 100
|
# 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
|
||||||
|
19
include/native/meson.build
Normal file
19
include/native/meson.build
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
install_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_sdl2.h',
|
||||||
|
'wsi/native_glfw.h',
|
||||||
|
subdir: 'dxvk/wsi',
|
||||||
|
)
|
@ -48,7 +48,6 @@ typedef const void* LPCVOID;
|
|||||||
typedef size_t SIZE_T;
|
typedef size_t SIZE_T;
|
||||||
|
|
||||||
typedef int8_t INT8;
|
typedef int8_t INT8;
|
||||||
|
|
||||||
typedef uint8_t UINT8;
|
typedef uint8_t UINT8;
|
||||||
typedef uint8_t BYTE;
|
typedef uint8_t BYTE;
|
||||||
|
|
||||||
@ -56,9 +55,13 @@ typedef int16_t SHORT;
|
|||||||
typedef uint16_t USHORT;
|
typedef uint16_t USHORT;
|
||||||
|
|
||||||
typedef int64_t LONGLONG;
|
typedef int64_t LONGLONG;
|
||||||
|
typedef int64_t INT64;
|
||||||
|
|
||||||
typedef uint64_t ULONGLONG;
|
typedef uint64_t ULONGLONG;
|
||||||
|
typedef uint64_t UINT64;
|
||||||
|
|
||||||
typedef intptr_t LONG_PTR;
|
typedef intptr_t LONG_PTR;
|
||||||
|
typedef uintptr_t ULONG_PTR;
|
||||||
|
|
||||||
typedef float FLOAT;
|
typedef float FLOAT;
|
||||||
|
|
||||||
@ -330,12 +333,21 @@ typedef struct RGNDATA {
|
|||||||
#define DECLARE_INTERFACE(x) struct x
|
#define DECLARE_INTERFACE(x) struct x
|
||||||
#define DECLARE_INTERFACE_(x, y) struct x : public y
|
#define DECLARE_INTERFACE_(x, y) struct x : public y
|
||||||
#else
|
#else
|
||||||
|
#ifdef CONST_VTABLE
|
||||||
#define DECLARE_INTERFACE(x) \
|
#define DECLARE_INTERFACE(x) \
|
||||||
typedef interface x { \
|
typedef interface x { \
|
||||||
const struct x##Vtbl *lpVtbl; \
|
const struct x##Vtbl *lpVtbl; \
|
||||||
} x; \
|
} x; \
|
||||||
typedef const struct x##Vtbl x##Vtbl; \
|
typedef const struct x##Vtbl x##Vtbl; \
|
||||||
const struct 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)
|
#define DECLARE_INTERFACE_(x, y) DECLARE_INTERFACE(x)
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
namespace dxvk::wsi {
|
namespace dxvk::wsi {
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 0bcc624926a25a2a273d07877fd25a6ff5ba1cfb
|
Subproject commit 8b246ff75c6615ba4532fe4fde20f1be090c3764
|
@ -1 +1 @@
|
|||||||
Subproject commit 85c2334e92e215cce34e8e0ed8b2dce4700f4a50
|
Subproject commit 46dc0f6e514f5730784bb2cac2a7c731636839e8
|
75
meson.build
75
meson.build
@ -1,11 +1,13 @@
|
|||||||
project('dxvk', ['c', 'cpp'], version : 'v2.2', meson_version : '>= 0.49', default_options : [ 'cpp_std=c++17', 'warning_level=2' ])
|
project('dxvk', ['c', 'cpp'], version : 'v2.4.0', meson_version : '>= 0.58', default_options : [ 'cpp_std=c++17', 'warning_level=2' ])
|
||||||
|
|
||||||
|
pkg = import('pkgconfig')
|
||||||
cpu_family = target_machine.cpu_family()
|
cpu_family = target_machine.cpu_family()
|
||||||
platform = target_machine.system()
|
platform = target_machine.system()
|
||||||
|
fs = import('fs')
|
||||||
|
|
||||||
cpp = meson.get_compiler('cpp')
|
cpp = meson.get_compiler('cpp')
|
||||||
cc = meson.get_compiler('c')
|
cc = meson.get_compiler('c')
|
||||||
dxvk_is_msvc = cpp.get_id() == 'msvc'
|
dxvk_is_msvc = cpp.get_argument_syntax() == 'msvc'
|
||||||
|
|
||||||
compiler_args = [
|
compiler_args = [
|
||||||
'-msse',
|
'-msse',
|
||||||
@ -33,21 +35,34 @@ if get_option('build_id')
|
|||||||
]
|
]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
dxvk_include_dirs = [
|
dxvk_include_dirs = ['./include']
|
||||||
'./include',
|
if fs.is_dir('./include/vulkan/include')
|
||||||
'./include/vulkan/include',
|
dxvk_include_dirs += ['./include/vulkan/include']
|
||||||
'./include/spirv/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
|
||||||
|
|
||||||
proj_displayinfo = subproject('libdisplay-info')
|
dep_displayinfo = dependency(
|
||||||
dep_displayinfo = proj_displayinfo.get_variable('di_dep')
|
'libdisplay-info',
|
||||||
|
version: ['>= 0.0.0', '< 0.2.0'],
|
||||||
|
fallback: ['libdisplay-info', 'di_dep'],
|
||||||
|
default_options: ['default_library=static'],
|
||||||
|
)
|
||||||
|
|
||||||
if platform == 'windows'
|
if platform == 'windows'
|
||||||
|
dxvk_so_version = {'name_prefix': ''}
|
||||||
|
|
||||||
compiler_args += [
|
compiler_args += [
|
||||||
'-DNOMINMAX',
|
'-DNOMINMAX',
|
||||||
'-D_WIN32_WINNT=0xa00',
|
'-D_WIN32_WINNT=0xa00',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if not dxvk_is_msvc
|
||||||
link_args += [
|
link_args += [
|
||||||
'-static',
|
'-static',
|
||||||
'-static-libgcc',
|
'-static-libgcc',
|
||||||
@ -71,6 +86,11 @@ if platform == 'windows'
|
|||||||
'-Wl,--kill-at',
|
'-Wl,--kill-at',
|
||||||
]
|
]
|
||||||
endif
|
endif
|
||||||
|
else
|
||||||
|
link_args += [
|
||||||
|
'/FILEALIGN:4096',
|
||||||
|
]
|
||||||
|
endif
|
||||||
|
|
||||||
lib_d3d9 = cpp.find_library('d3d9')
|
lib_d3d9 = cpp.find_library('d3d9')
|
||||||
lib_d3d11 = cpp.find_library('d3d11')
|
lib_d3d11 = cpp.find_library('d3d11')
|
||||||
@ -98,10 +118,21 @@ if platform == 'windows'
|
|||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
dxvk_wsi = 'win32'
|
|
||||||
dxvk_name_prefix = ''
|
dxvk_name_prefix = ''
|
||||||
compiler_args += ['-DDXVK_WSI_WIN32']
|
compiler_args += ['-DDXVK_WSI_WIN32']
|
||||||
else
|
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 = find_program('touch')
|
||||||
wrc_generator = generator(wrc, output : [ '@BASENAME@_ignored.h' ], arguments : [ '@OUTPUT@' ] )
|
wrc_generator = generator(wrc, output : [ '@BASENAME@_ignored.h' ], arguments : [ '@OUTPUT@' ] )
|
||||||
|
|
||||||
@ -111,17 +142,20 @@ else
|
|||||||
'./include/native/directx'
|
'./include/native/directx'
|
||||||
]
|
]
|
||||||
|
|
||||||
dxvk_wsi = get_option('dxvk_native_wsi')
|
lib_sdl2 = dependency('SDL2', required: false)
|
||||||
|
lib_glfw = dependency('glfw', required: false)
|
||||||
if dxvk_wsi == 'sdl2'
|
if lib_sdl2.found()
|
||||||
lib_sdl2 = cpp.find_library('SDL2')
|
|
||||||
compiler_args += ['-DDXVK_WSI_SDL2']
|
compiler_args += ['-DDXVK_WSI_SDL2']
|
||||||
elif dxvk_wsi == 'glfw'
|
endif
|
||||||
lib_glfw = cpp.find_library('glfw')
|
if lib_glfw.found()
|
||||||
compiler_args += ['-DDXVK_WSI_GLFW']
|
compiler_args += ['-DDXVK_WSI_GLFW']
|
||||||
endif
|
endif
|
||||||
|
if (not lib_sdl2.found() and not lib_glfw.found())
|
||||||
|
error('SDL2 or GLFW are required to build dxvk-native')
|
||||||
|
endif
|
||||||
|
|
||||||
dxvk_name_prefix = 'libdxvk_'
|
dxvk_name_prefix = 'dxvk_'
|
||||||
|
dxvk_pkg_prefix = 'dxvk-'
|
||||||
|
|
||||||
link_args += [
|
link_args += [
|
||||||
'-static-libgcc',
|
'-static-libgcc',
|
||||||
@ -137,13 +171,12 @@ add_project_link_arguments(cpp.get_supported_link_arguments(link_args), language
|
|||||||
add_project_link_arguments(cc.get_supported_link_arguments(link_args), language: 'c')
|
add_project_link_arguments(cc.get_supported_link_arguments(link_args), language: 'c')
|
||||||
|
|
||||||
exe_ext = ''
|
exe_ext = ''
|
||||||
dll_ext = ''
|
|
||||||
def_spec_ext = '.def'
|
def_spec_ext = '.def'
|
||||||
|
|
||||||
glsl_compiler = find_program('glslang', 'glslangValidator')
|
glsl_compiler = find_program('glslang', 'glslangValidator')
|
||||||
glsl_args = [
|
glsl_args = [
|
||||||
'--quiet',
|
'--quiet',
|
||||||
'--target-env', 'vulkan1.2',
|
'--target-env', 'vulkan1.3',
|
||||||
'--vn', '@BASENAME@',
|
'--vn', '@BASENAME@',
|
||||||
'--depfile', '@DEPFILE@',
|
'--depfile', '@DEPFILE@',
|
||||||
'@INPUT@',
|
'@INPUT@',
|
||||||
@ -162,4 +195,8 @@ dxvk_version = vcs_tag(
|
|||||||
output: 'version.h',
|
output: 'version.h',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if platform != 'windows'
|
||||||
|
subdir('include/native')
|
||||||
|
endif
|
||||||
|
|
||||||
subdir('src')
|
subdir('src')
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
option('enable_dxgi', type : 'boolean', value : true, description: 'Build DXGI')
|
option('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_d3d9', type : 'boolean', value : true, description: 'Build D3D9')
|
||||||
option('enable_d3d10', type : 'boolean', value : true, description: 'Build D3D10')
|
option('enable_d3d10', type : 'boolean', value : true, description: 'Build D3D10')
|
||||||
option('enable_d3d11', type : 'boolean', value : true, description: 'Build D3D11')
|
option('enable_d3d11', type : 'boolean', value : true, description: 'Build D3D11')
|
||||||
|
@ -25,6 +25,8 @@ shift 2
|
|||||||
opt_nopackage=0
|
opt_nopackage=0
|
||||||
opt_devbuild=0
|
opt_devbuild=0
|
||||||
opt_buildid=false
|
opt_buildid=false
|
||||||
|
opt_64_only=0
|
||||||
|
opt_32_only=0
|
||||||
|
|
||||||
CC=${CC:="gcc"}
|
CC=${CC:="gcc"}
|
||||||
CXX=${CXX:="g++"}
|
CXX=${CXX:="g++"}
|
||||||
@ -41,6 +43,12 @@ while [ $# -gt 0 ]; do
|
|||||||
"--build-id")
|
"--build-id")
|
||||||
opt_buildid=true
|
opt_buildid=true
|
||||||
;;
|
;;
|
||||||
|
"--64-only")
|
||||||
|
opt_64_only=1
|
||||||
|
;;
|
||||||
|
"--32-only")
|
||||||
|
opt_32_only=1
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unrecognized option: $1" >&2
|
echo "Unrecognized option: $1" >&2
|
||||||
exit 1
|
exit 1
|
||||||
@ -63,6 +71,7 @@ function build_arch {
|
|||||||
--bindir "$2" \
|
--bindir "$2" \
|
||||||
--libdir "$2" \
|
--libdir "$2" \
|
||||||
-Dbuild_id=$opt_buildid \
|
-Dbuild_id=$opt_buildid \
|
||||||
|
--force-fallback-for=libdisplay-info \
|
||||||
"$DXVK_BUILD_DIR/build.$1"
|
"$DXVK_BUILD_DIR/build.$1"
|
||||||
|
|
||||||
cd "$DXVK_BUILD_DIR/build.$1"
|
cd "$DXVK_BUILD_DIR/build.$1"
|
||||||
@ -80,8 +89,12 @@ function package {
|
|||||||
rm -R "dxvk-native-$DXVK_VERSION"
|
rm -R "dxvk-native-$DXVK_VERSION"
|
||||||
}
|
}
|
||||||
|
|
||||||
build_arch 64 lib
|
if [ $opt_32_only -eq 0 ]; then
|
||||||
build_arch 32 lib32
|
build_arch 64 lib
|
||||||
|
fi
|
||||||
|
if [ $opt_64_only -eq 0 ]; then
|
||||||
|
build_arch 32 lib32
|
||||||
|
fi
|
||||||
|
|
||||||
if [ $opt_nopackage -eq 0 ]; then
|
if [ $opt_nopackage -eq 0 ]; then
|
||||||
package
|
package
|
||||||
|
@ -15,16 +15,23 @@ else
|
|||||||
d3d10_d3d11_dep = d3d11_dep
|
d3d10_d3d11_dep = d3d11_dep
|
||||||
endif
|
endif
|
||||||
|
|
||||||
d3d10_core_dll = shared_library('d3d10core'+dll_ext, d3d10_core_src, d3d10_core_res,
|
d3d10_core_dll = shared_library(dxvk_name_prefix+'d3d10core', d3d10_core_src, d3d10_core_res,
|
||||||
name_prefix : dxvk_name_prefix,
|
|
||||||
dependencies : [ d3d10_d3d11_dep ],
|
dependencies : [ d3d10_d3d11_dep ],
|
||||||
include_directories : dxvk_include_path,
|
include_directories : dxvk_include_path,
|
||||||
install : true,
|
install : true,
|
||||||
vs_module_defs : 'd3d10core'+def_spec_ext,
|
vs_module_defs : 'd3d10core'+def_spec_ext,
|
||||||
link_args : d3d10_core_ld_args,
|
link_args : d3d10_core_ld_args,
|
||||||
link_depends : [ d3d10_core_link_depends ],
|
link_depends : [ d3d10_core_link_depends ],
|
||||||
|
kwargs : dxvk_so_version,
|
||||||
)
|
)
|
||||||
|
|
||||||
d3d10_core_dep = declare_dependency(
|
d3d10_core_dep = declare_dependency(
|
||||||
link_with : [ d3d10_core_dll ],
|
link_with : [ d3d10_core_dll ],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if platform != 'windows'
|
||||||
|
pkg.generate(d3d10_core_dll,
|
||||||
|
filebase: dxvk_pkg_prefix + 'd3d10core',
|
||||||
|
subdirs: 'dxvk',
|
||||||
|
)
|
||||||
|
endif
|
||||||
|
@ -12,7 +12,7 @@ namespace dxvk {
|
|||||||
const D3D11_ON_12_RESOURCE_INFO* p11on12Info)
|
const D3D11_ON_12_RESOURCE_INFO* p11on12Info)
|
||||||
: D3D11DeviceChild<ID3D11Buffer>(pDevice),
|
: D3D11DeviceChild<ID3D11Buffer>(pDevice),
|
||||||
m_desc (*pDesc),
|
m_desc (*pDesc),
|
||||||
m_resource (this),
|
m_resource (this, pDevice),
|
||||||
m_d3d10 (this) {
|
m_d3d10 (this) {
|
||||||
DxvkBufferCreateInfo info;
|
DxvkBufferCreateInfo info;
|
||||||
info.flags = 0;
|
info.flags = 0;
|
||||||
|
@ -386,11 +386,16 @@ namespace dxvk {
|
|||||||
const UINT Values[4]) {
|
const UINT Values[4]) {
|
||||||
D3D10DeviceLock lock = LockContext();
|
D3D10DeviceLock lock = LockContext();
|
||||||
|
|
||||||
auto uav = static_cast<D3D11UnorderedAccessView*>(pUnorderedAccessView);
|
if (!pUnorderedAccessView)
|
||||||
|
|
||||||
if (!uav)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Com<ID3D11UnorderedAccessView> qiUav;
|
||||||
|
|
||||||
|
if (FAILED(pUnorderedAccessView->QueryInterface(IID_PPV_ARGS(&qiUav))))
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto uav = static_cast<D3D11UnorderedAccessView*>(qiUav.ptr());
|
||||||
|
|
||||||
// Gather UAV format info. We'll use this to determine
|
// Gather UAV format info. We'll use this to determine
|
||||||
// whether we need to create a temporary view or not.
|
// whether we need to create a temporary view or not.
|
||||||
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
|
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
|
||||||
@ -923,6 +928,34 @@ namespace dxvk {
|
|||||||
const void* pSrcData,
|
const void* pSrcData,
|
||||||
UINT SrcRowPitch,
|
UINT SrcRowPitch,
|
||||||
UINT SrcDepthPitch) {
|
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<const char*>(pSrcData) + srcOffset;
|
||||||
|
}
|
||||||
|
|
||||||
UpdateResource(pDstResource, DstSubresource, pDstBox,
|
UpdateResource(pDstResource, DstSubresource, pDstBox,
|
||||||
pSrcData, SrcRowPitch, SrcDepthPitch, 0);
|
pSrcData, SrcRowPitch, SrcDepthPitch, 0);
|
||||||
}
|
}
|
||||||
|
@ -1130,7 +1130,7 @@ namespace dxvk {
|
|||||||
if (likely(pBuffer != nullptr))
|
if (likely(pBuffer != nullptr))
|
||||||
bufferSize = static_cast<D3D11Buffer*>(pBuffer)->Desc()->ByteWidth;
|
bufferSize = static_cast<D3D11Buffer*>(pBuffer)->Desc()->ByteWidth;
|
||||||
|
|
||||||
return bufferSize >= Offset + Size;
|
return uint64_t(bufferSize) >= uint64_t(Offset) + uint64_t(Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -19,6 +19,7 @@ namespace dxvk {
|
|||||||
m_csThread(Device, Device->createContext(DxvkContextType::Primary)),
|
m_csThread(Device, Device->createContext(DxvkContextType::Primary)),
|
||||||
m_maxImplicitDiscardSize(pParent->GetOptions()->maxImplicitDiscardSize),
|
m_maxImplicitDiscardSize(pParent->GetOptions()->maxImplicitDiscardSize),
|
||||||
m_submissionFence(new sync::CallbackFence()),
|
m_submissionFence(new sync::CallbackFence()),
|
||||||
|
m_flushTracker(pParent->GetOptions()->reproducibleCommandStream),
|
||||||
m_multithread(this, false, pParent->GetOptions()->enableContextLock),
|
m_multithread(this, false, pParent->GetOptions()->enableContextLock),
|
||||||
m_videoContext(this, Device) {
|
m_videoContext(this, Device) {
|
||||||
EmitCs([
|
EmitCs([
|
||||||
|
@ -89,6 +89,10 @@ namespace dxvk {
|
|||||||
void SynchronizeCsThread(
|
void SynchronizeCsThread(
|
||||||
uint64_t SequenceNumber);
|
uint64_t SequenceNumber);
|
||||||
|
|
||||||
|
D3D10Multithread& GetMultithread() {
|
||||||
|
return m_multithread;
|
||||||
|
}
|
||||||
|
|
||||||
D3D10DeviceLock LockContext() {
|
D3D10DeviceLock LockContext() {
|
||||||
return m_multithread.AcquireLock();
|
return m_multithread.AcquireLock();
|
||||||
}
|
}
|
||||||
|
@ -45,10 +45,10 @@ namespace dxvk {
|
|||||||
m_dxvkDevice (pContainer->GetDXVKDevice()),
|
m_dxvkDevice (pContainer->GetDXVKDevice()),
|
||||||
m_dxvkAdapter (m_dxvkDevice->adapter()),
|
m_dxvkAdapter (m_dxvkDevice->adapter()),
|
||||||
m_d3d11Formats (m_dxvkDevice),
|
m_d3d11Formats (m_dxvkDevice),
|
||||||
m_d3d11Options (m_dxvkDevice->instance()->config(), m_dxvkDevice),
|
m_d3d11Options (m_dxvkDevice->instance()->config()),
|
||||||
m_dxbcOptions (m_dxvkDevice, m_d3d11Options),
|
m_dxbcOptions (m_dxvkDevice, m_d3d11Options),
|
||||||
m_maxFeatureLevel (GetMaxFeatureLevel(m_dxvkDevice->instance(), m_dxvkDevice->adapter())),
|
m_maxFeatureLevel (GetMaxFeatureLevel(m_dxvkDevice->instance(), m_dxvkDevice->adapter())),
|
||||||
m_deviceFeatures (m_dxvkDevice->instance(), m_dxvkDevice->adapter(), m_featureLevel) {
|
m_deviceFeatures (m_dxvkDevice->instance(), m_dxvkDevice->adapter(), m_d3d11Options, m_featureLevel) {
|
||||||
m_initializer = new D3D11Initializer(this);
|
m_initializer = new D3D11Initializer(this);
|
||||||
m_context = new D3D11ImmediateContext(this, m_dxvkDevice);
|
m_context = new D3D11ImmediateContext(this, m_dxvkDevice);
|
||||||
m_d3d10Device = new D3D10Device(this, m_context.ptr());
|
m_d3d10Device = new D3D10Device(this, m_context.ptr());
|
||||||
@ -1348,7 +1348,7 @@ namespace dxvk {
|
|||||||
m_deviceFeatures = D3D11DeviceFeatures(
|
m_deviceFeatures = D3D11DeviceFeatures(
|
||||||
m_dxvkDevice->instance(),
|
m_dxvkDevice->instance(),
|
||||||
m_dxvkDevice->adapter(),
|
m_dxvkDevice->adapter(),
|
||||||
m_featureLevel);
|
m_d3d11Options, m_featureLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pChosenFeatureLevel)
|
if (pChosenFeatureLevel)
|
||||||
@ -3411,8 +3411,9 @@ namespace dxvk {
|
|||||||
|
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::EnqueueSetEvent(HANDLE hEvent) {
|
HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::EnqueueSetEvent(HANDLE hEvent) {
|
||||||
Logger::err("D3D11DXGIDevice::EnqueueSetEvent: Not implemented");
|
auto immediateContext = m_d3d11Device.GetContext();
|
||||||
return DXGI_ERROR_UNSUPPORTED;
|
immediateContext->Flush1(D3D11_CONTEXT_TYPE_ALL, hEvent);
|
||||||
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ namespace dxvk {
|
|||||||
D3D11DeviceFeatures::D3D11DeviceFeatures(
|
D3D11DeviceFeatures::D3D11DeviceFeatures(
|
||||||
const Rc<DxvkInstance>& Instance,
|
const Rc<DxvkInstance>& Instance,
|
||||||
const Rc<DxvkAdapter>& Adapter,
|
const Rc<DxvkAdapter>& Adapter,
|
||||||
|
const D3D11Options& Options,
|
||||||
D3D_FEATURE_LEVEL FeatureLevel)
|
D3D_FEATURE_LEVEL FeatureLevel)
|
||||||
: m_features (Adapter->features()),
|
: m_features (Adapter->features()),
|
||||||
m_properties (Adapter->devicePropertiesExt()) {
|
m_properties (Adapter->devicePropertiesExt()) {
|
||||||
@ -107,7 +108,7 @@ namespace dxvk {
|
|||||||
m_gpuVirtualAddress.MaxGPUVirtualAddressBitsPerProcess = 40;
|
m_gpuVirtualAddress.MaxGPUVirtualAddressBitsPerProcess = 40;
|
||||||
|
|
||||||
// Marker support only depends on the debug utils extension
|
// Marker support only depends on the debug utils extension
|
||||||
m_marker.Profile = Instance->extensions().extDebugUtils;
|
m_marker.Profile = static_cast<bool>(Instance->extensions().extDebugUtils);
|
||||||
|
|
||||||
// DXVK will keep all shaders in memory once created, and all Vulkan
|
// 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.
|
// drivers that we know of that can run DXVK have an on-disk cache.
|
||||||
@ -118,11 +119,11 @@ namespace dxvk {
|
|||||||
m_shaderMinPrecision.PixelShaderMinPrecision = 0;
|
m_shaderMinPrecision.PixelShaderMinPrecision = 0;
|
||||||
m_shaderMinPrecision.AllOtherShaderStagesMinPrecision = 0;
|
m_shaderMinPrecision.AllOtherShaderStagesMinPrecision = 0;
|
||||||
|
|
||||||
// Report native support for command lists here so that we do not actually have
|
// Report native support for command lists by default. Deferred context
|
||||||
// to re-implement the UpdateSubresource bug from the D3D11 runtime, see MSDN:
|
// usage can be beneficial for us as ExecuteCommandList has low overhead,
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ff476486(v=vs.85).aspx)
|
// and we avoid having to deal with known UpdateSubresource bugs this way.
|
||||||
m_threading.DriverConcurrentCreates = TRUE;
|
m_threading.DriverConcurrentCreates = TRUE;
|
||||||
m_threading.DriverCommandLists = TRUE;
|
m_threading.DriverCommandLists = Options.exposeDriverCommandLists;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -182,7 +183,8 @@ namespace dxvk {
|
|||||||
D3D_FEATURE_LEVEL D3D11DeviceFeatures::GetMaxFeatureLevel(
|
D3D_FEATURE_LEVEL D3D11DeviceFeatures::GetMaxFeatureLevel(
|
||||||
const Rc<DxvkInstance>& Instance,
|
const Rc<DxvkInstance>& Instance,
|
||||||
const Rc<DxvkAdapter>& Adapter) {
|
const Rc<DxvkAdapter>& Adapter) {
|
||||||
D3D11DeviceFeatures features(Instance, Adapter, D3D_FEATURE_LEVEL_12_1);
|
D3D11Options options(Instance->config());
|
||||||
|
D3D11DeviceFeatures features(Instance, Adapter, options, D3D_FEATURE_LEVEL_12_1);
|
||||||
return features.GetMaxFeatureLevel();
|
return features.GetMaxFeatureLevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "d3d11_include.h"
|
#include "d3d11_include.h"
|
||||||
|
#include "d3d11_options.h"
|
||||||
|
|
||||||
#include "../dxvk/dxvk_adapter.h"
|
#include "../dxvk/dxvk_adapter.h"
|
||||||
#include "../dxvk/dxvk_instance.h"
|
#include "../dxvk/dxvk_instance.h"
|
||||||
@ -21,6 +22,7 @@ namespace dxvk {
|
|||||||
D3D11DeviceFeatures(
|
D3D11DeviceFeatures(
|
||||||
const Rc<DxvkInstance>& Instance,
|
const Rc<DxvkInstance>& Instance,
|
||||||
const Rc<DxvkAdapter>& Adapter,
|
const Rc<DxvkAdapter>& Adapter,
|
||||||
|
const D3D11Options& Options,
|
||||||
D3D_FEATURE_LEVEL FeatureLevel);
|
D3D_FEATURE_LEVEL FeatureLevel);
|
||||||
|
|
||||||
~D3D11DeviceFeatures();
|
~D3D11DeviceFeatures();
|
||||||
|
@ -183,13 +183,7 @@ ID3D11VkExtContext1 : public ID3D11VkExtContext {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
struct __declspec(uuid("bb8a4fb9-3935-4762-b44b-35189a26414a")) ID3D11VkExtShader;
|
|
||||||
struct __declspec(uuid("8a6e3c42-f74c-45b7-8265-a231b677ca17")) ID3D11VkExtDevice;
|
|
||||||
struct __declspec(uuid("cfcf64ef-9586-46d0-bca4-97cf2ca61b06")) ID3D11VkExtDevice1;
|
|
||||||
struct __declspec(uuid("fd0bca13-5cb6-4c3a-987e-4750de2ca791")) ID3D11VkExtContext;
|
|
||||||
struct __declspec(uuid("874b09b2-ae0b-41d8-8476-5f3b7a0e879d")) ID3D11VkExtContext1;
|
|
||||||
#else
|
|
||||||
__CRT_UUID_DECL(ID3D11VkExtShader, 0xbb8a4fb9,0x3935,0x4762,0xb4,0x4b,0x35,0x18,0x9a,0x26,0x41,0x4a);
|
__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(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(ID3D11VkExtDevice1, 0xcfcf64ef,0x9586,0x46d0,0xbc,0xa4,0x97,0xcf,0x2c,0xa6,0x1b,0x06);
|
||||||
|
@ -38,7 +38,7 @@ extern "C" {
|
|||||||
DXGI_ADAPTER_DESC desc;
|
DXGI_ADAPTER_DESC desc;
|
||||||
pAdapter->GetDesc(&desc);
|
pAdapter->GetDesc(&desc);
|
||||||
|
|
||||||
dxvkInstance = new DxvkInstance();
|
dxvkInstance = new DxvkInstance(0);
|
||||||
dxvkAdapter = dxvkInstance->findAdapterByLuid(&desc.AdapterLuid);
|
dxvkAdapter = dxvkInstance->findAdapterByLuid(&desc.AdapterLuid);
|
||||||
|
|
||||||
if (dxvkAdapter == nullptr)
|
if (dxvkAdapter == nullptr)
|
||||||
@ -376,7 +376,7 @@ extern "C" {
|
|||||||
instanceInfo.extensionCount = instanceExtensions.size();
|
instanceInfo.extensionCount = instanceExtensions.size();
|
||||||
instanceInfo.extensionNames = instanceExtensions.data();
|
instanceInfo.extensionNames = instanceExtensions.data();
|
||||||
|
|
||||||
Rc<DxvkInstance> dxvkInstance = new DxvkInstance(instanceInfo);
|
Rc<DxvkInstance> dxvkInstance = new DxvkInstance(instanceInfo, 0);
|
||||||
|
|
||||||
// Find adapter by physical device handle
|
// Find adapter by physical device handle
|
||||||
Rc<DxvkAdapter> dxvkAdapter;
|
Rc<DxvkAdapter> dxvkAdapter;
|
||||||
|
@ -49,8 +49,6 @@ ID3D12DXVKInteropDevice : public IUnknown {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
struct __declspec(uuid("39da4e09-bd1c-4198-9fae-86bbe3be41fd")) ID3D12DXVKInteropDevice;
|
|
||||||
#else
|
|
||||||
__CRT_UUID_DECL(ID3D12DXVKInteropDevice, 0x39da4e09, 0xbd1c, 0x4198, 0x9f,0xae, 0x86,0xbb,0xe3,0xbe,0x41,0xfd)
|
__CRT_UUID_DECL(ID3D12DXVKInteropDevice, 0x39da4e09, 0xbd1c, 0x4198, 0x9f,0xae, 0x86,0xbb,0xe3,0xbe,0x41,0xfd)
|
||||||
#endif
|
#endif
|
||||||
|
@ -12,7 +12,7 @@ namespace dxvk {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
D3D11Options::D3D11Options(const Config& config, const Rc<DxvkDevice>& device) {
|
D3D11Options::D3D11Options(const Config& config) {
|
||||||
this->dcSingleUseMode = config.getOption<bool>("d3d11.dcSingleUseMode", true);
|
this->dcSingleUseMode = config.getOption<bool>("d3d11.dcSingleUseMode", true);
|
||||||
this->zeroInitWorkgroupMemory = config.getOption<bool>("d3d11.zeroInitWorkgroupMemory", false);
|
this->zeroInitWorkgroupMemory = config.getOption<bool>("d3d11.zeroInitWorkgroupMemory", false);
|
||||||
this->forceVolatileTgsmAccess = config.getOption<bool>("d3d11.forceVolatileTgsmAccess", false);
|
this->forceVolatileTgsmAccess = config.getOption<bool>("d3d11.forceVolatileTgsmAccess", false);
|
||||||
@ -30,8 +30,9 @@ namespace dxvk {
|
|||||||
this->deferSurfaceCreation = config.getOption<bool>("dxgi.deferSurfaceCreation", false);
|
this->deferSurfaceCreation = config.getOption<bool>("dxgi.deferSurfaceCreation", false);
|
||||||
this->numBackBuffers = config.getOption<int32_t>("dxgi.numBackBuffers", 0);
|
this->numBackBuffers = config.getOption<int32_t>("dxgi.numBackBuffers", 0);
|
||||||
this->maxFrameLatency = config.getOption<int32_t>("dxgi.maxFrameLatency", 0);
|
this->maxFrameLatency = config.getOption<int32_t>("dxgi.maxFrameLatency", 0);
|
||||||
this->maxFrameRate = config.getOption<int32_t>("dxgi.maxFrameRate", 0);
|
this->exposeDriverCommandLists = config.getOption<bool>("d3d11.exposeDriverCommandLists", true);
|
||||||
this->syncInterval = config.getOption<int32_t>("dxgi.syncInterval", -1);
|
this->longMad = config.getOption<bool>("d3d11.longMad", false);
|
||||||
|
this->reproducibleCommandStream = config.getOption<bool>("d3d11.reproducibleCommandStream", false);
|
||||||
|
|
||||||
// Clamp LOD bias so that people don't abuse this in unintended ways
|
// Clamp LOD bias so that people don't abuse this in unintended ways
|
||||||
this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f);
|
this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f);
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
namespace dxvk {
|
namespace dxvk {
|
||||||
|
|
||||||
struct D3D11Options {
|
struct D3D11Options {
|
||||||
D3D11Options(const Config& config, const Rc<DxvkDevice>& device);
|
D3D11Options(const Config& config);
|
||||||
|
|
||||||
/// Enables speed hack for mapping on deferred contexts
|
/// Enables speed hack for mapping on deferred contexts
|
||||||
///
|
///
|
||||||
@ -76,17 +76,10 @@ namespace dxvk {
|
|||||||
/// Overrides DXGI_SWAP_CHAIN_DESC::BufferCount.
|
/// Overrides DXGI_SWAP_CHAIN_DESC::BufferCount.
|
||||||
int32_t numBackBuffers;
|
int32_t numBackBuffers;
|
||||||
|
|
||||||
/// Sync interval. Overrides the value
|
|
||||||
/// passed to IDXGISwapChain::Present.
|
|
||||||
int32_t syncInterval;
|
|
||||||
|
|
||||||
/// Override maximum frame latency if the app specifies
|
/// Override maximum frame latency if the app specifies
|
||||||
/// a higher value. May help with frame timing issues.
|
/// a higher value. May help with frame timing issues.
|
||||||
int32_t maxFrameLatency;
|
int32_t maxFrameLatency;
|
||||||
|
|
||||||
/// Limit frame rate
|
|
||||||
int32_t maxFrameRate;
|
|
||||||
|
|
||||||
/// Limit discardable resource size
|
/// Limit discardable resource size
|
||||||
VkDeviceSize maxImplicitDiscardSize;
|
VkDeviceSize maxImplicitDiscardSize;
|
||||||
|
|
||||||
@ -117,8 +110,21 @@ namespace dxvk {
|
|||||||
/// race conditions.
|
/// race conditions.
|
||||||
bool enableContextLock;
|
bool enableContextLock;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
/// Shader dump path
|
/// Shader dump path
|
||||||
std::string shaderDumpPath;
|
std::string shaderDumpPath;
|
||||||
|
|
||||||
|
/// Should we make our Mads a FFma or do it the long way with an FMul and an FAdd?
|
||||||
|
bool longMad;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
@ -9,11 +9,10 @@
|
|||||||
namespace dxvk {
|
namespace dxvk {
|
||||||
|
|
||||||
D3D11DXGIKeyedMutex::D3D11DXGIKeyedMutex(
|
D3D11DXGIKeyedMutex::D3D11DXGIKeyedMutex(
|
||||||
ID3D11Resource* pResource)
|
ID3D11Resource* pResource,
|
||||||
: m_resource(pResource) {
|
D3D11Device* pDevice)
|
||||||
Com<ID3D11Device> device;
|
: m_resource(pResource),
|
||||||
m_resource->GetDevice(&device);
|
m_device(pDevice) {
|
||||||
m_device = static_cast<D3D11Device*>(device.ptr());
|
|
||||||
|
|
||||||
m_supported = m_device->GetDXVKDevice()->features().khrWin32KeyedMutex
|
m_supported = m_device->GetDXVKDevice()->features().khrWin32KeyedMutex
|
||||||
&& m_device->GetDXVKDevice()->vkd()->wine_vkAcquireKeyedMutex != nullptr
|
&& m_device->GetDXVKDevice()->vkd()->wine_vkAcquireKeyedMutex != nullptr
|
||||||
@ -112,7 +111,17 @@ namespace dxvk {
|
|||||||
D3D11CommonTexture* texture = GetCommonTexture(m_resource);
|
D3D11CommonTexture* texture = GetCommonTexture(m_resource);
|
||||||
Rc<DxvkDevice> dxvkDevice = m_device->GetDXVKDevice();
|
Rc<DxvkDevice> dxvkDevice = m_device->GetDXVKDevice();
|
||||||
|
|
||||||
m_device->GetContext()->WaitForResource(texture->GetImage(), DxvkCsThread::SynchronizeAll, D3D11_MAP_READ_WRITE, 0);
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
return dxvkDevice->vkd()->wine_vkReleaseKeyedMutex(dxvkDevice->handle(), texture->GetImage()->memory().memory(), Key) == VK_SUCCESS
|
return dxvkDevice->vkd()->wine_vkReleaseKeyedMutex(dxvkDevice->handle(), texture->GetImage()->memory().memory(), Key) == VK_SUCCESS
|
||||||
? S_OK
|
? S_OK
|
||||||
@ -120,9 +129,10 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
D3D11DXGIResource::D3D11DXGIResource(
|
D3D11DXGIResource::D3D11DXGIResource(
|
||||||
ID3D11Resource* pResource)
|
ID3D11Resource* pResource,
|
||||||
|
D3D11Device* pDevice)
|
||||||
: m_resource(pResource),
|
: m_resource(pResource),
|
||||||
m_keyedMutex(pResource) {
|
m_keyedMutex(pResource, pDevice) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,8 @@ namespace dxvk {
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
D3D11DXGIKeyedMutex(
|
D3D11DXGIKeyedMutex(
|
||||||
ID3D11Resource* pResource);
|
ID3D11Resource* pResource,
|
||||||
|
D3D11Device* pDevice);
|
||||||
|
|
||||||
~D3D11DXGIKeyedMutex();
|
~D3D11DXGIKeyedMutex();
|
||||||
|
|
||||||
@ -88,7 +89,8 @@ namespace dxvk {
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
D3D11DXGIResource(
|
D3D11DXGIResource(
|
||||||
ID3D11Resource* pResource);
|
ID3D11Resource* pResource,
|
||||||
|
D3D11Device* pDevice);
|
||||||
|
|
||||||
~D3D11DXGIResource();
|
~D3D11DXGIResource();
|
||||||
|
|
||||||
|
@ -99,7 +99,8 @@ namespace dxvk {
|
|||||||
|
|
||||||
if (riid == __uuidof(IUnknown)
|
if (riid == __uuidof(IUnknown)
|
||||||
|| riid == __uuidof(IDXGIVkSwapChain)
|
|| riid == __uuidof(IDXGIVkSwapChain)
|
||||||
|| riid == __uuidof(IDXGIVkSwapChain1)) {
|
|| riid == __uuidof(IDXGIVkSwapChain1)
|
||||||
|
|| riid == __uuidof(IDXGIVkSwapChain2)) {
|
||||||
*ppvObject = ref(this);
|
*ppvObject = ref(this);
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
@ -254,11 +255,6 @@ namespace dxvk {
|
|||||||
UINT SyncInterval,
|
UINT SyncInterval,
|
||||||
UINT PresentFlags,
|
UINT PresentFlags,
|
||||||
const DXGI_PRESENT_PARAMETERS* pPresentParameters) {
|
const DXGI_PRESENT_PARAMETERS* pPresentParameters) {
|
||||||
auto options = m_parent->GetOptions();
|
|
||||||
|
|
||||||
if (options->syncInterval >= 0)
|
|
||||||
SyncInterval = options->syncInterval;
|
|
||||||
|
|
||||||
if (!(PresentFlags & DXGI_PRESENT_TEST))
|
if (!(PresentFlags & DXGI_PRESENT_TEST))
|
||||||
m_dirty |= m_presenter->setSyncInterval(SyncInterval) != VK_SUCCESS;
|
m_dirty |= m_presenter->setSyncInterval(SyncInterval) != VK_SUCCESS;
|
||||||
|
|
||||||
@ -352,6 +348,15 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void STDMETHODCALLTYPE D3D11SwapChain::SetTargetFrameRate(
|
||||||
|
double FrameRate) {
|
||||||
|
m_targetFrameRate = FrameRate;
|
||||||
|
|
||||||
|
if (m_presenter != nullptr)
|
||||||
|
m_presenter->setFrameRateLimit(m_targetFrameRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
HRESULT D3D11SwapChain::PresentImage(UINT SyncInterval) {
|
HRESULT D3D11SwapChain::PresentImage(UINT SyncInterval) {
|
||||||
// Flush pending rendering commands before
|
// Flush pending rendering commands before
|
||||||
auto immediateContext = m_parent->GetContext();
|
auto immediateContext = m_parent->GetContext();
|
||||||
@ -501,7 +506,7 @@ namespace dxvk {
|
|||||||
presenterDesc.fullScreenExclusive = PickFullscreenMode();
|
presenterDesc.fullScreenExclusive = PickFullscreenMode();
|
||||||
|
|
||||||
m_presenter = new Presenter(m_device, m_frameLatencySignal, presenterDesc);
|
m_presenter = new Presenter(m_device, m_frameLatencySignal, presenterDesc);
|
||||||
m_presenter->setFrameRateLimit(m_parent->GetOptions()->maxFrameRate);
|
m_presenter->setFrameRateLimit(m_targetFrameRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ namespace dxvk {
|
|||||||
class D3D11Device;
|
class D3D11Device;
|
||||||
class D3D11DXGIDevice;
|
class D3D11DXGIDevice;
|
||||||
|
|
||||||
class D3D11SwapChain : public ComObject<IDXGIVkSwapChain1> {
|
class D3D11SwapChain : public ComObject<IDXGIVkSwapChain2> {
|
||||||
constexpr static uint32_t DefaultFrameLatency = 1;
|
constexpr static uint32_t DefaultFrameLatency = 1;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -86,6 +86,9 @@ namespace dxvk {
|
|||||||
void STDMETHODCALLTYPE GetFrameStatistics(
|
void STDMETHODCALLTYPE GetFrameStatistics(
|
||||||
DXGI_VK_FRAME_STATISTICS* pFrameStatistics);
|
DXGI_VK_FRAME_STATISTICS* pFrameStatistics);
|
||||||
|
|
||||||
|
void STDMETHODCALLTYPE SetTargetFrameRate(
|
||||||
|
double FrameRate);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
enum BindingIds : uint32_t {
|
enum BindingIds : uint32_t {
|
||||||
@ -129,6 +132,8 @@ namespace dxvk {
|
|||||||
std::optional<VkHdrMetadataEXT> m_hdrMetadata;
|
std::optional<VkHdrMetadataEXT> m_hdrMetadata;
|
||||||
bool m_dirtyHdrMetadata = true;
|
bool m_dirtyHdrMetadata = true;
|
||||||
|
|
||||||
|
double m_targetFrameRate = 0.0;
|
||||||
|
|
||||||
dxvk::mutex m_frameStatisticsLock;
|
dxvk::mutex m_frameStatisticsLock;
|
||||||
DXGI_VK_FRAME_STATISTICS m_frameStatistics = { };
|
DXGI_VK_FRAME_STATISTICS m_frameStatistics = { };
|
||||||
|
|
||||||
|
@ -58,9 +58,6 @@ namespace dxvk {
|
|||||||
"\n MiscFlags: ", m_desc.MiscFlags,
|
"\n MiscFlags: ", m_desc.MiscFlags,
|
||||||
"\n FeatureLevel: ", pDevice->GetFeatureLevel()));
|
"\n FeatureLevel: ", pDevice->GetFeatureLevel()));
|
||||||
|
|
||||||
if (m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX)
|
|
||||||
Logger::warn("D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX: not supported.");
|
|
||||||
|
|
||||||
imageInfo.shared = true;
|
imageInfo.shared = true;
|
||||||
imageInfo.sharing.mode = hSharedHandle == INVALID_HANDLE_VALUE ? DxvkSharedHandleMode::Export : DxvkSharedHandleMode::Import;
|
imageInfo.sharing.mode = hSharedHandle == INVALID_HANDLE_VALUE ? DxvkSharedHandleMode::Export : DxvkSharedHandleMode::Import;
|
||||||
imageInfo.sharing.type = (m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE)
|
imageInfo.sharing.type = (m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE)
|
||||||
@ -214,8 +211,11 @@ namespace dxvk {
|
|||||||
// For some formats, we need to enable sampled and/or
|
// For some formats, we need to enable sampled and/or
|
||||||
// render target capabilities if available, but these
|
// render target capabilities if available, but these
|
||||||
// should in no way affect the default image layout
|
// should in no way affect the default image layout
|
||||||
imageInfo.usage |= EnableMetaCopyUsage(imageInfo.format, imageInfo.tiling);
|
|
||||||
imageInfo.usage |= EnableMetaPackUsage(imageInfo.format, m_desc.CPUAccessFlags);
|
imageInfo.usage |= EnableMetaPackUsage(imageInfo.format, m_desc.CPUAccessFlags);
|
||||||
|
imageInfo.usage |= EnableMetaCopyUsage(imageInfo.format, imageInfo.tiling);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < imageInfo.viewFormatCount; i++)
|
||||||
|
imageInfo.usage |= EnableMetaCopyUsage(imageInfo.viewFormats[i], imageInfo.tiling);
|
||||||
|
|
||||||
// Check if we can actually create the image
|
// Check if we can actually create the image
|
||||||
if (!CheckImageSupport(&imageInfo, imageInfo.tiling)) {
|
if (!CheckImageSupport(&imageInfo, imageInfo.tiling)) {
|
||||||
@ -1099,7 +1099,7 @@ namespace dxvk {
|
|||||||
m_texture (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE1D, 0, VK_NULL_HANDLE, nullptr),
|
m_texture (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE1D, 0, VK_NULL_HANDLE, nullptr),
|
||||||
m_interop (this, &m_texture),
|
m_interop (this, &m_texture),
|
||||||
m_surface (this, &m_texture),
|
m_surface (this, &m_texture),
|
||||||
m_resource(this),
|
m_resource(this, pDevice),
|
||||||
m_d3d10 (this) {
|
m_d3d10 (this) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1205,7 +1205,7 @@ namespace dxvk {
|
|||||||
m_texture (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE2D, 0, VK_NULL_HANDLE, hSharedHandle),
|
m_texture (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE2D, 0, VK_NULL_HANDLE, hSharedHandle),
|
||||||
m_interop (this, &m_texture),
|
m_interop (this, &m_texture),
|
||||||
m_surface (this, &m_texture),
|
m_surface (this, &m_texture),
|
||||||
m_resource (this),
|
m_resource (this, pDevice),
|
||||||
m_d3d10 (this),
|
m_d3d10 (this),
|
||||||
m_swapChain (nullptr) {
|
m_swapChain (nullptr) {
|
||||||
}
|
}
|
||||||
@ -1220,7 +1220,7 @@ namespace dxvk {
|
|||||||
m_texture (this, pDevice, pDesc, nullptr, D3D11_RESOURCE_DIMENSION_TEXTURE2D, DxgiUsage, vkImage, nullptr),
|
m_texture (this, pDevice, pDesc, nullptr, D3D11_RESOURCE_DIMENSION_TEXTURE2D, DxgiUsage, vkImage, nullptr),
|
||||||
m_interop (this, &m_texture),
|
m_interop (this, &m_texture),
|
||||||
m_surface (this, &m_texture),
|
m_surface (this, &m_texture),
|
||||||
m_resource (this),
|
m_resource (this, pDevice),
|
||||||
m_d3d10 (this),
|
m_d3d10 (this),
|
||||||
m_swapChain (nullptr) {
|
m_swapChain (nullptr) {
|
||||||
|
|
||||||
@ -1236,7 +1236,7 @@ namespace dxvk {
|
|||||||
m_texture (this, pDevice, pDesc, nullptr, D3D11_RESOURCE_DIMENSION_TEXTURE2D, DxgiUsage, VK_NULL_HANDLE, nullptr),
|
m_texture (this, pDevice, pDesc, nullptr, D3D11_RESOURCE_DIMENSION_TEXTURE2D, DxgiUsage, VK_NULL_HANDLE, nullptr),
|
||||||
m_interop (this, &m_texture),
|
m_interop (this, &m_texture),
|
||||||
m_surface (this, &m_texture),
|
m_surface (this, &m_texture),
|
||||||
m_resource (this),
|
m_resource (this, pDevice),
|
||||||
m_d3d10 (this),
|
m_d3d10 (this),
|
||||||
m_swapChain (pSwapChain) {
|
m_swapChain (pSwapChain) {
|
||||||
|
|
||||||
@ -1384,7 +1384,7 @@ namespace dxvk {
|
|||||||
: D3D11DeviceChild<ID3D11Texture3D1>(pDevice),
|
: D3D11DeviceChild<ID3D11Texture3D1>(pDevice),
|
||||||
m_texture (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE3D, 0, VK_NULL_HANDLE, nullptr),
|
m_texture (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE3D, 0, VK_NULL_HANDLE, nullptr),
|
||||||
m_interop (this, &m_texture),
|
m_interop (this, &m_texture),
|
||||||
m_resource(this),
|
m_resource(this, pDevice),
|
||||||
m_d3d10 (this) {
|
m_d3d10 (this) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1262,12 +1262,28 @@ namespace dxvk {
|
|||||||
viewport.height = float(cStreamState.dstRect.bottom) - viewport.y;
|
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 uboData = { };
|
||||||
uboData.colorMatrix[0][0] = 1.0f;
|
uboData.colorMatrix[0][0] = 1.0f;
|
||||||
uboData.colorMatrix[1][1] = 1.0f;
|
uboData.colorMatrix[1][1] = 1.0f;
|
||||||
uboData.colorMatrix[2][2] = 1.0f;
|
uboData.colorMatrix[2][2] = 1.0f;
|
||||||
uboData.coordMatrix[0][0] = 1.0f;
|
uboData.coordMatrix[0][0] = float(srcRect.extent.width) / float(viewExtent.width);
|
||||||
uboData.coordMatrix[1][1] = 1.0f;
|
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.yMin = 0.0f;
|
||||||
uboData.yMax = 1.0f;
|
uboData.yMax = 1.0f;
|
||||||
uboData.isPlanar = cViews[1] != nullptr;
|
uboData.isPlanar = cViews[1] != nullptr;
|
||||||
@ -1290,17 +1306,14 @@ namespace dxvk {
|
|||||||
ctx->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_fs));
|
ctx->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_fs));
|
||||||
|
|
||||||
ctx->bindUniformBuffer(VK_SHADER_STAGE_FRAGMENT_BIT, 0, DxvkBufferSlice(m_ubo));
|
ctx->bindUniformBuffer(VK_SHADER_STAGE_FRAGMENT_BIT, 0, DxvkBufferSlice(m_ubo));
|
||||||
ctx->bindResourceSampler(VK_SHADER_STAGE_FRAGMENT_BIT, 1, Rc<DxvkSampler>(m_sampler));
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < cViews.size(); i++)
|
for (uint32_t i = 0; i < cViews.size(); i++)
|
||||||
ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 2 + i, Rc<DxvkImageView>(cViews[i]));
|
ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 1 + i, Rc<DxvkImageView>(cViews[i]));
|
||||||
|
|
||||||
ctx->draw(3, 1, 0, 0);
|
ctx->draw(3, 1, 0, 0);
|
||||||
|
|
||||||
ctx->bindResourceSampler(VK_SHADER_STAGE_FRAGMENT_BIT, 1, nullptr);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < cViews.size(); i++)
|
for (uint32_t i = 0; i < cViews.size(); i++)
|
||||||
ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 2 + i, nullptr);
|
ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 1 + i, nullptr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1315,38 +1328,14 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void D3D11VideoContext::CreateSampler() {
|
|
||||||
DxvkSamplerCreateInfo samplerInfo;
|
|
||||||
samplerInfo.magFilter = VK_FILTER_LINEAR;
|
|
||||||
samplerInfo.minFilter = VK_FILTER_LINEAR;
|
|
||||||
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
||||||
samplerInfo.mipmapLodBias = 0.0f;
|
|
||||||
samplerInfo.mipmapLodMin = 0.0f;
|
|
||||||
samplerInfo.mipmapLodMax = 0.0f;
|
|
||||||
samplerInfo.useAnisotropy = VK_FALSE;
|
|
||||||
samplerInfo.maxAnisotropy = 1.0f;
|
|
||||||
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
||||||
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
||||||
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
||||||
samplerInfo.compareToDepth = VK_FALSE;
|
|
||||||
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
|
|
||||||
samplerInfo.reductionMode = VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE;
|
|
||||||
samplerInfo.borderColor = VkClearColorValue();
|
|
||||||
samplerInfo.usePixelCoord = VK_FALSE;
|
|
||||||
samplerInfo.nonSeamless = VK_FALSE;
|
|
||||||
m_sampler = m_device->createSampler(samplerInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void D3D11VideoContext::CreateShaders() {
|
void D3D11VideoContext::CreateShaders() {
|
||||||
SpirvCodeBuffer vsCode(d3d11_video_blit_vert);
|
SpirvCodeBuffer vsCode(d3d11_video_blit_vert);
|
||||||
SpirvCodeBuffer fsCode(d3d11_video_blit_frag);
|
SpirvCodeBuffer fsCode(d3d11_video_blit_frag);
|
||||||
|
|
||||||
const std::array<DxvkBindingInfo, 4> fsBindings = {{
|
const std::array<DxvkBindingInfo, 3> fsBindings = {{
|
||||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_UNIFORM_READ_BIT, VK_TRUE },
|
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_UNIFORM_READ_BIT, VK_TRUE },
|
||||||
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, 0 },
|
{ 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 },
|
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 2, VK_IMAGE_VIEW_TYPE_2D, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT },
|
||||||
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 3, VK_IMAGE_VIEW_TYPE_2D, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT },
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
DxvkShaderCreateInfo vsInfo;
|
DxvkShaderCreateInfo vsInfo;
|
||||||
@ -1368,7 +1357,6 @@ namespace dxvk {
|
|||||||
if (std::exchange(m_resourcesCreated, true))
|
if (std::exchange(m_resourcesCreated, true))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CreateSampler();
|
|
||||||
CreateUniformBuffer();
|
CreateUniformBuffer();
|
||||||
CreateShaders();
|
CreateShaders();
|
||||||
}
|
}
|
||||||
|
@ -584,6 +584,7 @@ namespace dxvk {
|
|||||||
struct alignas(16) UboData {
|
struct alignas(16) UboData {
|
||||||
float colorMatrix[3][4];
|
float colorMatrix[3][4];
|
||||||
float coordMatrix[3][2];
|
float coordMatrix[3][2];
|
||||||
|
VkRect2D srcRect;
|
||||||
float yMin, yMax;
|
float yMin, yMax;
|
||||||
VkBool32 isPlanar;
|
VkBool32 isPlanar;
|
||||||
};
|
};
|
||||||
@ -593,7 +594,6 @@ namespace dxvk {
|
|||||||
Rc<DxvkDevice> m_device;
|
Rc<DxvkDevice> m_device;
|
||||||
Rc<DxvkShader> m_vs;
|
Rc<DxvkShader> m_vs;
|
||||||
Rc<DxvkShader> m_fs;
|
Rc<DxvkShader> m_fs;
|
||||||
Rc<DxvkSampler> m_sampler;
|
|
||||||
Rc<DxvkBuffer> m_ubo;
|
Rc<DxvkBuffer> m_ubo;
|
||||||
|
|
||||||
VkExtent2D m_dstExtent = { 0u, 0u };
|
VkExtent2D m_dstExtent = { 0u, 0u };
|
||||||
@ -613,8 +613,6 @@ namespace dxvk {
|
|||||||
|
|
||||||
void CreateUniformBuffer();
|
void CreateUniformBuffer();
|
||||||
|
|
||||||
void CreateSampler();
|
|
||||||
|
|
||||||
void CreateShaders();
|
void CreateShaders();
|
||||||
|
|
||||||
void CreateResources();
|
void CreateResources();
|
||||||
|
@ -298,7 +298,7 @@ namespace dxvk {
|
|||||||
D3D11_BUFFER_DESC bufferDesc;
|
D3D11_BUFFER_DESC bufferDesc;
|
||||||
static_cast<D3D11Buffer*>(pResource)->GetDesc(&bufferDesc);
|
static_cast<D3D11Buffer*>(pResource)->GetDesc(&bufferDesc);
|
||||||
|
|
||||||
if (bufferDesc.MiscFlags == D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) {
|
if (bufferDesc.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) {
|
||||||
pDesc->Format = DXGI_FORMAT_UNKNOWN;
|
pDesc->Format = DXGI_FORMAT_UNKNOWN;
|
||||||
pDesc->ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
|
pDesc->ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
|
||||||
pDesc->Buffer.FirstElement = 0;
|
pDesc->Buffer.FirstElement = 0;
|
||||||
|
@ -214,7 +214,7 @@ namespace dxvk {
|
|||||||
D3D11_BUFFER_DESC bufferDesc;
|
D3D11_BUFFER_DESC bufferDesc;
|
||||||
static_cast<D3D11Buffer*>(pResource)->GetDesc(&bufferDesc);
|
static_cast<D3D11Buffer*>(pResource)->GetDesc(&bufferDesc);
|
||||||
|
|
||||||
if (bufferDesc.MiscFlags == D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) {
|
if (bufferDesc.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) {
|
||||||
pDesc->Format = DXGI_FORMAT_UNKNOWN;
|
pDesc->Format = DXGI_FORMAT_UNKNOWN;
|
||||||
pDesc->ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
|
pDesc->ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
|
||||||
pDesc->Buffer.FirstElement = 0;
|
pDesc->Buffer.FirstElement = 0;
|
||||||
|
@ -77,18 +77,25 @@ else
|
|||||||
d3d11_dxgi_dep = dxgi_dep
|
d3d11_dxgi_dep = dxgi_dep
|
||||||
endif
|
endif
|
||||||
|
|
||||||
d3d11_dll = shared_library('d3d11'+dll_ext, dxgi_common_src + d3d11_src + d3d10_src,
|
d3d11_dll = shared_library(dxvk_name_prefix+'d3d11', dxgi_common_src + d3d11_src + d3d10_src,
|
||||||
glsl_generator.process(d3d11_shaders), d3d11_res,
|
glsl_generator.process(d3d11_shaders), d3d11_res,
|
||||||
name_prefix : dxvk_name_prefix,
|
|
||||||
dependencies : [ d3d11_dxgi_dep, dxbc_dep, dxvk_dep ],
|
dependencies : [ d3d11_dxgi_dep, dxbc_dep, dxvk_dep ],
|
||||||
include_directories : dxvk_include_path,
|
include_directories : dxvk_include_path,
|
||||||
install : true,
|
install : true,
|
||||||
vs_module_defs : 'd3d11'+def_spec_ext,
|
vs_module_defs : 'd3d11'+def_spec_ext,
|
||||||
link_args : d3d11_ld_args,
|
link_args : d3d11_ld_args,
|
||||||
link_depends : [ d3d11_link_depends ],
|
link_depends : [ d3d11_link_depends ],
|
||||||
|
kwargs : dxvk_so_version,
|
||||||
)
|
)
|
||||||
|
|
||||||
d3d11_dep = declare_dependency(
|
d3d11_dep = declare_dependency(
|
||||||
link_with : [ d3d11_dll ],
|
link_with : [ d3d11_dll ],
|
||||||
include_directories : [ dxvk_include_path ],
|
include_directories : [ dxvk_include_path ],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if platform != 'windows'
|
||||||
|
pkg.generate(d3d11_dll,
|
||||||
|
filebase: dxvk_pkg_prefix + 'd3d11',
|
||||||
|
subdirs: 'dxvk',
|
||||||
|
)
|
||||||
|
endif
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
|
#extension GL_EXT_samplerless_texture_functions : require
|
||||||
|
|
||||||
// Can't use matrix types here since even a two-row
|
// Can't use matrix types here since even a two-row
|
||||||
// matrix will be padded to 16 bytes per column for
|
// matrix will be padded to 16 bytes per column for
|
||||||
// absolutely no reason
|
// absolutely no reason
|
||||||
@ -11,6 +13,8 @@ uniform ubo_t {
|
|||||||
vec2 coord_matrix_c1;
|
vec2 coord_matrix_c1;
|
||||||
vec2 coord_matrix_c2;
|
vec2 coord_matrix_c2;
|
||||||
vec2 coord_matrix_c3;
|
vec2 coord_matrix_c3;
|
||||||
|
uvec2 src_offset;
|
||||||
|
uvec2 src_extent;
|
||||||
float y_min;
|
float y_min;
|
||||||
float y_max;
|
float y_max;
|
||||||
bool is_planar;
|
bool is_planar;
|
||||||
@ -19,9 +23,8 @@ uniform ubo_t {
|
|||||||
layout(location = 0) in vec2 i_texcoord;
|
layout(location = 0) in vec2 i_texcoord;
|
||||||
layout(location = 0) out vec4 o_color;
|
layout(location = 0) out vec4 o_color;
|
||||||
|
|
||||||
layout(set = 0, binding = 1) uniform sampler s_sampler;
|
layout(set = 0, binding = 1) uniform texture2D s_inputY;
|
||||||
layout(set = 0, binding = 2) uniform texture2D s_inputY;
|
layout(set = 0, binding = 2) uniform texture2D s_inputCbCr;
|
||||||
layout(set = 0, binding = 3) uniform texture2D s_inputCbCr;
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// Transform input texture coordinates to
|
// Transform input texture coordinates to
|
||||||
@ -31,25 +34,61 @@ void main() {
|
|||||||
coord_matrix_c2,
|
coord_matrix_c2,
|
||||||
coord_matrix_c3);
|
coord_matrix_c3);
|
||||||
|
|
||||||
vec2 coord = coord_matrix * vec3(i_texcoord, 1.0f);
|
// Load color space transform
|
||||||
|
|
||||||
// Fetch source image color
|
|
||||||
vec4 color = vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
|
||||||
|
|
||||||
if (is_planar) {
|
|
||||||
color.g = texture(sampler2D(s_inputY, s_sampler), coord).r;
|
|
||||||
color.rb = texture(sampler2D(s_inputCbCr, s_sampler), coord).gr;
|
|
||||||
color.g = clamp((color.g - y_min) / (y_max - y_min), 0.0f, 1.0f);
|
|
||||||
} else {
|
|
||||||
color = texture(sampler2D(s_inputY, s_sampler), coord);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Color space transformation
|
|
||||||
mat3x4 color_matrix = mat3x4(
|
mat3x4 color_matrix = mat3x4(
|
||||||
color_matrix_r1,
|
color_matrix_r1,
|
||||||
color_matrix_r2,
|
color_matrix_r2,
|
||||||
color_matrix_r3);
|
color_matrix_r3);
|
||||||
|
|
||||||
o_color.rgb = vec4(color.rgb, 1.0f) * color_matrix;
|
// Compute actual pixel coordinates to sample. We filter
|
||||||
o_color.a = color.a;
|
// 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;
|
||||||
}
|
}
|
||||||
|
3
src/d3d8/d3d8.def
Normal file
3
src/d3d8/d3d8.def
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
LIBRARY D3D8.DLL
|
||||||
|
EXPORTS
|
||||||
|
Direct3DCreate8 @ 5
|
7
src/d3d8/d3d8.sym
Normal file
7
src/d3d8/d3d8.sym
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
global:
|
||||||
|
Direct3DCreate8;
|
||||||
|
|
||||||
|
local:
|
||||||
|
*;
|
||||||
|
};
|
239
src/d3d8/d3d8_batch.h
Normal file
239
src/d3d8/d3d8_batch.h
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "d3d8_include.h"
|
||||||
|
#include "d3d8_buffer.h"
|
||||||
|
#include "d3d8_format.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
|
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) {
|
||||||
|
*ppbData = m_data.data() + OffsetToLock;
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE Unlock() {
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc) {
|
||||||
|
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() {
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* GetPtr(UINT byteOffset = 0) const {
|
||||||
|
return m_data.data() + byteOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT Size() const {
|
||||||
|
return m_data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<BYTE> m_data;
|
||||||
|
DWORD m_fvf;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Main handler for batching D3D8 draw calls.
|
||||||
|
class D3D8Batcher {
|
||||||
|
|
||||||
|
struct Batch {
|
||||||
|
D3DPRIMITIVETYPE PrimitiveType = D3DPT_INVALID;
|
||||||
|
std::vector<uint16_t> Indices;
|
||||||
|
UINT Offset = 0;
|
||||||
|
UINT MinVertex = UINT_MAX;
|
||||||
|
UINT MaxVertex = 0;
|
||||||
|
UINT PrimitiveCount = 0;
|
||||||
|
UINT DrawCallCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
D3D8Batcher(D3D8Device* pDevice8, Com<d3d9::IDirect3DDevice9>&& 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 = UINT_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<D3D8BatchBuffer*>(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<d3d9::IDirect3DDevice9> m_device;
|
||||||
|
|
||||||
|
D3D8BatchBuffer* m_stream = nullptr;
|
||||||
|
UINT m_stride = 0;
|
||||||
|
D3D8IndexBuffer* m_indices = nullptr;
|
||||||
|
INT m_baseVertexIndex = 0;
|
||||||
|
std::array<Batch, D3DPT_COUNT> m_batches;
|
||||||
|
};
|
||||||
|
}
|
102
src/d3d8/d3d8_buffer.h
Normal file
102
src/d3d8/d3d8_buffer.h
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "d3d8_include.h"
|
||||||
|
#include "d3d8_resource.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
template <typename D3D9, typename D3D8>
|
||||||
|
class D3D8Buffer : public D3D8Resource<D3D9, D3D8> {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D8Buffer(
|
||||||
|
D3D8Device* pDevice,
|
||||||
|
Com<D3D9>&& pBuffer,
|
||||||
|
D3DPOOL Pool,
|
||||||
|
DWORD Usage)
|
||||||
|
: D3D8Resource<D3D9, D3D8> (pDevice, std::move(pBuffer))
|
||||||
|
, m_pool (Pool)
|
||||||
|
, m_usage (Usage) {
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE Lock(
|
||||||
|
UINT OffsetToLock,
|
||||||
|
UINT SizeToLock,
|
||||||
|
BYTE** ppbData,
|
||||||
|
DWORD Flags) {
|
||||||
|
return this->GetD3D9()->Lock(
|
||||||
|
OffsetToLock,
|
||||||
|
SizeToLock,
|
||||||
|
reinterpret_cast<void**>(ppbData),
|
||||||
|
Flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE Unlock() {
|
||||||
|
return this->GetD3D9()->Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void STDMETHODCALLTYPE PreLoad() {
|
||||||
|
this->GetD3D9()->PreLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// This is the D3D8 pool, not necessarily what's given to D3D9.
|
||||||
|
const D3DPOOL m_pool;
|
||||||
|
// This is the D3D8 usage, not necessarily what's given to D3D9.
|
||||||
|
const DWORD m_usage;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
using D3D8VertexBufferBase = D3D8Buffer<d3d9::IDirect3DVertexBuffer9, IDirect3DVertexBuffer8>;
|
||||||
|
class D3D8VertexBuffer : public D3D8VertexBufferBase {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D8VertexBuffer(
|
||||||
|
D3D8Device* pDevice,
|
||||||
|
Com<d3d9::IDirect3DVertexBuffer9>&& pBuffer,
|
||||||
|
D3DPOOL Pool,
|
||||||
|
DWORD Usage)
|
||||||
|
: D3D8VertexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) {
|
||||||
|
}
|
||||||
|
|
||||||
|
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_VERTEXBUFFER; }
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc) {
|
||||||
|
HRESULT hr = GetD3D9()->GetDesc(reinterpret_cast<d3d9::D3DVERTEXBUFFER_DESC*>(pDesc));
|
||||||
|
if (!FAILED(hr)) {
|
||||||
|
pDesc->Pool = m_pool;
|
||||||
|
pDesc->Usage = m_usage;
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
using D3D8IndexBufferBase = D3D8Buffer<d3d9::IDirect3DIndexBuffer9, IDirect3DIndexBuffer8>;
|
||||||
|
class D3D8IndexBuffer final : public D3D8IndexBufferBase {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D8IndexBuffer(
|
||||||
|
D3D8Device* pDevice,
|
||||||
|
Com<d3d9::IDirect3DIndexBuffer9>&& pBuffer,
|
||||||
|
D3DPOOL Pool,
|
||||||
|
DWORD Usage)
|
||||||
|
: D3D8IndexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) {
|
||||||
|
}
|
||||||
|
|
||||||
|
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_INDEXBUFFER; }
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetDesc(D3DINDEXBUFFER_DESC* pDesc) final {
|
||||||
|
HRESULT hr = GetD3D9()->GetDesc(reinterpret_cast<d3d9::D3DINDEXBUFFER_DESC*>(pDesc));
|
||||||
|
if (!FAILED(hr)) {
|
||||||
|
pDesc->Pool = m_pool;
|
||||||
|
pDesc->Usage = m_usage;
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
8
src/d3d8/d3d8_caps.h
Normal file
8
src/d3d8/d3d8_caps.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace dxvk::d8caps {
|
||||||
|
|
||||||
|
inline constexpr uint32_t MAX_TEXTURE_STAGES = 8;
|
||||||
|
inline constexpr uint32_t MAX_STREAMS = 16;
|
||||||
|
|
||||||
|
}
|
164
src/d3d8/d3d8_d3d9_util.h
Normal file
164
src/d3d8/d3d8_d3d9_util.h
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Utility functions for converting
|
||||||
|
// between DirectX8 and DirectX9 types.
|
||||||
|
|
||||||
|
#include "d3d8_include.h"
|
||||||
|
#include "d3d8_format.h"
|
||||||
|
#include "d3d8_options.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
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);
|
||||||
|
pCaps8->PixelShaderVersion = D3DPS_VERSION(1, 4);
|
||||||
|
|
||||||
|
// This was removed by D3D9. We can probably render windowed.
|
||||||
|
pCaps8->Caps2 |= D3DCAPS2_CANRENDERWINDOWED;
|
||||||
|
|
||||||
|
// Replaced by D3DPRASTERCAPS_DEPTHBIAS in D3D9
|
||||||
|
pCaps8->RasterCaps |= D3DPRASTERCAPS_ZBIAS;
|
||||||
|
|
||||||
|
|
||||||
|
// 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_INVSRCCOLOR2
|
||||||
|
& ~D3DPBLENDCAPS_SRCCOLOR2;
|
||||||
|
|
||||||
|
pCaps8->LineCaps &= ~D3DLINECAPS_ANTIALIAS;
|
||||||
|
|
||||||
|
pCaps8->StencilCaps &= ~D3DSTENCILCAPS_TWOSIDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (9<-8) D3DD3DPRESENT_PARAMETERS: Returns D3D9's params given an input for D3D8
|
||||||
|
inline d3d9::D3DPRESENT_PARAMETERS ConvertPresentParameters9(const D3DPRESENT_PARAMETERS* pParams) {
|
||||||
|
|
||||||
|
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);
|
||||||
|
params.MultiSampleQuality = 0; // (D3D8: no MultiSampleQuality), TODO: get a value for this
|
||||||
|
|
||||||
|
|
||||||
|
UINT PresentationInterval = pParams->FullScreen_PresentationInterval;
|
||||||
|
|
||||||
|
if (pParams->Windowed) {
|
||||||
|
|
||||||
|
if (unlikely(PresentationInterval != D3DPRESENT_INTERVAL_DEFAULT)) {
|
||||||
|
// TODO: what does dx8 do if windowed app sets FullScreen_PresentationInterval?
|
||||||
|
Logger::warn(str::format(
|
||||||
|
"D3D8 Application is windowed yet requested FullScreen_PresentationInterval ", PresentationInterval,
|
||||||
|
" (should be D3DPRESENT_INTERVAL_DEFAULT). This will be ignored."));
|
||||||
|
}
|
||||||
|
|
||||||
|
// D3D8: For windowed swap chain, the back buffer is copied to the window immediately.
|
||||||
|
PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3DSWAPEFFECT SwapEffect = pParams->SwapEffect;
|
||||||
|
|
||||||
|
// D3DSWAPEFFECT_COPY_VSYNC has been removed
|
||||||
|
if (SwapEffect == D3DSWAPEFFECT_COPY_VSYNC) {
|
||||||
|
|
||||||
|
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) != 0) {
|
||||||
|
PresentationInterval = D3DPRESENT_INTERVAL_ONE;
|
||||||
|
// TODO: what does dx8 do if multiple D3DPRESENT_INTERVAL flags are set?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 -1
|
||||||
|
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_MIPFILTER;
|
||||||
|
case D3DTSS_MAXANISOTROPY: return d3d9::D3DSAMP_MAXANISOTROPY;
|
||||||
|
// 25:
|
||||||
|
case D3DTSS_ADDRESSW: return d3d9::D3DSAMP_ADDRESSW;
|
||||||
|
default: return d3d9::D3DSAMPLERSTATETYPE(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
1753
src/d3d8/d3d8_device.cpp
Normal file
1753
src/d3d8/d3d8_device.cpp
Normal file
@ -0,0 +1,1753 @@
|
|||||||
|
#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;
|
||||||
|
} else {
|
||||||
|
return Handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct D3D8VertexShaderInfo {
|
||||||
|
d3d9::IDirect3DVertexDeclaration9* pVertexDecl = nullptr;
|
||||||
|
d3d9::IDirect3DVertexShader9* pVertexShader = nullptr;
|
||||||
|
std::vector<DWORD> declaration;
|
||||||
|
std::vector<DWORD> function;
|
||||||
|
};
|
||||||
|
|
||||||
|
D3D8Device::D3D8Device(
|
||||||
|
D3D8Interface* pParent,
|
||||||
|
Com<d3d9::IDirect3DDevice9>&& 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) {
|
||||||
|
// Get the bridge interface to D3D9.
|
||||||
|
if (FAILED(GetD3D9()->QueryInterface(__uuidof(IDxvkD3D8Bridge), (void**)&m_bridge))) {
|
||||||
|
throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_bridge->SetAPIName("D3D8");
|
||||||
|
m_bridge->SetD3D8CompatibilityMode(true);
|
||||||
|
|
||||||
|
ResetState();
|
||||||
|
RecreateBackBuffersAndAutoDepthStencil();
|
||||||
|
|
||||||
|
if (m_d3d8Options.batching)
|
||||||
|
m_batcher = new D3D8Batcher(this, GetD3D9());
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
d3d9::IDirect3DQuery9* pQuery = nullptr;
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
if (DevInfoStructSize != sizeof(D3DDEVINFO_VCACHE))
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
|
memset(pDevInfoStruct, 0, sizeof(D3DDEVINFO_VCACHE));
|
||||||
|
return S_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case D3DDEVINFOID_RESOURCEMANAGER:
|
||||||
|
// May not be implemented by D9VK.
|
||||||
|
res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_RESOURCEMANAGER, &pQuery);
|
||||||
|
break;
|
||||||
|
case D3DDEVINFOID_VERTEXSTATS:
|
||||||
|
res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_VERTEXSTATS, &pQuery);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Logger::warn(str::format("D3D8Device::GetInfo: Unsupported device info ID: ", DevInfoID));
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(FAILED(res)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
// Immediately issue the query.
|
||||||
|
// D3D9 will begin it automatically before ending.
|
||||||
|
res = pQuery->Issue(D3DISSUE_END);
|
||||||
|
if (unlikely(FAILED(res))) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Will immediately issuing the query without doing any API calls
|
||||||
|
// actually yield meaingful results? And should we flush or let it mellow?
|
||||||
|
res = pQuery->GetData(pDevInfoStruct, DevInfoStructSize, D3DGETDATA_FLUSH);
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (pQuery != nullptr)
|
||||||
|
pQuery->Release();
|
||||||
|
|
||||||
|
if (unlikely(FAILED(res))) {
|
||||||
|
if (res == D3DERR_NOTAVAILABLE) // unsupported
|
||||||
|
return E_FAIL;
|
||||||
|
else // any unknown error
|
||||||
|
return S_FALSE;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
dxvk::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<D3D8Surface*>(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) {
|
||||||
|
|
||||||
|
Com<d3d9::IDirect3DSwapChain9> pSwapChain9;
|
||||||
|
d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters);
|
||||||
|
HRESULT res = GetD3D9()->CreateAdditionalSwapChain(
|
||||||
|
¶ms,
|
||||||
|
&pSwapChain9
|
||||||
|
);
|
||||||
|
|
||||||
|
*ppSwapChain = ref(new D3D8SwapChain(this, std::move(pSwapChain9)));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) {
|
||||||
|
StateChange();
|
||||||
|
|
||||||
|
m_presentParams = *pPresentationParameters;
|
||||||
|
ResetState();
|
||||||
|
|
||||||
|
d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters);
|
||||||
|
HRESULT res = GetD3D9()->Reset(¶ms);
|
||||||
|
|
||||||
|
if (FAILED(res))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
RecreateBackBuffersAndAutoDepthStencil();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::Present(
|
||||||
|
const RECT* pSourceRect,
|
||||||
|
const RECT* pDestRect,
|
||||||
|
HWND hDestWindowOverride,
|
||||||
|
const RGNDATA* pDirtyRegion) {
|
||||||
|
m_batcher->EndFrame();
|
||||||
|
StateChange();
|
||||||
|
return GetD3D9()->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetBackBuffer(
|
||||||
|
UINT iBackBuffer,
|
||||||
|
D3DBACKBUFFER_TYPE Type,
|
||||||
|
IDirect3DSurface8** ppBackBuffer) {
|
||||||
|
InitReturnPtr(ppBackBuffer);
|
||||||
|
|
||||||
|
if (iBackBuffer >= m_backBuffers.size() || m_backBuffers[iBackBuffer] == nullptr) {
|
||||||
|
Com<d3d9::IDirect3DSurface9> pSurface9;
|
||||||
|
HRESULT res = GetD3D9()->GetBackBuffer(0, iBackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9);
|
||||||
|
|
||||||
|
if (FAILED(res)) return res;
|
||||||
|
|
||||||
|
m_backBuffers[iBackBuffer] = new D3D8Surface(this, 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, (d3d9::D3DRASTER_STATUS*)pRasterStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void STDMETHODCALLTYPE D3D8Device::SetGammaRamp(DWORD Flags, const D3DGAMMARAMP* pRamp) {
|
||||||
|
StateChange();
|
||||||
|
// For swap chain 0
|
||||||
|
GetD3D9()->SetGammaRamp(0, Flags, reinterpret_cast<const d3d9::D3DGAMMARAMP*>(pRamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
void STDMETHODCALLTYPE D3D8Device::GetGammaRamp(D3DGAMMARAMP* pRamp) {
|
||||||
|
// For swap chain 0
|
||||||
|
GetD3D9()->GetGammaRamp(0, reinterpret_cast<d3d9::D3DGAMMARAMP*>(pRamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::CreateTexture(
|
||||||
|
UINT Width,
|
||||||
|
UINT Height,
|
||||||
|
UINT Levels,
|
||||||
|
DWORD Usage,
|
||||||
|
D3DFORMAT Format,
|
||||||
|
D3DPOOL Pool,
|
||||||
|
IDirect3DTexture8** ppTexture) {
|
||||||
|
InitReturnPtr(ppTexture);
|
||||||
|
|
||||||
|
// 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<d3d9::IDirect3DTexture9> pTex9 = nullptr;
|
||||||
|
HRESULT res = GetD3D9()->CreateTexture(
|
||||||
|
Width,
|
||||||
|
Height,
|
||||||
|
Levels,
|
||||||
|
Usage,
|
||||||
|
d3d9::D3DFORMAT(Format),
|
||||||
|
d3d9::D3DPOOL(Pool),
|
||||||
|
&pTex9,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (FAILED(res))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
*ppTexture = ref(new D3D8Texture2D(this, 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) {
|
||||||
|
Com<d3d9::IDirect3DVolumeTexture9> pVolume9 = nullptr;
|
||||||
|
HRESULT res = GetD3D9()->CreateVolumeTexture(
|
||||||
|
Width, Height, Depth, Levels,
|
||||||
|
Usage,
|
||||||
|
d3d9::D3DFORMAT(Format),
|
||||||
|
d3d9::D3DPOOL(Pool),
|
||||||
|
&pVolume9,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
*ppVolumeTexture = ref(new D3D8Texture3D(this, std::move(pVolume9)));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::CreateCubeTexture(
|
||||||
|
UINT EdgeLength,
|
||||||
|
UINT Levels,
|
||||||
|
DWORD Usage,
|
||||||
|
D3DFORMAT Format,
|
||||||
|
D3DPOOL Pool,
|
||||||
|
IDirect3DCubeTexture8** ppCubeTexture) {
|
||||||
|
Com<d3d9::IDirect3DCubeTexture9> pCube9 = nullptr;
|
||||||
|
HRESULT res = GetD3D9()->CreateCubeTexture(
|
||||||
|
EdgeLength,
|
||||||
|
Levels,
|
||||||
|
Usage,
|
||||||
|
d3d9::D3DFORMAT(Format),
|
||||||
|
d3d9::D3DPOOL(Pool),
|
||||||
|
&pCube9,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
*ppCubeTexture = ref(new D3D8TextureCube(this, std::move(pCube9)));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::CreateVertexBuffer(
|
||||||
|
UINT Length,
|
||||||
|
DWORD Usage,
|
||||||
|
DWORD FVF,
|
||||||
|
D3DPOOL Pool,
|
||||||
|
IDirect3DVertexBuffer8** ppVertexBuffer) {
|
||||||
|
InitReturnPtr(ppVertexBuffer);
|
||||||
|
|
||||||
|
if (ShouldBatch()) {
|
||||||
|
*ppVertexBuffer = m_batcher->CreateVertexBuffer(Length, Usage, FVF, Pool);
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Com<d3d9::IDirect3DVertexBuffer9> pVertexBuffer9 = nullptr;
|
||||||
|
|
||||||
|
HRESULT res = GetD3D9()->CreateVertexBuffer(Length, Usage, FVF, d3d9::D3DPOOL(Pool), &pVertexBuffer9, NULL);
|
||||||
|
|
||||||
|
if (!FAILED(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);
|
||||||
|
Com<d3d9::IDirect3DIndexBuffer9> pIndexBuffer9 = nullptr;
|
||||||
|
|
||||||
|
HRESULT res = GetD3D9()->CreateIndexBuffer(Length, Usage, d3d9::D3DFORMAT(Format), d3d9::D3DPOOL(Pool), &pIndexBuffer9, NULL);
|
||||||
|
|
||||||
|
if (!FAILED(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) {
|
||||||
|
Com<d3d9::IDirect3DSurface9> pSurf9 = nullptr;
|
||||||
|
HRESULT res = GetD3D9()->CreateRenderTarget(
|
||||||
|
Width,
|
||||||
|
Height,
|
||||||
|
d3d9::D3DFORMAT(Format),
|
||||||
|
d3d9::D3DMULTISAMPLE_TYPE(MultiSample),
|
||||||
|
0, // TODO: CreateRenderTarget MultisampleQuality
|
||||||
|
Lockable,
|
||||||
|
&pSurf9,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
*ppSurface = ref(new D3D8Surface(this, std::move(pSurf9)));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::CreateDepthStencilSurface(
|
||||||
|
UINT Width,
|
||||||
|
UINT Height,
|
||||||
|
D3DFORMAT Format,
|
||||||
|
D3DMULTISAMPLE_TYPE MultiSample,
|
||||||
|
IDirect3DSurface8** ppSurface) {
|
||||||
|
Com<d3d9::IDirect3DSurface9> pSurf9 = nullptr;
|
||||||
|
HRESULT res = GetD3D9()->CreateDepthStencilSurface(
|
||||||
|
Width,
|
||||||
|
Height,
|
||||||
|
d3d9::D3DFORMAT(Format),
|
||||||
|
d3d9::D3DMULTISAMPLE_TYPE(MultiSample),
|
||||||
|
0, // TODO: CreateDepthStencilSurface MultisampleQuality
|
||||||
|
true, // TODO: CreateDepthStencilSurface Discard
|
||||||
|
&pSurf9,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
*ppSurface = ref(new D3D8Surface(this, std::move(pSurf9)));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::CreateImageSurface(
|
||||||
|
UINT Width,
|
||||||
|
UINT Height,
|
||||||
|
D3DFORMAT Format,
|
||||||
|
IDirect3DSurface8** ppSurface) {
|
||||||
|
// FIXME: Handle D3DPOOL_SCRATCH in CopyRects
|
||||||
|
D3DPOOL pool = isUnsupportedSurfaceFormat(Format) ? D3DPOOL_SCRATCH : D3DPOOL_SYSTEMMEM;
|
||||||
|
|
||||||
|
Com<d3d9::IDirect3DSurface9> pSurf = nullptr;
|
||||||
|
HRESULT res = GetD3D9()->CreateOffscreenPlainSurface(
|
||||||
|
Width,
|
||||||
|
Height,
|
||||||
|
d3d9::D3DFORMAT(Format),
|
||||||
|
d3d9::D3DPOOL(pool),
|
||||||
|
&pSurf,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
*ppSurface = ref(new D3D8Surface(this, 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;
|
||||||
|
|
||||||
|
// CopyRects cannot perform format conversions.
|
||||||
|
if (srcDesc.Format != dstDesc.Format)
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
|
bool compressed = isDXT(srcDesc.Format);
|
||||||
|
|
||||||
|
res = src->LockRect(&srcLocked, &srcRect, D3DLOCK_READONLY);
|
||||||
|
if (FAILED(res))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
res = dst->LockRect(&dstLocked, &dstRect, 0);
|
||||||
|
if (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.
|
||||||
|
// TODO: Are rects always 4x4 aligned?
|
||||||
|
if (compressed) {
|
||||||
|
// Assume that DXT blocks are 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(
|
||||||
|
(uint8_t*)dstLocked.pBits + dstOffset,
|
||||||
|
(uint8_t*)srcLocked.pBits + srcOffset,
|
||||||
|
amplitude);
|
||||||
|
srcOffset += srcLocked.Pitch;
|
||||||
|
dstOffset += dstLocked.Pitch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = dst->UnlockRect();
|
||||||
|
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 │ - │
|
||||||
|
* │ MANAGED │ UpdateTextureFromBuffer │ memcpy │ memcpy │ - │
|
||||||
|
* │ SYSTEMMEM │ UpdateSurface │ memcpy │ memcpy │ - │
|
||||||
|
* │ SCRATCH │ - │ - │ - │ - │
|
||||||
|
* └────────────┴───────────────────────────┴───────────────────────┴───────────────────────┴──────────┘
|
||||||
|
*/
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::CopyRects(
|
||||||
|
IDirect3DSurface8* pSourceSurface,
|
||||||
|
const RECT* pSourceRectsArray,
|
||||||
|
UINT cRects,
|
||||||
|
IDirect3DSurface8* pDestinationSurface,
|
||||||
|
const POINT* pDestPointsArray) {
|
||||||
|
if (pSourceSurface == NULL || pDestinationSurface == NULL) {
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: No format conversion, no stretching, no clipping.
|
||||||
|
// All src/dest rectangles must fit within the dest surface.
|
||||||
|
|
||||||
|
Com<D3D8Surface> src = static_cast<D3D8Surface*>(pSourceSurface);
|
||||||
|
Com<D3D8Surface> dst = static_cast<D3D8Surface*>(pDestinationSurface);
|
||||||
|
|
||||||
|
d3d9::D3DSURFACE_DESC srcDesc, dstDesc;
|
||||||
|
src->GetD3D9()->GetDesc(&srcDesc);
|
||||||
|
dst->GetD3D9()->GetDesc(&dstDesc);
|
||||||
|
|
||||||
|
// This method cannot be applied to surfaces whose formats
|
||||||
|
// are classified as depth stencil formats.
|
||||||
|
if (unlikely(isDepthStencilFormat(D3DFORMAT(srcDesc.Format)) ||
|
||||||
|
isDepthStencilFormat(D3DFORMAT(dstDesc.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 unhandled = [&] {
|
||||||
|
Logger::warn(str::format("CopyRects: Hit unhandled 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("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:
|
||||||
|
default: {
|
||||||
|
// TODO: Unhandled case.
|
||||||
|
return unhandled();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// Dest: MANAGED
|
||||||
|
case d3d9::D3DPOOL_MANAGED:
|
||||||
|
switch (srcDesc.Pool) {
|
||||||
|
case d3d9::D3DPOOL_DEFAULT: {
|
||||||
|
// TODO: Copy on GPU (handle MANAGED similarly to SYSTEMMEM for now)
|
||||||
|
|
||||||
|
// Get temporary off-screen surface for stretching.
|
||||||
|
Com<d3d9::IDirect3DSurface9> 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: {
|
||||||
|
// SYSTEMMEM -> MANAGED: LockRect / memcpy
|
||||||
|
|
||||||
|
if (stretch) {
|
||||||
|
return logError(D3DERR_INVALIDCALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return logError(copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect));
|
||||||
|
}
|
||||||
|
case d3d9::D3DPOOL_SCRATCH:
|
||||||
|
default: {
|
||||||
|
// TODO: Unhandled case.
|
||||||
|
return unhandled();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// DEST: SYSTEMMEM
|
||||||
|
case d3d9::D3DPOOL_SYSTEMMEM: {
|
||||||
|
|
||||||
|
// RT (DEFAULT) -> SYSTEMMEM: Use GetRenderTargetData as fast path if possible
|
||||||
|
if ((srcDesc.Usage & D3DUSAGE_RENDERTARGET || m_renderTarget.ptr() == 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<d3d9::IDirect3DSurface9> 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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// SYSMEM/MANAGED -> SYSMEM: LockRect / memcpy
|
||||||
|
case d3d9::D3DPOOL_MANAGED:
|
||||||
|
case d3d9::D3DPOOL_SYSTEMMEM: {
|
||||||
|
if (stretch) {
|
||||||
|
return logError(D3DERR_INVALIDCALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return logError(copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect));
|
||||||
|
}
|
||||||
|
case d3d9::D3DPOOL_SCRATCH:
|
||||||
|
default: {
|
||||||
|
// TODO: Unhandled case.
|
||||||
|
return unhandled();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEST: SCRATCH
|
||||||
|
case d3d9::D3DPOOL_SCRATCH:
|
||||||
|
default: {
|
||||||
|
// TODO: Unhandled case.
|
||||||
|
return unhandled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::UpdateTexture(
|
||||||
|
IDirect3DBaseTexture8* pSourceTexture,
|
||||||
|
IDirect3DBaseTexture8* pDestinationTexture) {
|
||||||
|
D3D8Texture2D* src = static_cast<D3D8Texture2D*>(pSourceTexture);
|
||||||
|
D3D8Texture2D* dst = static_cast<D3D8Texture2D*>(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<D3D8Surface> surf = static_cast<D3D8Surface*>(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) {
|
||||||
|
HRESULT res;
|
||||||
|
|
||||||
|
if (pRenderTarget != NULL) {
|
||||||
|
D3D8Surface* surf = static_cast<D3D8Surface*>(pRenderTarget);
|
||||||
|
|
||||||
|
if(likely(m_renderTarget.ptr() != surf)) {
|
||||||
|
StateChange();
|
||||||
|
res = GetD3D9()->SetRenderTarget(0, surf->GetD3D9());
|
||||||
|
|
||||||
|
if (FAILED(res)) return res;
|
||||||
|
|
||||||
|
if (likely(m_renderTarget != surf))
|
||||||
|
m_renderTarget = surf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDepthStencilSurface is a separate call
|
||||||
|
D3D8Surface* zStencil = static_cast<D3D8Surface*>(pNewZStencil);
|
||||||
|
|
||||||
|
if(likely(m_depthStencil.ptr() != zStencil)) {
|
||||||
|
StateChange();
|
||||||
|
res = GetD3D9()->SetDepthStencilSurface(D3D8Surface::GetD3D9Nullable(zStencil));
|
||||||
|
|
||||||
|
if (FAILED(res)) return res;
|
||||||
|
|
||||||
|
if (likely(m_depthStencil != zStencil))
|
||||||
|
m_depthStencil = zStencil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetRenderTarget(IDirect3DSurface8** ppRenderTarget) {
|
||||||
|
InitReturnPtr(ppRenderTarget);
|
||||||
|
|
||||||
|
if (unlikely(m_renderTarget == nullptr)) {
|
||||||
|
Com<d3d9::IDirect3DSurface9> pRT9 = nullptr;
|
||||||
|
HRESULT res = GetD3D9()->GetRenderTarget(0, &pRT9); // use RT index 0
|
||||||
|
|
||||||
|
if(FAILED(res)) return res;
|
||||||
|
|
||||||
|
m_renderTarget = new D3D8Surface(this, std::move(pRT9));
|
||||||
|
|
||||||
|
*ppRenderTarget = m_renderTarget.ref();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ppRenderTarget = m_renderTarget.ref();
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetDepthStencilSurface(IDirect3DSurface8** ppZStencilSurface) {
|
||||||
|
InitReturnPtr(ppZStencilSurface);
|
||||||
|
|
||||||
|
if (unlikely(m_depthStencil == nullptr)) {
|
||||||
|
Com<d3d9::IDirect3DSurface9> pStencil9 = nullptr;
|
||||||
|
HRESULT res = GetD3D9()->GetDepthStencilSurface(&pStencil9);
|
||||||
|
|
||||||
|
if(FAILED(res)) return res;
|
||||||
|
|
||||||
|
m_depthStencil = new D3D8Surface(this, 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) {
|
||||||
|
StateChange();
|
||||||
|
return GetD3D9()->SetViewport(reinterpret_cast<const d3d9::D3DVIEWPORT9*>(pViewport));
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetViewport(D3DVIEWPORT8* pViewport) {
|
||||||
|
return GetD3D9()->GetViewport(reinterpret_cast<d3d9::D3DVIEWPORT9*>(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) {
|
||||||
|
Com<d3d9::IDirect3DStateBlock9> pStateBlock9;
|
||||||
|
HRESULT res = GetD3D9()->CreateStateBlock(d3d9::D3DSTATEBLOCKTYPE(Type), &pStateBlock9);
|
||||||
|
|
||||||
|
m_token++;
|
||||||
|
m_stateBlocks.emplace(std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(m_token),
|
||||||
|
std::forward_as_tuple(this, Type, pStateBlock9.ref()));
|
||||||
|
*pToken = m_token;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::CaptureStateBlock(DWORD Token) {
|
||||||
|
auto stateBlockIter = m_stateBlocks.find(Token);
|
||||||
|
|
||||||
|
if (unlikely(stateBlockIter == m_stateBlocks.end())) {
|
||||||
|
Logger::err("Invalid token passed to CaptureStateBlock");
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateBlockIter->second.Capture();
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::ApplyStateBlock(DWORD Token) {
|
||||||
|
StateChange();
|
||||||
|
|
||||||
|
auto stateBlockIter = m_stateBlocks.find(Token);
|
||||||
|
|
||||||
|
if (unlikely(stateBlockIter == m_stateBlocks.end())) {
|
||||||
|
Logger::err("Invalid token passed to ApplyStateBlock");
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateBlockIter->second.Apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::DeleteStateBlock(DWORD Token) {
|
||||||
|
// "Applications cannot delete a device-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::err("Invalid token passed to DeleteStateBlock");
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
if (unlikely(m_recorder != nullptr))
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
|
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 GetD3D9()->BeginStateBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::EndStateBlock(DWORD* pToken) {
|
||||||
|
if (unlikely(pToken == nullptr || m_recorder == nullptr))
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
|
Com<d3d9::IDirect3DStateBlock9> pStateBlock;
|
||||||
|
HRESULT res = GetD3D9()->EndStateBlock(&pStateBlock);
|
||||||
|
|
||||||
|
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<const d3d9::D3DCLIPSTATUS9*>(pClipStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetClipStatus(D3DCLIPSTATUS8* pClipStatus) {
|
||||||
|
return GetD3D9()->GetClipStatus(reinterpret_cast<d3d9::D3DCLIPSTATUS9*>(pClipStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetTexture(DWORD Stage, IDirect3DBaseTexture8** ppTexture) {
|
||||||
|
InitReturnPtr(ppTexture);
|
||||||
|
|
||||||
|
*ppTexture = m_textures[Stage].ref();
|
||||||
|
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) {
|
||||||
|
if (unlikely(Stage >= d8caps::MAX_TEXTURE_STAGES))
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
|
if (unlikely(ShouldRecord()))
|
||||||
|
return m_recorder->SetTexture(Stage, pTexture);
|
||||||
|
|
||||||
|
D3D8Texture2D* tex = static_cast<D3D8Texture2D*>(pTexture);
|
||||||
|
|
||||||
|
if(unlikely(m_textures[Stage].ptr() == tex))
|
||||||
|
return D3D_OK;
|
||||||
|
|
||||||
|
StateChange();
|
||||||
|
|
||||||
|
m_textures[Stage] = tex;
|
||||||
|
|
||||||
|
return GetD3D9()->SetTexture(Stage, D3D8Texture2D::GetD3D9Nullable(tex));
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetTextureStageState(
|
||||||
|
DWORD Stage,
|
||||||
|
D3DTEXTURESTAGESTATETYPE Type,
|
||||||
|
DWORD* pValue) {
|
||||||
|
d3d9::D3DSAMPLERSTATETYPE stateType = GetSamplerStateType9(Type);
|
||||||
|
|
||||||
|
if (stateType != -1) {
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
StateChange();
|
||||||
|
if (stateType != -1) {
|
||||||
|
// 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) {
|
||||||
|
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) {
|
||||||
|
return GetD3D9()->DrawIndexedPrimitive(
|
||||||
|
d3d9::D3DPRIMITIVETYPE(PrimitiveType),
|
||||||
|
m_baseVertexIndex, // set by SetIndices
|
||||||
|
MinVertexIndex,
|
||||||
|
NumVertices,
|
||||||
|
StartIndex,
|
||||||
|
PrimitiveCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::DrawPrimitiveUP(
|
||||||
|
D3DPRIMITIVETYPE PrimitiveType,
|
||||||
|
UINT PrimitiveCount,
|
||||||
|
const void* pVertexStreamZeroData,
|
||||||
|
UINT VertexStreamZeroStride) {
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
if (unlikely(!pDestBuffer))
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
D3D8VertexBuffer* buffer = static_cast<D3D8VertexBuffer*>(pDestBuffer);
|
||||||
|
return GetD3D9()->ProcessVertices(
|
||||||
|
SrcStartIndex,
|
||||||
|
DestIndex,
|
||||||
|
VertexCount,
|
||||||
|
buffer->GetD3D9(),
|
||||||
|
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<const float*>(pConstantData), ConstantCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount) {
|
||||||
|
return GetD3D9()->GetVertexShaderConstantF(Register, (float*)pConstantData, ConstantCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::SetStreamSource(
|
||||||
|
UINT StreamNumber,
|
||||||
|
IDirect3DVertexBuffer8* pStreamData,
|
||||||
|
UINT Stride) {
|
||||||
|
if (unlikely(StreamNumber >= d8caps::MAX_STREAMS))
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
|
D3D8VertexBuffer* buffer = static_cast<D3D8VertexBuffer*>(pStreamData);
|
||||||
|
|
||||||
|
if (ShouldBatch())
|
||||||
|
m_batcher->SetStream(StreamNumber, buffer, Stride);
|
||||||
|
|
||||||
|
m_streams[StreamNumber] = D3D8VBO {buffer, Stride};
|
||||||
|
|
||||||
|
// DXVK: Never fails
|
||||||
|
return GetD3D9()->SetStreamSource(StreamNumber, D3D8VertexBuffer::GetD3D9Nullable(buffer), 0, Stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetStreamSource(
|
||||||
|
UINT StreamNumber,
|
||||||
|
IDirect3DVertexBuffer8** ppStreamData,
|
||||||
|
UINT* pStride) {
|
||||||
|
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) {
|
||||||
|
if (unlikely(ShouldRecord()))
|
||||||
|
return m_recorder->SetIndices(pIndexData, BaseVertexIndex);
|
||||||
|
|
||||||
|
// used by DrawIndexedPrimitive
|
||||||
|
m_baseVertexIndex = static_cast<INT>(BaseVertexIndex);
|
||||||
|
|
||||||
|
D3D8IndexBuffer* buffer = static_cast<D3D8IndexBuffer*>(pIndexData);
|
||||||
|
|
||||||
|
if (ShouldBatch())
|
||||||
|
m_batcher->SetIndices(buffer, m_baseVertexIndex);
|
||||||
|
|
||||||
|
m_indices = buffer;
|
||||||
|
|
||||||
|
// DXVK: Never fails
|
||||||
|
return GetD3D9()->SetIndices(D3D8IndexBuffer::GetD3D9Nullable(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetIndices(
|
||||||
|
IDirect3DIndexBuffer8** ppIndexData,
|
||||||
|
UINT* pBaseVertexIndex) {
|
||||||
|
InitReturnPtr(ppIndexData);
|
||||||
|
|
||||||
|
*ppIndexData = m_indices.ref();
|
||||||
|
*pBaseVertexIndex = m_baseVertexIndex;
|
||||||
|
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount) {
|
||||||
|
return GetD3D9()->GetPixelShaderConstantF(Register, (float*)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<const float*>(pConstantData), ConstantCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::DrawRectPatch(
|
||||||
|
UINT Handle,
|
||||||
|
const float* pNumSegs,
|
||||||
|
const D3DRECTPATCH_INFO* pRectPatchInfo) {
|
||||||
|
return GetD3D9()->DrawRectPatch(Handle, pNumSegs, reinterpret_cast<const d3d9::D3DRECTPATCH_INFO*>(pRectPatchInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::DrawTriPatch(
|
||||||
|
UINT Handle,
|
||||||
|
const float* pNumSegs,
|
||||||
|
const D3DTRIPATCH_INFO* pTriPatchInfo) {
|
||||||
|
return GetD3D9()->DrawTriPatch(Handle, pNumSegs, reinterpret_cast<const d3d9::D3DTRIPATCH_INFO*>(pTriPatchInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::DeletePatch(UINT Handle) {
|
||||||
|
return GetD3D9()->DeletePatch(Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Render States //
|
||||||
|
|
||||||
|
// ZBIAS can be an integer from 0 to 1 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) {
|
||||||
|
d3d9::D3DRENDERSTATETYPE State9 = (d3d9::D3DRENDERSTATETYPE)State;
|
||||||
|
bool stateChange = true;
|
||||||
|
|
||||||
|
switch (State) {
|
||||||
|
// Most render states translate 1:1 to D3D9
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
// TODO: D3DRS_LINEPATTERN - vkCmdSetLineRasterizationModeEXT
|
||||||
|
case D3DRS_LINEPATTERN: {
|
||||||
|
[[maybe_unused]]
|
||||||
|
D3DLINEPATTERN pattern = bit::cast<D3DLINEPATTERN>(Value);
|
||||||
|
stateChange = false;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// Not supported by D3D8.
|
||||||
|
case D3DRS_ZVISIBLE:
|
||||||
|
stateChange = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// TODO: Not implemented by D9VK. Try anyway.
|
||||||
|
case D3DRS_EDGEANTIALIAS:
|
||||||
|
State9 = d3d9::D3DRS_ANTIALIASEDLINEENABLE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case D3DRS_ZBIAS:
|
||||||
|
State9 = d3d9::D3DRS_DEPTHBIAS;
|
||||||
|
Value = bit::cast<DWORD>(float(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: D3DRS_PATCHSEGMENTS
|
||||||
|
case D3DRS_PATCHSEGMENTS:
|
||||||
|
stateChange = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stateChange) {
|
||||||
|
DWORD value;
|
||||||
|
GetRenderState(State, &value);
|
||||||
|
if (value != Value)
|
||||||
|
StateChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetD3D9()->SetRenderState(State9, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue) {
|
||||||
|
d3d9::D3DRENDERSTATETYPE State9 = (d3d9::D3DRENDERSTATETYPE)State;
|
||||||
|
|
||||||
|
switch (State) {
|
||||||
|
// Most render states translate 1:1 to D3D9
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
// TODO: D3DRS_LINEPATTERN
|
||||||
|
case D3DRS_LINEPATTERN:
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Not supported by D3D8.
|
||||||
|
case D3DRS_ZVISIBLE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case D3DRS_EDGEANTIALIAS:
|
||||||
|
State9 = d3d9::D3DRS_ANTIALIASEDLINEENABLE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case D3DRS_ZBIAS: {
|
||||||
|
float bias = 0;
|
||||||
|
HRESULT res = GetD3D9()->GetRenderState(d3d9::D3DRS_DEPTHBIAS, (DWORD*)&bias);
|
||||||
|
*pValue = bit::cast<DWORD>(bias * ZBIAS_SCALE_INV);
|
||||||
|
return res;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case D3DRS_SOFTWAREVERTEXPROCESSING:
|
||||||
|
return GetD3D9()->GetSoftwareVertexProcessing();
|
||||||
|
|
||||||
|
// TODO: D3DRS_PATCHSEGMENTS
|
||||||
|
case D3DRS_PATCHSEGMENTS:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetD3D9()->GetRenderState(State9, pValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertex Shaders //
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::CreateVertexShader(
|
||||||
|
const DWORD* pDeclaration,
|
||||||
|
const DWORD* pFunction,
|
||||||
|
DWORD* pHandle,
|
||||||
|
DWORD Usage ) {
|
||||||
|
D3D8VertexShaderInfo& info = m_vertexShaders.emplace_back();
|
||||||
|
|
||||||
|
// Store D3D8 bytecodes in the shader info
|
||||||
|
if (pDeclaration != nullptr)
|
||||||
|
for (UINT i = 0; pDeclaration[i+1] != D3DVSD_END(); i++)
|
||||||
|
info.declaration.push_back(pDeclaration[i]);
|
||||||
|
|
||||||
|
if (pFunction != nullptr)
|
||||||
|
for (UINT i = 0; pFunction[i+1] != D3DVS_END(); i++)
|
||||||
|
info.function.push_back(pFunction[i]);
|
||||||
|
|
||||||
|
D3D9VertexShaderCode result = TranslateVertexShader8(pDeclaration, pFunction, m_d3d8Options);
|
||||||
|
|
||||||
|
// Create vertex declaration
|
||||||
|
HRESULT res = GetD3D9()->CreateVertexDeclaration(result.declaration, &(info.pVertexDecl));
|
||||||
|
if (FAILED(res))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if (pFunction != nullptr) {
|
||||||
|
res = GetD3D9()->CreateVertexShader(result.function.data(), &(info.pVertexShader));
|
||||||
|
} else {
|
||||||
|
// pFunction is NULL: fixed function pipeline
|
||||||
|
info.pVertexShader = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set bit to indicate this is not an FVF
|
||||||
|
*pHandle = getShaderHandle(m_vertexShaders.size() - 1);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline D3D8VertexShaderInfo* getVertexShaderInfo(D3D8Device* device, DWORD Handle) {
|
||||||
|
|
||||||
|
Handle = getShaderIndex(Handle);
|
||||||
|
|
||||||
|
if (unlikely(Handle >= device->m_vertexShaders.size())) {
|
||||||
|
Logger::debug(str::format("getVertexShaderInfo: Invalid vertex shader index ", std::hex, Handle));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D8VertexShaderInfo& info = device->m_vertexShaders[Handle];
|
||||||
|
|
||||||
|
if (unlikely(!info.pVertexDecl && !info.pVertexShader)) {
|
||||||
|
Logger::debug(str::format("getVertexShaderInfo: Application provided deleted vertex shader ", std::hex, Handle));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &info;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::SetVertexShader( DWORD Handle ) {
|
||||||
|
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();
|
||||||
|
|
||||||
|
// Cache current shader
|
||||||
|
m_currentVertexShader = Handle;
|
||||||
|
|
||||||
|
GetD3D9()->SetVertexDeclaration(info->pVertexDecl);
|
||||||
|
return GetD3D9()->SetVertexShader(info->pVertexShader);
|
||||||
|
|
||||||
|
} else if (m_currentVertexShader != Handle) {
|
||||||
|
StateChange();
|
||||||
|
|
||||||
|
// Cache current FVF
|
||||||
|
m_currentVertexShader = Handle;
|
||||||
|
|
||||||
|
//GetD3D9()->SetVertexDeclaration(nullptr);
|
||||||
|
GetD3D9()->SetVertexShader(nullptr);
|
||||||
|
return GetD3D9()->SetFVF( Handle );
|
||||||
|
}
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShader(DWORD* pHandle) {
|
||||||
|
// 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) {
|
||||||
|
if (!isFVF(Handle)) {
|
||||||
|
D3D8VertexShaderInfo* info = getVertexShaderInfo(this, Handle);
|
||||||
|
|
||||||
|
if (!info)
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
|
if (info->pVertexDecl)
|
||||||
|
info->pVertexDecl->Release();
|
||||||
|
if (info->pVertexShader)
|
||||||
|
info->pVertexShader->Release();
|
||||||
|
|
||||||
|
info->declaration.clear();
|
||||||
|
info->function.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderDeclaration(DWORD Handle, void* pData, DWORD* pSizeOfData) {
|
||||||
|
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) {
|
||||||
|
*pSizeOfData = ActualSize;
|
||||||
|
return D3DERR_MOREDATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(pData, pInfo->declaration.data(), ActualSize);
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData) {
|
||||||
|
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) {
|
||||||
|
*pSizeOfData = ActualSize;
|
||||||
|
return D3DERR_MOREDATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(pData, pInfo->function.data(), ActualSize);
|
||||||
|
return D3D_OK;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pixel Shaders //
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::CreatePixelShader(
|
||||||
|
const DWORD* pFunction,
|
||||||
|
DWORD* pHandle) {
|
||||||
|
d3d9::IDirect3DPixelShader9* pPixelShader;
|
||||||
|
|
||||||
|
HRESULT res = GetD3D9()->CreatePixelShader(pFunction, &pPixelShader);
|
||||||
|
|
||||||
|
m_pixelShaders.push_back(pPixelShader);
|
||||||
|
|
||||||
|
// Still set the shader bit, to prevent conflicts with NULL.
|
||||||
|
*pHandle = getShaderHandle(m_pixelShaders.size() - 1);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline d3d9::IDirect3DPixelShader9* getPixelShaderPtr(D3D8Device* device, DWORD Handle) {
|
||||||
|
Handle = getShaderIndex(Handle);
|
||||||
|
|
||||||
|
if (unlikely(Handle >= device->m_pixelShaders.size())) {
|
||||||
|
Logger::debug(str::format("getPixelShaderPtr: Invalid pixel shader index ", std::hex, Handle));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
d3d9::IDirect3DPixelShader9* pPixelShader = device->m_pixelShaders[Handle];
|
||||||
|
|
||||||
|
if (unlikely(pPixelShader == nullptr)) {
|
||||||
|
Logger::debug(str::format("getPixelShaderPtr: Application provided deleted pixel shader ", std::hex, Handle));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pPixelShader;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::SetPixelShader(DWORD Handle) {
|
||||||
|
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();
|
||||||
|
|
||||||
|
// Cache current pixel shader
|
||||||
|
m_currentPixelShader = Handle;
|
||||||
|
|
||||||
|
return GetD3D9()->SetPixelShader(pPixelShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShader(DWORD* pHandle) {
|
||||||
|
// Return cached shader
|
||||||
|
*pHandle = m_currentPixelShader;
|
||||||
|
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::DeletePixelShader(DWORD Handle) {
|
||||||
|
d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle);
|
||||||
|
|
||||||
|
if (unlikely(!pPixelShader)) {
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pPixelShader->Release();
|
||||||
|
|
||||||
|
m_pixelShaders[getShaderIndex(Handle)] = nullptr;
|
||||||
|
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData) {
|
||||||
|
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) {
|
||||||
|
*pSizeOfData = ActualSize;
|
||||||
|
return D3DERR_MOREDATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pPixelShader->GetFunction(pData, &SizeOfData);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
456
src/d3d8/d3d8_device.h
Normal file
456
src/d3d8/d3d8_device.h
Normal file
@ -0,0 +1,456 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "d3d8_include.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 <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
class D3D8Interface;
|
||||||
|
|
||||||
|
struct D3D8VertexShaderInfo;
|
||||||
|
|
||||||
|
using D3D8DeviceBase = D3D8WrappedObject<d3d9::IDirect3DDevice9, IDirect3DDevice8>;
|
||||||
|
class D3D8Device final : public D3D8DeviceBase {
|
||||||
|
|
||||||
|
friend class D3D8StateBlock;
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D8Device(
|
||||||
|
D3D8Interface* pParent,
|
||||||
|
Com<d3d9::IDirect3DDevice9>&& 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);
|
||||||
|
|
||||||
|
public: // Internal Methods //
|
||||||
|
|
||||||
|
inline bool ShouldRecord() { return m_recorder != nullptr; }
|
||||||
|
inline bool ShouldBatch() { return m_batcher != nullptr; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
// TODO: Some functions may need to be called here (e.g. SetTexture, etc.)
|
||||||
|
// in case Reset can be recorded by state blocks and other things.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void RecreateBackBuffersAndAutoDepthStencil() {
|
||||||
|
for (UINT i = 0; i < m_presentParams.BackBufferCount; i++) {
|
||||||
|
Com<d3d9::IDirect3DSurface9> pSurface9;
|
||||||
|
GetD3D9()->GetBackBuffer(0, i, d3d9::D3DBACKBUFFER_TYPE_MONO, &pSurface9);
|
||||||
|
m_backBuffers[i] = new D3D8Surface(this, std::move(pSurface9));
|
||||||
|
}
|
||||||
|
|
||||||
|
Com<d3d9::IDirect3DSurface9> pStencil9 = nullptr;
|
||||||
|
GetD3D9()->GetDepthStencilSurface(&pStencil9);
|
||||||
|
m_autoDepthStencil = new D3D8Surface(this, 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<IDxvkD3D8Bridge> m_bridge;
|
||||||
|
const D3D8Options& m_d3d8Options;
|
||||||
|
|
||||||
|
Com<D3D8Interface> m_parent;
|
||||||
|
|
||||||
|
D3DPRESENT_PARAMETERS m_presentParams;
|
||||||
|
|
||||||
|
D3D8StateBlock* m_recorder = nullptr;
|
||||||
|
DWORD m_recorderToken = 0;
|
||||||
|
DWORD m_token = 0;
|
||||||
|
std::unordered_map<DWORD, D3D8StateBlock> m_stateBlocks;
|
||||||
|
D3D8Batcher* m_batcher = nullptr;
|
||||||
|
|
||||||
|
struct D3D8VBO {
|
||||||
|
Com<D3D8VertexBuffer, false> buffer = nullptr;
|
||||||
|
UINT stride = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remember to fill() these in the constructor!
|
||||||
|
std::array<Com<D3D8Texture2D, false>, d8caps::MAX_TEXTURE_STAGES> m_textures;
|
||||||
|
std::array<D3D8VBO, d8caps::MAX_STREAMS> m_streams;
|
||||||
|
|
||||||
|
Com<D3D8IndexBuffer, false> m_indices;
|
||||||
|
INT m_baseVertexIndex = 0;
|
||||||
|
|
||||||
|
// TODO: Which of these should be a private ref
|
||||||
|
std::vector<Com<D3D8Surface, false>> m_backBuffers;
|
||||||
|
Com<D3D8Surface, false> m_autoDepthStencil;
|
||||||
|
|
||||||
|
Com<D3D8Surface, false> m_renderTarget;
|
||||||
|
Com<D3D8Surface, false> m_depthStencil;
|
||||||
|
|
||||||
|
std::vector<D3D8VertexShaderInfo> m_vertexShaders;
|
||||||
|
std::vector<d3d9::IDirect3DPixelShader9*> 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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
71
src/d3d8/d3d8_device_child.h
Normal file
71
src/d3d8/d3d8_device_child.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#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 <typename D3D9, typename D3D8>
|
||||||
|
class D3D8DeviceChild : public D3D8WrappedObject<D3D9, D3D8> {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D8DeviceChild(D3D8Device* pDevice, Com<D3D9>&& Object)
|
||||||
|
: D3D8WrappedObject<D3D9, D3D8>(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() {
|
||||||
|
// ignore Release calls on objects with 0 refCount
|
||||||
|
if(unlikely(!this->m_refCount))
|
||||||
|
return this->m_refCount;
|
||||||
|
|
||||||
|
uint32_t refCount = --this->m_refCount;
|
||||||
|
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<IDirect3DDevice8*>(m_parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D8Device* GetParent() {
|
||||||
|
return m_parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
D3D8Device* m_parent;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
220
src/d3d8/d3d8_format.h
Normal file
220
src/d3d8/d3d8_format.h
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
200
src/d3d8/d3d8_include.h
Normal file
200
src/d3d8/d3d8_include.h
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#ifdef _WIN32_WINNT
|
||||||
|
#undef _WIN32_WINNT
|
||||||
|
#endif
|
||||||
|
#define _WIN32_WINNT 0x0A00
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <d3d8.h>
|
||||||
|
|
||||||
|
// 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<d3d9::type> { static constexpr IID __uuid_inst = {l,w1,w2, {b1,b2,b3,b4,b5,b6,b7,b8}}; }; \
|
||||||
|
extern "C++" template<> constexpr const GUID &__mingw_uuidof<d3d9::type>() { return __mingw_uuidof_s<d3d9::type>::__uuid_inst; } \
|
||||||
|
extern "C++" template<> constexpr const GUID &__mingw_uuidof<d3d9::type*>() { return __mingw_uuidof_s<d3d9::type>::__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<d3d9::type>() { return GUID{a,b,c,{d,e,f,g,h,i,j,k}}; } } \
|
||||||
|
extern "C++" { template <> constexpr GUID __uuidof_helper<d3d9::type*>() { return __uuidof_helper<d3d9::type>(); } } \
|
||||||
|
extern "C++" { template <> constexpr GUID __uuidof_helper<const d3d9::type*>() { return __uuidof_helper<d3d9::type>(); } } \
|
||||||
|
extern "C++" { template <> constexpr GUID __uuidof_helper<d3d9::type&>() { return __uuidof_helper<d3d9::type>(); } } \
|
||||||
|
extern "C++" { template <> constexpr GUID __uuidof_helper<const d3d9::type&>() { return __uuidof_helper<d3d9::type>(); } } \
|
||||||
|
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 <d3d9.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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/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
|
||||||
|
|
||||||
|
#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
|
136
src/d3d8/d3d8_interface.cpp
Normal file
136
src/d3d8/d3d8_interface.cpp
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#include "d3d8_interface.h"
|
||||||
|
|
||||||
|
#include "d3d8_device.h"
|
||||||
|
#include "d3d8_texture.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace dxvk
|
||||||
|
{
|
||||||
|
D3D8Interface::D3D8Interface() {
|
||||||
|
m_d3d9 = d3d9::Direct3DCreate9(D3D_SDK_VERSION);
|
||||||
|
|
||||||
|
// Get the bridge interface to D3D9.
|
||||||
|
if (FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), (void**)&m_bridge))) {
|
||||||
|
throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!");
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
Com<d3d9::IDirect3DDevice9> pDevice9 = nullptr;
|
||||||
|
d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters);
|
||||||
|
HRESULT res = m_d3d9->CreateDevice(
|
||||||
|
Adapter,
|
||||||
|
(d3d9::D3DDEVTYPE)DeviceType,
|
||||||
|
hFocusWindow,
|
||||||
|
BehaviorFlags,
|
||||||
|
¶ms,
|
||||||
|
&pDevice9
|
||||||
|
);
|
||||||
|
|
||||||
|
if (FAILED(res)) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ppReturnedDeviceInterface = ref(new D3D8Device(
|
||||||
|
this, std::move(pDevice9),
|
||||||
|
DeviceType, hFocusWindow, BehaviorFlags,
|
||||||
|
pPresentationParameters
|
||||||
|
));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
163
src/d3d8/d3d8_interface.h
Normal file
163
src/d3d8/d3d8_interface.h
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
#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<IDirect3D8> {
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return m_d3d9->CheckDeviceType(
|
||||||
|
Adapter,
|
||||||
|
(d3d9::D3DDEVTYPE)DevType,
|
||||||
|
(d3d9::D3DFORMAT)AdapterFormat,
|
||||||
|
(d3d9::D3DFORMAT)BackBufferFormat,
|
||||||
|
bWindowed
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
d3d9::D3DCAPS9 caps9;
|
||||||
|
HRESULT res = m_d3d9->GetDeviceCaps(Adapter, (d3d9::D3DDEVTYPE)DeviceType, &caps9);
|
||||||
|
dxvk::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<UINT> m_adapterModeCounts;
|
||||||
|
std::vector<std::vector<d3d9::D3DDISPLAYMODE>> m_adapterModes;
|
||||||
|
|
||||||
|
d3d9::IDirect3D9* m_d3d9;
|
||||||
|
Com<IDxvkD3D8InterfaceBridge> m_bridge;
|
||||||
|
D3D8Options m_d3d8Options;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
24
src/d3d8/d3d8_main.cpp
Normal file
24
src/d3d8/d3d8_main.cpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#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 IDirect3D8* __stdcall Direct3DCreate8(UINT nSDKVersion) {
|
||||||
|
dxvk::Logger::trace("Direct3DCreate8 called");
|
||||||
|
|
||||||
|
IDirect3D8* pDirect3D = nullptr;
|
||||||
|
dxvk::CreateD3D8(&pDirect3D);
|
||||||
|
|
||||||
|
return pDirect3D;
|
||||||
|
}
|
||||||
|
}
|
55
src/d3d8/d3d8_options.cpp
Normal file
55
src/d3d8/d3d8_options.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#include "d3d8_options.h"
|
||||||
|
#include "../d3d9/d3d9_bridge.h"
|
||||||
|
#include "../util/config/config.h"
|
||||||
|
#include "../util/util_string.h"
|
||||||
|
|
||||||
|
#include <charconv>
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
static inline uint32_t parseDword(std::string_view str) {
|
||||||
|
uint32_t value = UINT32_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<std::string_view> 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
48
src/d3d8/d3d8_options.h
Normal file
48
src/d3d8/d3d8_options.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "d3d8_include.h"
|
||||||
|
#include "../d3d9/d3d9_bridge.h"
|
||||||
|
#include "../util/config/config.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
struct D3D8Options {
|
||||||
|
|
||||||
|
/// Some games rely on undefined behavior by using undeclared vertex shader inputs.
|
||||||
|
/// The simplest way to fix them is to simply 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.
|
||||||
|
/// e.g. "0:2,3:2,7:1" for float3 position : v0, float3 normal : v3, float2 uv : v7
|
||||||
|
std::vector<std::pair<D3DVSDE_REGISTER, D3DVSDT_TYPE>> forceVsDecl;
|
||||||
|
|
||||||
|
/// 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 outside of specifc games that benefit from it.
|
||||||
|
bool batching = false;
|
||||||
|
|
||||||
|
/// The Lord of the Rings: The Fellowship of the Ring tries to create a P8 texture
|
||||||
|
/// in D3DPOOL_MANAGED on Nvidia and Intel, which fails, but has a separate code
|
||||||
|
/// path for ATI/AMD that creates it in D3DPOOL_SCRATCH instead, which works.
|
||||||
|
///
|
||||||
|
/// The internal logic determining this path doesn't seem to be d3d-related, but
|
||||||
|
/// the game works universally if we mimic its own ATI/AMD workaround during P8
|
||||||
|
/// texture creation.
|
||||||
|
///
|
||||||
|
/// 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. Most likely ATI/AMD drivers never supported P8 in the first place.
|
||||||
|
bool placeP8InScratch = false;
|
||||||
|
|
||||||
|
D3D8Options() {}
|
||||||
|
D3D8Options(const Config& config) {
|
||||||
|
auto forceVsDeclStr = config.getOption<std::string>("d3d8.forceVsDecl", "");
|
||||||
|
batching = config.getOption<bool> ("d3d8.batching", batching);
|
||||||
|
placeP8InScratch = config.getOption<bool> ("d3d8.placeP8InScratch", placeP8InScratch);
|
||||||
|
|
||||||
|
parseVsDecl(forceVsDeclStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseVsDecl(const std::string& decl);
|
||||||
|
};
|
||||||
|
}
|
100
src/d3d8/d3d8_resource.h
Normal file
100
src/d3d8/d3d8_resource.h
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#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 <typename D3D9, typename D3D8>
|
||||||
|
class D3D8Resource : public D3D8DeviceChild<D3D9, D3D8> {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D8Resource(D3D8Device* pDevice, Com<D3D9>&& Object)
|
||||||
|
: D3D8DeviceChild<D3D9, D3D8>(pDevice, std::move(Object))
|
||||||
|
, m_priority ( 0 ) { }
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE SetPrivateData(
|
||||||
|
REFGUID refguid,
|
||||||
|
const void* pData,
|
||||||
|
DWORD SizeOfData,
|
||||||
|
DWORD Flags) final {
|
||||||
|
HRESULT hr;
|
||||||
|
if (Flags & D3DSPD_IUNKNOWN) {
|
||||||
|
IUnknown* unknown =
|
||||||
|
const_cast<IUnknown*>(
|
||||||
|
reinterpret_cast<const IUnknown*>(pData));
|
||||||
|
hr = m_privateData.setInterface(
|
||||||
|
refguid, unknown);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
hr = m_privateData.setData(
|
||||||
|
refguid, SizeOfData, pData);
|
||||||
|
|
||||||
|
if (FAILED(hr))
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetPrivateData(
|
||||||
|
REFGUID refguid,
|
||||||
|
void* pData,
|
||||||
|
DWORD* pSizeOfData) final {
|
||||||
|
HRESULT hr = m_privateData.getData(
|
||||||
|
refguid, reinterpret_cast<UINT*>(pSizeOfData), pData);
|
||||||
|
|
||||||
|
if (FAILED(hr))
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE FreePrivateData(REFGUID refguid) final {
|
||||||
|
HRESULT hr = m_privateData.setData(refguid, 0, nullptr);
|
||||||
|
|
||||||
|
if (FAILED(hr))
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD STDMETHODCALLTYPE SetPriority(DWORD PriorityNew) {
|
||||||
|
DWORD oldPriority = m_priority;
|
||||||
|
m_priority = PriorityNew;
|
||||||
|
return oldPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD STDMETHODCALLTYPE GetPriority() {
|
||||||
|
return m_priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual IUnknown* GetInterface(REFIID riid) override try {
|
||||||
|
return D3D8DeviceChild<D3D9, D3D8>::GetInterface(riid);
|
||||||
|
} catch (HRESULT err) {
|
||||||
|
if (riid == __uuidof(IDirect3DResource8))
|
||||||
|
return this;
|
||||||
|
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
DWORD m_priority;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
ComPrivateData m_privateData;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
336
src/d3d8/d3d8_shader.cpp
Normal file
336
src/d3d8/d3d8_shader.cpp
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a D3D8 vertex shader + declaration
|
||||||
|
* to a D3D9 vertex shader + declaration.
|
||||||
|
*/
|
||||||
|
D3D9VertexShaderCode TranslateVertexShader8(
|
||||||
|
const DWORD* pDeclaration,
|
||||||
|
const DWORD* pFunction,
|
||||||
|
const D3D8Options& options) {
|
||||||
|
using d3d9::D3DDECLTYPE;
|
||||||
|
using d3d9::D3DDECLTYPE_UNUSED;
|
||||||
|
|
||||||
|
D3D9VertexShaderCode result;
|
||||||
|
|
||||||
|
std::vector<DWORD>& tokens = result.function;
|
||||||
|
std::vector<DWORD> defs; // Constant definitions
|
||||||
|
|
||||||
|
// shaderInputRegisters:
|
||||||
|
// set bit N to enable input register vN
|
||||||
|
DWORD shaderInputRegisters = 0;
|
||||||
|
|
||||||
|
d3d9::D3DVERTEXELEMENT9* vertexElements = result.declaration;
|
||||||
|
unsigned int elementIdx = 0;
|
||||||
|
|
||||||
|
// These are used for pDeclaration and pFunction
|
||||||
|
int i = 0;
|
||||||
|
DWORD token;
|
||||||
|
|
||||||
|
std::stringstream dbg;
|
||||||
|
dbg << "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));
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
// RSQ swizzle fixup
|
||||||
|
if (opcode == D3DSIO_RSQ) {
|
||||||
|
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 result;
|
||||||
|
}
|
||||||
|
}
|
18
src/d3d8/d3d8_shader.h
Normal file
18
src/d3d8/d3d8_shader.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "d3d8_include.h"
|
||||||
|
#include "d3d8_options.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
struct D3D9VertexShaderCode {
|
||||||
|
d3d9::D3DVERTEXELEMENT9 declaration[MAXD3DDECLLENGTH + 1];
|
||||||
|
std::vector<DWORD> function;
|
||||||
|
};
|
||||||
|
|
||||||
|
D3D9VertexShaderCode TranslateVertexShader8(
|
||||||
|
const DWORD* pDeclaration,
|
||||||
|
const DWORD* pFunction,
|
||||||
|
const D3D8Options& overrides);
|
||||||
|
|
||||||
|
}
|
49
src/d3d8/d3d8_state_block.cpp
Normal file
49
src/d3d8/d3d8_state_block.cpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include "d3d8_device.h"
|
||||||
|
#include "d3d8_state_block.h"
|
||||||
|
|
||||||
|
HRESULT dxvk::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();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_capture.indices) {
|
||||||
|
m_baseVertexIndex = m_device->m_baseVertexIndex;
|
||||||
|
m_indices = m_device->m_indices.ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_capture.swvp)
|
||||||
|
m_device->GetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, (DWORD*)&m_isSWVP);
|
||||||
|
|
||||||
|
return m_stateBlock->Capture();
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT dxvk::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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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, m_isSWVP);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
134
src/d3d8/d3d8_state_block.h
Normal file
134
src/d3d8/d3d8_state_block.h
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "d3d8_caps.h"
|
||||||
|
#include "d3d8_include.h"
|
||||||
|
#include "d3d8_device.h"
|
||||||
|
#include "d3d8_device_child.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
struct D3D8StateCapture {
|
||||||
|
bool vs : 1;
|
||||||
|
bool ps : 1;
|
||||||
|
bool indices : 1;
|
||||||
|
bool swvp : 1;
|
||||||
|
|
||||||
|
bit::bitset<d8caps::MAX_TEXTURE_STAGES> textures;
|
||||||
|
|
||||||
|
D3D8StateCapture()
|
||||||
|
: vs(false)
|
||||||
|
, ps(false)
|
||||||
|
, indices(false)
|
||||||
|
, swvp(false) {
|
||||||
|
// Ensure all bits are initialized to false
|
||||||
|
textures.clearAll();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper class for D3D9 state blocks. Captures D3D8-specific state.
|
||||||
|
class D3D8StateBlock {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D8StateBlock(
|
||||||
|
D3D8Device* pDevice,
|
||||||
|
D3DSTATEBLOCKTYPE Type,
|
||||||
|
Com<d3d9::IDirect3DStateBlock9>&& pStateBlock)
|
||||||
|
: m_device(pDevice)
|
||||||
|
, m_stateBlock(std::move(pStateBlock))
|
||||||
|
, m_type(Type) {
|
||||||
|
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_textures.fill(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
~D3D8StateBlock() {}
|
||||||
|
|
||||||
|
// Construct a state block without a D3D9 object
|
||||||
|
D3D8StateBlock(D3D8Device* pDevice)
|
||||||
|
: D3D8StateBlock(pDevice, D3DSTATEBLOCKTYPE(0), nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach a D3D9 object to a state block that doesn't have one yet
|
||||||
|
void SetD3D9(Com<d3d9::IDirect3DStateBlock9>&& pStateBlock) {
|
||||||
|
if (likely(m_stateBlock == nullptr)) {
|
||||||
|
m_stateBlock = std::move(pStateBlock);
|
||||||
|
} else {
|
||||||
|
Logger::err("D3D8StateBlock::SetD3D9 called when m_stateBlock has already been initialized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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<d3d9::IDirect3DStateBlock9> m_stateBlock;
|
||||||
|
D3DSTATEBLOCKTYPE m_type;
|
||||||
|
|
||||||
|
private: // State Data //
|
||||||
|
|
||||||
|
D3D8StateCapture m_capture;
|
||||||
|
|
||||||
|
DWORD m_vertexShader; // vs
|
||||||
|
DWORD m_pixelShader; // ps
|
||||||
|
|
||||||
|
std::array<IDirect3DBaseTexture8*, d8caps::MAX_TEXTURE_STAGES> m_textures; // textures
|
||||||
|
|
||||||
|
IDirect3DIndexBuffer8* m_indices = nullptr; // indices
|
||||||
|
UINT m_baseVertexIndex; // indices
|
||||||
|
|
||||||
|
bool m_isSWVP; // D3DRS_SOFTWAREVERTEXPROCESSING
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
61
src/d3d8/d3d8_subresource.h
Normal file
61
src/d3d8/d3d8_subresource.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "d3d8_resource.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
// Base class for Surfaces and Volumes,
|
||||||
|
// which can be attached to Textures.
|
||||||
|
|
||||||
|
template <typename D3D9, typename D3D8>
|
||||||
|
class D3D8Subresource : public D3D8Resource<D3D9, D3D8> {
|
||||||
|
|
||||||
|
using Resource = D3D8Resource<D3D9, D3D8>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D8Subresource(
|
||||||
|
D3D8Device* pDevice,
|
||||||
|
Com<D3D9>&& Object,
|
||||||
|
IDirect3DBaseTexture8* pBaseTexture)
|
||||||
|
: Resource(pDevice, std::move(Object)),
|
||||||
|
m_container(pBaseTexture) {
|
||||||
|
}
|
||||||
|
|
||||||
|
~D3D8Subresource() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
26
src/d3d8/d3d8_surface.cpp
Normal file
26
src/d3d8/d3d8_surface.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
#include "d3d8_surface.h"
|
||||||
|
#include "d3d8_device.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
Com<d3d9::IDirect3DSurface9> 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<d3d9::IDirect3DSurface9> image = nullptr;
|
||||||
|
HRESULT res = GetParent()->GetD3D9()->CreateRenderTarget(
|
||||||
|
desc.Width, desc.Height, desc.Format,
|
||||||
|
d3d9::D3DMULTISAMPLE_NONE, 0,
|
||||||
|
FALSE,
|
||||||
|
&image,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (FAILED(res))
|
||||||
|
throw new DxvkError("D3D8: Failed to create blit image");
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
}
|
81
src/d3d8/d3d8_surface.h
Normal file
81
src/d3d8/d3d8_surface.h
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "d3d8_include.h"
|
||||||
|
#include "d3d8_subresource.h"
|
||||||
|
#include "d3d8_d3d9_util.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
// TODO: all inherited methods in D3D8Surface should be final like in d9vk
|
||||||
|
|
||||||
|
using D3D8SurfaceBase = D3D8Subresource<d3d9::IDirect3DSurface9, IDirect3DSurface8>;
|
||||||
|
class D3D8Surface final : public D3D8SurfaceBase {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D8Surface(
|
||||||
|
D3D8Device* pDevice,
|
||||||
|
IDirect3DBaseTexture8* pTexture,
|
||||||
|
Com<d3d9::IDirect3DSurface9>&& pSurface)
|
||||||
|
: D3D8SurfaceBase (pDevice, std::move(pSurface), pTexture) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// A surface does not need to be attached to a texture
|
||||||
|
D3D8Surface(
|
||||||
|
D3D8Device* pDevice,
|
||||||
|
Com<d3d9::IDirect3DSurface9>&& pSurface)
|
||||||
|
: D3D8Surface (pDevice, nullptr, std::move(pSurface)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() {
|
||||||
|
return D3DRESOURCETYPE(GetD3D9()->GetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetDesc(D3DSURFACE_DESC* pDesc) {
|
||||||
|
d3d9::D3DSURFACE_DESC desc;
|
||||||
|
HRESULT res = GetD3D9()->GetDesc(&desc);
|
||||||
|
ConvertSurfaceDesc8(&desc, pDesc);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE LockRect(D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
|
||||||
|
return GetD3D9()->LockRect((d3d9::D3DLOCKED_RECT*)pLockedRect, pRect, Flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE UnlockRect() {
|
||||||
|
return GetD3D9()->UnlockRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetDC(HDC* phDC) {
|
||||||
|
return GetD3D9()->GetDC(phDC);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE ReleaseDC(HDC hDC) {
|
||||||
|
return GetD3D9()->ReleaseDC(hDC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Allocate or reuse an image of the same size
|
||||||
|
* as this texture for performing blit into system mem.
|
||||||
|
*
|
||||||
|
* TODO: Consider creating only one texture to
|
||||||
|
* encompass all surface levels of a texture.
|
||||||
|
*/
|
||||||
|
Com<d3d9::IDirect3DSurface9> GetBlitImage() {
|
||||||
|
if (unlikely(m_blitImage == nullptr)) {
|
||||||
|
m_blitImage = CreateBlitImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_blitImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
Com<d3d9::IDirect3DSurface9> CreateBlitImage();
|
||||||
|
|
||||||
|
Com<d3d9::IDirect3DSurface9> m_blitImage = nullptr;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
42
src/d3d8/d3d8_swapchain.h
Normal file
42
src/d3d8/d3d8_swapchain.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "d3d8_device_child.h"
|
||||||
|
#include "d3d8_surface.h"
|
||||||
|
#include "d3d8_d3d9_util.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
using D3D8SwapChainBase = D3D8DeviceChild<d3d9::IDirect3DSwapChain9, IDirect3DSwapChain8>;
|
||||||
|
class D3D8SwapChain final : public D3D8SwapChainBase {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D8SwapChain(
|
||||||
|
D3D8Device* pDevice,
|
||||||
|
Com<d3d9::IDirect3DSwapChain9>&& pSwapChain)
|
||||||
|
: D3D8SwapChainBase(pDevice, std::move(pSwapChain)) {}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE Present(const RECT *src, const RECT *dst, HWND hWnd, const RGNDATA *dirtyRegion) final {
|
||||||
|
return GetD3D9()->Present(src, dst, hWnd, dirtyRegion, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetBackBuffer(UINT BackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface8** ppBackBuffer) final {
|
||||||
|
// Same logic as in D3D8Device::GetBackBuffer
|
||||||
|
if (unlikely(m_backBuffer == nullptr)) {
|
||||||
|
Com<d3d9::IDirect3DSurface9> pSurface9;
|
||||||
|
HRESULT res = GetD3D9()->GetBackBuffer(BackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9);
|
||||||
|
|
||||||
|
m_backBuffer = new D3D8Surface(GetParent(), std::move(pSurface9));
|
||||||
|
*ppBackBuffer = m_backBuffer.ref();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ppBackBuffer = m_backBuffer.ref();
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Com<D3D8Surface> m_backBuffer = nullptr;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
233
src/d3d8/d3d8_texture.h
Normal file
233
src/d3d8/d3d8_texture.h
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "d3d8_resource.h"
|
||||||
|
#include "d3d8_surface.h"
|
||||||
|
#include "d3d8_volume.h"
|
||||||
|
|
||||||
|
#include "d3d8_d3d9_util.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
template <typename SubresourceType, typename D3D9, typename D3D8>
|
||||||
|
class D3D8BaseTexture : public D3D8Resource<D3D9, D3D8> {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
constexpr static UINT CUBE_FACES = 6;
|
||||||
|
|
||||||
|
using SubresourceType8 = typename SubresourceType::D3D8;
|
||||||
|
using SubresourceType9 = typename SubresourceType::D3D9;
|
||||||
|
|
||||||
|
D3D8BaseTexture(
|
||||||
|
D3D8Device* pDevice,
|
||||||
|
Com<D3D9>&& pBaseTexture,
|
||||||
|
UINT SubresourceCount)
|
||||||
|
: D3D8Resource<D3D9, D3D8> ( pDevice, std::move(pBaseTexture) ) {
|
||||||
|
m_subresources.resize(SubresourceCount, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
~D3D8BaseTexture() {
|
||||||
|
for (size_t i = 0; i < m_subresources.size(); i++)
|
||||||
|
if (m_subresources[i] != nullptr)
|
||||||
|
m_subresources[i] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual IUnknown* GetInterface(REFIID riid) final override try {
|
||||||
|
return D3D8Resource<D3D9, D3D8>::GetInterface(riid);
|
||||||
|
} catch (HRESULT err) {
|
||||||
|
if (riid == __uuidof(IDirect3DBaseTexture8))
|
||||||
|
return this;
|
||||||
|
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(Index >= m_subresources.size()))
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
|
if (m_subresources[Index] == nullptr) {
|
||||||
|
try {
|
||||||
|
Com<SubresourceType9> subresource = LookupSubresource(Index);
|
||||||
|
|
||||||
|
// Cache the subresource
|
||||||
|
m_subresources[Index] = new SubresourceType(this->m_parent, this, std::move(subresource));
|
||||||
|
} catch (HRESULT res) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*ppSubresource = m_subresources[Index].ref();
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Com<SubresourceType9> LookupSubresource(UINT Index) {
|
||||||
|
Com<SubresourceType9> ptr = nullptr;
|
||||||
|
HRESULT res = D3DERR_INVALIDCALL;
|
||||||
|
if constexpr (std::is_same_v<D3D8, IDirect3DTexture8>) {
|
||||||
|
res = this->GetD3D9()->GetSurfaceLevel(Index, &ptr);
|
||||||
|
} else if constexpr (std::is_same_v<D3D8, IDirect3DVolume8>) {
|
||||||
|
res = this->GetD3D9()->GetVolumeLevel(Index, &ptr);
|
||||||
|
} else if constexpr (std::is_same_v<D3D8, IDirect3DCubeTexture8>) {
|
||||||
|
res = this->GetD3D9()->GetCubeMapSurface(d3d9::D3DCUBEMAP_FACES(Index % CUBE_FACES), Index / CUBE_FACES, &ptr);
|
||||||
|
}
|
||||||
|
if (FAILED(res))
|
||||||
|
throw res;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Com<SubresourceType, false>> m_subresources;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
using D3D8Texture2DBase = D3D8BaseTexture<D3D8Surface, d3d9::IDirect3DTexture9, IDirect3DTexture8>;
|
||||||
|
class D3D8Texture2D final : public D3D8Texture2DBase {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D8Texture2D(
|
||||||
|
D3D8Device* pDevice,
|
||||||
|
Com<d3d9::IDirect3DTexture9>&& pTexture)
|
||||||
|
: D3D8Texture2DBase(pDevice, std::move(pTexture), pTexture->GetLevelCount()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_TEXTURE; }
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) {
|
||||||
|
d3d9::D3DSURFACE_DESC surf;
|
||||||
|
HRESULT res = GetD3D9()->GetLevelDesc(Level, &surf);
|
||||||
|
ConvertSurfaceDesc8(&surf, pDesc);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetSurfaceLevel(UINT Level, IDirect3DSurface8** ppSurfaceLevel) {
|
||||||
|
return GetSubresource(Level, ppSurfaceLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE LockRect(UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
|
||||||
|
return GetD3D9()->LockRect(Level, reinterpret_cast<d3d9::D3DLOCKED_RECT*>(pLockedRect), pRect, Flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE UnlockRect(UINT Level) {
|
||||||
|
return GetD3D9()->UnlockRect(Level);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE AddDirtyRect(CONST RECT* pDirtyRect) {
|
||||||
|
return GetD3D9()->AddDirtyRect(pDirtyRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
using D3D8Texture3DBase = D3D8BaseTexture<D3D8Volume, d3d9::IDirect3DVolumeTexture9, IDirect3DVolumeTexture8>;
|
||||||
|
class D3D8Texture3D final : public D3D8Texture3DBase {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D8Texture3D(
|
||||||
|
D3D8Device* pDevice,
|
||||||
|
Com<d3d9::IDirect3DVolumeTexture9>&& pVolumeTexture)
|
||||||
|
: D3D8Texture3DBase(pDevice, std::move(pVolumeTexture), pVolumeTexture->GetLevelCount()) {}
|
||||||
|
|
||||||
|
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_VOLUMETEXTURE; }
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc) {
|
||||||
|
d3d9::D3DVOLUME_DESC vol;
|
||||||
|
HRESULT res = GetD3D9()->GetLevelDesc(Level, &vol);
|
||||||
|
ConvertVolumeDesc8(&vol, pDesc);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetVolumeLevel(UINT Level, IDirect3DVolume8** ppVolumeLevel) {
|
||||||
|
return GetSubresource(Level, ppVolumeLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE LockBox(UINT Level, D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) {
|
||||||
|
return GetD3D9()->LockBox(
|
||||||
|
Level,
|
||||||
|
reinterpret_cast<d3d9::D3DLOCKED_BOX*>(pLockedBox),
|
||||||
|
reinterpret_cast<const d3d9::D3DBOX*>(pBox),
|
||||||
|
Flags
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE UnlockBox(UINT Level) {
|
||||||
|
return GetD3D9()->UnlockBox(Level);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE AddDirtyBox(CONST D3DBOX* pDirtyBox) {
|
||||||
|
return GetD3D9()->AddDirtyBox(reinterpret_cast<const d3d9::D3DBOX*>(pDirtyBox));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
using D3D8TextureCubeBase = D3D8BaseTexture<D3D8Surface, d3d9::IDirect3DCubeTexture9, IDirect3DCubeTexture8>;
|
||||||
|
class D3D8TextureCube final : public D3D8TextureCubeBase {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D8TextureCube(
|
||||||
|
D3D8Device* pDevice,
|
||||||
|
Com<d3d9::IDirect3DCubeTexture9>&& pTexture)
|
||||||
|
: D3D8TextureCubeBase(pDevice, std::move(pTexture), pTexture->GetLevelCount() * CUBE_FACES) {
|
||||||
|
}
|
||||||
|
|
||||||
|
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_CUBETEXTURE; }
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) {
|
||||||
|
d3d9::D3DSURFACE_DESC surf;
|
||||||
|
HRESULT res = GetD3D9()->GetLevelDesc(Level, &surf);
|
||||||
|
ConvertSurfaceDesc8(&surf, pDesc);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetCubeMapSurface(D3DCUBEMAP_FACES Face, UINT Level, IDirect3DSurface8** ppSurfaceLevel) {
|
||||||
|
return GetSubresource((Level * CUBE_FACES) + Face, ppSurfaceLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE LockRect(
|
||||||
|
D3DCUBEMAP_FACES Face,
|
||||||
|
UINT Level,
|
||||||
|
D3DLOCKED_RECT* pLockedRect,
|
||||||
|
const RECT* pRect,
|
||||||
|
DWORD Flags) {
|
||||||
|
return GetD3D9()->LockRect(
|
||||||
|
d3d9::D3DCUBEMAP_FACES(Face),
|
||||||
|
Level,
|
||||||
|
reinterpret_cast<d3d9::D3DLOCKED_RECT*>(pLockedRect),
|
||||||
|
pRect,
|
||||||
|
Flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE UnlockRect(D3DCUBEMAP_FACES Face, UINT Level) {
|
||||||
|
return GetD3D9()->UnlockRect(d3d9::D3DCUBEMAP_FACES(Face), Level);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE AddDirtyRect(D3DCUBEMAP_FACES Face, const RECT* pDirtyRect) {
|
||||||
|
return GetD3D9()->AddDirtyRect(d3d9::D3DCUBEMAP_FACES(Face), pDirtyRect);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
40
src/d3d8/d3d8_volume.h
Normal file
40
src/d3d8/d3d8_volume.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "d3d8_subresource.h"
|
||||||
|
#include "d3d8_d3d9_util.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
using D3D8VolumeBase = D3D8Subresource<d3d9::IDirect3DVolume9, IDirect3DVolume8>;
|
||||||
|
class D3D8Volume final : public D3D8VolumeBase {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D8Volume(
|
||||||
|
D3D8Device* pDevice,
|
||||||
|
IDirect3DVolumeTexture8* pTexture,
|
||||||
|
Com<d3d9::IDirect3DVolume9>&& pVolume)
|
||||||
|
: D3D8VolumeBase(pDevice, std::move(pVolume), pTexture) {}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetDesc(D3DVOLUME_DESC* pDesc) {
|
||||||
|
d3d9::D3DVOLUME_DESC desc;
|
||||||
|
HRESULT res = GetD3D9()->GetDesc(&desc);
|
||||||
|
ConvertVolumeDesc8(&desc, pDesc);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) final {
|
||||||
|
return GetD3D9()->LockBox(
|
||||||
|
reinterpret_cast<d3d9::D3DLOCKED_BOX*>(pLockedBox),
|
||||||
|
reinterpret_cast<const d3d9::D3DBOX*>(pBox),
|
||||||
|
Flags
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE UnlockBox() final {
|
||||||
|
return GetD3D9()->UnlockBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
68
src/d3d8/d3d8_wrapped_object.h
Normal file
68
src/d3d8/d3d8_wrapped_object.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "d3d8_include.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
template <typename D3D9Type, typename D3D8Type>
|
||||||
|
class D3D8WrappedObject : public ComObjectClamp<D3D8Type> {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
using D3D9 = D3D9Type;
|
||||||
|
using D3D8 = D3D8Type;
|
||||||
|
|
||||||
|
D3D8WrappedObject(Com<D3D9>&& 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 <typename T>
|
||||||
|
static D3D9* GetD3D9Nullable(Com<T>& self) {
|
||||||
|
return GetD3D9Nullable(self.ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual IUnknown* GetInterface(REFIID riid) {
|
||||||
|
if (riid == __uuidof(IUnknown))
|
||||||
|
return this;
|
||||||
|
if (riid == __uuidof(D3D8))
|
||||||
|
return this;
|
||||||
|
|
||||||
|
throw E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (HRESULT err) {
|
||||||
|
Logger::warn("D3D8WrappedObject::QueryInterface: Unknown interface query");
|
||||||
|
Logger::warn(str::format(riid));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Com<D3D9> m_d3d9;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
42
src/d3d8/meson.build
Normal file
42
src/d3d8/meson.build
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
d3d8_res = wrc_generator.process('version.rc')
|
||||||
|
|
||||||
|
d3d8_src = [
|
||||||
|
'd3d8_main.cpp',
|
||||||
|
'd3d8_interface.cpp',
|
||||||
|
'd3d8_device.cpp',
|
||||||
|
'd3d8_options.cpp',
|
||||||
|
'd3d8_surface.cpp',
|
||||||
|
'd3d8_state_block.cpp',
|
||||||
|
'd3d8_shader.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
|
31
src/d3d8/version.rc
Normal file
31
src/d3d8/version.rc
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
// 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
|
@ -162,11 +162,18 @@ namespace dxvk {
|
|||||||
if (mapping.FormatSrgb == VK_FORMAT_UNDEFINED && srgb)
|
if (mapping.FormatSrgb == VK_FORMAT_UNDEFINED && srgb)
|
||||||
return D3DERR_NOTAVAILABLE;
|
return D3DERR_NOTAVAILABLE;
|
||||||
|
|
||||||
|
if (RType == D3DRTYPE_CUBETEXTURE && mapping.Aspect != VK_IMAGE_ASPECT_COLOR_BIT)
|
||||||
|
return D3DERR_NOTAVAILABLE;
|
||||||
|
|
||||||
if (RType == D3DRTYPE_VERTEXBUFFER || RType == D3DRTYPE_INDEXBUFFER)
|
if (RType == D3DRTYPE_VERTEXBUFFER || RType == D3DRTYPE_INDEXBUFFER)
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
|
|
||||||
// Let's actually ask Vulkan now that we got some quirks out the way!
|
// Let's actually ask Vulkan now that we got some quirks out the way!
|
||||||
return CheckDeviceVkFormat(mapping.FormatColor, Usage, RType);
|
VkFormat format = mapping.FormatColor;
|
||||||
|
if (unlikely(mapping.ConversionFormatInfo.FormatColor != VK_FORMAT_UNDEFINED)) {
|
||||||
|
format = mapping.ConversionFormatInfo.FormatColor;
|
||||||
|
}
|
||||||
|
return CheckDeviceVkFormat(format, Usage, RType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -224,11 +231,15 @@ namespace dxvk {
|
|||||||
if (!IsDepthFormat(DepthStencilFormat))
|
if (!IsDepthFormat(DepthStencilFormat))
|
||||||
return D3DERR_NOTAVAILABLE;
|
return D3DERR_NOTAVAILABLE;
|
||||||
|
|
||||||
|
auto dsfMapping = GetFormatMapping(DepthStencilFormat);
|
||||||
|
if (dsfMapping.FormatColor == VK_FORMAT_UNDEFINED)
|
||||||
|
return D3DERR_NOTAVAILABLE;
|
||||||
|
|
||||||
if (RenderTargetFormat == dxvk::D3D9Format::NULL_FORMAT)
|
if (RenderTargetFormat == dxvk::D3D9Format::NULL_FORMAT)
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
|
|
||||||
auto mapping = ConvertFormatUnfixed(RenderTargetFormat);
|
auto rtfMapping = GetFormatMapping(RenderTargetFormat);
|
||||||
if (mapping.FormatColor == VK_FORMAT_UNDEFINED)
|
if (rtfMapping.FormatColor == VK_FORMAT_UNDEFINED)
|
||||||
return D3DERR_NOTAVAILABLE;
|
return D3DERR_NOTAVAILABLE;
|
||||||
|
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
@ -240,7 +251,8 @@ namespace dxvk {
|
|||||||
D3D9Format SourceFormat,
|
D3D9Format SourceFormat,
|
||||||
D3D9Format TargetFormat) {
|
D3D9Format TargetFormat) {
|
||||||
bool sourceSupported = SourceFormat != D3D9Format::Unknown
|
bool sourceSupported = SourceFormat != D3D9Format::Unknown
|
||||||
&& IsSupportedBackBufferFormat(SourceFormat);
|
&& (IsSupportedBackBufferFormat(SourceFormat)
|
||||||
|
|| (IsFourCCFormat(SourceFormat) && !IsVendorFormat(SourceFormat)));
|
||||||
bool targetSupported = TargetFormat == D3D9Format::X1R5G5B5
|
bool targetSupported = TargetFormat == D3D9Format::X1R5G5B5
|
||||||
|| TargetFormat == D3D9Format::A1R5G5B5
|
|| TargetFormat == D3D9Format::A1R5G5B5
|
||||||
|| TargetFormat == D3D9Format::R5G6B5
|
|| TargetFormat == D3D9Format::R5G6B5
|
||||||
@ -271,6 +283,8 @@ namespace dxvk {
|
|||||||
|
|
||||||
auto& options = m_parent->GetOptions();
|
auto& options = m_parent->GetOptions();
|
||||||
|
|
||||||
|
const VkPhysicalDeviceLimits& limits = m_adapter->deviceProperties().limits;
|
||||||
|
|
||||||
// TODO: Actually care about what the adapter supports here.
|
// TODO: Actually care about what the adapter supports here.
|
||||||
// ^ For Intel and older cards most likely here.
|
// ^ For Intel and older cards most likely here.
|
||||||
|
|
||||||
@ -531,7 +545,7 @@ namespace dxvk {
|
|||||||
// Max Vertex Blend Matrix Index
|
// Max Vertex Blend Matrix Index
|
||||||
pCaps->MaxVertexBlendMatrixIndex = 0;
|
pCaps->MaxVertexBlendMatrixIndex = 0;
|
||||||
// Max Point Size
|
// Max Point Size
|
||||||
pCaps->MaxPointSize = 256.0f;
|
pCaps->MaxPointSize = limits.pointSizeRange[1];
|
||||||
// Max Primitive Count
|
// Max Primitive Count
|
||||||
pCaps->MaxPrimitiveCount = 0x00555555;
|
pCaps->MaxPrimitiveCount = 0x00555555;
|
||||||
// Max Vertex Index
|
// Max Vertex Index
|
||||||
@ -788,6 +802,7 @@ namespace dxvk {
|
|||||||
// Fix up the D3DFORMAT to match what we are enumerating
|
// Fix up the D3DFORMAT to match what we are enumerating
|
||||||
mode.Format = static_cast<D3DFORMAT>(Format);
|
mode.Format = static_cast<D3DFORMAT>(Format);
|
||||||
|
|
||||||
|
if (std::count(m_modes.begin(), m_modes.end(), mode) == 0)
|
||||||
m_modes.push_back(mode);
|
m_modes.push_back(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,10 @@ namespace dxvk {
|
|||||||
m_device->m_implicitSwapchain->SetApiName(name);
|
m_device->m_implicitSwapchain->SetApiName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DxvkD3D8Bridge::SetD3D8CompatibilityMode(const bool compatMode) {
|
||||||
|
m_device->SetD3D8CompatibilityMode(compatMode);
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT DxvkD3D8Bridge::UpdateTextureFromBuffer(
|
HRESULT DxvkD3D8Bridge::UpdateTextureFromBuffer(
|
||||||
IDirect3DSurface9* pDestSurface,
|
IDirect3DSurface9* pDestSurface,
|
||||||
IDirect3DSurface9* pSrcSurface,
|
IDirect3DSurface9* pSrcSurface,
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "../util/config/config.h"
|
#include "../util/config/config.h"
|
||||||
|
|
||||||
#include "../vulkan/vulkan_loader.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The D3D9 bridge allows D3D8 to access DXVK internals.
|
* The D3D9 bridge allows D3D8 to access DXVK internals.
|
||||||
* For Vulkan interop without needing DXVK internals, see d3d9_interop.h.
|
* For Vulkan interop without needing DXVK internals, see d3d9_interop.h.
|
||||||
@ -30,6 +28,13 @@ IDxvkD3D8Bridge : public IUnknown {
|
|||||||
*/
|
*/
|
||||||
virtual void SetAPIName(const char* name) = 0;
|
virtual void SetAPIName(const char* name) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Enables or disables D3D9-specific device features and validations
|
||||||
|
*
|
||||||
|
* \param [in] compatibility state
|
||||||
|
*/
|
||||||
|
virtual void SetD3D8CompatibilityMode(const bool compatMode) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Updates a D3D9 surface from a D3D9 buffer
|
* \brief Updates a D3D9 surface from a D3D9 buffer
|
||||||
*
|
*
|
||||||
@ -58,10 +63,7 @@ IDxvkD3D8InterfaceBridge : public IUnknown {
|
|||||||
virtual const dxvk::Config* GetConfig() const = 0;
|
virtual const dxvk::Config* GetConfig() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#ifndef _MSC_VER
|
||||||
struct DECLSPEC_UUID("D3D9D3D8-42A9-4C1E-AA97-BEEFCAFE2000") IDxvkD3D8Bridge;
|
|
||||||
struct DECLSPEC_UUID("D3D9D3D8-A407-773E-18E9-CAFEBEEF3000") IDxvkD3D8InterfaceBridge;
|
|
||||||
#else
|
|
||||||
__CRT_UUID_DECL(IDxvkD3D8Bridge, 0xD3D9D3D8, 0x42A9, 0x4C1E, 0xAA, 0x97, 0xBE, 0xEF, 0xCA, 0xFE, 0x20, 0x00);
|
__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);
|
__CRT_UUID_DECL(IDxvkD3D8InterfaceBridge, 0xD3D9D3D8, 0xA407, 0x773E, 0x18, 0xE9, 0xCA, 0xFE, 0xBE, 0xEF, 0x30, 0x00);
|
||||||
#endif
|
#endif
|
||||||
@ -83,6 +85,7 @@ namespace dxvk {
|
|||||||
void** ppvObject);
|
void** ppvObject);
|
||||||
|
|
||||||
void SetAPIName(const char* name);
|
void SetAPIName(const char* name);
|
||||||
|
void SetD3D8CompatibilityMode(const bool compatMode);
|
||||||
|
|
||||||
HRESULT UpdateTextureFromBuffer(
|
HRESULT UpdateTextureFromBuffer(
|
||||||
IDirect3DSurface9* pDestSurface,
|
IDirect3DSurface9* pDestSurface,
|
||||||
|
@ -125,7 +125,12 @@ namespace dxvk {
|
|||||||
|
|
||||||
template <D3D9_COMMON_BUFFER_TYPE Type>
|
template <D3D9_COMMON_BUFFER_TYPE Type>
|
||||||
inline DxvkBufferSlice GetBufferSlice(VkDeviceSize offset, VkDeviceSize length) const {
|
inline DxvkBufferSlice GetBufferSlice(VkDeviceSize offset, VkDeviceSize length) const {
|
||||||
return DxvkBufferSlice(GetBuffer<Type>(), offset, length);
|
if (likely(length && offset < m_desc.Size)) {
|
||||||
|
return DxvkBufferSlice(GetBuffer<Type>(), offset,
|
||||||
|
std::min<VkDeviceSize>(m_desc.Size - offset, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
return DxvkBufferSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline DxvkBufferSliceHandle AllocMapSlice() {
|
inline DxvkBufferSliceHandle AllocMapSlice() {
|
||||||
@ -206,6 +211,10 @@ namespace dxvk {
|
|||||||
: DxvkCsThread::SynchronizeAll;
|
: DxvkCsThread::SynchronizeAll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsSysmemDynamic() const {
|
||||||
|
return m_desc.Pool == D3DPOOL_SYSTEMMEM && (m_desc.Usage & D3DUSAGE_DYNAMIC) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Rc<DxvkBuffer> CreateBuffer() const;
|
Rc<DxvkBuffer> CreateBuffer() const;
|
||||||
|
@ -31,11 +31,12 @@ namespace dxvk {
|
|||||||
AddDirtyBox(nullptr, i);
|
AddDirtyBox(nullptr, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_desc.Pool != D3DPOOL_DEFAULT) {
|
if (m_desc.Pool != D3DPOOL_DEFAULT && pSharedHandle) {
|
||||||
SetAllNeedUpload();
|
|
||||||
if (pSharedHandle) {
|
|
||||||
throw DxvkError("D3D9: Incompatible pool type for texture sharing.");
|
throw DxvkError("D3D9: Incompatible pool type for texture sharing.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsPoolManaged(m_desc.Pool)) {
|
||||||
|
SetAllNeedUpload();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_mapping = pDevice->LookupFormat(m_desc.Format);
|
m_mapping = pDevice->LookupFormat(m_desc.Format);
|
||||||
@ -117,6 +118,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
HRESULT D3D9CommonTexture::NormalizeTextureProperties(
|
HRESULT D3D9CommonTexture::NormalizeTextureProperties(
|
||||||
D3D9DeviceEx* pDevice,
|
D3D9DeviceEx* pDevice,
|
||||||
|
D3DRESOURCETYPE ResourceType,
|
||||||
D3D9_COMMON_TEXTURE_DESC* pDesc) {
|
D3D9_COMMON_TEXTURE_DESC* pDesc) {
|
||||||
auto* options = pDevice->GetOptions();
|
auto* options = pDevice->GetOptions();
|
||||||
|
|
||||||
@ -130,6 +132,11 @@ namespace dxvk {
|
|||||||
options->disableA8RT)
|
options->disableA8RT)
|
||||||
return D3DERR_INVALIDCALL;
|
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
|
// If the mapping is invalid then lets return invalid
|
||||||
// Some edge cases:
|
// Some edge cases:
|
||||||
// NULL format does not map to anything, but should succeed
|
// NULL format does not map to anything, but should succeed
|
||||||
|
@ -179,11 +179,14 @@ namespace dxvk {
|
|||||||
* Fills in undefined values and validates the texture
|
* Fills in undefined values and validates the texture
|
||||||
* parameters. Any error returned by this method should
|
* parameters. Any error returned by this method should
|
||||||
* be forwarded to the application.
|
* be forwarded to the application.
|
||||||
|
* \param [in] pDevice D3D9 device
|
||||||
|
* \param [in] ResourceType Resource type
|
||||||
* \param [in,out] pDesc Texture description
|
* \param [in,out] pDesc Texture description
|
||||||
* \returns \c S_OK if the parameters are valid
|
* \returns \c S_OK if the parameters are valid
|
||||||
*/
|
*/
|
||||||
static HRESULT NormalizeTextureProperties(
|
static HRESULT NormalizeTextureProperties(
|
||||||
D3D9DeviceEx* pDevice,
|
D3D9DeviceEx* pDevice,
|
||||||
|
D3DRESOURCETYPE ResourceType,
|
||||||
D3D9_COMMON_TEXTURE_DESC* pDesc);
|
D3D9_COMMON_TEXTURE_DESC* pDesc);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,7 +13,7 @@ namespace dxvk {
|
|||||||
DxsoProgramType ShaderStage,
|
DxsoProgramType ShaderStage,
|
||||||
DxsoConstantBuffers BufferType,
|
DxsoConstantBuffers BufferType,
|
||||||
VkDeviceSize Size)
|
VkDeviceSize Size)
|
||||||
: D3D9ConstantBuffer(pDevice, getBufferUsage(pDevice, ShaderStage, BufferType), GetShaderStage(ShaderStage),
|
: D3D9ConstantBuffer(pDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, GetShaderStage(ShaderStage),
|
||||||
computeResourceSlotId(ShaderStage, DxsoBindingType::ConstantBuffer, BufferType),
|
computeResourceSlotId(ShaderStage, DxsoBindingType::ConstantBuffer, BufferType),
|
||||||
Size) {
|
Size) {
|
||||||
|
|
||||||
@ -135,21 +135,4 @@ namespace dxvk {
|
|||||||
device->properties().extRobustness2.robustUniformBufferAccessSizeAlignment);
|
device->properties().extRobustness2.robustUniformBufferAccessSizeAlignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VkBufferUsageFlags D3D9ConstantBuffer::getBufferUsage(
|
|
||||||
D3D9DeviceEx* pDevice,
|
|
||||||
DxsoProgramType ShaderStage,
|
|
||||||
DxsoConstantBuffers BufferType) {
|
|
||||||
VkBufferUsageFlags result = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
|
||||||
|
|
||||||
// We won't always need this, but the only buffer that
|
|
||||||
// this applies to is so large that it will not matter.
|
|
||||||
if (pDevice->CanSWVP()
|
|
||||||
&& ShaderStage == DxsoProgramType::VertexShader
|
|
||||||
&& BufferType == DxsoConstantBuffers::VSConstantBuffer)
|
|
||||||
result |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -80,11 +80,6 @@ namespace dxvk {
|
|||||||
|
|
||||||
VkDeviceSize getAlignment(const Rc<DxvkDevice>& device) const;
|
VkDeviceSize getAlignment(const Rc<DxvkDevice>& device) const;
|
||||||
|
|
||||||
static VkBufferUsageFlags getBufferUsage(
|
|
||||||
D3D9DeviceEx* pDevice,
|
|
||||||
DxsoProgramType ShaderStage,
|
|
||||||
DxsoConstantBuffers BufferType);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
@ -26,14 +26,14 @@ namespace dxvk {
|
|||||||
|
|
||||||
|
|
||||||
HRESULT D3D9Cursor::SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap) {
|
HRESULT D3D9Cursor::SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap) {
|
||||||
DWORD mask[32];
|
CursorMask mask;
|
||||||
std::memset(mask, ~0, sizeof(mask));
|
std::memset(mask, ~0, sizeof(mask));
|
||||||
|
|
||||||
ICONINFO info;
|
ICONINFO info;
|
||||||
info.fIcon = FALSE;
|
info.fIcon = FALSE;
|
||||||
info.xHotspot = XHotSpot;
|
info.xHotspot = XHotSpot;
|
||||||
info.yHotspot = YHotSpot;
|
info.yHotspot = YHotSpot;
|
||||||
info.hbmMask = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 1, mask);
|
info.hbmMask = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 1, &mask[0]);
|
||||||
info.hbmColor = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 32, &bitmap[0]);
|
info.hbmColor = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 32, &bitmap[0]);
|
||||||
|
|
||||||
if (m_hCursor != nullptr)
|
if (m_hCursor != nullptr)
|
||||||
|
@ -11,11 +11,20 @@ namespace dxvk {
|
|||||||
|
|
||||||
// Format Size of 4 bytes (ARGB)
|
// Format Size of 4 bytes (ARGB)
|
||||||
using CursorBitmap = uint8_t[HardwareCursorHeight * HardwareCursorPitch];
|
using CursorBitmap = uint8_t[HardwareCursorHeight * HardwareCursorPitch];
|
||||||
|
// Monochrome mask (1 bit)
|
||||||
|
using CursorMask = uint8_t[HardwareCursorHeight * HardwareCursorWidth / 8];
|
||||||
|
|
||||||
class D3D9Cursor {
|
class D3D9Cursor {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
~D3D9Cursor() {
|
||||||
|
if (m_hCursor != nullptr)
|
||||||
|
::DestroyCursor(m_hCursor);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void UpdateCursor(int X, int Y);
|
void UpdateCursor(int X, int Y);
|
||||||
|
|
||||||
BOOL ShowCursor(BOOL bShow);
|
BOOL ShowCursor(BOOL bShow);
|
||||||
|
@ -56,6 +56,7 @@ namespace dxvk {
|
|||||||
, m_csThread ( dxvkDevice, dxvkDevice->createContext(DxvkContextType::Primary) )
|
, m_csThread ( dxvkDevice, dxvkDevice->createContext(DxvkContextType::Primary) )
|
||||||
, m_csChunk ( AllocCsChunk() )
|
, m_csChunk ( AllocCsChunk() )
|
||||||
, m_submissionFence (new sync::Fence())
|
, m_submissionFence (new sync::Fence())
|
||||||
|
, m_flushTracker (m_d3d9Options.reproducibleCommandStream)
|
||||||
, m_d3d9Interop ( this )
|
, m_d3d9Interop ( this )
|
||||||
, m_d3d9On12 ( this )
|
, m_d3d9On12 ( this )
|
||||||
, m_d3d8Bridge ( this ) {
|
, m_d3d8Bridge ( this ) {
|
||||||
@ -455,6 +456,8 @@ namespace dxvk {
|
|||||||
SetDepthStencilSurface(nullptr);
|
SetDepthStencilSurface(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_flags.clr(D3D9DeviceFlag::InScene);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Before calling the IDirect3DDevice9::Reset method for a device,
|
* Before calling the IDirect3DDevice9::Reset method for a device,
|
||||||
* an application should release any explicit render targets,
|
* an application should release any explicit render targets,
|
||||||
@ -464,7 +467,7 @@ namespace dxvk {
|
|||||||
* We have to check after ResetState clears the references held by SetTexture, etc.
|
* We have to check after ResetState clears the references held by SetTexture, etc.
|
||||||
* This matches what Windows D3D9 does.
|
* This matches what Windows D3D9 does.
|
||||||
*/
|
*/
|
||||||
if (unlikely(m_losableResourceCounter.load() != 0 && !IsExtended())) {
|
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()));
|
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;
|
m_deviceLostState = D3D9DeviceLostState::NotReset;
|
||||||
return D3DERR_INVALIDCALL;
|
return D3DERR_INVALIDCALL;
|
||||||
@ -479,9 +482,20 @@ namespace dxvk {
|
|||||||
return hr;
|
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();
|
Flush();
|
||||||
SynchronizeCsThread(DxvkCsThread::SynchronizeAll);
|
SynchronizeCsThread(DxvkCsThread::SynchronizeAll);
|
||||||
|
|
||||||
|
if (m_d3d9Options.deferSurfaceCreation)
|
||||||
|
m_deviceHasBeenReset = true;
|
||||||
|
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,7 +595,7 @@ namespace dxvk {
|
|||||||
|| (Usage & D3DUSAGE_DYNAMIC)
|
|| (Usage & D3DUSAGE_DYNAMIC)
|
||||||
|| IsVendorFormat(EnumerateFormat(Format));
|
|| IsVendorFormat(EnumerateFormat(Format));
|
||||||
|
|
||||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
|
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc)))
|
||||||
return D3DERR_INVALIDCALL;
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -651,7 +665,7 @@ namespace dxvk {
|
|||||||
|| (Usage & D3DUSAGE_DYNAMIC)
|
|| (Usage & D3DUSAGE_DYNAMIC)
|
||||||
|| IsVendorFormat(EnumerateFormat(Format));
|
|| IsVendorFormat(EnumerateFormat(Format));
|
||||||
|
|
||||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
|
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_VOLUMETEXTURE, &desc)))
|
||||||
return D3DERR_INVALIDCALL;
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -708,7 +722,7 @@ namespace dxvk {
|
|||||||
|| (Usage & D3DUSAGE_DYNAMIC)
|
|| (Usage & D3DUSAGE_DYNAMIC)
|
||||||
|| IsVendorFormat(EnumerateFormat(Format));
|
|| IsVendorFormat(EnumerateFormat(Format));
|
||||||
|
|
||||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
|
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_CUBETEXTURE, &desc)))
|
||||||
return D3DERR_INVALIDCALL;
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1034,6 +1048,9 @@ namespace dxvk {
|
|||||||
if (srcTexInfo->Desc()->Format != dstTexInfo->Desc()->Format)
|
if (srcTexInfo->Desc()->Format != dstTexInfo->Desc()->Format)
|
||||||
return D3DERR_INVALIDCALL;
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
|
if (src->GetSurfaceExtent() != dst->GetSurfaceExtent())
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
if (dstTexInfo->Desc()->Pool == D3DPOOL_DEFAULT)
|
if (dstTexInfo->Desc()->Pool == D3DPOOL_DEFAULT)
|
||||||
return this->StretchRect(pRenderTarget, nullptr, pDestSurface, nullptr, D3DTEXF_NONE);
|
return this->StretchRect(pRenderTarget, nullptr, pDestSurface, nullptr, D3DTEXF_NONE);
|
||||||
|
|
||||||
@ -1077,7 +1094,12 @@ namespace dxvk {
|
|||||||
if (unlikely(iSwapChain != 0))
|
if (unlikely(iSwapChain != 0))
|
||||||
return D3DERR_INVALIDCALL;
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
return m_implicitSwapchain->GetFrontBufferData(pDestSurface);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1122,6 +1144,9 @@ namespace dxvk {
|
|||||||
if (unlikely((srcSubresource.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) && m_flags.test(D3D9DeviceFlag::InScene)))
|
if (unlikely((srcSubresource.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) && m_flags.test(D3D9DeviceFlag::InScene)))
|
||||||
return D3DERR_INVALIDCALL;
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
|
if (unlikely(Filter != D3DTEXF_NONE && Filter != D3DTEXF_LINEAR && Filter != D3DTEXF_POINT))
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
VkExtent3D srcExtent = srcImage->mipLevelExtent(srcSubresource.mipLevel);
|
VkExtent3D srcExtent = srcImage->mipLevelExtent(srcSubresource.mipLevel);
|
||||||
VkExtent3D dstExtent = dstImage->mipLevelExtent(dstSubresource.mipLevel);
|
VkExtent3D dstExtent = dstImage->mipLevelExtent(dstSubresource.mipLevel);
|
||||||
|
|
||||||
@ -1200,8 +1225,51 @@ namespace dxvk {
|
|||||||
uint32_t(blitInfo.dstOffsets[1].y - blitInfo.dstOffsets[0].y),
|
uint32_t(blitInfo.dstOffsets[1].y - blitInfo.dstOffsets[0].y),
|
||||||
uint32_t(blitInfo.dstOffsets[1].z - blitInfo.dstOffsets[0].z) };
|
uint32_t(blitInfo.dstOffsets[1].z - blitInfo.dstOffsets[0].z) };
|
||||||
|
|
||||||
|
bool srcIsDepth = IsDepthFormat(srcFormat);
|
||||||
|
bool dstIsDepth = IsDepthFormat(dstFormat);
|
||||||
|
if (unlikely(srcIsDepth || dstIsDepth)) {
|
||||||
|
if (unlikely(!srcIsDepth || !dstIsDepth))
|
||||||
|
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)
|
// Copies would only work if the extents match. (ie. no stretching)
|
||||||
bool stretch = srcCopyExtent != dstCopyExtent;
|
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(dstIsDepth))
|
||||||
|
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;
|
fastPath &= !stretch;
|
||||||
|
|
||||||
if (!fastPath || needsResolve) {
|
if (!fastPath || needsResolve) {
|
||||||
@ -1595,6 +1663,23 @@ namespace dxvk {
|
|||||||
|
|
||||||
m_flags.clr(D3D9DeviceFlag::InScene);
|
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 = 0; i < caps::MaxStreams; i++) {
|
||||||
|
if (m_state.vertexBuffers[i].vertexBuffer == nullptr) {
|
||||||
|
EmitCs([cIndex = i](DxvkContext* ctx) {
|
||||||
|
ctx->bindVertexBuffer(cIndex, DxvkBufferSlice(), 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2360,6 +2445,7 @@ namespace dxvk {
|
|||||||
try {
|
try {
|
||||||
const Com<D3D9StateBlock> sb = new D3D9StateBlock(this, ConvertStateBlockType(Type));
|
const Com<D3D9StateBlock> sb = new D3D9StateBlock(this, ConvertStateBlockType(Type));
|
||||||
*ppSB = sb.ref();
|
*ppSB = sb.ref();
|
||||||
|
if (!m_isD3D8Compatible)
|
||||||
m_losableResourceCounter++;
|
m_losableResourceCounter++;
|
||||||
|
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
@ -2392,6 +2478,7 @@ namespace dxvk {
|
|||||||
return D3DERR_INVALIDCALL;
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
*ppSB = m_recorder.ref();
|
*ppSB = m_recorder.ref();
|
||||||
|
if (!m_isD3D8Compatible)
|
||||||
m_losableResourceCounter++;
|
m_losableResourceCounter++;
|
||||||
m_recorder = nullptr;
|
m_recorder = nullptr;
|
||||||
|
|
||||||
@ -2610,7 +2697,21 @@ namespace dxvk {
|
|||||||
if (unlikely(!PrimitiveCount))
|
if (unlikely(!PrimitiveCount))
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
||||||
PrepareDraw(PrimitiveType);
|
bool dynamicSysmemVBOs;
|
||||||
|
uint32_t firstIndex = 0;
|
||||||
|
int32_t baseVertexIndex = 0;
|
||||||
|
uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount);
|
||||||
|
UploadDynamicSysmemBuffers(
|
||||||
|
StartVertex,
|
||||||
|
vertexCount,
|
||||||
|
firstIndex,
|
||||||
|
0,
|
||||||
|
baseVertexIndex,
|
||||||
|
&dynamicSysmemVBOs,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
PrepareDraw(PrimitiveType, !dynamicSysmemVBOs, false);
|
||||||
|
|
||||||
EmitCs([this,
|
EmitCs([this,
|
||||||
cPrimType = PrimitiveType,
|
cPrimType = PrimitiveType,
|
||||||
@ -2631,7 +2732,6 @@ namespace dxvk {
|
|||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawIndexedPrimitive(
|
HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawIndexedPrimitive(
|
||||||
D3DPRIMITIVETYPE PrimitiveType,
|
D3DPRIMITIVETYPE PrimitiveType,
|
||||||
INT BaseVertexIndex,
|
INT BaseVertexIndex,
|
||||||
@ -2647,7 +2747,20 @@ namespace dxvk {
|
|||||||
if (unlikely(!PrimitiveCount))
|
if (unlikely(!PrimitiveCount))
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
||||||
PrepareDraw(PrimitiveType);
|
bool dynamicSysmemVBOs;
|
||||||
|
bool dynamicSysmemIBO;
|
||||||
|
uint32_t indexCount = GetVertexCount(PrimitiveType, PrimitiveCount);
|
||||||
|
UploadDynamicSysmemBuffers(
|
||||||
|
MinVertexIndex,
|
||||||
|
NumVertices,
|
||||||
|
StartIndex,
|
||||||
|
indexCount,
|
||||||
|
BaseVertexIndex,
|
||||||
|
&dynamicSysmemVBOs,
|
||||||
|
&dynamicSysmemIBO
|
||||||
|
);
|
||||||
|
|
||||||
|
PrepareDraw(PrimitiveType, !dynamicSysmemVBOs, !dynamicSysmemIBO);
|
||||||
|
|
||||||
EmitCs([this,
|
EmitCs([this,
|
||||||
cPrimType = PrimitiveType,
|
cPrimType = PrimitiveType,
|
||||||
@ -2683,7 +2796,7 @@ namespace dxvk {
|
|||||||
if (unlikely(!PrimitiveCount))
|
if (unlikely(!PrimitiveCount))
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
||||||
PrepareDraw(PrimitiveType);
|
PrepareDraw(PrimitiveType, false, false);
|
||||||
|
|
||||||
uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount);
|
uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount);
|
||||||
|
|
||||||
@ -2735,7 +2848,7 @@ namespace dxvk {
|
|||||||
if (unlikely(!PrimitiveCount))
|
if (unlikely(!PrimitiveCount))
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
||||||
PrepareDraw(PrimitiveType);
|
PrepareDraw(PrimitiveType, false, false);
|
||||||
|
|
||||||
uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount);
|
uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount);
|
||||||
|
|
||||||
@ -2822,7 +2935,7 @@ namespace dxvk {
|
|||||||
D3D9CommonBuffer* dst = static_cast<D3D9VertexBuffer*>(pDestBuffer)->GetCommonBuffer();
|
D3D9CommonBuffer* dst = static_cast<D3D9VertexBuffer*>(pDestBuffer)->GetCommonBuffer();
|
||||||
D3D9VertexDecl* decl = static_cast<D3D9VertexDecl*> (pVertexDecl);
|
D3D9VertexDecl* decl = static_cast<D3D9VertexDecl*> (pVertexDecl);
|
||||||
|
|
||||||
PrepareDraw(D3DPT_FORCE_DWORD);
|
PrepareDraw(D3DPT_FORCE_DWORD, true, true);
|
||||||
|
|
||||||
if (decl == nullptr) {
|
if (decl == nullptr) {
|
||||||
DWORD FVF = dst->Desc()->FVF;
|
DWORD FVF = dst->Desc()->FVF;
|
||||||
@ -2837,19 +2950,24 @@ namespace dxvk {
|
|||||||
decl = iter->second.ptr();
|
decl = iter->second.ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t offset = DestIndex * decl->GetSize();
|
uint32_t offset = DestIndex * decl->GetSize(0);
|
||||||
|
|
||||||
auto slice = dst->GetBufferSlice<D3D9_COMMON_BUFFER_TYPE_REAL>();
|
auto slice = dst->GetBufferSlice<D3D9_COMMON_BUFFER_TYPE_REAL>();
|
||||||
slice = slice.subSlice(offset, slice.length() - offset);
|
slice = slice.subSlice(offset, slice.length() - offset);
|
||||||
|
|
||||||
|
D3D9CompactVertexElements elements;
|
||||||
|
for (const D3DVERTEXELEMENT9& element : decl->GetElements()) {
|
||||||
|
elements.emplace_back(element);
|
||||||
|
}
|
||||||
|
|
||||||
EmitCs([this,
|
EmitCs([this,
|
||||||
cDecl = ref(decl),
|
cVertexElements = std::move(elements),
|
||||||
cVertexCount = VertexCount,
|
cVertexCount = VertexCount,
|
||||||
cStartIndex = SrcStartIndex,
|
cStartIndex = SrcStartIndex,
|
||||||
cInstanceCount = GetInstanceCount(),
|
cInstanceCount = GetInstanceCount(),
|
||||||
cBufferSlice = slice
|
cBufferSlice = slice
|
||||||
](DxvkContext* ctx) mutable {
|
](DxvkContext* ctx) mutable {
|
||||||
Rc<DxvkShader> shader = m_swvpEmulator.GetShaderModule(this, cDecl);
|
Rc<DxvkShader> shader = m_swvpEmulator.GetShaderModule(this, std::move(cVertexElements));
|
||||||
|
|
||||||
auto drawInfo = GenerateDrawInfo(D3DPT_POINTLIST, cVertexCount, cInstanceCount);
|
auto drawInfo = GenerateDrawInfo(D3DPT_POINTLIST, cVertexCount, cInstanceCount);
|
||||||
|
|
||||||
@ -2884,7 +3002,7 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dst->GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_BUFFER) {
|
if (dst->GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_BUFFER) {
|
||||||
uint32_t copySize = VertexCount * decl->GetSize();
|
uint32_t copySize = VertexCount * decl->GetSize(0);
|
||||||
|
|
||||||
EmitCs([
|
EmitCs([
|
||||||
cSrcBuffer = dst->GetBuffer<D3D9_COMMON_BUFFER_TYPE_REAL>(),
|
cSrcBuffer = dst->GetBuffer<D3D9_COMMON_BUFFER_TYPE_REAL>(),
|
||||||
@ -3220,6 +3338,11 @@ namespace dxvk {
|
|||||||
|
|
||||||
vbo.offset = OffsetInBytes;
|
vbo.offset = OffsetInBytes;
|
||||||
vbo.stride = Stride;
|
vbo.stride = Stride;
|
||||||
|
} 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)
|
if (needsUpdate)
|
||||||
@ -3325,6 +3448,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
m_state.indices = buffer;
|
m_state.indices = buffer;
|
||||||
|
|
||||||
|
if (buffer != nullptr)
|
||||||
BindIndices();
|
BindIndices();
|
||||||
|
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
@ -3726,7 +3850,7 @@ namespace dxvk {
|
|||||||
desc.IsAttachmentOnly = TRUE;
|
desc.IsAttachmentOnly = TRUE;
|
||||||
desc.IsLockable = Lockable;
|
desc.IsLockable = Lockable;
|
||||||
|
|
||||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
|
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc)))
|
||||||
return D3DERR_INVALIDCALL;
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -3774,7 +3898,7 @@ namespace dxvk {
|
|||||||
// Docs: Off-screen plain surfaces are always lockable, regardless of their pool types.
|
// Docs: Off-screen plain surfaces are always lockable, regardless of their pool types.
|
||||||
desc.IsLockable = TRUE;
|
desc.IsLockable = TRUE;
|
||||||
|
|
||||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
|
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc)))
|
||||||
return D3DERR_INVALIDCALL;
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
if (pSharedHandle != nullptr && Pool != D3DPOOL_DEFAULT)
|
if (pSharedHandle != nullptr && Pool != D3DPOOL_DEFAULT)
|
||||||
@ -3829,7 +3953,7 @@ namespace dxvk {
|
|||||||
// Docs don't say anything, so just assume it's lockable.
|
// Docs don't say anything, so just assume it's lockable.
|
||||||
desc.IsLockable = TRUE;
|
desc.IsLockable = TRUE;
|
||||||
|
|
||||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
|
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc)))
|
||||||
return D3DERR_INVALIDCALL;
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -5087,6 +5211,193 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void D3D9DeviceEx::UploadDynamicSysmemBuffers(
|
||||||
|
UINT& FirstVertexIndex,
|
||||||
|
UINT NumVertices,
|
||||||
|
UINT& FirstIndex,
|
||||||
|
UINT NumIndices,
|
||||||
|
INT& BaseVertexIndex,
|
||||||
|
bool* pDynamicVBOs,
|
||||||
|
bool* pDynamicIBO
|
||||||
|
) {
|
||||||
|
bool dynamicSysmemVBOs = true;
|
||||||
|
for (uint32_t i = 0; i < caps::MaxStreams && dynamicSysmemVBOs; i++) {
|
||||||
|
auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer);
|
||||||
|
dynamicSysmemVBOs &= vbo == nullptr || vbo->IsSysmemDynamic();
|
||||||
|
}
|
||||||
|
D3D9CommonBuffer* ibo = GetCommonBuffer(m_state.indices);
|
||||||
|
bool dynamicSysmemIBO = NumIndices != 0 && ibo != nullptr && ibo->IsSysmemDynamic();
|
||||||
|
|
||||||
|
*pDynamicVBOs = dynamicSysmemVBOs;
|
||||||
|
|
||||||
|
if (pDynamicIBO)
|
||||||
|
*pDynamicIBO = dynamicSysmemIBO;
|
||||||
|
|
||||||
|
if (likely(!dynamicSysmemVBOs && !dynamicSysmemIBO))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 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<VBOCopy, caps::MaxStreams> vboCopies = {};
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < caps::MaxStreams && dynamicSysmemVBOs; i++) {
|
||||||
|
auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer);
|
||||||
|
if (likely(vbo == nullptr)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
for (uint32_t i = 0; i < caps::MaxStreams && dynamicSysmemVBOs; i++) {
|
||||||
|
const VBOCopy& copy = vboCopies[i];
|
||||||
|
|
||||||
|
if (likely(copy.copyBufferLength != 0)) {
|
||||||
|
const auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer);
|
||||||
|
uint8_t* data = reinterpret_cast<uint8_t*>(upSlice.mapPtr) + copy.dstOffset;
|
||||||
|
const uint8_t* src = reinterpret_cast<uint8_t*>(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dynamicSysmemVBOs) {
|
||||||
|
// 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<uint8_t*>(upSlice.mapPtr) + iboUPBufferOffset;
|
||||||
|
uint8_t* src = reinterpret_cast<uint8_t*>(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::EmitCsChunk(DxvkCsChunkRef&& chunk) {
|
void D3D9DeviceEx::EmitCsChunk(DxvkCsChunkRef&& chunk) {
|
||||||
m_csSeqNum = m_csThread.dispatchChunk(std::move(chunk));
|
m_csSeqNum = m_csThread.dispatchChunk(std::move(chunk));
|
||||||
}
|
}
|
||||||
@ -5133,7 +5444,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
// Round to nearest
|
// Round to nearest
|
||||||
_controlfp(_RC_NEAR, _MCW_RC);
|
_controlfp(_RC_NEAR, _MCW_RC);
|
||||||
#elif (defined(__GNUC__) || defined(__MINGW32__)) && (defined(__i386__) || defined(__x86_64__) || defined(__ia64))
|
#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.
|
// For GCC/MinGW we can use inline asm to set it.
|
||||||
// This only works for x86 and x64 processors however.
|
// This only works for x86 and x64 processors however.
|
||||||
|
|
||||||
@ -5255,7 +5566,7 @@ namespace dxvk {
|
|||||||
uint32_t floatCount = m_vsFloatConstsCount;
|
uint32_t floatCount = m_vsFloatConstsCount;
|
||||||
if (constSet.meta.needsConstantCopies) {
|
if (constSet.meta.needsConstantCopies) {
|
||||||
auto shader = GetCommonShader(m_state.vertexShader);
|
auto shader = GetCommonShader(m_state.vertexShader);
|
||||||
floatCount = std::max(floatCount, shader->GetMaxDefinedConstant());
|
floatCount = std::max(floatCount, shader->GetMaxDefinedConstant() + 1);
|
||||||
}
|
}
|
||||||
floatCount = std::min(floatCount, constSet.meta.maxConstIndexF);
|
floatCount = std::min(floatCount, constSet.meta.maxConstIndexF);
|
||||||
|
|
||||||
@ -5317,7 +5628,7 @@ namespace dxvk {
|
|||||||
uint32_t floatCount = ShaderStage == DxsoProgramType::VertexShader ? m_vsFloatConstsCount : m_psFloatConstsCount;
|
uint32_t floatCount = ShaderStage == DxsoProgramType::VertexShader ? m_vsFloatConstsCount : m_psFloatConstsCount;
|
||||||
if (constSet.meta.needsConstantCopies) {
|
if (constSet.meta.needsConstantCopies) {
|
||||||
auto shader = GetCommonShader(Shader);
|
auto shader = GetCommonShader(Shader);
|
||||||
floatCount = std::max(floatCount, shader->GetMaxDefinedConstant());
|
floatCount = std::max(floatCount, shader->GetMaxDefinedConstant() + 1);
|
||||||
}
|
}
|
||||||
floatCount = std::min(constSet.meta.maxConstIndexF, floatCount);
|
floatCount = std::min(constSet.meta.maxConstIndexF, floatCount);
|
||||||
|
|
||||||
@ -5450,10 +5761,13 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <bool Synchronize9On12>
|
||||||
void D3D9DeviceEx::Flush() {
|
void D3D9DeviceEx::ExecuteFlush() {
|
||||||
D3D9DeviceLock lock = LockDevice();
|
D3D9DeviceLock lock = LockDevice();
|
||||||
|
|
||||||
|
if constexpr (Synchronize9On12)
|
||||||
|
m_submitStatus.result = VK_NOT_READY;
|
||||||
|
|
||||||
m_initializer->Flush();
|
m_initializer->Flush();
|
||||||
m_converter->Flush();
|
m_converter->Flush();
|
||||||
|
|
||||||
@ -5465,16 +5779,32 @@ namespace dxvk {
|
|||||||
|
|
||||||
EmitCs<false>([
|
EmitCs<false>([
|
||||||
cSubmissionFence = m_submissionFence,
|
cSubmissionFence = m_submissionFence,
|
||||||
cSubmissionId = submissionId
|
cSubmissionId = submissionId,
|
||||||
|
cSubmissionStatus = Synchronize9On12 ? &m_submitStatus : nullptr
|
||||||
] (DxvkContext* ctx) {
|
] (DxvkContext* ctx) {
|
||||||
ctx->signal(cSubmissionFence, cSubmissionId);
|
ctx->signal(cSubmissionFence, cSubmissionId);
|
||||||
ctx->flushCommandList(nullptr);
|
ctx->flushCommandList(cSubmissionStatus);
|
||||||
});
|
});
|
||||||
|
|
||||||
FlushCsChunk();
|
FlushCsChunk();
|
||||||
|
|
||||||
m_flushSeqNum = m_csSeqNum;
|
m_flushSeqNum = m_csSeqNum;
|
||||||
m_flushTracker.notifyFlush(m_flushSeqNum, submissionId);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void D3D9DeviceEx::Flush() {
|
||||||
|
ExecuteFlush<false>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void D3D9DeviceEx::FlushAndSync9On12() {
|
||||||
|
ExecuteFlush<true>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -6449,7 +6779,7 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void D3D9DeviceEx::PrepareDraw(D3DPRIMITIVETYPE PrimitiveType) {
|
void D3D9DeviceEx::PrepareDraw(D3DPRIMITIVETYPE PrimitiveType, bool UploadVBOs, bool UploadIBO) {
|
||||||
if (unlikely(m_activeHazardsRT != 0 || m_activeHazardsDS != 0))
|
if (unlikely(m_activeHazardsRT != 0 || m_activeHazardsDS != 0))
|
||||||
MarkRenderHazards();
|
MarkRenderHazards();
|
||||||
|
|
||||||
@ -6462,7 +6792,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
for (uint32_t i = 0; i < caps::MaxStreams; i++) {
|
for (uint32_t i = 0; i < caps::MaxStreams; i++) {
|
||||||
auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer);
|
auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer);
|
||||||
if (vbo != nullptr && vbo->NeedsUpload())
|
if (vbo != nullptr && vbo->NeedsUpload() && UploadVBOs)
|
||||||
FlushBuffer(vbo);
|
FlushBuffer(vbo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6478,7 +6808,7 @@ namespace dxvk {
|
|||||||
GenerateTextureMips(texturesToGen);
|
GenerateTextureMips(texturesToGen);
|
||||||
|
|
||||||
auto* ibo = GetCommonBuffer(m_state.indices);
|
auto* ibo = GetCommonBuffer(m_state.indices);
|
||||||
if (ibo != nullptr && ibo->NeedsUpload())
|
if (ibo != nullptr && ibo->NeedsUpload() && UploadIBO)
|
||||||
FlushBuffer(ibo);
|
FlushBuffer(ibo);
|
||||||
|
|
||||||
UpdateFog();
|
UpdateFog();
|
||||||
@ -6611,6 +6941,19 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BindSpecConstants();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -6981,6 +7324,7 @@ namespace dxvk {
|
|||||||
key.Data.Contents.TransformFlags |= transformFlags << (i * 3);
|
key.Data.Contents.TransformFlags |= transformFlags << (i * 3);
|
||||||
key.Data.Contents.TexcoordFlags |= indexFlags << (i * 3);
|
key.Data.Contents.TexcoordFlags |= indexFlags << (i * 3);
|
||||||
key.Data.Contents.TexcoordIndices |= index << (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.TexcoordDeclMask = m_state.vertexDecl != nullptr ? m_state.vertexDecl->GetTexcoordMask() : 0;
|
||||||
@ -7151,6 +7495,8 @@ namespace dxvk {
|
|||||||
|
|
||||||
stage.Projected = (ttff & D3DTTFF_PROJECTED) ? 1 : 0;
|
stage.Projected = (ttff & D3DTTFF_PROJECTED) ? 1 : 0;
|
||||||
stage.ProjectedCount = (ttff & D3DTTFF_PROJECTED) ? count : 0;
|
stage.ProjectedCount = (ttff & D3DTTFF_PROJECTED) ? count : 0;
|
||||||
|
|
||||||
|
stage.SampleDref = (m_depthTextures & (1 << idx)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& stage0 = key.Stages[0].Contents;
|
auto& stage0 = key.Stages[0].Contents;
|
||||||
@ -7433,6 +7779,8 @@ namespace dxvk {
|
|||||||
rs[D3DRS_CLIPPLANEENABLE] = 0;
|
rs[D3DRS_CLIPPLANEENABLE] = 0;
|
||||||
m_flags.set(D3D9DeviceFlag::DirtyClipPlanes);
|
m_flags.set(D3D9DeviceFlag::DirtyClipPlanes);
|
||||||
|
|
||||||
|
const VkPhysicalDeviceLimits& limits = m_dxvkDevice->adapter()->deviceProperties().limits;
|
||||||
|
|
||||||
rs[D3DRS_POINTSPRITEENABLE] = FALSE;
|
rs[D3DRS_POINTSPRITEENABLE] = FALSE;
|
||||||
rs[D3DRS_POINTSCALEENABLE] = FALSE;
|
rs[D3DRS_POINTSCALEENABLE] = FALSE;
|
||||||
rs[D3DRS_POINTSCALE_A] = bit::cast<DWORD>(1.0f);
|
rs[D3DRS_POINTSCALE_A] = bit::cast<DWORD>(1.0f);
|
||||||
@ -7440,7 +7788,7 @@ namespace dxvk {
|
|||||||
rs[D3DRS_POINTSCALE_C] = bit::cast<DWORD>(0.0f);
|
rs[D3DRS_POINTSCALE_C] = bit::cast<DWORD>(0.0f);
|
||||||
rs[D3DRS_POINTSIZE] = bit::cast<DWORD>(1.0f);
|
rs[D3DRS_POINTSIZE] = bit::cast<DWORD>(1.0f);
|
||||||
rs[D3DRS_POINTSIZE_MIN] = bit::cast<DWORD>(1.0f);
|
rs[D3DRS_POINTSIZE_MIN] = bit::cast<DWORD>(1.0f);
|
||||||
rs[D3DRS_POINTSIZE_MAX] = bit::cast<DWORD>(64.0f);
|
rs[D3DRS_POINTSIZE_MAX] = bit::cast<DWORD>(limits.pointSizeRange[1]);
|
||||||
UpdatePushConstant<D3D9RenderStateItem::PointSize>();
|
UpdatePushConstant<D3D9RenderStateItem::PointSize>();
|
||||||
UpdatePushConstant<D3D9RenderStateItem::PointSizeMin>();
|
UpdatePushConstant<D3D9RenderStateItem::PointSizeMin>();
|
||||||
UpdatePushConstant<D3D9RenderStateItem::PointSizeMax>();
|
UpdatePushConstant<D3D9RenderStateItem::PointSizeMax>();
|
||||||
@ -7614,8 +7962,10 @@ namespace dxvk {
|
|||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
m_implicitSwapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode);
|
m_implicitSwapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode);
|
||||||
|
m_mostRecentlyUsedSwapchain = m_implicitSwapchain.ptr();
|
||||||
|
}
|
||||||
|
|
||||||
if (pPresentationParameters->EnableAutoDepthStencil) {
|
if (pPresentationParameters->EnableAutoDepthStencil) {
|
||||||
D3D9_COMMON_TEXTURE_DESC desc;
|
D3D9_COMMON_TEXTURE_DESC desc;
|
||||||
@ -7635,7 +7985,7 @@ namespace dxvk {
|
|||||||
// Docs: Also note that - unlike textures - swap chain back buffers, render targets [..] can be locked
|
// Docs: Also note that - unlike textures - swap chain back buffers, render targets [..] can be locked
|
||||||
desc.IsLockable = TRUE;
|
desc.IsLockable = TRUE;
|
||||||
|
|
||||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
|
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc)))
|
||||||
return D3DERR_NOTAVAILABLE;
|
return D3DERR_NOTAVAILABLE;
|
||||||
|
|
||||||
m_autoDepthStencil = new D3D9Surface(this, &desc, nullptr, nullptr);
|
m_autoDepthStencil = new D3D9Surface(this, &desc, nullptr, nullptr);
|
||||||
|
@ -66,6 +66,8 @@ namespace dxvk {
|
|||||||
DirtyInputLayout,
|
DirtyInputLayout,
|
||||||
DirtyViewportScissor,
|
DirtyViewportScissor,
|
||||||
DirtyMultiSampleState,
|
DirtyMultiSampleState,
|
||||||
|
DirtyVertexBuffers,
|
||||||
|
DirtyIndexBuffer,
|
||||||
|
|
||||||
DirtyFogState,
|
DirtyFogState,
|
||||||
DirtyFogColor,
|
DirtyFogColor,
|
||||||
@ -764,6 +766,24 @@ namespace dxvk {
|
|||||||
HRESULT UnlockBuffer(
|
HRESULT UnlockBuffer(
|
||||||
D3D9CommonBuffer* pResource);
|
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 UploadDynamicSysmemBuffers(
|
||||||
|
UINT& FirstVertexIndex,
|
||||||
|
UINT NumVertices,
|
||||||
|
UINT& FirstIndex,
|
||||||
|
UINT NumIndices,
|
||||||
|
INT& BaseVertexIndex,
|
||||||
|
bool* pDynamicVBOs,
|
||||||
|
bool* pDynamicIBO);
|
||||||
|
|
||||||
|
|
||||||
void SetupFPU();
|
void SetupFPU();
|
||||||
|
|
||||||
int64_t DetermineInitialTextureMemory();
|
int64_t DetermineInitialTextureMemory();
|
||||||
@ -773,6 +793,7 @@ namespace dxvk {
|
|||||||
void SynchronizeCsThread(uint64_t SequenceNumber);
|
void SynchronizeCsThread(uint64_t SequenceNumber);
|
||||||
|
|
||||||
void Flush();
|
void Flush();
|
||||||
|
void FlushAndSync9On12();
|
||||||
|
|
||||||
void EndFrame();
|
void EndFrame();
|
||||||
|
|
||||||
@ -816,7 +837,7 @@ namespace dxvk {
|
|||||||
inline bool IsAlphaToCoverageEnabled() {
|
inline bool IsAlphaToCoverageEnabled() {
|
||||||
const bool alphaTest = m_state.renderStates[D3DRS_ALPHATESTENABLE] != 0;
|
const bool alphaTest = m_state.renderStates[D3DRS_ALPHATESTENABLE] != 0;
|
||||||
|
|
||||||
return m_amdATOC || (m_nvATOC && alphaTest);
|
return (m_amdATOC || (m_nvATOC && alphaTest)) && m_flags.test(D3D9DeviceFlag::ValidSampleMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool IsDepthBiasEnabled() {
|
inline bool IsDepthBiasEnabled() {
|
||||||
@ -895,7 +916,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
uint32_t GetInstanceCount() const;
|
uint32_t GetInstanceCount() const;
|
||||||
|
|
||||||
void PrepareDraw(D3DPRIMITIVETYPE PrimitiveType);
|
void PrepareDraw(D3DPRIMITIVETYPE PrimitiveType, bool UploadVBOs, bool UploadIBOs);
|
||||||
|
|
||||||
template <DxsoProgramType ShaderStage>
|
template <DxsoProgramType ShaderStage>
|
||||||
void BindShader(
|
void BindShader(
|
||||||
@ -970,6 +991,17 @@ namespace dxvk {
|
|||||||
void TouchMappedTexture(D3D9CommonTexture* pTexture);
|
void TouchMappedTexture(D3D9CommonTexture* pTexture);
|
||||||
void RemoveMappedTexture(D3D9CommonTexture* pTexture);
|
void RemoveMappedTexture(D3D9CommonTexture* pTexture);
|
||||||
|
|
||||||
|
bool IsD3D8Compatible() const {
|
||||||
|
return m_isD3D8Compatible;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetD3D8CompatibilityMode(bool compatMode) {
|
||||||
|
if (compatMode)
|
||||||
|
Logger::info("The D3D9 device is now operating in D3D8 compatibility mode.");
|
||||||
|
|
||||||
|
m_isD3D8Compatible = compatMode;
|
||||||
|
}
|
||||||
|
|
||||||
// Device Lost
|
// Device Lost
|
||||||
bool IsDeviceLost() const {
|
bool IsDeviceLost() const {
|
||||||
return m_deviceLostState != D3D9DeviceLostState::Ok;
|
return m_deviceLostState != D3D9DeviceLostState::Ok;
|
||||||
@ -1022,6 +1054,15 @@ namespace dxvk {
|
|||||||
bool CanSWVP() const {
|
bool CanSWVP() const {
|
||||||
return m_behaviorFlags & (D3DCREATE_MIXED_VERTEXPROCESSING | D3DCREATE_SOFTWARE_VERTEXPROCESSING);
|
return m_behaviorFlags & (D3DCREATE_MIXED_VERTEXPROCESSING | D3DCREATE_SOFTWARE_VERTEXPROCESSING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Device Reset detection for D3D9SwapChainEx::Present
|
||||||
|
bool IsDeviceReset() {
|
||||||
|
return std::exchange(m_deviceHasBeenReset, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool Synchronize9On12>
|
||||||
|
void ExecuteFlush();
|
||||||
|
|
||||||
void DetermineConstantLayouts(bool canSWVP);
|
void DetermineConstantLayouts(bool canSWVP);
|
||||||
|
|
||||||
D3D9BufferSlice AllocUPBuffer(VkDeviceSize size);
|
D3D9BufferSlice AllocUPBuffer(VkDeviceSize size);
|
||||||
@ -1046,7 +1087,7 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t GetUPBufferSize(uint32_t vertexCount, uint32_t stride) {
|
inline uint32_t GetUPBufferSize(uint32_t vertexCount, uint32_t stride) {
|
||||||
return (vertexCount - 1) * stride + std::max(m_state.vertexDecl->GetSize(), stride);
|
return (vertexCount - 1) * stride + std::max(m_state.vertexDecl->GetSize(0), stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void FillUPVertexBuffer(void* buffer, const void* userData, uint32_t dataSize, uint32_t bufferSize) {
|
inline void FillUPVertexBuffer(void* buffer, const void* userData, uint32_t dataSize, uint32_t bufferSize) {
|
||||||
@ -1204,6 +1245,34 @@ namespace dxvk {
|
|||||||
|
|
||||||
uint64_t GetCurrentSequenceNumber();
|
uint64_t GetCurrentSequenceNumber();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the swapchain that was used the most recently for presenting
|
||||||
|
* Has to be externally synchronized.
|
||||||
|
*
|
||||||
|
* @return D3D9SwapChainEx* Swapchain
|
||||||
|
*/
|
||||||
|
D3D9SwapChainEx* GetMostRecentlyUsedSwapchain() {
|
||||||
|
return m_mostRecentlyUsedSwapchain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the swapchain that was used the most recently for presenting
|
||||||
|
* Has to be externally synchronized.
|
||||||
|
*
|
||||||
|
* @param swapchain Swapchain
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
Com<D3D9InterfaceEx> m_parent;
|
Com<D3D9InterfaceEx> m_parent;
|
||||||
D3DDEVTYPE m_deviceType;
|
D3DDEVTYPE m_deviceType;
|
||||||
HWND m_window;
|
HWND m_window;
|
||||||
@ -1318,6 +1387,7 @@ namespace dxvk {
|
|||||||
D3D9ShaderMasks m_psShaderMasks = FixedFunctionMask;
|
D3D9ShaderMasks m_psShaderMasks = FixedFunctionMask;
|
||||||
|
|
||||||
bool m_isSWVP;
|
bool m_isSWVP;
|
||||||
|
bool m_isD3D8Compatible = false;
|
||||||
bool m_amdATOC = false;
|
bool m_amdATOC = false;
|
||||||
bool m_nvATOC = false;
|
bool m_nvATOC = false;
|
||||||
bool m_ffZTest = false;
|
bool m_ffZTest = false;
|
||||||
@ -1325,6 +1395,7 @@ namespace dxvk {
|
|||||||
VkImageLayout m_hazardLayout = VK_IMAGE_LAYOUT_GENERAL;
|
VkImageLayout m_hazardLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||||
|
|
||||||
bool m_usingGraphicsPipelines = false;
|
bool m_usingGraphicsPipelines = false;
|
||||||
|
bool m_deviceHasBeenReset = false;
|
||||||
|
|
||||||
DxvkDepthBiasRepresentation m_depthBiasRepresentation = { VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT, false };
|
DxvkDepthBiasRepresentation m_depthBiasRepresentation = { VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT, false };
|
||||||
float m_depthBiasScale = 0.0f;
|
float m_depthBiasScale = 0.0f;
|
||||||
@ -1354,6 +1425,8 @@ namespace dxvk {
|
|||||||
|
|
||||||
Rc<sync::Fence> m_submissionFence;
|
Rc<sync::Fence> m_submissionFence;
|
||||||
uint64_t m_submissionId = 0ull;
|
uint64_t m_submissionId = 0ull;
|
||||||
|
DxvkSubmitStatus m_submitStatus;
|
||||||
|
|
||||||
uint64_t m_flushSeqNum = 0ull;
|
uint64_t m_flushSeqNum = 0ull;
|
||||||
GpuFlushTracker m_flushTracker;
|
GpuFlushTracker m_flushTracker;
|
||||||
|
|
||||||
@ -1364,6 +1437,8 @@ namespace dxvk {
|
|||||||
HWND m_fullscreenWindow = NULL;
|
HWND m_fullscreenWindow = NULL;
|
||||||
std::atomic<uint32_t> m_losableResourceCounter = { 0 };
|
std::atomic<uint32_t> m_losableResourceCounter = { 0 };
|
||||||
|
|
||||||
|
D3D9SwapChainEx* m_mostRecentlyUsedSwapchain = nullptr;
|
||||||
|
|
||||||
#ifdef D3D9_ALLOW_UNMAPPING
|
#ifdef D3D9_ALLOW_UNMAPPING
|
||||||
lru_list<D3D9CommonTexture*> m_mappedTextures;
|
lru_list<D3D9CommonTexture*> m_mappedTextures;
|
||||||
#endif
|
#endif
|
||||||
|
@ -15,6 +15,7 @@ namespace dxvk {
|
|||||||
D3D9FixedFunctionOptions::D3D9FixedFunctionOptions(const D3D9Options* options) {
|
D3D9FixedFunctionOptions::D3D9FixedFunctionOptions(const D3D9Options* options) {
|
||||||
invariantPosition = options->invariantPosition;
|
invariantPosition = options->invariantPosition;
|
||||||
forceSampleRateShading = options->forceSampleRateShading;
|
forceSampleRateShading = options->forceSampleRateShading;
|
||||||
|
drefScaling = options->drefScaling;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t DoFixedFunctionFog(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, const D3D9FogContext& fogCtx) {
|
uint32_t DoFixedFunctionFog(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, const D3D9FogContext& fogCtx) {
|
||||||
@ -336,7 +337,7 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count) {
|
uint32_t SetupRenderStateBlock(SpirvModule& spvModule) {
|
||||||
uint32_t floatType = spvModule.defFloatType(32);
|
uint32_t floatType = spvModule.defFloatType(32);
|
||||||
uint32_t uintType = spvModule.defIntType(32, 0);
|
uint32_t uintType = spvModule.defIntType(32, 0);
|
||||||
uint32_t vec3Type = spvModule.defVectorType(floatType, 3);
|
uint32_t vec3Type = spvModule.defVectorType(floatType, 3);
|
||||||
@ -357,7 +358,7 @@ namespace dxvk {
|
|||||||
floatType,
|
floatType,
|
||||||
}};
|
}};
|
||||||
|
|
||||||
uint32_t rsStruct = spvModule.defStructTypeUnique(count, rsMembers.data());
|
uint32_t rsStruct = spvModule.defStructTypeUnique(rsMembers.size(), rsMembers.data());
|
||||||
uint32_t rsBlock = spvModule.newVar(
|
uint32_t rsBlock = spvModule.newVar(
|
||||||
spvModule.defPointerType(rsStruct, spv::StorageClassPushConstant),
|
spvModule.defPointerType(rsStruct, spv::StorageClassPushConstant),
|
||||||
spv::StorageClassPushConstant);
|
spv::StorageClassPushConstant);
|
||||||
@ -369,9 +370,6 @@ namespace dxvk {
|
|||||||
|
|
||||||
uint32_t memberIdx = 0;
|
uint32_t memberIdx = 0;
|
||||||
auto SetMemberName = [&](const char* name, uint32_t offset) {
|
auto SetMemberName = [&](const char* name, uint32_t offset) {
|
||||||
if (memberIdx >= count)
|
|
||||||
return;
|
|
||||||
|
|
||||||
spvModule.setDebugMemberName (rsStruct, memberIdx, name);
|
spvModule.setDebugMemberName (rsStruct, memberIdx, name);
|
||||||
spvModule.memberDecorateOffset (rsStruct, memberIdx, offset);
|
spvModule.memberDecorateOffset (rsStruct, memberIdx, offset);
|
||||||
memberIdx++;
|
memberIdx++;
|
||||||
@ -781,8 +779,6 @@ namespace dxvk {
|
|||||||
uint32_t m_inputMask = 0u;
|
uint32_t m_inputMask = 0u;
|
||||||
uint32_t m_outputMask = 0u;
|
uint32_t m_outputMask = 0u;
|
||||||
uint32_t m_flatShadingMask = 0u;
|
uint32_t m_flatShadingMask = 0u;
|
||||||
uint32_t m_pushConstOffset = 0u;
|
|
||||||
uint32_t m_pushConstSize = 0u;
|
|
||||||
|
|
||||||
DxsoProgramType m_programType;
|
DxsoProgramType m_programType;
|
||||||
D3D9FFShaderKeyVS m_vsKey;
|
D3D9FFShaderKeyVS m_vsKey;
|
||||||
@ -892,8 +888,8 @@ namespace dxvk {
|
|||||||
info.inputMask = m_inputMask;
|
info.inputMask = m_inputMask;
|
||||||
info.outputMask = m_outputMask;
|
info.outputMask = m_outputMask;
|
||||||
info.flatShadingInputs = m_flatShadingMask;
|
info.flatShadingInputs = m_flatShadingMask;
|
||||||
info.pushConstOffset = m_pushConstOffset;
|
info.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
info.pushConstSize = m_pushConstSize;
|
info.pushConstSize = sizeof(D3D9RenderStateInfo);
|
||||||
|
|
||||||
return new DxvkShader(info, m_module.compile());
|
return new DxvkShader(info, m_module.compile());
|
||||||
}
|
}
|
||||||
@ -1111,31 +1107,63 @@ namespace dxvk {
|
|||||||
const uint32_t wIndex = 3;
|
const uint32_t wIndex = 3;
|
||||||
|
|
||||||
uint32_t flags = (m_vsKey.Data.Contents.TransformFlags >> (i * 3)) & 0b111;
|
uint32_t flags = (m_vsKey.Data.Contents.TransformFlags >> (i * 3)) & 0b111;
|
||||||
uint32_t count;
|
|
||||||
|
// 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) {
|
switch (inputFlags) {
|
||||||
default:
|
default:
|
||||||
case (DXVK_TSS_TCI_PASSTHRU >> TCIOffset):
|
case (DXVK_TSS_TCI_PASSTHRU >> TCIOffset):
|
||||||
transformed = m_vs.in.TEXCOORD[inputIndex & 0xFF];
|
transformed = m_vs.in.TEXCOORD[inputIndex & 0xFF];
|
||||||
// flags is actually the number of elements that get passed
|
|
||||||
// to the rasterizer.
|
if (texcoordCount < 4) {
|
||||||
count = flags;
|
// Vulkan sets the w component to 1.0 if that's not provided by the vertex buffer, D3D9 expects 0 here
|
||||||
if (texcoordCount) {
|
transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(0), transformed, 1, &wIndex);
|
||||||
// Clamp by the number of elements in the texcoord input.
|
}
|
||||||
if (!count || count > texcoordCount)
|
|
||||||
|
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;
|
count = texcoordCount;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
flags = D3DTTFF_DISABLE;
|
projIndex = count != 0 ? count - 1 : 4;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case (DXVK_TSS_TCI_CAMERASPACENORMAL >> TCIOffset):
|
case (DXVK_TSS_TCI_CAMERASPACENORMAL >> TCIOffset):
|
||||||
transformed = outNrm;
|
transformed = outNrm;
|
||||||
count = 4;
|
if (!applyTransform) {
|
||||||
|
count = 3;
|
||||||
|
projIndex = 4;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case (DXVK_TSS_TCI_CAMERASPACEPOSITION >> TCIOffset):
|
case (DXVK_TSS_TCI_CAMERASPACEPOSITION >> TCIOffset):
|
||||||
transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(1.0f), vtx, 1, &wIndex);
|
transformed = vtx;
|
||||||
count = 4;
|
if (!applyTransform) {
|
||||||
|
Logger::warn(str::format("!applyTransform flags: ", flags, " projidx: ", projIndex));
|
||||||
|
count = 3;
|
||||||
|
projIndex = 4;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case (DXVK_TSS_TCI_CAMERASPACEREFLECTIONVECTOR >> TCIOffset): {
|
case (DXVK_TSS_TCI_CAMERASPACEREFLECTIONVECTOR >> TCIOffset): {
|
||||||
@ -1150,7 +1178,10 @@ namespace dxvk {
|
|||||||
transformIndices[3] = m_module.constf32(1.0f);
|
transformIndices[3] = m_module.constf32(1.0f);
|
||||||
|
|
||||||
transformed = m_module.opCompositeConstruct(m_vec4Type, transformIndices.size(), transformIndices.data());
|
transformed = m_module.opCompositeConstruct(m_vec4Type, transformIndices.size(), transformIndices.data());
|
||||||
count = 4;
|
if (!applyTransform) {
|
||||||
|
count = 3;
|
||||||
|
projIndex = 4;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1174,35 +1205,27 @@ namespace dxvk {
|
|||||||
transformIndices[3] = m_module.constf32(1.0f);
|
transformIndices[3] = m_module.constf32(1.0f);
|
||||||
|
|
||||||
transformed = m_module.opCompositeConstruct(m_vec4Type, transformIndices.size(), transformIndices.data());
|
transformed = m_module.opCompositeConstruct(m_vec4Type, transformIndices.size(), transformIndices.data());
|
||||||
count = 4;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t type = flags;
|
if (applyTransform && !m_vsKey.Data.Contents.HasPositionT) {
|
||||||
if (type != D3DTTFF_DISABLE) {
|
|
||||||
if (!m_vsKey.Data.Contents.HasPositionT) {
|
|
||||||
for (uint32_t j = count; j < 4; j++) {
|
|
||||||
// If we're outside the component count of the vertex decl for this texcoord then we pad with zeroes.
|
|
||||||
// Otherwise, pad with ones.
|
|
||||||
|
|
||||||
// Very weird quirk in order to get texcoord transforms to work like they do in native.
|
|
||||||
// In future, maybe we could sort this out properly by chopping matrices of different sizes, but thats
|
|
||||||
// a project for another day.
|
|
||||||
uint32_t texcoordCount = (m_vsKey.Data.Contents.TexcoordDeclMask >> (3 * inputIndex)) & 0x7;
|
|
||||||
uint32_t value = j > texcoordCount ? m_module.constf32(0) : m_module.constf32(1);
|
|
||||||
transformed = m_module.opCompositeInsert(m_vec4Type, value, transformed, 1, &j);
|
|
||||||
}
|
|
||||||
|
|
||||||
transformed = m_module.opVectorTimesMatrix(m_vec4Type, transformed, m_vs.constants.texcoord[i]);
|
transformed = m_module.opVectorTimesMatrix(m_vec4Type, transformed, m_vs.constants.texcoord[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pad the unused section of it with the value for projection.
|
if (m_vsKey.Data.Contents.Projected && projIndex < 4) {
|
||||||
uint32_t lastIdx = count - 1;
|
// 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, &lastIdx);
|
uint32_t projValue = m_module.opCompositeExtract(m_floatType, transformed, 1, &projIndex);
|
||||||
|
|
||||||
for (uint32_t j = count; j < 4; j++)
|
// The w component is only used for projection or unused, so always insert the component that's supposed to be divided by there.
|
||||||
transformed = m_module.opCompositeInsert(m_vec4Type, projValue, transformed, 1, &j);
|
// 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);
|
m_module.opStore(m_vs.out.TEXCOORD[i], transformed);
|
||||||
@ -1315,6 +1338,8 @@ namespace dxvk {
|
|||||||
uint32_t midDot = m_module.opDot(m_floatType, normal, 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));
|
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));
|
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);
|
uint32_t specularness = m_module.opPow(m_floatType, midDot, m_vs.constants.materialPower);
|
||||||
specularness = m_module.opFMul(m_floatType, specularness, atten);
|
specularness = m_module.opFMul(m_floatType, specularness, atten);
|
||||||
specularness = m_module.opSelect(m_floatType, doSpec, specularness, m_module.constf32(0.0f));
|
specularness = m_module.opSelect(m_floatType, doSpec, specularness, m_module.constf32(0.0f));
|
||||||
@ -1384,20 +1409,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
|
|
||||||
void D3D9FFShaderCompiler::setupRenderStateInfo() {
|
void D3D9FFShaderCompiler::setupRenderStateInfo() {
|
||||||
uint32_t count;
|
m_rsBlock = SetupRenderStateBlock(m_module);
|
||||||
|
|
||||||
if (m_programType == DxsoProgramType::PixelShader) {
|
|
||||||
m_pushConstOffset = 0;
|
|
||||||
m_pushConstSize = offsetof(D3D9RenderStateInfo, pointSize);
|
|
||||||
count = 5;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_pushConstOffset = offsetof(D3D9RenderStateInfo, pointSize);
|
|
||||||
m_pushConstSize = sizeof(float) * 6;
|
|
||||||
count = 11;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_rsBlock = SetupRenderStateBlock(m_module, count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1789,6 +1801,11 @@ namespace dxvk {
|
|||||||
return coords;
|
return coords;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto ScalarReplicate = [&](uint32_t reg) {
|
||||||
|
std::array<uint32_t, 4> replicant = { reg, reg, reg, reg };
|
||||||
|
return m_module.opCompositeConstruct(m_vec4Type, replicant.size(), replicant.data());
|
||||||
|
};
|
||||||
|
|
||||||
auto GetTexture = [&]() {
|
auto GetTexture = [&]() {
|
||||||
if (!processedTexture) {
|
if (!processedTexture) {
|
||||||
SpirvImageOperands imageOperands;
|
SpirvImageOperands imageOperands;
|
||||||
@ -1808,21 +1825,17 @@ namespace dxvk {
|
|||||||
texcoord = m_module.opVectorShuffle(texcoord_t,
|
texcoord = m_module.opVectorShuffle(texcoord_t,
|
||||||
texcoord, texcoord, texcoordCnt, indices.data());
|
texcoord, texcoord, texcoordCnt, indices.data());
|
||||||
|
|
||||||
uint32_t projIdx = m_fsKey.Stages[i].Contents.ProjectedCount;
|
bool shouldProject = m_fsKey.Stages[i].Contents.Projected;
|
||||||
if (projIdx == 0 || projIdx > texcoordCnt)
|
|
||||||
projIdx = texcoordCnt;
|
|
||||||
--projIdx;
|
|
||||||
|
|
||||||
uint32_t projValue = 0;
|
uint32_t projValue = 0;
|
||||||
|
|
||||||
if (m_fsKey.Stages[i].Contents.Projected) {
|
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);
|
projValue = m_module.opCompositeExtract(m_floatType, m_ps.in.TEXCOORD[i], 1, &projIdx);
|
||||||
uint32_t insertIdx = texcoordCnt - 1;
|
uint32_t insertIdx = texcoordCnt - 1;
|
||||||
texcoord = m_module.opCompositeInsert(texcoord_t, projValue, texcoord, 1, &insertIdx);
|
texcoord = m_module.opCompositeInsert(texcoord_t, projValue, texcoord, 1, &insertIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldProject = m_fsKey.Stages[i].Contents.Projected;
|
|
||||||
|
|
||||||
if (i != 0 && (
|
if (i != 0 && (
|
||||||
m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAP ||
|
m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAP ||
|
||||||
m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAPLUMINANCE)) {
|
m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAPLUMINANCE)) {
|
||||||
@ -1836,10 +1849,23 @@ namespace dxvk {
|
|||||||
shouldProject = false;
|
shouldProject = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldProject)
|
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);
|
texture = m_module.opImageSampleProjImplicitLod(m_vec4Type, imageVarId, texcoord, imageOperands);
|
||||||
else
|
} else {
|
||||||
texture = m_module.opImageSampleImplicitLod(m_vec4Type, imageVarId, texcoord, imageOperands);
|
texture = m_module.opImageSampleImplicitLod(m_vec4Type, imageVarId, texcoord, imageOperands);
|
||||||
|
}
|
||||||
|
|
||||||
if (i != 0 && m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAPLUMINANCE) {
|
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 index = m_module.constu32(D3D9SharedPSStages_Count * (i - 1) + D3D9SharedPSStages_BumpEnvLScale);
|
||||||
@ -1867,11 +1893,6 @@ namespace dxvk {
|
|||||||
return texture;
|
return texture;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto ScalarReplicate = [&](uint32_t reg) {
|
|
||||||
std::array<uint32_t, 4> replicant = { reg, reg, reg, reg };
|
|
||||||
return m_module.opCompositeConstruct(m_vec4Type, replicant.size(), replicant.data());
|
|
||||||
};
|
|
||||||
|
|
||||||
auto AlphaReplicate = [&](uint32_t reg) {
|
auto AlphaReplicate = [&](uint32_t reg) {
|
||||||
uint32_t alphaComponentId = 3;
|
uint32_t alphaComponentId = 3;
|
||||||
uint32_t alpha = m_module.opCompositeExtract(m_floatType, reg, 1, &alphaComponentId);
|
uint32_t alpha = m_module.opCompositeExtract(m_floatType, reg, 1, &alphaComponentId);
|
||||||
@ -2268,6 +2289,11 @@ namespace dxvk {
|
|||||||
dimensionality = spv::Dim2D;
|
dimensionality = spv::Dim2D;
|
||||||
sampler.texcoordCnt = 2;
|
sampler.texcoordCnt = 2;
|
||||||
viewType = VK_IMAGE_VIEW_TYPE_2D;
|
viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
|
||||||
|
// Z coordinate for Dref sampling
|
||||||
|
if (m_fsKey.Stages[i].Contents.SampleDref)
|
||||||
|
sampler.texcoordCnt++;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case D3DRTYPE_CUBETEXTURE:
|
case D3DRTYPE_CUBETEXTURE:
|
||||||
dimensionality = spv::DimCube;
|
dimensionality = spv::DimCube;
|
||||||
|
@ -50,8 +50,13 @@ namespace dxvk {
|
|||||||
|
|
||||||
bool invariantPosition;
|
bool invariantPosition;
|
||||||
bool forceSampleRateShading;
|
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 oFog if VS
|
||||||
// Returns new oColor if PS
|
// Returns new oColor if PS
|
||||||
uint32_t DoFixedFunctionFog(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, const D3D9FogContext& fogCtx);
|
uint32_t DoFixedFunctionFog(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, const D3D9FogContext& fogCtx);
|
||||||
@ -59,7 +64,7 @@ namespace dxvk {
|
|||||||
void DoFixedFunctionAlphaTest(SpirvModule& spvModule, const D3D9AlphaTestContext& ctx);
|
void DoFixedFunctionAlphaTest(SpirvModule& spvModule, const D3D9AlphaTestContext& ctx);
|
||||||
|
|
||||||
// Returns a render state block
|
// Returns a render state block
|
||||||
uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count);
|
uint32_t SetupRenderStateBlock(SpirvModule& spvModule);
|
||||||
|
|
||||||
struct D3D9PointSizeInfoVS {
|
struct D3D9PointSizeInfoVS {
|
||||||
uint32_t defaultValue;
|
uint32_t defaultValue;
|
||||||
@ -128,9 +133,11 @@ namespace dxvk {
|
|||||||
uint32_t VertexBlendCount : 3;
|
uint32_t VertexBlendCount : 3;
|
||||||
|
|
||||||
uint32_t VertexClipping : 1;
|
uint32_t VertexClipping : 1;
|
||||||
|
|
||||||
|
uint32_t Projected : 8;
|
||||||
} Contents;
|
} Contents;
|
||||||
|
|
||||||
uint32_t Primitive[4];
|
uint32_t Primitive[5];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -163,6 +170,7 @@ namespace dxvk {
|
|||||||
uint32_t Projected : 1;
|
uint32_t Projected : 1;
|
||||||
|
|
||||||
uint32_t ProjectedCount : 3;
|
uint32_t ProjectedCount : 3;
|
||||||
|
uint32_t SampleDref : 1;
|
||||||
|
|
||||||
uint32_t TextureBound : 1;
|
uint32_t TextureBound : 1;
|
||||||
|
|
||||||
|
@ -438,9 +438,15 @@ namespace dxvk {
|
|||||||
D3D9VkFormatTable::D3D9VkFormatTable(
|
D3D9VkFormatTable::D3D9VkFormatTable(
|
||||||
const Rc<DxvkAdapter>& adapter,
|
const Rc<DxvkAdapter>& adapter,
|
||||||
const D3D9Options& options) {
|
const D3D9Options& options) {
|
||||||
m_dfSupport = options.supportDFFormats;
|
|
||||||
|
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;
|
m_x4r4g4b4Support = options.supportX4R4G4B4;
|
||||||
m_d32supportFinal = options.supportD32;
|
// 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,
|
// AMD do not support 24-bit depth buffers on Vulkan,
|
||||||
// so we have to fall back to a 32-bit depth format.
|
// so we have to fall back to a 32-bit depth format.
|
||||||
@ -473,15 +479,15 @@ namespace dxvk {
|
|||||||
if (Format == D3D9Format::X4R4G4B4 && !m_x4r4g4b4Support)
|
if (Format == D3D9Format::X4R4G4B4 && !m_x4r4g4b4Support)
|
||||||
return D3D9_VK_FORMAT_MAPPING();
|
return D3D9_VK_FORMAT_MAPPING();
|
||||||
|
|
||||||
|
if (Format == D3D9Format::D16_LOCKABLE && !m_d16lockableSupport)
|
||||||
|
return D3D9_VK_FORMAT_MAPPING();
|
||||||
|
|
||||||
if (Format == D3D9Format::DF16 && !m_dfSupport)
|
if (Format == D3D9Format::DF16 && !m_dfSupport)
|
||||||
return D3D9_VK_FORMAT_MAPPING();
|
return D3D9_VK_FORMAT_MAPPING();
|
||||||
|
|
||||||
if (Format == D3D9Format::DF24 && !m_dfSupport)
|
if (Format == D3D9Format::DF24 && !m_dfSupport)
|
||||||
return D3D9_VK_FORMAT_MAPPING();
|
return D3D9_VK_FORMAT_MAPPING();
|
||||||
|
|
||||||
if (Format == D3D9Format::D32 && !m_d32supportFinal)
|
|
||||||
return D3D9_VK_FORMAT_MAPPING();
|
|
||||||
|
|
||||||
if (!m_d24s8Support && mapping.FormatColor == VK_FORMAT_D24_UNORM_S8_UINT)
|
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;
|
mapping.FormatColor = mapping.Aspect & VK_IMAGE_ASPECT_STENCIL_BIT ? VK_FORMAT_D32_SFLOAT_S8_UINT : VK_FORMAT_D32_SFLOAT;
|
||||||
|
|
||||||
|
@ -217,7 +217,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
bool m_dfSupport;
|
bool m_dfSupport;
|
||||||
bool m_x4r4g4b4Support;
|
bool m_x4r4g4b4Support;
|
||||||
bool m_d32supportFinal;
|
bool m_d16lockableSupport;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool IsFourCCFormat(D3D9Format format) {
|
inline bool IsFourCCFormat(D3D9Format format) {
|
||||||
@ -230,6 +230,7 @@ namespace dxvk {
|
|||||||
&& format != D3D9Format::MULTI2_ARGB8
|
&& format != D3D9Format::MULTI2_ARGB8
|
||||||
&& format != D3D9Format::UYVY
|
&& format != D3D9Format::UYVY
|
||||||
&& format != D3D9Format::R8G8_B8G8
|
&& format != D3D9Format::R8G8_B8G8
|
||||||
|
&& format != D3D9Format::YUY2
|
||||||
&& format != D3D9Format::G8R8_G8B8
|
&& format != D3D9Format::G8R8_G8B8
|
||||||
&& format != D3D9Format::DXT1
|
&& format != D3D9Format::DXT1
|
||||||
&& format != D3D9Format::DXT2
|
&& format != D3D9Format::DXT2
|
||||||
|
@ -134,7 +134,7 @@ namespace dxvk {
|
|||||||
info.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
info.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||||
info.bindingCount = bindings.size();
|
info.bindingCount = bindings.size();
|
||||||
info.bindings = bindings.data();
|
info.bindings = bindings.data();
|
||||||
info.pushConstOffset = 0;
|
info.pushConstStages = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||||
info.pushConstSize = sizeof(VkExtent2D);
|
info.pushConstSize = sizeof(VkExtent2D);
|
||||||
|
|
||||||
return new DxvkShader(info, std::move(code));
|
return new DxvkShader(info, std::move(code));
|
||||||
|
@ -101,9 +101,7 @@ IDirect3DDevice9On12 : public IUnknown {
|
|||||||
virtual HRESULT STDMETHODCALLTYPE ReturnUnderlyingResource(IDirect3DResource9* resource, UINT num_sync, UINT64* signal_values, ID3D12Fence** fences) = 0;
|
virtual HRESULT STDMETHODCALLTYPE ReturnUnderlyingResource(IDirect3DResource9* resource, UINT num_sync, UINT64* signal_values, ID3D12Fence** fences) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
struct __declspec(uuid("e7fda234-b589-4049-940d-8878977531c8")) IDirect3DDevice9On12;
|
|
||||||
#else
|
|
||||||
__CRT_UUID_DECL(IDirect3DDevice9On12, 0xe7fda234,0xb589,0x4049,0x94,0x0d,0x88,0x78,0x97,0x75,0x31,0xc8);
|
__CRT_UUID_DECL(IDirect3DDevice9On12, 0xe7fda234,0xb589,0x4049,0x94,0x0d,0x88,0x78,0x97,0x75,0x31,0xc8);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ namespace dxvk {
|
|||||||
Singleton<DxvkInstance> g_dxvkInstance;
|
Singleton<DxvkInstance> g_dxvkInstance;
|
||||||
|
|
||||||
D3D9InterfaceEx::D3D9InterfaceEx(bool bExtended)
|
D3D9InterfaceEx::D3D9InterfaceEx(bool bExtended)
|
||||||
: m_instance ( g_dxvkInstance.acquire() )
|
: m_instance ( g_dxvkInstance.acquire(DxvkInstanceFlag::ClientApiIsD3D9) )
|
||||||
, m_d3d8Bridge ( this )
|
, m_d3d8Bridge ( this )
|
||||||
, m_extended ( bExtended )
|
, m_extended ( bExtended )
|
||||||
, m_d3d9Options ( nullptr, m_instance->config() )
|
, m_d3d9Options ( nullptr, m_instance->config() )
|
||||||
@ -354,6 +354,12 @@ namespace dxvk {
|
|||||||
|| pPresentationParameters == nullptr)
|
|| pPresentationParameters == nullptr)
|
||||||
return D3DERR_INVALIDCALL;
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
|
// creating a device with D3DCREATE_PUREDEVICE only works in conjunction
|
||||||
|
// with D3DCREATE_HARDWARE_VERTEXPROCESSING on native drivers
|
||||||
|
if (BehaviorFlags & D3DCREATE_PUREDEVICE &&
|
||||||
|
!(BehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING))
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
auto* adapter = GetAdapter(Adapter);
|
auto* adapter = GetAdapter(Adapter);
|
||||||
|
|
||||||
if (adapter == nullptr)
|
if (adapter == nullptr)
|
||||||
|
@ -229,12 +229,7 @@ ID3D9VkExtSwapchain : public IUnknown {
|
|||||||
virtual void STDMETHODCALLTYPE UnlockAdditionalFormats() = 0;
|
virtual void STDMETHODCALLTYPE UnlockAdditionalFormats() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
struct __declspec(uuid("3461a81b-ce41-485b-b6b5-fcf08ba6a6bd")) ID3D9VkInteropInterface;
|
|
||||||
struct __declspec(uuid("d56344f5-8d35-46fd-806d-94c351b472c1")) ID3D9VkInteropTexture;
|
|
||||||
struct __declspec(uuid("2eaa4b89-0107-4bdb-87f7-0f541c493ce0")) ID3D9VkInteropDevice;
|
|
||||||
struct __declspec(uuid("13776e93-4aa9-430a-a4ec-fe9e281181d5")) ID3D9VkExtSwapchain;
|
|
||||||
#else
|
|
||||||
__CRT_UUID_DECL(ID3D9VkInteropInterface, 0x3461a81b,0xce41,0x485b,0xb6,0xb5,0xfc,0xf0,0x8b,0xa6,0xa6,0xbd);
|
__CRT_UUID_DECL(ID3D9VkInteropInterface, 0x3461a81b,0xce41,0x485b,0xb6,0xb5,0xfc,0xf0,0x8b,0xa6,0xa6,0xbd);
|
||||||
__CRT_UUID_DECL(ID3D9VkInteropTexture, 0xd56344f5,0x8d35,0x46fd,0x80,0x6d,0x94,0xc3,0x51,0xb4,0x72,0xc1);
|
__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(ID3D9VkInteropDevice, 0x2eaa4b89,0x0107,0x4bdb,0x87,0xf7,0x0f,0x54,0x1c,0x49,0x3c,0xe0);
|
||||||
|
@ -81,7 +81,7 @@ namespace dxvk {
|
|||||||
D3D9Memory (D3D9Memory&& other);
|
D3D9Memory (D3D9Memory&& other);
|
||||||
D3D9Memory& operator = (D3D9Memory&& other);
|
D3D9Memory& operator = (D3D9Memory&& other);
|
||||||
|
|
||||||
operator bool() const { return m_chunk != nullptr; }
|
explicit operator bool() const { return m_chunk != nullptr; }
|
||||||
|
|
||||||
void Map();
|
void Map();
|
||||||
void Unmap();
|
void Unmap();
|
||||||
@ -139,7 +139,7 @@ namespace dxvk {
|
|||||||
D3D9Memory (D3D9Memory&& other);
|
D3D9Memory (D3D9Memory&& other);
|
||||||
D3D9Memory& operator = (D3D9Memory&& other);
|
D3D9Memory& operator = (D3D9Memory&& other);
|
||||||
|
|
||||||
operator bool() const { return m_ptr != nullptr; }
|
explicit operator bool() const { return m_ptr != nullptr; }
|
||||||
|
|
||||||
void Map() {}
|
void Map() {}
|
||||||
void Unmap() {}
|
void Unmap() {}
|
||||||
|
@ -30,8 +30,11 @@ namespace dxvk {
|
|||||||
return E_NOINTERFACE;
|
return E_NOINTERFACE;
|
||||||
}
|
}
|
||||||
HRESULT STDMETHODCALLTYPE D3D9On12::ReturnUnderlyingResource(IDirect3DResource9* resource, UINT num_sync, UINT64* signal_values, ID3D12Fence** fences) {
|
HRESULT STDMETHODCALLTYPE D3D9On12::ReturnUnderlyingResource(IDirect3DResource9* resource, UINT num_sync, UINT64* signal_values, ID3D12Fence** fences) {
|
||||||
|
if (num_sync)
|
||||||
Logger::err("D3D9On12::GetD3D12Device: ReturnUnderlyingResource: Stub");
|
Logger::err("D3D9On12::GetD3D12Device: ReturnUnderlyingResource: Stub");
|
||||||
return E_NOINTERFACE;
|
|
||||||
|
m_device->FlushAndSync9On12();
|
||||||
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,8 @@ namespace dxvk {
|
|||||||
this->customDeviceId = parsePciId(config.getOption<std::string>("d3d9.customDeviceId"));
|
this->customDeviceId = parsePciId(config.getOption<std::string>("d3d9.customDeviceId"));
|
||||||
this->customDeviceDesc = config.getOption<std::string>("d3d9.customDeviceDesc");
|
this->customDeviceDesc = config.getOption<std::string>("d3d9.customDeviceDesc");
|
||||||
|
|
||||||
const int32_t vendorId = this->customDeviceId != -1
|
const uint32_t vendorId = this->customVendorId != -1
|
||||||
? this->customDeviceId
|
? this->customVendorId
|
||||||
: (adapter != nullptr ? adapter->deviceProperties().vendorID : 0);
|
: (adapter != nullptr ? adapter->deviceProperties().vendorID : 0);
|
||||||
|
|
||||||
this->maxFrameLatency = config.getOption<int32_t> ("d3d9.maxFrameLatency", 0);
|
this->maxFrameLatency = config.getOption<int32_t> ("d3d9.maxFrameLatency", 0);
|
||||||
@ -55,12 +55,12 @@ namespace dxvk {
|
|||||||
this->maxAvailableMemory = config.getOption<int32_t> ("d3d9.maxAvailableMemory", 4096);
|
this->maxAvailableMemory = config.getOption<int32_t> ("d3d9.maxAvailableMemory", 4096);
|
||||||
this->supportDFFormats = config.getOption<bool> ("d3d9.supportDFFormats", true);
|
this->supportDFFormats = config.getOption<bool> ("d3d9.supportDFFormats", true);
|
||||||
this->supportX4R4G4B4 = config.getOption<bool> ("d3d9.supportX4R4G4B4", true);
|
this->supportX4R4G4B4 = config.getOption<bool> ("d3d9.supportX4R4G4B4", true);
|
||||||
this->supportD32 = config.getOption<bool> ("d3d9.supportD32", true);
|
this->supportD16Lockable = config.getOption<bool> ("d3d9.supportD16Lockable", false);
|
||||||
this->useD32forD24 = config.getOption<bool> ("d3d9.useD32forD24", false);
|
this->useD32forD24 = config.getOption<bool> ("d3d9.useD32forD24", false);
|
||||||
this->disableA8RT = config.getOption<bool> ("d3d9.disableA8RT", false);
|
this->disableA8RT = config.getOption<bool> ("d3d9.disableA8RT", false);
|
||||||
this->invariantPosition = config.getOption<bool> ("d3d9.invariantPosition", true);
|
this->invariantPosition = config.getOption<bool> ("d3d9.invariantPosition", true);
|
||||||
this->memoryTrackTest = config.getOption<bool> ("d3d9.memoryTrackTest", false);
|
this->memoryTrackTest = config.getOption<bool> ("d3d9.memoryTrackTest", false);
|
||||||
this->supportVCache = config.getOption<bool> ("d3d9.supportVCache", vendorId == 0x10de);
|
this->supportVCache = config.getOption<bool> ("d3d9.supportVCache", vendorId == uint32_t(DxvkGpuVendor::Nvidia));
|
||||||
this->enableDialogMode = config.getOption<bool> ("d3d9.enableDialogMode", false);
|
this->enableDialogMode = config.getOption<bool> ("d3d9.enableDialogMode", false);
|
||||||
this->forceSamplerTypeSpecConstants = config.getOption<bool> ("d3d9.forceSamplerTypeSpecConstants", false);
|
this->forceSamplerTypeSpecConstants = config.getOption<bool> ("d3d9.forceSamplerTypeSpecConstants", false);
|
||||||
this->forceSwapchainMSAA = config.getOption<int32_t> ("d3d9.forceSwapchainMSAA", -1);
|
this->forceSwapchainMSAA = config.getOption<int32_t> ("d3d9.forceSwapchainMSAA", -1);
|
||||||
@ -76,6 +76,11 @@ namespace dxvk {
|
|||||||
this->deviceLossOnFocusLoss = config.getOption<bool> ("d3d9.deviceLossOnFocusLoss", false);
|
this->deviceLossOnFocusLoss = config.getOption<bool> ("d3d9.deviceLossOnFocusLoss", false);
|
||||||
this->samplerLodBias = config.getOption<float> ("d3d9.samplerLodBias", 0.0f);
|
this->samplerLodBias = config.getOption<float> ("d3d9.samplerLodBias", 0.0f);
|
||||||
this->clampNegativeLodBias = config.getOption<bool> ("d3d9.clampNegativeLodBias", false);
|
this->clampNegativeLodBias = config.getOption<bool> ("d3d9.clampNegativeLodBias", false);
|
||||||
|
this->countLosableResources = config.getOption<bool> ("d3d9.countLosableResources", true);
|
||||||
|
this->reproducibleCommandStream = config.getOption<bool> ("d3d9.reproducibleCommandStream", false);
|
||||||
|
|
||||||
|
// D3D8 options
|
||||||
|
this->drefScaling = config.getOption<int32_t> ("d3d8.scaleDref", 0);
|
||||||
|
|
||||||
// Clamp LOD bias so that people don't abuse this in unintended ways
|
// Clamp LOD bias so that people don't abuse this in unintended ways
|
||||||
this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f);
|
this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f);
|
||||||
@ -89,8 +94,8 @@ namespace dxvk {
|
|||||||
d3d9FloatEmulation = D3D9FloatEmulation::Enabled;
|
d3d9FloatEmulation = D3D9FloatEmulation::Enabled;
|
||||||
} else {
|
} else {
|
||||||
bool hasMulz = adapter != nullptr
|
bool hasMulz = adapter != nullptr
|
||||||
&& (adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV, 0, 0)
|
&& (adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV)
|
||||||
|| adapter->matchesDriver(VK_DRIVER_ID_MESA_NVK, 0, 0));
|
|| adapter->matchesDriver(VK_DRIVER_ID_MESA_NVK));
|
||||||
d3d9FloatEmulation = hasMulz ? D3D9FloatEmulation::Strict : D3D9FloatEmulation::Enabled;
|
d3d9FloatEmulation = hasMulz ? D3D9FloatEmulation::Strict : D3D9FloatEmulation::Enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,8 +78,8 @@ namespace dxvk {
|
|||||||
/// Support X4R4G4B4
|
/// Support X4R4G4B4
|
||||||
bool supportX4R4G4B4;
|
bool supportX4R4G4B4;
|
||||||
|
|
||||||
/// Support D32
|
/// Support D16_LOCKABLE
|
||||||
bool supportD32;
|
bool supportD16Lockable;
|
||||||
|
|
||||||
/// Use D32f for D24
|
/// Use D32f for D24
|
||||||
bool useD32forD24;
|
bool useD32forD24;
|
||||||
@ -152,6 +152,17 @@ namespace dxvk {
|
|||||||
|
|
||||||
/// Enable emulation of device loss when a fullscreen app loses focus
|
/// Enable emulation of device loss when a fullscreen app loses focus
|
||||||
bool deviceLossOnFocusLoss;
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -41,11 +41,6 @@ namespace dxvk {
|
|||||||
case D3DQUERYTYPE_TIMESTAMPFREQ:
|
case D3DQUERYTYPE_TIMESTAMPFREQ:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case D3DQUERYTYPE_VERTEXSTATS:
|
|
||||||
m_query[0] = dxvkDevice->createGpuQuery(
|
|
||||||
VK_QUERY_TYPE_PIPELINE_STATISTICS, 0, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw DxvkError(str::format("D3D9Query: Unsupported query type ", m_queryType));
|
throw DxvkError(str::format("D3D9Query: Unsupported query type ", m_queryType));
|
||||||
}
|
}
|
||||||
@ -222,12 +217,10 @@ namespace dxvk {
|
|||||||
|
|
||||||
switch (m_queryType) {
|
switch (m_queryType) {
|
||||||
case D3DQUERYTYPE_VCACHE:
|
case D3DQUERYTYPE_VCACHE:
|
||||||
// Don't know what the hell any of this means.
|
m_dataCache.VCache.Pattern = MAKEFOURCC('C', 'A', 'C', 'H');
|
||||||
// Nor do I care. This just makes games work.
|
|
||||||
m_dataCache.VCache.Pattern = MAKEFOURCC('H', 'C', 'A', 'C');
|
|
||||||
m_dataCache.VCache.OptMethod = 1;
|
m_dataCache.VCache.OptMethod = 1;
|
||||||
m_dataCache.VCache.CacheSize = 24;
|
m_dataCache.VCache.CacheSize = 16;
|
||||||
m_dataCache.VCache.MagicNumber = 20;
|
m_dataCache.VCache.MagicNumber = 7;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case D3DQUERYTYPE_OCCLUSION:
|
case D3DQUERYTYPE_OCCLUSION:
|
||||||
@ -246,11 +239,6 @@ namespace dxvk {
|
|||||||
m_dataCache.TimestampFreq = GetTimestampQueryFrequency();
|
m_dataCache.TimestampFreq = GetTimestampQueryFrequency();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case D3DQUERYTYPE_VERTEXSTATS:
|
|
||||||
m_dataCache.VertexStats.NumRenderedTriangles = queryData[0].statistic.iaPrimitives;
|
|
||||||
m_dataCache.VertexStats.NumExtraClippingTriangles = queryData[0].statistic.clipPrimitives;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -276,7 +264,6 @@ namespace dxvk {
|
|||||||
void D3D9Query::Begin(DxvkContext* ctx) {
|
void D3D9Query::Begin(DxvkContext* ctx) {
|
||||||
switch (m_queryType) {
|
switch (m_queryType) {
|
||||||
case D3DQUERYTYPE_OCCLUSION:
|
case D3DQUERYTYPE_OCCLUSION:
|
||||||
case D3DQUERYTYPE_VERTEXSTATS:
|
|
||||||
ctx->beginQuery(m_query[0]);
|
ctx->beginQuery(m_query[0]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -296,7 +283,6 @@ namespace dxvk {
|
|||||||
ctx->writeTimestamp(m_query[0]);
|
ctx->writeTimestamp(m_query[0]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case D3DQUERYTYPE_VERTEXSTATS:
|
|
||||||
case D3DQUERYTYPE_OCCLUSION:
|
case D3DQUERYTYPE_OCCLUSION:
|
||||||
ctx->endQuery(m_query[0]);
|
ctx->endQuery(m_query[0]);
|
||||||
break;
|
break;
|
||||||
@ -314,7 +300,6 @@ namespace dxvk {
|
|||||||
|
|
||||||
bool D3D9Query::QueryBeginnable(D3DQUERYTYPE QueryType) {
|
bool D3D9Query::QueryBeginnable(D3DQUERYTYPE QueryType) {
|
||||||
return QueryType == D3DQUERYTYPE_OCCLUSION
|
return QueryType == D3DQUERYTYPE_OCCLUSION
|
||||||
|| QueryType == D3DQUERYTYPE_VERTEXSTATS
|
|
||||||
|| QueryType == D3DQUERYTYPE_TIMESTAMPDISJOINT;
|
|| QueryType == D3DQUERYTYPE_TIMESTAMPDISJOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,7 +323,6 @@ namespace dxvk {
|
|||||||
case D3DQUERYTYPE_TIMESTAMP:
|
case D3DQUERYTYPE_TIMESTAMP:
|
||||||
case D3DQUERYTYPE_TIMESTAMPDISJOINT:
|
case D3DQUERYTYPE_TIMESTAMPDISJOINT:
|
||||||
case D3DQUERYTYPE_TIMESTAMPFREQ:
|
case D3DQUERYTYPE_TIMESTAMPFREQ:
|
||||||
case D3DQUERYTYPE_VERTEXSTATS:
|
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -169,11 +169,11 @@ namespace dxvk {
|
|||||||
|
|
||||||
constexpr D3DLIGHT9 DefaultLight = {
|
constexpr D3DLIGHT9 DefaultLight = {
|
||||||
D3DLIGHT_DIRECTIONAL, // Type
|
D3DLIGHT_DIRECTIONAL, // Type
|
||||||
{1.0f, 1.0f, 1.0f, 1.0f}, // Diffuse
|
{1.0f, 1.0f, 1.0f, 0.0f}, // Diffuse
|
||||||
{0.0f, 0.0f, 0.0f, 0.0f}, // Specular
|
{0.0f, 0.0f, 0.0f, 0.0f}, // Specular
|
||||||
{0.0f, 0.0f, 0.0f, 0.0f}, // Ambient
|
{0.0f, 0.0f, 0.0f, 0.0f}, // Ambient
|
||||||
{0.0f, 0.0f, 0.0f}, // Position
|
{0.0f, 0.0f, 0.0f}, // Position
|
||||||
{0.0f, 0.0f, 0.0f}, // Direction
|
{0.0f, 0.0f, 1.0f}, // Direction
|
||||||
0.0f, // Range
|
0.0f, // Range
|
||||||
0.0f, // Falloff
|
0.0f, // Falloff
|
||||||
0.0f, 0.0f, 0.0f, // Attenuations [constant, linear, quadratic]
|
0.0f, 0.0f, 0.0f, // Attenuations [constant, linear, quadratic]
|
||||||
@ -195,7 +195,7 @@ namespace dxvk {
|
|||||||
const T* operator & () const { ensure(); return m_data.get(); }
|
const T* operator & () const { ensure(); return m_data.get(); }
|
||||||
T* operator & () { ensure(); return m_data.get(); }
|
T* operator & () { ensure(); return m_data.get(); }
|
||||||
|
|
||||||
operator bool() { return m_data != nullptr; }
|
explicit operator bool() const { return m_data != nullptr; }
|
||||||
operator T() { ensure(); return *m_data; }
|
operator T() { ensure(); return *m_data; }
|
||||||
|
|
||||||
void ensure() const { if (!m_data) m_data = std::make_unique<T>(); }
|
void ensure() const { if (!m_data) m_data = std::make_unique<T>(); }
|
||||||
@ -213,7 +213,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
T& operator=(const T& x) { m_data = x; return m_data; }
|
T& operator=(const T& x) { m_data = x; return m_data; }
|
||||||
|
|
||||||
operator bool() { return true; }
|
explicit operator bool() const { return true; }
|
||||||
operator T() { return m_data; }
|
operator T() { return m_data; }
|
||||||
|
|
||||||
const T* operator -> () const { return &m_data; }
|
const T* operator -> () const { return &m_data; }
|
||||||
|
@ -18,6 +18,7 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
D3D9StateBlock::~D3D9StateBlock() {
|
D3D9StateBlock::~D3D9StateBlock() {
|
||||||
|
if (!m_parent->IsD3D8Compatible())
|
||||||
m_parent->DecrementLosableCounter();
|
m_parent->DecrementLosableCounter();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +49,7 @@ namespace dxvk {
|
|||||||
if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl))
|
if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl))
|
||||||
SetVertexDeclaration(m_deviceState->vertexDecl.ptr());
|
SetVertexDeclaration(m_deviceState->vertexDecl.ptr());
|
||||||
|
|
||||||
ApplyOrCapture<D3D9StateFunction::Capture>();
|
ApplyOrCapture<D3D9StateFunction::Capture, true>();
|
||||||
|
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
}
|
}
|
||||||
@ -60,7 +61,7 @@ namespace dxvk {
|
|||||||
if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl) && m_state.vertexDecl != nullptr)
|
if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl) && m_state.vertexDecl != nullptr)
|
||||||
m_parent->SetVertexDeclaration(m_state.vertexDecl.ptr());
|
m_parent->SetVertexDeclaration(m_state.vertexDecl.ptr());
|
||||||
|
|
||||||
ApplyOrCapture<D3D9StateFunction::Apply>();
|
ApplyOrCapture<D3D9StateFunction::Apply, false>();
|
||||||
m_applying = false;
|
m_applying = false;
|
||||||
|
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
@ -121,6 +122,20 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT D3D9StateBlock::SetStreamSourceWithoutOffset(
|
||||||
|
UINT StreamNumber,
|
||||||
|
D3D9VertexBuffer* pStreamData,
|
||||||
|
UINT Stride) {
|
||||||
|
m_state.vertexBuffers[StreamNumber].vertexBuffer = pStreamData;
|
||||||
|
|
||||||
|
m_state.vertexBuffers[StreamNumber].stride = Stride;
|
||||||
|
|
||||||
|
m_captures.flags.set(D3D9CapturedStateFlag::VertexBuffers);
|
||||||
|
m_captures.vertexBuffers.set(StreamNumber, true);
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
HRESULT D3D9StateBlock::SetStreamSourceFreq(UINT StreamNumber, UINT Setting) {
|
HRESULT D3D9StateBlock::SetStreamSourceFreq(UINT StreamNumber, UINT Setting) {
|
||||||
m_state.streamFreq[StreamNumber] = Setting;
|
m_state.streamFreq[StreamNumber] = Setting;
|
||||||
|
|
||||||
@ -571,8 +586,12 @@ namespace dxvk {
|
|||||||
m_captures.flags.set(D3D9CapturedStateFlag::Material);
|
m_captures.flags.set(D3D9CapturedStateFlag::Material);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Type != D3D9StateBlockType::None)
|
if (Type != D3D9StateBlockType::None) {
|
||||||
this->Capture();
|
if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl))
|
||||||
|
SetVertexDeclaration(m_deviceState->vertexDecl.ptr());
|
||||||
|
|
||||||
|
ApplyOrCapture<D3D9StateFunction::Capture, false>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -115,6 +115,11 @@ namespace dxvk {
|
|||||||
UINT OffsetInBytes,
|
UINT OffsetInBytes,
|
||||||
UINT Stride);
|
UINT Stride);
|
||||||
|
|
||||||
|
HRESULT SetStreamSourceWithoutOffset(
|
||||||
|
UINT StreamNumber,
|
||||||
|
D3D9VertexBuffer* pStreamData,
|
||||||
|
UINT Stride);
|
||||||
|
|
||||||
HRESULT SetStreamSourceFreq(UINT StreamNumber, UINT Setting);
|
HRESULT SetStreamSourceFreq(UINT StreamNumber, UINT Setting);
|
||||||
|
|
||||||
HRESULT SetStateTexture(DWORD StateSampler, IDirect3DBaseTexture9* pTexture);
|
HRESULT SetStateTexture(DWORD StateSampler, IDirect3DBaseTexture9* pTexture);
|
||||||
@ -181,7 +186,7 @@ namespace dxvk {
|
|||||||
Capture
|
Capture
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Dst, typename Src>
|
template <typename Dst, typename Src, bool IgnoreStreamOffset>
|
||||||
void ApplyOrCapture(Dst* dst, const Src* src) {
|
void ApplyOrCapture(Dst* dst, const Src* src) {
|
||||||
if (m_captures.flags.test(D3D9CapturedStateFlag::StreamFreq)) {
|
if (m_captures.flags.test(D3D9CapturedStateFlag::StreamFreq)) {
|
||||||
for (uint32_t idx : bit::BitMask(m_captures.streamFreq.dword(0)))
|
for (uint32_t idx : bit::BitMask(m_captures.streamFreq.dword(0)))
|
||||||
@ -211,11 +216,19 @@ namespace dxvk {
|
|||||||
if (m_captures.flags.test(D3D9CapturedStateFlag::VertexBuffers)) {
|
if (m_captures.flags.test(D3D9CapturedStateFlag::VertexBuffers)) {
|
||||||
for (uint32_t idx : bit::BitMask(m_captures.vertexBuffers.dword(0))) {
|
for (uint32_t idx : bit::BitMask(m_captures.vertexBuffers.dword(0))) {
|
||||||
const auto& vbo = src->vertexBuffers[idx];
|
const auto& vbo = src->vertexBuffers[idx];
|
||||||
|
if constexpr (!IgnoreStreamOffset) {
|
||||||
dst->SetStreamSource(
|
dst->SetStreamSource(
|
||||||
idx,
|
idx,
|
||||||
vbo.vertexBuffer.ptr(),
|
vbo.vertexBuffer.ptr(),
|
||||||
vbo.offset,
|
vbo.offset,
|
||||||
vbo.stride);
|
vbo.stride);
|
||||||
|
} else {
|
||||||
|
// For whatever reason, D3D9 doesn't capture the stream offset
|
||||||
|
dst->SetStreamSourceWithoutOffset(
|
||||||
|
idx,
|
||||||
|
vbo.vertexBuffer.ptr(),
|
||||||
|
vbo.stride);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,12 +337,12 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <D3D9StateFunction Func>
|
template <D3D9StateFunction Func, bool IgnoreStreamOffset>
|
||||||
void ApplyOrCapture() {
|
void ApplyOrCapture() {
|
||||||
if constexpr (Func == D3D9StateFunction::Apply)
|
if constexpr (Func == D3D9StateFunction::Apply)
|
||||||
ApplyOrCapture(m_parent, &m_state);
|
ApplyOrCapture<D3D9DeviceEx, D3D9CapturableState, IgnoreStreamOffset>(m_parent, &m_state);
|
||||||
else if constexpr (Func == D3D9StateFunction::Capture)
|
else if constexpr (Func == D3D9StateFunction::Capture)
|
||||||
ApplyOrCapture(this, m_deviceState);
|
ApplyOrCapture<D3D9StateBlock, D3D9DeviceState, IgnoreStreamOffset>(this, m_deviceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
template <
|
||||||
|
@ -65,6 +65,16 @@ namespace dxvk {
|
|||||||
if (this_thread::isInModuleDetachment())
|
if (this_thread::isInModuleDetachment())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Locking here and in Device::GetFrontBufferData
|
||||||
|
// ensures that other threads don't accidentally access a stale pointer.
|
||||||
|
D3D9DeviceLock lock = m_parent->LockDevice();
|
||||||
|
|
||||||
|
if (m_parent->GetMostRecentlyUsedSwapchain() == this) {
|
||||||
|
m_parent->ResetMostRecentlyUsedSwapchain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DestroyBackBuffers();
|
DestroyBackBuffers();
|
||||||
|
|
||||||
ResetWindowProc(m_window);
|
ResetWindowProc(m_window);
|
||||||
@ -112,6 +122,8 @@ namespace dxvk {
|
|||||||
DWORD dwFlags) {
|
DWORD dwFlags) {
|
||||||
D3D9DeviceLock lock = m_parent->LockDevice();
|
D3D9DeviceLock lock = m_parent->LockDevice();
|
||||||
|
|
||||||
|
m_parent->SetMostRecentlyUsedSwapchain(this);
|
||||||
|
|
||||||
if (unlikely(m_parent->IsDeviceLost()))
|
if (unlikely(m_parent->IsDeviceLost()))
|
||||||
return D3DERR_DEVICELOST;
|
return D3DERR_DEVICELOST;
|
||||||
|
|
||||||
@ -147,6 +159,8 @@ namespace dxvk {
|
|||||||
bool recreate = false;
|
bool recreate = false;
|
||||||
recreate |= m_wctx->presenter == nullptr;
|
recreate |= m_wctx->presenter == nullptr;
|
||||||
recreate |= m_dialog != m_lastDialog;
|
recreate |= m_dialog != m_lastDialog;
|
||||||
|
if (options->deferSurfaceCreation)
|
||||||
|
recreate |= m_parent->IsDeviceReset();
|
||||||
|
|
||||||
if (m_wctx->presenter != nullptr) {
|
if (m_wctx->presenter != nullptr) {
|
||||||
m_dirty |= m_wctx->presenter->setSyncInterval(presentInterval) != VK_SUCCESS;
|
m_dirty |= m_wctx->presenter->setSyncInterval(presentInterval) != VK_SUCCESS;
|
||||||
@ -158,6 +172,9 @@ namespace dxvk {
|
|||||||
|
|
||||||
m_lastDialog = m_dialog;
|
m_lastDialog = m_dialog;
|
||||||
|
|
||||||
|
if (m_window == nullptr)
|
||||||
|
return D3D_OK;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
const bool useGDIFallback = m_partialCopy && !HasFrontBuffer();
|
const bool useGDIFallback = m_partialCopy && !HasFrontBuffer();
|
||||||
if (useGDIFallback)
|
if (useGDIFallback)
|
||||||
@ -177,6 +194,7 @@ namespace dxvk {
|
|||||||
if (!m_wctx->presenter->hasSwapChain())
|
if (!m_wctx->presenter->hasSwapChain())
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
|
|
||||||
|
UpdateTargetFrameRate(presentInterval);
|
||||||
PresentImage(presentInterval);
|
PresentImage(presentInterval);
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
} catch (const DxvkError& e) {
|
} catch (const DxvkError& e) {
|
||||||
@ -380,6 +398,20 @@ namespace dxvk {
|
|||||||
blitInfo.srcOffsets[0] = VkOffset3D{ 0, 0, 0 };
|
blitInfo.srcOffsets[0] = VkOffset3D{ 0, 0, 0 };
|
||||||
blitInfo.srcOffsets[1] = VkOffset3D{ int32_t(srcExtent.width), int32_t(srcExtent.height), 1 };
|
blitInfo.srcOffsets[1] = VkOffset3D{ int32_t(srcExtent.width), int32_t(srcExtent.height), 1 };
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (m_presentParams.Windowed) {
|
||||||
|
// In windowed mode, GetFrontBufferData takes a screenshot of the entire screen.
|
||||||
|
// So place the copy of the front buffer at the position of the window.
|
||||||
|
POINT point = { 0, 0 };
|
||||||
|
if (ClientToScreen(m_window, &point) != 0) {
|
||||||
|
blitInfo.dstOffsets[0].x = point.x;
|
||||||
|
blitInfo.dstOffsets[0].y = point.y;
|
||||||
|
blitInfo.dstOffsets[1].x += point.x;
|
||||||
|
blitInfo.dstOffsets[1].y += point.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
m_parent->EmitCs([
|
m_parent->EmitCs([
|
||||||
cDstImage = blittedSrc,
|
cDstImage = blittedSrc,
|
||||||
cDstMap = dstTexInfo->GetMapping().Swizzle,
|
cDstMap = dstTexInfo->GetMapping().Swizzle,
|
||||||
@ -899,7 +931,6 @@ namespace dxvk {
|
|||||||
presenterDesc.fullScreenExclusive = PickFullscreenMode();
|
presenterDesc.fullScreenExclusive = PickFullscreenMode();
|
||||||
|
|
||||||
m_wctx->presenter = new Presenter(m_device, m_wctx->frameLatencySignal, presenterDesc);
|
m_wctx->presenter = new Presenter(m_device, m_wctx->frameLatencySignal, presenterDesc);
|
||||||
m_wctx->presenter->setFrameRateLimit(m_parent->GetOptions()->maxFrameRate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -966,6 +997,9 @@ namespace dxvk {
|
|||||||
|
|
||||||
|
|
||||||
void D3D9SwapChainEx::UpdateWindowCtx() {
|
void D3D9SwapChainEx::UpdateWindowCtx() {
|
||||||
|
if (m_window == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!m_presenters.count(m_window)) {
|
if (!m_presenters.count(m_window)) {
|
||||||
auto res = m_presenters.emplace(
|
auto res = m_presenters.emplace(
|
||||||
std::piecewise_construct,
|
std::piecewise_construct,
|
||||||
@ -1079,6 +1113,17 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void D3D9SwapChainEx::UpdateTargetFrameRate(uint32_t SyncInterval) {
|
||||||
|
double frameRateOption = double(m_parent->GetOptions()->maxFrameRate);
|
||||||
|
double frameRate = std::max(frameRateOption, 0.0);
|
||||||
|
|
||||||
|
if (SyncInterval && frameRateOption == 0.0)
|
||||||
|
frameRate = -m_displayRefreshRate / double(SyncInterval);
|
||||||
|
|
||||||
|
m_wctx->presenter->setFrameRateLimit(frameRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void D3D9SwapChainEx::SyncFrameLatency() {
|
void D3D9SwapChainEx::SyncFrameLatency() {
|
||||||
// Wait for the sync event so that we respect the maximum frame latency
|
// Wait for the sync event so that we respect the maximum frame latency
|
||||||
m_wctx->frameLatencySignal->wait(m_wctx->frameId - GetActualFrameLatency());
|
m_wctx->frameLatencySignal->wait(m_wctx->frameId - GetActualFrameLatency());
|
||||||
@ -1295,10 +1340,10 @@ namespace dxvk {
|
|||||||
|| dstRect.right - dstRect.left != LONG(width)
|
|| dstRect.right - dstRect.left != LONG(width)
|
||||||
|| dstRect.bottom - dstRect.top != LONG(height);
|
|| dstRect.bottom - dstRect.top != LONG(height);
|
||||||
|
|
||||||
bool recreate =
|
bool recreate = m_wctx != nullptr
|
||||||
m_wctx->presenter == nullptr
|
&& (m_wctx->presenter == nullptr
|
||||||
|| m_wctx->presenter->info().imageExtent.width != width
|
|| m_wctx->presenter->info().imageExtent.width != width
|
||||||
|| m_wctx->presenter->info().imageExtent.height != height;
|
|| m_wctx->presenter->info().imageExtent.height != height);
|
||||||
|
|
||||||
m_swapchainExtent = { width, height };
|
m_swapchainExtent = { width, height };
|
||||||
m_dstRect = dstRect;
|
m_dstRect = dstRect;
|
||||||
|
@ -213,6 +213,8 @@ namespace dxvk {
|
|||||||
|
|
||||||
void InitRamp();
|
void InitRamp();
|
||||||
|
|
||||||
|
void UpdateTargetFrameRate(uint32_t SyncInterval);
|
||||||
|
|
||||||
uint32_t GetActualFrameLatency();
|
uint32_t GetActualFrameLatency();
|
||||||
|
|
||||||
uint32_t PickFormats(
|
uint32_t PickFormats(
|
||||||
|
@ -9,13 +9,14 @@ namespace dxvk {
|
|||||||
|
|
||||||
// Doesn't compare everything, only what we use in SWVP.
|
// Doesn't compare everything, only what we use in SWVP.
|
||||||
|
|
||||||
size_t D3D9VertexDeclHash::operator () (const D3D9VertexElements& key) const {
|
size_t D3D9VertexDeclHash::operator () (const D3D9CompactVertexElements& key) const {
|
||||||
DxvkHashState hash;
|
DxvkHashState hash;
|
||||||
|
|
||||||
std::hash<BYTE> bytehash;
|
std::hash<BYTE> bytehash;
|
||||||
std::hash<WORD> wordhash;
|
std::hash<WORD> wordhash;
|
||||||
|
|
||||||
for (auto& element : key) {
|
for (uint32_t i = 0; i < key.size(); i++) {
|
||||||
|
const auto& element = key[i];
|
||||||
hash.add(wordhash(element.Stream));
|
hash.add(wordhash(element.Stream));
|
||||||
hash.add(wordhash(element.Offset));
|
hash.add(wordhash(element.Offset));
|
||||||
hash.add(bytehash(element.Type));
|
hash.add(bytehash(element.Type));
|
||||||
@ -27,7 +28,7 @@ namespace dxvk {
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D9VertexDeclEq::operator () (const D3D9VertexElements& a, const D3D9VertexElements& b) const {
|
bool D3D9VertexDeclEq::operator () (const D3D9CompactVertexElements& a, const D3D9CompactVertexElements& b) const {
|
||||||
if (a.size() != b.size())
|
if (a.size() != b.size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -109,7 +110,7 @@ namespace dxvk {
|
|||||||
m_module.opLabel(m_module.allocateId());
|
m_module.opLabel(m_module.allocateId());
|
||||||
}
|
}
|
||||||
|
|
||||||
void compile(const D3D9VertexDecl* pDecl) {
|
void compile(const D3D9CompactVertexElements& elements) {
|
||||||
uint32_t uint_t = m_module.defIntType(32, false);
|
uint32_t uint_t = m_module.defIntType(32, false);
|
||||||
uint32_t float_t = m_module.defFloatType(32);
|
uint32_t float_t = m_module.defFloatType(32);
|
||||||
uint32_t vec4_t = m_module.defVectorType(float_t, 4);
|
uint32_t vec4_t = m_module.defVectorType(float_t, 4);
|
||||||
@ -144,13 +145,22 @@ namespace dxvk {
|
|||||||
uint32_t primitiveId = m_module.opLoad(uint_t, primitiveIdPtr);
|
uint32_t primitiveId = m_module.opLoad(uint_t, primitiveIdPtr);
|
||||||
|
|
||||||
// The size of any given vertex
|
// The size of any given vertex
|
||||||
uint32_t vertexSize = m_module.constu32(pDecl->GetSize() / sizeof(uint32_t));
|
uint32_t size = 0;
|
||||||
|
for (uint32_t i = 0; i < elements.size(); i++) {
|
||||||
|
const auto& element = elements[i];
|
||||||
|
if (element.Stream == 0 && element.Type != D3DDECLTYPE_UNUSED) {
|
||||||
|
size = std::max(size, element.Offset + GetDecltypeSize(D3DDECLTYPE(element.Type)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t vertexSize = m_module.constu32(size / sizeof(uint32_t));
|
||||||
|
|
||||||
//The offset of this vertex from the beginning of the buffer
|
//The offset of this vertex from the beginning of the buffer
|
||||||
uint32_t thisVertexOffset = m_module.opIMul(uint_t, vertexSize, primitiveId);
|
uint32_t thisVertexOffset = m_module.opIMul(uint_t, vertexSize, primitiveId);
|
||||||
|
|
||||||
|
|
||||||
for (auto& element : pDecl->GetElements()) {
|
for (uint32_t i = 0; i < elements.size(); i++) {
|
||||||
|
const auto& element = elements[i];
|
||||||
// Load the slot associated with this element
|
// Load the slot associated with this element
|
||||||
DxsoSemantic semantic = { DxsoUsage(element.Usage), element.UsageIndex };
|
DxsoSemantic semantic = { DxsoUsage(element.Usage), element.UsageIndex };
|
||||||
|
|
||||||
@ -297,9 +307,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Rc<DxvkShader> D3D9SWVPEmulator::GetShaderModule(D3D9DeviceEx* pDevice, const D3D9VertexDecl* pDecl) {
|
Rc<DxvkShader> D3D9SWVPEmulator::GetShaderModule(D3D9DeviceEx* pDevice, D3D9CompactVertexElements&& elements) {
|
||||||
auto& elements = pDecl->GetElements();
|
|
||||||
|
|
||||||
// Use the shader's unique key for the lookup
|
// Use the shader's unique key for the lookup
|
||||||
{ std::unique_lock<dxvk::mutex> lock(m_mutex);
|
{ std::unique_lock<dxvk::mutex> lock(m_mutex);
|
||||||
|
|
||||||
@ -317,7 +325,7 @@ namespace dxvk {
|
|||||||
// This shader has not been compiled yet, so we have to create a
|
// 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.
|
// new module. This takes a while, so we won't lock the structure.
|
||||||
D3D9SWVPEmulatorGenerator generator(name);
|
D3D9SWVPEmulatorGenerator generator(name);
|
||||||
generator.compile(pDecl);
|
generator.compile(elements);
|
||||||
Rc<DxvkShader> shader = generator.finalize();
|
Rc<DxvkShader> shader = generator.finalize();
|
||||||
|
|
||||||
shader->setShaderKey(key);
|
shader->setShaderKey(key);
|
||||||
@ -338,7 +346,8 @@ namespace dxvk {
|
|||||||
// that object instead and discard the newly created module.
|
// that object instead and discard the newly created module.
|
||||||
{ std::unique_lock<dxvk::mutex> lock(m_mutex);
|
{ std::unique_lock<dxvk::mutex> lock(m_mutex);
|
||||||
|
|
||||||
auto status = m_modules.insert({ elements, shader });
|
std::pair<D3D9CompactVertexElements, Rc<DxvkShader>> pair = { std::move(elements), shader };
|
||||||
|
auto status = m_modules.insert(std::move(pair));
|
||||||
if (!status.second)
|
if (!status.second)
|
||||||
return status.first->second;
|
return status.first->second;
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user