Compare commits

...

69 Commits

Author SHA1 Message Date
Tiagoquix 79eea564fb Add dxvk.maxChunkSize 1 to Origin 2024-05-31 23:46:26 +02:00
Robin Kertels 7df8017e46 [d3d9] FF: Apply transform flags count to generated texture coords
... and fix cases where projection doesn't get applied.
2024-05-27 20:52:32 +00:00
WinterSnowfall c98152683f [d3d9] Tweak VCache query results 2024-05-27 19:53:19 +02:00
Blisto91 890ad3f47f [CI] Update CI actions 2024-05-27 19:38:16 +02:00
Robin Kertels 60cfafe027 [d3d9] Fix strange type in dynamic vertex upload 2024-05-26 04:58:47 +00:00
Robin Kertels 889802887f [d3d9] Rework uploading dynamic sysmem buffers at draw time
... and handle mismatching vertex sizes and vertex strides.
2024-05-23 16:44:49 +02:00
WinterSnowfall a1ce690c5c [d3d9] Determine DF format support in the options parser 2024-05-23 16:37:09 +02:00
WinterSnowfall 07d007c642 [d3d9] Use customVendorId to determine the options vendorId 2024-05-23 16:37:09 +02:00
Danylo Piliaiev 58d8ea2d31 [d3d11,d3d9,util] Add a config option for reproducible VK output
It ensures that for the same D3D commands the output VK commands
don't change between runs.

Useful for comparative benchmarking, can negatively affect performance.

Signed-off-by: Danylo Piliaiev <dpiliaiev@igalia.com>
2024-05-23 15:20:28 +02:00
Philip Rebohle 61bd62c327 [dxvk] Allow descriptor pool overallocation if supported 2024-05-23 14:27:28 +02:00
Philip Rebohle 7bc77b597e [dxvk] Enable VK_NV_descriptor_pool_overallocation if available 2024-05-23 14:27:28 +02:00
Simon McVittie 2ff2c826a5 [build] Generate pkg-config metadata to link to DXVK libraries
This allows dependent projects to query the version and location of DXVK
via the pkg-config interface.

The include directories aren't yet set, because the headers aren't
installed; that will follow in a subsequent commit.

The naming of these pkg-config files is based on proposed Fedora packages
for DXVK 2.0, and is not compatible with older Fedora packages for DXVK
1.x (which used the naming convention dxvk-native-d3d9 and so on).
Packagers can create symlinks such as dxvk-native-d3d9.pc -> dxvk-d3d9.pc
if they want to retain compatibility with older names.

Signed-off-by: Simon McVittie <smcv@collabora.com>
2024-05-21 19:18:12 +00:00
Simon McVittie f9b4046223 [build] Install headers for native builds
When building a game that has been ported to Linux using DXVK Native,
these headers are necessary to provide the Direct3D and DXVK APIs.

Signed-off-by: Simon McVittie <smcv@collabora.com>
2024-05-21 19:18:12 +00:00
Simon McVittie 83436a97f2 [meson] Set the stem of library names instead of the name_prefix
This is necessary for compatibility with Meson's pkg module, which
generates pkg-config metadata containing "-lNAME" where NAME is the
first argument to shared_library(). Changing the name_prefix parameter
would break that.

Conversely, including .dll or .so in the first parameter would also
break that, so remove the `+dll_ext` part (in practice this is not a
functional change, because `dll_ext` is always set to an empty string).

Signed-off-by: Simon McVittie <smcv@collabora.com>
2024-05-21 19:18:12 +00:00
Ethan Lee e991bfa604 [ci] Use a tarball for the steamrt-sniper artifact.
Zips can't preserve the symlink, so make the .tar.gz package with package_native.sh and zip that up instead.
2024-05-21 19:18:12 +00:00
Ethan Lee f33453afbb [build] Add soversion to dxvk-native binaries 2024-05-21 19:18:12 +00:00
Robin Kertels 65dd3c7df3 [d3d9] Always enable STORAGE_BUFFER usage
Fixes a validation error. Drivers don't care about buffer usage bits anyway.
2024-05-21 20:32:13 +02:00
Robin Kertels dfc3776b24 [d3d9] FF: Fix a bunch of wine tests with FF texture coordinates 2024-05-21 20:32:13 +02:00
Philip Rebohle 3420cd78ac [dxvk] Use new Version helper to deal with driver version numbers 2024-05-20 18:30:36 +02:00
Philip Rebohle 4225f35034 [util] Add version helper class
Useful to decode, store and compare human-readable driver versions.
2024-05-20 18:30:36 +02:00
Philip Rebohle 2cb2f8694e [dxvk] Use VK_MAKE_API_VERSION instead of VK_MAKE_VERSION.
The old macro is deprecated.
2024-05-20 18:30:36 +02:00
Blisto91 c1f665f92b [util] Disable supportDFFormats for Prototype
Incorrect shadows on AMD & Intel
2024-05-18 19:31:07 +02:00
WinterSnowfall 20185a5309 [d3d9] Do not enable support for DF formats on Nvidia 2024-05-18 16:16:34 +00:00
Blisto91 0c2efda804 [meta] Add DXVK Native section to the readme 2024-05-16 10:57:35 +00:00
Blisto91 c7d61b2fc0 [native] Change DXVK_WSIDRIVER to DXVK_WSI_DRIVER 2024-05-16 10:57:35 +00:00
Ethan Lee 6259e86392 [meson] Use dependency() instead of find_library() for SDL2/GLFW detection.
Since we're not linking to the libraries anymore, it doesn't make much sense to
use find_library, and in fact we need to use dependency() in order to get the
right CFLAGS for includes, defines, etc, so use that instead.

As a result, we can remove the 'SDL2/' folders from the includes, making the SDL
includes more correct.
2024-05-13 13:18:03 +00:00
Ethan Lee d5d236a1e2 [wsi] Refactor platform system to support multiple WSI implementations 2024-05-13 13:18:03 +00:00
Ethan Lee 10b83d184b [native] Dynamically load SDL2/GLFW at runtime.
Removing these link-time dependencies is important for making a single binary that is compatible with either backend, regardless of whether or not each one is currently available to the program.
2024-05-13 13:18:03 +00:00
Ethan Lee 0f7c1f753a [wsi] Refactor the WSI backends to be implementations of a WsiDriver interface.
Rather than directly calling functions, the API now calls shared functions that call into a WsiDriver instance, which is allocated and implemented by the backend. Functionally this should be the same, it just has the extra allocation for the function table.

This prepares the WSI library for supporting multiple implementations in a single binary.
2024-05-13 13:18:03 +00:00
Ethan Lee 529129c332 [dxvk] Move getInstanceExtensions platform logic to wsi.
This ensures that all of the WSI backend logic is in one place rather than two.
2024-05-13 13:18:03 +00:00
Ethan Lee 4055a92856 [wsi] Add init/quit functions, integrate them into DxvkInstance.
This is preparation for loading/unloading WSI backends at runtime, which will be in an upcoming commit.
2024-05-13 13:18:03 +00:00
Blisto91 7bad17c1d1 [util] Set deviceLossOnFocusLoss for The Sims 3
Prevents the game black screening on alt-tab
2024-05-11 14:38:43 +02:00
Blisto91 6b76d70d9d [util] Enable d3d11.longMad for Guild Wars 2
Fixes invisibility effect flicker when invariantPosition is enabled
2024-05-09 00:47:13 +02:00
Philip Rebohle 611dc60018 [d3d9] Do not support cube textures with depth formats 2024-05-08 17:05:48 +00:00
WinterSnowfall b2789ab894 [d3d9] Validate DS format support during CheckDepthStencilMatch 2024-05-06 20:26:09 +00:00
Philip Rebohle ab715a8876 [d3d11] Implement better filtering when blitting video content
Unlike linear filtering this guarantees that we never read outside the source
region, and this also lets us perform color space conversion prior to filtering.
2024-05-03 16:23:17 +02:00
talkingerbil 1fb35b6d19
[dxgi] Initialize UMD version quad to a max signed int64 (#3985) 2024-05-03 16:22:58 +02:00
Rémi Bernon 4333ee872d [d3d11] Use nearest filter for ID3D11VideoContext scaling 2024-05-02 18:17:54 +02:00
Rémi Bernon b99d42c688 [d3d11] Implement VideoProcessorSetStreamSourceRect scaling 2024-05-02 18:17:54 +02:00
Blisto91 dacb8b434b [util] Add configs for Delta Force Xtreme 1 & 2
Prevents the games from black screening on Alt-Tab and helps big performance dips.
2024-05-01 14:08:03 +02:00
Philip Rebohle ea4cb84d8a [dxvk] Remove workaround for non-dynamic depth clip
Kind of pointless and everyone supports the required EDS3 subset anyway.
2024-04-29 17:43:40 +02:00
Philip Rebohle 65373792d2 [dxvk] Forward link flags when using shader identifiers
Fixes a long-standing bug that now causes validation errors.
2024-04-29 17:43:40 +02:00
Lierrmm 29253da356 feat: add H2M-Mod to config 2024-04-29 16:19:42 +02:00
Robin Kertels 79398b468d [util] Enable longMad for Red Faction Guerrila Remastered 2024-04-29 13:17:21 +02:00
Robin Kertels e7d14e97de [dxbc] Implement option to split up fma 2024-04-29 13:17:21 +02:00
Philip Rebohle c613078ba8 [dxvk] Bump internal version number
Potentially useful for drivers and tools to deal with the new pipeline
layout changes.
2024-04-26 19:54:52 +02:00
Philip Rebohle 2970645f33 [dxvk] Fix push constant compatibility for pipeline libraries
When linking pipelines, all pipeline libraries are required to declare
the exact same set of push constants, even for stages not part of the
respective libraries.

This invalidates all fossilize databases.
2024-04-26 19:54:52 +02:00
Philip Rebohle 462165da19 [util] Add Deck profile for Fallout 4
Should fix the FPS problem on Deck OLED.
2024-04-26 14:34:08 +02:00
Philip Rebohle 3f27a0ee58 [util] Add a way to define app profiles exclusive to Steam Deck 2024-04-26 14:34:08 +02:00
Katharine Chui aac3396671 [dxgi] unchain DxgiFactory::CreateSwapChain and CreateSwapChainForHwnd
similar to https://github.com/doitsujin/dxvk/pull/3966, avoid
chaining so that dxgi tools attempting to wrap swapchains don't
end up double wrapping

ref: https://github.com/SpecialKO/SpecialK/issues/168
2024-04-25 12:07:50 +02:00
Katharine Chui 92a43ebf65 [dxgi] unchain DxgiSwapChain::Present1 and Present
dxgi hooking tools might hook both, eg. https://github.com/SpecialKO/SpecialK/issues/167
2024-04-22 14:04:43 +02:00
Blisto91 8ba5256dc7 [util] Set deferSurfaceCreation for 9th Dawn II
OpenGL game that also spins up d3d9. Will black screen without deferSurfaceCreation when using dxvk
2024-04-22 04:48:56 +02:00
Philip Rebohle 2b70ba8f77 [dxbc] Do not emit OpImageQueryLevels for multisampled images 2024-04-19 13:55:31 +02:00
Philip Rebohle 9c66c4bf1d [build] Target SPIR-V 1.6 for built-in GLSL shaders
Silences a Mesa warning when the HUD is enabled.
2024-04-19 13:36:32 +02:00
Philip Rebohle 00872e9e4f [dxvk] Fix render target clears with format reinterpretation
With LOAD_OP_CLEAR, we cannot rely on the clear actually being performed
with the view format in mind. Use a vkCmdClearAttachment path instead.
2024-04-19 13:08:36 +02:00
Philip Rebohle 35157357dd [dxvk] Fix stencil discard being broken 2024-04-19 01:43:23 +02:00
Philip Rebohle 617ebf4e05 [dxbc] Take used components into account for PS inputs 2024-04-19 01:01:52 +02:00
Philip Rebohle c2489d5a45 [dxbc] Fix array register anaylsis with multiple dst operands 2024-04-19 01:01:52 +02:00
Philip Rebohle 6ef98c613f [dxvk] Re-enable maintenance4 feature
Sileces some validation errors.
2024-04-19 01:01:52 +02:00
Philip Rebohle 7441137a33 [dxbc] Ignore system value components when declaring inputs 2024-04-19 01:01:52 +02:00
WinterSnowfall 571948cfc0 [d3d9] Remove support for VERTEXSTATS queries 2024-04-13 19:11:00 +01:00
Martino Fontana 133f0794bc [util] Remove framerate limiter for Nier Replicant
Without mods, Nier Replicant runs faster when going above 60 FPS.
The game had an official patch that implemented a framerate limiter to prevent this. This limiter is terrible, because it's not a stable 60 FPS, but a weird 57-58 FPS. There's no way to disable this in-game, it has to be done by editing a config file or through a mod.

So, why remove the default frame limiter from DXVK?
- In the default case (a user plays the game as it is), it does nothing, since 57-58 is lower than 60.
- If a user is going out of their way to edit the config file, why would they assume that DXVK already provides a frame limiter? They are going to follow [a guide](https://www.pcgamingwiki.com/wiki/NieR_Replicant#Framerate_limited_to_57.7E58_FPS) that says to set up a frame limiter, it doesn't say "set up a frame limiter, unless you are using DXVK, in that case it's already there".
- They are using [Special K in order to use a mod to play at high refresh rates at normal speed](https://wiki.special-k.info/SpecialK/Custom/Replicant). In this case, DXVK's default limiter is harmful, since it is not documented that it's there by default.

Since this default limiter is useless in the first two cases and harmful in the third, I think it should be removed.
The alternative would be to document this (e.g. in PCGamingWiki), but the instructions wouldn't look pretty... "After following the instructions to use Special K in order to play at higher framerates at normal speed, if are using DXVK/Proton, also do these things to disable its default 60 FPS cap: [...]"

Especially because that the game isn't broken in the default case, I don't think DXVK should tamper with these things in a way that requires documentation to revert.

Tested Special K's mod to play at higher refresh rates on Linux.
2024-04-08 21:48:47 +02:00
Philip Rebohle 44695f9311 [dxvk] Adjust desciptor pool reset heuristic
Drastically limits the amount of descriptor memory we allocate in situations
where an application renders without presenting anything to a swap chain.

The new limit is a bit tight for some real-world use cases (e.g. Ashes of the Singularity),
but at worst we will start calling vkAllocateDescriptorSets once per set and draw.
2024-04-08 15:40:25 +02:00
Casey Bowman 49e9ea5f5a [dxgi] Force vendor ID change when XeSS is detected on an Intel GPU
Games using libxess.dll or wrapper modules will crash.
To work around this, we hide the Intel GPU's vendor ID to avoid using the
XeSS module.
2024-04-03 20:32:04 +02:00
Blisto91 198bd3a4b8 [d3d11] Remove missed Shared Keyedmutex warning
D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX is implemented.
2024-04-03 20:31:52 +02:00
Philip Rebohle f06c646315 [dxbc] Remove broken atomic counter subgroup optimization
This is not a legal optimization inside non-uniform control flow due
to Vulkan's extremely permissive convergence rules, and apparently
breaks on Nvidia as a result.

Mesa drivers already do the same thing internally anyway.
2024-04-03 14:55:43 +02:00
Philip Rebohle 855b2746b6 [util] Remove TRAHA Global config
This game apparently no longer exists.
2024-03-26 13:42:55 +01:00
Blisto91 28c7c09bf5 [dxgi] Remove useMonitorFallback option
QueryDisplayConfig optimization is now in Proton 9 Wine
2024-03-21 17:23:38 +01:00
Philip Rebohle 2742486540 [dxvk] Don't query color space support for null surfaces
Fixes a crash in games with the dxgi.deferSurfaceCreation workaround set.
Note that this potentially breaks HDR.
2024-03-21 15:32:48 +01:00
93 changed files with 1898 additions and 882 deletions

View File

@ -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: with:
name: dxvk-${{ env.VERSION_NAME }} name: dxvk-native-${{ env.VERSION_NAME }}
path: build/dxvk-native-${{ env.VERSION_NAME }} path: build/dxvk-native-${{ env.VERSION_NAME }}.tar.gz
if-no-files-found: error 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:
name: dxvk-${{ env.VERSION_NAME }}
pattern: dxvk*
delete-merged: true

View File

@ -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

View File

@ -163,3 +163,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.

View File

@ -312,6 +312,20 @@
# d3d11.exposeDriverCommandLists = True # 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
@ -499,6 +513,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
@ -514,7 +529,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
@ -644,18 +661,6 @@
# d3d9.textureMemory = 100 # d3d9.textureMemory = 100
# Always enumerate all monitors on each dxgi output
#
# Used to avoid performance degradation in some games
# (will be deprecated once QueryDisplayConfig optimization
# is in Proton Wine).
#
# Supported values:
# - True/False
# dxgi.useMonitorFallback = False
# Hide integrated graphics from applications # Hide integrated graphics from applications
# #
# Only has an effect when dedicated GPUs are present on the system. It is # Only has an effect when dedicated GPUs are present on the system. It is

View 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',
)

View File

@ -1,6 +1,6 @@
#include <windows.h> #include <windows.h>
#include <SDL2/SDL.h> #include <SDL.h>
namespace dxvk::wsi { namespace dxvk::wsi {
@ -22,4 +22,4 @@ namespace dxvk::wsi {
return reinterpret_cast<HMONITOR>(static_cast<intptr_t>(displayId + 1)); return reinterpret_cast<HMONITOR>(static_cast<intptr_t>(displayId + 1));
} }
} }

View File

@ -1,5 +1,6 @@
project('dxvk', ['c', 'cpp'], version : 'v2.3.1', meson_version : '>= 0.58', default_options : [ 'cpp_std=c++17', 'warning_level=2' ]) project('dxvk', ['c', 'cpp'], version : 'v2.3.1', 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') fs = import('fs')
@ -54,6 +55,8 @@ dep_displayinfo = dependency(
) )
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',
@ -115,10 +118,13 @@ 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 = meson.project_version().strip('v').split('.')
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@' ] )
@ -128,17 +134,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',
@ -154,13 +163,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@',
@ -179,4 +187,8 @@ dxvk_version = vcs_tag(
output: 'version.h', output: 'version.h',
) )
if platform != 'windows'
subdir('include/native')
endif
subdir('src') subdir('src')

View File

@ -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

View File

@ -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([

View File

@ -32,6 +32,8 @@ namespace dxvk {
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->maxFrameRate = config.getOption<int32_t>("dxgi.maxFrameRate", 0);
this->exposeDriverCommandLists = config.getOption<bool>("d3d11.exposeDriverCommandLists", true); this->exposeDriverCommandLists = config.getOption<bool>("d3d11.exposeDriverCommandLists", true);
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);

View File

@ -120,6 +120,14 @@ namespace dxvk {
/// 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;
}; };
} }

View File

@ -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)

View File

@ -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();
} }

View File

@ -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();

View File

@ -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

View File

@ -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;
} }

View File

@ -162,6 +162,9 @@ 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;
@ -224,11 +227,15 @@ namespace dxvk {
if (!IsDepthFormat(DepthStencilFormat)) if (!IsDepthFormat(DepthStencilFormat))
return D3DERR_NOTAVAILABLE; return D3DERR_NOTAVAILABLE;
auto dsfMapping = ConvertFormatUnfixed(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 = ConvertFormatUnfixed(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;

View File

@ -118,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();
@ -131,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

View File

@ -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);
/** /**

View File

@ -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;
}
} }

View File

@ -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);
}; };
} }

View File

@ -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 ) {
@ -594,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 {
@ -664,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 {
@ -721,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 {
@ -3798,7 +3799,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 {
@ -3846,7 +3847,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)
@ -3901,7 +3902,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 {
@ -5158,7 +5159,7 @@ namespace dxvk {
return D3D_OK; return D3D_OK;
} }
void D3D9DeviceEx::UploadDynamicSysmemBuffers( void D3D9DeviceEx::UploadDynamicSysmemBuffers(
UINT& FirstVertexIndex, UINT& FirstVertexIndex,
@ -5190,25 +5191,58 @@ namespace dxvk {
// First we calculate the size of that UP buffer slice // First we calculate the size of that UP buffer slice
// and store all sizes and offsets into it. // and store all sizes and offsets into it.
uint32_t upBufferSize = 0; struct VBOCopy {
std::array<uint32_t, caps::MaxStreams> vboUPBufferOffsets = {}; uint32_t srcOffset;
std::array<uint32_t, caps::MaxStreams> vboUPBufferSizes = {}; uint32_t dstOffset;
for (uint32_t i = 0; i < caps::MaxStreams && dynamicSysmemVBOs; i++) { uint32_t copyBufferLength;
vboUPBufferOffsets[i] = upBufferSize; 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); auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer);
if (likely(vbo == nullptr)) { if (likely(vbo == nullptr)) {
vboUPBufferSizes[i] = 0;
continue; continue;
} }
const uint32_t vertexStride = m_state.vertexDecl->GetSize(i); const uint32_t vertexSize = m_state.vertexDecl->GetSize(i);
uint32_t offset = (FirstVertexIndex + BaseVertexIndex) * vertexStride; const uint32_t vertexStride = m_state.vertexBuffers[i].stride;
const uint32_t vertexBufferSize = vbo->Desc()->Size; const uint32_t srcStride = vertexStride;
if (offset < vertexBufferSize) { const uint32_t dstStride = std::min(vertexStride, vertexSize);
const uint32_t vertexDataSize = std::min(NumVertices * vertexStride, vertexBufferSize - offset);
vboUPBufferSizes[i] = vertexDataSize; uint32_t elementCount = NumVertices;
upBufferSize += vertexDataSize; 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 iboUPBufferSize = 0;
@ -5221,13 +5255,13 @@ namespace dxvk {
uint32_t indexBufferSize = ibo->Desc()->Size; uint32_t indexBufferSize = ibo->Desc()->Size;
if (offset < indexBufferSize) { if (offset < indexBufferSize) {
iboUPBufferSize = std::min(NumIndices * indexStride, indexBufferSize - offset); iboUPBufferSize = std::min(NumIndices * indexStride, indexBufferSize - offset);
iboUPBufferOffset = upBufferSize; iboUPBufferOffset = totalUpBufferSize;
upBufferSize += iboUPBufferSize; totalUpBufferSize += iboUPBufferSize;
} }
} }
} }
if (unlikely(upBufferSize == 0)) { if (unlikely(totalUpBufferSize == 0)) {
*pDynamicVBOs = false; *pDynamicVBOs = false;
if (pDynamicIBO) if (pDynamicIBO)
*pDynamicIBO = false; *pDynamicIBO = false;
@ -5235,47 +5269,49 @@ namespace dxvk {
return; return;
} }
auto upSlice = AllocUPBuffer(upBufferSize); auto upSlice = AllocUPBuffer(totalUpBufferSize);
// Now copy the actual data and bind it. // Now copy the actual data and bind it.
if (dynamicSysmemVBOs) { for (uint32_t i = 0; i < caps::MaxStreams && dynamicSysmemVBOs; i++) {
for (uint32_t i = 0; i < caps::MaxStreams; i++) { const VBOCopy& copy = vboCopies[i];
if (unlikely(vboUPBufferSizes[i] == 0)) {
EmitCs([ if (likely(copy.copyBufferLength != 0)) {
cStream = i const auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer);
](DxvkContext* ctx) { uint8_t* data = reinterpret_cast<uint8_t*>(upSlice.mapPtr) + copy.dstOffset;
ctx->bindVertexBuffer(cStream, DxvkBufferSlice(), 0); const uint8_t* src = reinterpret_cast<uint8_t*>(vbo->GetMappedSlice().mapPtr) + copy.srcOffset;
});
m_flags.set(D3D9DeviceFlag::DirtyVertexBuffers); if (likely(copy.copyElementStride == copy.copyElementSize)) {
continue; 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* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer);
const uint32_t vertexStride = m_state.vertexDecl->GetSize(i);
uint32_t offset = (BaseVertexIndex + FirstVertexIndex) * vertexStride + m_state.vertexBuffers[i].offset;
uint8_t* data = reinterpret_cast<uint8_t*>(upSlice.mapPtr) + vboUPBufferOffsets[i];
uint8_t* src = reinterpret_cast<uint8_t*>(vbo->GetMappedSlice().mapPtr) + offset;
std::memcpy(data, src, vboUPBufferSizes[i]);
auto vboSlice = upSlice.slice.subSlice(vboUPBufferOffsets[i], vboUPBufferSizes[i]);
EmitCs([
cStream = i,
cBufferSlice = std::move(vboSlice),
cStride = vertexStride
](DxvkContext* ctx) mutable {
ctx->bindVertexBuffer(cStream, std::move(cBufferSlice), cStride);
});
m_flags.set(D3D9DeviceFlag::DirtyVertexBuffers);
} }
// Change the draw call parameters to reflect the changed vertex buffers auto vboSlice = upSlice.slice.subSlice(copy.dstOffset, copy.copyBufferLength);
if (NumIndices != 0) { EmitCs([
BaseVertexIndex = -FirstVertexIndex; cStream = i,
} else { cBufferSlice = std::move(vboSlice),
FirstVertexIndex = 0; cStride = copy.copyElementSize
} ](DxvkContext* ctx) mutable {
ctx->bindVertexBuffer(cStream, std::move(cBufferSlice), cStride);
});
m_flags.set(D3D9DeviceFlag::DirtyVertexBuffers);
}
// Change the draw call parameters to reflect the changed vertex buffers
if (NumIndices != 0) {
BaseVertexIndex = -FirstVertexIndex;
} else {
FirstVertexIndex = 0;
} }
if (dynamicSysmemIBO) { if (dynamicSysmemIBO) {
@ -5292,7 +5328,7 @@ namespace dxvk {
uint8_t* data = reinterpret_cast<uint8_t*>(upSlice.mapPtr) + iboUPBufferOffset; uint8_t* data = reinterpret_cast<uint8_t*>(upSlice.mapPtr) + iboUPBufferOffset;
uint8_t* src = reinterpret_cast<uint8_t*>(ibo->GetMappedSlice().mapPtr) + offset; uint8_t* src = reinterpret_cast<uint8_t*>(ibo->GetMappedSlice().mapPtr) + offset;
std::memcpy(data, src, iboUPBufferSize); std::memcpy(data, src, iboUPBufferSize);
auto iboSlice = upSlice.slice.subSlice(iboUPBufferOffset, iboUPBufferSize); auto iboSlice = upSlice.slice.subSlice(iboUPBufferOffset, iboUPBufferSize);
EmitCs([ EmitCs([
cBufferSlice = std::move(iboSlice), cBufferSlice = std::move(iboSlice),
@ -7891,7 +7927,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);

View File

@ -336,7 +336,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 +357,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 +369,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 +778,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 +887,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,6 +1106,17 @@ 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;
if (flags == D3DTTFF_COUNT1) {
// D3DTTFF_COUNT1 behaves like D3DTTFF_DISABLE on NV and like D3DTTFF_COUNT2 on AMD.
// The Nvidia behavior is easier to implement.
flags = D3DTTFF_DISABLE;
}
// 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; uint32_t count;
switch (inputFlags) { switch (inputFlags) {
default: default:
@ -1121,27 +1127,30 @@ namespace dxvk {
count = flags; count = flags;
if (texcoordCount) { if (texcoordCount) {
// Clamp by the number of elements in the texcoord input. // Clamp by the number of elements in the texcoord input.
if (!count || count > texcoordCount) if (!count || count > texcoordCount) {
count = texcoordCount; count = texcoordCount;
} }
else } else {
count = 0;
flags = D3DTTFF_DISABLE; flags = D3DTTFF_DISABLE;
applyTransform = false;
}
break; break;
case (DXVK_TSS_TCI_CAMERASPACENORMAL >> TCIOffset): case (DXVK_TSS_TCI_CAMERASPACENORMAL >> TCIOffset):
transformed = outNrm; transformed = outNrm;
count = 4; count = std::min(flags, 4u);
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 = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(1.0f), vtx, 1, &wIndex);
count = 4; count = std::min(flags, 4u);
break; break;
case (DXVK_TSS_TCI_CAMERASPACEREFLECTIONVECTOR >> TCIOffset): { case (DXVK_TSS_TCI_CAMERASPACEREFLECTIONVECTOR >> TCIOffset): {
uint32_t vtx3 = m_module.opVectorShuffle(m_vec3Type, vtx, vtx, 3, indices.data()); uint32_t vtx3 = m_module.opVectorShuffle(m_vec3Type, vtx, vtx, 3, indices.data());
vtx3 = m_module.opNormalize(m_vec3Type, vtx3); vtx3 = m_module.opNormalize(m_vec3Type, vtx3);
uint32_t reflection = m_module.opReflect(m_vec3Type, vtx3, normal); uint32_t reflection = m_module.opReflect(m_vec3Type, vtx3, normal);
std::array<uint32_t, 4> transformIndices; std::array<uint32_t, 4> transformIndices;
@ -1150,7 +1159,7 @@ 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; count = std::min(flags, 4u);
break; break;
} }
@ -1174,37 +1183,43 @@ 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; count = std::min(flags, 4u);
break; break;
} }
} }
uint32_t type = flags; if (applyTransform && !m_vsKey.Data.Contents.HasPositionT) {
if (type != D3DTTFF_DISABLE) { for (uint32_t j = count; j < 4; j++) {
if (!m_vsKey.Data.Contents.HasPositionT) { // If we're outside the component count of the vertex decl for this texcoord then we pad with zeroes.
for (uint32_t j = count; j < 4; j++) { // Otherwise, pad with ones.
// 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. // 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 // In future, maybe we could sort this out properly by chopping matrices of different sizes, but thats
// a project for another day. // a project for another day.
uint32_t texcoordCount = (m_vsKey.Data.Contents.TexcoordDeclMask >> (3 * inputIndex)) & 0x7; uint32_t texcoordCount = (m_vsKey.Data.Contents.TexcoordDeclMask >> (3 * inputIndex)) & 0x7;
uint32_t value = j > texcoordCount ? m_module.constf32(0) : m_module.constf32(1); 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.opCompositeInsert(m_vec4Type, value, transformed, 1, &j);
}
transformed = m_module.opVectorTimesMatrix(m_vec4Type, transformed, m_vs.constants.texcoord[i]);
} }
// Pad the unused section of it with the value for projection. transformed = m_module.opVectorTimesMatrix(m_vec4Type, transformed, m_vs.constants.texcoord[i]);
uint32_t lastIdx = count - 1;
uint32_t projValue = m_module.opCompositeExtract(m_floatType, transformed, 1, &lastIdx);
for (uint32_t j = count; j < 4; j++)
transformed = m_module.opCompositeInsert(m_vec4Type, projValue, transformed, 1, &j);
} }
// The projection idx is always based on the flags, even when the input mode is not DXVK_TSS_TCI_PASSTHRU.
uint32_t projValue;
if (count < 3) {
// Not enough components to do projection.
// Native drivers render normally or garbage with D3DFVF_TEXCOORDSIZE2 or D3DTTFF_COUNT <3
projValue = m_module.constf32(1.0f);
} else {
uint32_t projIdx = count - 1;
projValue = m_module.opCompositeExtract(m_floatType, transformed, 1, &projIdx);
}
// The w component is only used for projection or unused, so always insert the component that's supposed to be divided by there.
// The fragment shader will then decide whether to project or not.
uint32_t wIdx = 3;
transformed = m_module.opCompositeInsert(m_vec4Type, projValue, transformed, 1, &wIdx);
m_module.opStore(m_vs.out.TEXCOORD[i], transformed); m_module.opStore(m_vs.out.TEXCOORD[i], transformed);
} }
@ -1384,20 +1399,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);
} }
@ -1808,22 +1810,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 = 4; // Always use w if ProjectedCount is 0.
}
--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)) {

View File

@ -59,7 +59,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;

View File

@ -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));

View File

@ -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);
@ -53,14 +53,14 @@ namespace dxvk {
this->deferSurfaceCreation = config.getOption<bool> ("d3d9.deferSurfaceCreation", false); this->deferSurfaceCreation = config.getOption<bool> ("d3d9.deferSurfaceCreation", false);
this->samplerAnisotropy = config.getOption<int32_t> ("d3d9.samplerAnisotropy", -1); this->samplerAnisotropy = config.getOption<int32_t> ("d3d9.samplerAnisotropy", -1);
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", vendorId != uint32_t(DxvkGpuVendor::Nvidia));
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->supportD32 = config.getOption<bool> ("d3d9.supportD32", true);
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);
@ -77,6 +77,7 @@ namespace dxvk {
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->countLosableResources = config.getOption<bool> ("d3d9.countLosableResources", true);
this->reproducibleCommandStream = config.getOption<bool> ("d3d9.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);
@ -90,8 +91,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;
} }

View File

@ -155,6 +155,11 @@ namespace dxvk {
/// Disable counting losable resources and rejecting calls to Reset() if any are still alive /// Disable counting losable resources and rejecting calls to Reset() if any are still alive
bool countLosableResources; 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;
}; };
} }

View File

@ -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:

View File

@ -57,17 +57,24 @@ if platform != 'windows'
d3d9_link_depends += files('d3d9.sym') d3d9_link_depends += files('d3d9.sym')
endif endif
d3d9_dll = shared_library('d3d9'+dll_ext, d3d9_src, glsl_generator.process(d3d9_shaders), d3d9_res, d3d9_dll = shared_library(dxvk_name_prefix+'d3d9', d3d9_src, glsl_generator.process(d3d9_shaders), d3d9_res,
name_prefix : dxvk_name_prefix,
dependencies : [ dxso_dep, dxvk_dep ], dependencies : [ dxso_dep, dxvk_dep ],
include_directories : dxvk_include_path, include_directories : dxvk_include_path,
install : true, install : true,
vs_module_defs : 'd3d9'+def_spec_ext, vs_module_defs : 'd3d9'+def_spec_ext,
link_args : d3d9_ld_args, link_args : d3d9_ld_args,
link_depends : [ d3d9_link_depends ], link_depends : [ d3d9_link_depends ],
kwargs : dxvk_so_version,
) )
d3d9_dep = declare_dependency( d3d9_dep = declare_dependency(
link_with : [ d3d9_dll ], link_with : [ d3d9_dll ],
include_directories : [ dxvk_include_path ], include_directories : [ dxvk_include_path ],
) )
if platform != 'windows'
pkg.generate(d3d9_dll,
filebase: dxvk_pkg_prefix + 'd3d9',
subdirs: 'dxvk',
)
endif

View File

@ -87,9 +87,9 @@ namespace dxvk {
} }
for (uint32_t i = 0; i < ins.dstCount; i++) { for (uint32_t i = 0; i < ins.dstCount; i++) {
if (ins.dst[0].type == DxbcOperandType::IndexableTemp) { if (ins.dst[i].type == DxbcOperandType::IndexableTemp) {
uint32_t index = ins.dst[0].idx[0].offset; uint32_t index = ins.dst[i].idx[0].offset;
m_analysis->xRegMasks[index] |= ins.dst[0].mask; m_analysis->xRegMasks[index] |= ins.dst[i].mask;
} }
} }
} }

View File

@ -23,7 +23,11 @@ namespace dxvk {
entry.systemValue = static_cast<DxbcSystemValue>(reader.readu32()); entry.systemValue = static_cast<DxbcSystemValue>(reader.readu32());
entry.componentType = componentTypes.at(reader.readu32()); entry.componentType = componentTypes.at(reader.readu32());
entry.registerId = reader.readu32(); entry.registerId = reader.readu32();
entry.componentMask = bit::extract(reader.readu32(), 0, 3);
uint32_t mask = reader.readu32();
entry.componentMask = bit::extract(mask, 0, 3);
entry.componentUsed = bit::extract(mask, 8, 11);
if (hasPrecision) if (hasPrecision)
reader.readu32(); reader.readu32();

View File

@ -20,6 +20,7 @@ namespace dxvk {
uint32_t semanticIndex; uint32_t semanticIndex;
uint32_t registerId; uint32_t registerId;
DxbcRegMask componentMask; DxbcRegMask componentMask;
DxbcRegMask componentUsed;
DxbcScalarType componentType; DxbcScalarType componentType;
DxbcSystemValue systemValue; DxbcSystemValue systemValue;
uint32_t streamId; uint32_t streamId;

View File

@ -257,14 +257,13 @@ namespace dxvk {
info.outputMask = m_outputMask; info.outputMask = m_outputMask;
info.uniformSize = m_immConstData.size(); info.uniformSize = m_immConstData.size();
info.uniformData = m_immConstData.data(); info.uniformData = m_immConstData.data();
info.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT;
info.pushConstSize = sizeof(DxbcPushConstants);
info.outputTopology = m_outputTopology; info.outputTopology = m_outputTopology;
if (m_programInfo.type() == DxbcProgramType::HullShader) if (m_programInfo.type() == DxbcProgramType::HullShader)
info.patchVertexCount = m_hs.vertexCountIn; info.patchVertexCount = m_hs.vertexCountIn;
if (m_programInfo.type() == DxbcProgramType::PixelShader && m_ps.pushConstantId)
info.pushConstSize = sizeof(DxbcPushConstants);
if (m_moduleInfo.xfb) { if (m_moduleInfo.xfb) {
info.xfbRasterizedStream = m_moduleInfo.xfb->rasterizedStream; info.xfbRasterizedStream = m_moduleInfo.xfb->rasterizedStream;
@ -1624,8 +1623,13 @@ namespace dxvk {
case DxbcOpcode::Mad: case DxbcOpcode::Mad:
case DxbcOpcode::DFma: case DxbcOpcode::DFma:
dst.id = m_module.opFFma(typeId, if (likely(!m_moduleInfo.options.longMad)) {
src.at(0).id, src.at(1).id, src.at(2).id); dst.id = m_module.opFFma(typeId,
src.at(0).id, src.at(1).id, src.at(2).id);
} else {
dst.id = m_module.opFMul(typeId, src.at(0).id, src.at(1).id);
dst.id = m_module.opFAdd(typeId, dst.id, src.at(2).id);
}
break; break;
case DxbcOpcode::Max: case DxbcOpcode::Max:
@ -2464,58 +2468,6 @@ namespace dxvk {
if (m_uavs.at(registerId).ctrId == 0) if (m_uavs.at(registerId).ctrId == 0)
m_uavs.at(registerId).ctrId = emitDclUavCounter(registerId); m_uavs.at(registerId).ctrId = emitDclUavCounter(registerId);
// Only use subgroup ops on compute to avoid having to
// deal with helper invocations or hardware limitations
bool useSubgroupOps = m_moduleInfo.options.useSubgroupOpsForAtomicCounters
&& m_programInfo.type() == DxbcProgramType::ComputeShader;
// Current block ID used in a phi later on
uint32_t baseBlockId = m_module.getBlockId();
// In case we have subgroup ops enabled, we need to
// count the number of active lanes, the lane index,
// and we need to perform the atomic op conditionally
uint32_t laneCount = 0;
uint32_t laneIndex = 0;
DxbcConditional elect;
if (useSubgroupOps) {
m_module.enableCapability(spv::CapabilityGroupNonUniform);
m_module.enableCapability(spv::CapabilityGroupNonUniformBallot);
uint32_t ballot = m_module.opGroupNonUniformBallot(
getVectorTypeId({ DxbcScalarType::Uint32, 4 }),
m_module.constu32(spv::ScopeSubgroup),
m_module.constBool(true));
laneCount = m_module.opGroupNonUniformBallotBitCount(
getScalarTypeId(DxbcScalarType::Uint32),
m_module.constu32(spv::ScopeSubgroup),
spv::GroupOperationReduce, ballot);
laneIndex = m_module.opGroupNonUniformBallotBitCount(
getScalarTypeId(DxbcScalarType::Uint32),
m_module.constu32(spv::ScopeSubgroup),
spv::GroupOperationExclusiveScan, ballot);
// Elect one lane to perform the atomic op
uint32_t election = m_module.opGroupNonUniformElect(
m_module.defBoolType(),
m_module.constu32(spv::ScopeSubgroup));
elect.labelIf = m_module.allocateId();
elect.labelEnd = m_module.allocateId();
m_module.opSelectionMerge(elect.labelEnd, spv::SelectionControlMaskNone);
m_module.opBranchConditional(election, elect.labelIf, elect.labelEnd);
m_module.opLabel(elect.labelIf);
} else {
// We're going to use this for the increment
laneCount = m_module.constu32(1);
}
// Get a pointer to the atomic counter in question // Get a pointer to the atomic counter in question
DxbcRegisterInfo ptrType; DxbcRegisterInfo ptrType;
ptrType.type.ctype = DxbcScalarType::Uint32; ptrType.type.ctype = DxbcScalarType::Uint32;
@ -2547,13 +2499,14 @@ namespace dxvk {
switch (ins.op) { switch (ins.op) {
case DxbcOpcode::ImmAtomicAlloc: case DxbcOpcode::ImmAtomicAlloc:
value.id = m_module.opAtomicIAdd(typeId, ptrId, value.id = m_module.opAtomicIAdd(typeId, ptrId,
scopeId, semanticsId, laneCount); scopeId, semanticsId, m_module.constu32(1));
break; break;
case DxbcOpcode::ImmAtomicConsume: case DxbcOpcode::ImmAtomicConsume:
value.id = m_module.opAtomicISub(typeId, ptrId, value.id = m_module.opAtomicISub(typeId, ptrId,
scopeId, semanticsId, laneCount); scopeId, semanticsId, m_module.constu32(1));
value.id = m_module.opISub(typeId, value.id, laneCount); value.id = m_module.opISub(typeId, value.id,
m_module.constu32(1));
break; break;
default: default:
@ -2563,26 +2516,6 @@ namespace dxvk {
return; return;
} }
// If we're using subgroup ops, we have to broadcast
// the result of the atomic op and compute the index
if (useSubgroupOps) {
m_module.opBranch(elect.labelEnd);
m_module.opLabel (elect.labelEnd);
uint32_t undef = m_module.constUndef(typeId);
std::array<SpirvPhiLabel, 2> phiLabels = {{
{ value.id, elect.labelIf },
{ undef, baseBlockId },
}};
value.id = m_module.opPhi(typeId,
phiLabels.size(), phiLabels.data());
value.id = m_module.opGroupNonUniformBroadcastFirst(typeId,
m_module.constu32(spv::ScopeSubgroup), value.id);
value.id = m_module.opIAdd(typeId, value.id, laneIndex);
}
// Store the result // Store the result
emitRegisterStore(ins.dst[0], value); emitRegisterStore(ins.dst[0], value);
} }
@ -5584,12 +5517,12 @@ namespace dxvk {
result.type.ctype = DxbcScalarType::Uint32; result.type.ctype = DxbcScalarType::Uint32;
result.type.ccount = 1; result.type.ccount = 1;
if (info.image.sampled == 1) { if (info.image.ms == 0 && info.image.sampled == 1) {
result.id = m_module.opImageQueryLevels( result.id = m_module.opImageQueryLevels(
getVectorTypeId(result.type), getVectorTypeId(result.type),
m_module.opLoad(info.typeId, info.varId)); m_module.opLoad(info.typeId, info.varId));
} else { } else {
// Report one LOD in case of UAVs // Report one LOD in case of UAVs or multisampled images
result.id = m_module.constu32(1); result.id = m_module.constu32(1);
} }
@ -7799,6 +7732,21 @@ namespace dxvk {
return DxbcRegMask::firstN(getTexCoordDim(imageType)); return DxbcRegMask::firstN(getTexCoordDim(imageType));
} }
bool DxbcCompiler::ignoreInputSystemValue(DxbcSystemValue sv) const {
switch (sv) {
case DxbcSystemValue::Position:
case DxbcSystemValue::IsFrontFace:
case DxbcSystemValue::SampleIndex:
case DxbcSystemValue::PrimitiveId:
case DxbcSystemValue::Coverage:
return m_programInfo.type() == DxbcProgramType::PixelShader;
default:
return false;
}
}
DxbcVectorType DxbcCompiler::getInputRegType(uint32_t regIdx) const { DxbcVectorType DxbcCompiler::getInputRegType(uint32_t regIdx) const {
switch (m_programInfo.type()) { switch (m_programInfo.type()) {
@ -7829,8 +7777,25 @@ namespace dxvk {
result.ctype = DxbcScalarType::Float32; result.ctype = DxbcScalarType::Float32;
result.ccount = 4; result.ccount = 4;
if (m_isgn->findByRegister(regIdx)) if (m_isgn == nullptr || !m_isgn->findByRegister(regIdx))
result.ccount = m_isgn->regMask(regIdx).minComponents(); return result;
DxbcRegMask mask(0u);
DxbcRegMask used(0u);
for (const auto& e : *m_isgn) {
if (e.registerId == regIdx && !ignoreInputSystemValue(e.systemValue)) {
mask |= e.componentMask;
used |= e.componentUsed;
}
}
if (m_programInfo.type() == DxbcProgramType::PixelShader) {
if ((used.raw() & mask.raw()) == used.raw())
mask = used;
}
result.ccount = mask.minComponents();
return result; return result;
} }
} }

View File

@ -1221,6 +1221,9 @@ namespace dxvk {
uint32_t getUavCoherence( uint32_t getUavCoherence(
uint32_t registerId, uint32_t registerId,
DxbcUavFlags flags); DxbcUavFlags flags);
bool ignoreInputSystemValue(
DxbcSystemValue sv) const;
/////////////////////////// ///////////////////////////
// Type definition methods // Type definition methods

View File

@ -17,9 +17,6 @@ namespace dxvk {
useDepthClipWorkaround useDepthClipWorkaround
= !devFeatures.extDepthClipEnable.depthClipEnable; = !devFeatures.extDepthClipEnable.depthClipEnable;
useSubgroupOpsForAtomicCounters
= (devInfo.vk11.subgroupSupportedStages & VK_SHADER_STAGE_COMPUTE_BIT)
&& (devInfo.vk11.subgroupSupportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT);
VkFormatFeatureFlags2 r32Features VkFormatFeatureFlags2 r32Features
= device->getFormatFeatures(VK_FORMAT_R32_SFLOAT).optimal = device->getFormatFeatures(VK_FORMAT_R32_SFLOAT).optimal
@ -41,6 +38,7 @@ namespace dxvk {
disableMsaa = options.disableMsaa; disableMsaa = options.disableMsaa;
forceSampleRateShading = options.forceSampleRateShading; forceSampleRateShading = options.forceSampleRateShading;
enableSampleShadingInterlock = device->features().extFragmentShaderInterlock.fragmentShaderSampleInterlock; enableSampleShadingInterlock = device->features().extFragmentShaderInterlock.fragmentShaderSampleInterlock;
longMad = options.longMad;
// Figure out float control flags to match D3D11 rules // Figure out float control flags to match D3D11 rules
if (options.floatControls) { if (options.floatControls) {

View File

@ -30,10 +30,6 @@ namespace dxvk {
/// Determines whether raw access chains are supported /// Determines whether raw access chains are supported
bool supportsRawAccessChains = false; bool supportsRawAccessChains = false;
/// Use subgroup operations to reduce the number of
/// atomic operations for append/consume buffers.
bool useSubgroupOpsForAtomicCounters = false;
/// Clear thread-group shared memory to zero /// Clear thread-group shared memory to zero
bool zeroInitWorkgroupMemory = false; bool zeroInitWorkgroupMemory = false;
@ -58,6 +54,9 @@ namespace dxvk {
/// Minimum storage buffer alignment /// Minimum storage buffer alignment
VkDeviceSize minSsboAlignment = 0; VkDeviceSize minSsboAlignment = 0;
/// Should we make our Mads a FFma or do it the long way with an FMul and an FAdd?
bool longMad;
}; };
} }

View File

@ -130,7 +130,7 @@ namespace dxvk {
// We can't really reconstruct the version numbers // We can't really reconstruct the version numbers
// returned by Windows drivers from Vulkan data // returned by Windows drivers from Vulkan data
if (SUCCEEDED(hr) && pUMDVersion) if (SUCCEEDED(hr) && pUMDVersion)
pUMDVersion->QuadPart = ~0ull; pUMDVersion->QuadPart = INT64_MAX;
if (FAILED(hr)) { if (FAILED(hr)) {
Logger::err("DXGI: CheckInterfaceSupport: Unsupported interface"); Logger::err("DXGI: CheckInterfaceSupport: Unsupported interface");

View File

@ -124,13 +124,10 @@ namespace dxvk {
} }
} }
// If any monitors are left on the list, enable the // If any monitors are left on the list, enable the
// fallback to always enumerate all monitors. // fallback to always enumerate all monitors.
if ((m_monitorFallback = !monitors.empty())) if ((m_monitorFallback = !monitors.empty()))
Logger::warn("DXGI: Found monitors not associated with any adapter, using fallback"); Logger::warn("DXGI: Found monitors not associated with any adapter, using fallback");
else
m_monitorFallback = m_options.useMonitorFallback;
} }
@ -233,7 +230,7 @@ namespace dxvk {
descFs.Windowed = pDesc->Windowed; descFs.Windowed = pDesc->Windowed;
IDXGISwapChain1* swapChain = nullptr; IDXGISwapChain1* swapChain = nullptr;
HRESULT hr = CreateSwapChainForHwnd( HRESULT hr = CreateSwapChainForHwndBase(
pDevice, pDesc->OutputWindow, pDevice, pDesc->OutputWindow,
&desc, &descFs, nullptr, &desc, &descFs, nullptr,
&swapChain); &swapChain);
@ -247,6 +244,19 @@ namespace dxvk {
IUnknown* pDevice, IUnknown* pDevice,
HWND hWnd, HWND hWnd,
const DXGI_SWAP_CHAIN_DESC1* pDesc, const DXGI_SWAP_CHAIN_DESC1* pDesc,
const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc,
IDXGIOutput* pRestrictToOutput,
IDXGISwapChain1** ppSwapChain) {
return CreateSwapChainForHwndBase(
pDevice, hWnd,
pDesc, pFullscreenDesc, pRestrictToOutput,
ppSwapChain);
}
HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainForHwndBase(
IUnknown* pDevice,
HWND hWnd,
const DXGI_SWAP_CHAIN_DESC1* pDesc,
const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc, const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc,
IDXGIOutput* pRestrictToOutput, IDXGIOutput* pRestrictToOutput,
IDXGISwapChain1** ppSwapChain) { IDXGISwapChain1** ppSwapChain) {

View File

@ -200,6 +200,14 @@ namespace dxvk {
UINT m_flags; UINT m_flags;
BOOL m_monitorFallback; BOOL m_monitorFallback;
HRESULT STDMETHODCALLTYPE CreateSwapChainForHwndBase(
IUnknown* pDevice,
HWND hWnd,
const DXGI_SWAP_CHAIN_DESC1* pDesc,
const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc,
IDXGIOutput* pRestrictToOutput,
IDXGISwapChain1** ppSwapChain);
}; };
} }

View File

@ -26,6 +26,21 @@ namespace dxvk {
return id; return id;
} }
/* First generation XeSS causes crash on proton for Intel due to missing
* Intel interface. Avoid crash by pretending to be non-Intel if the
* libxess.dll module is loaded by an application.
*/
static bool isXessUsed() {
#ifdef _WIN32
if (GetModuleHandleA("libxess") != nullptr ||
GetModuleHandleA("libxess_dx11") != nullptr)
return true;
else
return false;
#else
return false;
#endif
}
static bool isNvapiEnabled() { static bool isNvapiEnabled() {
return env::getEnvVar("DXVK_ENABLE_NVAPI") == "1"; return env::getEnvVar("DXVK_ENABLE_NVAPI") == "1";
@ -95,15 +110,17 @@ namespace dxvk {
this->hideAmdGpu = config.getOption<Tristate>("dxgi.hideAmdGpu", Tristate::Auto) == Tristate::True; this->hideAmdGpu = config.getOption<Tristate>("dxgi.hideAmdGpu", Tristate::Auto) == Tristate::True;
this->hideIntelGpu = config.getOption<Tristate>("dxgi.hideIntelGpu", Tristate::Auto) == Tristate::True; this->hideIntelGpu = config.getOption<Tristate>("dxgi.hideIntelGpu", Tristate::Auto) == Tristate::True;
/* Force vendor ID to non-Intel ID when XeSS is in use */
if (isXessUsed()) {
Logger::info(str::format("Detected XeSS usage, hiding Intel GPU Vendor"));
this->hideIntelGpu = true;
}
this->enableHDR = config.getOption<bool>("dxgi.enableHDR", env::getEnvVar("DXVK_HDR") == "1"); this->enableHDR = config.getOption<bool>("dxgi.enableHDR", env::getEnvVar("DXVK_HDR") == "1");
if (this->enableHDR && isHDRDisallowed()) { if (this->enableHDR && isHDRDisallowed()) {
Logger::info("HDR was configured to be enabled, but has been force disabled as a UE4 DX11 game was detected."); Logger::info("HDR was configured to be enabled, but has been force disabled as a UE4 DX11 game was detected.");
this->enableHDR = false; this->enableHDR = false;
} }
this->useMonitorFallback = config.getOption<bool>("dxgi.useMonitorFallback", env::getEnvVar("DXVK_MONITOR_FALLBACK") == "1");
if (this->useMonitorFallback)
Logger::info("Enabled useMonitorFallback option");
} }
} }

View File

@ -49,9 +49,6 @@ namespace dxvk {
/// Enable HDR /// Enable HDR
bool enableHDR; bool enableHDR;
/// Use monitor fallback to enumerating all monitors per output
bool useMonitorFallback;
/// Sync interval. Overrides the value /// Sync interval. Overrides the value
/// passed to IDXGISwapChain::Present. /// passed to IDXGISwapChain::Present.
int32_t syncInterval; int32_t syncInterval;

View File

@ -303,15 +303,22 @@ namespace dxvk {
HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present(UINT SyncInterval, UINT Flags) { HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present(UINT SyncInterval, UINT Flags) {
return Present1(SyncInterval, Flags, nullptr); return PresentBase(SyncInterval, Flags, nullptr);
} }
HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present1( HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present1(
UINT SyncInterval, UINT SyncInterval,
UINT PresentFlags, UINT PresentFlags,
const DXGI_PRESENT_PARAMETERS* pPresentParameters) { const DXGI_PRESENT_PARAMETERS* pPresentParameters) {
return PresentBase(SyncInterval, PresentFlags, pPresentParameters);
}
HRESULT STDMETHODCALLTYPE DxgiSwapChain::PresentBase(
UINT SyncInterval,
UINT PresentFlags,
const DXGI_PRESENT_PARAMETERS* pPresentParameters) {
if (SyncInterval > 4) if (SyncInterval > 4)
return DXGI_ERROR_INVALID_CALL; return DXGI_ERROR_INVALID_CALL;

View File

@ -233,6 +233,10 @@ namespace dxvk {
DXGI_FORMAT Format, DXGI_FORMAT Format,
DXGI_COLOR_SPACE_TYPE ColorSpace); DXGI_COLOR_SPACE_TYPE ColorSpace);
HRESULT STDMETHODCALLTYPE PresentBase(
UINT SyncInterval,
UINT PresentFlags,
const DXGI_PRESENT_PARAMETERS* pPresentParameters);
}; };
} }

View File

@ -21,17 +21,24 @@ if platform != 'windows'
dxgi_link_depends += files('dxgi.sym') dxgi_link_depends += files('dxgi.sym')
endif endif
dxgi_dll = shared_library('dxgi'+dll_ext, dxgi_src, dxgi_res, dxgi_dll = shared_library(dxvk_name_prefix+'dxgi', dxgi_src, dxgi_res,
name_prefix : dxvk_name_prefix,
dependencies : [ dxvk_dep ], dependencies : [ dxvk_dep ],
include_directories : dxvk_include_path, include_directories : dxvk_include_path,
install : true, install : true,
vs_module_defs : 'dxgi'+def_spec_ext, vs_module_defs : 'dxgi'+def_spec_ext,
link_args : dxgi_ld_args, link_args : dxgi_ld_args,
link_depends : [ dxgi_link_depends ], link_depends : [ dxgi_link_depends ],
kwargs : dxvk_so_version,
) )
dxgi_dep = declare_dependency( dxgi_dep = declare_dependency(
link_with : [ dxgi_dll ], link_with : [ dxgi_dll ],
include_directories : [ dxvk_include_path ], include_directories : [ dxvk_include_path ],
) )
if platform != 'windows'
pkg.generate(dxgi_dll,
filebase: dxvk_pkg_prefix + 'dxgi',
subdirs: 'dxvk',
)
endif

View File

@ -228,8 +228,8 @@ namespace dxvk {
info.bindings = m_bindings.data(); info.bindings = m_bindings.data();
info.inputMask = m_inputMask; info.inputMask = m_inputMask;
info.outputMask = m_outputMask; info.outputMask = m_outputMask;
info.pushConstOffset = m_pushConstOffset; info.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
info.pushConstSize = m_pushConstSize; info.pushConstSize = sizeof(D3D9RenderStateInfo);
if (m_programInfo.type() == DxsoProgramTypes::PixelShader) if (m_programInfo.type() == DxsoProgramTypes::PixelShader)
info.flatShadingInputs = m_ps.flatShadingMask; info.flatShadingInputs = m_ps.flatShadingMask;
@ -3561,30 +3561,7 @@ void DxsoCompiler::emitControlFlowGenericLoop(
void DxsoCompiler::setupRenderStateInfo() { void DxsoCompiler::setupRenderStateInfo() {
uint32_t count; m_rsBlock = SetupRenderStateBlock(m_module);
// Only need alpha ref for PS 3.
// No FF fog component.
if (m_programInfo.type() == DxsoProgramType::PixelShader) {
if (m_programInfo.majorVersion() == 3) {
m_pushConstOffset = offsetof(D3D9RenderStateInfo, alphaRef);
m_pushConstSize = sizeof(float);
}
else {
m_pushConstOffset = 0;
m_pushConstSize = offsetof(D3D9RenderStateInfo, pointSize);
}
count = 5;
}
else {
m_pushConstOffset = offsetof(D3D9RenderStateInfo, pointSize);
// Point scale never triggers on programmable
m_pushConstSize = sizeof(float) * 3;
count = 8;
}
m_rsBlock = SetupRenderStateBlock(m_module, count);
} }

View File

@ -344,8 +344,6 @@ namespace dxvk {
// covers vertex input and fragment output. // covers vertex input and fragment output.
uint32_t m_inputMask = 0u; uint32_t m_inputMask = 0u;
uint32_t m_outputMask = 0u; uint32_t m_outputMask = 0u;
uint32_t m_pushConstOffset = 0u;
uint32_t m_pushConstSize = 0u;
/////////////////////////////////// ///////////////////////////////////
// Shader-specific data structures // Shader-specific data structures

View File

@ -346,6 +346,11 @@ namespace dxvk {
enabledFeatures.vk13.synchronization2 = VK_TRUE; enabledFeatures.vk13.synchronization2 = VK_TRUE;
enabledFeatures.vk13.dynamicRendering = VK_TRUE; enabledFeatures.vk13.dynamicRendering = VK_TRUE;
// Maintenance4 may cause performance problems on amdvlk in some cases
if (m_deviceInfo.vk12.driverID != VK_DRIVER_ID_AMD_OPEN_SOURCE
&& m_deviceInfo.vk12.driverID != VK_DRIVER_ID_AMD_PROPRIETARY)
enabledFeatures.vk13.maintenance4 = VK_TRUE;
// We expose depth clip rather than depth clamp to client APIs // We expose depth clip rather than depth clamp to client APIs
enabledFeatures.extDepthClipEnable.depthClipEnable = enabledFeatures.extDepthClipEnable.depthClipEnable =
m_deviceFeatures.extDepthClipEnable.depthClipEnable; m_deviceFeatures.extDepthClipEnable.depthClipEnable;
@ -410,11 +415,15 @@ namespace dxvk {
m_deviceFeatures.khrPresentWait.presentWait; m_deviceFeatures.khrPresentWait.presentWait;
// Unless we're on an Nvidia driver where these extensions are known to be broken // Unless we're on an Nvidia driver where these extensions are known to be broken
if (matchesDriver(VK_DRIVER_ID_NVIDIA_PROPRIETARY, 0, VK_MAKE_VERSION(535, 0, 0))) { if (matchesDriver(VK_DRIVER_ID_NVIDIA_PROPRIETARY, Version(), Version(535, 0, 0))) {
enabledFeatures.khrPresentId.presentId = VK_FALSE; enabledFeatures.khrPresentId.presentId = VK_FALSE;
enabledFeatures.khrPresentWait.presentWait = VK_FALSE; enabledFeatures.khrPresentWait.presentWait = VK_FALSE;
} }
// Enable descriptor pool overallocation if supported
enabledFeatures.nvDescriptorPoolOverallocation.descriptorPoolOverallocation =
m_deviceFeatures.nvDescriptorPoolOverallocation.descriptorPoolOverallocation;
// Enable raw access chains for shader backends // Enable raw access chains for shader backends
enabledFeatures.nvRawAccessChains.shaderRawAccessChains = enabledFeatures.nvRawAccessChains.shaderRawAccessChains =
m_deviceFeatures.nvRawAccessChains.shaderRawAccessChains; m_deviceFeatures.nvRawAccessChains.shaderRawAccessChains;
@ -425,10 +434,7 @@ namespace dxvk {
// Log feature support info an extension list // Log feature support info an extension list
Logger::info(str::format("Device properties:" Logger::info(str::format("Device properties:"
"\n Device : ", m_deviceInfo.core.properties.deviceName, "\n Device : ", m_deviceInfo.core.properties.deviceName,
"\n Driver : ", m_deviceInfo.vk12.driverName, " ", "\n Driver : ", m_deviceInfo.vk12.driverName, " ", m_deviceInfo.driverVersion.toString()));
VK_VERSION_MAJOR(m_deviceInfo.core.properties.driverVersion), ".",
VK_VERSION_MINOR(m_deviceInfo.core.properties.driverVersion), ".",
VK_VERSION_PATCH(m_deviceInfo.core.properties.driverVersion)));
Logger::info("Enabled device extensions:"); Logger::info("Enabled device extensions:");
this->logNameList(extensionNameList); this->logNameList(extensionNameList);
@ -610,6 +616,10 @@ namespace dxvk {
enabledFeatures.khrPresentWait = *reinterpret_cast<const VkPhysicalDevicePresentWaitFeaturesKHR*>(f); enabledFeatures.khrPresentWait = *reinterpret_cast<const VkPhysicalDevicePresentWaitFeaturesKHR*>(f);
break; break;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_POOL_OVERALLOCATION_FEATURES_NV:
enabledFeatures.nvDescriptorPoolOverallocation = *reinterpret_cast<const VkPhysicalDeviceDescriptorPoolOverallocationFeaturesNV*>(f);
break;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAW_ACCESS_CHAINS_FEATURES_NV: case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAW_ACCESS_CHAINS_FEATURES_NV:
enabledFeatures.nvRawAccessChains = *reinterpret_cast<const VkPhysicalDeviceRawAccessChainsFeaturesNV*>(f); enabledFeatures.nvRawAccessChains = *reinterpret_cast<const VkPhysicalDeviceRawAccessChainsFeaturesNV*>(f);
break; break;
@ -626,9 +636,7 @@ namespace dxvk {
Logger::info(str::format("Device properties:" Logger::info(str::format("Device properties:"
"\n Device name: ", m_deviceInfo.core.properties.deviceName, "\n Device name: ", m_deviceInfo.core.properties.deviceName,
"\n Driver: ", m_deviceInfo.vk12.driverName, " ", "\n Driver: ", m_deviceInfo.vk12.driverName, " ",
VK_VERSION_MAJOR(m_deviceInfo.core.properties.driverVersion), ".", m_deviceInfo.driverVersion.toString()));
VK_VERSION_MINOR(m_deviceInfo.core.properties.driverVersion), ".",
VK_VERSION_PATCH(m_deviceInfo.core.properties.driverVersion)));
Logger::info("Enabled device extensions:"); Logger::info("Enabled device extensions:");
this->logNameList(extensionNameList); this->logNameList(extensionNameList);
@ -664,26 +672,29 @@ namespace dxvk {
bool DxvkAdapter::matchesDriver( bool DxvkAdapter::matchesDriver(
VkDriverIdKHR driver, VkDriverIdKHR driver,
uint32_t minVer, Version minVer,
uint32_t maxVer) const { Version maxVer) const {
bool driverMatches = driver == m_deviceInfo.vk12.driverID; bool driverMatches = driver == m_deviceInfo.vk12.driverID;
if (minVer) driverMatches &= m_deviceInfo.core.properties.driverVersion >= minVer; if (minVer) driverMatches &= m_deviceInfo.driverVersion >= minVer;
if (maxVer) driverMatches &= m_deviceInfo.core.properties.driverVersion < maxVer; if (maxVer) driverMatches &= m_deviceInfo.driverVersion < maxVer;
return driverMatches; return driverMatches;
} }
bool DxvkAdapter::matchesDriver(
VkDriverIdKHR driver) const {
return driver == m_deviceInfo.vk12.driverID;
}
void DxvkAdapter::logAdapterInfo() const { void DxvkAdapter::logAdapterInfo() const {
const auto deviceInfo = this->devicePropertiesExt(); const auto deviceInfo = this->devicePropertiesExt();
const auto memoryInfo = this->memoryProperties(); const auto memoryInfo = this->memoryProperties();
Logger::info(str::format(deviceInfo.core.properties.deviceName, ":", Logger::info(str::format(deviceInfo.core.properties.deviceName, ":",
"\n Driver : ", deviceInfo.vk12.driverName, " ", "\n Driver : ", deviceInfo.vk12.driverName, " ", deviceInfo.driverVersion.toString()));
VK_VERSION_MAJOR(deviceInfo.core.properties.driverVersion), ".",
VK_VERSION_MINOR(deviceInfo.core.properties.driverVersion), ".",
VK_VERSION_PATCH(deviceInfo.core.properties.driverVersion)));
for (uint32_t i = 0; i < memoryInfo.memoryHeapCount; i++) { for (uint32_t i = 0; i < memoryInfo.memoryHeapCount; i++) {
constexpr VkDeviceSize mib = 1024 * 1024; constexpr VkDeviceSize mib = 1024 * 1024;
@ -785,22 +796,8 @@ namespace dxvk {
m_vki->vkGetPhysicalDeviceProperties2(m_handle, &m_deviceInfo.core); m_vki->vkGetPhysicalDeviceProperties2(m_handle, &m_deviceInfo.core);
// Some drivers reports the driver version in a slightly different format // Some drivers reports the driver version in a slightly different format
switch (m_deviceInfo.vk12.driverID) { m_deviceInfo.driverVersion = decodeDriverVersion(
case VK_DRIVER_ID_NVIDIA_PROPRIETARY: m_deviceInfo.vk12.driverID, m_deviceInfo.core.properties.driverVersion);
m_deviceInfo.core.properties.driverVersion = VK_MAKE_VERSION(
(m_deviceInfo.core.properties.driverVersion >> 22) & 0x3ff,
(m_deviceInfo.core.properties.driverVersion >> 14) & 0x0ff,
(m_deviceInfo.core.properties.driverVersion >> 6) & 0x0ff);
break;
case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
m_deviceInfo.core.properties.driverVersion = VK_MAKE_VERSION(
m_deviceInfo.core.properties.driverVersion >> 14,
m_deviceInfo.core.properties.driverVersion & 0x3fff, 0);
break;
default:;
}
} }
@ -935,6 +932,11 @@ namespace dxvk {
m_deviceFeatures.khrPresentWait.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.khrPresentWait); m_deviceFeatures.khrPresentWait.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.khrPresentWait);
} }
if (m_deviceExtensions.supports(VK_NV_DESCRIPTOR_POOL_OVERALLOCATION_EXTENSION_NAME)) {
m_deviceFeatures.nvDescriptorPoolOverallocation.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_POOL_OVERALLOCATION_FEATURES_NV;
m_deviceFeatures.nvDescriptorPoolOverallocation.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.nvDescriptorPoolOverallocation);
}
if (m_deviceExtensions.supports(VK_NV_RAW_ACCESS_CHAINS_EXTENSION_NAME)) { if (m_deviceExtensions.supports(VK_NV_RAW_ACCESS_CHAINS_EXTENSION_NAME)) {
m_deviceFeatures.nvRawAccessChains.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAW_ACCESS_CHAINS_FEATURES_NV; m_deviceFeatures.nvRawAccessChains.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAW_ACCESS_CHAINS_FEATURES_NV;
m_deviceFeatures.nvRawAccessChains.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.nvRawAccessChains); m_deviceFeatures.nvRawAccessChains.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.nvRawAccessChains);
@ -1007,6 +1009,7 @@ namespace dxvk {
&devExtensions.khrPresentWait, &devExtensions.khrPresentWait,
&devExtensions.khrSwapchain, &devExtensions.khrSwapchain,
&devExtensions.khrWin32KeyedMutex, &devExtensions.khrWin32KeyedMutex,
&devExtensions.nvDescriptorPoolOverallocation,
&devExtensions.nvRawAccessChains, &devExtensions.nvRawAccessChains,
&devExtensions.nvxBinaryImport, &devExtensions.nvxBinaryImport,
&devExtensions.nvxImageViewHandle, &devExtensions.nvxImageViewHandle,
@ -1147,6 +1150,11 @@ namespace dxvk {
enabledFeatures.khrPresentWait.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.khrPresentWait); enabledFeatures.khrPresentWait.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.khrPresentWait);
} }
if (devExtensions.nvDescriptorPoolOverallocation) {
enabledFeatures.nvDescriptorPoolOverallocation.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_POOL_OVERALLOCATION_FEATURES_NV;
enabledFeatures.nvDescriptorPoolOverallocation.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.nvDescriptorPoolOverallocation);
}
if (devExtensions.nvRawAccessChains) { if (devExtensions.nvRawAccessChains) {
enabledFeatures.nvRawAccessChains.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAW_ACCESS_CHAINS_FEATURES_NV; enabledFeatures.nvRawAccessChains.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAW_ACCESS_CHAINS_FEATURES_NV;
enabledFeatures.nvRawAccessChains.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.nvRawAccessChains); enabledFeatures.nvRawAccessChains.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.nvRawAccessChains);
@ -1298,6 +1306,8 @@ namespace dxvk {
"\n presentId : ", features.khrPresentId.presentId ? "1" : "0", "\n presentId : ", features.khrPresentId.presentId ? "1" : "0",
"\n", VK_KHR_PRESENT_WAIT_EXTENSION_NAME, "\n", VK_KHR_PRESENT_WAIT_EXTENSION_NAME,
"\n presentWait : ", features.khrPresentWait.presentWait ? "1" : "0", "\n presentWait : ", features.khrPresentWait.presentWait ? "1" : "0",
"\n", VK_NV_DESCRIPTOR_POOL_OVERALLOCATION_EXTENSION_NAME,
"\n descriptorPoolOverallocation : ", features.nvDescriptorPoolOverallocation.descriptorPoolOverallocation ? "1" : "0",
"\n", VK_NV_RAW_ACCESS_CHAINS_EXTENSION_NAME, "\n", VK_NV_RAW_ACCESS_CHAINS_EXTENSION_NAME,
"\n shaderRawAccessChains : ", features.nvRawAccessChains.shaderRawAccessChains ? "1" : "0", "\n shaderRawAccessChains : ", features.nvRawAccessChains.shaderRawAccessChains ? "1" : "0",
"\n", VK_NVX_BINARY_IMPORT_EXTENSION_NAME, "\n", VK_NVX_BINARY_IMPORT_EXTENSION_NAME,
@ -1316,4 +1326,25 @@ namespace dxvk {
"\n Sparse : ", queues.sparse != VK_QUEUE_FAMILY_IGNORED ? str::format(queues.sparse) : "n/a")); "\n Sparse : ", queues.sparse != VK_QUEUE_FAMILY_IGNORED ? str::format(queues.sparse) : "n/a"));
} }
Version DxvkAdapter::decodeDriverVersion(VkDriverId driverId, uint32_t version) {
switch (driverId) {
case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
return Version(
(version >> 22) & 0x3ff,
(version >> 14) & 0x0ff,
(version >> 6) & 0x0ff);
break;
case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
return Version(version >> 14, version & 0x3fff, 0);
default:
return Version(
VK_API_VERSION_MAJOR(version),
VK_API_VERSION_MINOR(version),
VK_API_VERSION_PATCH(version));
}
}
} }

View File

