mirror of https://github.com/doitsujin/dxvk
Compare commits
73 Commits
Author | SHA1 | Date |
---|---|---|
Blisto91 | 8d965359a5 | |
Philip Rebohle | fd978704fb | |
Tiagoquix | ee18aecb8a | |
Blisto91 | c2fd91f835 | |
Tiagoquix | 79eea564fb | |
Robin Kertels | 7df8017e46 | |
WinterSnowfall | c98152683f | |
Blisto91 | 890ad3f47f | |
Robin Kertels | 60cfafe027 | |
Robin Kertels | 889802887f | |
WinterSnowfall | a1ce690c5c | |
WinterSnowfall | 07d007c642 | |
Danylo Piliaiev | 58d8ea2d31 | |
Philip Rebohle | 61bd62c327 | |
Philip Rebohle | 7bc77b597e | |
Simon McVittie | 2ff2c826a5 | |
Simon McVittie | f9b4046223 | |
Simon McVittie | 83436a97f2 | |
Ethan Lee | e991bfa604 | |
Ethan Lee | f33453afbb | |
Robin Kertels | 65dd3c7df3 | |
Robin Kertels | dfc3776b24 | |
Philip Rebohle | 3420cd78ac | |
Philip Rebohle | 4225f35034 | |
Philip Rebohle | 2cb2f8694e | |
Blisto91 | c1f665f92b | |
WinterSnowfall | 20185a5309 | |
Blisto91 | 0c2efda804 | |
Blisto91 | c7d61b2fc0 | |
Ethan Lee | 6259e86392 | |
Ethan Lee | d5d236a1e2 | |
Ethan Lee | 10b83d184b | |
Ethan Lee | 0f7c1f753a | |
Ethan Lee | 529129c332 | |
Ethan Lee | 4055a92856 | |
Blisto91 | 7bad17c1d1 | |
Blisto91 | 6b76d70d9d | |
Philip Rebohle | 611dc60018 | |
WinterSnowfall | b2789ab894 | |
Philip Rebohle | ab715a8876 | |
talkingerbil | 1fb35b6d19 | |
Rémi Bernon | 4333ee872d | |
Rémi Bernon | b99d42c688 | |
Blisto91 | dacb8b434b | |
Philip Rebohle | ea4cb84d8a | |
Philip Rebohle | 65373792d2 | |
Lierrmm | 29253da356 | |
Robin Kertels | 79398b468d | |
Robin Kertels | e7d14e97de | |
Philip Rebohle | c613078ba8 | |
Philip Rebohle | 2970645f33 | |
Philip Rebohle | 462165da19 | |
Philip Rebohle | 3f27a0ee58 | |
Katharine Chui | aac3396671 | |
Katharine Chui | 92a43ebf65 | |
Blisto91 | 8ba5256dc7 | |
Philip Rebohle | 2b70ba8f77 | |
Philip Rebohle | 9c66c4bf1d | |
Philip Rebohle | 00872e9e4f | |
Philip Rebohle | 35157357dd | |
Philip Rebohle | 617ebf4e05 | |
Philip Rebohle | c2489d5a45 | |
Philip Rebohle | 6ef98c613f | |
Philip Rebohle | 7441137a33 | |
WinterSnowfall | 571948cfc0 | |
Martino Fontana | 133f0794bc | |
Philip Rebohle | 44695f9311 | |
Casey Bowman | 49e9ea5f5a | |
Blisto91 | 198bd3a4b8 | |
Philip Rebohle | f06c646315 | |
Philip Rebohle | 855b2746b6 | |
Blisto91 | 28c7c09bf5 | |
Philip Rebohle | 2742486540 |
|
@ -9,13 +9,13 @@ jobs:
|
|||
steps:
|
||||
- name: Checkout code
|
||||
id: checkout-code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup problem matcher
|
||||
uses: Joshua-Ashton/gcc-problem-matcher@v2
|
||||
uses: Joshua-Ashton/gcc-problem-matcher@v3
|
||||
|
||||
- name: Build release
|
||||
id: build-release
|
||||
|
@ -28,9 +28,9 @@ jobs:
|
|||
|
||||
- name: Upload artifacts
|
||||
id: upload-artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dxvk-${{ env.VERSION_NAME }}
|
||||
name: dxvk-win-${{ env.VERSION_NAME }}
|
||||
path: build/dxvk-${{ env.VERSION_NAME }}
|
||||
if-no-files-found: error
|
||||
|
||||
|
@ -41,26 +41,43 @@ jobs:
|
|||
steps:
|
||||
- name: Checkout code
|
||||
id: checkout-code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup problem matcher
|
||||
uses: Joshua-Ashton/gcc-problem-matcher@v2
|
||||
uses: Joshua-Ashton/gcc-problem-matcher@v3
|
||||
|
||||
- name: Build release
|
||||
id: build-release
|
||||
shell: bash
|
||||
run: |
|
||||
export VERSION_NAME="${GITHUB_REF##*/}-${GITHUB_SHA##*/}"
|
||||
./package-native.sh ${VERSION_NAME} build --no-package
|
||||
./package-native.sh ${VERSION_NAME} build
|
||||
echo "VERSION_NAME=${VERSION_NAME}" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload artifacts
|
||||
id: upload-artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dxvk-${{ env.VERSION_NAME }}
|
||||
path: build/dxvk-native-${{ env.VERSION_NAME }}
|
||||
name: dxvk-native-${{ env.VERSION_NAME }}
|
||||
path: build/dxvk-native-${{ env.VERSION_NAME }}.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
merge-artifacts:
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [artifacts-mingw-w64, artifacts-steamrt-sniper]
|
||||
steps:
|
||||
- name: Get version
|
||||
id: get-version
|
||||
shell: bash
|
||||
run: |
|
||||
echo "VERSION_NAME=${GITHUB_REF##*/}-${GITHUB_SHA##*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Merge Artifacts
|
||||
uses: actions/upload-artifact/merge@v4
|
||||
with:
|
||||
name: dxvk-${{ env.VERSION_NAME }}
|
||||
pattern: dxvk*
|
||||
delete-merged: true
|
||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
|||
steps:
|
||||
- name: Checkout code
|
||||
id: checkout-code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
|
|
19
README.md
19
README.md
|
@ -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
|
||||
ship its mingw-w64-gcc binary with `--enable-threads=win32` you might have to
|
||||
recompile locally or open a bug at your distro's bugtracker to ask for it.
|
||||
|
||||
# DXVK Native
|
||||
|
||||
DXVK Native is a version of DXVK which allows it to be used natively without Wine.
|
||||
|
||||
This is primarily useful for game and application ports to either avoid having to write another rendering backend, or to help with port bringup during development.
|
||||
|
||||
[Release builds](https://github.com/doitsujin/dxvk/releases) are built using the Steam Runtime.
|
||||
|
||||
### How does it work?
|
||||
|
||||
DXVK Native replaces certain Windows-isms with a platform and framework-agnostic replacement, for example, `HWND`s can become `SDL_Window*`s, etc.
|
||||
All it takes to do that is to add another WSI backend.
|
||||
|
||||
**Note:** DXVK Native requires a backend to be explicitly set via the `DXVK_WSI_DRIVER` environment variable. The current built-in options are `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.
|
||||
|
|
39
dxvk.conf
39
dxvk.conf
|
@ -1,3 +1,10 @@
|
|||
# Device filter. Only exposes devices whose Vulkan device name contains
|
||||
# the given string. May be useful to force an application to run on a
|
||||
# specific GPU, but not applications launched by that application.
|
||||
|
||||
# dxvk.deviceFilter = ""
|
||||
|
||||
|
||||
# Expose the HDR10 ColorSpace (DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020)
|
||||
# to the application by default.
|
||||
# This shows to the game that the global Windows 'HDR Mode' is enabled.
|
||||
|
@ -10,6 +17,7 @@
|
|||
|
||||
# dxgi.enableHDR = True
|
||||
|
||||
|
||||
# Create the VkSurface on the first call to IDXGISwapChain::Present,
|
||||
# rather than when creating the swap chain. Some games that start
|
||||
# rendering with a different graphics API may require this option,
|
||||
|
@ -312,6 +320,20 @@
|
|||
# d3d11.exposeDriverCommandLists = True
|
||||
|
||||
|
||||
# Reproducible Command Stream
|
||||
#
|
||||
# Ensure that for the same D3D commands the output VK commands
|
||||
# don't change between runs. Useful for comparative benchmarking,
|
||||
# can negatively affect performance and can break some games
|
||||
# that don't use queries correctly.
|
||||
#
|
||||
# Supported values:
|
||||
# - True/False
|
||||
|
||||
# d3d11.reproducibleCommandStream = False
|
||||
# d3d9.reproducibleCommandStream = False
|
||||
|
||||
|
||||
# Sets number of pipeline compiler threads.
|
||||
#
|
||||
# If the graphics pipeline library feature is enabled, the given
|
||||
|
@ -499,6 +521,7 @@
|
|||
# Supported values:
|
||||
# - True/False
|
||||
|
||||
# d3d11.longMad = False
|
||||
# d3d9.longMad = False
|
||||
|
||||
# Device Local Constant Buffers
|
||||
|
@ -514,7 +537,9 @@
|
|||
|
||||
# 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:
|
||||
# - True/False
|
||||
|
@ -644,18 +669,6 @@
|
|||
|
||||
# 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
|
||||
#
|
||||
# Only has an effect when dedicated GPUs are present on the system. It is
|
||||
|
|
|
@ -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',
|
||||
)
|
|
@ -1,6 +1,6 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL.h>
|
||||
|
||||
namespace dxvk::wsi {
|
||||
|
||||
|
@ -22,4 +22,4 @@ namespace dxvk::wsi {
|
|||
return reinterpret_cast<HMONITOR>(static_cast<intptr_t>(displayId + 1));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
32
meson.build
32
meson.build
|
@ -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' ])
|
||||
|
||||
pkg = import('pkgconfig')
|
||||
cpu_family = target_machine.cpu_family()
|
||||
platform = target_machine.system()
|
||||
fs = import('fs')
|
||||
|
@ -54,6 +55,8 @@ dep_displayinfo = dependency(
|
|||
)
|
||||
|
||||
if platform == 'windows'
|
||||
dxvk_so_version = {'name_prefix': ''}
|
||||
|
||||
compiler_args += [
|
||||
'-DNOMINMAX',
|
||||
'-D_WIN32_WINNT=0xa00',
|
||||
|
@ -115,10 +118,13 @@ if platform == 'windows'
|
|||
)
|
||||
endif
|
||||
|
||||
dxvk_wsi = 'win32'
|
||||
dxvk_name_prefix = ''
|
||||
compiler_args += ['-DDXVK_WSI_WIN32']
|
||||
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_generator = generator(wrc, output : [ '@BASENAME@_ignored.h' ], arguments : [ '@OUTPUT@' ] )
|
||||
|
||||
|
@ -128,17 +134,20 @@ else
|
|||
'./include/native/directx'
|
||||
]
|
||||
|
||||
dxvk_wsi = get_option('dxvk_native_wsi')
|
||||
|
||||
if dxvk_wsi == 'sdl2'
|
||||
lib_sdl2 = cpp.find_library('SDL2')
|
||||
lib_sdl2 = dependency('SDL2', required: false)
|
||||
lib_glfw = dependency('glfw', required: false)
|
||||
if lib_sdl2.found()
|
||||
compiler_args += ['-DDXVK_WSI_SDL2']
|
||||
elif dxvk_wsi == 'glfw'
|
||||
lib_glfw = cpp.find_library('glfw')
|
||||
endif
|
||||
if lib_glfw.found()
|
||||
compiler_args += ['-DDXVK_WSI_GLFW']
|
||||
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 += [
|
||||
'-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')
|
||||
|
||||
exe_ext = ''
|
||||
dll_ext = ''
|
||||
def_spec_ext = '.def'
|
||||
|
||||
glsl_compiler = find_program('glslang', 'glslangValidator')
|
||||
glsl_args = [
|
||||
'--quiet',
|
||||
'--target-env', 'vulkan1.2',
|
||||
'--target-env', 'vulkan1.3',
|
||||
'--vn', '@BASENAME@',
|
||||
'--depfile', '@DEPFILE@',
|
||||
'@INPUT@',
|
||||
|
@ -179,4 +187,8 @@ dxvk_version = vcs_tag(
|
|||
output: 'version.h',
|
||||
)
|
||||
|
||||
if platform != 'windows'
|
||||
subdir('include/native')
|
||||
endif
|
||||
|
||||
subdir('src')
|
||||
|
|
|
@ -15,16 +15,23 @@ else
|
|||
d3d10_d3d11_dep = d3d11_dep
|
||||
endif
|
||||
|
||||
d3d10_core_dll = shared_library('d3d10core'+dll_ext, d3d10_core_src, d3d10_core_res,
|
||||
name_prefix : dxvk_name_prefix,
|
||||
d3d10_core_dll = shared_library(dxvk_name_prefix+'d3d10core', d3d10_core_src, d3d10_core_res,
|
||||
dependencies : [ d3d10_d3d11_dep ],
|
||||
include_directories : dxvk_include_path,
|
||||
install : true,
|
||||
vs_module_defs : 'd3d10core'+def_spec_ext,
|
||||
link_args : d3d10_core_ld_args,
|
||||
link_depends : [ d3d10_core_link_depends ],
|
||||
kwargs : dxvk_so_version,
|
||||
)
|
||||
|
||||
d3d10_core_dep = declare_dependency(
|
||||
link_with : [ d3d10_core_dll ],
|
||||
)
|
||||
|
||||
if platform != 'windows'
|
||||
pkg.generate(d3d10_core_dll,
|
||||
filebase: dxvk_pkg_prefix + 'd3d10core',
|
||||
subdirs: 'dxvk',
|
||||
)
|
||||
endif
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace dxvk {
|
|||
m_csThread(Device, Device->createContext(DxvkContextType::Primary)),
|
||||
m_maxImplicitDiscardSize(pParent->GetOptions()->maxImplicitDiscardSize),
|
||||
m_submissionFence(new sync::CallbackFence()),
|
||||
m_flushTracker(pParent->GetOptions()->reproducibleCommandStream),
|
||||
m_multithread(this, false, pParent->GetOptions()->enableContextLock),
|
||||
m_videoContext(this, Device) {
|
||||
EmitCs([
|
||||
|
|
|
@ -32,6 +32,8 @@ namespace dxvk {
|
|||
this->maxFrameLatency = config.getOption<int32_t>("dxgi.maxFrameLatency", 0);
|
||||
this->maxFrameRate = config.getOption<int32_t>("dxgi.maxFrameRate", 0);
|
||||
this->exposeDriverCommandLists = config.getOption<bool>("d3d11.exposeDriverCommandLists", true);
|
||||
this->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
|
||||
this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f);
|
||||
|
|
|
@ -120,6 +120,14 @@ namespace dxvk {
|
|||
|
||||
/// Shader dump path
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
|
@ -58,9 +58,6 @@ namespace dxvk {
|
|||
"\n MiscFlags: ", m_desc.MiscFlags,
|
||||
"\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.sharing.mode = hSharedHandle == INVALID_HANDLE_VALUE ? DxvkSharedHandleMode::Export : DxvkSharedHandleMode::Import;
|
||||
imageInfo.sharing.type = (m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE)
|
||||
|
|
|
@ -1262,12 +1262,28 @@ namespace dxvk {
|
|||
viewport.height = float(cStreamState.dstRect.bottom) - viewport.y;
|
||||
}
|
||||
|
||||
VkExtent3D viewExtent = cViews[0]->mipLevelExtent(0);
|
||||
|
||||
VkRect2D srcRect;
|
||||
srcRect.offset = { 0, 0 };
|
||||
srcRect.extent = { viewExtent.width, viewExtent.height };
|
||||
|
||||
if (cStreamState.srcRectEnabled) {
|
||||
srcRect.offset.x = cStreamState.srcRect.left;
|
||||
srcRect.offset.y = cStreamState.srcRect.top;
|
||||
srcRect.extent.width = cStreamState.srcRect.right - srcRect.offset.x;
|
||||
srcRect.extent.height = cStreamState.srcRect.bottom - srcRect.offset.y;
|
||||
}
|
||||
|
||||
UboData uboData = { };
|
||||
uboData.colorMatrix[0][0] = 1.0f;
|
||||
uboData.colorMatrix[1][1] = 1.0f;
|
||||
uboData.colorMatrix[2][2] = 1.0f;
|
||||
uboData.coordMatrix[0][0] = 1.0f;
|
||||
uboData.coordMatrix[1][1] = 1.0f;
|
||||
uboData.coordMatrix[0][0] = float(srcRect.extent.width) / float(viewExtent.width);
|
||||
uboData.coordMatrix[1][1] = float(srcRect.extent.height) / float(viewExtent.height);
|
||||
uboData.coordMatrix[2][0] = float(srcRect.offset.x) / float(viewExtent.width);
|
||||
uboData.coordMatrix[2][1] = float(srcRect.offset.y) / float(viewExtent.height);
|
||||
uboData.srcRect = srcRect;
|
||||
uboData.yMin = 0.0f;
|
||||
uboData.yMax = 1.0f;
|
||||
uboData.isPlanar = cViews[1] != nullptr;
|
||||
|
@ -1290,17 +1306,14 @@ namespace dxvk {
|
|||
ctx->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_fs));
|
||||
|
||||
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++)
|
||||
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->bindResourceSampler(VK_SHADER_STAGE_FRAGMENT_BIT, 1, nullptr);
|
||||
|
||||
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() {
|
||||
SpirvCodeBuffer vsCode(d3d11_video_blit_vert);
|
||||
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_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, 3, VK_IMAGE_VIEW_TYPE_2D, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT },
|
||||
}};
|
||||
|
||||
DxvkShaderCreateInfo vsInfo;
|
||||
|
@ -1368,7 +1357,6 @@ namespace dxvk {
|
|||
if (std::exchange(m_resourcesCreated, true))
|
||||
return;
|
||||
|
||||
CreateSampler();
|
||||
CreateUniformBuffer();
|
||||
CreateShaders();
|
||||
}
|
||||
|
|
|
@ -584,6 +584,7 @@ namespace dxvk {
|
|||
struct alignas(16) UboData {
|
||||
float colorMatrix[3][4];
|
||||
float coordMatrix[3][2];
|
||||
VkRect2D srcRect;
|
||||
float yMin, yMax;
|
||||
VkBool32 isPlanar;
|
||||
};
|
||||
|
@ -593,7 +594,6 @@ namespace dxvk {
|
|||
Rc<DxvkDevice> m_device;
|
||||
Rc<DxvkShader> m_vs;
|
||||
Rc<DxvkShader> m_fs;
|
||||
Rc<DxvkSampler> m_sampler;
|
||||
Rc<DxvkBuffer> m_ubo;
|
||||
|
||||
VkExtent2D m_dstExtent = { 0u, 0u };
|
||||
|
@ -613,8 +613,6 @@ namespace dxvk {
|
|||
|
||||
void CreateUniformBuffer();
|
||||
|
||||
void CreateSampler();
|
||||
|
||||
void CreateShaders();
|
||||
|
||||
void CreateResources();
|
||||
|
|
|
@ -77,18 +77,25 @@ else
|
|||
d3d11_dxgi_dep = dxgi_dep
|
||||
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,
|
||||
name_prefix : dxvk_name_prefix,
|
||||
dependencies : [ d3d11_dxgi_dep, dxbc_dep, dxvk_dep ],
|
||||
include_directories : dxvk_include_path,
|
||||
install : true,
|
||||
vs_module_defs : 'd3d11'+def_spec_ext,
|
||||
link_args : d3d11_ld_args,
|
||||
link_depends : [ d3d11_link_depends ],
|
||||
kwargs : dxvk_so_version,
|
||||
)
|
||||
|
||||
d3d11_dep = declare_dependency(
|
||||
link_with : [ d3d11_dll ],
|
||||
include_directories : [ dxvk_include_path ],
|
||||
)
|
||||
|
||||
if platform != 'windows'
|
||||
pkg.generate(d3d11_dll,
|
||||
filebase: dxvk_pkg_prefix + 'd3d11',
|
||||
subdirs: 'dxvk',
|
||||
)
|
||||
endif
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#version 450
|
||||
|
||||
#extension GL_EXT_samplerless_texture_functions : require
|
||||
|
||||
// Can't use matrix types here since even a two-row
|
||||
// matrix will be padded to 16 bytes per column for
|
||||
// absolutely no reason
|
||||
|
@ -11,6 +13,8 @@ uniform ubo_t {
|
|||
vec2 coord_matrix_c1;
|
||||
vec2 coord_matrix_c2;
|
||||
vec2 coord_matrix_c3;
|
||||
uvec2 src_offset;
|
||||
uvec2 src_extent;
|
||||
float y_min;
|
||||
float y_max;
|
||||
bool is_planar;
|
||||
|
@ -19,9 +23,8 @@ uniform ubo_t {
|
|||
layout(location = 0) in vec2 i_texcoord;
|
||||
layout(location = 0) out vec4 o_color;
|
||||
|
||||
layout(set = 0, binding = 1) uniform sampler s_sampler;
|
||||
layout(set = 0, binding = 2) uniform texture2D s_inputY;
|
||||
layout(set = 0, binding = 3) uniform texture2D s_inputCbCr;
|
||||
layout(set = 0, binding = 1) uniform texture2D s_inputY;
|
||||
layout(set = 0, binding = 2) uniform texture2D s_inputCbCr;
|
||||
|
||||
void main() {
|
||||
// Transform input texture coordinates to
|
||||
|
@ -31,25 +34,61 @@ void main() {
|
|||
coord_matrix_c2,
|
||||
coord_matrix_c3);
|
||||
|
||||
vec2 coord = coord_matrix * vec3(i_texcoord, 1.0f);
|
||||
|
||||
// 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
|
||||
// Load color space transform
|
||||
mat3x4 color_matrix = mat3x4(
|
||||
color_matrix_r1,
|
||||
color_matrix_r2,
|
||||
color_matrix_r3);
|
||||
|
||||
o_color.rgb = vec4(color.rgb, 1.0f) * color_matrix;
|
||||
o_color.a = color.a;
|
||||
// Compute actual pixel coordinates to sample. We filter
|
||||
// manually in order to avoid bleeding from pixels outside
|
||||
// the source rectangle.
|
||||
vec2 abs_size_y = vec2(textureSize(s_inputY, 0));
|
||||
vec2 abs_size_c = vec2(textureSize(s_inputCbCr, 0));
|
||||
|
||||
vec2 coord = coord_matrix * vec3(i_texcoord, 1.0f);
|
||||
coord -= 0.5f / abs_size_y;
|
||||
|
||||
vec2 size_factor = abs_size_c / abs_size_y;
|
||||
|
||||
vec2 src_lo = vec2(src_offset);
|
||||
vec2 src_hi = vec2(src_offset + src_extent - 1u);
|
||||
|
||||
vec2 abs_coord = coord * abs_size_y;
|
||||
vec2 fract_coord = fract(clamp(abs_coord, src_lo, src_hi));
|
||||
|
||||
vec4 accum = vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ivec2 offset = ivec2(i & 1, i >> 1);
|
||||
|
||||
// Compute exact pixel coordinates for the current
|
||||
// iteration and clamp it to the source rectangle.
|
||||
vec2 fetch_coord = clamp(abs_coord + vec2(offset), src_lo, src_hi);
|
||||
|
||||
// Fetch actual pixel color in source color space
|
||||
vec4 color;
|
||||
|
||||
if (is_planar) {
|
||||
color.g = texelFetch(s_inputY, ivec2(fetch_coord), 0).r;
|
||||
color.rb = texelFetch(s_inputCbCr, ivec2(fetch_coord * size_factor), 0).gr;
|
||||
color.g = clamp((color.g - y_min) / (y_max - y_min), 0.0f, 1.0f);
|
||||
color.a = 1.0f;
|
||||
} else {
|
||||
color = texelFetch(s_inputY, ivec2(fetch_coord), 0);
|
||||
}
|
||||
|
||||
// Transform color space before accumulation
|
||||
color.rgb = vec4(color.rgb, 1.0f) * color_matrix;
|
||||
|
||||
// Filter and accumulate final pixel color
|
||||
vec2 factor = fract_coord;
|
||||
|
||||
if (offset.x == 0) factor.x = 1.0f - factor.x;
|
||||
if (offset.y == 0) factor.y = 1.0f - factor.y;
|
||||
|
||||
accum += factor.x * factor.y * color;
|
||||
}
|
||||
|
||||
o_color = accum;
|
||||
}
|
||||
|
|
|
@ -162,6 +162,9 @@ namespace dxvk {
|
|||
if (mapping.FormatSrgb == VK_FORMAT_UNDEFINED && srgb)
|
||||
return D3DERR_NOTAVAILABLE;
|
||||
|
||||
if (RType == D3DRTYPE_CUBETEXTURE && mapping.Aspect != VK_IMAGE_ASPECT_COLOR_BIT)
|
||||
return D3DERR_NOTAVAILABLE;
|
||||
|
||||
if (RType == D3DRTYPE_VERTEXBUFFER || RType == D3DRTYPE_INDEXBUFFER)
|
||||
return D3D_OK;
|
||||
|
||||
|
@ -224,11 +227,15 @@ namespace dxvk {
|
|||
if (!IsDepthFormat(DepthStencilFormat))
|
||||
return D3DERR_NOTAVAILABLE;
|
||||
|
||||
auto dsfMapping = ConvertFormatUnfixed(DepthStencilFormat);
|
||||
if (dsfMapping.FormatColor == VK_FORMAT_UNDEFINED)
|
||||
return D3DERR_NOTAVAILABLE;
|
||||
|
||||
if (RenderTargetFormat == dxvk::D3D9Format::NULL_FORMAT)
|
||||
return D3D_OK;
|
||||
|
||||
auto mapping = ConvertFormatUnfixed(RenderTargetFormat);
|
||||
if (mapping.FormatColor == VK_FORMAT_UNDEFINED)
|
||||
auto rtfMapping = ConvertFormatUnfixed(RenderTargetFormat);
|
||||
if (rtfMapping.FormatColor == VK_FORMAT_UNDEFINED)
|
||||
return D3DERR_NOTAVAILABLE;
|
||||
|
||||
return D3D_OK;
|
||||
|
|
|
@ -118,6 +118,7 @@ namespace dxvk {
|
|||
|
||||
HRESULT D3D9CommonTexture::NormalizeTextureProperties(
|
||||
D3D9DeviceEx* pDevice,
|
||||
D3DRESOURCETYPE ResourceType,
|
||||
D3D9_COMMON_TEXTURE_DESC* pDesc) {
|
||||
auto* options = pDevice->GetOptions();
|
||||
|
||||
|
@ -131,6 +132,11 @@ namespace dxvk {
|
|||
options->disableA8RT)
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// Cube textures with depth formats are not supported on any native
|
||||
// driver, and allowing them triggers a broken code path in Gothic 3.
|
||||
if (ResourceType == D3DRTYPE_CUBETEXTURE && mapping.Aspect != VK_IMAGE_ASPECT_COLOR_BIT)
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// If the mapping is invalid then lets return invalid
|
||||
// Some edge cases:
|
||||
// NULL format does not map to anything, but should succeed
|
||||
|
|
|
@ -179,11 +179,14 @@ namespace dxvk {
|
|||
* Fills in undefined values and validates the texture
|
||||
* parameters. Any error returned by this method should
|
||||
* be forwarded to the application.
|
||||
* \param [in] pDevice D3D9 device
|
||||
* \param [in] ResourceType Resource type
|
||||
* \param [in,out] pDesc Texture description
|
||||
* \returns \c S_OK if the parameters are valid
|
||||
*/
|
||||
static HRESULT NormalizeTextureProperties(
|
||||
D3D9DeviceEx* pDevice,
|
||||
D3DRESOURCETYPE ResourceType,
|
||||
D3D9_COMMON_TEXTURE_DESC* pDesc);
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace dxvk {
|
|||
DxsoProgramType ShaderStage,
|
||||
DxsoConstantBuffers BufferType,
|
||||
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),
|
||||
Size) {
|
||||
|
||||
|
@ -135,21 +135,4 @@ namespace dxvk {
|
|||
device->properties().extRobustness2.robustUniformBufferAccessSizeAlignment);
|
||||
}
|
||||
|
||||
|
||||
VkBufferUsageFlags D3D9ConstantBuffer::getBufferUsage(
|
||||
D3D9DeviceEx* pDevice,
|
||||
DxsoProgramType ShaderStage,
|
||||
DxsoConstantBuffers BufferType) {
|
||||
VkBufferUsageFlags result = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
||||
|
||||
// We won't always need this, but the only buffer that
|
||||
// this applies to is so large that it will not matter.
|
||||
if (pDevice->CanSWVP()
|
||||
&& ShaderStage == DxsoProgramType::VertexShader
|
||||
&& BufferType == DxsoConstantBuffers::VSConstantBuffer)
|
||||
result |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -80,11 +80,6 @@ namespace dxvk {
|
|||
|
||||
VkDeviceSize getAlignment(const Rc<DxvkDevice>& device) const;
|
||||
|
||||
static VkBufferUsageFlags getBufferUsage(
|
||||
D3D9DeviceEx* pDevice,
|
||||
DxsoProgramType ShaderStage,
|
||||
DxsoConstantBuffers BufferType);
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -56,6 +56,7 @@ namespace dxvk {
|
|||
, m_csThread ( dxvkDevice, dxvkDevice->createContext(DxvkContextType::Primary) )
|
||||
, m_csChunk ( AllocCsChunk() )
|
||||
, m_submissionFence (new sync::Fence())
|
||||
, m_flushTracker (m_d3d9Options.reproducibleCommandStream)
|
||||
, m_d3d9Interop ( this )
|
||||
, m_d3d9On12 ( this )
|
||||
, m_d3d8Bridge ( this ) {
|
||||
|
@ -594,7 +595,7 @@ namespace dxvk {
|
|||
|| (Usage & D3DUSAGE_DYNAMIC)
|
||||
|| IsVendorFormat(EnumerateFormat(Format));
|
||||
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
try {
|
||||
|
@ -664,7 +665,7 @@ namespace dxvk {
|
|||
|| (Usage & D3DUSAGE_DYNAMIC)
|
||||
|| IsVendorFormat(EnumerateFormat(Format));
|
||||
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_VOLUMETEXTURE, &desc)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
try {
|
||||
|
@ -721,7 +722,7 @@ namespace dxvk {
|
|||
|| (Usage & D3DUSAGE_DYNAMIC)
|
||||
|| IsVendorFormat(EnumerateFormat(Format));
|
||||
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_CUBETEXTURE, &desc)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
try {
|
||||
|
@ -3798,7 +3799,7 @@ namespace dxvk {
|
|||
desc.IsAttachmentOnly = TRUE;
|
||||
desc.IsLockable = Lockable;
|
||||
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
try {
|
||||
|
@ -3846,7 +3847,7 @@ namespace dxvk {
|
|||
// Docs: Off-screen plain surfaces are always lockable, regardless of their pool types.
|
||||
desc.IsLockable = TRUE;
|
||||
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
if (pSharedHandle != nullptr && Pool != D3DPOOL_DEFAULT)
|
||||
|
@ -3901,7 +3902,7 @@ namespace dxvk {
|
|||
// Docs don't say anything, so just assume it's lockable.
|
||||
desc.IsLockable = TRUE;
|
||||
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
try {
|
||||
|
@ -5158,7 +5159,7 @@ namespace dxvk {
|
|||
return D3D_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void D3D9DeviceEx::UploadDynamicSysmemBuffers(
|
||||
UINT& FirstVertexIndex,
|
||||
|
@ -5190,25 +5191,58 @@ namespace dxvk {
|
|||
// First we calculate the size of that UP buffer slice
|
||||
// and store all sizes and offsets into it.
|
||||
|
||||
uint32_t upBufferSize = 0;
|
||||
std::array<uint32_t, caps::MaxStreams> vboUPBufferOffsets = {};
|
||||
std::array<uint32_t, caps::MaxStreams> vboUPBufferSizes = {};
|
||||
for (uint32_t i = 0; i < caps::MaxStreams && dynamicSysmemVBOs; i++) {
|
||||
vboUPBufferOffsets[i] = upBufferSize;
|
||||
struct VBOCopy {
|
||||
uint32_t srcOffset;
|
||||
uint32_t dstOffset;
|
||||
uint32_t copyBufferLength;
|
||||
uint32_t copyElementCount;
|
||||
uint32_t copyElementSize;
|
||||
uint32_t copyElementStride;
|
||||
};
|
||||
uint32_t totalUpBufferSize = 0;
|
||||
std::array<VBOCopy, caps::MaxStreams> vboCopies = {};
|
||||
|
||||
for (uint32_t i = 0; i < caps::MaxStreams && dynamicSysmemVBOs; i++) {
|
||||
auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer);
|
||||
if (likely(vbo == nullptr)) {
|
||||
vboUPBufferSizes[i] = 0;
|
||||
continue;
|
||||
}
|
||||
const uint32_t vertexStride = m_state.vertexDecl->GetSize(i);
|
||||
uint32_t offset = (FirstVertexIndex + BaseVertexIndex) * vertexStride;
|
||||
const uint32_t vertexBufferSize = vbo->Desc()->Size;
|
||||
if (offset < vertexBufferSize) {
|
||||
const uint32_t vertexDataSize = std::min(NumVertices * vertexStride, vertexBufferSize - offset);
|
||||
vboUPBufferSizes[i] = vertexDataSize;
|
||||
upBufferSize += vertexDataSize;
|
||||
const uint32_t vertexSize = m_state.vertexDecl->GetSize(i);
|
||||
const uint32_t vertexStride = m_state.vertexBuffers[i].stride;
|
||||
const uint32_t srcStride = vertexStride;
|
||||
const uint32_t dstStride = std::min(vertexStride, vertexSize);
|
||||
|
||||
uint32_t elementCount = NumVertices;
|
||||
if (m_state.streamFreq[i] & D3DSTREAMSOURCE_INSTANCEDATA) {
|
||||
elementCount = GetInstanceCount();
|
||||
}
|
||||
const uint32_t vboOffset = m_state.vertexBuffers[i].offset;
|
||||
const uint32_t vertexOffset = (FirstVertexIndex + BaseVertexIndex) * srcStride;
|
||||
const uint32_t vertexBufferSize = vbo->Desc()->Size;
|
||||
const uint32_t srcOffset = vboOffset + vertexOffset;
|
||||
|
||||
if (unlikely(srcOffset > vertexBufferSize)) {
|
||||
// All vertices are out of bounds
|
||||
vboCopies[i].copyBufferLength = 0;
|
||||
} else if (unlikely(srcOffset + elementCount * srcStride > vertexBufferSize)) {
|
||||
// Some vertices are (partially) out of bounds
|
||||
uint32_t boundVertexBufferRange = vertexBufferSize - vboOffset;
|
||||
elementCount = boundVertexBufferRange / srcStride;
|
||||
// Copy all complete vertices
|
||||
vboCopies[i].copyBufferLength = elementCount * dstStride;
|
||||
// Copy the remaining partial vertex
|
||||
vboCopies[i].copyBufferLength += std::min(dstStride, boundVertexBufferRange % srcStride);
|
||||
} else {
|
||||
// No vertices are out of bounds
|
||||
vboCopies[i].copyBufferLength = elementCount * dstStride;
|
||||
}
|
||||
|
||||
vboCopies[i].copyElementCount = elementCount;
|
||||
vboCopies[i].copyElementStride = srcStride;
|
||||
vboCopies[i].copyElementSize = dstStride;
|
||||
vboCopies[i].srcOffset = srcOffset;
|
||||
vboCopies[i].dstOffset = totalUpBufferSize;
|
||||
totalUpBufferSize += vboCopies[i].copyBufferLength;
|
||||
}
|
||||
|
||||
uint32_t iboUPBufferSize = 0;
|
||||
|
@ -5221,13 +5255,13 @@ namespace dxvk {
|
|||
uint32_t indexBufferSize = ibo->Desc()->Size;
|
||||
if (offset < indexBufferSize) {
|
||||
iboUPBufferSize = std::min(NumIndices * indexStride, indexBufferSize - offset);
|
||||
iboUPBufferOffset = upBufferSize;
|
||||
upBufferSize += iboUPBufferSize;
|
||||
iboUPBufferOffset = totalUpBufferSize;
|
||||
totalUpBufferSize += iboUPBufferSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(upBufferSize == 0)) {
|
||||
if (unlikely(totalUpBufferSize == 0)) {
|
||||
*pDynamicVBOs = false;
|
||||
if (pDynamicIBO)
|
||||
*pDynamicIBO = false;
|
||||
|
@ -5235,47 +5269,49 @@ namespace dxvk {
|
|||
return;
|
||||
}
|
||||
|
||||
auto upSlice = AllocUPBuffer(upBufferSize);
|
||||
auto upSlice = AllocUPBuffer(totalUpBufferSize);
|
||||
|
||||
// Now copy the actual data and bind it.
|
||||
if (dynamicSysmemVBOs) {
|
||||
for (uint32_t i = 0; i < caps::MaxStreams; i++) {
|
||||
if (unlikely(vboUPBufferSizes[i] == 0)) {
|
||||
EmitCs([
|
||||
cStream = i
|
||||
](DxvkContext* ctx) {
|
||||
ctx->bindVertexBuffer(cStream, DxvkBufferSlice(), 0);
|
||||
});
|
||||
m_flags.set(D3D9DeviceFlag::DirtyVertexBuffers);
|
||||
continue;
|
||||
for (uint32_t i = 0; i < caps::MaxStreams && dynamicSysmemVBOs; i++) {
|
||||
const VBOCopy& copy = vboCopies[i];
|
||||
|
||||
if (likely(copy.copyBufferLength != 0)) {
|
||||
const auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer);
|
||||
uint8_t* data = reinterpret_cast<uint8_t*>(upSlice.mapPtr) + copy.dstOffset;
|
||||
const uint8_t* src = reinterpret_cast<uint8_t*>(vbo->GetMappedSlice().mapPtr) + copy.srcOffset;
|
||||
|
||||
if (likely(copy.copyElementStride == copy.copyElementSize)) {
|
||||
std::memcpy(data, src, copy.copyBufferLength);
|
||||
} else {
|
||||
for (uint32_t j = 0; j < copy.copyElementCount; j++) {
|
||||
std::memcpy(data + j * copy.copyElementSize, src + j * copy.copyElementStride, copy.copyElementSize);
|
||||
}
|
||||
if (unlikely(copy.copyBufferLength > copy.copyElementCount * copy.copyElementSize)) {
|
||||
// Partial vertex at the end
|
||||
std::memcpy(
|
||||
data + copy.copyElementCount * copy.copyElementSize,
|
||||
src + copy.copyElementCount * copy.copyElementStride,
|
||||
copy.copyBufferLength - copy.copyElementCount * copy.copyElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
auto* 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
|
||||
if (NumIndices != 0) {
|
||||
BaseVertexIndex = -FirstVertexIndex;
|
||||
} else {
|
||||
FirstVertexIndex = 0;
|
||||
}
|
||||
auto vboSlice = upSlice.slice.subSlice(copy.dstOffset, copy.copyBufferLength);
|
||||
EmitCs([
|
||||
cStream = i,
|
||||
cBufferSlice = std::move(vboSlice),
|
||||
cStride = copy.copyElementSize
|
||||
](DxvkContext* ctx) mutable {
|
||||
ctx->bindVertexBuffer(cStream, std::move(cBufferSlice), cStride);
|
||||
});
|
||||
m_flags.set(D3D9DeviceFlag::DirtyVertexBuffers);
|
||||
}
|
||||
|
||||
// Change the draw call parameters to reflect the changed vertex buffers
|
||||
if (NumIndices != 0) {
|
||||
BaseVertexIndex = -FirstVertexIndex;
|
||||
} else {
|
||||
FirstVertexIndex = 0;
|
||||
}
|
||||
|
||||
if (dynamicSysmemIBO) {
|
||||
|
@ -5292,7 +5328,7 @@ namespace dxvk {
|
|||
uint8_t* data = reinterpret_cast<uint8_t*>(upSlice.mapPtr) + iboUPBufferOffset;
|
||||
uint8_t* src = reinterpret_cast<uint8_t*>(ibo->GetMappedSlice().mapPtr) + offset;
|
||||
std::memcpy(data, src, iboUPBufferSize);
|
||||
|
||||
|
||||
auto iboSlice = upSlice.slice.subSlice(iboUPBufferOffset, iboUPBufferSize);
|
||||
EmitCs([
|
||||
cBufferSlice = std::move(iboSlice),
|
||||
|
@ -7891,7 +7927,7 @@ namespace dxvk {
|
|||
// Docs: Also note that - unlike textures - swap chain back buffers, render targets [..] can be locked
|
||||
desc.IsLockable = TRUE;
|
||||
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc)))
|
||||
return D3DERR_NOTAVAILABLE;
|
||||
|
||||
m_autoDepthStencil = new D3D9Surface(this, &desc, nullptr, nullptr);
|
||||
|
|
|
@ -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 uintType = spvModule.defIntType(32, 0);
|
||||
uint32_t vec3Type = spvModule.defVectorType(floatType, 3);
|
||||
|
@ -357,7 +357,7 @@ namespace dxvk {
|
|||
floatType,
|
||||
}};
|
||||
|
||||
uint32_t rsStruct = spvModule.defStructTypeUnique(count, rsMembers.data());
|
||||
uint32_t rsStruct = spvModule.defStructTypeUnique(rsMembers.size(), rsMembers.data());
|
||||
uint32_t rsBlock = spvModule.newVar(
|
||||
spvModule.defPointerType(rsStruct, spv::StorageClassPushConstant),
|
||||
spv::StorageClassPushConstant);
|
||||
|
@ -369,9 +369,6 @@ namespace dxvk {
|
|||
|
||||
uint32_t memberIdx = 0;
|
||||
auto SetMemberName = [&](const char* name, uint32_t offset) {
|
||||
if (memberIdx >= count)
|
||||
return;
|
||||
|
||||
spvModule.setDebugMemberName (rsStruct, memberIdx, name);
|
||||
spvModule.memberDecorateOffset (rsStruct, memberIdx, offset);
|
||||
memberIdx++;
|
||||
|
@ -781,8 +778,6 @@ namespace dxvk {
|
|||
uint32_t m_inputMask = 0u;
|
||||
uint32_t m_outputMask = 0u;
|
||||
uint32_t m_flatShadingMask = 0u;
|
||||
uint32_t m_pushConstOffset = 0u;
|
||||
uint32_t m_pushConstSize = 0u;
|
||||
|
||||
DxsoProgramType m_programType;
|
||||
D3D9FFShaderKeyVS m_vsKey;
|
||||
|
@ -892,8 +887,8 @@ namespace dxvk {
|
|||
info.inputMask = m_inputMask;
|
||||
info.outputMask = m_outputMask;
|
||||
info.flatShadingInputs = m_flatShadingMask;
|
||||
info.pushConstOffset = m_pushConstOffset;
|
||||
info.pushConstSize = m_pushConstSize;
|
||||
info.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
info.pushConstSize = sizeof(D3D9RenderStateInfo);
|
||||
|
||||
return new DxvkShader(info, m_module.compile());
|
||||
}
|
||||
|
@ -1111,6 +1106,17 @@ namespace dxvk {
|
|||
const uint32_t wIndex = 3;
|
||||
|
||||
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;
|
||||
switch (inputFlags) {
|
||||
default:
|
||||
|
@ -1121,27 +1127,30 @@ namespace dxvk {
|
|||
count = flags;
|
||||
if (texcoordCount) {
|
||||
// Clamp by the number of elements in the texcoord input.
|
||||
if (!count || count > texcoordCount)
|
||||
if (!count || count > texcoordCount) {
|
||||
count = texcoordCount;
|
||||
}
|
||||
else
|
||||
}
|
||||
} else {
|
||||
count = 0;
|
||||
flags = D3DTTFF_DISABLE;
|
||||
applyTransform = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case (DXVK_TSS_TCI_CAMERASPACENORMAL >> TCIOffset):
|
||||
transformed = outNrm;
|
||||
count = 4;
|
||||
count = std::min(flags, 4u);
|
||||
break;
|
||||
|
||||
case (DXVK_TSS_TCI_CAMERASPACEPOSITION >> TCIOffset):
|
||||
transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(1.0f), vtx, 1, &wIndex);
|
||||
count = 4;
|
||||
count = std::min(flags, 4u);
|
||||
break;
|
||||
|
||||
case (DXVK_TSS_TCI_CAMERASPACEREFLECTIONVECTOR >> TCIOffset): {
|
||||
uint32_t vtx3 = m_module.opVectorShuffle(m_vec3Type, vtx, vtx, 3, indices.data());
|
||||
vtx3 = m_module.opNormalize(m_vec3Type, vtx3);
|
||||
|
||||
|
||||
uint32_t reflection = m_module.opReflect(m_vec3Type, vtx3, normal);
|
||||
|
||||
std::array<uint32_t, 4> transformIndices;
|
||||
|
@ -1150,7 +1159,7 @@ namespace dxvk {
|
|||
transformIndices[3] = m_module.constf32(1.0f);
|
||||
|
||||
transformed = m_module.opCompositeConstruct(m_vec4Type, transformIndices.size(), transformIndices.data());
|
||||
count = 4;
|
||||
count = std::min(flags, 4u);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1174,37 +1183,43 @@ namespace dxvk {
|
|||
transformIndices[3] = m_module.constf32(1.0f);
|
||||
|
||||
transformed = m_module.opCompositeConstruct(m_vec4Type, transformIndices.size(), transformIndices.data());
|
||||
count = 4;
|
||||
count = std::min(flags, 4u);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t type = flags;
|
||||
if (type != D3DTTFF_DISABLE) {
|
||||
if (!m_vsKey.Data.Contents.HasPositionT) {
|
||||
for (uint32_t j = count; j < 4; j++) {
|
||||
// If we're outside the component count of the vertex decl for this texcoord then we pad with zeroes.
|
||||
// Otherwise, pad with ones.
|
||||
if (applyTransform && !m_vsKey.Data.Contents.HasPositionT) {
|
||||
for (uint32_t j = count; j < 4; j++) {
|
||||
// If we're outside the component count of the vertex decl for this texcoord then we pad with zeroes.
|
||||
// Otherwise, pad with ones.
|
||||
|
||||
// Very weird quirk in order to get texcoord transforms to work like they do in native.
|
||||
// In future, maybe we could sort this out properly by chopping matrices of different sizes, but thats
|
||||
// a project for another day.
|
||||
uint32_t texcoordCount = (m_vsKey.Data.Contents.TexcoordDeclMask >> (3 * inputIndex)) & 0x7;
|
||||
uint32_t value = j > texcoordCount ? m_module.constf32(0) : m_module.constf32(1);
|
||||
transformed = m_module.opCompositeInsert(m_vec4Type, value, transformed, 1, &j);
|
||||
}
|
||||
|
||||
transformed = m_module.opVectorTimesMatrix(m_vec4Type, transformed, m_vs.constants.texcoord[i]);
|
||||
// Very weird quirk in order to get texcoord transforms to work like they do in native.
|
||||
// In future, maybe we could sort this out properly by chopping matrices of different sizes, but thats
|
||||
// a project for another day.
|
||||
uint32_t texcoordCount = (m_vsKey.Data.Contents.TexcoordDeclMask >> (3 * inputIndex)) & 0x7;
|
||||
uint32_t value = j > texcoordCount ? m_module.constf32(0) : m_module.constf32(1);
|
||||
transformed = m_module.opCompositeInsert(m_vec4Type, value, transformed, 1, &j);
|
||||
}
|
||||
|
||||
// Pad the unused section of it with the value for projection.
|
||||
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);
|
||||
transformed = m_module.opVectorTimesMatrix(m_vec4Type, transformed, m_vs.constants.texcoord[i]);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -1384,20 +1399,7 @@ namespace dxvk {
|
|||
|
||||
|
||||
void D3D9FFShaderCompiler::setupRenderStateInfo() {
|
||||
uint32_t count;
|
||||
|
||||
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);
|
||||
m_rsBlock = SetupRenderStateBlock(m_module);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1808,22 +1810,17 @@ namespace dxvk {
|
|||
texcoord = m_module.opVectorShuffle(texcoord_t,
|
||||
texcoord, texcoord, texcoordCnt, indices.data());
|
||||
|
||||
uint32_t projIdx = m_fsKey.Stages[i].Contents.ProjectedCount;
|
||||
if (projIdx == 0 || projIdx > texcoordCnt) {
|
||||
projIdx = 4; // Always use w if ProjectedCount is 0.
|
||||
}
|
||||
--projIdx;
|
||||
|
||||
bool shouldProject = m_fsKey.Stages[i].Contents.Projected;
|
||||
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);
|
||||
uint32_t insertIdx = texcoordCnt - 1;
|
||||
texcoord = m_module.opCompositeInsert(texcoord_t, projValue, texcoord, 1, &insertIdx);
|
||||
}
|
||||
|
||||
bool shouldProject = m_fsKey.Stages[i].Contents.Projected;
|
||||
|
||||
if (i != 0 && (
|
||||
m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAP ||
|
||||
m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAPLUMINANCE)) {
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace dxvk {
|
|||
void DoFixedFunctionAlphaTest(SpirvModule& spvModule, const D3D9AlphaTestContext& ctx);
|
||||
|
||||
// Returns a render state block
|
||||
uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count);
|
||||
uint32_t SetupRenderStateBlock(SpirvModule& spvModule);
|
||||
|
||||
struct D3D9PointSizeInfoVS {
|
||||
uint32_t defaultValue;
|
||||
|
|
|
@ -134,7 +134,7 @@ namespace dxvk {
|
|||
info.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
info.bindingCount = bindings.size();
|
||||
info.bindings = bindings.data();
|
||||
info.pushConstOffset = 0;
|
||||
info.pushConstStages = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
info.pushConstSize = sizeof(VkExtent2D);
|
||||
|
||||
return new DxvkShader(info, std::move(code));
|
||||
|
|
|
@ -37,8 +37,8 @@ namespace dxvk {
|
|||
this->customDeviceId = parsePciId(config.getOption<std::string>("d3d9.customDeviceId"));
|
||||
this->customDeviceDesc = config.getOption<std::string>("d3d9.customDeviceDesc");
|
||||
|
||||
const int32_t vendorId = this->customDeviceId != -1
|
||||
? this->customDeviceId
|
||||
const uint32_t vendorId = this->customVendorId != -1
|
||||
? this->customVendorId
|
||||
: (adapter != nullptr ? adapter->deviceProperties().vendorID : 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->samplerAnisotropy = config.getOption<int32_t> ("d3d9.samplerAnisotropy", -1);
|
||||
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->supportD32 = config.getOption<bool> ("d3d9.supportD32", true);
|
||||
this->useD32forD24 = config.getOption<bool> ("d3d9.useD32forD24", false);
|
||||
this->disableA8RT = config.getOption<bool> ("d3d9.disableA8RT", false);
|
||||
this->invariantPosition = config.getOption<bool> ("d3d9.invariantPosition", true);
|
||||
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->forceSamplerTypeSpecConstants = config.getOption<bool> ("d3d9.forceSamplerTypeSpecConstants", false);
|
||||
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->clampNegativeLodBias = config.getOption<bool> ("d3d9.clampNegativeLodBias", false);
|
||||
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
|
||||
this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f);
|
||||
|
@ -90,8 +91,8 @@ namespace dxvk {
|
|||
d3d9FloatEmulation = D3D9FloatEmulation::Enabled;
|
||||
} else {
|
||||
bool hasMulz = adapter != nullptr
|
||||
&& (adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV, 0, 0)
|
||||
|| adapter->matchesDriver(VK_DRIVER_ID_MESA_NVK, 0, 0));
|
||||
&& (adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV)
|
||||
|| adapter->matchesDriver(VK_DRIVER_ID_MESA_NVK));
|
||||
d3d9FloatEmulation = hasMulz ? D3D9FloatEmulation::Strict : D3D9FloatEmulation::Enabled;
|
||||
}
|
||||
|
||||
|
|
|
@ -155,6 +155,11 @@ namespace dxvk {
|
|||
|
||||
/// Disable counting losable resources and rejecting calls to Reset() if any are still alive
|
||||
bool countLosableResources;
|
||||
|
||||
/// Ensure that for the same D3D commands the output VK commands
|
||||
/// don't change between runs. Useful for comparative benchmarking,
|
||||
/// can negatively affect performance.
|
||||
bool reproducibleCommandStream;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -41,11 +41,6 @@ namespace dxvk {
|
|||
case D3DQUERYTYPE_TIMESTAMPFREQ:
|
||||
break;
|
||||
|
||||
case D3DQUERYTYPE_VERTEXSTATS:
|
||||
m_query[0] = dxvkDevice->createGpuQuery(
|
||||
VK_QUERY_TYPE_PIPELINE_STATISTICS, 0, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw DxvkError(str::format("D3D9Query: Unsupported query type ", m_queryType));
|
||||
}
|
||||
|
@ -222,12 +217,10 @@ namespace dxvk {
|
|||
|
||||
switch (m_queryType) {
|
||||
case D3DQUERYTYPE_VCACHE:
|
||||
// Don't know what the hell any of this means.
|
||||
// Nor do I care. This just makes games work.
|
||||
m_dataCache.VCache.Pattern = MAKEFOURCC('H', 'C', 'A', 'C');
|
||||
m_dataCache.VCache.Pattern = MAKEFOURCC('C', 'A', 'C', 'H');
|
||||
m_dataCache.VCache.OptMethod = 1;
|
||||
m_dataCache.VCache.CacheSize = 24;
|
||||
m_dataCache.VCache.MagicNumber = 20;
|
||||
m_dataCache.VCache.CacheSize = 16;
|
||||
m_dataCache.VCache.MagicNumber = 7;
|
||||
break;
|
||||
|
||||
case D3DQUERYTYPE_OCCLUSION:
|
||||
|
@ -246,11 +239,6 @@ namespace dxvk {
|
|||
m_dataCache.TimestampFreq = GetTimestampQueryFrequency();
|
||||
break;
|
||||
|
||||
case D3DQUERYTYPE_VERTEXSTATS:
|
||||
m_dataCache.VertexStats.NumRenderedTriangles = queryData[0].statistic.iaPrimitives;
|
||||
m_dataCache.VertexStats.NumExtraClippingTriangles = queryData[0].statistic.clipPrimitives;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -276,7 +264,6 @@ namespace dxvk {
|
|||
void D3D9Query::Begin(DxvkContext* ctx) {
|
||||
switch (m_queryType) {
|
||||
case D3DQUERYTYPE_OCCLUSION:
|
||||
case D3DQUERYTYPE_VERTEXSTATS:
|
||||
ctx->beginQuery(m_query[0]);
|
||||
break;
|
||||
|
||||
|
@ -296,7 +283,6 @@ namespace dxvk {
|
|||
ctx->writeTimestamp(m_query[0]);
|
||||
break;
|
||||
|
||||
case D3DQUERYTYPE_VERTEXSTATS:
|
||||
case D3DQUERYTYPE_OCCLUSION:
|
||||
ctx->endQuery(m_query[0]);
|
||||
break;
|
||||
|
@ -314,7 +300,6 @@ namespace dxvk {
|
|||
|
||||
bool D3D9Query::QueryBeginnable(D3DQUERYTYPE QueryType) {
|
||||
return QueryType == D3DQUERYTYPE_OCCLUSION
|
||||
|| QueryType == D3DQUERYTYPE_VERTEXSTATS
|
||||
|| QueryType == D3DQUERYTYPE_TIMESTAMPDISJOINT;
|
||||
}
|
||||
|
||||
|
@ -338,7 +323,6 @@ namespace dxvk {
|
|||
case D3DQUERYTYPE_TIMESTAMP:
|
||||
case D3DQUERYTYPE_TIMESTAMPDISJOINT:
|
||||
case D3DQUERYTYPE_TIMESTAMPFREQ:
|
||||
case D3DQUERYTYPE_VERTEXSTATS:
|
||||
return D3D_OK;
|
||||
|
||||
default:
|
||||
|
|
|
@ -57,17 +57,24 @@ if platform != 'windows'
|
|||
d3d9_link_depends += files('d3d9.sym')
|
||||
endif
|
||||
|
||||
d3d9_dll = shared_library('d3d9'+dll_ext, d3d9_src, glsl_generator.process(d3d9_shaders), d3d9_res,
|
||||
name_prefix : dxvk_name_prefix,
|
||||
d3d9_dll = shared_library(dxvk_name_prefix+'d3d9', d3d9_src, glsl_generator.process(d3d9_shaders), d3d9_res,
|
||||
dependencies : [ dxso_dep, dxvk_dep ],
|
||||
include_directories : dxvk_include_path,
|
||||
install : true,
|
||||
vs_module_defs : 'd3d9'+def_spec_ext,
|
||||
link_args : d3d9_ld_args,
|
||||
link_depends : [ d3d9_link_depends ],
|
||||
kwargs : dxvk_so_version,
|
||||
)
|
||||
|
||||
d3d9_dep = declare_dependency(
|
||||
link_with : [ d3d9_dll ],
|
||||
include_directories : [ dxvk_include_path ],
|
||||
)
|
||||
|
||||
if platform != 'windows'
|
||||
pkg.generate(d3d9_dll,
|
||||
filebase: dxvk_pkg_prefix + 'd3d9',
|
||||
subdirs: 'dxvk',
|
||||
)
|
||||
endif
|
||||
|
|
|
@ -87,9 +87,9 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
for (uint32_t i = 0; i < ins.dstCount; i++) {
|
||||
if (ins.dst[0].type == DxbcOperandType::IndexableTemp) {
|
||||
uint32_t index = ins.dst[0].idx[0].offset;
|
||||
m_analysis->xRegMasks[index] |= ins.dst[0].mask;
|
||||
if (ins.dst[i].type == DxbcOperandType::IndexableTemp) {
|
||||
uint32_t index = ins.dst[i].idx[0].offset;
|
||||
m_analysis->xRegMasks[index] |= ins.dst[i].mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,11 @@ namespace dxvk {
|
|||
entry.systemValue = static_cast<DxbcSystemValue>(reader.readu32());
|
||||
entry.componentType = componentTypes.at(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)
|
||||
reader.readu32();
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace dxvk {
|
|||
uint32_t semanticIndex;
|
||||
uint32_t registerId;
|
||||
DxbcRegMask componentMask;
|
||||
DxbcRegMask componentUsed;
|
||||
DxbcScalarType componentType;
|
||||
DxbcSystemValue systemValue;
|
||||
uint32_t streamId;
|
||||
|
|
|
@ -257,14 +257,13 @@ namespace dxvk {
|
|||
info.outputMask = m_outputMask;
|
||||
info.uniformSize = m_immConstData.size();
|
||||
info.uniformData = m_immConstData.data();
|
||||
info.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
info.pushConstSize = sizeof(DxbcPushConstants);
|
||||
info.outputTopology = m_outputTopology;
|
||||
|
||||
if (m_programInfo.type() == DxbcProgramType::HullShader)
|
||||
info.patchVertexCount = m_hs.vertexCountIn;
|
||||
|
||||
if (m_programInfo.type() == DxbcProgramType::PixelShader && m_ps.pushConstantId)
|
||||
info.pushConstSize = sizeof(DxbcPushConstants);
|
||||
|
||||
if (m_moduleInfo.xfb) {
|
||||
info.xfbRasterizedStream = m_moduleInfo.xfb->rasterizedStream;
|
||||
|
||||
|
@ -1624,8 +1623,13 @@ namespace dxvk {
|
|||
|
||||
case DxbcOpcode::Mad:
|
||||
case DxbcOpcode::DFma:
|
||||
dst.id = m_module.opFFma(typeId,
|
||||
src.at(0).id, src.at(1).id, src.at(2).id);
|
||||
if (likely(!m_moduleInfo.options.longMad)) {
|
||||
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;
|
||||
|
||||
case DxbcOpcode::Max:
|
||||
|
@ -2464,58 +2468,6 @@ namespace dxvk {
|
|||
if (m_uavs.at(registerId).ctrId == 0)
|
||||
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
|
||||
DxbcRegisterInfo ptrType;
|
||||
ptrType.type.ctype = DxbcScalarType::Uint32;
|
||||
|
@ -2547,13 +2499,14 @@ namespace dxvk {
|
|||
switch (ins.op) {
|
||||
case DxbcOpcode::ImmAtomicAlloc:
|
||||
value.id = m_module.opAtomicIAdd(typeId, ptrId,
|
||||
scopeId, semanticsId, laneCount);
|
||||
scopeId, semanticsId, m_module.constu32(1));
|
||||
break;
|
||||
|
||||
case DxbcOpcode::ImmAtomicConsume:
|
||||
value.id = m_module.opAtomicISub(typeId, ptrId,
|
||||
scopeId, semanticsId, laneCount);
|
||||
value.id = m_module.opISub(typeId, value.id, laneCount);
|
||||
scopeId, semanticsId, m_module.constu32(1));
|
||||
value.id = m_module.opISub(typeId, value.id,
|
||||
m_module.constu32(1));
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -2563,26 +2516,6 @@ namespace dxvk {
|
|||
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
|
||||
emitRegisterStore(ins.dst[0], value);
|
||||
}
|
||||
|
@ -5584,12 +5517,12 @@ namespace dxvk {
|
|||
result.type.ctype = DxbcScalarType::Uint32;
|
||||
result.type.ccount = 1;
|
||||
|
||||
if (info.image.sampled == 1) {
|
||||
if (info.image.ms == 0 && info.image.sampled == 1) {
|
||||
result.id = m_module.opImageQueryLevels(
|
||||
getVectorTypeId(result.type),
|
||||
m_module.opLoad(info.typeId, info.varId));
|
||||
} else {
|
||||
// Report one LOD in case of UAVs
|
||||
// Report one LOD in case of UAVs or multisampled images
|
||||
result.id = m_module.constu32(1);
|
||||
}
|
||||
|
||||
|
@ -7799,6 +7732,21 @@ namespace dxvk {
|
|||
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 {
|
||||
switch (m_programInfo.type()) {
|
||||
|
@ -7829,8 +7777,25 @@ namespace dxvk {
|
|||
result.ctype = DxbcScalarType::Float32;
|
||||
result.ccount = 4;
|
||||
|
||||
if (m_isgn->findByRegister(regIdx))
|
||||
result.ccount = m_isgn->regMask(regIdx).minComponents();
|
||||
if (m_isgn == nullptr || !m_isgn->findByRegister(regIdx))
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1221,6 +1221,9 @@ namespace dxvk {
|
|||
uint32_t getUavCoherence(
|
||||
uint32_t registerId,
|
||||
DxbcUavFlags flags);
|
||||
|
||||
bool ignoreInputSystemValue(
|
||||
DxbcSystemValue sv) const;
|
||||
|
||||
///////////////////////////
|
||||
// Type definition methods
|
||||
|
|
|
@ -17,9 +17,6 @@ namespace dxvk {
|
|||
|
||||
useDepthClipWorkaround
|
||||
= !devFeatures.extDepthClipEnable.depthClipEnable;
|
||||
useSubgroupOpsForAtomicCounters
|
||||
= (devInfo.vk11.subgroupSupportedStages & VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
&& (devInfo.vk11.subgroupSupportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT);
|
||||
|
||||
VkFormatFeatureFlags2 r32Features
|
||||
= device->getFormatFeatures(VK_FORMAT_R32_SFLOAT).optimal
|
||||
|
@ -41,6 +38,7 @@ namespace dxvk {
|
|||
disableMsaa = options.disableMsaa;
|
||||
forceSampleRateShading = options.forceSampleRateShading;
|
||||
enableSampleShadingInterlock = device->features().extFragmentShaderInterlock.fragmentShaderSampleInterlock;
|
||||
longMad = options.longMad;
|
||||
|
||||
// Figure out float control flags to match D3D11 rules
|
||||
if (options.floatControls) {
|
||||
|
|
|
@ -30,10 +30,6 @@ namespace dxvk {
|
|||
/// Determines whether raw access chains are supported
|
||||
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
|
||||
bool zeroInitWorkgroupMemory = false;
|
||||
|
||||
|
@ -58,6 +54,9 @@ namespace dxvk {
|
|||
|
||||
/// Minimum storage buffer alignment
|
||||
VkDeviceSize minSsboAlignment = 0;
|
||||
|
||||
/// Should we make our Mads a FFma or do it the long way with an FMul and an FAdd?
|
||||
bool longMad;
|
||||
};
|
||||
|
||||
}
|
|
@ -130,7 +130,7 @@ namespace dxvk {
|
|||
// We can't really reconstruct the version numbers
|
||||
// returned by Windows drivers from Vulkan data
|
||||
if (SUCCEEDED(hr) && pUMDVersion)
|
||||
pUMDVersion->QuadPart = ~0ull;
|
||||
pUMDVersion->QuadPart = INT64_MAX;
|
||||
|
||||
if (FAILED(hr)) {
|
||||
Logger::err("DXGI: CheckInterfaceSupport: Unsupported interface");
|
||||
|
|
|
@ -124,13 +124,10 @@ namespace dxvk {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// If any monitors are left on the list, enable the
|
||||
// fallback to always enumerate all monitors.
|
||||
if ((m_monitorFallback = !monitors.empty()))
|
||||
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;
|
||||
|
||||
IDXGISwapChain1* swapChain = nullptr;
|
||||
HRESULT hr = CreateSwapChainForHwnd(
|
||||
HRESULT hr = CreateSwapChainForHwndBase(
|
||||
pDevice, pDesc->OutputWindow,
|
||||
&desc, &descFs, nullptr,
|
||||
&swapChain);
|
||||
|
@ -247,6 +244,19 @@ namespace dxvk {
|
|||
IUnknown* pDevice,
|
||||
HWND hWnd,
|
||||
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,
|
||||
IDXGIOutput* pRestrictToOutput,
|
||||
IDXGISwapChain1** ppSwapChain) {
|
||||
|
|
|
@ -200,6 +200,14 @@ namespace dxvk {
|
|||
UINT m_flags;
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,21 @@ namespace dxvk {
|
|||
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() {
|
||||
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->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");
|
||||
if (this->enableHDR && isHDRDisallowed()) {
|
||||
Logger::info("HDR was configured to be enabled, but has been force disabled as a UE4 DX11 game was detected.");
|
||||
this->enableHDR = false;
|
||||
}
|
||||
|
||||
this->useMonitorFallback = config.getOption<bool>("dxgi.useMonitorFallback", env::getEnvVar("DXVK_MONITOR_FALLBACK") == "1");
|
||||
if (this->useMonitorFallback)
|
||||
Logger::info("Enabled useMonitorFallback option");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -49,9 +49,6 @@ namespace dxvk {
|
|||
/// Enable HDR
|
||||
bool enableHDR;
|
||||
|
||||
/// Use monitor fallback to enumerating all monitors per output
|
||||
bool useMonitorFallback;
|
||||
|
||||
/// Sync interval. Overrides the value
|
||||
/// passed to IDXGISwapChain::Present.
|
||||
int32_t syncInterval;
|
||||
|
|
|
@ -303,15 +303,22 @@ namespace dxvk {
|
|||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present(UINT SyncInterval, UINT Flags) {
|
||||
return Present1(SyncInterval, Flags, nullptr);
|
||||
return PresentBase(SyncInterval, Flags, nullptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present1(
|
||||
UINT SyncInterval,
|
||||
UINT PresentFlags,
|
||||
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)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
|
|
|
@ -233,6 +233,10 @@ namespace dxvk {
|
|||
DXGI_FORMAT Format,
|
||||
DXGI_COLOR_SPACE_TYPE ColorSpace);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE PresentBase(
|
||||
UINT SyncInterval,
|
||||
UINT PresentFlags,
|
||||
const DXGI_PRESENT_PARAMETERS* pPresentParameters);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -21,17 +21,24 @@ if platform != 'windows'
|
|||
dxgi_link_depends += files('dxgi.sym')
|
||||
endif
|
||||
|
||||
dxgi_dll = shared_library('dxgi'+dll_ext, dxgi_src, dxgi_res,
|
||||
name_prefix : dxvk_name_prefix,
|
||||
dxgi_dll = shared_library(dxvk_name_prefix+'dxgi', dxgi_src, dxgi_res,
|
||||
dependencies : [ dxvk_dep ],
|
||||
include_directories : dxvk_include_path,
|
||||
install : true,
|
||||
vs_module_defs : 'dxgi'+def_spec_ext,
|
||||
link_args : dxgi_ld_args,
|
||||
link_depends : [ dxgi_link_depends ],
|
||||
kwargs : dxvk_so_version,
|
||||
)
|
||||
|
||||
dxgi_dep = declare_dependency(
|
||||
link_with : [ dxgi_dll ],
|
||||
include_directories : [ dxvk_include_path ],
|
||||
)
|
||||
|
||||
if platform != 'windows'
|
||||
pkg.generate(dxgi_dll,
|
||||
filebase: dxvk_pkg_prefix + 'dxgi',
|
||||
subdirs: 'dxvk',
|
||||
)
|
||||
endif
|
||||
|
|
|
@ -228,8 +228,8 @@ namespace dxvk {
|
|||
info.bindings = m_bindings.data();
|
||||
info.inputMask = m_inputMask;
|
||||
info.outputMask = m_outputMask;
|
||||
info.pushConstOffset = m_pushConstOffset;
|
||||
info.pushConstSize = m_pushConstSize;
|
||||
info.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
info.pushConstSize = sizeof(D3D9RenderStateInfo);
|
||||
|
||||
if (m_programInfo.type() == DxsoProgramTypes::PixelShader)
|
||||
info.flatShadingInputs = m_ps.flatShadingMask;
|
||||
|
@ -3561,30 +3561,7 @@ void DxsoCompiler::emitControlFlowGenericLoop(
|
|||
|
||||
|
||||
void DxsoCompiler::setupRenderStateInfo() {
|
||||
uint32_t count;
|
||||
|
||||
// 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);
|
||||
m_rsBlock = SetupRenderStateBlock(m_module);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -344,8 +344,6 @@ namespace dxvk {
|
|||
// covers vertex input and fragment output.
|
||||
uint32_t m_inputMask = 0u;
|
||||
uint32_t m_outputMask = 0u;
|
||||
uint32_t m_pushConstOffset = 0u;
|
||||
uint32_t m_pushConstSize = 0u;
|
||||
|
||||
///////////////////////////////////
|
||||
// Shader-specific data structures
|
||||
|
|
|
@ -346,6 +346,11 @@ namespace dxvk {
|
|||
enabledFeatures.vk13.synchronization2 = 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
|
||||
enabledFeatures.extDepthClipEnable.depthClipEnable =
|
||||
m_deviceFeatures.extDepthClipEnable.depthClipEnable;
|
||||
|
@ -410,11 +415,15 @@ namespace dxvk {
|
|||
m_deviceFeatures.khrPresentWait.presentWait;
|
||||
|
||||
// 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.khrPresentWait.presentWait = VK_FALSE;
|
||||
}
|
||||
|
||||
// Enable descriptor pool overallocation if supported
|
||||
enabledFeatures.nvDescriptorPoolOverallocation.descriptorPoolOverallocation =
|
||||
m_deviceFeatures.nvDescriptorPoolOverallocation.descriptorPoolOverallocation;
|
||||
|
||||
// Enable raw access chains for shader backends
|
||||
enabledFeatures.nvRawAccessChains.shaderRawAccessChains =
|
||||
m_deviceFeatures.nvRawAccessChains.shaderRawAccessChains;
|
||||
|
@ -425,10 +434,7 @@ namespace dxvk {
|
|||
// Log feature support info an extension list
|
||||
Logger::info(str::format("Device properties:"
|
||||
"\n Device : ", m_deviceInfo.core.properties.deviceName,
|
||||
"\n Driver : ", m_deviceInfo.vk12.driverName, " ",
|
||||
VK_VERSION_MAJOR(m_deviceInfo.core.properties.driverVersion), ".",
|
||||
VK_VERSION_MINOR(m_deviceInfo.core.properties.driverVersion), ".",
|
||||
VK_VERSION_PATCH(m_deviceInfo.core.properties.driverVersion)));
|
||||
"\n Driver : ", m_deviceInfo.vk12.driverName, " ", m_deviceInfo.driverVersion.toString()));
|
||||
|
||||
Logger::info("Enabled device extensions:");
|
||||
this->logNameList(extensionNameList);
|
||||
|
@ -610,6 +616,10 @@ namespace dxvk {
|
|||
enabledFeatures.khrPresentWait = *reinterpret_cast<const VkPhysicalDevicePresentWaitFeaturesKHR*>(f);
|
||||
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:
|
||||
enabledFeatures.nvRawAccessChains = *reinterpret_cast<const VkPhysicalDeviceRawAccessChainsFeaturesNV*>(f);
|
||||
break;
|
||||
|
@ -626,9 +636,7 @@ namespace dxvk {
|
|||
Logger::info(str::format("Device properties:"
|
||||
"\n Device name: ", m_deviceInfo.core.properties.deviceName,
|
||||
"\n Driver: ", m_deviceInfo.vk12.driverName, " ",
|
||||
VK_VERSION_MAJOR(m_deviceInfo.core.properties.driverVersion), ".",
|
||||
VK_VERSION_MINOR(m_deviceInfo.core.properties.driverVersion), ".",
|
||||
VK_VERSION_PATCH(m_deviceInfo.core.properties.driverVersion)));
|
||||
m_deviceInfo.driverVersion.toString()));
|
||||
|
||||
Logger::info("Enabled device extensions:");
|
||||
this->logNameList(extensionNameList);
|
||||
|
@ -664,26 +672,29 @@ namespace dxvk {
|
|||
|
||||
bool DxvkAdapter::matchesDriver(
|
||||
VkDriverIdKHR driver,
|
||||
uint32_t minVer,
|
||||
uint32_t maxVer) const {
|
||||
Version minVer,
|
||||
Version maxVer) const {
|
||||
bool driverMatches = driver == m_deviceInfo.vk12.driverID;
|
||||
|
||||
if (minVer) driverMatches &= m_deviceInfo.core.properties.driverVersion >= minVer;
|
||||
if (maxVer) driverMatches &= m_deviceInfo.core.properties.driverVersion < maxVer;
|
||||
if (minVer) driverMatches &= m_deviceInfo.driverVersion >= minVer;
|
||||
if (maxVer) driverMatches &= m_deviceInfo.driverVersion < maxVer;
|
||||
|
||||
return driverMatches;
|
||||
}
|
||||
|
||||
|
||||
bool DxvkAdapter::matchesDriver(
|
||||
VkDriverIdKHR driver) const {
|
||||
return driver == m_deviceInfo.vk12.driverID;
|
||||
}
|
||||
|
||||
|
||||
void DxvkAdapter::logAdapterInfo() const {
|
||||
const auto deviceInfo = this->devicePropertiesExt();
|
||||
const auto memoryInfo = this->memoryProperties();
|
||||
|
||||
Logger::info(str::format(deviceInfo.core.properties.deviceName, ":",
|
||||
"\n Driver : ", deviceInfo.vk12.driverName, " ",
|
||||
VK_VERSION_MAJOR(deviceInfo.core.properties.driverVersion), ".",
|
||||
VK_VERSION_MINOR(deviceInfo.core.properties.driverVersion), ".",
|
||||
VK_VERSION_PATCH(deviceInfo.core.properties.driverVersion)));
|
||||
"\n Driver : ", deviceInfo.vk12.driverName, " ", deviceInfo.driverVersion.toString()));
|
||||
|
||||
for (uint32_t i = 0; i < memoryInfo.memoryHeapCount; i++) {
|
||||
constexpr VkDeviceSize mib = 1024 * 1024;
|
||||
|
@ -785,22 +796,8 @@ namespace dxvk {
|
|||
m_vki->vkGetPhysicalDeviceProperties2(m_handle, &m_deviceInfo.core);
|
||||
|
||||
// Some drivers reports the driver version in a slightly different format
|
||||
switch (m_deviceInfo.vk12.driverID) {
|
||||
case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
|
||||
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:;
|
||||
}
|
||||
m_deviceInfo.driverVersion = decodeDriverVersion(
|
||||
m_deviceInfo.vk12.driverID, m_deviceInfo.core.properties.driverVersion);
|
||||
}
|
||||
|
||||
|
||||
|
@ -935,6 +932,11 @@ namespace dxvk {
|
|||
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)) {
|
||||
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);
|
||||
|
@ -1007,6 +1009,7 @@ namespace dxvk {
|
|||
&devExtensions.khrPresentWait,
|
||||
&devExtensions.khrSwapchain,
|
||||
&devExtensions.khrWin32KeyedMutex,
|
||||
&devExtensions.nvDescriptorPoolOverallocation,
|
||||
&devExtensions.nvRawAccessChains,
|
||||
&devExtensions.nvxBinaryImport,
|
||||
&devExtensions.nvxImageViewHandle,
|
||||
|
@ -1147,6 +1150,11 @@ namespace dxvk {
|
|||
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) {
|
||||
enabledFeatures.nvRawAccessChains.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAW_ACCESS_CHAINS_FEATURES_NV;
|
||||
enabledFeatures.nvRawAccessChains.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.nvRawAccessChains);
|
||||
|
@ -1298,6 +1306,8 @@ namespace dxvk {
|
|||
"\n presentId : ", features.khrPresentId.presentId ? "1" : "0",
|
||||
"\n", VK_KHR_PRESENT_WAIT_EXTENSION_NAME,
|
||||
"\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 shaderRawAccessChains : ", features.nvRawAccessChains.shaderRawAccessChains ? "1" : "0",
|
||||
"\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"));
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -255,8 +255,17 @@ namespace dxvk {
|
|||
*/
|
||||
bool matchesDriver(
|
||||
VkDriverIdKHR driver,
|
||||
uint32_t minVer,
|
||||
uint32_t maxVer) const;
|
||||
Version minVer,
|
||||
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
|
||||
|
@ -343,6 +352,8 @@ namespace dxvk {
|
|||
static void logFeatures(const DxvkDeviceFeatures& features);
|
||||
static void logQueueFamilies(const DxvkAdapterQueueIndices& queues);
|
||||
|
||||
static Version decodeDriverVersion(VkDriverId driverId, uint32_t version);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -48,9 +48,7 @@ namespace dxvk {
|
|||
// Retrieve actual pipeline handle on first use. This
|
||||
// may wait for an ongoing compile job to finish, or
|
||||
// compile the pipeline immediately on the calling thread.
|
||||
m_libraryHandle = m_library->acquirePipelineHandle(
|
||||
DxvkShaderPipelineLibraryCompileArgs());
|
||||
|
||||
m_libraryHandle = m_library->acquirePipelineHandle().handle;
|
||||
return m_libraryHandle;
|
||||
} else {
|
||||
// Slow path for compute shaders that do use spec constants
|
||||
|
|
|
@ -1997,7 +1997,7 @@ namespace dxvk {
|
|||
depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
depthOp.loadLayout = imageView->imageInfo().layout;
|
||||
depthOp.storeLayout = imageView->imageInfo().layout;
|
||||
|
||||
|
||||
if (clearAspects & VK_IMAGE_ASPECT_COLOR_BIT)
|
||||
colorOp.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
else if (discardAspects & VK_IMAGE_ASPECT_COLOR_BIT)
|
||||
|
@ -2010,7 +2010,7 @@ namespace dxvk {
|
|||
|
||||
if (clearAspects & VK_IMAGE_ASPECT_STENCIL_BIT)
|
||||
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;
|
||||
|
||||
if (attachmentIndex >= 0 && !m_state.om.framebufferInfo.isWritable(attachmentIndex, clearAspects | discardAspects)) {
|
||||
|
@ -2041,6 +2041,8 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
if (attachmentIndex < 0) {
|
||||
bool hasViewFormatMismatch = imageView->info().format != imageView->imageInfo().format;
|
||||
|
||||
if (m_execBarriers.isImageDirty(imageView->image(), imageView->imageSubresources(), DxvkAccess::Write))
|
||||
m_execBarriers.recordCommands(m_cmd);
|
||||
|
||||
|
@ -2075,6 +2077,11 @@ namespace dxvk {
|
|||
|
||||
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.pColorAttachments = &attachmentInfo;
|
||||
|
||||
|
@ -2110,6 +2117,20 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
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_execBarriers.accessImage(
|
||||
|
@ -4741,8 +4762,10 @@ namespace dxvk {
|
|||
this->renderPassEmitPostBarriers(framebufferInfo, ops);
|
||||
|
||||
uint32_t colorInfoCount = 0;
|
||||
uint32_t lateClearCount = 0;
|
||||
|
||||
std::array<VkRenderingAttachmentInfo, MaxNumRenderTargets> colorInfos;
|
||||
std::array<VkClearAttachment, MaxNumRenderTargets> lateClears;
|
||||
|
||||
for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
|
||||
const auto& colorTarget = framebufferInfo.getColorTarget(i);
|
||||
|
@ -4754,9 +4777,21 @@ namespace dxvk {
|
|||
colorInfos[i].loadOp = ops.colorOps[i].loadOp;
|
||||
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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
@ -4804,6 +4839,15 @@ namespace dxvk {
|
|||
|
||||
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++) {
|
||||
m_cmd->trackResource<DxvkAccess::None> (framebufferInfo.getAttachment(i).view);
|
||||
m_cmd->trackResource<DxvkAccess::Write>(framebufferInfo.getAttachment(i).view->image());
|
||||
|
@ -4940,7 +4984,7 @@ namespace dxvk {
|
|||
// Mark compute resources and push constants as dirty
|
||||
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.clr(DxvkContextFlag::CpDirtyPipelineState);
|
||||
|
@ -5014,7 +5058,7 @@ namespace dxvk {
|
|||
|
||||
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.clr(DxvkContextFlag::GpDirtyPipeline);
|
||||
|
@ -5881,16 +5925,19 @@ namespace dxvk {
|
|||
auto bindings = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS
|
||||
? m_state.gp.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)
|
||||
return;
|
||||
|
||||
// Push constants should be compatible between complete and
|
||||
// independent layouts, so always ask for the complete one
|
||||
m_cmd->cmdPushConstants(
|
||||
bindings->getPipelineLayout(false),
|
||||
bindings->getPipelineLayout(independentSets),
|
||||
pushConstRange.stageFlags,
|
||||
pushConstRange.offset,
|
||||
pushConstRange.size,
|
||||
|
|
|
@ -72,7 +72,7 @@ namespace dxvk {
|
|||
// memory bloat. This may be necessary for off-screen
|
||||
// rendering applications, or in situations where games
|
||||
// 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() {
|
||||
// As a heuristic to save memory, check how many descriptors
|
||||
// have actively been used in the past couple of submissions.
|
||||
bool isLowUsageFrame = false;
|
||||
|
||||
// As a heuristic to save memory, check how many descriptor
|
||||
// sets were actually being used in past submissions.
|
||||
size_t poolCount = m_descriptorPools.size();
|
||||
bool needsReset = poolCount > MaxDesiredPoolCount;
|
||||
|
||||
if (poolCount > 1 || m_setsAllocated > m_manager->getMaxSetCount() / 2) {
|
||||
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;
|
||||
|
||||
if (m_lowUsageFrames < 16) {
|
||||
if (!needsReset) {
|
||||
for (auto& entry : m_setLists)
|
||||
entry.second.reset();
|
||||
} else {
|
||||
// If most sets are no longer being used, reset and destroy
|
||||
// descriptor pools and reset all lookup tables in order to
|
||||
// accomodate more descriptors of different layouts.
|
||||
// If most sets are no longer needed, reset and destroy
|
||||
// descriptor pools and reset all lookup tables in order
|
||||
// to accomodate more descriptors of different layouts.
|
||||
for (auto pool : m_descriptorPools)
|
||||
m_manager->recycleVulkanDescriptorPool(pool);
|
||||
|
||||
|
@ -131,7 +127,6 @@ namespace dxvk {
|
|||
m_setMaps.clear();
|
||||
|
||||
m_setsAllocated = 0;
|
||||
m_lowUsageFrames = 0;
|
||||
}
|
||||
|
||||
m_cachedEntry = { nullptr, nullptr };
|
||||
|
@ -311,7 +306,12 @@ namespace dxvk {
|
|||
info.maxSets = m_maxSets;
|
||||
info.poolSizeCount = pools.size();
|
||||
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;
|
||||
|
||||
if (vk->vkCreateDescriptorPool(vk->device(), &info, nullptr, &pool) != VK_SUCCESS)
|
||||
|
|
|
@ -80,7 +80,7 @@ namespace dxvk {
|
|||
* to be updated.
|
||||
*/
|
||||
class DxvkDescriptorPool : public RcObject {
|
||||
|
||||
constexpr static uint32_t MaxDesiredPoolCount = 2;
|
||||
public:
|
||||
|
||||
DxvkDescriptorPool(
|
||||
|
@ -155,8 +155,6 @@ namespace dxvk {
|
|||
|
||||
uint32_t m_prevSetsAllocated = 0;
|
||||
|
||||
uint32_t m_lowUsageFrames = 0;
|
||||
|
||||
DxvkDescriptorSetMap* getSetMapCached(
|
||||
const DxvkBindingLayoutObjects* layout);
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ namespace dxvk {
|
|||
|
||||
// Disable lifetime tracking for drivers that do not have any
|
||||
// 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 true;
|
||||
|
@ -325,12 +325,12 @@ namespace dxvk {
|
|||
DxvkDevicePerfHints DxvkDevice::getPerfHints() {
|
||||
DxvkDevicePerfHints hints;
|
||||
hints.preferFbDepthStencilCopy = m_features.extShaderStencilExport
|
||||
&& (m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV_KHR, 0, 0)
|
||||
|| m_adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, 0, 0)
|
||||
|| m_adapter->matchesDriver(VK_DRIVER_ID_AMD_PROPRIETARY_KHR, 0, 0));
|
||||
&& (m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV_KHR)
|
||||
|| m_adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR)
|
||||
|| m_adapter->matchesDriver(VK_DRIVER_ID_AMD_PROPRIETARY_KHR));
|
||||
hints.preferFbResolve = m_features.amdShaderFragmentMask
|
||||
&& (m_adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, 0, 0)
|
||||
|| m_adapter->matchesDriver(VK_DRIVER_ID_AMD_PROPRIETARY_KHR, 0, 0));
|
||||
&& (m_adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR)
|
||||
|| m_adapter->matchesDriver(VK_DRIVER_ID_AMD_PROPRIETARY_KHR));
|
||||
return hints;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,11 +2,16 @@
|
|||
|
||||
namespace dxvk {
|
||||
|
||||
DxvkDeviceFilter::DxvkDeviceFilter(DxvkDeviceFilterFlags flags)
|
||||
DxvkDeviceFilter::DxvkDeviceFilter(
|
||||
DxvkDeviceFilterFlags flags,
|
||||
const DxvkOptions& options)
|
||||
: m_flags(flags) {
|
||||
m_matchDeviceName = env::getEnvVar("DXVK_FILTER_DEVICE_NAME");
|
||||
|
||||
if (m_matchDeviceName.size() != 0)
|
||||
|
||||
if (m_matchDeviceName.empty())
|
||||
m_matchDeviceName = options.deviceFilter;
|
||||
|
||||
if (!m_matchDeviceName.empty())
|
||||
m_flags.set(DxvkDeviceFilterFlag::MatchDeviceName);
|
||||
}
|
||||
|
||||
|
@ -17,10 +22,10 @@ namespace dxvk {
|
|||
|
||||
|
||||
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 ",
|
||||
VK_VERSION_MAJOR(properties.apiVersion), ".",
|
||||
VK_VERSION_MINOR(properties.apiVersion), " adapter: ",
|
||||
VK_API_VERSION_MAJOR(properties.apiVersion), ".",
|
||||
VK_API_VERSION_MINOR(properties.apiVersion), " adapter: ",
|
||||
properties.deviceName));
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "dxvk_adapter.h"
|
||||
#include "dxvk_options.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
|
@ -31,7 +32,10 @@ namespace dxvk {
|
|||
|
||||
public:
|
||||
|
||||
DxvkDeviceFilter(DxvkDeviceFilterFlags flags);
|
||||
DxvkDeviceFilter(
|
||||
DxvkDeviceFilterFlags flags,
|
||||
const DxvkOptions& options);
|
||||
|
||||
~DxvkDeviceFilter();
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "dxvk_include.h"
|
||||
|
||||
#include "../util/util_version.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
/**
|
||||
|
@ -13,6 +15,7 @@ namespace dxvk {
|
|||
* so before using them, check whether they are supported.
|
||||
*/
|
||||
struct DxvkDeviceInfo {
|
||||
Version driverVersion;
|
||||
VkPhysicalDeviceProperties2 core;
|
||||
VkPhysicalDeviceVulkan11Properties vk11;
|
||||
VkPhysicalDeviceVulkan12Properties vk12;
|
||||
|
@ -68,6 +71,7 @@ namespace dxvk {
|
|||
VkPhysicalDeviceMaintenance5FeaturesKHR khrMaintenance5;
|
||||
VkPhysicalDevicePresentIdFeaturesKHR khrPresentId;
|
||||
VkPhysicalDevicePresentWaitFeaturesKHR khrPresentWait;
|
||||
VkPhysicalDeviceDescriptorPoolOverallocationFeaturesNV nvDescriptorPoolOverallocation;
|
||||
VkPhysicalDeviceRawAccessChainsFeaturesNV nvRawAccessChains;
|
||||
VkBool32 nvxBinaryImport;
|
||||
VkBool32 nvxImageViewHandle;
|
||||
|
|
|
@ -325,6 +325,7 @@ namespace dxvk {
|
|||
DxvkExt khrPresentWait = { VK_KHR_PRESENT_WAIT_EXTENSION_NAME, DxvkExtMode::Optional };
|
||||
DxvkExt khrSwapchain = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, DxvkExtMode::Required };
|
||||
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 nvxBinaryImport = { VK_NVX_BINARY_IMPORT_EXTENSION_NAME, DxvkExtMode::Disabled };
|
||||
DxvkExt nvxImageViewHandle = { VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME, DxvkExtMode::Disabled };
|
||||
|
|
|
@ -1111,7 +1111,9 @@ namespace dxvk {
|
|||
|
||||
if (doCreateBasePipeline)
|
||||
baseHandle = this->getBasePipeline(state);
|
||||
else
|
||||
|
||||
// Fast-linking may fail in some situations
|
||||
if (!baseHandle)
|
||||
fastHandle = this->getOptimizedPipeline(state);
|
||||
|
||||
// Log pipeline state if requested, or on failure
|
||||
|
@ -1148,6 +1150,13 @@ namespace dxvk {
|
|||
|| (state.rs.lineMode() != VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT && isLineRendering))
|
||||
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 tessellation shaders are present, the input patch
|
||||
// vertex count must match the shader's definition.
|
||||
|
@ -1219,9 +1228,6 @@ namespace dxvk {
|
|||
key.viLibrary = m_manager->createVertexInputLibrary(viState);
|
||||
key.foLibrary = m_manager->createFragmentOutputLibrary(foState);
|
||||
|
||||
if (!m_device->features().extExtendedDynamicState3.extendedDynamicState3DepthClipEnable)
|
||||
key.args.depthClipEnable = state.rs.depthClipEnable();
|
||||
|
||||
auto entry = m_basePipelines.find(key);
|
||||
if (entry != m_basePipelines.end())
|
||||
return entry->second;
|
||||
|
@ -1236,10 +1242,11 @@ namespace dxvk {
|
|||
const DxvkGraphicsPipelineBaseInstanceKey& key) const {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
DxvkShaderPipelineLibraryHandle vs = m_vsLibrary->acquirePipelineHandle();
|
||||
DxvkShaderPipelineLibraryHandle fs = m_fsLibrary->acquirePipelineHandle();
|
||||
|
||||
std::array<VkPipeline, 4> libraries = {{
|
||||
key.viLibrary->getHandle(),
|
||||
m_vsLibrary->acquirePipelineHandle(key.args),
|
||||
m_fsLibrary->acquirePipelineHandle(key.args),
|
||||
key.viLibrary->getHandle(), vs.handle, fs.handle,
|
||||
key.foLibrary->getHandle(),
|
||||
}};
|
||||
|
||||
|
@ -1248,13 +1255,14 @@ namespace dxvk {
|
|||
libInfo.pLibraries = libraries.data();
|
||||
|
||||
VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &libInfo };
|
||||
info.flags = vs.linkFlags | fs.linkFlags;
|
||||
info.layout = m_bindings->getPipelineLayout(true);
|
||||
info.basePipelineIndex = -1;
|
||||
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
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));
|
||||
|
||||
return pipeline;
|
||||
|
|
|
@ -381,19 +381,16 @@ namespace dxvk {
|
|||
struct DxvkGraphicsPipelineBaseInstanceKey {
|
||||
const DxvkGraphicsPipelineVertexInputLibrary* viLibrary = nullptr;
|
||||
const DxvkGraphicsPipelineFragmentOutputLibrary* foLibrary = nullptr;
|
||||
DxvkShaderPipelineLibraryCompileArgs args;
|
||||
|
||||
bool eq(const DxvkGraphicsPipelineBaseInstanceKey& other) const {
|
||||
return viLibrary == other.viLibrary
|
||||
&& foLibrary == other.foLibrary
|
||||
&& args == other.args;
|
||||
&& foLibrary == other.foLibrary;
|
||||
}
|
||||
|
||||
size_t hash() const {
|
||||
DxvkHashState hash;
|
||||
hash.add(size_t(viLibrary));
|
||||
hash.add(size_t(foLibrary));
|
||||
hash.add(args.hash());
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "dxvk_openvr.h"
|
||||
#include "dxvk_openxr.h"
|
||||
#include "dxvk_platform_exts.h"
|
||||
#include "../wsi/wsi_platform.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
@ -20,6 +21,8 @@ namespace dxvk {
|
|||
Logger::info(str::format("Game: ", env::getExeName()));
|
||||
Logger::info(str::format("DXVK: ", DXVK_VERSION));
|
||||
|
||||
wsi::init();
|
||||
|
||||
m_config = Config::getUserConfig();
|
||||
m_config.merge(Config::getAppConfig(env::getExePath()));
|
||||
m_config.logOptions();
|
||||
|
@ -64,6 +67,8 @@ namespace dxvk {
|
|||
DxvkInstance::~DxvkInstance() {
|
||||
if (m_messenger)
|
||||
m_vki->vkDestroyDebugUtilsMessengerEXT(m_vki->instance(), m_messenger, nullptr);
|
||||
|
||||
wsi::quit();
|
||||
}
|
||||
|
||||
|
||||
|
@ -177,8 +182,8 @@ namespace dxvk {
|
|||
appInfo.pApplicationName = appName.c_str();
|
||||
appInfo.applicationVersion = flags.raw();
|
||||
appInfo.pEngineName = "DXVK";
|
||||
appInfo.engineVersion = VK_MAKE_VERSION(2, 3, 1);
|
||||
appInfo.apiVersion = VK_MAKE_VERSION(1, 3, 0);
|
||||
appInfo.engineVersion = VK_MAKE_API_VERSION(0, 2, 3, 2);
|
||||
appInfo.apiVersion = VK_MAKE_API_VERSION(0, 1, 3, 0);
|
||||
|
||||
VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
|
||||
info.pApplicationInfo = &appInfo;
|
||||
|
@ -251,7 +256,7 @@ namespace dxvk {
|
|||
filterFlags.set(DxvkDeviceFilterFlag::SkipCpuDevices);
|
||||
}
|
||||
|
||||
DxvkDeviceFilter filter(filterFlags);
|
||||
DxvkDeviceFilter filter(filterFlags, m_options);
|
||||
std::vector<Rc<DxvkAdapter>> result;
|
||||
|
||||
uint32_t numDGPU = 0;
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace dxvk {
|
|||
hud = config.getOption<std::string>("dxvk.hud", "");
|
||||
tearFree = config.getOption<Tristate>("dxvk.tearFree", Tristate::Auto);
|
||||
hideIntegratedGraphics = config.getOption<bool> ("dxvk.hideIntegratedGraphics", false);
|
||||
deviceFilter = config.getOption<std::string>("dxvk.deviceFilter", "");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,6 +41,9 @@ namespace dxvk {
|
|||
// present. May be necessary for some games that
|
||||
// incorrectly assume monitor layouts.
|
||||
bool hideIntegratedGraphics;
|
||||
|
||||
// Device name
|
||||
std::string deviceFilter;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -205,7 +205,7 @@ namespace dxvk {
|
|||
|
||||
|
||||
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) {
|
||||
for (uint32_t i = 0; i < layout.m_bindings.size(); i++)
|
||||
m_bindings[i].merge(layout.m_bindings[i]);
|
||||
|
||||
addPushConstantRange(layout.m_pushConst);
|
||||
m_pushConstStages |= layout.m_pushConstStages;
|
||||
}
|
||||
|
||||
|
||||
|
@ -266,6 +272,9 @@ namespace dxvk {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (m_pushConstStages != other.m_pushConstStages)
|
||||
return false;
|
||||
|
||||
if (m_pushConst.stageFlags != other.m_pushConst.stageFlags
|
||||
|| m_pushConst.offset != other.m_pushConst.offset
|
||||
|| m_pushConst.size != other.m_pushConst.size)
|
||||
|
@ -282,6 +291,7 @@ namespace dxvk {
|
|||
for (uint32_t i = 0; i < m_bindings.size(); i++)
|
||||
hash.add(m_bindings[i].hash());
|
||||
|
||||
hash.add(m_pushConstStages);
|
||||
hash.add(m_pushConst.stageFlags);
|
||||
hash.add(m_pushConst.offset);
|
||||
hash.add(m_pushConst.size);
|
||||
|
@ -334,15 +344,16 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
// 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 };
|
||||
pipelineLayoutInfo.setLayoutCount = setCount;
|
||||
pipelineLayoutInfo.pSetLayouts = setLayouts.data();
|
||||
|
||||
if (pushConst.stageFlags && pushConst.size) {
|
||||
if (pushConstComplete.stageFlags && pushConstComplete.size) {
|
||||
pipelineLayoutInfo.pushConstantRangeCount = 1;
|
||||
pipelineLayoutInfo.pPushConstantRanges = &pushConst;
|
||||
pipelineLayoutInfo.pPushConstantRanges = &pushConstComplete;
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
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))
|
||||
throw DxvkError("DxvkBindingLayoutObjects: Failed to create pipeline layout");
|
||||
}
|
||||
|
|
|
@ -292,8 +292,19 @@ namespace dxvk {
|
|||
* \brief Retrieves push constant range
|
||||
* \returns Push constant range
|
||||
*/
|
||||
VkPushConstantRange getPushConstantRange() const {
|
||||
return m_pushConst;
|
||||
VkPushConstantRange getPushConstantRange(bool independent) const {
|
||||
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);
|
||||
|
||||
/**
|
||||
* \brief Adds a stage that actively uses push constants
|
||||
* \param [in] stage Shader stage
|
||||
*/
|
||||
void addPushConstantStage(VkShaderStageFlagBits stage);
|
||||
|
||||
/**
|
||||
* \brief Merges binding layouts
|
||||
*
|
||||
|
@ -353,6 +370,7 @@ namespace dxvk {
|
|||
|
||||
std::array<DxvkBindingList, DxvkDescriptorSets::SetCount> m_bindings;
|
||||
VkPushConstantRange m_pushConst;
|
||||
VkShaderStageFlags m_pushConstStages;
|
||||
VkShaderStageFlags m_stages;
|
||||
|
||||
};
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
#include "../dxvk_platform_exts.h"
|
||||
#include "dxvk_platform_exts.h"
|
||||
#include "../wsi/wsi_platform.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
DxvkPlatformExts DxvkPlatformExts::s_instance;
|
||||
|
||||
std::string_view DxvkPlatformExts::getName() {
|
||||
return "Win32 WSI";
|
||||
return "Platform WSI";
|
||||
}
|
||||
|
||||
|
||||
DxvkNameSet DxvkPlatformExts::getInstanceExtensions() {
|
||||
std::vector<const char *> extensionNames = wsi::getInstanceExtensions();
|
||||
|
||||
DxvkNameSet names;
|
||||
names.add(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
|
||||
for (const char* name : extensionNames)
|
||||
names.add(name);
|
||||
|
||||
return names;
|
||||
}
|
||||
|
@ -33,4 +37,4 @@ namespace dxvk {
|
|||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -382,6 +382,9 @@ namespace dxvk {
|
|||
|
||||
|
||||
bool Presenter::supportsColorSpace(VkColorSpaceKHR colorspace) {
|
||||
if (!m_surface)
|
||||
return false;
|
||||
|
||||
std::vector<VkSurfaceFormatKHR> surfaceFormats;
|
||||
getSupportedFormats(surfaceFormats, VK_FULL_SCREEN_EXCLUSIVE_DEFAULT_EXT);
|
||||
|
||||
|
|
|
@ -59,8 +59,8 @@ namespace dxvk {
|
|||
|
||||
if (info.pushConstSize) {
|
||||
VkPushConstantRange pushConst;
|
||||
pushConst.stageFlags = info.stage;
|
||||
pushConst.offset = info.pushConstOffset;
|
||||
pushConst.stageFlags = info.pushConstStages;
|
||||
pushConst.offset = 0;
|
||||
pushConst.size = info.pushConstSize;
|
||||
|
||||
m_bindings.addPushConstantRange(pushConst);
|
||||
|
@ -75,6 +75,8 @@ namespace dxvk {
|
|||
|
||||
// Run an analysis pass over the SPIR-V code to gather some
|
||||
// info that we may need during pipeline compilation.
|
||||
bool usesPushConstants = false;
|
||||
|
||||
std::vector<BindingOffsets> bindingOffsets;
|
||||
std::vector<uint32_t> varIds;
|
||||
std::vector<uint32_t> sampleMaskIds;
|
||||
|
@ -154,6 +156,9 @@ namespace dxvk {
|
|||
if (std::find(sampleMaskIds.begin(), sampleMaskIds.end(), ins.arg(2)) != sampleMaskIds.end())
|
||||
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.
|
||||
|
@ -169,6 +174,11 @@ namespace dxvk {
|
|||
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
|
||||
// doesn't actually support pipeline libraries
|
||||
m_needsLibraryCompile = canUsePipelineLibrary(true);
|
||||
|
@ -1012,7 +1022,7 @@ namespace dxvk {
|
|||
|
||||
|
||||
DxvkShaderPipelineLibrary::~DxvkShaderPipelineLibrary() {
|
||||
this->destroyShaderPipelinesLocked();
|
||||
this->destroyShaderPipelineLocked();
|
||||
}
|
||||
|
||||
|
||||
|
@ -1032,22 +1042,17 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
VkPipeline DxvkShaderPipelineLibrary::acquirePipelineHandle(
|
||||
const DxvkShaderPipelineLibraryCompileArgs& args) {
|
||||
DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::acquirePipelineHandle() {
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
if (m_device->mustTrackPipelineLifetime())
|
||||
m_useCount += 1;
|
||||
|
||||
VkPipeline& pipeline = (m_shaders.vs && !args.depthClipEnable)
|
||||
? m_pipelineNoDepthClip
|
||||
: m_pipeline;
|
||||
if (m_pipeline.handle)
|
||||
return m_pipeline;
|
||||
|
||||
if (pipeline)
|
||||
return pipeline;
|
||||
|
||||
pipeline = compileShaderPipelineLocked(args);
|
||||
return pipeline;
|
||||
m_pipeline = compileShaderPipelineLocked();
|
||||
return m_pipeline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1056,7 +1061,7 @@ namespace dxvk {
|
|||
std::lock_guard lock(m_mutex);
|
||||
|
||||
if (!(--m_useCount))
|
||||
this->destroyShaderPipelinesLocked();
|
||||
this->destroyShaderPipelineLocked();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1069,17 +1074,16 @@ namespace dxvk {
|
|||
return;
|
||||
|
||||
// Compile the pipeline with default args
|
||||
VkPipeline pipeline = compileShaderPipelineLocked(
|
||||
DxvkShaderPipelineLibraryCompileArgs());
|
||||
DxvkShaderPipelineLibraryHandle pipeline = compileShaderPipelineLocked();
|
||||
|
||||
// On 32-bit, destroy the pipeline immediately in order to
|
||||
// save memory. We should hit the driver's disk cache once
|
||||
// we need to recreate the pipeline.
|
||||
if (m_device->mustTrackPipelineLifetime()) {
|
||||
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
|
||||
|
@ -1087,37 +1091,32 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void DxvkShaderPipelineLibrary::destroyShaderPipelinesLocked() {
|
||||
void DxvkShaderPipelineLibrary::destroyShaderPipelineLocked() {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
vk->vkDestroyPipeline(vk->device(), m_pipeline, nullptr);
|
||||
vk->vkDestroyPipeline(vk->device(), m_pipelineNoDepthClip, nullptr);
|
||||
vk->vkDestroyPipeline(vk->device(), m_pipeline.handle, nullptr);
|
||||
|
||||
m_pipeline = VK_NULL_HANDLE;
|
||||
m_pipelineNoDepthClip = VK_NULL_HANDLE;
|
||||
m_pipeline.handle = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
|
||||
VkPipeline DxvkShaderPipelineLibrary::compileShaderPipelineLocked(
|
||||
const DxvkShaderPipelineLibraryCompileArgs& args) {
|
||||
DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::compileShaderPipelineLocked() {
|
||||
this->notifyLibraryCompile();
|
||||
|
||||
// If this is not the first time we're compiling the pipeline,
|
||||
// try to get a cache hit using the shader module identifier
|
||||
// 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()) {
|
||||
pipeline = this->compileShaderPipeline(args,
|
||||
VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT);
|
||||
}
|
||||
if (m_compiledOnce && canUsePipelineCacheControl())
|
||||
pipeline = this->compileShaderPipeline(VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT);
|
||||
|
||||
if (!pipeline)
|
||||
pipeline = this->compileShaderPipeline(args, 0);
|
||||
if (!pipeline.handle)
|
||||
pipeline = this->compileShaderPipeline(0);
|
||||
|
||||
// Well that didn't work
|
||||
if (!pipeline)
|
||||
return VK_NULL_HANDLE;
|
||||
if (!pipeline.handle)
|
||||
return { VK_NULL_HANDLE, 0 };
|
||||
|
||||
// Increment stat counter the first time this
|
||||
// shader pipeline gets compiled successfully
|
||||
|
@ -1134,8 +1133,7 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
VkPipeline DxvkShaderPipelineLibrary::compileShaderPipeline(
|
||||
const DxvkShaderPipelineLibraryCompileArgs& args,
|
||||
DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::compileShaderPipeline(
|
||||
VkPipelineCreateFlags flags) {
|
||||
DxvkShaderStageInfo stageInfo(m_device);
|
||||
VkShaderStageFlags stageMask = getShaderStages();
|
||||
|
@ -1151,7 +1149,7 @@ namespace dxvk {
|
|||
// Fail if we have no idenfitier for whatever reason, caller
|
||||
// should fall back to the slow path if this happens
|
||||
if (!identifier->identifierSize)
|
||||
return VK_NULL_HANDLE;
|
||||
return { VK_NULL_HANDLE, 0 };
|
||||
|
||||
stageInfo.addStage(stage, *identifier, nullptr);
|
||||
} else {
|
||||
|
@ -1168,22 +1166,21 @@ namespace dxvk {
|
|||
}
|
||||
}
|
||||
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
|
||||
if (stageMask & VK_SHADER_STAGE_VERTEX_BIT)
|
||||
return compileVertexShaderPipeline(args, stageInfo, flags);
|
||||
|
||||
if (stageMask & VK_SHADER_STAGE_FRAGMENT_BIT)
|
||||
return compileFragmentShaderPipeline(stageInfo, flags);
|
||||
|
||||
if (stageMask & VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
return compileComputeShaderPipeline(stageInfo, flags);
|
||||
pipeline = compileVertexShaderPipeline(stageInfo, flags);
|
||||
else if (stageMask & VK_SHADER_STAGE_FRAGMENT_BIT)
|
||||
pipeline = compileFragmentShaderPipeline(stageInfo, flags);
|
||||
else if (stageMask & VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
pipeline = compileComputeShaderPipeline(stageInfo, flags);
|
||||
|
||||
// Should be unreachable
|
||||
return VK_NULL_HANDLE;
|
||||
return { pipeline, flags };
|
||||
}
|
||||
|
||||
|
||||
VkPipeline DxvkShaderPipelineLibrary::compileVertexShaderPipeline(
|
||||
const DxvkShaderPipelineLibraryCompileArgs& args,
|
||||
const DxvkShaderStageInfo& stageInfo,
|
||||
VkPipelineCreateFlags flags) {
|
||||
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
|
||||
if (!m_device->features().extExtendedDynamicState3.extendedDynamicState3DepthClipEnable) {
|
||||
rsDepthClipInfo.pNext = std::exchange(rsInfo.pNext, &rsDepthClipInfo);
|
||||
rsDepthClipInfo.depthClipEnable = args.depthClipEnable;
|
||||
rsDepthClipInfo.depthClipEnable = VK_TRUE;
|
||||
}
|
||||
} 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
|
||||
|
@ -1256,7 +1253,7 @@ namespace dxvk {
|
|||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
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));
|
||||
|
||||
return vr ? VK_NULL_HANDLE : pipeline;
|
||||
|
@ -1367,7 +1364,7 @@ namespace dxvk {
|
|||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
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));
|
||||
|
||||
return vr ? VK_NULL_HANDLE : pipeline;
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace dxvk {
|
|||
/// Flat shading input mask
|
||||
uint32_t flatShadingInputs = 0;
|
||||
/// Push constant range
|
||||
uint32_t pushConstOffset = 0;
|
||||
VkShaderStageFlags pushConstStages = 0;
|
||||
uint32_t pushConstSize = 0;
|
||||
/// Uniform buffer data
|
||||
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
|
||||
*
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -521,11 +512,9 @@ namespace dxvk {
|
|||
* Either returns an already compiled pipeline library object, or
|
||||
* performs the compilation step if that has not happened yet.
|
||||
* Increments the use count by one.
|
||||
* \param [in] args Compile arguments
|
||||
* \returns Vulkan pipeline handle
|
||||
*/
|
||||
VkPipeline acquirePipelineHandle(
|
||||
const DxvkShaderPipelineLibraryCompileArgs& args);
|
||||
DxvkShaderPipelineLibraryHandle acquirePipelineHandle();
|
||||
|
||||
/**
|
||||
* \brief Releases pipeline
|
||||
|
@ -552,26 +541,22 @@ namespace dxvk {
|
|||
DxvkShaderSet m_shaders;
|
||||
const DxvkBindingLayoutObjects* m_layout;
|
||||
|
||||
dxvk::mutex m_mutex;
|
||||
VkPipeline m_pipeline = VK_NULL_HANDLE;
|
||||
VkPipeline m_pipelineNoDepthClip = VK_NULL_HANDLE;
|
||||
uint32_t m_useCount = 0u;
|
||||
bool m_compiledOnce = false;
|
||||
dxvk::mutex m_mutex;
|
||||
DxvkShaderPipelineLibraryHandle m_pipeline = { VK_NULL_HANDLE, 0 };
|
||||
uint32_t m_useCount = 0u;
|
||||
bool m_compiledOnce = false;
|
||||
|
||||
dxvk::mutex m_identifierMutex;
|
||||
DxvkShaderIdentifierSet m_identifiers;
|
||||
dxvk::mutex m_identifierMutex;
|
||||
DxvkShaderIdentifierSet m_identifiers;
|
||||
|
||||
void destroyShaderPipelinesLocked();
|
||||
void destroyShaderPipelineLocked();
|
||||
|
||||
VkPipeline compileShaderPipelineLocked(
|
||||
const DxvkShaderPipelineLibraryCompileArgs& args);
|
||||
DxvkShaderPipelineLibraryHandle compileShaderPipelineLocked();
|
||||
|
||||
VkPipeline compileShaderPipeline(
|
||||
const DxvkShaderPipelineLibraryCompileArgs& args,
|
||||
DxvkShaderPipelineLibraryHandle compileShaderPipeline(
|
||||
VkPipelineCreateFlags flags);
|
||||
|
||||
VkPipeline compileVertexShaderPipeline(
|
||||
const DxvkShaderPipelineLibraryCompileArgs& args,
|
||||
const DxvkShaderStageInfo& stageInfo,
|
||||
VkPipelineCreateFlags flags);
|
||||
|
||||
|
|
|
@ -329,6 +329,8 @@ namespace dxvk {
|
|||
|
||||
DxvkShaderCreateInfo vsInfo;
|
||||
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vsInfo.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
vsInfo.pushConstSize = sizeof(PresenterArgs);
|
||||
vsInfo.outputMask = 0x1;
|
||||
m_vs = new DxvkShader(vsInfo, std::move(vsCode));
|
||||
|
||||
|
@ -336,6 +338,7 @@ namespace dxvk {
|
|||
fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fsInfo.bindingCount = fsBindings.size();
|
||||
fsInfo.bindings = fsBindings.data();
|
||||
fsInfo.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fsInfo.pushConstSize = sizeof(PresenterArgs);
|
||||
fsInfo.inputMask = 0x1;
|
||||
fsInfo.outputMask = 0x1;
|
||||
|
|
|
@ -129,12 +129,8 @@ namespace dxvk::hud {
|
|||
|
||||
std::string driverInfo = props.vk12.driverInfo;
|
||||
|
||||
if (driverInfo.empty()) {
|
||||
driverInfo = str::format(
|
||||
VK_VERSION_MAJOR(props.core.properties.driverVersion), ".",
|
||||
VK_VERSION_MINOR(props.core.properties.driverVersion), ".",
|
||||
VK_VERSION_PATCH(props.core.properties.driverVersion));
|
||||
}
|
||||
if (driverInfo.empty())
|
||||
driverInfo = props.driverVersion.toString();
|
||||
|
||||
m_deviceName = props.core.properties.deviceName;
|
||||
m_driverName = str::format("Driver: ", props.vk12.driverName);
|
||||
|
|
|
@ -183,14 +183,17 @@ namespace dxvk::hud {
|
|||
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vsInfo.bindingCount = vsBindings.size();
|
||||
vsInfo.bindings = vsBindings.data();
|
||||
vsInfo.outputMask = 0x3;
|
||||
vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vsInfo.pushConstSize = sizeof(HudTextPushConstants);
|
||||
vsInfo.outputMask = 0x3;
|
||||
result.vert = new DxvkShader(vsInfo, std::move(vsCode));
|
||||
|
||||
DxvkShaderCreateInfo fsInfo;
|
||||
fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fsInfo.bindingCount = fsBindings.size();
|
||||
fsInfo.bindings = fsBindings.data();
|
||||
fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
fsInfo.pushConstSize = sizeof(HudTextPushConstants);
|
||||
fsInfo.inputMask = 0x3;
|
||||
fsInfo.outputMask = 0x1;
|
||||
result.frag = new DxvkShader(fsInfo, std::move(fsCode));
|
||||
|
@ -212,6 +215,7 @@ namespace dxvk::hud {
|
|||
DxvkShaderCreateInfo vsInfo;
|
||||
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vsInfo.outputMask = 0x1;
|
||||
vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
vsInfo.pushConstSize = sizeof(HudGraphPushConstants);
|
||||
result.vert = new DxvkShader(vsInfo, std::move(vsCode));
|
||||
|
||||
|
@ -221,6 +225,7 @@ namespace dxvk::hud {
|
|||
fsInfo.bindings = fsBindings.data();
|
||||
fsInfo.inputMask = 0x1;
|
||||
fsInfo.outputMask = 0x1;
|
||||
fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fsInfo.pushConstSize = sizeof(HudGraphPushConstants);
|
||||
result.frag = new DxvkShader(fsInfo, std::move(fsCode));
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@ dxvk_src = [
|
|||
'dxvk_options.cpp',
|
||||
'dxvk_pipelayout.cpp',
|
||||
'dxvk_pipemanager.cpp',
|
||||
'dxvk_platform_exts.cpp',
|
||||
'dxvk_presenter.cpp',
|
||||
'dxvk_queue.cpp',
|
||||
'dxvk_resource.cpp',
|
||||
|
@ -117,20 +118,6 @@ if platform == 'windows'
|
|||
]
|
||||
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') ]
|
||||
if platform == 'linux'
|
||||
dxvk_extra_deps += [ cpp.find_library('dl', required: false) ]
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -13,7 +13,10 @@
|
|||
|
||||
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 */
|
||||
{ R"(\\ACS\.exe$)", {{
|
||||
{ "dxgi.customVendorId", "10de" },
|
||||
|
@ -122,8 +125,6 @@ namespace dxvk {
|
|||
}} },
|
||||
/* NieR Replicant */
|
||||
{ R"(\\NieR Replicant ver\.1\.22474487139\.exe)", {{
|
||||
{ "dxgi.syncInterval", "1" },
|
||||
{ "dxgi.maxFrameRate", "60" },
|
||||
{ "d3d11.cachedDynamicResources", "vi" },
|
||||
}} },
|
||||
/* SteamVR performance test */
|
||||
|
@ -138,6 +139,10 @@ namespace dxvk {
|
|||
{ R"(\\h1(_[ms]p64_ship|-mod)\.exe$)", {{
|
||||
{ "dxgi.customVendorId", "10de" },
|
||||
}} },
|
||||
/* H2M-Mod - Modern Warfare Remastered */
|
||||
{ R"(\\h2m-mod\.exe$)", {{
|
||||
{ "dxgi.customVendorId", "10de" },
|
||||
}} },
|
||||
/* Modern Warfare 2 Campaign Remastered *
|
||||
* AMD AGS crash same as above */
|
||||
{ R"(\\MW2CR\.exe$)", {{
|
||||
|
@ -331,11 +336,6 @@ namespace dxvk {
|
|||
{ R"(\\SonicFrontiers\.exe$)", {{
|
||||
{ "dxgi.maxFrameLatency", "1" },
|
||||
}} },
|
||||
/* TRAHA Global *
|
||||
* Shadow issues when it sees AMD/Nvidia */
|
||||
{ R"(\\RapaNui-Win64-Shipping\.exe$)", {{
|
||||
{ "dxgi.customVendorId", "8086" },
|
||||
}} },
|
||||
/* SpellForce 3 Reforced & expansions *
|
||||
* Greatly improves CPU bound performance */
|
||||
{ R"(\\SF3ClientFinal\.exe$)", {{
|
||||
|
@ -370,6 +370,14 @@ namespace dxvk {
|
|||
{ R"(\\EADesktop\.exe$)", {{
|
||||
{ "dxvk.maxChunkSize", "1" },
|
||||
}} },
|
||||
/* Origin app (legacy EA Desktop) */
|
||||
{ R"(\\Origin\\(Origin|OriginWebHelperService)\.exe$)", {{
|
||||
{ "dxvk.maxChunkSize", "1" },
|
||||
}} },
|
||||
/* Ubisoft Connect (UPlay) */
|
||||
{ R"(\\Ubisoft\\Ubisoft Game Launcher\\(UbisoftConnect|upc)\.exe$)", {{
|
||||
{ "dxvk.maxChunkSize", "1" },
|
||||
}} },
|
||||
/* GOG Galaxy */
|
||||
{ R"(\\GalaxyClient\.exe$)", {{
|
||||
{ "dxvk.maxChunkSize", "1" },
|
||||
|
@ -411,11 +419,6 @@ namespace dxvk {
|
|||
{ R"(\\RidersRepublic(_BE)?\.exe$)", {{
|
||||
{ "dxgi.hideAmdGpu", "True" },
|
||||
}} },
|
||||
/* HoloCure - Save the Fans!
|
||||
Same as Cyberpunk 2077 */
|
||||
{ R"(\\HoloCure\.exe$)", {{
|
||||
{ "dxgi.useMonitorFallback", "True" },
|
||||
}} },
|
||||
/* Kenshi *
|
||||
* Helps CPU bound performance */
|
||||
{ R"(\\kenshi_x64\.exe$)", {{
|
||||
|
@ -429,6 +432,25 @@ namespace dxvk {
|
|||
{ "d3d11.exposeDriverCommandLists", "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" },
|
||||
}} },
|
||||
/* Ghostbusters: The Video Game Remastered *
|
||||
* Flickering on character faces */
|
||||
{ R"(\\ghost\.exe$)", {{
|
||||
{ "d3d11.longMad", "True" },
|
||||
}} },
|
||||
/* Watch_Dogs - Some objects flicker without */
|
||||
{ R"(\\watch_dogs\.exe$)", {{
|
||||
{ "d3d11.longMad", "True" },
|
||||
}} },
|
||||
|
||||
/**********************************************/
|
||||
/* D3D9 GAMES */
|
||||
|
@ -877,6 +899,27 @@ namespace dxvk {
|
|||
{ R"(\\ShippingPC-SkyGame\.exe$)", {{
|
||||
{ "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 +944,6 @@ namespace dxvk {
|
|||
{ R"(\\RiftApart\.exe$)", {{
|
||||
{ "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
|
||||
* by available VRAM, which causes issues on some
|
||||
* systems with integrated graphics. */
|
||||
|
@ -922,6 +958,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) {
|
||||
return ch == ' ' || ch == '\x9' || ch == '\r';
|
||||
}
|
||||
|
@ -1171,20 +1229,22 @@ namespace dxvk {
|
|||
|
||||
|
||||
Config Config::getAppConfig(const std::string& appName) {
|
||||
auto appConfig = std::find_if(g_appDefaults.begin(), g_appDefaults.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);
|
||||
});
|
||||
const Config* config = nullptr;
|
||||
|
||||
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
|
||||
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));
|
||||
|
||||
return appConfig->second;
|
||||
return *config;
|
||||
}
|
||||
|
||||
return Config();
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
|
||||
namespace dxvk {
|
||||
|
||||
GpuFlushTracker::GpuFlushTracker(
|
||||
bool ensureReproducibleHeuristic)
|
||||
: m_ensureReproducibleHeuristic(ensureReproducibleHeuristic) {
|
||||
|
||||
}
|
||||
|
||||
bool GpuFlushTracker::considerFlush(
|
||||
GpuFlushType flushType,
|
||||
uint64_t chunkId,
|
||||
|
@ -17,6 +23,9 @@ namespace dxvk {
|
|||
if (!chunkCount)
|
||||
return false;
|
||||
|
||||
if (m_ensureReproducibleHeuristic && flushType != GpuFlushType::ExplicitFlush)
|
||||
return false;
|
||||
|
||||
// Take any earlier missed flush with a stronger hint into account, so
|
||||
// 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.
|
||||
|
|
|
@ -34,6 +34,8 @@ namespace dxvk {
|
|||
|
||||
public:
|
||||
|
||||
GpuFlushTracker(bool ensureReproducibleHeuristic);
|
||||
|
||||
/**
|
||||
* \brief Checks whether a context flush should be performed
|
||||
*
|
||||
|
@ -61,6 +63,8 @@ namespace dxvk {
|
|||
|
||||
private:
|
||||
|
||||
bool m_ensureReproducibleHeuristic;
|
||||
|
||||
GpuFlushType m_lastMissedType = GpuFlushType::ImplicitWeakHint;
|
||||
|
||||
uint64_t m_lastFlushChunkId = 0ull;
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
#if defined(DXVK_WSI_GLFW)
|
||||
|
||||
#include "../wsi_monitor.h"
|
||||
|
||||
#include "wsi/native_wsi.h"
|
||||
|
@ -11,22 +13,22 @@
|
|||
|
||||
namespace dxvk::wsi {
|
||||
|
||||
HMONITOR getDefaultMonitor() {
|
||||
HMONITOR GlfwWsiDriver::getDefaultMonitor() {
|
||||
return enumMonitors(0);
|
||||
}
|
||||
|
||||
|
||||
HMONITOR enumMonitors(uint32_t index) {
|
||||
HMONITOR GlfwWsiDriver::enumMonitors(uint32_t index) {
|
||||
return isDisplayValid(int32_t(index))
|
||||
? toHmonitor(index)
|
||||
: 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);
|
||||
}
|
||||
|
||||
bool getDisplayName(
|
||||
bool GlfwWsiDriver::getDisplayName(
|
||||
HMONITOR hMonitor,
|
||||
WCHAR (&Name)[32]) {
|
||||
const int32_t displayId = fromHmonitor(hMonitor);
|
||||
|
@ -46,7 +48,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool getDesktopCoordinates(
|
||||
bool GlfwWsiDriver::getDesktopCoordinates(
|
||||
HMONITOR hMonitor,
|
||||
RECT* pRect) {
|
||||
const int32_t displayId = fromHmonitor(hMonitor);
|
||||
|
@ -97,7 +99,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool getDisplayMode(
|
||||
bool GlfwWsiDriver::getDisplayMode(
|
||||
HMONITOR hMonitor,
|
||||
uint32_t ModeNumber,
|
||||
WsiMode* pMode) {
|
||||
|
@ -121,7 +123,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool getCurrentDisplayMode(
|
||||
bool GlfwWsiDriver::getCurrentDisplayMode(
|
||||
HMONITOR hMonitor,
|
||||
WsiMode* pMode) {
|
||||
const int32_t displayId = fromHmonitor(hMonitor);
|
||||
|
@ -141,7 +143,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool getDesktopDisplayMode(
|
||||
bool GlfwWsiDriver::getDesktopDisplayMode(
|
||||
HMONITOR hMonitor,
|
||||
WsiMode* pMode) {
|
||||
const int32_t displayId = fromHmonitor(hMonitor);
|
||||
|
@ -159,9 +161,11 @@ namespace dxvk::wsi {
|
|||
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.");
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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
|
|
@ -3,21 +3,108 @@
|
|||
#include "../../vulkan/vulkan_loader.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "../wsi_monitor.h"
|
||||
#include "../wsi_platform.h"
|
||||
|
||||
namespace dxvk::wsi {
|
||||
|
||||
/**
|
||||
* \brief Impl-dependent state
|
||||
*/
|
||||
struct DxvkWindowState {
|
||||
class GlfwWsiDriver : public WsiDriver {
|
||||
private:
|
||||
HMODULE libglfw;
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -1,6 +1,8 @@
|
|||
#if defined(DXVK_WSI_GLFW)
|
||||
|
||||
#include "../wsi_window.h"
|
||||
|
||||
#include "native/wsi/native_wsi.h"
|
||||
#include "native/wsi/native_glfw.h"
|
||||
#include "wsi_platform_glfw.h"
|
||||
|
||||
#include "../../util/util_string.h"
|
||||
|
@ -12,7 +14,7 @@
|
|||
|
||||
namespace dxvk::wsi {
|
||||
|
||||
void getWindowSize(
|
||||
void GlfwWsiDriver::getWindowSize(
|
||||
HWND hWindow,
|
||||
uint32_t* pWidth,
|
||||
uint32_t* pHeight) {
|
||||
|
@ -29,7 +31,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
void resizeWindow(
|
||||
void GlfwWsiDriver::resizeWindow(
|
||||
HWND hWindow,
|
||||
DxvkWindowState* pState,
|
||||
uint32_t Width,
|
||||
|
@ -40,7 +42,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool setWindowMode(
|
||||
bool GlfwWsiDriver::setWindowMode(
|
||||
HMONITOR hMonitor,
|
||||
HWND hWindow,
|
||||
const WsiMode& pMode) {
|
||||
|
@ -67,7 +69,7 @@ namespace dxvk::wsi {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool enterFullscreenMode(
|
||||
bool GlfwWsiDriver::enterFullscreenMode(
|
||||
HMONITOR hMonitor,
|
||||
HWND hWindow,
|
||||
DxvkWindowState* pState,
|
||||
|
@ -89,7 +91,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool leaveFullscreenMode(
|
||||
bool GlfwWsiDriver::leaveFullscreenMode(
|
||||
HWND hWindow,
|
||||
DxvkWindowState* pState,
|
||||
bool restoreCoordinates) {
|
||||
|
@ -103,13 +105,13 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool restoreDisplayMode() {
|
||||
bool GlfwWsiDriver::restoreDisplayMode() {
|
||||
// Don't need to do anything with GLFW here.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
HMONITOR getWindowMonitor(HWND hWindow) {
|
||||
HMONITOR GlfwWsiDriver::getWindowMonitor(HWND hWindow) {
|
||||
// TODO: implement this with glfwGetWindowMonitor
|
||||
// (or maybe not? glfwGetWindowMonitor only seems to reference *fullscreen* windows)
|
||||
// GLFWwindow* window = fromHwnd(hWindow);
|
||||
|
@ -119,19 +121,19 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool isWindow(HWND hWindow) {
|
||||
bool GlfwWsiDriver::isWindow(HWND hWindow) {
|
||||
GLFWwindow* window = fromHwnd(hWindow);
|
||||
return window != nullptr;
|
||||
}
|
||||
|
||||
void updateFullscreenWindow(
|
||||
void GlfwWsiDriver::updateFullscreenWindow(
|
||||
HMONITOR hMonitor,
|
||||
HWND hWindow,
|
||||
bool forceTopmost) {
|
||||
// Don't need to do anything with GLFW here.
|
||||
}
|
||||
|
||||
VkResult createSurface(
|
||||
VkResult GlfwWsiDriver::createSurface(
|
||||
HWND hWindow,
|
||||
PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,
|
||||
VkInstance instance,
|
||||
|
@ -141,4 +143,6 @@ namespace dxvk::wsi {
|
|||
return glfwCreateWindowSurface(instance, window, nullptr, pSurface);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,33 +1,24 @@
|
|||
wsi_common_src = [
|
||||
wsi_src = [
|
||||
'wsi_edid.cpp',
|
||||
]
|
||||
|
||||
wsi_win32_src = [
|
||||
'wsi_platform.cpp',
|
||||
'win32/wsi_monitor_win32.cpp',
|
||||
'win32/wsi_platform_win32.cpp',
|
||||
'win32/wsi_window_win32.cpp',
|
||||
]
|
||||
|
||||
wsi_sdl2_src = [
|
||||
'sdl2/wsi_monitor_sdl2.cpp',
|
||||
'sdl2/wsi_platform_sdl2.cpp',
|
||||
'sdl2/wsi_window_sdl2.cpp',
|
||||
]
|
||||
|
||||
wsi_glfw_src = [
|
||||
'glfw/wsi_monitor_glfw.cpp',
|
||||
'glfw/wsi_platform_glfw.cpp',
|
||||
'glfw/wsi_window_glfw.cpp',
|
||||
]
|
||||
|
||||
if dxvk_wsi == 'win32'
|
||||
wsi_src = wsi_common_src + wsi_win32_src
|
||||
wsi_deps = [ dep_displayinfo ]
|
||||
elif dxvk_wsi == 'sdl2'
|
||||
wsi_src = wsi_common_src + wsi_sdl2_src
|
||||
wsi_deps = [ dep_displayinfo, lib_sdl2 ]
|
||||
elif dxvk_wsi == 'glfw'
|
||||
wsi_src = wsi_common_src + wsi_glfw_src
|
||||
wsi_deps = [ dep_displayinfo, lib_glfw ]
|
||||
else
|
||||
error('Unknown wsi')
|
||||
wsi_deps = [ dep_displayinfo ]
|
||||
|
||||
if platform != 'windows'
|
||||
wsi_deps += [
|
||||
lib_sdl2.partial_dependency(compile_args: true, includes: true),
|
||||
lib_glfw.partial_dependency(compile_args: true, includes: true),
|
||||
]
|
||||
endif
|
||||
|
||||
wsi_lib = static_library('wsi', wsi_src,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#if defined(DXVK_WSI_SDL2)
|
||||
|
||||
#include "../wsi_monitor.h"
|
||||
|
||||
#include "wsi/native_wsi.h"
|
||||
|
@ -12,22 +14,22 @@
|
|||
|
||||
namespace dxvk::wsi {
|
||||
|
||||
HMONITOR getDefaultMonitor() {
|
||||
HMONITOR Sdl2WsiDriver::getDefaultMonitor() {
|
||||
return enumMonitors(0);
|
||||
}
|
||||
|
||||
|
||||
HMONITOR enumMonitors(uint32_t index) {
|
||||
HMONITOR Sdl2WsiDriver::enumMonitors(uint32_t index) {
|
||||
return isDisplayValid(int32_t(index))
|
||||
? toHmonitor(index)
|
||||
: 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);
|
||||
}
|
||||
|
||||
bool getDisplayName(
|
||||
bool Sdl2WsiDriver::getDisplayName(
|
||||
HMONITOR hMonitor,
|
||||
WCHAR (&Name)[32]) {
|
||||
const int32_t displayId = fromHmonitor(hMonitor);
|
||||
|
@ -47,7 +49,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool getDesktopCoordinates(
|
||||
bool Sdl2WsiDriver::getDesktopCoordinates(
|
||||
HMONITOR hMonitor,
|
||||
RECT* pRect) {
|
||||
const int32_t displayId = fromHmonitor(hMonitor);
|
||||
|
@ -90,7 +92,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool getDisplayMode(
|
||||
bool Sdl2WsiDriver::getDisplayMode(
|
||||
HMONITOR hMonitor,
|
||||
uint32_t ModeNumber,
|
||||
WsiMode* pMode) {
|
||||
|
@ -109,7 +111,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool getCurrentDisplayMode(
|
||||
bool Sdl2WsiDriver::getCurrentDisplayMode(
|
||||
HMONITOR hMonitor,
|
||||
WsiMode* pMode) {
|
||||
const int32_t displayId = fromHmonitor(hMonitor);
|
||||
|
@ -129,7 +131,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool getDesktopDisplayMode(
|
||||
bool Sdl2WsiDriver::getDesktopDisplayMode(
|
||||
HMONITOR hMonitor,
|
||||
WsiMode* pMode) {
|
||||
const int32_t displayId = fromHmonitor(hMonitor);
|
||||
|
@ -148,9 +150,11 @@ namespace dxvk::wsi {
|
|||
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.");
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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
|
|
@ -1,21 +1,108 @@
|
|||
#pragma once
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL.h>
|
||||
|
||||
#include "../wsi_monitor.h"
|
||||
#include "../wsi_platform.h"
|
||||
|
||||
namespace dxvk::wsi {
|
||||
|
||||
/**
|
||||
* \brief Impl-dependent state
|
||||
*/
|
||||
struct DxvkWindowState {
|
||||
class Sdl2WsiDriver : public WsiDriver {
|
||||
private:
|
||||
HMODULE libsdl;
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -1,17 +1,19 @@
|
|||
#if defined(DXVK_WSI_SDL2)
|
||||
|
||||
#include "../wsi_window.h"
|
||||
|
||||
#include "native/wsi/native_wsi.h"
|
||||
#include "native/wsi/native_sdl2.h"
|
||||
#include "wsi_platform_sdl2.h"
|
||||
|
||||
#include "../../util/util_string.h"
|
||||
#include "../../util/log/log.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
#include <SDL_vulkan.h>
|
||||
|
||||
namespace dxvk::wsi {
|
||||
|
||||
void getWindowSize(
|
||||
void Sdl2WsiDriver::getWindowSize(
|
||||
HWND hWindow,
|
||||
uint32_t* pWidth,
|
||||
uint32_t* pHeight) {
|
||||
|
@ -28,7 +30,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
void resizeWindow(
|
||||
void Sdl2WsiDriver::resizeWindow(
|
||||
HWND hWindow,
|
||||
DxvkWindowState* pState,
|
||||
uint32_t Width,
|
||||
|
@ -39,7 +41,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool setWindowMode(
|
||||
bool Sdl2WsiDriver::setWindowMode(
|
||||
HMONITOR hMonitor,
|
||||
HWND hWindow,
|
||||
const WsiMode& pMode) {
|
||||
|
@ -73,7 +75,7 @@ namespace dxvk::wsi {
|
|||
|
||||
|
||||
|
||||
bool enterFullscreenMode(
|
||||
bool Sdl2WsiDriver::enterFullscreenMode(
|
||||
HMONITOR hMonitor,
|
||||
HWND hWindow,
|
||||
DxvkWindowState* pState,
|
||||
|
@ -99,7 +101,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool leaveFullscreenMode(
|
||||
bool Sdl2WsiDriver::leaveFullscreenMode(
|
||||
HWND hWindow,
|
||||
DxvkWindowState* pState,
|
||||
bool restoreCoordinates) {
|
||||
|
@ -114,13 +116,13 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool restoreDisplayMode() {
|
||||
bool Sdl2WsiDriver::restoreDisplayMode() {
|
||||
// Don't need to do anything with SDL2 here.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
HMONITOR getWindowMonitor(HWND hWindow) {
|
||||
HMONITOR Sdl2WsiDriver::getWindowMonitor(HWND hWindow) {
|
||||
SDL_Window* window = fromHwnd(hWindow);
|
||||
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);
|
||||
return window != nullptr;
|
||||
}
|
||||
|
||||
|
||||
void updateFullscreenWindow(
|
||||
void Sdl2WsiDriver::updateFullscreenWindow(
|
||||
HMONITOR hMonitor,
|
||||
HWND hWindow,
|
||||
bool forceTopmost) {
|
||||
|
@ -142,7 +144,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
VkResult createSurface(
|
||||
VkResult Sdl2WsiDriver::createSurface(
|
||||
HWND hWindow,
|
||||
PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,
|
||||
VkInstance instance,
|
||||
|
@ -155,3 +157,5 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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/log/log.h"
|
||||
|
@ -13,7 +15,7 @@
|
|||
|
||||
namespace dxvk::wsi {
|
||||
|
||||
HMONITOR getDefaultMonitor() {
|
||||
HMONITOR Win32WsiDriver::getDefaultMonitor() {
|
||||
return ::MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY);
|
||||
}
|
||||
|
||||
|
@ -45,7 +47,7 @@ namespace dxvk::wsi {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
HMONITOR enumMonitors(uint32_t index) {
|
||||
HMONITOR Win32WsiDriver::enumMonitors(uint32_t index) {
|
||||
MonitorEnumInfo info;
|
||||
info.iMonitorId = index;
|
||||
info.oMonitor = nullptr;
|
||||
|
@ -58,7 +60,7 @@ namespace dxvk::wsi {
|
|||
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)
|
||||
return enumMonitors(index);
|
||||
|
||||
|
@ -132,7 +134,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool getDisplayName(
|
||||
bool Win32WsiDriver::getDisplayName(
|
||||
HMONITOR hMonitor,
|
||||
WCHAR (&Name)[32]) {
|
||||
// Query monitor info to get the device name
|
||||
|
@ -150,7 +152,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool getDesktopCoordinates(
|
||||
bool Win32WsiDriver::getDesktopCoordinates(
|
||||
HMONITOR hMonitor,
|
||||
RECT* pRect) {
|
||||
::MONITORINFOEXW monInfo;
|
||||
|
@ -200,7 +202,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool getDisplayMode(
|
||||
bool Win32WsiDriver::getDisplayMode(
|
||||
HMONITOR hMonitor,
|
||||
uint32_t modeNumber,
|
||||
WsiMode* pMode) {
|
||||
|
@ -208,14 +210,14 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool getCurrentDisplayMode(
|
||||
bool Win32WsiDriver::getCurrentDisplayMode(
|
||||
HMONITOR hMonitor,
|
||||
WsiMode* pMode) {
|
||||
return retrieveDisplayMode(hMonitor, ENUM_CURRENT_SETTINGS, pMode);
|
||||
}
|
||||
|
||||
|
||||
bool getDesktopDisplayMode(
|
||||
bool Win32WsiDriver::getDesktopDisplayMode(
|
||||
HMONITOR hMonitor,
|
||||
WsiMode* pMode) {
|
||||
return retrieveDisplayMode(hMonitor, ENUM_REGISTRY_SETTINGS, pMode);
|
||||
|
@ -308,7 +310,7 @@ namespace dxvk::wsi {
|
|||
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 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"));
|
||||
|
@ -370,3 +372,5 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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
|
|
@ -2,15 +2,91 @@
|
|||
|
||||
#include <windows.h>
|
||||
|
||||
#include "../wsi_platform.h"
|
||||
|
||||
namespace dxvk::wsi {
|
||||
|
||||
/**
|
||||
* \brief Impl-dependent state
|
||||
*/
|
||||
struct DxvkWindowState {
|
||||
LONG style = 0;
|
||||
LONG exstyle = 0;
|
||||
RECT rect = { 0, 0, 0, 0 };
|
||||
class Win32WsiDriver : public WsiDriver {
|
||||
public:
|
||||
// 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);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "../wsi_window.h"
|
||||
#include "../wsi_monitor.h"
|
||||
#if defined(DXVK_WSI_WIN32)
|
||||
|
||||
#include "wsi_platform_win32.h"
|
||||
|
||||
#include "../../util/util_string.h"
|
||||
#include "../../util/log/log.h"
|
||||
|
@ -94,7 +95,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
void getWindowSize(
|
||||
void Win32WsiDriver::getWindowSize(
|
||||
HWND hWindow,
|
||||
uint32_t* pWidth,
|
||||
uint32_t* pHeight) {
|
||||
|
@ -109,7 +110,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
void resizeWindow(
|
||||
void Win32WsiDriver::resizeWindow(
|
||||
HWND hWindow,
|
||||
DxvkWindowState* pState,
|
||||
uint32_t width,
|
||||
|
@ -130,7 +131,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool setWindowMode(
|
||||
bool Win32WsiDriver::setWindowMode(
|
||||
HMONITOR hMonitor,
|
||||
HWND hWindow,
|
||||
const WsiMode& mode) {
|
||||
|
@ -163,21 +164,21 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool enterFullscreenMode(
|
||||
bool Win32WsiDriver::enterFullscreenMode(
|
||||
HMONITOR hMonitor,
|
||||
HWND hWindow,
|
||||
DxvkWindowState* pState,
|
||||
[[maybe_unused]]
|
||||
bool modeSwitch) {
|
||||
// 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.
|
||||
LONG style = ::GetWindowLongW(hWindow, GWL_STYLE);
|
||||
LONG exstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE);
|
||||
|
||||
pState->style = style;
|
||||
pState->exstyle = exstyle;
|
||||
pState->win.style = style;
|
||||
pState->win.exstyle = exstyle;
|
||||
|
||||
style &= ~WS_OVERLAPPEDWINDOW;
|
||||
exstyle &= ~WS_EX_OVERLAPPEDWINDOW;
|
||||
|
@ -196,7 +197,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool leaveFullscreenMode(
|
||||
bool Win32WsiDriver::leaveFullscreenMode(
|
||||
HWND hWindow,
|
||||
DxvkWindowState* pState,
|
||||
bool restoreCoordinates) {
|
||||
|
@ -205,27 +206,27 @@ namespace dxvk::wsi {
|
|||
LONG curStyle = ::GetWindowLongW(hWindow, GWL_STYLE) & ~WS_VISIBLE;
|
||||
LONG curExstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE) & ~WS_EX_TOPMOST;
|
||||
|
||||
if (curStyle == (pState->style & ~(WS_VISIBLE | WS_OVERLAPPEDWINDOW))
|
||||
&& curExstyle == (pState->exstyle & ~(WS_EX_TOPMOST | WS_EX_OVERLAPPEDWINDOW))) {
|
||||
::SetWindowLongW(hWindow, GWL_STYLE, pState->style);
|
||||
::SetWindowLongW(hWindow, GWL_EXSTYLE, pState->exstyle);
|
||||
if (curStyle == (pState->win.style & ~(WS_VISIBLE | WS_OVERLAPPEDWINDOW))
|
||||
&& curExstyle == (pState->win.exstyle & ~(WS_EX_TOPMOST | WS_EX_OVERLAPPEDWINDOW))) {
|
||||
::SetWindowLongW(hWindow, GWL_STYLE, pState->win.style);
|
||||
::SetWindowLongW(hWindow, GWL_EXSTYLE, pState->win.exstyle);
|
||||
}
|
||||
|
||||
// Restore window position and apply the style
|
||||
UINT flags = SWP_FRAMECHANGED | SWP_NOACTIVATE;
|
||||
const RECT rect = pState->rect;
|
||||
const RECT rect = pState->win.rect;
|
||||
|
||||
if (!restoreCoordinates)
|
||||
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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool restoreDisplayMode() {
|
||||
bool Win32WsiDriver::restoreDisplayMode() {
|
||||
bool success = true;
|
||||
bool result = ::EnumDisplayMonitors(nullptr, nullptr,
|
||||
&restoreDisplayModeCallback,
|
||||
|
@ -235,7 +236,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
HMONITOR getWindowMonitor(HWND hWindow) {
|
||||
HMONITOR Win32WsiDriver::getWindowMonitor(HWND hWindow) {
|
||||
RECT windowRect = { 0, 0, 0, 0 };
|
||||
::GetWindowRect(hWindow, &windowRect);
|
||||
|
||||
|
@ -248,12 +249,12 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
bool isWindow(HWND hWindow) {
|
||||
bool Win32WsiDriver::isWindow(HWND hWindow) {
|
||||
return ::IsWindow(hWindow);
|
||||
}
|
||||
|
||||
|
||||
void updateFullscreenWindow(
|
||||
void Win32WsiDriver::updateFullscreenWindow(
|
||||
HMONITOR hMonitor,
|
||||
HWND hWindow,
|
||||
bool forceTopmost) {
|
||||
|
@ -274,7 +275,7 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
|
||||
VkResult createSurface(
|
||||
VkResult Win32WsiDriver::createSurface(
|
||||
HWND hWindow,
|
||||
PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,
|
||||
VkInstance instance,
|
||||
|
@ -296,3 +297,5 @@ namespace dxvk::wsi {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +1,114 @@
|
|||
#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)
|
||||
#include "win32/wsi_platform_win32.h"
|
||||
#elif defined(DXVK_WSI_SDL2)
|
||||
#include "sdl2/wsi_platform_sdl2.h"
|
||||
#elif defined(DXVK_WSI_GLFW)
|
||||
#include "glfw/wsi_platform_glfw.h"
|
||||
extern WsiBootstrap Win32WSI;
|
||||
#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();
|
||||
|
||||
}
|
||||
|
|
|
@ -3,12 +3,30 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include "wsi_monitor.h"
|
||||
#include "wsi_platform.h"
|
||||
|
||||
#include "../vulkan/vulkan_loader.h"
|
||||
|
||||
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
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue