Compare commits

...

48 Commits

Author SHA1 Message Date
Blisto91 c1f665f92b [util] Disable supportDFFormats for Prototype
Incorrect shadows on AMD & Intel
2024-05-18 19:31:07 +02:00
WinterSnowfall 20185a5309 [d3d9] Do not enable support for DF formats on Nvidia 2024-05-18 16:16:34 +00:00
Blisto91 0c2efda804 [meta] Add DXVK Native section to the readme 2024-05-16 10:57:35 +00:00
Blisto91 c7d61b2fc0 [native] Change DXVK_WSIDRIVER to DXVK_WSI_DRIVER 2024-05-16 10:57:35 +00:00
Ethan Lee 6259e86392 [meson] Use dependency() instead of find_library() for SDL2/GLFW detection.
Since we're not linking to the libraries anymore, it doesn't make much sense to
use find_library, and in fact we need to use dependency() in order to get the
right CFLAGS for includes, defines, etc, so use that instead.

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

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

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

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

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

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

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

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

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

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

View File

@ -163,3 +163,22 @@ For non debian based distros, make sure that your mingw-w64-gcc cross compiler
does have `--enable-threads=posix` enabled during configure. If your distro does
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.

View File

@ -499,6 +499,7 @@
# Supported values:
# - True/False
# d3d11.longMad = False
# d3d9.longMad = False
# Device Local Constant Buffers
@ -514,7 +515,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 +647,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

View File

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

View File

@ -115,7 +115,6 @@ if platform == 'windows'
)
endif
dxvk_wsi = 'win32'
dxvk_name_prefix = ''
compiler_args += ['-DDXVK_WSI_WIN32']
else
@ -128,15 +127,17 @@ 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_'
@ -160,7 +161,7 @@ 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@',

View File

@ -32,6 +32,7 @@ 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);
// Clamp LOD bias so that people don't abuse this in unintended ways
this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f);

View File

@ -120,6 +120,9 @@ 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;
};
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -594,7 +594,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 +664,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 +721,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 +3798,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 +3846,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 +3901,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 {
@ -7891,7 +7891,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);

View File

@ -336,7 +336,7 @@ namespace dxvk {
}
uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count) {
uint32_t SetupRenderStateBlock(SpirvModule& spvModule) {
uint32_t floatType = spvModule.defFloatType(32);
uint32_t 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());
}
@ -1384,20 +1379,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);
}

View File

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

View File

@ -438,7 +438,11 @@ namespace dxvk {
D3D9VkFormatTable::D3D9VkFormatTable(
const Rc<DxvkAdapter>& adapter,
const D3D9Options& options) {
m_dfSupport = options.supportDFFormats;
const auto& props = adapter->deviceProperties();
uint32_t vendorId = options.customVendorId == -1 ? props.vendorID : uint32_t(options.customVendorId);
m_dfSupport = options.supportDFFormats && DxvkGpuVendor(vendorId) != DxvkGpuVendor::Nvidia;
m_x4r4g4b4Support = options.supportX4R4G4B4;
m_d32supportFinal = options.supportD32;

View File

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

View File

@ -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));
}
@ -246,11 +241,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 +266,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 +285,6 @@ namespace dxvk {
ctx->writeTimestamp(m_query[0]);
break;
case D3DQUERYTYPE_VERTEXSTATS:
case D3DQUERYTYPE_OCCLUSION:
ctx->endQuery(m_query[0]);
break;
@ -314,7 +302,6 @@ namespace dxvk {
bool D3D9Query::QueryBeginnable(D3DQUERYTYPE QueryType) {
return QueryType == D3DQUERYTYPE_OCCLUSION
|| QueryType == D3DQUERYTYPE_VERTEXSTATS
|| QueryType == D3DQUERYTYPE_TIMESTAMPDISJOINT;
}
@ -338,7 +325,6 @@ namespace dxvk {
case D3DQUERYTYPE_TIMESTAMP:
case D3DQUERYTYPE_TIMESTAMPDISJOINT:
case D3DQUERYTYPE_TIMESTAMPFREQ:
case D3DQUERYTYPE_VERTEXSTATS:
return D3D_OK;
default:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,7 +182,7 @@ namespace dxvk {
appInfo.pApplicationName = appName.c_str();
appInfo.applicationVersion = flags.raw();
appInfo.pEngineName = "DXVK";
appInfo.engineVersion = VK_MAKE_VERSION(2, 3, 1);
appInfo.engineVersion = VK_MAKE_VERSION(2, 3, 2);
appInfo.apiVersion = VK_MAKE_VERSION(1, 3, 0);
VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,10 @@
namespace dxvk {
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$)", {{
@ -411,11 +411,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 +424,16 @@ 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" },
}} },
/**********************************************/
/* D3D9 GAMES */
@ -877,6 +882,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 +927,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 +941,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 +1212,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();

View File

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

View File

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

View File

@ -3,21 +3,108 @@
#include "../../vulkan/vulkan_loader.h"
#include <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;
}
}
}

View File

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

View File

@ -1,6 +1,8 @@
#if defined(DXVK_WSI_GLFW)
#include "../wsi_window.h"
#include "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

View File

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

View File

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

View File

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

View File

@ -1,21 +1,108 @@
#pragma once
#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;
}
}
}

View File

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

View File

@ -1,17 +1,19 @@
#if defined(DXVK_WSI_SDL2)
#include "../wsi_window.h"
#include "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

View File

@ -1,4 +1,6 @@
#include "../wsi_monitor.h"
#if defined(DXVK_WSI_WIN32)
#include "wsi_platform_win32.h"
#include "../../util/util_string.h"
#include "../../util/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

View File

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

View File

@ -2,15 +2,91 @@
#include <windows.h>
#include "../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);
};
}
}

View File

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

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

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

View File

@ -1,9 +1,114 @@
#pragma once
#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();
}

View File

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