@ -255,8 +255,17 @@ namespace dxvk {
*/ */
bool matchesDriver( bool matchesDriver(
VkDriverIdKHR driver, VkDriverIdKHR driver,
uint32_t minVer, Version minVer,
uint32_t maxVer) const; Version maxVer) const;
/**
* \brief Tests if the driver matches certain criteria
*
* \param [in] driver Driver ID
* \returns \c True if the driver matches these criteria
*/
bool matchesDriver(
VkDriverIdKHR driver) const;
/** /**
* \brief Logs DXVK adapter info * \brief Logs DXVK adapter info
@ -343,6 +352,8 @@ namespace dxvk {
static void logFeatures(const DxvkDeviceFeatures& features); static void logFeatures(const DxvkDeviceFeatures& features);
static void logQueueFamilies(const DxvkAdapterQueueIndices& queues); static void logQueueFamilies(const DxvkAdapterQueueIndices& queues);
static Version decodeDriverVersion(VkDriverId driverId, uint32_t version);
}; };
} }

View File

@ -48,9 +48,7 @@ namespace dxvk {
// Retrieve actual pipeline handle on first use. This // Retrieve actual pipeline handle on first use. This
// may wait for an ongoing compile job to finish, or // may wait for an ongoing compile job to finish, or
// compile the pipeline immediately on the calling thread. // compile the pipeline immediately on the calling thread.
m_libraryHandle = m_library->acquirePipelineHandle( m_libraryHandle = m_library->acquirePipelineHandle().handle;
DxvkShaderPipelineLibraryCompileArgs());
return m_libraryHandle; return m_libraryHandle;
} else { } else {
// Slow path for compute shaders that do use spec constants // Slow path for compute shaders that do use spec constants

View File

@ -1997,7 +1997,7 @@ namespace dxvk {
depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_LOAD; depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_LOAD;
depthOp.loadLayout = imageView->imageInfo().layout; depthOp.loadLayout = imageView->imageInfo().layout;
depthOp.storeLayout = imageView->imageInfo().layout; depthOp.storeLayout = imageView->imageInfo().layout;
if (clearAspects & VK_IMAGE_ASPECT_COLOR_BIT) if (clearAspects & VK_IMAGE_ASPECT_COLOR_BIT)
colorOp.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; colorOp.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
else if (discardAspects & VK_IMAGE_ASPECT_COLOR_BIT) else if (discardAspects & VK_IMAGE_ASPECT_COLOR_BIT)
@ -2010,7 +2010,7 @@ namespace dxvk {
if (clearAspects & VK_IMAGE_ASPECT_STENCIL_BIT) if (clearAspects & VK_IMAGE_ASPECT_STENCIL_BIT)
depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_CLEAR; depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_CLEAR;
else if (discardAspects & VK_IMAGE_ASPECT_DEPTH_BIT) else if (discardAspects & VK_IMAGE_ASPECT_STENCIL_BIT)
depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_DONT_CARE; depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
if (attachmentIndex >= 0 && !m_state.om.framebufferInfo.isWritable(attachmentIndex, clearAspects | discardAspects)) { if (attachmentIndex >= 0 && !m_state.om.framebufferInfo.isWritable(attachmentIndex, clearAspects | discardAspects)) {
@ -2041,6 +2041,8 @@ namespace dxvk {
} }
if (attachmentIndex < 0) { if (attachmentIndex < 0) {
bool hasViewFormatMismatch = imageView->info().format != imageView->imageInfo().format;
if (m_execBarriers.isImageDirty(imageView->image(), imageView->imageSubresources(), DxvkAccess::Write)) if (m_execBarriers.isImageDirty(imageView->image(), imageView->imageSubresources(), DxvkAccess::Write))
m_execBarriers.recordCommands(m_cmd); m_execBarriers.recordCommands(m_cmd);
@ -2075,6 +2077,11 @@ namespace dxvk {
attachmentInfo.loadOp = colorOp.loadOp; attachmentInfo.loadOp = colorOp.loadOp;
// We can't use LOAD_OP_CLEAR if the view format does not match the
// underlying image format, so just discard here and use clear later.
if (hasViewFormatMismatch && attachmentInfo.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR)
attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
renderingInfo.colorAttachmentCount = 1; renderingInfo.colorAttachmentCount = 1;
renderingInfo.pColorAttachments = &attachmentInfo; renderingInfo.pColorAttachments = &attachmentInfo;
@ -2110,6 +2117,20 @@ namespace dxvk {
} }
m_cmd->cmdBeginRendering(&renderingInfo); m_cmd->cmdBeginRendering(&renderingInfo);
if (hasViewFormatMismatch) {
VkClearAttachment clearInfo = { };
clearInfo.aspectMask = imageView->info().aspect;
clearInfo.clearValue = clearValue;
VkClearRect clearRect = { };
clearRect.rect.extent.width = extent.width;
clearRect.rect.extent.height = extent.height;
clearRect.layerCount = imageView->info().numLayers;
m_cmd->cmdClearAttachments(1, &clearInfo, 1, &clearRect);
}
m_cmd->cmdEndRendering(); m_cmd->cmdEndRendering();
m_execBarriers.accessImage( m_execBarriers.accessImage(
@ -4741,8 +4762,10 @@ namespace dxvk {
this->renderPassEmitPostBarriers(framebufferInfo, ops); this->renderPassEmitPostBarriers(framebufferInfo, ops);
uint32_t colorInfoCount = 0; uint32_t colorInfoCount = 0;
uint32_t lateClearCount = 0;
std::array<VkRenderingAttachmentInfo, MaxNumRenderTargets> colorInfos; std::array<VkRenderingAttachmentInfo, MaxNumRenderTargets> colorInfos;
std::array<VkClearAttachment, MaxNumRenderTargets> lateClears;
for (uint32_t i = 0; i < MaxNumRenderTargets; i++) { for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
const auto& colorTarget = framebufferInfo.getColorTarget(i); const auto& colorTarget = framebufferInfo.getColorTarget(i);
@ -4754,9 +4777,21 @@ namespace dxvk {
colorInfos[i].loadOp = ops.colorOps[i].loadOp; colorInfos[i].loadOp = ops.colorOps[i].loadOp;
colorInfos[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; colorInfos[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
if (ops.colorOps[i].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) if (ops.colorOps[i].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) {
colorInfos[i].clearValue.color = ops.colorOps[i].clearValue; colorInfos[i].clearValue.color = ops.colorOps[i].clearValue;
// We can't use LOAD_OP_CLEAR if the view format does not match the
// underlying image format, so just discard here and use clear later.
if (colorTarget.view->info().format != colorTarget.view->imageInfo().format) {
colorInfos[i].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
auto& clear = lateClears[lateClearCount++];
clear.colorAttachment = i;
clear.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
clear.clearValue.color = ops.colorOps[i].clearValue;
}
}
colorInfoCount = i + 1; colorInfoCount = i + 1;
} }
} }
@ -4804,6 +4839,15 @@ namespace dxvk {
m_cmd->cmdBeginRendering(&renderingInfo); m_cmd->cmdBeginRendering(&renderingInfo);
if (lateClearCount) {
VkClearRect clearRect = { };
clearRect.rect.extent.width = fbSize.width;
clearRect.rect.extent.height = fbSize.height;
clearRect.layerCount = fbSize.layers;
m_cmd->cmdClearAttachments(lateClearCount, lateClears.data(), 1, &clearRect);
}
for (uint32_t i = 0; i < framebufferInfo.numAttachments(); i++) { for (uint32_t i = 0; i < framebufferInfo.numAttachments(); i++) {
m_cmd->trackResource<DxvkAccess::None> (framebufferInfo.getAttachment(i).view); m_cmd->trackResource<DxvkAccess::None> (framebufferInfo.getAttachment(i).view);
m_cmd->trackResource<DxvkAccess::Write>(framebufferInfo.getAttachment(i).view->image()); m_cmd->trackResource<DxvkAccess::Write>(framebufferInfo.getAttachment(i).view->image());
@ -4940,7 +4984,7 @@ namespace dxvk {
// Mark compute resources and push constants as dirty // Mark compute resources and push constants as dirty
m_descriptorState.dirtyStages(VK_SHADER_STAGE_COMPUTE_BIT); m_descriptorState.dirtyStages(VK_SHADER_STAGE_COMPUTE_BIT);
if (newPipeline->getBindings()->layout().getPushConstantRange().size) if (newPipeline->getBindings()->layout().getPushConstantRange(true).size)
m_flags.set(DxvkContextFlag::DirtyPushConstants); m_flags.set(DxvkContextFlag::DirtyPushConstants);
m_flags.clr(DxvkContextFlag::CpDirtyPipelineState); m_flags.clr(DxvkContextFlag::CpDirtyPipelineState);
@ -5014,7 +5058,7 @@ namespace dxvk {
m_descriptorState.dirtyStages(VK_SHADER_STAGE_ALL_GRAPHICS); m_descriptorState.dirtyStages(VK_SHADER_STAGE_ALL_GRAPHICS);
if (newPipeline->getBindings()->layout().getPushConstantRange().size) if (newPipeline->getBindings()->layout().getPushConstantRange(true).size)
m_flags.set(DxvkContextFlag::DirtyPushConstants); m_flags.set(DxvkContextFlag::DirtyPushConstants);
m_flags.clr(DxvkContextFlag::GpDirtyPipeline); m_flags.clr(DxvkContextFlag::GpDirtyPipeline);
@ -5881,16 +5925,19 @@ namespace dxvk {
auto bindings = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS auto bindings = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS
? m_state.gp.pipeline->getBindings() ? m_state.gp.pipeline->getBindings()
: m_state.cp.pipeline->getBindings(); : m_state.cp.pipeline->getBindings();
VkPushConstantRange pushConstRange = bindings->layout().getPushConstantRange(); // Optimized pipelines may have push constants trimmed, so look up
// the exact layout used for the currently bound pipeline.
bool independentSets = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS
&& m_flags.test(DxvkContextFlag::GpIndependentSets);
VkPushConstantRange pushConstRange = bindings->layout().getPushConstantRange(independentSets);
if (!pushConstRange.size) if (!pushConstRange.size)
return; return;
// Push constants should be compatible between complete and
// independent layouts, so always ask for the complete one
m_cmd->cmdPushConstants( m_cmd->cmdPushConstants(
bindings->getPipelineLayout(false), bindings->getPipelineLayout(independentSets),
pushConstRange.stageFlags, pushConstRange.stageFlags,
pushConstRange.offset, pushConstRange.offset,
pushConstRange.size, pushConstRange.size,

View File

@ -72,7 +72,7 @@ namespace dxvk {
// memory bloat. This may be necessary for off-screen // memory bloat. This may be necessary for off-screen
// rendering applications, or in situations where games // rendering applications, or in situations where games
// pre-render a lot of images without presenting in between. // pre-render a lot of images without presenting in between.
return m_descriptorPools.size() >= 8; return m_descriptorPools.size() > MaxDesiredPoolCount;
} }
@ -100,29 +100,25 @@ namespace dxvk {
void DxvkDescriptorPool::reset() { void DxvkDescriptorPool::reset() {
// As a heuristic to save memory, check how many descriptors // As a heuristic to save memory, check how many descriptor
// have actively been used in the past couple of submissions. // sets were actually being used in past submissions.
bool isLowUsageFrame = false;
size_t poolCount = m_descriptorPools.size(); size_t poolCount = m_descriptorPools.size();
bool needsReset = poolCount > MaxDesiredPoolCount;
if (poolCount > 1 || m_setsAllocated > m_manager->getMaxSetCount() / 2) { if (poolCount > 1 || m_setsAllocated > m_manager->getMaxSetCount() / 2) {
double factor = std::max(11.0 / 3.0 - double(poolCount) / 3.0, 1.0); double factor = std::max(11.0 / 3.0 - double(poolCount) / 3.0, 1.0);
isLowUsageFrame = double(m_setsUsed) * factor < double(m_setsAllocated); needsReset = double(m_setsUsed) * factor < double(m_setsAllocated);
} }
m_lowUsageFrames = isLowUsageFrame
? m_lowUsageFrames + 1
: 0;
m_setsUsed = 0; m_setsUsed = 0;
if (m_lowUsageFrames < 16) { if (!needsReset) {
for (auto& entry : m_setLists) for (auto& entry : m_setLists)
entry.second.reset(); entry.second.reset();
} else { } else {
// If most sets are no longer being used, reset and destroy // If most sets are no longer needed, reset and destroy
// descriptor pools and reset all lookup tables in order to // descriptor pools and reset all lookup tables in order
// accomodate more descriptors of different layouts. // to accomodate more descriptors of different layouts.
for (auto pool : m_descriptorPools) for (auto pool : m_descriptorPools)
m_manager->recycleVulkanDescriptorPool(pool); m_manager->recycleVulkanDescriptorPool(pool);
@ -131,7 +127,6 @@ namespace dxvk {
m_setMaps.clear(); m_setMaps.clear();
m_setsAllocated = 0; m_setsAllocated = 0;
m_lowUsageFrames = 0;
} }
m_cachedEntry = { nullptr, nullptr }; m_cachedEntry = { nullptr, nullptr };
@ -311,7 +306,12 @@ namespace dxvk {
info.maxSets = m_maxSets; info.maxSets = m_maxSets;
info.poolSizeCount = pools.size(); info.poolSizeCount = pools.size();
info.pPoolSizes = pools.data(); info.pPoolSizes = pools.data();
if (m_device->features().nvDescriptorPoolOverallocation.descriptorPoolOverallocation) {
info.flags |= VK_DESCRIPTOR_POOL_CREATE_ALLOW_OVERALLOCATION_POOLS_BIT_NV
| VK_DESCRIPTOR_POOL_CREATE_ALLOW_OVERALLOCATION_SETS_BIT_NV;
}
VkDescriptorPool pool = VK_NULL_HANDLE; VkDescriptorPool pool = VK_NULL_HANDLE;
if (vk->vkCreateDescriptorPool(vk->device(), &info, nullptr, &pool) != VK_SUCCESS) if (vk->vkCreateDescriptorPool(vk->device(), &info, nullptr, &pool) != VK_SUCCESS)

View File

@ -80,7 +80,7 @@ namespace dxvk {
* to be updated. * to be updated.
*/ */
class DxvkDescriptorPool : public RcObject { class DxvkDescriptorPool : public RcObject {
constexpr static uint32_t MaxDesiredPoolCount = 2;
public: public:
DxvkDescriptorPool( DxvkDescriptorPool(
@ -155,8 +155,6 @@ namespace dxvk {
uint32_t m_prevSetsAllocated = 0; uint32_t m_prevSetsAllocated = 0;
uint32_t m_lowUsageFrames = 0;
DxvkDescriptorSetMap* getSetMapCached( DxvkDescriptorSetMap* getSetMapCached(
const DxvkBindingLayoutObjects* layout); const DxvkBindingLayoutObjects* layout);

View File

@ -82,7 +82,7 @@ namespace dxvk {
// Disable lifetime tracking for drivers that do not have any // Disable lifetime tracking for drivers that do not have any
// significant issues with 32-bit address space to begin with // significant issues with 32-bit address space to begin with
if (m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV_KHR, 0, 0)) if (m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV_KHR))
return false; return false;
return true; return true;
@ -325,12 +325,12 @@ namespace dxvk {
DxvkDevicePerfHints DxvkDevice::getPerfHints() { DxvkDevicePerfHints DxvkDevice::getPerfHints() {
DxvkDevicePerfHints hints; DxvkDevicePerfHints hints;
hints.preferFbDepthStencilCopy = m_features.extShaderStencilExport hints.preferFbDepthStencilCopy = m_features.extShaderStencilExport
&& (m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV_KHR, 0, 0) && (m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV_KHR)
|| m_adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, 0, 0) || m_adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR)
|| m_adapter->matchesDriver(VK_DRIVER_ID_AMD_PROPRIETARY_KHR, 0, 0)); || m_adapter->matchesDriver(VK_DRIVER_ID_AMD_PROPRIETARY_KHR));
hints.preferFbResolve = m_features.amdShaderFragmentMask hints.preferFbResolve = m_features.amdShaderFragmentMask
&& (m_adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, 0, 0) && (m_adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR)
|| m_adapter->matchesDriver(VK_DRIVER_ID_AMD_PROPRIETARY_KHR, 0, 0)); || m_adapter->matchesDriver(VK_DRIVER_ID_AMD_PROPRIETARY_KHR));
return hints; return hints;
} }

View File

@ -17,10 +17,10 @@ namespace dxvk {
bool DxvkDeviceFilter::testAdapter(const VkPhysicalDeviceProperties& properties) const { bool DxvkDeviceFilter::testAdapter(const VkPhysicalDeviceProperties& properties) const {
if (properties.apiVersion < VK_MAKE_VERSION(1, 3, 0)) { if (properties.apiVersion < VK_MAKE_API_VERSION(0, 1, 3, 0)) {
Logger::warn(str::format("Skipping Vulkan ", Logger::warn(str::format("Skipping Vulkan ",
VK_VERSION_MAJOR(properties.apiVersion), ".", VK_API_VERSION_MAJOR(properties.apiVersion), ".",
VK_VERSION_MINOR(properties.apiVersion), " adapter: ", VK_API_VERSION_MINOR(properties.apiVersion), " adapter: ",
properties.deviceName)); properties.deviceName));
return false; return false;
} }

View File

@ -2,6 +2,8 @@
#include "dxvk_include.h" #include "dxvk_include.h"
#include "../util/util_version.h"
namespace dxvk { namespace dxvk {
/** /**
@ -13,6 +15,7 @@ namespace dxvk {
* so before using them, check whether they are supported. * so before using them, check whether they are supported.
*/ */
struct DxvkDeviceInfo { struct DxvkDeviceInfo {
Version driverVersion;
VkPhysicalDeviceProperties2 core; VkPhysicalDeviceProperties2 core;
VkPhysicalDeviceVulkan11Properties vk11; VkPhysicalDeviceVulkan11Properties vk11;
VkPhysicalDeviceVulkan12Properties vk12; VkPhysicalDeviceVulkan12Properties vk12;
@ -68,6 +71,7 @@ namespace dxvk {
VkPhysicalDeviceMaintenance5FeaturesKHR khrMaintenance5; VkPhysicalDeviceMaintenance5FeaturesKHR khrMaintenance5;
VkPhysicalDevicePresentIdFeaturesKHR khrPresentId; VkPhysicalDevicePresentIdFeaturesKHR khrPresentId;
VkPhysicalDevicePresentWaitFeaturesKHR khrPresentWait; VkPhysicalDevicePresentWaitFeaturesKHR khrPresentWait;
VkPhysicalDeviceDescriptorPoolOverallocationFeaturesNV nvDescriptorPoolOverallocation;
VkPhysicalDeviceRawAccessChainsFeaturesNV nvRawAccessChains; VkPhysicalDeviceRawAccessChainsFeaturesNV nvRawAccessChains;
VkBool32 nvxBinaryImport; VkBool32 nvxBinaryImport;
VkBool32 nvxImageViewHandle; VkBool32 nvxImageViewHandle;

View File

@ -325,6 +325,7 @@ namespace dxvk {
DxvkExt khrPresentWait = { VK_KHR_PRESENT_WAIT_EXTENSION_NAME, DxvkExtMode::Optional }; DxvkExt khrPresentWait = { VK_KHR_PRESENT_WAIT_EXTENSION_NAME, DxvkExtMode::Optional };
DxvkExt khrSwapchain = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, DxvkExtMode::Required }; DxvkExt khrSwapchain = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, DxvkExtMode::Required };
DxvkExt khrWin32KeyedMutex = { VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME, DxvkExtMode::Optional }; DxvkExt khrWin32KeyedMutex = { VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME, DxvkExtMode::Optional };
DxvkExt nvDescriptorPoolOverallocation = { VK_NV_DESCRIPTOR_POOL_OVERALLOCATION_EXTENSION_NAME, DxvkExtMode::Optional };
DxvkExt nvRawAccessChains = { VK_NV_RAW_ACCESS_CHAINS_EXTENSION_NAME, DxvkExtMode::Optional }; DxvkExt nvRawAccessChains = { VK_NV_RAW_ACCESS_CHAINS_EXTENSION_NAME, DxvkExtMode::Optional };
DxvkExt nvxBinaryImport = { VK_NVX_BINARY_IMPORT_EXTENSION_NAME, DxvkExtMode::Disabled }; DxvkExt nvxBinaryImport = { VK_NVX_BINARY_IMPORT_EXTENSION_NAME, DxvkExtMode::Disabled };
DxvkExt nvxImageViewHandle = { VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME, DxvkExtMode::Disabled }; DxvkExt nvxImageViewHandle = { VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME, DxvkExtMode::Disabled };

View File

@ -1111,7 +1111,9 @@ namespace dxvk {
if (doCreateBasePipeline) if (doCreateBasePipeline)
baseHandle = this->getBasePipeline(state); baseHandle = this->getBasePipeline(state);
else
// Fast-linking may fail in some situations
if (!baseHandle)
fastHandle = this->getOptimizedPipeline(state); fastHandle = this->getOptimizedPipeline(state);
// Log pipeline state if requested, or on failure // Log pipeline state if requested, or on failure
@ -1148,6 +1150,13 @@ namespace dxvk {
|| (state.rs.lineMode() != VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT && isLineRendering)) || (state.rs.lineMode() != VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT && isLineRendering))
return false; return false;
// Depth clip is assumed to be enabled. If the driver does not
// support dynamic depth clip, we'd have to late-compile anyway
// unless the pipeline is used multiple times.
if (!m_device->features().extExtendedDynamicState3.extendedDynamicState3DepthClipEnable
&& !state.rs.depthClipEnable())
return false;
if (m_shaders.tcs != nullptr) { if (m_shaders.tcs != nullptr) {
// If tessellation shaders are present, the input patch // If tessellation shaders are present, the input patch
// vertex count must match the shader's definition. // vertex count must match the shader's definition.
@ -1219,9 +1228,6 @@ namespace dxvk {
key.viLibrary = m_manager->createVertexInputLibrary(viState); key.viLibrary = m_manager->createVertexInputLibrary(viState);
key.foLibrary = m_manager->createFragmentOutputLibrary(foState); key.foLibrary = m_manager->createFragmentOutputLibrary(foState);
if (!m_device->features().extExtendedDynamicState3.extendedDynamicState3DepthClipEnable)
key.args.depthClipEnable = state.rs.depthClipEnable();
auto entry = m_basePipelines.find(key); auto entry = m_basePipelines.find(key);
if (entry != m_basePipelines.end()) if (entry != m_basePipelines.end())
return entry->second; return entry->second;
@ -1236,10 +1242,11 @@ namespace dxvk {
const DxvkGraphicsPipelineBaseInstanceKey& key) const { const DxvkGraphicsPipelineBaseInstanceKey& key) const {
auto vk = m_device->vkd(); auto vk = m_device->vkd();
DxvkShaderPipelineLibraryHandle vs = m_vsLibrary->acquirePipelineHandle();
DxvkShaderPipelineLibraryHandle fs = m_fsLibrary->acquirePipelineHandle();
std::array<VkPipeline, 4> libraries = {{ std::array<VkPipeline, 4> libraries = {{
key.viLibrary->getHandle(), key.viLibrary->getHandle(), vs.handle, fs.handle,
m_vsLibrary->acquirePipelineHandle(key.args),
m_fsLibrary->acquirePipelineHandle(key.args),
key.foLibrary->getHandle(), key.foLibrary->getHandle(),
}}; }};
@ -1248,13 +1255,14 @@ namespace dxvk {
libInfo.pLibraries = libraries.data(); libInfo.pLibraries = libraries.data();
VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &libInfo }; VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &libInfo };
info.flags = vs.linkFlags | fs.linkFlags;
info.layout = m_bindings->getPipelineLayout(true); info.layout = m_bindings->getPipelineLayout(true);
info.basePipelineIndex = -1; info.basePipelineIndex = -1;
VkPipeline pipeline = VK_NULL_HANDLE; VkPipeline pipeline = VK_NULL_HANDLE;
VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline); VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline);
if (vr != VK_SUCCESS) if (vr && vr != VK_PIPELINE_COMPILE_REQUIRED_EXT)
Logger::err(str::format("DxvkGraphicsPipeline: Failed to create base pipeline: ", vr)); Logger::err(str::format("DxvkGraphicsPipeline: Failed to create base pipeline: ", vr));
return pipeline; return pipeline;

View File

@ -381,19 +381,16 @@ namespace dxvk {
struct DxvkGraphicsPipelineBaseInstanceKey { struct DxvkGraphicsPipelineBaseInstanceKey {
const DxvkGraphicsPipelineVertexInputLibrary* viLibrary = nullptr; const DxvkGraphicsPipelineVertexInputLibrary* viLibrary = nullptr;
const DxvkGraphicsPipelineFragmentOutputLibrary* foLibrary = nullptr; const DxvkGraphicsPipelineFragmentOutputLibrary* foLibrary = nullptr;
DxvkShaderPipelineLibraryCompileArgs args;
bool eq(const DxvkGraphicsPipelineBaseInstanceKey& other) const { bool eq(const DxvkGraphicsPipelineBaseInstanceKey& other) const {
return viLibrary == other.viLibrary return viLibrary == other.viLibrary
&& foLibrary == other.foLibrary && foLibrary == other.foLibrary;
&& args == other.args;
} }
size_t hash() const { size_t hash() const {
DxvkHashState hash; DxvkHashState hash;
hash.add(size_t(viLibrary)); hash.add(size_t(viLibrary));
hash.add(size_t(foLibrary)); hash.add(size_t(foLibrary));
hash.add(args.hash());
return hash; return hash;
} }
}; };

View File

@ -4,6 +4,7 @@
#include "dxvk_openvr.h" #include "dxvk_openvr.h"
#include "dxvk_openxr.h" #include "dxvk_openxr.h"
#include "dxvk_platform_exts.h" #include "dxvk_platform_exts.h"
#include "../wsi/wsi_platform.h"
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
@ -20,6 +21,8 @@ namespace dxvk {
Logger::info(str::format("Game: ", env::getExeName())); Logger::info(str::format("Game: ", env::getExeName()));
Logger::info(str::format("DXVK: ", DXVK_VERSION)); Logger::info(str::format("DXVK: ", DXVK_VERSION));
wsi::init();
m_config = Config::getUserConfig(); m_config = Config::getUserConfig();
m_config.merge(Config::getAppConfig(env::getExePath())); m_config.merge(Config::getAppConfig(env::getExePath()));
m_config.logOptions(); m_config.logOptions();
@ -64,6 +67,8 @@ namespace dxvk {
DxvkInstance::~DxvkInstance() { DxvkInstance::~DxvkInstance() {
if (m_messenger) if (m_messenger)
m_vki->vkDestroyDebugUtilsMessengerEXT(m_vki->instance(), m_messenger, nullptr); m_vki->vkDestroyDebugUtilsMessengerEXT(m_vki->instance(), m_messenger, nullptr);
wsi::quit();
} }
@ -177,8 +182,8 @@ namespace dxvk {
appInfo.pApplicationName = appName.c_str(); appInfo.pApplicationName = appName.c_str();
appInfo.applicationVersion = flags.raw(); appInfo.applicationVersion = flags.raw();
appInfo.pEngineName = "DXVK"; appInfo.pEngineName = "DXVK";
appInfo.engineVersion = VK_MAKE_VERSION(2, 3, 1); appInfo.engineVersion = VK_MAKE_API_VERSION(0, 2, 3, 2);
appInfo.apiVersion = VK_MAKE_VERSION(1, 3, 0); appInfo.apiVersion = VK_MAKE_API_VERSION(0, 1, 3, 0);
VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
info.pApplicationInfo = &appInfo; info.pApplicationInfo = &appInfo;

View File

@ -205,7 +205,7 @@ namespace dxvk {
DxvkBindingLayout::DxvkBindingLayout(VkShaderStageFlags stages) DxvkBindingLayout::DxvkBindingLayout(VkShaderStageFlags stages)
: m_pushConst { 0, 0, 0 }, m_stages(stages) { : m_pushConst { 0, 0, 0 }, m_pushConstStages(0), m_stages(stages) {
} }
@ -249,11 +249,17 @@ namespace dxvk {
} }
void DxvkBindingLayout::addPushConstantStage(VkShaderStageFlagBits stage) {
m_pushConstStages |= stage;
}
void DxvkBindingLayout::merge(const DxvkBindingLayout& layout) { void DxvkBindingLayout::merge(const DxvkBindingLayout& layout) {
for (uint32_t i = 0; i < layout.m_bindings.size(); i++) for (uint32_t i = 0; i < layout.m_bindings.size(); i++)
m_bindings[i].merge(layout.m_bindings[i]); m_bindings[i].merge(layout.m_bindings[i]);
addPushConstantRange(layout.m_pushConst); addPushConstantRange(layout.m_pushConst);
m_pushConstStages |= layout.m_pushConstStages;
} }
@ -266,6 +272,9 @@ namespace dxvk {
return false; return false;
} }
if (m_pushConstStages != other.m_pushConstStages)
return false;
if (m_pushConst.stageFlags != other.m_pushConst.stageFlags if (m_pushConst.stageFlags != other.m_pushConst.stageFlags
|| m_pushConst.offset != other.m_pushConst.offset || m_pushConst.offset != other.m_pushConst.offset
|| m_pushConst.size != other.m_pushConst.size) || m_pushConst.size != other.m_pushConst.size)
@ -282,6 +291,7 @@ namespace dxvk {
for (uint32_t i = 0; i < m_bindings.size(); i++) for (uint32_t i = 0; i < m_bindings.size(); i++)
hash.add(m_bindings[i].hash()); hash.add(m_bindings[i].hash());
hash.add(m_pushConstStages);
hash.add(m_pushConst.stageFlags); hash.add(m_pushConst.stageFlags);
hash.add(m_pushConst.offset); hash.add(m_pushConst.offset);
hash.add(m_pushConst.size); hash.add(m_pushConst.size);
@ -334,15 +344,16 @@ namespace dxvk {
} }
// Create pipeline layout objects // Create pipeline layout objects
VkPushConstantRange pushConst = m_layout.getPushConstantRange(); VkPushConstantRange pushConstComplete = m_layout.getPushConstantRange(false);
VkPushConstantRange pushConstIndependent = m_layout.getPushConstantRange(true);
VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
pipelineLayoutInfo.setLayoutCount = setCount; pipelineLayoutInfo.setLayoutCount = setCount;
pipelineLayoutInfo.pSetLayouts = setLayouts.data(); pipelineLayoutInfo.pSetLayouts = setLayouts.data();
if (pushConst.stageFlags && pushConst.size) { if (pushConstComplete.stageFlags && pushConstComplete.size) {
pipelineLayoutInfo.pushConstantRangeCount = 1; pipelineLayoutInfo.pushConstantRangeCount = 1;
pipelineLayoutInfo.pPushConstantRanges = &pushConst; pipelineLayoutInfo.pPushConstantRanges = &pushConstComplete;
} }
// If the full set is defined, create a layout without INDEPENDENT_SET_BITS // If the full set is defined, create a layout without INDEPENDENT_SET_BITS
@ -356,6 +367,11 @@ namespace dxvk {
if (m_device->canUseGraphicsPipelineLibrary() && (m_layout.getStages() & VK_SHADER_STAGE_ALL_GRAPHICS)) { if (m_device->canUseGraphicsPipelineLibrary() && (m_layout.getStages() & VK_SHADER_STAGE_ALL_GRAPHICS)) {
pipelineLayoutInfo.flags = VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT; pipelineLayoutInfo.flags = VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT;
if (pushConstIndependent.stageFlags && pushConstIndependent.size) {
pipelineLayoutInfo.pushConstantRangeCount = 1;
pipelineLayoutInfo.pPushConstantRanges = &pushConstIndependent;
}
if (vk->vkCreatePipelineLayout(vk->device(), &pipelineLayoutInfo, nullptr, &m_independentLayout)) if (vk->vkCreatePipelineLayout(vk->device(), &pipelineLayoutInfo, nullptr, &m_independentLayout))
throw DxvkError("DxvkBindingLayoutObjects: Failed to create pipeline layout"); throw DxvkError("DxvkBindingLayoutObjects: Failed to create pipeline layout");
} }

View File

@ -292,8 +292,19 @@ namespace dxvk {
* \brief Retrieves push constant range * \brief Retrieves push constant range
* \returns Push constant range * \returns Push constant range
*/ */
VkPushConstantRange getPushConstantRange() const { VkPushConstantRange getPushConstantRange(bool independent) const {
return m_pushConst; VkPushConstantRange result = m_pushConst;
if (!independent) {
result.stageFlags &= m_pushConstStages;
if (!result.stageFlags) {
result.offset = 0;
result.size = 0;
}
}
return result;
} }
/** /**
@ -324,6 +335,12 @@ namespace dxvk {
*/ */
void addPushConstantRange(VkPushConstantRange range); void addPushConstantRange(VkPushConstantRange range);
/**
* \brief Adds a stage that actively uses push constants
* \param [in] stage Shader stage
*/
void addPushConstantStage(VkShaderStageFlagBits stage);
/** /**
* \brief Merges binding layouts * \brief Merges binding layouts
* *
@ -353,6 +370,7 @@ namespace dxvk {
std::array<DxvkBindingList, DxvkDescriptorSets::SetCount> m_bindings; std::array<DxvkBindingList, DxvkDescriptorSets::SetCount> m_bindings;
VkPushConstantRange m_pushConst; VkPushConstantRange m_pushConst;
VkShaderStageFlags m_pushConstStages;
VkShaderStageFlags m_stages; VkShaderStageFlags m_stages;
}; };

View File

@ -1,17 +1,21 @@
#include "../dxvk_platform_exts.h" #include "dxvk_platform_exts.h"
#include "../wsi/wsi_platform.h"
namespace dxvk { namespace dxvk {
DxvkPlatformExts DxvkPlatformExts::s_instance; DxvkPlatformExts DxvkPlatformExts::s_instance;
std::string_view DxvkPlatformExts::getName() { std::string_view DxvkPlatformExts::getName() {
return "Win32 WSI"; return "Platform WSI";
} }
DxvkNameSet DxvkPlatformExts::getInstanceExtensions() { DxvkNameSet DxvkPlatformExts::getInstanceExtensions() {
std::vector<const char *> extensionNames = wsi::getInstanceExtensions();
DxvkNameSet names; DxvkNameSet names;
names.add(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); for (const char* name : extensionNames)
names.add(name);
return names; return names;
} }
@ -33,4 +37,4 @@ namespace dxvk {
} }
} }

View File

@ -382,6 +382,9 @@ namespace dxvk {
bool Presenter::supportsColorSpace(VkColorSpaceKHR colorspace) { bool Presenter::supportsColorSpace(VkColorSpaceKHR colorspace) {
if (!m_surface)
return false;
std::vector<VkSurfaceFormatKHR> surfaceFormats; std::vector<VkSurfaceFormatKHR> surfaceFormats;
getSupportedFormats(surfaceFormats, VK_FULL_SCREEN_EXCLUSIVE_DEFAULT_EXT); getSupportedFormats(surfaceFormats, VK_FULL_SCREEN_EXCLUSIVE_DEFAULT_EXT);

View File

@ -59,8 +59,8 @@ namespace dxvk {
if (info.pushConstSize) { if (info.pushConstSize) {
VkPushConstantRange pushConst; VkPushConstantRange pushConst;
pushConst.stageFlags = info.stage; pushConst.stageFlags = info.pushConstStages;
pushConst.offset = info.pushConstOffset; pushConst.offset = 0;
pushConst.size = info.pushConstSize; pushConst.size = info.pushConstSize;
m_bindings.addPushConstantRange(pushConst); m_bindings.addPushConstantRange(pushConst);
@ -75,6 +75,8 @@ namespace dxvk {
// Run an analysis pass over the SPIR-V code to gather some // Run an analysis pass over the SPIR-V code to gather some
// info that we may need during pipeline compilation. // info that we may need during pipeline compilation.
bool usesPushConstants = false;
std::vector<BindingOffsets> bindingOffsets; std::vector<BindingOffsets> bindingOffsets;
std::vector<uint32_t> varIds; std::vector<uint32_t> varIds;
std::vector<uint32_t> sampleMaskIds; std::vector<uint32_t> sampleMaskIds;
@ -154,6 +156,9 @@ namespace dxvk {
if (std::find(sampleMaskIds.begin(), sampleMaskIds.end(), ins.arg(2)) != sampleMaskIds.end()) if (std::find(sampleMaskIds.begin(), sampleMaskIds.end(), ins.arg(2)) != sampleMaskIds.end())
m_flags.set(DxvkShaderFlag::ExportsSampleMask); m_flags.set(DxvkShaderFlag::ExportsSampleMask);
} }
if (ins.arg(3) == spv::StorageClassPushConstant)
usesPushConstants = true;
} }
// Ignore the actual shader code, there's nothing interesting for us in there. // Ignore the actual shader code, there's nothing interesting for us in there.
@ -169,6 +174,11 @@ namespace dxvk {
m_bindingOffsets.push_back(info); m_bindingOffsets.push_back(info);
} }
// Set flag for stages that actually use push constants
// so that they can be trimmed for optimized pipelines.
if (usesPushConstants)
m_bindings.addPushConstantStage(info.stage);
// Don't set pipeline library flag if the shader // Don't set pipeline library flag if the shader
// doesn't actually support pipeline libraries // doesn't actually support pipeline libraries
m_needsLibraryCompile = canUsePipelineLibrary(true); m_needsLibraryCompile = canUsePipelineLibrary(true);
@ -1012,7 +1022,7 @@ namespace dxvk {
DxvkShaderPipelineLibrary::~DxvkShaderPipelineLibrary() { DxvkShaderPipelineLibrary::~DxvkShaderPipelineLibrary() {
this->destroyShaderPipelinesLocked(); this->destroyShaderPipelineLocked();
} }
@ -1032,22 +1042,17 @@ namespace dxvk {
} }
VkPipeline DxvkShaderPipelineLibrary::acquirePipelineHandle( DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::acquirePipelineHandle() {
const DxvkShaderPipelineLibraryCompileArgs& args) {
std::lock_guard lock(m_mutex); std::lock_guard lock(m_mutex);
if (m_device->mustTrackPipelineLifetime()) if (m_device->mustTrackPipelineLifetime())
m_useCount += 1; m_useCount += 1;
VkPipeline& pipeline = (m_shaders.vs && !args.depthClipEnable) if (m_pipeline.handle)
? m_pipelineNoDepthClip return m_pipeline;
: m_pipeline;
if (pipeline) m_pipeline = compileShaderPipelineLocked();
return pipeline; return m_pipeline;
pipeline = compileShaderPipelineLocked(args);
return pipeline;
} }
@ -1056,7 +1061,7 @@ namespace dxvk {
std::lock_guard lock(m_mutex); std::lock_guard lock(m_mutex);
if (!(--m_useCount)) if (!(--m_useCount))
this->destroyShaderPipelinesLocked(); this->destroyShaderPipelineLocked();
} }
} }
@ -1069,17 +1074,16 @@ namespace dxvk {
return; return;
// Compile the pipeline with default args // Compile the pipeline with default args
VkPipeline pipeline = compileShaderPipelineLocked( DxvkShaderPipelineLibraryHandle pipeline = compileShaderPipelineLocked();
DxvkShaderPipelineLibraryCompileArgs());
// On 32-bit, destroy the pipeline immediately in order to // On 32-bit, destroy the pipeline immediately in order to
// save memory. We should hit the driver's disk cache once // save memory. We should hit the driver's disk cache once
// we need to recreate the pipeline. // we need to recreate the pipeline.
if (m_device->mustTrackPipelineLifetime()) { if (m_device->mustTrackPipelineLifetime()) {
auto vk = m_device->vkd(); auto vk = m_device->vkd();
vk->vkDestroyPipeline(vk->device(), pipeline, nullptr); vk->vkDestroyPipeline(vk->device(), pipeline.handle, nullptr);
pipeline = VK_NULL_HANDLE; pipeline.handle = VK_NULL_HANDLE;
} }
// Write back pipeline handle for future use // Write back pipeline handle for future use
@ -1087,37 +1091,32 @@ namespace dxvk {
} }
void DxvkShaderPipelineLibrary::destroyShaderPipelinesLocked() { void DxvkShaderPipelineLibrary::destroyShaderPipelineLocked() {
auto vk = m_device->vkd(); auto vk = m_device->vkd();
vk->vkDestroyPipeline(vk->device(), m_pipeline, nullptr); vk->vkDestroyPipeline(vk->device(), m_pipeline.handle, nullptr);
vk->vkDestroyPipeline(vk->device(), m_pipelineNoDepthClip, nullptr);
m_pipeline = VK_NULL_HANDLE; m_pipeline.handle = VK_NULL_HANDLE;
m_pipelineNoDepthClip = VK_NULL_HANDLE;
} }
VkPipeline DxvkShaderPipelineLibrary::compileShaderPipelineLocked( DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::compileShaderPipelineLocked() {
const DxvkShaderPipelineLibraryCompileArgs& args) {
this->notifyLibraryCompile(); this->notifyLibraryCompile();
// If this is not the first time we're compiling the pipeline, // If this is not the first time we're compiling the pipeline,
// try to get a cache hit using the shader module identifier // try to get a cache hit using the shader module identifier
// so that we don't have to decompress our SPIR-V shader again. // so that we don't have to decompress our SPIR-V shader again.
VkPipeline pipeline = VK_NULL_HANDLE; DxvkShaderPipelineLibraryHandle pipeline = { VK_NULL_HANDLE, 0 };
if (m_compiledOnce && canUsePipelineCacheControl()) { if (m_compiledOnce && canUsePipelineCacheControl())
pipeline = this->compileShaderPipeline(args, pipeline = this->compileShaderPipeline(VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT);
VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT);
}
if (!pipeline) if (!pipeline.handle)
pipeline = this->compileShaderPipeline(args, 0); pipeline = this->compileShaderPipeline(0);
// Well that didn't work // Well that didn't work
if (!pipeline) if (!pipeline.handle)
return VK_NULL_HANDLE; return { VK_NULL_HANDLE, 0 };
// Increment stat counter the first time this // Increment stat counter the first time this
// shader pipeline gets compiled successfully // shader pipeline gets compiled successfully
@ -1134,8 +1133,7 @@ namespace dxvk {
} }
VkPipeline DxvkShaderPipelineLibrary::compileShaderPipeline( DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::compileShaderPipeline(
const DxvkShaderPipelineLibraryCompileArgs& args,
VkPipelineCreateFlags flags) { VkPipelineCreateFlags flags) {
DxvkShaderStageInfo stageInfo(m_device); DxvkShaderStageInfo stageInfo(m_device);
VkShaderStageFlags stageMask = getShaderStages(); VkShaderStageFlags stageMask = getShaderStages();
@ -1151,7 +1149,7 @@ namespace dxvk {
// Fail if we have no idenfitier for whatever reason, caller // Fail if we have no idenfitier for whatever reason, caller
// should fall back to the slow path if this happens // should fall back to the slow path if this happens
if (!identifier->identifierSize) if (!identifier->identifierSize)
return VK_NULL_HANDLE; return { VK_NULL_HANDLE, 0 };
stageInfo.addStage(stage, *identifier, nullptr); stageInfo.addStage(stage, *identifier, nullptr);
} else { } else {
@ -1168,22 +1166,21 @@ namespace dxvk {
} }
} }
VkPipeline pipeline = VK_NULL_HANDLE;
if (stageMask & VK_SHADER_STAGE_VERTEX_BIT) if (stageMask & VK_SHADER_STAGE_VERTEX_BIT)
return compileVertexShaderPipeline(args, stageInfo, flags); pipeline = compileVertexShaderPipeline(stageInfo, flags);
else if (stageMask & VK_SHADER_STAGE_FRAGMENT_BIT)
if (stageMask & VK_SHADER_STAGE_FRAGMENT_BIT) pipeline = compileFragmentShaderPipeline(stageInfo, flags);
return compileFragmentShaderPipeline(stageInfo, flags); else if (stageMask & VK_SHADER_STAGE_COMPUTE_BIT)
pipeline = compileComputeShaderPipeline(stageInfo, flags);
if (stageMask & VK_SHADER_STAGE_COMPUTE_BIT)
return compileComputeShaderPipeline(stageInfo, flags);
// Should be unreachable // Should be unreachable
return VK_NULL_HANDLE; return { pipeline, flags };
} }
VkPipeline DxvkShaderPipelineLibrary::compileVertexShaderPipeline( VkPipeline DxvkShaderPipelineLibrary::compileVertexShaderPipeline(
const DxvkShaderPipelineLibraryCompileArgs& args,
const DxvkShaderStageInfo& stageInfo, const DxvkShaderStageInfo& stageInfo,
VkPipelineCreateFlags flags) { VkPipelineCreateFlags flags) {
auto vk = m_device->vkd(); auto vk = m_device->vkd();
@ -1230,10 +1227,10 @@ namespace dxvk {
// Only use the fixed depth clip state if we can't make it dynamic // Only use the fixed depth clip state if we can't make it dynamic
if (!m_device->features().extExtendedDynamicState3.extendedDynamicState3DepthClipEnable) { if (!m_device->features().extExtendedDynamicState3.extendedDynamicState3DepthClipEnable) {
rsDepthClipInfo.pNext = std::exchange(rsInfo.pNext, &rsDepthClipInfo); rsDepthClipInfo.pNext = std::exchange(rsInfo.pNext, &rsDepthClipInfo);
rsDepthClipInfo.depthClipEnable = args.depthClipEnable; rsDepthClipInfo.depthClipEnable = VK_TRUE;
} }
} else { } else {
rsInfo.depthClampEnable = !args.depthClipEnable; rsInfo.depthClampEnable = VK_FALSE;
} }
// Only the view mask is used as input, and since we do not use MultiView, it is always 0 // Only the view mask is used as input, and since we do not use MultiView, it is always 0
@ -1256,7 +1253,7 @@ namespace dxvk {
VkPipeline pipeline = VK_NULL_HANDLE; VkPipeline pipeline = VK_NULL_HANDLE;
VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline); VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline);
if (vr && !(flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT)) if (vr && vr != VK_PIPELINE_COMPILE_REQUIRED_EXT)
Logger::err(str::format("DxvkShaderPipelineLibrary: Failed to create vertex shader pipeline: ", vr)); Logger::err(str::format("DxvkShaderPipelineLibrary: Failed to create vertex shader pipeline: ", vr));
return vr ? VK_NULL_HANDLE : pipeline; return vr ? VK_NULL_HANDLE : pipeline;
@ -1367,7 +1364,7 @@ namespace dxvk {
VkPipeline pipeline = VK_NULL_HANDLE; VkPipeline pipeline = VK_NULL_HANDLE;
VkResult vr = vk->vkCreateComputePipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline); VkResult vr = vk->vkCreateComputePipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline);
if (vr && !(flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT)) if (vr && vr != VK_PIPELINE_COMPILE_REQUIRED_EXT)
Logger::err(str::format("DxvkShaderPipelineLibrary: Failed to create compute shader pipeline: ", vr)); Logger::err(str::format("DxvkShaderPipelineLibrary: Failed to create compute shader pipeline: ", vr));
return vr ? VK_NULL_HANDLE : pipeline; return vr ? VK_NULL_HANDLE : pipeline;

View File

@ -52,7 +52,7 @@ namespace dxvk {
/// Flat shading input mask /// Flat shading input mask
uint32_t flatShadingInputs = 0; uint32_t flatShadingInputs = 0;
/// Push constant range /// Push constant range
uint32_t pushConstOffset = 0; VkShaderStageFlags pushConstStages = 0;
uint32_t pushConstSize = 0; uint32_t pushConstSize = 0;
/// Uniform buffer data /// Uniform buffer data
uint32_t uniformSize = 0; uint32_t uniformSize = 0;
@ -368,26 +368,6 @@ namespace dxvk {
}; };
/**
* \brief Shader pipeline library compile args
*/
struct DxvkShaderPipelineLibraryCompileArgs {
VkBool32 depthClipEnable = VK_TRUE;
bool operator == (const DxvkShaderPipelineLibraryCompileArgs& other) const {
return depthClipEnable == other.depthClipEnable;
}
bool operator != (const DxvkShaderPipelineLibraryCompileArgs& other) const {
return !this->operator == (other);
}
size_t hash() const {
return size_t(depthClipEnable);
}
};
/** /**
* \brief Shader set * \brief Shader set
* *
@ -482,6 +462,17 @@ namespace dxvk {
}; };
/**
* \brief Pipeline library handle
*
* Stores a pipeline library handle and the necessary link flags.
*/
struct DxvkShaderPipelineLibraryHandle {
VkPipeline handle;
VkPipelineCreateFlags linkFlags;
};
/** /**
* \brief Shader pipeline library * \brief Shader pipeline library
* *
@ -521,11 +512,9 @@ namespace dxvk {
* Either returns an already compiled pipeline library object, or * Either returns an already compiled pipeline library object, or
* performs the compilation step if that has not happened yet. * performs the compilation step if that has not happened yet.
* Increments the use count by one. * Increments the use count by one.
* \param [in] args Compile arguments
* \returns Vulkan pipeline handle * \returns Vulkan pipeline handle
*/ */
VkPipeline acquirePipelineHandle( DxvkShaderPipelineLibraryHandle acquirePipelineHandle();
const DxvkShaderPipelineLibraryCompileArgs& args);
/** /**
* \brief Releases pipeline * \brief Releases pipeline
@ -552,26 +541,22 @@ namespace dxvk {
DxvkShaderSet m_shaders; DxvkShaderSet m_shaders;
const DxvkBindingLayoutObjects* m_layout; const DxvkBindingLayoutObjects* m_layout;
dxvk::mutex m_mutex; dxvk::mutex m_mutex;
VkPipeline m_pipeline = VK_NULL_HANDLE; DxvkShaderPipelineLibraryHandle m_pipeline = { VK_NULL_HANDLE, 0 };
VkPipeline m_pipelineNoDepthClip = VK_NULL_HANDLE; uint32_t m_useCount = 0u;
uint32_t m_useCount = 0u; bool m_compiledOnce = false;
bool m_compiledOnce = false;
dxvk::mutex m_identifierMutex; dxvk::mutex m_identifierMutex;
DxvkShaderIdentifierSet m_identifiers; DxvkShaderIdentifierSet m_identifiers;
void destroyShaderPipelinesLocked(); void destroyShaderPipelineLocked();
VkPipeline compileShaderPipelineLocked( DxvkShaderPipelineLibraryHandle compileShaderPipelineLocked();
const DxvkShaderPipelineLibraryCompileArgs& args);
VkPipeline compileShaderPipeline( DxvkShaderPipelineLibraryHandle compileShaderPipeline(
const DxvkShaderPipelineLibraryCompileArgs& args,
VkPipelineCreateFlags flags); VkPipelineCreateFlags flags);
VkPipeline compileVertexShaderPipeline( VkPipeline compileVertexShaderPipeline(
const DxvkShaderPipelineLibraryCompileArgs& args,
const DxvkShaderStageInfo& stageInfo, const DxvkShaderStageInfo& stageInfo,
VkPipelineCreateFlags flags); VkPipelineCreateFlags flags);

View File

@ -329,6 +329,8 @@ namespace dxvk {
DxvkShaderCreateInfo vsInfo; DxvkShaderCreateInfo vsInfo;
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vsInfo.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT;
vsInfo.pushConstSize = sizeof(PresenterArgs);
vsInfo.outputMask = 0x1; vsInfo.outputMask = 0x1;
m_vs = new DxvkShader(vsInfo, std::move(vsCode)); m_vs = new DxvkShader(vsInfo, std::move(vsCode));
@ -336,6 +338,7 @@ namespace dxvk {
fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fsInfo.bindingCount = fsBindings.size(); fsInfo.bindingCount = fsBindings.size();
fsInfo.bindings = fsBindings.data(); fsInfo.bindings = fsBindings.data();
fsInfo.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT;
fsInfo.pushConstSize = sizeof(PresenterArgs); fsInfo.pushConstSize = sizeof(PresenterArgs);
fsInfo.inputMask = 0x1; fsInfo.inputMask = 0x1;
fsInfo.outputMask = 0x1; fsInfo.outputMask = 0x1;

View File

@ -129,12 +129,8 @@ namespace dxvk::hud {
std::string driverInfo = props.vk12.driverInfo; std::string driverInfo = props.vk12.driverInfo;
if (driverInfo.empty()) { if (driverInfo.empty())
driverInfo = str::format( driverInfo = props.driverVersion.toString();
VK_VERSION_MAJOR(props.core.properties.driverVersion), ".",
VK_VERSION_MINOR(props.core.properties.driverVersion), ".",
VK_VERSION_PATCH(props.core.properties.driverVersion));
}
m_deviceName = props.core.properties.deviceName; m_deviceName = props.core.properties.deviceName;
m_driverName = str::format("Driver: ", props.vk12.driverName); m_driverName = str::format("Driver: ", props.vk12.driverName);

View File

@ -183,14 +183,17 @@ namespace dxvk::hud {
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vsInfo.bindingCount = vsBindings.size(); vsInfo.bindingCount = vsBindings.size();
vsInfo.bindings = vsBindings.data(); vsInfo.bindings = vsBindings.data();
vsInfo.outputMask = 0x3; vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT;
vsInfo.pushConstSize = sizeof(HudTextPushConstants); vsInfo.pushConstSize = sizeof(HudTextPushConstants);
vsInfo.outputMask = 0x3;
result.vert = new DxvkShader(vsInfo, std::move(vsCode)); result.vert = new DxvkShader(vsInfo, std::move(vsCode));
DxvkShaderCreateInfo fsInfo; DxvkShaderCreateInfo fsInfo;
fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fsInfo.bindingCount = fsBindings.size(); fsInfo.bindingCount = fsBindings.size();
fsInfo.bindings = fsBindings.data(); fsInfo.bindings = fsBindings.data();
fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT;
fsInfo.pushConstSize = sizeof(HudTextPushConstants);
fsInfo.inputMask = 0x3; fsInfo.inputMask = 0x3;
fsInfo.outputMask = 0x1; fsInfo.outputMask = 0x1;
result.frag = new DxvkShader(fsInfo, std::move(fsCode)); result.frag = new DxvkShader(fsInfo, std::move(fsCode));
@ -212,6 +215,7 @@ namespace dxvk::hud {
DxvkShaderCreateInfo vsInfo; DxvkShaderCreateInfo vsInfo;
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vsInfo.outputMask = 0x1; vsInfo.outputMask = 0x1;
vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
vsInfo.pushConstSize = sizeof(HudGraphPushConstants); vsInfo.pushConstSize = sizeof(HudGraphPushConstants);
result.vert = new DxvkShader(vsInfo, std::move(vsCode)); result.vert = new DxvkShader(vsInfo, std::move(vsCode));
@ -221,6 +225,7 @@ namespace dxvk::hud {
fsInfo.bindings = fsBindings.data(); fsInfo.bindings = fsBindings.data();
fsInfo.inputMask = 0x1; fsInfo.inputMask = 0x1;
fsInfo.outputMask = 0x1; fsInfo.outputMask = 0x1;
fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
fsInfo.pushConstSize = sizeof(HudGraphPushConstants); fsInfo.pushConstSize = sizeof(HudGraphPushConstants);
result.frag = new DxvkShader(fsInfo, std::move(fsCode)); result.frag = new DxvkShader(fsInfo, std::move(fsCode));

View File

@ -89,6 +89,7 @@ dxvk_src = [
'dxvk_options.cpp', 'dxvk_options.cpp',
'dxvk_pipelayout.cpp', 'dxvk_pipelayout.cpp',
'dxvk_pipemanager.cpp', 'dxvk_pipemanager.cpp',
'dxvk_platform_exts.cpp',
'dxvk_presenter.cpp', 'dxvk_presenter.cpp',
'dxvk_queue.cpp', 'dxvk_queue.cpp',
'dxvk_resource.cpp', 'dxvk_resource.cpp',
@ -117,20 +118,6 @@ if platform == 'windows'
] ]
endif endif
if dxvk_wsi == 'win32'
dxvk_src += [
'platform/dxvk_win32_exts.cpp'
]
elif dxvk_wsi == 'sdl2'
dxvk_src += [
'platform/dxvk_sdl2_exts.cpp'
]
elif dxvk_wsi == 'glfw'
dxvk_src += [
'platform/dxvk_glfw_exts.cpp'
]
endif
dxvk_extra_deps = [ dependency('threads') ] dxvk_extra_deps = [ dependency('threads') ]
if platform == 'linux' if platform == 'linux'
dxvk_extra_deps += [ cpp.find_library('dl', required: false) ] dxvk_extra_deps += [ cpp.find_library('dl', required: false) ]

View File

@ -1,49 +0,0 @@
#include "../dxvk_platform_exts.h"
#include "../../vulkan/vulkan_loader.h"
#include <GLFW/glfw3.h>
namespace dxvk {
DxvkPlatformExts DxvkPlatformExts::s_instance;
std::string_view DxvkPlatformExts::getName() {
return "GLFW WSI";
}
DxvkNameSet DxvkPlatformExts::getInstanceExtensions() {
if (!glfwVulkanSupported())
throw DxvkError(str::format("GLFW WSI: Vulkan is not supported in any capacity!"));
uint32_t extensionCount = 0;
const char** extensionArray = glfwGetRequiredInstanceExtensions(&extensionCount);
if (extensionCount == 0)
throw DxvkError(str::format("GLFW WSI: Failed to get required instance extensions"));
DxvkNameSet names;
for (uint32_t i = 0; i < extensionCount; ++i) {
names.add(extensionArray[i]);
}
return names;
}
DxvkNameSet DxvkPlatformExts::getDeviceExtensions(
uint32_t adapterId) {
return DxvkNameSet();
}
void DxvkPlatformExts::initInstanceExtensions() {
//Nothing needs to be done here on GLFW
}
void DxvkPlatformExts::initDeviceExtensions(
const DxvkInstance* instance) {
//Nothing needs to be done here on GLFW
}
}

View File

@ -1,50 +0,0 @@
#include "../dxvk_platform_exts.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h>
namespace dxvk {
DxvkPlatformExts DxvkPlatformExts::s_instance;
std::string_view DxvkPlatformExts::getName() {
return "SDL2 WSI";
}
DxvkNameSet DxvkPlatformExts::getInstanceExtensions() {
SDL_Vulkan_LoadLibrary(nullptr);
uint32_t extensionCount = 0;
if (!SDL_Vulkan_GetInstanceExtensions(nullptr, &extensionCount, nullptr))
throw DxvkError(str::format("SDL2 WSI: Failed to get instance extension count. ", SDL_GetError()));
auto extensionNames = std::vector<const char *>(extensionCount);
if (!SDL_Vulkan_GetInstanceExtensions(nullptr, &extensionCount, extensionNames.data()))
throw DxvkError(str::format("SDL2 WSI: Failed to get instance extensions. ", SDL_GetError()));
DxvkNameSet names;
for (const char* name : extensionNames)
names.add(name);
return names;
}
DxvkNameSet DxvkPlatformExts::getDeviceExtensions(
uint32_t adapterId) {
return DxvkNameSet();
}
void DxvkPlatformExts::initInstanceExtensions() {
}
void DxvkPlatformExts::initDeviceExtensions(
const DxvkInstance* instance) {
}
}

View File

@ -13,7 +13,10 @@
namespace dxvk { namespace dxvk {
const static std::vector<std::pair<const char*, Config>> g_appDefaults = {{ using ProfileList = std::vector<std::pair<const char*, Config>>;
const static ProfileList g_profiles = {{
/* Assassin's Creed Syndicate: amdags issues */ /* Assassin's Creed Syndicate: amdags issues */
{ R"(\\ACS\.exe$)", {{ { R"(\\ACS\.exe$)", {{
{ "dxgi.customVendorId", "10de" }, { "dxgi.customVendorId", "10de" },
@ -122,8 +125,6 @@ namespace dxvk {
}} }, }} },
/* NieR Replicant */ /* NieR Replicant */
{ R"(\\NieR Replicant ver\.1\.22474487139\.exe)", {{ { R"(\\NieR Replicant ver\.1\.22474487139\.exe)", {{
{ "dxgi.syncInterval", "1" },
{ "dxgi.maxFrameRate", "60" },
{ "d3d11.cachedDynamicResources", "vi" }, { "d3d11.cachedDynamicResources", "vi" },
}} }, }} },
/* SteamVR performance test */ /* SteamVR performance test */
@ -138,6 +139,10 @@ namespace dxvk {
{ R"(\\h1(_[ms]p64_ship|-mod)\.exe$)", {{ { R"(\\h1(_[ms]p64_ship|-mod)\.exe$)", {{
{ "dxgi.customVendorId", "10de" }, { "dxgi.customVendorId", "10de" },
}} }, }} },
/* H2M-Mod - Modern Warfare Remastered */
{ R"(\\h2m-mod\.exe$)", {{
{ "dxgi.customVendorId", "10de" },
}} },
/* Modern Warfare 2 Campaign Remastered * /* Modern Warfare 2 Campaign Remastered *
* AMD AGS crash same as above */ * AMD AGS crash same as above */
{ R"(\\MW2CR\.exe$)", {{ { R"(\\MW2CR\.exe$)", {{
@ -331,11 +336,6 @@ namespace dxvk {
{ R"(\\SonicFrontiers\.exe$)", {{ { R"(\\SonicFrontiers\.exe$)", {{
{ "dxgi.maxFrameLatency", "1" }, { "dxgi.maxFrameLatency", "1" },
}} }, }} },
/* TRAHA Global *
* Shadow issues when it sees AMD/Nvidia */
{ R"(\\RapaNui-Win64-Shipping\.exe$)", {{
{ "dxgi.customVendorId", "8086" },
}} },
/* SpellForce 3 Reforced & expansions * /* SpellForce 3 Reforced & expansions *
* Greatly improves CPU bound performance */ * Greatly improves CPU bound performance */
{ R"(\\SF3ClientFinal\.exe$)", {{ { R"(\\SF3ClientFinal\.exe$)", {{
@ -370,6 +370,10 @@ namespace dxvk {
{ R"(\\EADesktop\.exe$)", {{ { R"(\\EADesktop\.exe$)", {{
{ "dxvk.maxChunkSize", "1" }, { "dxvk.maxChunkSize", "1" },
}} }, }} },
/* Origin app (legacy EA Desktop) */
{ R"(\\Origin\.exe$)", {{
{ "dxvk.maxChunkSize", "1" },
}} },
/* GOG Galaxy */ /* GOG Galaxy */
{ R"(\\GalaxyClient\.exe$)", {{ { R"(\\GalaxyClient\.exe$)", {{
{ "dxvk.maxChunkSize", "1" }, { "dxvk.maxChunkSize", "1" },
@ -411,11 +415,6 @@ namespace dxvk {
{ R"(\\RidersRepublic(_BE)?\.exe$)", {{ { R"(\\RidersRepublic(_BE)?\.exe$)", {{
{ "dxgi.hideAmdGpu", "True" }, { "dxgi.hideAmdGpu", "True" },
}} }, }} },
/* HoloCure - Save the Fans!
Same as Cyberpunk 2077 */
{ R"(\\HoloCure\.exe$)", {{
{ "dxgi.useMonitorFallback", "True" },
}} },
/* Kenshi * /* Kenshi *
* Helps CPU bound performance */ * Helps CPU bound performance */
{ R"(\\kenshi_x64\.exe$)", {{ { R"(\\kenshi_x64\.exe$)", {{
@ -429,6 +428,16 @@ namespace dxvk {
{ "d3d11.exposeDriverCommandLists", "False" }, { "d3d11.exposeDriverCommandLists", "False" },
{ "dxgi.hideNvidiaGpu", "False" }, { "dxgi.hideNvidiaGpu", "False" },
}} }, }} },
/* Red Faction Guerrilla Re-Mars-tered *
* Broken skybox */
{ R"(\\rfg\.exe$)", {{
{ "d3d11.longMad", "True" },
}} },
/* Guild Wars 2 - Fixes invisibility effect *
* flicker when invariantPosition is enabled */
{ R"(\\Gw2-64\.exe$)", {{
{ "d3d11.longMad", "True" },
}} },
/**********************************************/ /**********************************************/
/* D3D9 GAMES */ /* D3D9 GAMES */
@ -877,6 +886,27 @@ namespace dxvk {
{ R"(\\ShippingPC-SkyGame\.exe$)", {{ { R"(\\ShippingPC-SkyGame\.exe$)", {{
{ "d3d9.maxFrameRate", "60" }, { "d3d9.maxFrameRate", "60" },
}} }, }} },
/* 9th Dawn II *
* OpenGL game that also spins up d3d9 *
* Black screens without config */
{ R"(\\ninthdawnii\.exe$)", {{
{ "d3d9.deferSurfaceCreation", "True" },
}} },
/* Delta Force: Xtreme 1 & 2 *
* Black screen on Alt-Tab and performance */
{ R"(\\(DFX|dfx2)\.exe$)", {{
{ "d3d9.deviceLossOnFocusLoss", "True" },
{ "d3d9.cachedDynamicBuffers", "True" },
}} },
/* The Sims 3 - Black screen on alt-tab */
{ R"(\\TS3(W)?\.exe$)", {{
{ "d3d9.deviceLossOnFocusLoss", "True" },
}} },
/* Prototype *
* Incorrect shadows on AMD & Intel */
{ R"(\\prototypef\.exe$)", {{
{ "d3d9.supportDFFormats", "False" },
}} },
/**********************************************/ /**********************************************/
@ -901,13 +931,6 @@ namespace dxvk {
{ R"(\\RiftApart\.exe$)", {{ { R"(\\RiftApart\.exe$)", {{
{ "dxgi.hideNvidiaGpu", "False" }, { "dxgi.hideNvidiaGpu", "False" },
}} }, }} },
/* CP2077 enumerates display outputs each frame.
* Avoid using QueryDisplayConfig to avoid
* performance degradation until the
* optimization of that function is in Proton. */
{ R"(\\Cyberpunk2077\.exe$)", {{
{ "dxgi.useMonitorFallback", "True" },
}} },
/* Metro Exodus Enhanced Edition picks GPU adapters /* Metro Exodus Enhanced Edition picks GPU adapters
* by available VRAM, which causes issues on some * by available VRAM, which causes issues on some
* systems with integrated graphics. */ * systems with integrated graphics. */
@ -922,6 +945,28 @@ namespace dxvk {
}}; }};
const static ProfileList g_deckProfiles = {{
/* Fallout 4: Defaults to 45 FPS on OLED, but also breaks above 60 FPS */
{ R"(\\Fallout4\.exe$)", {{
{ "dxgi.syncInterval", "1" },
{ "dxgi.maxFrameRate", "60" },
}} },
}};
const Config* findProfile(const ProfileList& profiles, const std::string& appName) {
auto appConfig = std::find_if(profiles.begin(), profiles.end(),
[&appName] (const std::pair<const char*, Config>& pair) {
std::regex expr(pair.first, std::regex::extended | std::regex::icase);
return std::regex_search(appName, expr);
});
return appConfig != profiles.end()
? &appConfig->second
: nullptr;
}
static bool isWhitespace(char ch) { static bool isWhitespace(char ch) {
return ch == ' ' || ch == '\x9' || ch == '\r'; return ch == ' ' || ch == '\x9' || ch == '\r';
} }
@ -1171,20 +1216,22 @@ namespace dxvk {
Config Config::getAppConfig(const std::string& appName) { Config Config::getAppConfig(const std::string& appName) {
auto appConfig = std::find_if(g_appDefaults.begin(), g_appDefaults.end(), const Config* config = nullptr;
[&appName] (const std::pair<const char*, Config>& pair) {
std::regex expr(pair.first, std::regex::extended | std::regex::icase);
return std::regex_search(appName, expr);
});
if (appConfig != g_appDefaults.end()) { if (env::getEnvVar("SteamDeck") == "1")
config = findProfile(g_deckProfiles, appName);
if (!config)
config = findProfile(g_profiles, appName);
if (config) {
// Inform the user that we loaded a default config // Inform the user that we loaded a default config
Logger::info(str::format("Found built-in config:")); Logger::info(str::format("Found built-in config:"));
for (auto& pair : appConfig->second.m_options) for (auto& pair : config->m_options)
Logger::info(str::format(" ", pair.first, " = ", pair.second)); Logger::info(str::format(" ", pair.first, " = ", pair.second));
return appConfig->second; return *config;
} }
return Config(); return Config();

View File

@ -2,6 +2,12 @@
namespace dxvk { namespace dxvk {
GpuFlushTracker::GpuFlushTracker(
bool ensureReproducibleHeuristic)
: m_ensureReproducibleHeuristic(ensureReproducibleHeuristic) {
}
bool GpuFlushTracker::considerFlush( bool GpuFlushTracker::considerFlush(
GpuFlushType flushType, GpuFlushType flushType,
uint64_t chunkId, uint64_t chunkId,
@ -17,6 +23,9 @@ namespace dxvk {
if (!chunkCount) if (!chunkCount)
return false; return false;
if (m_ensureReproducibleHeuristic && flushType != GpuFlushType::ExplicitFlush)
return false;
// Take any earlier missed flush with a stronger hint into account, so // Take any earlier missed flush with a stronger hint into account, so
// that we still flush those as soon as possible. Ignore synchronization // that we still flush those as soon as possible. Ignore synchronization
// commands since they will either perform a flush or not need it at all. // commands since they will either perform a flush or not need it at all.

View File

@ -34,6 +34,8 @@ namespace dxvk {
public: public:
GpuFlushTracker(bool ensureReproducibleHeuristic);
/** /**
* \brief Checks whether a context flush should be performed * \brief Checks whether a context flush should be performed
* *
@ -61,6 +63,8 @@ namespace dxvk {
private: private:
bool m_ensureReproducibleHeuristic;
GpuFlushType m_lastMissedType = GpuFlushType::ImplicitWeakHint; GpuFlushType m_lastMissedType = GpuFlushType::ImplicitWeakHint;
uint64_t m_lastFlushChunkId = 0ull; uint64_t m_lastFlushChunkId = 0ull;

48
src/util/util_version.h Normal file
View File

@ -0,0 +1,48 @@
#pragma once
#include <cstdint>
#include "../vulkan/vulkan_loader.h"
#include "util_string.h"
namespace dxvk {
/**
* \brief Decoded driver version
*/
class Version {
public:
Version() = default;
Version(uint32_t major, uint32_t minor, uint32_t patch)
: m_raw((uint64_t(major) << 48) | (uint64_t(minor) << 24) | uint64_t(patch)) { }
uint32_t major() const { return uint32_t(m_raw >> 48); }
uint32_t minor() const { return uint32_t(m_raw >> 24) & 0xffffffu; }
uint32_t patch() const { return uint32_t(m_raw) & 0xffffffu; }
bool operator == (const Version& other) const { return m_raw == other.m_raw; }
bool operator != (const Version& other) const { return m_raw != other.m_raw; }
bool operator >= (const Version& other) const { return m_raw >= other.m_raw; }
bool operator <= (const Version& other) const { return m_raw <= other.m_raw; }
bool operator > (const Version& other) const { return m_raw > other.m_raw; }
bool operator < (const Version& other) const { return m_raw < other.m_raw; }
std::string toString() const {
return str::format(major(), ".", minor(), ".", patch());
}
explicit operator bool () const {
return m_raw != 0;
}
private:
uint64_t m_raw = 0;
};
}

View File

@ -1,3 +1,5 @@
#if defined(DXVK_WSI_GLFW)
#include "../wsi_monitor.h" #include "../wsi_monitor.h"
#include "wsi/native_wsi.h" #include "wsi/native_wsi.h"
@ -11,22 +13,22 @@
namespace dxvk::wsi { namespace dxvk::wsi {
HMONITOR getDefaultMonitor() { HMONITOR GlfwWsiDriver::getDefaultMonitor() {
return enumMonitors(0); return enumMonitors(0);
} }
HMONITOR enumMonitors(uint32_t index) { HMONITOR GlfwWsiDriver::enumMonitors(uint32_t index) {
return isDisplayValid(int32_t(index)) return isDisplayValid(int32_t(index))
? toHmonitor(index) ? toHmonitor(index)
: nullptr; : nullptr;
} }
HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) { HMONITOR GlfwWsiDriver::enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) {
return enumMonitors(index); return enumMonitors(index);
} }
bool getDisplayName( bool GlfwWsiDriver::getDisplayName(
HMONITOR hMonitor, HMONITOR hMonitor,
WCHAR (&Name)[32]) { WCHAR (&Name)[32]) {
const int32_t displayId = fromHmonitor(hMonitor); const int32_t displayId = fromHmonitor(hMonitor);
@ -46,7 +48,7 @@ namespace dxvk::wsi {
} }
bool getDesktopCoordinates( bool GlfwWsiDriver::getDesktopCoordinates(
HMONITOR hMonitor, HMONITOR hMonitor,
RECT* pRect) { RECT* pRect) {
const int32_t displayId = fromHmonitor(hMonitor); const int32_t displayId = fromHmonitor(hMonitor);
@ -97,7 +99,7 @@ namespace dxvk::wsi {
} }
bool getDisplayMode( bool GlfwWsiDriver::getDisplayMode(
HMONITOR hMonitor, HMONITOR hMonitor,
uint32_t ModeNumber, uint32_t ModeNumber,
WsiMode* pMode) { WsiMode* pMode) {
@ -121,7 +123,7 @@ namespace dxvk::wsi {
} }
bool getCurrentDisplayMode( bool GlfwWsiDriver::getCurrentDisplayMode(
HMONITOR hMonitor, HMONITOR hMonitor,
WsiMode* pMode) { WsiMode* pMode) {
const int32_t displayId = fromHmonitor(hMonitor); const int32_t displayId = fromHmonitor(hMonitor);
@ -141,7 +143,7 @@ namespace dxvk::wsi {
} }
bool getDesktopDisplayMode( bool GlfwWsiDriver::getDesktopDisplayMode(
HMONITOR hMonitor, HMONITOR hMonitor,
WsiMode* pMode) { WsiMode* pMode) {
const int32_t displayId = fromHmonitor(hMonitor); const int32_t displayId = fromHmonitor(hMonitor);
@ -159,9 +161,11 @@ namespace dxvk::wsi {
return true; return true;
} }
std::vector<uint8_t> getMonitorEdid(HMONITOR hMonitor) { std::vector<uint8_t> GlfwWsiDriver::getMonitorEdid(HMONITOR hMonitor) {
Logger::err("getMonitorEdid not implemented on this platform."); Logger::err("getMonitorEdid not implemented on this platform.");
return {}; return {};
} }
} }
#endif

View File

@ -0,0 +1,71 @@
#if defined(DXVK_WSI_GLFW)
#include "wsi_platform_glfw.h"
#include "../../util/util_error.h"
#include "../../util/util_string.h"
#include "../../util/util_win32_compat.h"
namespace dxvk::wsi {
GlfwWsiDriver::GlfwWsiDriver() {
libglfw = LoadLibraryA( // FIXME: Get soname as string from meson
#if defined(_WIN32)
"glfw.dll"
#elif defined(__APPLE__)
"libglfw.3.dylib"
#else
"libglfw.so.3"
#endif
);
if (libglfw == nullptr)
throw DxvkError("GLFW WSI: Failed to load GLFW DLL.");
#define GLFW_PROC(ret, name, params) \
name = reinterpret_cast<pfn_##name>(GetProcAddress(libglfw, #name)); \
if (name == nullptr) { \
FreeLibrary(libglfw); \
libglfw = nullptr; \
throw DxvkError("GLFW WSI: Failed to load " #name "."); \
}
#include "wsi_platform_glfw_funcs.h"
}
GlfwWsiDriver::~GlfwWsiDriver() {
FreeLibrary(libglfw);
}
std::vector<const char *> GlfwWsiDriver::getInstanceExtensions() {
if (!glfwVulkanSupported())
throw DxvkError(str::format("GLFW WSI: Vulkan is not supported in any capacity!"));
uint32_t extensionCount = 0;
const char** extensionArray = glfwGetRequiredInstanceExtensions(&extensionCount);
if (extensionCount == 0)
throw DxvkError(str::format("GLFW WSI: Failed to get required instance extensions"));
std::vector<const char *> names(extensionCount);
for (uint32_t i = 0; i < extensionCount; ++i) {
names.push_back(extensionArray[i]);
}
return names;
}
static bool createGlfwWsiDriver(WsiDriver **driver) {
try {
*driver = new GlfwWsiDriver();
} catch (const DxvkError& e) {
return false;
}
return true;
}
WsiBootstrap GlfwWSI = {
"GLFW",
createGlfwWsiDriver
};
}
#endif

View File

@ -3,21 +3,108 @@
#include "../../vulkan/vulkan_loader.h" #include "../../vulkan/vulkan_loader.h"
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include "../wsi_monitor.h" #include "../wsi_platform.h"
namespace dxvk::wsi { namespace dxvk::wsi {
/** class GlfwWsiDriver : public WsiDriver {
* \brief Impl-dependent state private:
*/ HMODULE libglfw;
struct DxvkWindowState { #define GLFW_PROC(ret, name, params) \
typedef ret (*pfn_##name) params; \
pfn_##name name;
#include "wsi_platform_glfw_funcs.h"
inline bool isDisplayValid(int32_t displayId) {
int32_t displayCount = 0;
glfwGetMonitors(&displayCount);
return displayId < displayCount && displayId >= 0;
}
public:
GlfwWsiDriver();
~GlfwWsiDriver();
// Platform
virtual std::vector<const char *> getInstanceExtensions();
// Monitor
virtual HMONITOR getDefaultMonitor();
virtual HMONITOR enumMonitors(uint32_t index);
virtual HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index);
virtual bool getDisplayName(
HMONITOR hMonitor,
WCHAR (&Name)[32]);
virtual bool getDesktopCoordinates(
HMONITOR hMonitor,
RECT* pRect);
virtual bool getDisplayMode(
HMONITOR hMonitor,
uint32_t modeNumber,
WsiMode* pMode);
virtual bool getCurrentDisplayMode(
HMONITOR hMonitor,
WsiMode* pMode);
virtual bool getDesktopDisplayMode(
HMONITOR hMonitor,
WsiMode* pMode);
virtual WsiEdidData getMonitorEdid(HMONITOR hMonitor);
// Window
virtual void getWindowSize(
HWND hWindow,
uint32_t* pWidth,
uint32_t* pWeight);
virtual void resizeWindow(
HWND hWindow,
DxvkWindowState* pState,
uint32_t width,
uint32_t weight);
virtual bool setWindowMode(
HMONITOR hMonitor,
HWND hWindow,
const WsiMode& mode);
virtual bool enterFullscreenMode(
HMONITOR hMonitor,
HWND hWindow,
DxvkWindowState* pState,
[[maybe_unused]]
bool modeSwitch);
virtual bool leaveFullscreenMode(
HWND hWindow,
DxvkWindowState* pState,
bool restoreCoordinates);
virtual bool restoreDisplayMode();
virtual HMONITOR getWindowMonitor(HWND hWindow);
virtual bool isWindow(HWND hWindow);
virtual void updateFullscreenWindow(
HMONITOR hMonitor,
HWND hWindow,
bool forceTopmost);
virtual VkResult createSurface(
HWND hWindow,
PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,
VkInstance instance,
VkSurfaceKHR* pSurface);
}; };
inline bool isDisplayValid(int32_t displayId) {
int32_t displayCount = 0;
glfwGetMonitors(&displayCount);
return displayId < displayCount && displayId >= 0; }
}
}

View File

@ -0,0 +1,12 @@
GLFW_PROC(VkResult, glfwCreateWindowSurface, (VkInstance, GLFWwindow*, const VkAllocationCallbacks*, VkSurfaceKHR*))
GLFW_PROC(GLFWmonitor**, glfwGetMonitors, (int*))
GLFW_PROC(void, glfwGetMonitorWorkarea, (GLFWmonitor*, int*, int*, int*, int*))
GLFW_PROC(GLFWmonitor*, glfwGetPrimaryMonitor, (void))
GLFW_PROC(const char**, glfwGetRequiredInstanceExtensions, (uint32_t*))
GLFW_PROC(const GLFWvidmode*, glfwGetVideoMode, (GLFWmonitor*))
GLFW_PROC(const GLFWvidmode*, glfwGetVideoModes, (GLFWmonitor*, int*))
GLFW_PROC(void, glfwGetWindowSize, (GLFWwindow*, int*, int*))
GLFW_PROC(void, glfwSetWindowMonitor, (GLFWwindow*, GLFWmonitor*, int, int, int, int, int))
GLFW_PROC(void, glfwSetWindowSize, (GLFWwindow*, int, int))
GLFW_PROC(int, glfwVulkanSupported, (void))
#undef GLFW_PROC

View File

@ -1,6 +1,8 @@
#if defined(DXVK_WSI_GLFW)
#include "../wsi_window.h" #include "../wsi_window.h"
#include "native/wsi/native_wsi.h" #include "native/wsi/native_glfw.h"
#include "wsi_platform_glfw.h" #include "wsi_platform_glfw.h"
#include "../../util/util_string.h" #include "../../util/util_string.h"
@ -12,7 +14,7 @@
namespace dxvk::wsi { namespace dxvk::wsi {
void getWindowSize( void GlfwWsiDriver::getWindowSize(
HWND hWindow, HWND hWindow,
uint32_t* pWidth, uint32_t* pWidth,
uint32_t* pHeight) { uint32_t* pHeight) {
@ -29,7 +31,7 @@ namespace dxvk::wsi {
} }
void resizeWindow( void GlfwWsiDriver::resizeWindow(
HWND hWindow, HWND hWindow,
DxvkWindowState* pState, DxvkWindowState* pState,
uint32_t Width, uint32_t Width,
@ -40,7 +42,7 @@ namespace dxvk::wsi {
} }
bool setWindowMode( bool GlfwWsiDriver::setWindowMode(
HMONITOR hMonitor, HMONITOR hMonitor,
HWND hWindow, HWND hWindow,
const WsiMode& pMode) { const WsiMode& pMode) {
@ -67,7 +69,7 @@ namespace dxvk::wsi {
return true; return true;
} }
bool enterFullscreenMode( bool GlfwWsiDriver::enterFullscreenMode(
HMONITOR hMonitor, HMONITOR hMonitor,
HWND hWindow, HWND hWindow,
DxvkWindowState* pState, DxvkWindowState* pState,
@ -89,7 +91,7 @@ namespace dxvk::wsi {
} }
bool leaveFullscreenMode( bool GlfwWsiDriver::leaveFullscreenMode(
HWND hWindow, HWND hWindow,
DxvkWindowState* pState, DxvkWindowState* pState,
bool restoreCoordinates) { bool restoreCoordinates) {
@ -103,13 +105,13 @@ namespace dxvk::wsi {
} }
bool restoreDisplayMode() { bool GlfwWsiDriver::restoreDisplayMode() {
// Don't need to do anything with GLFW here. // Don't need to do anything with GLFW here.
return true; return true;
} }
HMONITOR getWindowMonitor(HWND hWindow) { HMONITOR GlfwWsiDriver::getWindowMonitor(HWND hWindow) {
// TODO: implement this with glfwGetWindowMonitor // TODO: implement this with glfwGetWindowMonitor
// (or maybe not? glfwGetWindowMonitor only seems to reference *fullscreen* windows) // (or maybe not? glfwGetWindowMonitor only seems to reference *fullscreen* windows)
// GLFWwindow* window = fromHwnd(hWindow); // GLFWwindow* window = fromHwnd(hWindow);
@ -119,19 +121,19 @@ namespace dxvk::wsi {
} }
bool isWindow(HWND hWindow) { bool GlfwWsiDriver::isWindow(HWND hWindow) {
GLFWwindow* window = fromHwnd(hWindow); GLFWwindow* window = fromHwnd(hWindow);
return window != nullptr; return window != nullptr;
} }
void updateFullscreenWindow( void GlfwWsiDriver::updateFullscreenWindow(
HMONITOR hMonitor, HMONITOR hMonitor,
HWND hWindow, HWND hWindow,
bool forceTopmost) { bool forceTopmost) {
// Don't need to do anything with GLFW here. // Don't need to do anything with GLFW here.
} }
VkResult createSurface( VkResult GlfwWsiDriver::createSurface(
HWND hWindow, HWND hWindow,
PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr, PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,
VkInstance instance, VkInstance instance,
@ -141,4 +143,6 @@ namespace dxvk::wsi {
return glfwCreateWindowSurface(instance, window, nullptr, pSurface); return glfwCreateWindowSurface(instance, window, nullptr, pSurface);
} }
} }
#endif

View File

@ -1,33 +1,24 @@
wsi_common_src = [ wsi_src = [
'wsi_edid.cpp', 'wsi_edid.cpp',
] 'wsi_platform.cpp',
wsi_win32_src = [
'win32/wsi_monitor_win32.cpp', 'win32/wsi_monitor_win32.cpp',
'win32/wsi_platform_win32.cpp',
'win32/wsi_window_win32.cpp', 'win32/wsi_window_win32.cpp',
]
wsi_sdl2_src = [
'sdl2/wsi_monitor_sdl2.cpp', 'sdl2/wsi_monitor_sdl2.cpp',
'sdl2/wsi_platform_sdl2.cpp',
'sdl2/wsi_window_sdl2.cpp', 'sdl2/wsi_window_sdl2.cpp',
]
wsi_glfw_src = [
'glfw/wsi_monitor_glfw.cpp', 'glfw/wsi_monitor_glfw.cpp',
'glfw/wsi_platform_glfw.cpp',
'glfw/wsi_window_glfw.cpp', 'glfw/wsi_window_glfw.cpp',
] ]
if dxvk_wsi == 'win32' wsi_deps = [ dep_displayinfo ]
wsi_src = wsi_common_src + wsi_win32_src
wsi_deps = [ dep_displayinfo ] if platform != 'windows'
elif dxvk_wsi == 'sdl2' wsi_deps += [
wsi_src = wsi_common_src + wsi_sdl2_src lib_sdl2.partial_dependency(compile_args: true, includes: true),
wsi_deps = [ dep_displayinfo, lib_sdl2 ] lib_glfw.partial_dependency(compile_args: true, includes: true),
elif dxvk_wsi == 'glfw' ]
wsi_src = wsi_common_src + wsi_glfw_src
wsi_deps = [ dep_displayinfo, lib_glfw ]
else
error('Unknown wsi')
endif endif
wsi_lib = static_library('wsi', wsi_src, wsi_lib = static_library('wsi', wsi_src,

View File

@ -1,3 +1,5 @@
#if defined(DXVK_WSI_SDL2)
#include "../wsi_monitor.h" #include "../wsi_monitor.h"
#include "wsi/native_wsi.h" #include "wsi/native_wsi.h"
@ -12,22 +14,22 @@
namespace dxvk::wsi { namespace dxvk::wsi {
HMONITOR getDefaultMonitor() { HMONITOR Sdl2WsiDriver::getDefaultMonitor() {
return enumMonitors(0); return enumMonitors(0);
} }
HMONITOR enumMonitors(uint32_t index) { HMONITOR Sdl2WsiDriver::enumMonitors(uint32_t index) {
return isDisplayValid(int32_t(index)) return isDisplayValid(int32_t(index))
? toHmonitor(index) ? toHmonitor(index)
: nullptr; : nullptr;
} }
HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) { HMONITOR Sdl2WsiDriver::enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) {
return enumMonitors(index); return enumMonitors(index);
} }
bool getDisplayName( bool Sdl2WsiDriver::getDisplayName(
HMONITOR hMonitor, HMONITOR hMonitor,
WCHAR (&Name)[32]) { WCHAR (&Name)[32]) {
const int32_t displayId = fromHmonitor(hMonitor); const int32_t displayId = fromHmonitor(hMonitor);
@ -47,7 +49,7 @@ namespace dxvk::wsi {
} }
bool getDesktopCoordinates( bool Sdl2WsiDriver::getDesktopCoordinates(
HMONITOR hMonitor, HMONITOR hMonitor,
RECT* pRect) { RECT* pRect) {
const int32_t displayId = fromHmonitor(hMonitor); const int32_t displayId = fromHmonitor(hMonitor);
@ -90,7 +92,7 @@ namespace dxvk::wsi {
} }
bool getDisplayMode( bool Sdl2WsiDriver::getDisplayMode(
HMONITOR hMonitor, HMONITOR hMonitor,
uint32_t ModeNumber, uint32_t ModeNumber,
WsiMode* pMode) { WsiMode* pMode) {
@ -109,7 +111,7 @@ namespace dxvk::wsi {
} }
bool getCurrentDisplayMode( bool Sdl2WsiDriver::getCurrentDisplayMode(
HMONITOR hMonitor, HMONITOR hMonitor,
WsiMode* pMode) { WsiMode* pMode) {
const int32_t displayId = fromHmonitor(hMonitor); const int32_t displayId = fromHmonitor(hMonitor);
@ -129,7 +131,7 @@ namespace dxvk::wsi {
} }
bool getDesktopDisplayMode( bool Sdl2WsiDriver::getDesktopDisplayMode(
HMONITOR hMonitor, HMONITOR hMonitor,
WsiMode* pMode) { WsiMode* pMode) {
const int32_t displayId = fromHmonitor(hMonitor); const int32_t displayId = fromHmonitor(hMonitor);
@ -148,9 +150,11 @@ namespace dxvk::wsi {
return true; return true;
} }
std::vector<uint8_t> getMonitorEdid(HMONITOR hMonitor) { std::vector<uint8_t> Sdl2WsiDriver::getMonitorEdid(HMONITOR hMonitor) {
Logger::err("getMonitorEdid not implemented on this platform."); Logger::err("getMonitorEdid not implemented on this platform.");
return {}; return {};
} }
} }
#endif

View File

@ -0,0 +1,69 @@
#if defined(DXVK_WSI_SDL2)
#include "wsi_platform_sdl2.h"
#include "../../util/util_error.h"
#include "../../util/util_string.h"
#include "../../util/util_win32_compat.h"
#include <SDL2/SDL_vulkan.h>
namespace dxvk::wsi {
Sdl2WsiDriver::Sdl2WsiDriver() {
libsdl = LoadLibraryA( // FIXME: Get soname as string from meson
#if defined(_WIN32)
"SDL2.dll"
#elif defined(__APPLE__)
"libSDL2-2.0.0.dylib"
#else
"libSDL2-2.0.so.0"
#endif
);
if (libsdl == nullptr)
throw DxvkError("SDL2 WSI: Failed to load SDL2 DLL.");
#define SDL_PROC(ret, name, params) \
name = reinterpret_cast<pfn_##name>(GetProcAddress(libsdl, #name)); \
if (name == nullptr) { \
FreeLibrary(libsdl); \
libsdl = nullptr; \
throw DxvkError("SDL2 WSI: Failed to load " #name "."); \
}
#include "wsi_platform_sdl2_funcs.h"
}
Sdl2WsiDriver::~Sdl2WsiDriver() {
FreeLibrary(libsdl);
}
std::vector<const char *> Sdl2WsiDriver::getInstanceExtensions() {
SDL_Vulkan_LoadLibrary(nullptr);
uint32_t extensionCount = 0;
if (!SDL_Vulkan_GetInstanceExtensions(nullptr, &extensionCount, nullptr))
throw DxvkError(str::format("SDL2 WSI: Failed to get instance extension count. ", SDL_GetError()));
auto extensionNames = std::vector<const char *>(extensionCount);
if (!SDL_Vulkan_GetInstanceExtensions(nullptr, &extensionCount, extensionNames.data()))
throw DxvkError(str::format("SDL2 WSI: Failed to get instance extensions. ", SDL_GetError()));
return extensionNames;
}
static bool createSdl2WsiDriver(WsiDriver **driver) {
try {
*driver = new Sdl2WsiDriver();
} catch (const DxvkError& e) {
return false;
}
return true;
}
WsiBootstrap Sdl2WSI = {
"SDL2",
createSdl2WsiDriver
};
}
#endif

View File

@ -1,21 +1,108 @@
#pragma once #pragma once
#include <SDL2/SDL.h> #include <SDL.h>
#include "../wsi_monitor.h" #include "../wsi_platform.h"
namespace dxvk::wsi { namespace dxvk::wsi {
/** class Sdl2WsiDriver : public WsiDriver {
* \brief Impl-dependent state private:
*/ HMODULE libsdl;
struct DxvkWindowState { #define SDL_PROC(ret, name, params) \
typedef ret (SDLCALL *pfn_##name) params; \
pfn_##name name;
#include "wsi_platform_sdl2_funcs.h"
inline bool isDisplayValid(int32_t displayId) {
const int32_t displayCount = SDL_GetNumVideoDisplays();
return displayId < displayCount && displayId >= 0;
}
public:
Sdl2WsiDriver();
~Sdl2WsiDriver();
// Platform
virtual std::vector<const char *> getInstanceExtensions();
// Monitor
virtual HMONITOR getDefaultMonitor();
virtual HMONITOR enumMonitors(uint32_t index);
virtual HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index);
virtual bool getDisplayName(
HMONITOR hMonitor,
WCHAR (&Name)[32]);
virtual bool getDesktopCoordinates(
HMONITOR hMonitor,
RECT* pRect);
virtual bool getDisplayMode(
HMONITOR hMonitor,
uint32_t modeNumber,
WsiMode* pMode);
virtual bool getCurrentDisplayMode(
HMONITOR hMonitor,
WsiMode* pMode);
virtual bool getDesktopDisplayMode(
HMONITOR hMonitor,
WsiMode* pMode);
virtual WsiEdidData getMonitorEdid(HMONITOR hMonitor);
// Window
virtual void getWindowSize(
HWND hWindow,
uint32_t* pWidth,
uint32_t* pWeight);
virtual void resizeWindow(
HWND hWindow,
DxvkWindowState* pState,
uint32_t width,
uint32_t weight);
virtual bool setWindowMode(
HMONITOR hMonitor,
HWND hWindow,
const WsiMode& mode);
virtual bool enterFullscreenMode(
HMONITOR hMonitor,
HWND hWindow,
DxvkWindowState* pState,
[[maybe_unused]]
bool modeSwitch);
virtual bool leaveFullscreenMode(
HWND hWindow,
DxvkWindowState* pState,
bool restoreCoordinates);
virtual bool restoreDisplayMode();
virtual HMONITOR getWindowMonitor(HWND hWindow);
virtual bool isWindow(HWND hWindow);
virtual void updateFullscreenWindow(
HMONITOR hMonitor,
HWND hWindow,
bool forceTopmost);
virtual VkResult createSurface(
HWND hWindow,
PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,
VkInstance instance,
VkSurfaceKHR* pSurface);
}; };
inline bool isDisplayValid(int32_t displayId) {
const int32_t displayCount = SDL_GetNumVideoDisplays();
return displayId < displayCount && displayId >= 0;
}
} }

View File

@ -0,0 +1,16 @@
SDL_PROC(SDL_DisplayMode*, SDL_GetClosestDisplayMode, (int, const SDL_DisplayMode*, SDL_DisplayMode*))
SDL_PROC(int, SDL_GetCurrentDisplayMode, (int, SDL_DisplayMode*))
SDL_PROC(int, SDL_GetDesktopDisplayMode, (int, SDL_DisplayMode*))
SDL_PROC(int, SDL_GetDisplayBounds, (int, SDL_Rect*))
SDL_PROC(int, SDL_GetDisplayMode, (int, int, SDL_DisplayMode*))
SDL_PROC(const char*, SDL_GetError, (void))
SDL_PROC(int, SDL_GetNumVideoDisplays, (void))
SDL_PROC(int, SDL_GetWindowDisplayIndex, (SDL_Window*))
SDL_PROC(int, SDL_SetWindowDisplayMode, (SDL_Window*, const SDL_DisplayMode*))
SDL_PROC(int, SDL_SetWindowFullscreen, (SDL_Window*, Uint32))
SDL_PROC(void, SDL_GetWindowSize, (SDL_Window*, int*, int*))
SDL_PROC(void, SDL_SetWindowSize, (SDL_Window*, int, int))
SDL_PROC(SDL_bool, SDL_Vulkan_CreateSurface, (SDL_Window*, VkInstance, VkSurfaceKHR*))
SDL_PROC(SDL_bool, SDL_Vulkan_GetInstanceExtensions, (SDL_Window*, unsigned int*, const char**))
SDL_PROC(int, SDL_Vulkan_LoadLibrary, (const char*))
#undef SDL_PROC

View File

@ -1,17 +1,19 @@
#if defined(DXVK_WSI_SDL2)
#include "../wsi_window.h" #include "../wsi_window.h"
#include "native/wsi/native_wsi.h" #include "native/wsi/native_sdl2.h"
#include "wsi_platform_sdl2.h" #include "wsi_platform_sdl2.h"
#include "../../util/util_string.h" #include "../../util/util_string.h"
#include "../../util/log/log.h" #include "../../util/log/log.h"
#include <windows.h> #include <windows.h>
#include <SDL2/SDL_vulkan.h> #include <SDL_vulkan.h>
namespace dxvk::wsi { namespace dxvk::wsi {
void getWindowSize( void Sdl2WsiDriver::getWindowSize(
HWND hWindow, HWND hWindow,
uint32_t* pWidth, uint32_t* pWidth,
uint32_t* pHeight) { uint32_t* pHeight) {
@ -28,7 +30,7 @@ namespace dxvk::wsi {
} }
void resizeWindow( void Sdl2WsiDriver::resizeWindow(
HWND hWindow, HWND hWindow,
DxvkWindowState* pState, DxvkWindowState* pState,
uint32_t Width, uint32_t Width,
@ -39,7 +41,7 @@ namespace dxvk::wsi {
} }
bool setWindowMode( bool Sdl2WsiDriver::setWindowMode(
HMONITOR hMonitor, HMONITOR hMonitor,
HWND hWindow, HWND hWindow,
const WsiMode& pMode) { const WsiMode& pMode) {
@ -73,7 +75,7 @@ namespace dxvk::wsi {
bool enterFullscreenMode( bool Sdl2WsiDriver::enterFullscreenMode(
HMONITOR hMonitor, HMONITOR hMonitor,
HWND hWindow, HWND hWindow,
DxvkWindowState* pState, DxvkWindowState* pState,
@ -99,7 +101,7 @@ namespace dxvk::wsi {
} }
bool leaveFullscreenMode( bool Sdl2WsiDriver::leaveFullscreenMode(
HWND hWindow, HWND hWindow,
DxvkWindowState* pState, DxvkWindowState* pState,
bool restoreCoordinates) { bool restoreCoordinates) {
@ -114,13 +116,13 @@ namespace dxvk::wsi {
} }
bool restoreDisplayMode() { bool Sdl2WsiDriver::restoreDisplayMode() {
// Don't need to do anything with SDL2 here. // Don't need to do anything with SDL2 here.
return true; return true;
} }
HMONITOR getWindowMonitor(HWND hWindow) { HMONITOR Sdl2WsiDriver::getWindowMonitor(HWND hWindow) {
SDL_Window* window = fromHwnd(hWindow); SDL_Window* window = fromHwnd(hWindow);
const int32_t displayId = SDL_GetWindowDisplayIndex(window); const int32_t displayId = SDL_GetWindowDisplayIndex(window);
@ -128,13 +130,13 @@ namespace dxvk::wsi {
} }
bool isWindow(HWND hWindow) { bool Sdl2WsiDriver::isWindow(HWND hWindow) {
SDL_Window* window = fromHwnd(hWindow); SDL_Window* window = fromHwnd(hWindow);
return window != nullptr; return window != nullptr;
} }
void updateFullscreenWindow( void Sdl2WsiDriver::updateFullscreenWindow(
HMONITOR hMonitor, HMONITOR hMonitor,
HWND hWindow, HWND hWindow,
bool forceTopmost) { bool forceTopmost) {
@ -142,7 +144,7 @@ namespace dxvk::wsi {
} }
VkResult createSurface( VkResult Sdl2WsiDriver::createSurface(
HWND hWindow, HWND hWindow,
PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr, PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,
VkInstance instance, VkInstance instance,
@ -155,3 +157,5 @@ namespace dxvk::wsi {
} }
} }
#endif

View File

@ -1,4 +1,6 @@
#include "../wsi_monitor.h" #if defined(DXVK_WSI_WIN32)
#include "wsi_platform_win32.h"
#include "../../util/util_string.h" #include "../../util/util_string.h"
#include "../../util/log/log.h" #include "../../util/log/log.h"
@ -13,7 +15,7 @@
namespace dxvk::wsi { namespace dxvk::wsi {
HMONITOR getDefaultMonitor() { HMONITOR Win32WsiDriver::getDefaultMonitor() {
return ::MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY); return ::MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY);
} }
@ -45,7 +47,7 @@ namespace dxvk::wsi {
return FALSE; return FALSE;
} }
HMONITOR enumMonitors(uint32_t index) { HMONITOR Win32WsiDriver::enumMonitors(uint32_t index) {
MonitorEnumInfo info; MonitorEnumInfo info;
info.iMonitorId = index; info.iMonitorId = index;
info.oMonitor = nullptr; info.oMonitor = nullptr;
@ -58,7 +60,7 @@ namespace dxvk::wsi {
return info.oMonitor; return info.oMonitor;
} }
HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) { HMONITOR Win32WsiDriver::enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) {
if (!numLUIDs) if (!numLUIDs)
return enumMonitors(index); return enumMonitors(index);
@ -132,7 +134,7 @@ namespace dxvk::wsi {
} }
bool getDisplayName( bool Win32WsiDriver::getDisplayName(
HMONITOR hMonitor, HMONITOR hMonitor,
WCHAR (&Name)[32]) { WCHAR (&Name)[32]) {
// Query monitor info to get the device name // Query monitor info to get the device name
@ -150,7 +152,7 @@ namespace dxvk::wsi {
} }
bool getDesktopCoordinates( bool Win32WsiDriver::getDesktopCoordinates(
HMONITOR hMonitor, HMONITOR hMonitor,
RECT* pRect) { RECT* pRect) {
::MONITORINFOEXW monInfo; ::MONITORINFOEXW monInfo;
@ -200,7 +202,7 @@ namespace dxvk::wsi {
} }
bool getDisplayMode( bool Win32WsiDriver::getDisplayMode(
HMONITOR hMonitor, HMONITOR hMonitor,
uint32_t modeNumber, uint32_t modeNumber,
WsiMode* pMode) { WsiMode* pMode) {
@ -208,14 +210,14 @@ namespace dxvk::wsi {
} }
bool getCurrentDisplayMode( bool Win32WsiDriver::getCurrentDisplayMode(
HMONITOR hMonitor, HMONITOR hMonitor,
WsiMode* pMode) { WsiMode* pMode) {
return retrieveDisplayMode(hMonitor, ENUM_CURRENT_SETTINGS, pMode); return retrieveDisplayMode(hMonitor, ENUM_CURRENT_SETTINGS, pMode);
} }
bool getDesktopDisplayMode( bool Win32WsiDriver::getDesktopDisplayMode(
HMONITOR hMonitor, HMONITOR hMonitor,
WsiMode* pMode) { WsiMode* pMode) {
return retrieveDisplayMode(hMonitor, ENUM_REGISTRY_SETTINGS, pMode); return retrieveDisplayMode(hMonitor, ENUM_REGISTRY_SETTINGS, pMode);
@ -308,7 +310,7 @@ namespace dxvk::wsi {
wchar_t extraChars[MAX_DEVICE_ID_LEN]; wchar_t extraChars[MAX_DEVICE_ID_LEN];
}; };
WsiEdidData getMonitorEdid(HMONITOR hMonitor) { WsiEdidData Win32WsiDriver::getMonitorEdid(HMONITOR hMonitor) {
static constexpr GUID GUID_DEVINTERFACE_MONITOR = { 0xe6f07b5f, 0xee97, 0x4a90, 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 }; static constexpr GUID GUID_DEVINTERFACE_MONITOR = { 0xe6f07b5f, 0xee97, 0x4a90, 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 };
static auto pfnSetupDiGetClassDevsW = reinterpret_cast<decltype(SetupDiGetClassDevsW)*> (::GetProcAddress(::GetModuleHandleW(L"setupapi.dll"), "SetupDiGetClassDevsW")); static auto pfnSetupDiGetClassDevsW = reinterpret_cast<decltype(SetupDiGetClassDevsW)*> (::GetProcAddress(::GetModuleHandleW(L"setupapi.dll"), "SetupDiGetClassDevsW"));
static auto pfnSetupDiEnumDeviceInterfaces = reinterpret_cast<decltype(SetupDiEnumDeviceInterfaces)*> (::GetProcAddress(::GetModuleHandleW(L"setupapi.dll"), "SetupDiEnumDeviceInterfaces")); static auto pfnSetupDiEnumDeviceInterfaces = reinterpret_cast<decltype(SetupDiEnumDeviceInterfaces)*> (::GetProcAddress(::GetModuleHandleW(L"setupapi.dll"), "SetupDiEnumDeviceInterfaces"));
@ -370,3 +372,5 @@ namespace dxvk::wsi {
} }
} }
#endif

View File

@ -0,0 +1,23 @@
#if defined(DXVK_WSI_WIN32)
#include "wsi_platform_win32.h"
namespace dxvk::wsi {
std::vector<const char *> Win32WsiDriver::getInstanceExtensions() {
return { VK_KHR_WIN32_SURFACE_EXTENSION_NAME };
}
static bool createWin32WsiDriver(WsiDriver **driver) {
*driver = new Win32WsiDriver();
return true;
}
WsiBootstrap Win32WSI = {
"Win32",
createWin32WsiDriver
};
}
#endif

View File

@ -2,15 +2,91 @@
#include <windows.h> #include <windows.h>
#include "../wsi_platform.h"
namespace dxvk::wsi { namespace dxvk::wsi {
/** class Win32WsiDriver : public WsiDriver {
* \brief Impl-dependent state public:
*/ // Platform
struct DxvkWindowState { virtual std::vector<const char *> getInstanceExtensions();
LONG style = 0;
LONG exstyle = 0; // Monitor
RECT rect = { 0, 0, 0, 0 }; virtual HMONITOR getDefaultMonitor();
virtual HMONITOR enumMonitors(uint32_t index);
virtual HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index);
virtual bool getDisplayName(
HMONITOR hMonitor,
WCHAR (&Name)[32]);
virtual bool getDesktopCoordinates(
HMONITOR hMonitor,
RECT* pRect);
virtual bool getDisplayMode(
HMONITOR hMonitor,
uint32_t modeNumber,
WsiMode* pMode);
virtual bool getCurrentDisplayMode(
HMONITOR hMonitor,
WsiMode* pMode);
virtual bool getDesktopDisplayMode(
HMONITOR hMonitor,
WsiMode* pMode);
virtual WsiEdidData getMonitorEdid(HMONITOR hMonitor);
// Window
virtual void getWindowSize(
HWND hWindow,
uint32_t* pWidth,
uint32_t* pWeight);
virtual void resizeWindow(
HWND hWindow,
DxvkWindowState* pState,
uint32_t width,
uint32_t weight);
virtual bool setWindowMode(
HMONITOR hMonitor,
HWND hWindow,
const WsiMode& mode);
virtual bool enterFullscreenMode(
HMONITOR hMonitor,
HWND hWindow,
DxvkWindowState* pState,
[[maybe_unused]]
bool modeSwitch);
virtual bool leaveFullscreenMode(
HWND hWindow,
DxvkWindowState* pState,
bool restoreCoordinates);
virtual bool restoreDisplayMode();
virtual HMONITOR getWindowMonitor(HWND hWindow);
virtual bool isWindow(HWND hWindow);
virtual void updateFullscreenWindow(
HMONITOR hMonitor,
HWND hWindow,
bool forceTopmost);
virtual VkResult createSurface(
HWND hWindow,
PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,
VkInstance instance,
VkSurfaceKHR* pSurface);
}; };
} }

View File

@ -1,5 +1,6 @@
#include "../wsi_window.h" #if defined(DXVK_WSI_WIN32)
#include "../wsi_monitor.h"
#include "wsi_platform_win32.h"
#include "../../util/util_string.h" #include "../../util/util_string.h"
#include "../../util/log/log.h" #include "../../util/log/log.h"
@ -94,7 +95,7 @@ namespace dxvk::wsi {
} }
void getWindowSize( void Win32WsiDriver::getWindowSize(
HWND hWindow, HWND hWindow,
uint32_t* pWidth, uint32_t* pWidth,
uint32_t* pHeight) { uint32_t* pHeight) {
@ -109,7 +110,7 @@ namespace dxvk::wsi {
} }
void resizeWindow( void Win32WsiDriver::resizeWindow(
HWND hWindow, HWND hWindow,
DxvkWindowState* pState, DxvkWindowState* pState,
uint32_t width, uint32_t width,
@ -130,7 +131,7 @@ namespace dxvk::wsi {
} }
bool setWindowMode( bool Win32WsiDriver::setWindowMode(
HMONITOR hMonitor, HMONITOR hMonitor,
HWND hWindow, HWND hWindow,
const WsiMode& mode) { const WsiMode& mode) {
@ -163,21 +164,21 @@ namespace dxvk::wsi {
} }
bool enterFullscreenMode( bool Win32WsiDriver::enterFullscreenMode(
HMONITOR hMonitor, HMONITOR hMonitor,
HWND hWindow, HWND hWindow,
DxvkWindowState* pState, DxvkWindowState* pState,
[[maybe_unused]] [[maybe_unused]]
bool modeSwitch) { bool modeSwitch) {
// Find a display mode that matches what we need // Find a display mode that matches what we need
::GetWindowRect(hWindow, &pState->rect); ::GetWindowRect(hWindow, &pState->win.rect);
// Change the window flags to remove the decoration etc. // Change the window flags to remove the decoration etc.
LONG style = ::GetWindowLongW(hWindow, GWL_STYLE); LONG style = ::GetWindowLongW(hWindow, GWL_STYLE);
LONG exstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE); LONG exstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE);
pState->style = style; pState->win.style = style;
pState->exstyle = exstyle; pState->win.exstyle = exstyle;
style &= ~WS_OVERLAPPEDWINDOW; style &= ~WS_OVERLAPPEDWINDOW;
exstyle &= ~WS_EX_OVERLAPPEDWINDOW; exstyle &= ~WS_EX_OVERLAPPEDWINDOW;
@ -196,7 +197,7 @@ namespace dxvk::wsi {
} }
bool leaveFullscreenMode( bool Win32WsiDriver::leaveFullscreenMode(
HWND hWindow, HWND hWindow,
DxvkWindowState* pState, DxvkWindowState* pState,
bool restoreCoordinates) { bool restoreCoordinates) {
@ -205,27 +206,27 @@ namespace dxvk::wsi {
LONG curStyle = ::GetWindowLongW(hWindow, GWL_STYLE) & ~WS_VISIBLE; LONG curStyle = ::GetWindowLongW(hWindow, GWL_STYLE) & ~WS_VISIBLE;
LONG curExstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE) & ~WS_EX_TOPMOST; LONG curExstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE) & ~WS_EX_TOPMOST;
if (curStyle == (pState->style & ~(WS_VISIBLE | WS_OVERLAPPEDWINDOW)) if (curStyle == (pState->win.style & ~(WS_VISIBLE | WS_OVERLAPPEDWINDOW))
&& curExstyle == (pState->exstyle & ~(WS_EX_TOPMOST | WS_EX_OVERLAPPEDWINDOW))) { && curExstyle == (pState->win.exstyle & ~(WS_EX_TOPMOST | WS_EX_OVERLAPPEDWINDOW))) {
::SetWindowLongW(hWindow, GWL_STYLE, pState->style); ::SetWindowLongW(hWindow, GWL_STYLE, pState->win.style);
::SetWindowLongW(hWindow, GWL_EXSTYLE, pState->exstyle); ::SetWindowLongW(hWindow, GWL_EXSTYLE, pState->win.exstyle);
} }
// Restore window position and apply the style // Restore window position and apply the style
UINT flags = SWP_FRAMECHANGED | SWP_NOACTIVATE; UINT flags = SWP_FRAMECHANGED | SWP_NOACTIVATE;
const RECT rect = pState->rect; const RECT rect = pState->win.rect;
if (!restoreCoordinates) if (!restoreCoordinates)
flags |= SWP_NOSIZE | SWP_NOMOVE; flags |= SWP_NOSIZE | SWP_NOMOVE;
::SetWindowPos(hWindow, (pState->exstyle & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_NOTOPMOST, ::SetWindowPos(hWindow, (pState->win.exstyle & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_NOTOPMOST,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, flags); rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, flags);
return true; return true;
} }
bool restoreDisplayMode() { bool Win32WsiDriver::restoreDisplayMode() {
bool success = true; bool success = true;
bool result = ::EnumDisplayMonitors(nullptr, nullptr, bool result = ::EnumDisplayMonitors(nullptr, nullptr,
&restoreDisplayModeCallback, &restoreDisplayModeCallback,
@ -235,7 +236,7 @@ namespace dxvk::wsi {
} }
HMONITOR getWindowMonitor(HWND hWindow) { HMONITOR Win32WsiDriver::getWindowMonitor(HWND hWindow) {
RECT windowRect = { 0, 0, 0, 0 }; RECT windowRect = { 0, 0, 0, 0 };
::GetWindowRect(hWindow, &windowRect); ::GetWindowRect(hWindow, &windowRect);
@ -248,12 +249,12 @@ namespace dxvk::wsi {
} }
bool isWindow(HWND hWindow) { bool Win32WsiDriver::isWindow(HWND hWindow) {
return ::IsWindow(hWindow); return ::IsWindow(hWindow);
} }
void updateFullscreenWindow( void Win32WsiDriver::updateFullscreenWindow(
HMONITOR hMonitor, HMONITOR hMonitor,
HWND hWindow, HWND hWindow,
bool forceTopmost) { bool forceTopmost) {
@ -274,7 +275,7 @@ namespace dxvk::wsi {
} }
VkResult createSurface( VkResult Win32WsiDriver::createSurface(
HWND hWindow, HWND hWindow,
PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr, PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,
VkInstance instance, VkInstance instance,
@ -296,3 +297,5 @@ namespace dxvk::wsi {
} }
} }
#endif

177
src/wsi/wsi_platform.cpp Normal file
View File

@ -0,0 +1,177 @@
#include "wsi_platform.h"
#include "wsi_monitor.h"
#include "wsi_window.h"
#include "../util/util_env.h"
#include "../util/util_error.h"
namespace dxvk::wsi {
static WsiDriver* s_driver = nullptr;
static int s_refcount = 0;
static const WsiBootstrap *wsiBootstrap[] = {
#if defined(DXVK_WSI_WIN32)
&Win32WSI,
#endif
#if defined(DXVK_WSI_SDL2)
&Sdl2WSI,
#endif
#if defined(DXVK_WSI_GLFW)
&GlfwWSI,
#endif
};
void init() {
if (s_refcount++ > 0)
return;
std::string hint = dxvk::env::getEnvVar("DXVK_WSI_DRIVER");
if (hint == "") {
// At least for Windows, it is reasonable to fall back to a default;
// for other platforms however we _need_ to know which WSI to use!
#if defined(DXVK_WSI_WIN32)
hint = "Win32";
#else
throw DxvkError("DXVK_WSI_DRIVER environment variable unset");
#endif
}
bool success = false;
for (const WsiBootstrap *b : wsiBootstrap) {
if (hint == b->name && b->createDriver(&s_driver)) {
success = true;
break;
}
}
if (!success)
throw DxvkError("Failed to initialize WSI.");
}
void quit() {
if (s_refcount == 0)
return;
s_refcount--;
if (s_refcount == 0) {
delete s_driver;
s_driver = nullptr;
}
}
std::vector<const char *> getInstanceExtensions() {
return s_driver->getInstanceExtensions();
}
void getWindowSize(
HWND hWindow,
uint32_t* pWidth,
uint32_t* pHeight) {
s_driver->getWindowSize(hWindow, pWidth, pHeight);
}
void resizeWindow(
HWND hWindow,
DxvkWindowState* pState,
uint32_t width,
uint32_t height) {
s_driver->resizeWindow(hWindow, pState, width, height);
}
bool setWindowMode(
HMONITOR hMonitor,
HWND hWindow,
const WsiMode& mode) {
return s_driver->setWindowMode(hMonitor, hWindow, mode);
}
bool enterFullscreenMode(
HMONITOR hMonitor,
HWND hWindow,
DxvkWindowState* pState,
[[maybe_unused]]
bool modeSwitch) {
return s_driver->enterFullscreenMode(hMonitor, hWindow, pState, modeSwitch);
}
bool leaveFullscreenMode(
HWND hWindow,
DxvkWindowState* pState,
bool restoreCoordinates) {
return s_driver->leaveFullscreenMode(hWindow, pState, restoreCoordinates);
}
bool restoreDisplayMode() {
return s_driver->restoreDisplayMode();
}
HMONITOR getWindowMonitor(HWND hWindow) {
return s_driver->getWindowMonitor(hWindow);
}
bool isWindow(HWND hWindow) {
return s_driver->isWindow(hWindow);
}
void updateFullscreenWindow(
HMONITOR hMonitor,
HWND hWindow,
bool forceTopmost) {
s_driver->updateFullscreenWindow(hMonitor, hWindow, forceTopmost);
}
VkResult createSurface(
HWND hWindow,
PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,
VkInstance instance,
VkSurfaceKHR* pSurface) {
return s_driver->createSurface(hWindow, pfnVkGetInstanceProcAddr, instance, pSurface);
}
HMONITOR getDefaultMonitor() {
return s_driver->getDefaultMonitor();
}
HMONITOR enumMonitors(uint32_t index) {
return s_driver->enumMonitors(index);
}
HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) {
return s_driver->enumMonitors(adapterLUID, numLUIDs, index);
}
bool getDisplayName(
HMONITOR hMonitor,
WCHAR (&Name)[32]) {
return s_driver->getDisplayName(hMonitor, Name);
}
bool getDesktopCoordinates(
HMONITOR hMonitor,
RECT* pRect) {
return s_driver->getDesktopCoordinates(hMonitor, pRect);
}
bool getDisplayMode(
HMONITOR hMonitor,
uint32_t modeNumber,
WsiMode* pMode) {
return s_driver->getDisplayMode(hMonitor, modeNumber, pMode);
}
bool getCurrentDisplayMode(
HMONITOR hMonitor,
WsiMode* pMode) {
return s_driver->getCurrentDisplayMode(hMonitor, pMode);
}
bool getDesktopDisplayMode(
HMONITOR hMonitor,
WsiMode* pMode) {
return s_driver->getDesktopDisplayMode(hMonitor, pMode);
}
WsiEdidData getMonitorEdid(HMONITOR hMonitor) {
return s_driver->getMonitorEdid(hMonitor);
}
}

View File

@ -1,9 +1,114 @@
#pragma once #pragma once
#include "wsi_window.h"
#include <vector>
namespace dxvk::wsi {
class WsiDriver {
public:
virtual ~WsiDriver() {
}
// Platform
virtual std::vector<const char *> getInstanceExtensions() = 0;
// Monitor
virtual HMONITOR getDefaultMonitor() = 0;
virtual HMONITOR enumMonitors(uint32_t index) = 0;
virtual HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) = 0;
virtual bool getDisplayName(
HMONITOR hMonitor,
WCHAR (&Name)[32]) = 0;
virtual bool getDesktopCoordinates(
HMONITOR hMonitor,
RECT* pRect) = 0;
virtual bool getDisplayMode(
HMONITOR hMonitor,
uint32_t modeNumber,
WsiMode* pMode) = 0;
virtual bool getCurrentDisplayMode(
HMONITOR hMonitor,
WsiMode* pMode) = 0;
virtual bool getDesktopDisplayMode(
HMONITOR hMonitor,
WsiMode* pMode) = 0;
virtual WsiEdidData getMonitorEdid(HMONITOR hMonitor) = 0;
// Window
virtual void getWindowSize(
HWND hWindow,
uint32_t* pWidth,
uint32_t* pWeight) = 0;
virtual void resizeWindow(
HWND hWindow,
DxvkWindowState* pState,
uint32_t width,
uint32_t weight) = 0;
virtual bool setWindowMode(
HMONITOR hMonitor,
HWND hWindow,
const WsiMode& mode) = 0;
virtual bool enterFullscreenMode(
HMONITOR hMonitor,
HWND hWindow,
DxvkWindowState* pState,
[[maybe_unused]]
bool modeSwitch) = 0;
virtual bool leaveFullscreenMode(
HWND hWindow,
DxvkWindowState* pState,
bool restoreCoordinates) = 0;
virtual bool restoreDisplayMode() = 0;
virtual HMONITOR getWindowMonitor(HWND hWindow) = 0;
virtual bool isWindow(HWND hWindow) = 0;
virtual void updateFullscreenWindow(
HMONITOR hMonitor,
HWND hWindow,
bool forceTopmost) = 0;
virtual VkResult createSurface(
HWND hWindow,
PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,
VkInstance instance,
VkSurfaceKHR* pSurface) = 0;
};
struct WsiBootstrap {
const std::string name;
bool (*createDriver)(WsiDriver **driver);
};
#if defined(DXVK_WSI_WIN32) #if defined(DXVK_WSI_WIN32)
#include "win32/wsi_platform_win32.h" extern WsiBootstrap Win32WSI;
#elif defined(DXVK_WSI_SDL2)
#include "sdl2/wsi_platform_sdl2.h"
#elif defined(DXVK_WSI_GLFW)
#include "glfw/wsi_platform_glfw.h"
#endif #endif
#if defined(DXVK_WSI_SDL2)
extern WsiBootstrap Sdl2WSI;
#endif
#if defined(DXVK_WSI_GLFW)
extern WsiBootstrap GlfwWSI;
#endif
void init();
void quit();
std::vector<const char *> getInstanceExtensions();
}

View File

@ -3,12 +3,30 @@
#include <windows.h> #include <windows.h>
#include "wsi_monitor.h" #include "wsi_monitor.h"
#include "wsi_platform.h"
#include "../vulkan/vulkan_loader.h" #include "../vulkan/vulkan_loader.h"
namespace dxvk::wsi { namespace dxvk::wsi {
/**
* \brief Impl-dependent state
*/
struct DxvkWindowState {
#if defined(DXVK_WSI_WIN32)
struct {
LONG style = 0;
LONG exstyle = 0;
RECT rect = { 0, 0, 0, 0 };
} win;
#endif
#if defined(DXVK_WSI_SDL2)
// Nothing to store
#endif
#if defined(DXVK_WSI_GLFW)
// Nothing to store
#endif
};
/** /**
* \brief The size of the window * \brief The size of the window
* *