mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-03-06 20:58:37 +01:00
Compare commits
304 commits
Author | SHA1 | Date | |
---|---|---|---|
|
a08579e555 | ||
|
45ec01a0a1 | ||
|
542e0d2ab0 | ||
|
91b48dd31d | ||
|
d8eb4d0d66 | ||
|
f161d9bc7b | ||
|
634f38b38d | ||
|
ff8378be19 | ||
|
ad75fb17cd | ||
|
76244812fb | ||
|
9c0dec9f58 | ||
|
a4a5bf5d63 | ||
|
49f0968f57 | ||
|
ec0deb73da | ||
|
fed51e6c92 | ||
|
196258111c | ||
|
d04fe1cdc0 | ||
|
3cf453160c | ||
|
9769df9dd8 | ||
|
106032fa65 | ||
|
d2b53b76de | ||
|
02dc403074 | ||
|
420d083677 | ||
|
cd29f0feaa | ||
|
baaf72a373 | ||
|
1f0ad760e1 | ||
|
fe58b393d4 | ||
|
c21f4e0190 | ||
|
416f9c5a4a | ||
|
5b68884fd9 | ||
|
8f84085370 | ||
|
b35e69b467 | ||
|
11dc0e7ce8 | ||
|
6b5d595b3e | ||
|
396a4e0235 | ||
|
c04410ca00 | ||
|
fcbdff3b72 | ||
|
0359a3521d | ||
|
b41253e18d | ||
|
96a260e94e | ||
|
2cb9d40f88 | ||
|
2cd305e606 | ||
|
ec5bd04378 | ||
|
43f360d20c | ||
|
6f59124e9a | ||
|
d6d13edb7a | ||
|
31192b6d3f | ||
|
31a4679960 | ||
|
13bd234cea | ||
|
dafb71b18e | ||
|
a0c8bbaf10 | ||
|
9c395fd60d | ||
|
866228a37d | ||
|
60bf1f9ec4 | ||
|
95e12decf1 | ||
|
e3f047a96a | ||
|
7208d06548 | ||
|
fc3cf3e822 | ||
|
c69dbc4490 | ||
|
a0ea29a2fa | ||
|
9b37ba679a | ||
|
016f05a770 | ||
|
7e503fa053 | ||
|
7815b6942d | ||
|
fc3d3ae331 | ||
|
20dc389ab7 | ||
|
9f9d51d52c | ||
|
9389456d20 | ||
|
69171873fa | ||
|
c0983a32be | ||
|
3e6dfcfb15 | ||
|
9d890c75ac | ||
|
771f14c466 | ||
|
b0d881046f | ||
|
f2ab76c8db | ||
|
4fdbfffdcc | ||
|
a61c114519 | ||
|
41ec7b6a02 | ||
|
be61341178 | ||
|
6080e6d24d | ||
|
75599780f2 | ||
|
5f16faaae0 | ||
|
94b48c1633 | ||
|
f7d56886c5 | ||
|
0691a7fc46 | ||
|
a135e01f89 | ||
|
1d8fb818fc | ||
|
6f7a468174 | ||
|
dd1ca4ce59 | ||
|
c475960754 | ||
|
e01a6eec3e | ||
|
d94e3633dc | ||
|
8c7da07085 | ||
|
22052106d8 | ||
|
3716d48c89 | ||
|
317607e192 | ||
|
19361c962c | ||
|
636669e1a5 | ||
|
b03d457ffb | ||
|
a2c9c0f740 | ||
|
07f7ccdc96 | ||
|
d37a13847a | ||
|
18e5c12b6d | ||
|
48d145fff6 | ||
|
04d2609a91 | ||
|
3bbae86ec9 | ||
|
53b076be61 | ||
|
009f8ee356 | ||
|
be9391ded5 | ||
|
96337f11d4 | ||
|
24b58e5858 | ||
|
21eb682b39 | ||
|
a7c1e7a2a0 | ||
|
978d7cb65b | ||
|
23067c48c7 | ||
|
4c8ee300b5 | ||
|
4282829f38 | ||
|
92523fc0dd | ||
|
4a89b75bb7 | ||
|
274c590ad6 | ||
|
84b2ac3f97 | ||
|
8efa3ed84a | ||
|
3269f92138 | ||
|
0aebedae16 | ||
|
f2802dd2ff | ||
|
9389a0ca96 | ||
|
6d9e0baa27 | ||
|
9573c389de | ||
|
3453732bba | ||
|
0a84dbb787 | ||
|
f5a5ec7c88 | ||
|
1cabbee2bb | ||
|
563129c863 | ||
|
c5a3aa73a0 | ||
|
80bfd2ed97 | ||
|
813ae020b6 | ||
|
7e3c6f819a | ||
|
94b5cfc3d8 | ||
|
beaf6dd2a6 | ||
|
cf9ccf711b | ||
|
94254f556e | ||
|
70492b2954 | ||
|
b1174a1bdf | ||
|
efeb15edbd | ||
|
ac17d5f9b1 | ||
|
11ec65d516 | ||
|
4d3b35ae53 | ||
|
3aeedb9c98 | ||
|
9b5499caf2 | ||
|
fcabffc1e5 | ||
|
821386aeff | ||
|
fa2d791d79 | ||
|
80fb7e2294 | ||
|
00299b264b | ||
|
4a92b92f58 | ||
|
6c1bc35264 | ||
|
7d35afdc37 | ||
|
f615594f22 | ||
|
76f9bec7cc | ||
|
0a19e5b6db | ||
|
946419cda2 | ||
|
13554f18bd | ||
|
54a26dde3d | ||
|
357484bd4b | ||
|
9664e0b850 | ||
|
e5a81f8c7e | ||
|
215cebc019 | ||
|
a413eb0843 | ||
|
0434b23167 | ||
|
cf946eb981 | ||
|
3a34dae722 | ||
|
bd69d1efdc | ||
|
e3f1d4c021 | ||
|
5569274a97 | ||
|
dd5b28e557 | ||
|
8860bde34b | ||
|
8776cf2ce6 | ||
|
0cad97d5ec | ||
|
06bdc88eb6 | ||
|
1d90772eb2 | ||
|
3d35925a05 | ||
|
5b21286377 | ||
|
ad0920fe1e | ||
|
c6dc7e0939 | ||
|
48d24893d4 | ||
|
753c11d144 | ||
|
7482e45534 | ||
|
e757170211 | ||
|
e2a078d534 | ||
|
d846e89a4b | ||
|
e40f03b96a | ||
|
088915e24c | ||
|
470764e24c | ||
|
0983ab1f39 | ||
|
20b047600f | ||
|
b61ea1db76 | ||
|
4184452f49 | ||
|
736ee20eb5 | ||
|
4f9b106d40 | ||
|
86c317fbbc | ||
|
59e53c1863 | ||
|
539da5abdf | ||
|
0e9efa5183 | ||
|
4b009237d7 | ||
|
66dc0a9383 | ||
|
4c2990f199 | ||
|
c700d76477 | ||
|
859117797f | ||
|
4fcbf52752 | ||
|
0e3ed707ba | ||
|
50375ee1ee | ||
|
21f69eceaa | ||
|
23f08eaedd | ||
|
2ee598b4af | ||
|
95b8dc59fb | ||
|
273e4abb14 | ||
|
552470de63 | ||
|
55639889c7 | ||
|
53e5c4875f | ||
|
b73f9d8ecb | ||
|
84ccad3528 | ||
|
a3ba8fb4dc | ||
|
685126482d | ||
|
863591275c | ||
|
c52a68a5be | ||
|
5ed2d990af | ||
|
3dbe8ad43c | ||
|
f9c3dd1f5f | ||
|
c43cf6895b | ||
|
785649f3b8 | ||
|
42adc4ac11 | ||
|
b03ff775ce | ||
|
81c3242b6d | ||
|
816f8e9c87 | ||
|
0f9245ff33 | ||
|
cb42ea21be | ||
|
d200184306 | ||
|
a09d372caf | ||
|
8be30d7d5a | ||
|
5134a4b3c5 | ||
|
c1ed8cd1f3 | ||
|
0eeedf9259 | ||
|
d5744e5a81 | ||
|
d85d07c0ec | ||
|
1d790970d5 | ||
|
b586294e29 | ||
|
ed3c02906c | ||
|
84317f913a | ||
|
ff41a5ab12 | ||
|
ad11509e83 | ||
|
43838d3df8 | ||
|
06b44c6237 | ||
|
63e88debee | ||
|
ab9646551f | ||
|
073806df7c | ||
|
c707d9026f | ||
|
3d401690cb | ||
|
5f5c9a4cdd | ||
|
af45295a2f | ||
|
5b7726cf6f | ||
|
d5832c3075 | ||
|
f17317061c | ||
|
7f4e25267c | ||
|
07397305f1 | ||
|
efb9d444c1 | ||
|
071dec7148 | ||
|
279c6f150a | ||
|
50f9630250 | ||
|
485090d039 | ||
|
498ecca81d | ||
|
9bb06baaaa | ||
|
ca2afb0b8b | ||
|
1b9ea8c6e3 | ||
|
d9b5f09239 | ||
|
71bd780340 | ||
|
b62a8c78b4 | ||
|
8c3d7a1979 | ||
|
4970dc3358 | ||
|
1721be4973 | ||
|
d2d46be8da | ||
|
3339b165cd | ||
|
3e77893ef7 | ||
|
098d5adca5 | ||
|
2507820339 | ||
|
7f4f927980 | ||
|
5487f8b9a0 | ||
|
4151d35e8e | ||
|
639f815432 | ||
|
13ec120289 | ||
|
9d37e4abb4 | ||
|
7bb8819fbc | ||
|
8017607fe7 | ||
|
84ad2ea261 | ||
|
8ead28e481 | ||
|
475bf4e9c1 | ||
|
150cb0d4c7 | ||
|
28a08cae6d | ||
|
9a244e8951 | ||
|
24f98c5835 | ||
|
33498eb512 | ||
|
8b9b46dfff | ||
|
7425a33dd7 | ||
|
39f4d804a2 | ||
|
1eb0c687a6 |
215 changed files with 13957 additions and 5343 deletions
2
RELEASE
2
RELEASE
|
@ -1 +1 @@
|
|||
2.5.2
|
||||
2.5.3
|
||||
|
|
126
dxvk.conf
126
dxvk.conf
|
@ -18,6 +18,29 @@
|
|||
# dxgi.enableHDR = True
|
||||
|
||||
|
||||
# Expose support for dcomp swap chains with a dummy window.
|
||||
#
|
||||
# This is not a valid implementation of DirectComposition swapchains,
|
||||
# however some games may rely on this functionality to be present while
|
||||
# others may require swap chain creation to fail.
|
||||
#
|
||||
# Supported values: True, False
|
||||
|
||||
# dxgi.enableDummyCompositionSwapchain = False
|
||||
|
||||
|
||||
# Allows the Vulkan driver to opt-in to exclusive full-screen mode on
|
||||
# Windows. Certain features, such as variable refresh rate or HDR, will
|
||||
# not work without this setting, however enabling it will break certain
|
||||
# games that use additional GDI windows, and it will also break alt+tab.
|
||||
#
|
||||
# This setting has no effect on non-Windows platforms.
|
||||
#
|
||||
# Supported values: True, False
|
||||
|
||||
# dxvk.allowFse = False
|
||||
|
||||
|
||||
# Enables Unreal Engine 4 HDR workarounds for games that do not follow
|
||||
# the standard -Win64-Shipping.exe naming scheme. May be needed to avoid
|
||||
# crashes in D3D11 games on HDR-enabled systems due to statically linked
|
||||
|
@ -64,6 +87,46 @@
|
|||
# d3d9.maxFrameRate = 0
|
||||
|
||||
|
||||
# Controls latency sleep and Nvidia Reflex support.
|
||||
#
|
||||
# Supported values:
|
||||
# - Auto: By default, DXVK only supports latency sleep in D3D11 games that
|
||||
# use Reflex if the graphics driver supports VK_NV_low_latency2,
|
||||
# and if dxvk-nvapi is enabled in Proton.
|
||||
# - True: Enables built-in latency reduction based on internal timings.
|
||||
# This assumes that input sampling for any given frame happens after
|
||||
# the D3D9 or DXGI Present call returns; games that render and present
|
||||
# asynchronously will not behave as intended.
|
||||
# Similarly, this will not have any effect in games with built-in frame
|
||||
# rate limiters, or if an external limiter (such as MangoHud) is used.
|
||||
# In some games, enabling this may reduce performance or lead to less
|
||||
# consistent frame pacing.
|
||||
# The implementation will either use VK_NV_low_latency2 if supported
|
||||
# by the driver, or a custom algorithm.
|
||||
# - False: Disable Reflex support as well as built-in latency reduction.
|
||||
|
||||
# dxvk.latencySleep = Auto
|
||||
|
||||
|
||||
# Tolerance for the latency sleep heuristic, in microseconds. Higher values
|
||||
# increase latency, but may lead to better frame pacing in some cases. Does
|
||||
# not have any effect if NV_low_latency2 is used.
|
||||
#
|
||||
# Supported values: Any non-negative number
|
||||
|
||||
# dxvk.latencyTolerance = 1000
|
||||
|
||||
|
||||
# Disables the use of VK_NV_low_latency2. This will make Reflex unavailable
|
||||
# in games, and if dxvk.latencySleep is set to True, a custom algorithm will
|
||||
# be used for latency control. By default, the extension will not be used in
|
||||
# 32-bit applications due to driver issues.
|
||||
#
|
||||
# Supported values: Auto, True, False
|
||||
|
||||
# dxvk.disableNvLowLatency2 = Auto
|
||||
|
||||
|
||||
# Override PCI vendor and device IDs reported to the application. Can
|
||||
# cause the app to adjust behaviour depending on the selected values.
|
||||
#
|
||||
|
@ -128,15 +191,6 @@
|
|||
# dxgi.maxSharedMemory = 0
|
||||
|
||||
|
||||
# Override back buffer count for the Vulkan swap chain.
|
||||
# Setting this to 0 or less will have no effect.
|
||||
#
|
||||
# Supported values: Any number greater than or equal to 2.
|
||||
|
||||
# dxgi.numBackBuffers = 0
|
||||
# d3d9.numBackBuffers = 0
|
||||
|
||||
|
||||
# Overrides synchronization interval (Vsync) for presentation.
|
||||
# Setting this to 0 disables vertical synchronization entirely.
|
||||
# A positive value 'n' will enable Vsync and repeat the same
|
||||
|
@ -148,10 +202,10 @@
|
|||
# d3d9.presentInterval = -1
|
||||
|
||||
|
||||
# Controls tearing behaviour with regards to in-game Vsync settings.
|
||||
#
|
||||
# True enables the mailbox present mode in case regular Vsync is disabled.
|
||||
# This should avoid tearing, but may be unsupported on some systems
|
||||
# or require setting dxgi.numBackBuffers to a higher value in order
|
||||
# to work properly.
|
||||
# This eliminates tearing, but may be unsupported on some systems.
|
||||
#
|
||||
# False enables the relaxed fifo present mode in case regular Vsync is enabled.
|
||||
# This should result in tearing but reduce stutter if FPS are too low,
|
||||
|
@ -164,15 +218,14 @@
|
|||
# dxvk.tearFree = Auto
|
||||
|
||||
|
||||
# Assume that command lists created from deferred contexts are only used
|
||||
# once. This is extremely common and may improve performance while reducing
|
||||
# the amount of memory wasted if games keep their command list objects alive
|
||||
# for too long, but may also lead to rendering issues if command lists are
|
||||
# submitted multiple times.
|
||||
# Controls tiler optimizations. Enabling these will alter the behaviour of
|
||||
# submission heuristics and enables some non-default behaviour in DXVK.
|
||||
# This option is only intended to be changed for performance testing and
|
||||
# debugging purposes.
|
||||
#
|
||||
# Supported values: True, False
|
||||
# Supported values: Auto, True, False
|
||||
|
||||
# d3d11.dcSingleUseMode = True
|
||||
# dxvk.tilerMode = Auto
|
||||
|
||||
|
||||
# Override the maximum feature level that a D3D11 device can be created
|
||||
|
@ -194,22 +247,24 @@
|
|||
|
||||
# Enables relaxed pipeline barriers around UAV writes.
|
||||
#
|
||||
# This may improve performance in some games, but may also introduce
|
||||
# rendering issues. Please don't report bugs with the option enabled.
|
||||
# Ignores write-after-write hazards in compute shaders, and all UAV
|
||||
# hazards in graphics shaders. This may improve performance in some
|
||||
# games, but may also introduce rendering issues. Please don't report
|
||||
# bugs with the option enabled.
|
||||
#
|
||||
# Supported values: True, False
|
||||
|
||||
# d3d11.relaxedBarriers = False
|
||||
|
||||
|
||||
# Ignores barriers around UAV writes from fragment shaders.
|
||||
# Enables relaxed UAV pipeline barriers in graphics shaders only.
|
||||
#
|
||||
# This may improve performance in some games, but may also introduce
|
||||
# rendering issues. Please don't report bugs with the option enabled.
|
||||
# Similar to the relaxedBarriers option, except it does not apply to
|
||||
# compute UAVs. Please do not report bugs with this option enabled.
|
||||
#
|
||||
# Supported values: True, False
|
||||
|
||||
# d3d11.ignoreGraphicsBarriers = False
|
||||
# d3d11.relaxedGraphicsBarriers = False
|
||||
|
||||
|
||||
# Overrides anisotropic filtering for all samplers. Set this to a positive
|
||||
|
@ -277,6 +332,26 @@
|
|||
# d3d11.zeroWorkgroupMemory = False
|
||||
|
||||
|
||||
# Forces insertion of memory barriers after writes to group-shared memory in
|
||||
# compute shaders. This is only intended to be used as a workaround for games
|
||||
# that don't properly synchronize access to groupshard variables, and may have
|
||||
# a negative performance impact as it prevents compiler optimizations.
|
||||
#
|
||||
# Supported values: True, False
|
||||
|
||||
# d3d11.forceVolatileTgsmAccess = False
|
||||
|
||||
|
||||
# Forces insertion of full memory and control barriers after accessing any
|
||||
# read-write UAV inside compute shaders. This is only intended to be used as
|
||||
# a workaround for games that do not synchronize access to coherent UAVs,
|
||||
# and will likely have a negative performance impact.
|
||||
#
|
||||
# Supported values: True, False
|
||||
|
||||
# d3d11.forceComputeUavBarriers = False
|
||||
|
||||
|
||||
# Clears mapped memory to zero when suballocated memory is freed. This will
|
||||
# drastically increase CPU overhead and should only be used as a last resort
|
||||
# if a game does not properly initialize mapped buffers on its own.
|
||||
|
@ -421,6 +496,7 @@
|
|||
# capabilities that the applicatation queries.
|
||||
#
|
||||
# Supported values:
|
||||
# - 0: Fixed-function only
|
||||
# - 1: Shader Model 1
|
||||
# - 2: Shader Model 2
|
||||
# - 3: Shader Model 3
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 46dc0f6e514f5730784bb2cac2a7c731636839e8
|
||||
Subproject commit 234c4b7370a8ea3239a214c9e871e4b17c89f4ab
|
|
@ -1,4 +1,4 @@
|
|||
project('dxvk', ['c', 'cpp'], version : 'v2.5.2', meson_version : '>= 0.58', default_options : [ 'cpp_std=c++17', 'b_vscrt=static_from_buildtype', 'warning_level=2' ])
|
||||
project('dxvk', ['c', 'cpp'], version : '2.5.3', meson_version : '>= 0.58', default_options : [ 'cpp_std=c++17', 'b_vscrt=static_from_buildtype', 'warning_level=2' ])
|
||||
|
||||
pkg = import('pkgconfig')
|
||||
cpu_family = target_machine.cpu_family()
|
||||
|
@ -80,12 +80,16 @@ if platform == 'windows'
|
|||
]
|
||||
endif
|
||||
|
||||
# Enable stdcall fixup on 32-bit
|
||||
if cpu_family == 'x86'
|
||||
# Enable stdcall fixup on 32-bit
|
||||
link_args += [
|
||||
'-Wl,--enable-stdcall-fixup',
|
||||
'-Wl,--kill-at',
|
||||
]
|
||||
# Fix stack alignment issues with mingw on 32-bit
|
||||
compiler_args += [
|
||||
'-mpreferred-stack-boundary=2'
|
||||
]
|
||||
endif
|
||||
else
|
||||
# setup file alignment + enable PDB output for MSVC builds
|
||||
|
|
|
@ -37,8 +37,8 @@ namespace dxvk {
|
|||
D3D11UserDefinedAnnotation<ContextType>::D3D11UserDefinedAnnotation(
|
||||
ContextType* container,
|
||||
const Rc<DxvkDevice>& dxvkDevice)
|
||||
: m_container(container), m_eventDepth(0),
|
||||
m_annotationsEnabled(dxvkDevice->instance()->extensions().extDebugUtils) {
|
||||
: m_container(container),
|
||||
m_annotationsEnabled(dxvkDevice->debugFlags().test(DxvkDebugFlag::Markers)) {
|
||||
if (!IsDeferred && m_annotationsEnabled)
|
||||
RegisterUserDefinedAnnotation<true>(this);
|
||||
}
|
||||
|
@ -75,19 +75,16 @@ namespace dxvk {
|
|||
INT STDMETHODCALLTYPE D3D11UserDefinedAnnotation<ContextType>::BeginEvent(
|
||||
D3DCOLOR Color,
|
||||
LPCWSTR Name) {
|
||||
if (!m_annotationsEnabled)
|
||||
if (!m_annotationsEnabled || !Name)
|
||||
return -1;
|
||||
|
||||
D3D10DeviceLock lock = m_container->LockContext();
|
||||
|
||||
m_container->EmitCs([color = Color, labelName = dxvk::str::fromws(Name)](DxvkContext *ctx) {
|
||||
VkDebugUtilsLabelEXT label;
|
||||
label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
|
||||
label.pNext = nullptr;
|
||||
label.pLabelName = labelName.c_str();
|
||||
DecodeD3DCOLOR(color, label.color);
|
||||
|
||||
ctx->beginDebugLabel(&label);
|
||||
m_container->EmitCs([
|
||||
cColor = Color,
|
||||
cLabel = dxvk::str::fromws(Name)
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->beginDebugLabel(vk::makeLabel(cColor, cLabel.c_str()));
|
||||
});
|
||||
|
||||
return m_eventDepth++;
|
||||
|
@ -101,11 +98,14 @@ namespace dxvk {
|
|||
|
||||
D3D10DeviceLock lock = m_container->LockContext();
|
||||
|
||||
m_container->EmitCs([](DxvkContext *ctx) {
|
||||
if (!m_eventDepth)
|
||||
return 0;
|
||||
|
||||
m_container->EmitCs([] (DxvkContext* ctx) {
|
||||
ctx->endDebugLabel();
|
||||
});
|
||||
|
||||
return m_eventDepth--;
|
||||
return --m_eventDepth;
|
||||
}
|
||||
|
||||
|
||||
|
@ -113,19 +113,16 @@ namespace dxvk {
|
|||
void STDMETHODCALLTYPE D3D11UserDefinedAnnotation<ContextType>::SetMarker(
|
||||
D3DCOLOR Color,
|
||||
LPCWSTR Name) {
|
||||
if (!m_annotationsEnabled)
|
||||
if (!m_annotationsEnabled || !Name)
|
||||
return;
|
||||
|
||||
D3D10DeviceLock lock = m_container->LockContext();
|
||||
|
||||
m_container->EmitCs([color = Color, labelName = dxvk::str::fromws(Name)](DxvkContext *ctx) {
|
||||
VkDebugUtilsLabelEXT label;
|
||||
label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
|
||||
label.pNext = nullptr;
|
||||
label.pLabelName = labelName.c_str();
|
||||
DecodeD3DCOLOR(color, label.color);
|
||||
|
||||
ctx->insertDebugLabel(&label);
|
||||
m_container->EmitCs([
|
||||
cColor = Color,
|
||||
cLabel = dxvk::str::fromws(Name)
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->insertDebugLabel(vk::makeLabel(cColor, cLabel.c_str()));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -48,9 +48,10 @@ namespace dxvk {
|
|||
|
||||
private:
|
||||
|
||||
ContextType* m_container;
|
||||
int32_t m_eventDepth;
|
||||
bool m_annotationsEnabled;
|
||||
ContextType* m_container = nullptr;
|
||||
int32_t m_eventDepth = 0u;
|
||||
bool m_annotationsEnabled = false;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "d3d11_buffer.h"
|
||||
#include "d3d11_context.h"
|
||||
#include "d3d11_context_imm.h"
|
||||
#include "d3d11_device.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
@ -211,6 +212,18 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void D3D11Buffer::SetDebugName(const char* pName) {
|
||||
if (m_buffer) {
|
||||
m_parent->GetContext()->InjectCs(DxvkCsQueue::HighPriority, [
|
||||
cBuffer = m_buffer,
|
||||
cName = std::string(pName ? pName : "")
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->setDebugName(cBuffer, cName.c_str());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HRESULT D3D11Buffer::NormalizeBufferProperties(D3D11_BUFFER_DESC* pDesc) {
|
||||
// Zero-sized buffers are illegal
|
||||
if (!pDesc->ByteWidth && !(pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL))
|
||||
|
@ -370,6 +383,8 @@ namespace dxvk {
|
|||
| VK_ACCESS_INDIRECT_COMMAND_READ_BIT
|
||||
| VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT
|
||||
| VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT;
|
||||
info.debugName = "SO counter";
|
||||
|
||||
return device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,8 @@ namespace dxvk {
|
|||
void STDMETHODCALLTYPE GetDesc(
|
||||
D3D11_BUFFER_DESC *pDesc) final;
|
||||
|
||||
void STDMETHODCALLTYPE SetDebugName(const char* pName) final;
|
||||
|
||||
bool CheckViewCompatibility(
|
||||
UINT BindFlags,
|
||||
DXGI_FORMAT Format) const;
|
||||
|
|
|
@ -10,20 +10,12 @@ namespace dxvk {
|
|||
* Used to identify the type of command
|
||||
* data most recently added to a CS chunk.
|
||||
*/
|
||||
enum class D3D11CmdType {
|
||||
enum class D3D11CmdType : uint32_t {
|
||||
None,
|
||||
DrawIndirect,
|
||||
DrawIndirectIndexed,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Command data header
|
||||
*
|
||||
* Stores the command type. All command
|
||||
* data structs must inherit this struct.
|
||||
*/
|
||||
struct D3D11CmdData {
|
||||
D3D11CmdType type;
|
||||
Draw,
|
||||
DrawIndexed,
|
||||
};
|
||||
|
||||
|
||||
|
@ -34,7 +26,7 @@ namespace dxvk {
|
|||
* the first draw, as well as the number of
|
||||
* draws to execute.
|
||||
*/
|
||||
struct D3D11CmdDrawIndirectData : public D3D11CmdData {
|
||||
struct D3D11CmdDrawIndirectData {
|
||||
uint32_t offset;
|
||||
uint32_t count;
|
||||
uint32_t stride;
|
||||
|
|
|
@ -74,8 +74,6 @@ namespace dxvk {
|
|||
m_resources.push_back(std::move(entry));
|
||||
}
|
||||
|
||||
pCommandList->MarkSubmitted();
|
||||
|
||||
// Return ID of the last chunk added. The command list
|
||||
// added can never be empty, so do not handle zero.
|
||||
return m_chunks.size() - 1;
|
||||
|
@ -102,8 +100,6 @@ namespace dxvk {
|
|||
while (j < m_resources.size() && m_resources[j].chunkId == i)
|
||||
TrackResourceSequenceNumber(m_resources[j++].ref, seq);
|
||||
}
|
||||
|
||||
MarkSubmitted();
|
||||
}
|
||||
|
||||
|
||||
|
@ -151,14 +147,4 @@ namespace dxvk {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void D3D11CommandList::MarkSubmitted() {
|
||||
if (m_submitted.exchange(true) && !m_warned.exchange(true)
|
||||
&& m_parent->GetOptions()->dcSingleUseMode) {
|
||||
Logger::warn(
|
||||
"D3D11: Command list submitted multiple times,\n"
|
||||
" but d3d11.dcSingleUseMode is enabled");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -55,15 +55,10 @@ namespace dxvk {
|
|||
std::vector<Com<D3D11Query, false>> m_queries;
|
||||
std::vector<TrackedResource> m_resources;
|
||||
|
||||
std::atomic<bool> m_submitted = { false };
|
||||
std::atomic<bool> m_warned = { false };
|
||||
|
||||
void TrackResourceSequenceNumber(
|
||||
const D3D11ResourceRef& Resource,
|
||||
uint64_t Seq);
|
||||
|
||||
void MarkSubmitted();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -75,6 +75,11 @@ namespace dxvk {
|
|||
// Use a local staging buffer to handle tiny uploads, most
|
||||
// of the time we're fine with hitting the global allocator
|
||||
constexpr static VkDeviceSize StagingBufferSize = 256ull << 10;
|
||||
|
||||
protected:
|
||||
// Compile-time debug flag to force lazy binding on (True) or off (False)
|
||||
constexpr static Tristate DebugLazyBinding = Tristate::Auto;
|
||||
|
||||
public:
|
||||
|
||||
D3D11CommonContext(
|
||||
|
@ -788,9 +793,11 @@ namespace dxvk {
|
|||
|
||||
DxvkStagingBuffer m_staging;
|
||||
|
||||
D3D11CmdType m_csDataType = D3D11CmdType::None;
|
||||
|
||||
DxvkCsChunkFlags m_csFlags;
|
||||
DxvkCsChunkRef m_csChunk;
|
||||
D3D11CmdData* m_cmdData;
|
||||
DxvkCsDataBlock* m_csData = nullptr;
|
||||
|
||||
DxvkLocalAllocationCache m_allocationCache;
|
||||
|
||||
|
@ -799,6 +806,30 @@ namespace dxvk {
|
|||
DxvkBufferSlice AllocStagingBuffer(
|
||||
VkDeviceSize Size);
|
||||
|
||||
void ApplyDirtyConstantBuffers(
|
||||
DxbcProgramType Stage,
|
||||
const DxbcBindingMask& BoundMask,
|
||||
DxbcBindingMask& DirtyMask);
|
||||
|
||||
void ApplyDirtySamplers(
|
||||
DxbcProgramType Stage,
|
||||
const DxbcBindingMask& BoundMask,
|
||||
DxbcBindingMask& DirtyMask);
|
||||
|
||||
void ApplyDirtyShaderResources(
|
||||
DxbcProgramType Stage,
|
||||
const DxbcBindingMask& BoundMask,
|
||||
DxbcBindingMask& DirtyMask);
|
||||
|
||||
void ApplyDirtyUnorderedAccessViews(
|
||||
DxbcProgramType Stage,
|
||||
const DxbcBindingMask& BoundMask,
|
||||
DxbcBindingMask& DirtyMask);
|
||||
|
||||
void ApplyDirtyGraphicsBindings();
|
||||
|
||||
void ApplyDirtyComputeBindings();
|
||||
|
||||
void ApplyInputLayout();
|
||||
|
||||
void ApplyPrimitiveTopology();
|
||||
|
@ -817,6 +848,12 @@ namespace dxvk {
|
|||
|
||||
void ApplyViewportState();
|
||||
|
||||
void BatchDraw(
|
||||
const VkDrawIndirectCommand& draw);
|
||||
|
||||
void BatchDrawIndexed(
|
||||
const VkDrawIndexedIndirectCommand& draw);
|
||||
|
||||
template<DxbcProgramType ShaderStage>
|
||||
void BindShader(
|
||||
const D3D11CommonShader* pShaderModule);
|
||||
|
@ -854,35 +891,33 @@ namespace dxvk {
|
|||
D3D11Buffer* pBuffer,
|
||||
UINT Offset);
|
||||
|
||||
template<DxbcProgramType ShaderStage>
|
||||
void BindConstantBuffer(
|
||||
DxbcProgramType ShaderStage,
|
||||
UINT Slot,
|
||||
D3D11Buffer* pBuffer,
|
||||
UINT Offset,
|
||||
UINT Length);
|
||||
|
||||
template<DxbcProgramType ShaderStage>
|
||||
void BindConstantBufferRange(
|
||||
DxbcProgramType ShaderStage,
|
||||
UINT Slot,
|
||||
UINT Offset,
|
||||
UINT Length);
|
||||
|
||||
template<DxbcProgramType ShaderStage>
|
||||
void BindSampler(
|
||||
DxbcProgramType ShaderStage,
|
||||
UINT Slot,
|
||||
D3D11SamplerState* pSampler);
|
||||
|
||||
template<DxbcProgramType ShaderStage>
|
||||
void BindShaderResource(
|
||||
DxbcProgramType ShaderStage,
|
||||
UINT Slot,
|
||||
D3D11ShaderResourceView* pResource);
|
||||
|
||||
template<DxbcProgramType ShaderStage>
|
||||
void BindUnorderedAccessView(
|
||||
UINT UavSlot,
|
||||
D3D11UnorderedAccessView* pUav,
|
||||
UINT CtrSlot,
|
||||
UINT Counter);
|
||||
DxbcProgramType ShaderStage,
|
||||
UINT Slot,
|
||||
D3D11UnorderedAccessView* pUav);
|
||||
|
||||
VkClearValue ConvertColorValue(
|
||||
const FLOAT Color[4],
|
||||
|
@ -911,6 +946,36 @@ namespace dxvk {
|
|||
DxvkBufferSlice BufferSlice,
|
||||
UINT Flags);
|
||||
|
||||
template<typename T>
|
||||
bool DirtyBindingGeneric(
|
||||
DxbcProgramType ShaderStage,
|
||||
T BoundMask,
|
||||
T& DirtyMask,
|
||||
T DirtyBit,
|
||||
bool IsNull);
|
||||
|
||||
bool DirtyConstantBuffer(
|
||||
DxbcProgramType ShaderStage,
|
||||
uint32_t Slot,
|
||||
bool IsNull);
|
||||
|
||||
bool DirtySampler(
|
||||
DxbcProgramType ShaderStage,
|
||||
uint32_t Slot,
|
||||
bool IsNull);
|
||||
|
||||
bool DirtyShaderResource(
|
||||
DxbcProgramType ShaderStage,
|
||||
uint32_t Slot,
|
||||
bool IsNull);
|
||||
|
||||
bool DirtyComputeUnorderedAccessView(
|
||||
uint32_t Slot,
|
||||
bool IsNull);
|
||||
|
||||
bool DirtyGraphicsUnorderedAccessView(
|
||||
uint32_t Slot);
|
||||
|
||||
void DiscardBuffer(
|
||||
ID3D11Resource* pResource);
|
||||
|
||||
|
@ -943,10 +1008,16 @@ namespace dxvk {
|
|||
|
||||
D3D11MaxUsedBindings GetMaxUsedBindings();
|
||||
|
||||
bool HasDirtyComputeBindings();
|
||||
|
||||
bool HasDirtyGraphicsBindings();
|
||||
|
||||
void ResetCommandListState();
|
||||
|
||||
void ResetContextState();
|
||||
|
||||
void ResetDirtyTracking();
|
||||
|
||||
void ResetStagingBuffer();
|
||||
|
||||
template<DxbcProgramType ShaderStage, typename T>
|
||||
|
@ -969,17 +1040,17 @@ namespace dxvk {
|
|||
|
||||
void RestoreCommandListState();
|
||||
|
||||
template<DxbcProgramType Stage>
|
||||
void RestoreConstantBuffers();
|
||||
void RestoreConstantBuffers(
|
||||
DxbcProgramType Stage);
|
||||
|
||||
template<DxbcProgramType Stage>
|
||||
void RestoreSamplers();
|
||||
void RestoreSamplers(
|
||||
DxbcProgramType Stage);
|
||||
|
||||
template<DxbcProgramType Stage>
|
||||
void RestoreShaderResources();
|
||||
void RestoreShaderResources(
|
||||
DxbcProgramType Stage);
|
||||
|
||||
template<DxbcProgramType Stage>
|
||||
void RestoreUnorderedAccessViews();
|
||||
void RestoreUnorderedAccessViews(
|
||||
DxbcProgramType Stage);
|
||||
|
||||
template<DxbcProgramType ShaderStage>
|
||||
void SetConstantBuffers(
|
||||
|
@ -1063,6 +1134,10 @@ namespace dxvk {
|
|||
UINT SrcDepthPitch,
|
||||
UINT CopyFlags);
|
||||
|
||||
void UpdateUnorderedAccessViewCounter(
|
||||
D3D11UnorderedAccessView* pUav,
|
||||
uint32_t CounterValue);
|
||||
|
||||
bool ValidateRenderTargets(
|
||||
UINT NumViews,
|
||||
ID3D11RenderTargetView* const* ppRenderTargetViews,
|
||||
|
@ -1083,48 +1158,49 @@ namespace dxvk {
|
|||
DxvkMultisampleState* pMsState,
|
||||
UINT SampleMask);
|
||||
|
||||
template<bool AllowFlush = !IsDeferred, typename Cmd>
|
||||
template<bool AllowFlush = true, typename Cmd>
|
||||
void EmitCs(Cmd&& command) {
|
||||
m_cmdData = nullptr;
|
||||
if (unlikely(m_csDataType != D3D11CmdType::None)) {
|
||||
m_csData = nullptr;
|
||||
m_csDataType = D3D11CmdType::None;
|
||||
}
|
||||
|
||||
if (unlikely(!m_csChunk->push(command))) {
|
||||
GetTypedContext()->EmitCsChunk(std::move(m_csChunk));
|
||||
m_csChunk = AllocCsChunk();
|
||||
|
||||
if constexpr (AllowFlush)
|
||||
if constexpr (!IsDeferred && AllowFlush)
|
||||
GetTypedContext()->ConsiderFlush(GpuFlushType::ImplicitWeakHint);
|
||||
|
||||
m_csChunk->push(command);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename M, bool AllowFlush = !IsDeferred, typename Cmd, typename... Args>
|
||||
M* EmitCsCmd(Cmd&& command, Args&&... args) {
|
||||
M* data = m_csChunk->pushCmd<M, Cmd, Args...>(
|
||||
command, std::forward<Args>(args)...);
|
||||
template<typename M, bool AllowFlush = true, typename Cmd>
|
||||
void EmitCsCmd(D3D11CmdType type, size_t count, Cmd&& command) {
|
||||
m_csDataType = type;
|
||||
m_csData = m_csChunk->pushCmd<M, Cmd>(command, count);
|
||||
|
||||
if (unlikely(!data)) {
|
||||
if (unlikely(!m_csData)) {
|
||||
GetTypedContext()->EmitCsChunk(std::move(m_csChunk));
|
||||
m_csChunk = AllocCsChunk();
|
||||
|
||||
if constexpr (AllowFlush)
|
||||
if constexpr (!IsDeferred && AllowFlush)
|
||||
GetTypedContext()->ConsiderFlush(GpuFlushType::ImplicitWeakHint);
|
||||
|
||||
// We must record this command after the potential
|
||||
// flush since the caller may still access the data
|
||||
data = m_csChunk->pushCmd<M, Cmd, Args...>(
|
||||
command, std::forward<Args>(args)...);
|
||||
m_csData = m_csChunk->pushCmd<M, Cmd>(command, count);
|
||||
}
|
||||
|
||||
m_cmdData = data;
|
||||
return data;
|
||||
}
|
||||
|
||||
void FlushCsChunk() {
|
||||
if (likely(!m_csChunk->empty())) {
|
||||
m_csData = nullptr;
|
||||
m_csDataType = D3D11CmdType::None;
|
||||
|
||||
GetTypedContext()->EmitCsChunk(std::move(m_csChunk));
|
||||
m_csChunk = AllocCsChunk();
|
||||
m_cmdData = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace dxvk {
|
|||
D3D11Device* pParent,
|
||||
const Rc<DxvkDevice>& Device,
|
||||
UINT ContextFlags)
|
||||
: D3D11CommonContext<D3D11DeferredContext>(pParent, Device, ContextFlags, GetCsChunkFlags(pParent)),
|
||||
: D3D11CommonContext<D3D11DeferredContext>(pParent, Device, ContextFlags, 0u),
|
||||
m_commandList (CreateCommandList()) {
|
||||
ResetContextState();
|
||||
}
|
||||
|
@ -436,12 +436,4 @@ namespace dxvk {
|
|||
m_mappedResources.push_back({ Cookie, MapInfo });
|
||||
}
|
||||
|
||||
|
||||
DxvkCsChunkFlags D3D11DeferredContext::GetCsChunkFlags(
|
||||
D3D11Device* pDevice) {
|
||||
return pDevice->GetOptions()->dcSingleUseMode
|
||||
? DxvkCsChunkFlags(DxvkCsChunkFlag::SingleUse)
|
||||
: DxvkCsChunkFlags();
|
||||
}
|
||||
|
||||
}
|
|
@ -48,12 +48,15 @@ namespace dxvk {
|
|||
D3D10DeviceLock lock = m_ctx->LockContext();
|
||||
m_ctx->SetDrawBuffers(pBufferForArgs, nullptr);
|
||||
|
||||
if (unlikely(m_ctx->HasDirtyGraphicsBindings()))
|
||||
m_ctx->ApplyDirtyGraphicsBindings();
|
||||
|
||||
m_ctx->EmitCs([
|
||||
cCount = DrawCount,
|
||||
cOffset = ByteOffsetForArgs,
|
||||
cStride = ByteStrideForArgs
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->drawIndirect(cOffset, cCount, cStride);
|
||||
ctx->drawIndirect(cOffset, cCount, cStride, false);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -67,12 +70,15 @@ namespace dxvk {
|
|||
D3D10DeviceLock lock = m_ctx->LockContext();
|
||||
m_ctx->SetDrawBuffers(pBufferForArgs, nullptr);
|
||||
|
||||
if (unlikely(m_ctx->HasDirtyGraphicsBindings()))
|
||||
m_ctx->ApplyDirtyGraphicsBindings();
|
||||
|
||||
m_ctx->EmitCs([
|
||||
cCount = DrawCount,
|
||||
cOffset = ByteOffsetForArgs,
|
||||
cStride = ByteStrideForArgs
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->drawIndexedIndirect(cOffset, cCount, cStride);
|
||||
ctx->drawIndexedIndirect(cOffset, cCount, cStride, false);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -88,6 +94,9 @@ namespace dxvk {
|
|||
D3D10DeviceLock lock = m_ctx->LockContext();
|
||||
m_ctx->SetDrawBuffers(pBufferForArgs, pBufferForCount);
|
||||
|
||||
if (unlikely(m_ctx->HasDirtyGraphicsBindings()))
|
||||
m_ctx->ApplyDirtyGraphicsBindings();
|
||||
|
||||
m_ctx->EmitCs([
|
||||
cMaxCount = MaxDrawCount,
|
||||
cArgOffset = ByteOffsetForArgs,
|
||||
|
@ -110,6 +119,9 @@ namespace dxvk {
|
|||
D3D10DeviceLock lock = m_ctx->LockContext();
|
||||
m_ctx->SetDrawBuffers(pBufferForArgs, pBufferForCount);
|
||||
|
||||
if (unlikely(m_ctx->HasDirtyGraphicsBindings()))
|
||||
m_ctx->ApplyDirtyGraphicsBindings();
|
||||
|
||||
m_ctx->EmitCs([
|
||||
cMaxCount = MaxDrawCount,
|
||||
cArgOffset = ByteOffsetForArgs,
|
||||
|
@ -146,11 +158,10 @@ namespace dxvk {
|
|||
D3D11Device* parent = static_cast<D3D11Device*>(m_ctx->GetParentInterface());
|
||||
DxvkBarrierControlFlags flags = parent->GetOptionsBarrierControlFlags();
|
||||
|
||||
if (ControlFlags & D3D11_VK_BARRIER_CONTROL_IGNORE_WRITE_AFTER_WRITE)
|
||||
flags.set(DxvkBarrierControl::IgnoreWriteAfterWrite);
|
||||
|
||||
if (ControlFlags & D3D11_VK_BARRIER_CONTROL_IGNORE_GRAPHICS_UAV)
|
||||
flags.set(DxvkBarrierControl::IgnoreGraphicsBarriers);
|
||||
if (ControlFlags & D3D11_VK_BARRIER_CONTROL_IGNORE_WRITE_AFTER_WRITE) {
|
||||
flags.set(DxvkBarrierControl::ComputeAllowReadWriteOverlap,
|
||||
DxvkBarrierControl::GraphicsAllowReadWriteOverlap);
|
||||
}
|
||||
|
||||
m_ctx->EmitCs([cFlags = flags] (DxvkContext* ctx) {
|
||||
ctx->setBarrierControl(cFlags);
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace dxvk {
|
|||
: D3D11CommonContext<D3D11ImmediateContext>(pParent, Device, 0, DxvkCsChunkFlag::SingleUse),
|
||||
m_csThread(Device, Device->createContext()),
|
||||
m_submissionFence(new sync::CallbackFence()),
|
||||
m_flushTracker(pParent->GetOptions()->reproducibleCommandStream),
|
||||
m_flushTracker(GetMaxFlushType(pParent, Device)),
|
||||
m_stagingBufferFence(new sync::Fence(0)),
|
||||
m_multithread(this, false, pParent->GetOptions()->enableContextLock),
|
||||
m_videoContext(this, Device) {
|
||||
|
@ -97,6 +97,10 @@ namespace dxvk {
|
|||
// Ignore the DONOTFLUSH flag here as some games will spin
|
||||
// on queries without ever flushing the context otherwise.
|
||||
D3D10DeviceLock lock = LockContext();
|
||||
|
||||
if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))
|
||||
m_flushReason = "Query read-back";
|
||||
|
||||
ConsiderFlush(GpuFlushType::ImplicitSynchronization);
|
||||
}
|
||||
|
||||
|
@ -156,6 +160,9 @@ namespace dxvk {
|
|||
void STDMETHODCALLTYPE D3D11ImmediateContext::Flush() {
|
||||
D3D10DeviceLock lock = LockContext();
|
||||
|
||||
if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))
|
||||
m_flushReason = "Explicit Flush";
|
||||
|
||||
ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, true);
|
||||
}
|
||||
|
||||
|
@ -165,6 +172,9 @@ namespace dxvk {
|
|||
HANDLE hEvent) {
|
||||
D3D10DeviceLock lock = LockContext();
|
||||
|
||||
if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))
|
||||
m_flushReason = "Explicit Flush";
|
||||
|
||||
ExecuteFlush(GpuFlushType::ExplicitFlush, hEvent, true);
|
||||
}
|
||||
|
||||
|
@ -185,6 +195,9 @@ namespace dxvk {
|
|||
ctx->signalFence(cFence, cValue);
|
||||
});
|
||||
|
||||
if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))
|
||||
m_flushReason = "Fence signal";
|
||||
|
||||
ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, true);
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -199,6 +212,9 @@ namespace dxvk {
|
|||
if (!fence)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))
|
||||
m_flushReason = "Fence wait";
|
||||
|
||||
ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, true);
|
||||
|
||||
EmitCs([
|
||||
|
@ -219,6 +235,11 @@ namespace dxvk {
|
|||
|
||||
auto commandList = static_cast<D3D11CommandList*>(pCommandList);
|
||||
|
||||
// Reset dirty binding tracking before submitting any CS chunks.
|
||||
// This is needed so that any submission that might occur during
|
||||
// this call does not disrupt bindings set by the deferred context.
|
||||
ResetDirtyTracking();
|
||||
|
||||
// Clear state so that the command list can't observe any
|
||||
// current context state. The command list itself will clean
|
||||
// up after execution to ensure that no state changes done
|
||||
|
@ -754,7 +775,11 @@ namespace dxvk {
|
|||
if (!pState)
|
||||
return;
|
||||
|
||||
// Reset all state affected by the current context state
|
||||
// Clear dirty tracking here since all context state will be
|
||||
// re-applied anyway when the context state is swapped in again.
|
||||
ResetDirtyTracking();
|
||||
|
||||
// Reset all state affected by the current context state.
|
||||
ResetCommandListState();
|
||||
|
||||
Com<D3D11DeviceContextState, false> oldState = std::move(m_stateObject);
|
||||
|
@ -861,11 +886,22 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void D3D11ImmediateContext::EndFrame() {
|
||||
void D3D11ImmediateContext::EndFrame(
|
||||
Rc<DxvkLatencyTracker> LatencyTracker) {
|
||||
D3D10DeviceLock lock = LockContext();
|
||||
|
||||
EmitCs<false>([] (DxvkContext* ctx) {
|
||||
// Don't keep draw buffers alive indefinitely. This cannot be
|
||||
// done in ExecuteFlush because command recording itself might
|
||||
// flush, so no state changes are allowed to happen there.
|
||||
SetDrawBuffers(nullptr, nullptr);
|
||||
|
||||
EmitCs<false>([
|
||||
cTracker = std::move(LatencyTracker)
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->endFrame();
|
||||
|
||||
if (cTracker && cTracker->needsAutoMarkers())
|
||||
ctx->endLatencyTracking(cTracker);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -883,42 +919,44 @@ namespace dxvk {
|
|||
// Wait for any CS chunk using the resource to execute, since
|
||||
// otherwise we cannot accurately determine if the resource is
|
||||
// actually being used by the GPU right now.
|
||||
bool isInUse = Resource.isInUse(access);
|
||||
|
||||
if (!isInUse) {
|
||||
if (!Resource.isInUse(access)) {
|
||||
SynchronizeCsThread(SequenceNumber);
|
||||
isInUse = Resource.isInUse(access);
|
||||
|
||||
if (!Resource.isInUse(access))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture))) {
|
||||
m_flushReason = str::format("Map ", Resource.getDebugName(), " (MAP",
|
||||
MapType != D3D11_MAP_WRITE ? "_READ" : "",
|
||||
MapType != D3D11_MAP_READ ? "_WRITE" : "", ")");
|
||||
}
|
||||
|
||||
if (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT) {
|
||||
if (isInUse) {
|
||||
// We don't have to wait, but misbehaving games may
|
||||
// still try to spin on `Map` until the resource is
|
||||
// idle, so we should flush pending commands
|
||||
ConsiderFlush(GpuFlushType::ImplicitSynchronization);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (isInUse) {
|
||||
// Make sure pending commands using the resource get
|
||||
// executed on the the GPU if we have to wait for it
|
||||
ExecuteFlush(GpuFlushType::ImplicitSynchronization, nullptr, false);
|
||||
SynchronizeCsThread(SequenceNumber);
|
||||
|
||||
m_device->waitForResource(Resource, access);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void D3D11ImmediateContext::InjectCsChunk(
|
||||
DxvkCsQueue Queue,
|
||||
DxvkCsChunkRef&& Chunk,
|
||||
bool Synchronize) {
|
||||
// Do not update the sequence number when emitting a chunk
|
||||
// from an external source since that would break tracking
|
||||
m_csThread.injectChunk(std::move(Chunk), Synchronize);
|
||||
m_csThread.injectChunk(Queue, std::move(Chunk), Synchronize);
|
||||
}
|
||||
|
||||
|
||||
|
@ -963,8 +1001,106 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void D3D11ImmediateContext::ApplyDirtyNullBindings() {
|
||||
// At the end of a submission, set all bindings that have not been applied yet
|
||||
// to null on the DXVK context. This way, we avoid keeping resources alive that
|
||||
// are bound to the DXVK context but not to the immediate context.
|
||||
//
|
||||
// Note: This requires that all methods that may modify dirty bindings on the
|
||||
// DXVK context also reset the corresponding dirty bits *before* performing the
|
||||
// bind operation, or otherwise an implicit flush can potentially override them.
|
||||
auto& dirtyState = m_state.lazy.bindingsDirty;
|
||||
|
||||
EmitCs<false>([
|
||||
cDirtyState = dirtyState
|
||||
] (DxvkContext* ctx) {
|
||||
for (uint32_t i = 0; i < uint32_t(DxbcProgramType::Count); i++) {
|
||||
auto dxStage = DxbcProgramType(i);
|
||||
auto vkStage = GetShaderStage(dxStage);
|
||||
|
||||
// Unbind all dirty constant buffers
|
||||
auto cbvSlot = computeConstantBufferBinding(dxStage, 0);
|
||||
|
||||
for (uint32_t index : bit::BitMask(cDirtyState[dxStage].cbvMask))
|
||||
ctx->bindUniformBuffer(vkStage, cbvSlot + index, DxvkBufferSlice());
|
||||
|
||||
// Unbind all dirty samplers
|
||||
auto samplerSlot = computeSamplerBinding(dxStage, 0);
|
||||
|
||||
for (uint32_t index : bit::BitMask(cDirtyState[dxStage].samplerMask))
|
||||
ctx->bindResourceSampler(vkStage, samplerSlot + index, nullptr);
|
||||
|
||||
// Unbind all dirty shader resource views
|
||||
auto srvSlot = computeSrvBinding(dxStage, 0);
|
||||
|
||||
for (uint32_t m = 0; m < cDirtyState[dxStage].srvMask.size(); m++) {
|
||||
for (uint32_t index : bit::BitMask(cDirtyState[dxStage].srvMask[m]))
|
||||
ctx->bindResourceImageView(vkStage, srvSlot + index + m * 64u, nullptr);
|
||||
}
|
||||
|
||||
// Unbind all dirty unordered access views
|
||||
VkShaderStageFlags uavStages = 0u;
|
||||
|
||||
if (dxStage == DxbcProgramType::ComputeShader)
|
||||
uavStages = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
else if (dxStage == DxbcProgramType::PixelShader)
|
||||
uavStages = VK_SHADER_STAGE_ALL_GRAPHICS;
|
||||
|
||||
if (uavStages) {
|
||||
auto uavSlot = computeUavBinding(dxStage, 0);
|
||||
auto ctrSlot = computeUavCounterBinding(dxStage, 0);
|
||||
|
||||
for (uint32_t index : bit::BitMask(cDirtyState[dxStage].uavMask)) {
|
||||
ctx->bindResourceImageView(vkStage, uavSlot + index, nullptr);
|
||||
ctx->bindResourceBufferView(vkStage, ctrSlot + index, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Since we set the DXVK context bindings to null, any bindings that are null
|
||||
// on the D3D context are no longer dirty, so we can clear the respective bits.
|
||||
for (uint32_t i = 0; i < uint32_t(DxbcProgramType::Count); i++) {
|
||||
auto stage = DxbcProgramType(i);
|
||||
|
||||
for (uint32_t index : bit::BitMask(dirtyState[stage].cbvMask)) {
|
||||
if (!m_state.cbv[stage].buffers[index].buffer.ptr())
|
||||
dirtyState[stage].cbvMask &= ~(1u << index);
|
||||
}
|
||||
|
||||
for (uint32_t index : bit::BitMask(dirtyState[stage].samplerMask)) {
|
||||
if (!m_state.samplers[stage].samplers[index])
|
||||
dirtyState[stage].samplerMask &= ~(1u << index);
|
||||
}
|
||||
|
||||
for (uint32_t m = 0; m < dirtyState[stage].srvMask.size(); m++) {
|
||||
for (uint32_t index : bit::BitMask(dirtyState[stage].srvMask[m])) {
|
||||
if (!m_state.srv[stage].views[index + m * 64u].ptr())
|
||||
dirtyState[stage].srvMask[m] &= ~(uint64_t(1u) << index);
|
||||
}
|
||||
}
|
||||
|
||||
if (stage == DxbcProgramType::ComputeShader || stage == DxbcProgramType::PixelShader) {
|
||||
auto& uavs = stage == DxbcProgramType::ComputeShader ? m_state.uav.views : m_state.om.uavs;
|
||||
|
||||
for (uint32_t index : bit::BitMask(dirtyState[stage].uavMask)) {
|
||||
if (!uavs[index].ptr())
|
||||
dirtyState[stage].uavMask &= ~(uint64_t(1u) << index);
|
||||
}
|
||||
}
|
||||
|
||||
if (dirtyState[stage].empty())
|
||||
m_state.lazy.shadersDirty.clr(stage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void D3D11ImmediateContext::ConsiderFlush(
|
||||
GpuFlushType FlushType) {
|
||||
// In stress test mode, behave as if this would always flush
|
||||
if (DebugLazyBinding == Tristate::True)
|
||||
ApplyDirtyNullBindings();
|
||||
|
||||
uint64_t chunkId = GetCurrentSequenceNumber();
|
||||
uint64_t submissionId = m_submissionFence->value();
|
||||
|
||||
|
@ -986,6 +1122,9 @@ namespace dxvk {
|
|||
if (!GetPendingCsChunks() && !hEvent)
|
||||
return;
|
||||
|
||||
// Unbind unused resources
|
||||
ApplyDirtyNullBindings();
|
||||
|
||||
// Signal the submission fence and flush the command list
|
||||
uint64_t submissionId = ++m_submissionId;
|
||||
|
||||
|
@ -1000,11 +1139,14 @@ namespace dxvk {
|
|||
cSubmissionId = submissionId,
|
||||
cSubmissionStatus = synchronizeSubmission ? &m_submitStatus : nullptr,
|
||||
cStagingFence = m_stagingBufferFence,
|
||||
cStagingMemory = GetStagingMemoryStatistics().allocatedTotal
|
||||
cStagingMemory = GetStagingMemoryStatistics().allocatedTotal,
|
||||
cFlushReason = std::exchange(m_flushReason, std::string())
|
||||
] (DxvkContext* ctx) {
|
||||
auto debugLabel = vk::makeLabel(0xff5959, cFlushReason.c_str());
|
||||
|
||||
ctx->signal(cSubmissionFence, cSubmissionId);
|
||||
ctx->signal(cStagingFence, cStagingMemory);
|
||||
ctx->flushCommandList(cSubmissionStatus);
|
||||
ctx->flushCommandList(&debugLabel, cSubmissionStatus);
|
||||
});
|
||||
|
||||
FlushCsChunk();
|
||||
|
@ -1028,6 +1170,9 @@ namespace dxvk {
|
|||
// Notify the device that the context has been flushed,
|
||||
// this resets some resource initialization heuristics.
|
||||
m_parent->NotifyContextFlush();
|
||||
|
||||
// No point in tracking this across submissions
|
||||
m_hasPendingMsaaResolve = false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1067,4 +1212,16 @@ namespace dxvk {
|
|||
return stats;
|
||||
}
|
||||
|
||||
|
||||
GpuFlushType D3D11ImmediateContext::GetMaxFlushType(
|
||||
D3D11Device* pParent,
|
||||
const Rc<DxvkDevice>& Device) {
|
||||
if (pParent->GetOptions()->reproducibleCommandStream)
|
||||
return GpuFlushType::ExplicitFlush;
|
||||
else if (Device->perfHints().preferRenderPassOps)
|
||||
return GpuFlushType::ImplicitMediumHint;
|
||||
else
|
||||
return GpuFlushType::ImplicitWeakHint;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -98,16 +98,18 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
void InjectCsChunk(
|
||||
DxvkCsQueue Queue,
|
||||
DxvkCsChunkRef&& Chunk,
|
||||
bool Synchronize);
|
||||
|
||||
template<typename Fn>
|
||||
void InjectCs(
|
||||
DxvkCsQueue Queue,
|
||||
Fn&& Command) {
|
||||
auto chunk = AllocCsChunk();
|
||||
chunk->push(std::move(Command));
|
||||
|
||||
InjectCsChunk(std::move(chunk), false);
|
||||
InjectCsChunk(Queue, std::move(chunk), false);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -129,11 +131,15 @@ namespace dxvk {
|
|||
VkDeviceSize m_discardMemoryCounter = 0u;
|
||||
VkDeviceSize m_discardMemoryOnFlush = 0u;
|
||||
|
||||
bool m_hasPendingMsaaResolve = false;
|
||||
|
||||
D3D10Multithread m_multithread;
|
||||
D3D11VideoContext m_videoContext;
|
||||
|
||||
Com<D3D11DeviceContextState, false> m_stateObject;
|
||||
|
||||
std::string m_flushReason;
|
||||
|
||||
HRESULT MapBuffer(
|
||||
D3D11Buffer* pResource,
|
||||
D3D11_MAP MapType,
|
||||
|
@ -169,7 +175,8 @@ namespace dxvk {
|
|||
|
||||
void SynchronizeDevice();
|
||||
|
||||
void EndFrame();
|
||||
void EndFrame(
|
||||
Rc<DxvkLatencyTracker> LatencyTracker);
|
||||
|
||||
bool WaitForResource(
|
||||
const DxvkPagedResource& Resource,
|
||||
|
@ -190,6 +197,8 @@ namespace dxvk {
|
|||
|
||||
uint64_t GetPendingCsChunks();
|
||||
|
||||
void ApplyDirtyNullBindings();
|
||||
|
||||
void ConsiderFlush(
|
||||
GpuFlushType FlushType);
|
||||
|
||||
|
@ -205,6 +214,10 @@ namespace dxvk {
|
|||
|
||||
DxvkStagingBufferStats GetStagingMemoryStatistics();
|
||||
|
||||
static GpuFlushType GetMaxFlushType(
|
||||
D3D11Device* pParent,
|
||||
const Rc<DxvkDevice>& Device);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -199,10 +199,11 @@ namespace dxvk {
|
|||
UINT stencilRef = D3D11_DEFAULT_STENCIL_REFERENCE;
|
||||
|
||||
UINT maxRtv = 0u;
|
||||
UINT minUav = D3D11_1_UAV_SLOT_COUNT;
|
||||
UINT maxUav = 0u;
|
||||
|
||||
void reset() {
|
||||
for (uint32_t i = 0; i < maxUav; i++)
|
||||
for (uint32_t i = minUav; i < maxUav; i++)
|
||||
uavs[i] = nullptr;
|
||||
|
||||
for (uint32_t i = 0; i < maxRtv; i++)
|
||||
|
@ -220,8 +221,9 @@ namespace dxvk {
|
|||
sampleMask = D3D11_DEFAULT_SAMPLE_MASK;
|
||||
stencilRef = D3D11_DEFAULT_STENCIL_REFERENCE;
|
||||
|
||||
maxRtv = 0;
|
||||
maxUav = 0;
|
||||
maxRtv = 0u;
|
||||
minUav = D3D11_1_UAV_SLOT_COUNT;
|
||||
maxUav = 0u;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -232,12 +234,12 @@ namespace dxvk {
|
|||
* argument and draw count buffer.
|
||||
*/
|
||||
struct D3D11ContextStateID {
|
||||
Com<D3D11Buffer, false> argBuffer = nullptr;
|
||||
Com<D3D11Buffer, false> cntBuffer = nullptr;
|
||||
uint64_t argBufferCookie = 0u;
|
||||
uint64_t cntBufferCookie = 0u;
|
||||
|
||||
void reset() {
|
||||
argBuffer = nullptr;
|
||||
cntBuffer = nullptr;
|
||||
argBufferCookie = 0u;
|
||||
cntBufferCookie = 0u;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -303,6 +305,32 @@ namespace dxvk {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Lazy binding state
|
||||
*
|
||||
* Keeps track of what state needs to be
|
||||
* re-applied to the context.
|
||||
*/
|
||||
struct D3D11LazyBindings {
|
||||
DxbcProgramTypeFlags shadersUsed = 0u;
|
||||
DxbcProgramTypeFlags shadersDirty = 0u;
|
||||
DxbcProgramTypeFlags graphicsUavShaders = 0u;
|
||||
|
||||
D3D11ShaderStageState<DxbcBindingMask> bindingsUsed;
|
||||
D3D11ShaderStageState<DxbcBindingMask> bindingsDirty;
|
||||
|
||||
void reset() {
|
||||
shadersUsed = 0u;
|
||||
shadersDirty = 0u;
|
||||
graphicsUavShaders = 0u;
|
||||
|
||||
bindingsUsed.reset();
|
||||
bindingsDirty.reset();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Context state
|
||||
*/
|
||||
|
@ -325,6 +353,8 @@ namespace dxvk {
|
|||
D3D11SrvBindings srv;
|
||||
D3D11UavBindings uav;
|
||||
D3D11SamplerBindings samplers;
|
||||
|
||||
D3D11LazyBindings lazy;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -342,7 +372,7 @@ namespace dxvk {
|
|||
* \brief Maximum used binding numbers for all context state
|
||||
*/
|
||||
struct D3D11MaxUsedBindings {
|
||||
std::array<D3D11MaxUsedStageBindings, 6> stages;
|
||||
std::array<D3D11MaxUsedStageBindings, uint32_t(DxbcProgramType::Count)> stages;
|
||||
uint32_t vbCount;
|
||||
uint32_t soCount;
|
||||
};
|
||||
|
|
|
@ -2828,7 +2828,7 @@ namespace dxvk {
|
|||
feedback = ctx->ensureImageCompatibility(cImage, usageInfo);
|
||||
});
|
||||
|
||||
m_device->GetContext()->InjectCsChunk(std::move(chunk), true);
|
||||
m_device->GetContext()->InjectCsChunk(DxvkCsQueue::HighPriority, std::move(chunk), true);
|
||||
|
||||
if (!feedback) {
|
||||
Logger::err(str::format("Failed to lock image:"
|
||||
|
@ -2852,7 +2852,7 @@ namespace dxvk {
|
|||
ctx->ensureBufferAddress(cBuffer);
|
||||
});
|
||||
|
||||
m_device->GetContext()->InjectCsChunk(std::move(chunk), true);
|
||||
m_device->GetContext()->InjectCsChunk(DxvkCsQueue::HighPriority, std::move(chunk), true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3058,6 +3058,198 @@ namespace dxvk {
|
|||
|
||||
|
||||
|
||||
D3D11ReflexDevice::D3D11ReflexDevice(
|
||||
D3D11DXGIDevice* pContainer,
|
||||
D3D11Device* pDevice)
|
||||
: m_container(pContainer), m_device(pDevice) {
|
||||
auto dxvkDevice = pDevice->GetDXVKDevice();
|
||||
|
||||
m_reflexEnabled = dxvkDevice->features().nvLowLatency2
|
||||
&& dxvkDevice->config().latencySleep == Tristate::Auto;
|
||||
}
|
||||
|
||||
|
||||
D3D11ReflexDevice::~D3D11ReflexDevice() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
ULONG STDMETHODCALLTYPE D3D11ReflexDevice::AddRef() {
|
||||
return m_container->AddRef();
|
||||
}
|
||||
|
||||
|
||||
ULONG STDMETHODCALLTYPE D3D11ReflexDevice::Release() {
|
||||
return m_container->Release();
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::QueryInterface(
|
||||
REFIID riid,
|
||||
void** ppvObject) {
|
||||
return m_container->QueryInterface(riid, ppvObject);
|
||||
}
|
||||
|
||||
|
||||
BOOL STDMETHODCALLTYPE D3D11ReflexDevice::SupportsLowLatency() {
|
||||
return m_reflexEnabled;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::LatencySleep() {
|
||||
if (!m_reflexEnabled)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
// Don't keep object locked while sleeping
|
||||
Rc<DxvkReflexLatencyTrackerNv> tracker;
|
||||
|
||||
{ std::lock_guard lock(m_mutex);
|
||||
tracker = m_tracker;
|
||||
}
|
||||
|
||||
if (tracker)
|
||||
tracker->latencySleep();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::SetLatencySleepMode(
|
||||
BOOL LowLatencyEnable,
|
||||
BOOL LowLatencyBoost,
|
||||
UINT32 MinIntervalUs) {
|
||||
if (!m_reflexEnabled)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
if (m_tracker) {
|
||||
m_tracker->setLatencySleepMode(
|
||||
LowLatencyEnable, LowLatencyBoost, MinIntervalUs);
|
||||
}
|
||||
|
||||
// Write back in case we have no swapchain yet
|
||||
m_enableLowLatency = LowLatencyEnable;
|
||||
m_enableBoost = LowLatencyBoost;
|
||||
m_minIntervalUs = MinIntervalUs;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::SetLatencyMarker(
|
||||
UINT64 FrameId,
|
||||
UINT32 MarkerType) {
|
||||
if (!m_reflexEnabled)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
if (m_tracker) {
|
||||
auto marker = VkLatencyMarkerNV(MarkerType);
|
||||
m_tracker->setLatencyMarker(FrameId, marker);
|
||||
|
||||
if (marker == VK_LATENCY_MARKER_RENDERSUBMIT_START_NV) {
|
||||
m_device->GetContext()->InjectCs(DxvkCsQueue::Ordered, [
|
||||
cTracker = m_tracker,
|
||||
cFrameId = FrameId
|
||||
] (DxvkContext* ctx) {
|
||||
uint64_t frameId = cTracker->frameIdFromAppFrameId(cFrameId);
|
||||
|
||||
if (frameId)
|
||||
ctx->beginLatencyTracking(cTracker, frameId);
|
||||
});
|
||||
} else if (marker == VK_LATENCY_MARKER_RENDERSUBMIT_END_NV) {
|
||||
m_device->GetContext()->InjectCs(DxvkCsQueue::Ordered, [
|
||||
cTracker = m_tracker
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->endLatencyTracking(cTracker);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::GetLatencyInfo(
|
||||
D3D_LOW_LATENCY_RESULTS* pLowLatencyResults) {
|
||||
constexpr static size_t FrameCount = 64;
|
||||
|
||||
if (!pLowLatencyResults)
|
||||
return E_INVALIDARG;
|
||||
|
||||
for (size_t i = 0; i < FrameCount; i++)
|
||||
pLowLatencyResults->frameReports[i] = D3D_LOW_LATENCY_FRAME_REPORT();
|
||||
|
||||
if (!m_reflexEnabled)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
if (!m_tracker)
|
||||
return S_OK;
|
||||
|
||||
// Apparently we have to report all 64 frames, or nothing
|
||||
std::array<DxvkReflexFrameReport, FrameCount> reports = { };
|
||||
uint32_t reportCount = m_tracker->getFrameReports(FrameCount, reports.data());
|
||||
|
||||
if (reportCount < FrameCount)
|
||||
return S_OK;
|
||||
|
||||
for (uint32_t i = 0; i < FrameCount; i++) {
|
||||
auto& src = reports[i];
|
||||
auto& dst = pLowLatencyResults->frameReports[i];
|
||||
|
||||
dst.frameID = src.report.presentID;
|
||||
dst.inputSampleTime = src.report.inputSampleTimeUs;
|
||||
dst.simStartTime = src.report.simStartTimeUs;
|
||||
dst.simEndTime = src.report.simEndTimeUs;
|
||||
dst.renderSubmitStartTime = src.report.renderSubmitStartTimeUs;
|
||||
dst.renderSubmitEndTime = src.report.renderSubmitEndTimeUs;
|
||||
dst.presentStartTime = src.report.presentStartTimeUs;
|
||||
dst.presentEndTime = src.report.presentEndTimeUs;
|
||||
dst.driverStartTime = src.report.driverStartTimeUs;
|
||||
dst.driverEndTime = src.report.driverEndTimeUs;
|
||||
dst.osRenderQueueStartTime = src.report.osRenderQueueStartTimeUs;
|
||||
dst.osRenderQueueEndTime = src.report.osRenderQueueEndTimeUs;
|
||||
dst.gpuRenderStartTime = src.report.gpuRenderStartTimeUs;
|
||||
dst.gpuRenderEndTime = src.report.gpuRenderEndTimeUs;
|
||||
dst.gpuActiveRenderTimeUs = src.gpuActiveTimeUs;
|
||||
dst.gpuFrameTimeUs = 0;
|
||||
|
||||
if (i) {
|
||||
dst.gpuFrameTimeUs = reports[i - 0].report.gpuRenderEndTimeUs
|
||||
- reports[i - 1].report.gpuRenderEndTimeUs;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
void D3D11ReflexDevice::RegisterLatencyTracker(
|
||||
Rc<DxvkLatencyTracker> Tracker) {
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
if (m_tracker)
|
||||
return;
|
||||
|
||||
if ((m_tracker = dynamic_cast<DxvkReflexLatencyTrackerNv*>(Tracker.ptr())))
|
||||
m_tracker->setLatencySleepMode(m_enableLowLatency, m_enableBoost, m_minIntervalUs);
|
||||
}
|
||||
|
||||
|
||||
void D3D11ReflexDevice::UnregisterLatencyTracker(
|
||||
Rc<DxvkLatencyTracker> Tracker) {
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
if (m_tracker == Tracker)
|
||||
m_tracker = nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
DXGIVkSwapChainFactory::DXGIVkSwapChainFactory(
|
||||
D3D11DXGIDevice* pContainer,
|
||||
D3D11Device* pDevice)
|
||||
|
@ -3159,6 +3351,7 @@ namespace dxvk {
|
|||
m_d3d11DeviceExt(this, &m_d3d11Device),
|
||||
m_d3d11Interop (this, &m_d3d11Device),
|
||||
m_d3d11Video (this, &m_d3d11Device),
|
||||
m_d3d11Reflex (this, &m_d3d11Device),
|
||||
m_d3d11on12 (this, &m_d3d11Device, pD3D12Device, pD3D12Queue),
|
||||
m_metaDevice (this),
|
||||
m_dxvkFactory (this, &m_d3d11Device) {
|
||||
|
@ -3231,8 +3424,14 @@ namespace dxvk {
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
if (riid == __uuidof(ID3DLowLatencyDevice)) {
|
||||
*ppvObject = ref(&m_d3d11Reflex);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (m_d3d11on12.Is11on12Device()) {
|
||||
if (riid == __uuidof(ID3D11On12Device)) {
|
||||
if (riid == __uuidof(ID3D11On12Device)
|
||||
|| riid == __uuidof(ID3D11On12Device1_DXVK)) {
|
||||
*ppvObject = ref(&m_d3d11on12);
|
||||
return S_OK;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "../dxgi/dxgi_interfaces.h"
|
||||
|
||||
#include "../dxvk/dxvk_cs.h"
|
||||
#include "../dxvk/dxvk_latency_reflex.h"
|
||||
|
||||
#include "../d3d10/d3d10_device.h"
|
||||
|
||||
|
@ -428,6 +429,13 @@ namespace dxvk {
|
|||
m_initializer->NotifyContextFlush();
|
||||
}
|
||||
|
||||
void InitShaderIcb(
|
||||
D3D11CommonShader* pShader,
|
||||
size_t IcbSize,
|
||||
const void* pIcbData) {
|
||||
return m_initializer->InitShaderIcb(pShader, IcbSize, pIcbData);
|
||||
}
|
||||
|
||||
VkPipelineStageFlags GetEnabledShaderStages() const {
|
||||
return m_dxvkDevice->getShaderPipelineStages();
|
||||
}
|
||||
|
@ -471,13 +479,13 @@ namespace dxvk {
|
|||
const Rc<DxvkAdapter>& Adapter);
|
||||
|
||||
DxvkBarrierControlFlags GetOptionsBarrierControlFlags() {
|
||||
DxvkBarrierControlFlags barrierControl;
|
||||
DxvkBarrierControlFlags barrierControl = 0u;
|
||||
|
||||
if (m_d3d11Options.relaxedBarriers)
|
||||
barrierControl.set(DxvkBarrierControl::IgnoreWriteAfterWrite);
|
||||
barrierControl.set(DxvkBarrierControl::ComputeAllowWriteOnlyOverlap);
|
||||
|
||||
if (m_d3d11Options.ignoreGraphicsBarriers)
|
||||
barrierControl.set(DxvkBarrierControl::IgnoreGraphicsBarriers);
|
||||
if (m_d3d11Options.relaxedBarriers || m_d3d11Options.relaxedGraphicsBarriers)
|
||||
barrierControl.set(DxvkBarrierControl::GraphicsAllowReadWriteOverlap);
|
||||
|
||||
return barrierControl;
|
||||
}
|
||||
|
@ -751,6 +759,66 @@ namespace dxvk {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Nvidia Reflex interop
|
||||
*/
|
||||
class D3D11ReflexDevice : public ID3DLowLatencyDevice {
|
||||
|
||||
public:
|
||||
|
||||
D3D11ReflexDevice(
|
||||
D3D11DXGIDevice* pContainer,
|
||||
D3D11Device* pDevice);
|
||||
|
||||
~D3D11ReflexDevice();
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef();
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(
|
||||
REFIID riid,
|
||||
void** ppvObject);
|
||||
|
||||
BOOL STDMETHODCALLTYPE SupportsLowLatency();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE LatencySleep();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetLatencySleepMode(
|
||||
BOOL LowLatencyEnable,
|
||||
BOOL LowLatencyBoost,
|
||||
UINT32 MinIntervalUs);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetLatencyMarker(
|
||||
UINT64 FrameId,
|
||||
UINT32 MarkerType);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetLatencyInfo(
|
||||
D3D_LOW_LATENCY_RESULTS* pLowLatencyResults);
|
||||
|
||||
void RegisterLatencyTracker(
|
||||
Rc<DxvkLatencyTracker> Tracker);
|
||||
|
||||
void UnregisterLatencyTracker(
|
||||
Rc<DxvkLatencyTracker> Tracker);
|
||||
|
||||
private:
|
||||
|
||||
D3D11DXGIDevice* m_container;
|
||||
D3D11Device* m_device;
|
||||
|
||||
bool m_reflexEnabled = false;
|
||||
|
||||
dxvk::mutex m_mutex;
|
||||
|
||||
bool m_enableLowLatency = false;
|
||||
bool m_enableBoost = false;
|
||||
uint64_t m_minIntervalUs = 0u;
|
||||
|
||||
Rc<DxvkReflexLatencyTrackerNv> m_tracker;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief DXVK swap chain factory
|
||||
*/
|
||||
|
@ -914,6 +982,7 @@ namespace dxvk {
|
|||
D3D11DeviceExt m_d3d11DeviceExt;
|
||||
D3D11VkInterop m_d3d11Interop;
|
||||
D3D11VideoDevice m_d3d11Video;
|
||||
D3D11ReflexDevice m_d3d11Reflex;
|
||||
D3D11on12Device m_d3d11on12;
|
||||
DXGIDXVKDevice m_metaDevice;
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ namespace dxvk {
|
|||
|
||||
HRESULT STDMETHODCALLTYPE GetPrivateData(
|
||||
REFGUID guid,
|
||||
UINT *pDataSize,
|
||||
void *pData) final {
|
||||
UINT* pDataSize,
|
||||
void* pData) final {
|
||||
return m_privateData.getData(
|
||||
guid, pDataSize, pData);
|
||||
}
|
||||
|
@ -29,14 +29,18 @@ namespace dxvk {
|
|||
HRESULT STDMETHODCALLTYPE SetPrivateData(
|
||||
REFGUID guid,
|
||||
UINT DataSize,
|
||||
const void *pData) final {
|
||||
const void* pData) final {
|
||||
// WKPDID_D3DDebugObjectName, can't use directly due to MSVC link errors
|
||||
if (guid == GUID{0x429b8c22,0x9188,0x4b0c,0x87,0x42,0xac,0xb0,0xbf,0x85,0xc2,0x00})
|
||||
SetDebugName(static_cast<const char*>(pData));
|
||||
|
||||
return m_privateData.setData(
|
||||
guid, DataSize, pData);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(
|
||||
REFGUID guid,
|
||||
const IUnknown *pUnknown) final {
|
||||
const IUnknown* pUnknown) final {
|
||||
return m_privateData.setInterface(
|
||||
guid, pUnknown);
|
||||
}
|
||||
|
@ -46,6 +50,10 @@ namespace dxvk {
|
|||
*ppDevice = ref(GetParentInterface());
|
||||
}
|
||||
|
||||
virtual void STDMETHODCALLTYPE SetDebugName(const char* pName) {
|
||||
// No-op by default
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
ID3D11Device* GetParentInterface() const {
|
||||
|
|
|
@ -77,6 +77,33 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void D3D11Initializer::InitShaderIcb(
|
||||
D3D11CommonShader* pShader,
|
||||
size_t IcbSize,
|
||||
const void* pIcbData) {
|
||||
std::lock_guard<dxvk::mutex> lock(m_mutex);
|
||||
m_transferCommands += 1;
|
||||
|
||||
auto icbSlice = pShader->GetIcb();
|
||||
auto srcSlice = m_stagingBuffer.alloc(icbSlice.length());
|
||||
|
||||
std::memcpy(srcSlice.mapPtr(0), pIcbData, IcbSize);
|
||||
|
||||
if (IcbSize < icbSlice.length())
|
||||
std::memset(srcSlice.mapPtr(IcbSize), 0, icbSlice.length() - IcbSize);
|
||||
|
||||
EmitCs([
|
||||
cIcbSlice = std::move(icbSlice),
|
||||
cSrcSlice = std::move(srcSlice)
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->copyBuffer(cIcbSlice.buffer(), cIcbSlice.offset(),
|
||||
cSrcSlice.buffer(), cSrcSlice.offset(), cIcbSlice.length());
|
||||
});
|
||||
|
||||
ThrottleAllocationLocked();
|
||||
}
|
||||
|
||||
|
||||
void D3D11Initializer::InitDeviceLocalBuffer(
|
||||
D3D11Buffer* pBuffer,
|
||||
const D3D11_SUBRESOURCE_DATA* pInitialData) {
|
||||
|
@ -337,7 +364,7 @@ namespace dxvk {
|
|||
cSignalValue = stats.allocatedTotal
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->signal(cSignal, cSignalValue);
|
||||
ctx->flushCommandList(nullptr);
|
||||
ctx->flushCommandList(nullptr, nullptr);
|
||||
});
|
||||
|
||||
FlushCsChunk();
|
||||
|
@ -371,7 +398,7 @@ namespace dxvk {
|
|||
|
||||
|
||||
void D3D11Initializer::FlushCsChunkLocked() {
|
||||
m_parent->GetContext()->InjectCsChunk(std::move(m_csChunk), false);
|
||||
m_parent->GetContext()->InjectCsChunk(DxvkCsQueue::HighPriority, std::move(m_csChunk), false);
|
||||
m_csChunk = m_parent->AllocCsChunk(DxvkCsChunkFlag::SingleUse);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
#include "../dxvk/dxvk_staging.h"
|
||||
|
||||
#include "d3d11_buffer.h"
|
||||
#include "d3d11_shader.h"
|
||||
#include "d3d11_texture.h"
|
||||
#include "d3d11_view_uav.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
|
@ -57,6 +59,11 @@ namespace dxvk {
|
|||
void InitUavCounter(
|
||||
D3D11UnorderedAccessView* pUav);
|
||||
|
||||
void InitShaderIcb(
|
||||
D3D11CommonShader* pShader,
|
||||
size_t IcbSize,
|
||||
const void* pIcbData);
|
||||
|
||||
private:
|
||||
|
||||
dxvk::mutex m_mutex;
|
||||
|
|
|
@ -24,7 +24,9 @@ enum D3D11_VK_EXTENSION : uint32_t {
|
|||
*/
|
||||
enum D3D11_VK_BARRIER_CONTROL : uint32_t {
|
||||
D3D11_VK_BARRIER_CONTROL_IGNORE_WRITE_AFTER_WRITE = 1 << 0,
|
||||
D3D11_VK_BARRIER_CONTROL_IGNORE_GRAPHICS_UAV = 1 << 1,
|
||||
|
||||
// Removed:
|
||||
// D3D11_VK_BARRIER_CONTROL_IGNORE_GRAPHICS_UAV = 1 << 1,
|
||||
};
|
||||
|
||||
|
||||
|
@ -183,10 +185,70 @@ ID3D11VkExtContext1 : public ID3D11VkExtContext {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Frame reports used for Reflex interop
|
||||
*/
|
||||
struct D3D_LOW_LATENCY_FRAME_REPORT
|
||||
{
|
||||
UINT64 frameID;
|
||||
UINT64 inputSampleTime;
|
||||
UINT64 simStartTime;
|
||||
UINT64 simEndTime;
|
||||
UINT64 renderSubmitStartTime;
|
||||
UINT64 renderSubmitEndTime;
|
||||
UINT64 presentStartTime;
|
||||
UINT64 presentEndTime;
|
||||
UINT64 driverStartTime;
|
||||
UINT64 driverEndTime;
|
||||
UINT64 osRenderQueueStartTime;
|
||||
UINT64 osRenderQueueEndTime;
|
||||
UINT64 gpuRenderStartTime;
|
||||
UINT64 gpuRenderEndTime;
|
||||
UINT32 gpuActiveRenderTimeUs;
|
||||
UINT32 gpuFrameTimeUs;
|
||||
UINT8 rsvd[120];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Data structure used for Reflex interop
|
||||
*/
|
||||
struct D3D_LOW_LATENCY_RESULTS
|
||||
{
|
||||
UINT32 version;
|
||||
D3D_LOW_LATENCY_FRAME_REPORT frameReports[64];
|
||||
UINT8 rsvd[32];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief D3D interop interface for Nvidia Reflex
|
||||
*/
|
||||
MIDL_INTERFACE("f3112584-41f9-348d-a59b-00b7e1d285d6")
|
||||
ID3DLowLatencyDevice : public IUnknown {
|
||||
virtual BOOL STDMETHODCALLTYPE SupportsLowLatency() = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE LatencySleep() = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetLatencySleepMode(
|
||||
BOOL LowLatencyEnable,
|
||||
BOOL LowLatencyBoost,
|
||||
UINT32 MinIntervalUs) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetLatencyMarker(
|
||||
UINT64 FrameId,
|
||||
UINT32 MarkerType) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetLatencyInfo(
|
||||
D3D_LOW_LATENCY_RESULTS* pLowLatencyResults) = 0;
|
||||
};
|
||||
|
||||
|
||||
#ifndef _MSC_VER
|
||||
__CRT_UUID_DECL(ID3D11VkExtShader, 0xbb8a4fb9,0x3935,0x4762,0xb4,0x4b,0x35,0x18,0x9a,0x26,0x41,0x4a);
|
||||
__CRT_UUID_DECL(ID3D11VkExtDevice, 0x8a6e3c42,0xf74c,0x45b7,0x82,0x65,0xa2,0x31,0xb6,0x77,0xca,0x17);
|
||||
__CRT_UUID_DECL(ID3D11VkExtDevice1, 0xcfcf64ef,0x9586,0x46d0,0xbc,0xa4,0x97,0xcf,0x2c,0xa6,0x1b,0x06);
|
||||
__CRT_UUID_DECL(ID3D11VkExtContext, 0xfd0bca13,0x5cb6,0x4c3a,0x98,0x7e,0x47,0x50,0xde,0x2c,0xa7,0x91);
|
||||
__CRT_UUID_DECL(ID3D11VkExtContext1, 0x874b09b2,0xae0b,0x41d8,0x84,0x76,0x5f,0x3b,0x7a,0x0e,0x87,0x9d);
|
||||
__CRT_UUID_DECL(ID3DLowLatencyDevice, 0xf3112584,0x41f9,0x348d,0xa5,0x9b,0x00,0xb7,0xe1,0xd2,0x85,0xd6);
|
||||
#endif
|
||||
|
|
|
@ -147,4 +147,11 @@ namespace dxvk {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D11on12Device::GetD3D12Device(
|
||||
REFIID riid,
|
||||
void** ppvDevice) {
|
||||
return m_d3d12Queue->GetDevice(riid, ppvDevice);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,21 @@
|
|||
|
||||
#include "../util/log/log.h"
|
||||
|
||||
/**
|
||||
* \brief Declaration of the ID3D11On12Device1 interface
|
||||
*
|
||||
* Various different headers that we need to be compatible with
|
||||
* can't seem to agree on the signature of GetD3D12Device, and
|
||||
* older wine/mingw headers don't support this interface at all.
|
||||
*/
|
||||
MIDL_INTERFACE("bdb64df4-ea2f-4c70-b861-aaab1258bb5d")
|
||||
ID3D11On12Device1_DXVK : public ID3D11On12Device {
|
||||
virtual HRESULT STDMETHODCALLTYPE GetD3D12Device(
|
||||
REFIID riid,
|
||||
void** ppvDevice) = 0;
|
||||
};
|
||||
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
class D3D11Device;
|
||||
|
@ -22,7 +37,7 @@ namespace dxvk {
|
|||
};
|
||||
|
||||
|
||||
class D3D11on12Device : public ID3D11On12Device {
|
||||
class D3D11on12Device : public ID3D11On12Device1_DXVK {
|
||||
|
||||
public:
|
||||
|
||||
|
@ -58,6 +73,10 @@ namespace dxvk {
|
|||
ID3D11Resource* const* ppResources,
|
||||
UINT ResourceCount);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetD3D12Device(
|
||||
REFIID riid,
|
||||
void** ppvDevice);
|
||||
|
||||
bool Is11on12Device() const {
|
||||
return m_d3d12Device != nullptr;
|
||||
}
|
||||
|
@ -73,3 +92,7 @@ namespace dxvk {
|
|||
};
|
||||
|
||||
}
|
||||
|
||||
#ifndef _MSC_VER
|
||||
__CRT_UUID_DECL(ID3D11On12Device1_DXVK, 0xbdb64df4,0xea2f,0x4c70,0xb8,0x61,0xaa,0xab,0x12,0x58,0xbb,0x5d);
|
||||
#endif
|
||||
|
|
|
@ -13,11 +13,11 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
D3D11Options::D3D11Options(const Config& config) {
|
||||
this->dcSingleUseMode = config.getOption<bool>("d3d11.dcSingleUseMode", true);
|
||||
this->zeroInitWorkgroupMemory = config.getOption<bool>("d3d11.zeroInitWorkgroupMemory", false);
|
||||
this->forceVolatileTgsmAccess = config.getOption<bool>("d3d11.forceVolatileTgsmAccess", false);
|
||||
this->forceComputeUavBarriers = config.getOption<bool>("d3d11.forceComputeUavBarriers", false);
|
||||
this->relaxedBarriers = config.getOption<bool>("d3d11.relaxedBarriers", false);
|
||||
this->ignoreGraphicsBarriers = config.getOption<bool>("d3d11.ignoreGraphicsBarriers", false);
|
||||
this->relaxedGraphicsBarriers = config.getOption<bool>("d3d11.relaxedGraphicsBarriers", false);
|
||||
this->maxTessFactor = config.getOption<int32_t>("d3d11.maxTessFactor", 0);
|
||||
this->samplerAnisotropy = config.getOption<int32_t>("d3d11.samplerAnisotropy", -1);
|
||||
this->samplerLodBias = config.getOption<float>("d3d11.samplerLodBias", 0.0f);
|
||||
|
@ -28,7 +28,6 @@ namespace dxvk {
|
|||
this->disableMsaa = config.getOption<bool>("d3d11.disableMsaa", false);
|
||||
this->enableContextLock = config.getOption<bool>("d3d11.enableContextLock", false);
|
||||
this->deferSurfaceCreation = config.getOption<bool>("dxgi.deferSurfaceCreation", false);
|
||||
this->numBackBuffers = config.getOption<int32_t>("dxgi.numBackBuffers", 0);
|
||||
this->maxFrameLatency = config.getOption<int32_t>("dxgi.maxFrameLatency", 0);
|
||||
this->exposeDriverCommandLists = config.getOption<bool>("d3d11.exposeDriverCommandLists", true);
|
||||
this->reproducibleCommandStream = config.getOption<bool>("d3d11.reproducibleCommandStream", false);
|
||||
|
|
|
@ -13,13 +13,6 @@ namespace dxvk {
|
|||
struct D3D11Options {
|
||||
D3D11Options(const Config& config);
|
||||
|
||||
/// Enables speed hack for mapping on deferred contexts
|
||||
///
|
||||
/// This can substantially speed up some games, but may
|
||||
/// cause issues if the game submits command lists more
|
||||
/// than once.
|
||||
bool dcSingleUseMode = false;
|
||||
|
||||
/// Zero-initialize workgroup memory
|
||||
///
|
||||
/// Workargound for games that don't initialize
|
||||
|
@ -33,6 +26,13 @@ namespace dxvk {
|
|||
/// without explicit synchronization.
|
||||
bool forceVolatileTgsmAccess = false;
|
||||
|
||||
/// Force UAV synchronization insided compute shaders
|
||||
///
|
||||
/// Workaround for compute shaders that access overlapping
|
||||
/// memory regions within a UAV without proper workgroup
|
||||
/// synchroniation. Will have a negative performance impact.
|
||||
bool forceComputeUavBarriers = false;
|
||||
|
||||
/// Use relaxed memory barriers
|
||||
///
|
||||
/// May improve performance in some games,
|
||||
|
@ -43,7 +43,7 @@ namespace dxvk {
|
|||
///
|
||||
/// May improve performance in some games,
|
||||
/// but might also cause rendering issues.
|
||||
bool ignoreGraphicsBarriers = false;
|
||||
bool relaxedGraphicsBarriers = false;
|
||||
|
||||
/// Maximum tessellation factor.
|
||||
///
|
||||
|
@ -72,10 +72,6 @@ namespace dxvk {
|
|||
/// Enable float control bits
|
||||
bool floatControls = true;
|
||||
|
||||
/// Back buffer count for the Vulkan swap chain.
|
||||
/// Overrides DXGI_SWAP_CHAIN_DESC::BufferCount.
|
||||
int32_t numBackBuffers = 0;
|
||||
|
||||
/// Override maximum frame latency if the app specifies
|
||||
/// a higher value. May help with frame timing issues.
|
||||
int32_t maxFrameLatency = 0;
|
||||
|
|
|
@ -59,25 +59,33 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
// Create shader constant buffer if necessary
|
||||
const DxvkShaderCreateInfo& shaderInfo = m_shader->info();
|
||||
auto icb = module.icbInfo();
|
||||
|
||||
if (shaderInfo.uniformSize) {
|
||||
DxvkBufferCreateInfo info;
|
||||
info.size = shaderInfo.uniformSize;
|
||||
info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
||||
info.stages = util::pipelineStages(shaderInfo.stage);
|
||||
info.access = VK_ACCESS_UNIFORM_READ_BIT;
|
||||
if (icb.size) {
|
||||
DxvkBufferCreateInfo info = { };
|
||||
info.size = align(icb.size, 256u);
|
||||
info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
|
||||
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT
|
||||
| VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
||||
info.stages = util::pipelineStages(m_shader->info().stage);
|
||||
info.access = VK_ACCESS_UNIFORM_READ_BIT
|
||||
| VK_ACCESS_TRANSFER_READ_BIT
|
||||
| VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
info.debugName = "Icb";
|
||||
|
||||
VkMemoryPropertyFlags memFlags
|
||||
= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
|
||||
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
m_buffer = pDevice->GetDXVKDevice()->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
|
||||
m_buffer = pDevice->GetDXVKDevice()->createBuffer(info, memFlags);
|
||||
std::memcpy(m_buffer->mapPtr(0), shaderInfo.uniformData, shaderInfo.uniformSize);
|
||||
// Upload immediate constant buffer to VRAM
|
||||
pDevice->InitShaderIcb(this, icb.size, icb.data);
|
||||
}
|
||||
|
||||
pDevice->GetDXVKDevice()->registerShader(m_shader);
|
||||
|
||||
// Write back binding mask
|
||||
auto bindings = module.bindings();
|
||||
|
||||
if (bindings)
|
||||
m_bindings = *bindings;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -53,11 +53,17 @@ namespace dxvk {
|
|||
return m_shader->debugName();
|
||||
}
|
||||
|
||||
DxbcBindingMask GetBindingMask() const {
|
||||
return m_bindings;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Rc<DxvkShader> m_shader;
|
||||
Rc<DxvkBuffer> m_buffer;
|
||||
|
||||
DxbcBindingMask m_bindings = { };
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#include "d3d11_device.h"
|
||||
#include "d3d11_swapchain.h"
|
||||
|
||||
#include "../dxvk/dxvk_latency_builtin.h"
|
||||
|
||||
#include "../util/util_win32_compat.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
@ -68,10 +70,6 @@ namespace dxvk {
|
|||
CreatePresenter();
|
||||
CreateBackBuffers();
|
||||
CreateBlitter();
|
||||
CreateHud();
|
||||
|
||||
if (!pDevice->GetOptions()->deferSurfaceCreation)
|
||||
RecreateSwapChain();
|
||||
}
|
||||
|
||||
|
||||
|
@ -81,10 +79,10 @@ namespace dxvk {
|
|||
if (this_thread::isInModuleDetachment())
|
||||
return;
|
||||
|
||||
m_device->waitForSubmission(&m_presentStatus);
|
||||
m_device->waitForIdle();
|
||||
m_presenter->destroyResources();
|
||||
|
||||
DestroyFrameLatencyEvent();
|
||||
DestroyLatencyTracker();
|
||||
}
|
||||
|
||||
|
||||
|
@ -177,11 +175,11 @@ namespace dxvk {
|
|||
const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
||||
const UINT* pNodeMasks,
|
||||
IUnknown* const* ppPresentQueues) {
|
||||
m_dirty |= m_desc.Format != pDesc->Format
|
||||
|| m_desc.Width != pDesc->Width
|
||||
|| m_desc.Height != pDesc->Height
|
||||
|| m_desc.BufferCount != pDesc->BufferCount
|
||||
|| m_desc.Flags != pDesc->Flags;
|
||||
if (m_desc.Format != pDesc->Format)
|
||||
m_presenter->setSurfaceFormat(GetSurfaceFormat(pDesc->Format));
|
||||
|
||||
if (m_desc.Width != pDesc->Width || m_desc.Height != pDesc->Height)
|
||||
m_presenter->setSurfaceExtent({ m_desc.Width, m_desc.Height });
|
||||
|
||||
m_desc = *pDesc;
|
||||
CreateBackBuffers();
|
||||
|
@ -254,33 +252,24 @@ namespace dxvk {
|
|||
UINT SyncInterval,
|
||||
UINT PresentFlags,
|
||||
const DXGI_PRESENT_PARAMETERS* pPresentParameters) {
|
||||
if (!(PresentFlags & DXGI_PRESENT_TEST))
|
||||
m_dirty |= m_presenter->setSyncInterval(SyncInterval) != VK_SUCCESS;
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (!m_presenter->hasSwapChain()) {
|
||||
RecreateSwapChain();
|
||||
m_dirty = false;
|
||||
}
|
||||
|
||||
if (!m_presenter->hasSwapChain())
|
||||
hr = DXGI_STATUS_OCCLUDED;
|
||||
|
||||
if (m_device->getDeviceStatus() != VK_SUCCESS)
|
||||
hr = DXGI_ERROR_DEVICE_RESET;
|
||||
|
||||
if (PresentFlags & DXGI_PRESENT_TEST)
|
||||
if (PresentFlags & DXGI_PRESENT_TEST) {
|
||||
if (hr != S_OK)
|
||||
return hr;
|
||||
|
||||
VkResult status = m_presenter->checkSwapChainStatus();
|
||||
return status == VK_SUCCESS ? S_OK : DXGI_STATUS_OCCLUDED;
|
||||
}
|
||||
|
||||
if (hr != S_OK) {
|
||||
SyncFrameLatency();
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (std::exchange(m_dirty, false))
|
||||
RecreateSwapChain();
|
||||
|
||||
try {
|
||||
hr = PresentImage(SyncInterval);
|
||||
} catch (const DxvkError& e) {
|
||||
|
@ -293,6 +282,18 @@ namespace dxvk {
|
|||
// applications using the semaphore may deadlock. This works because
|
||||
// we do not increment the frame ID in those situations.
|
||||
SyncFrameLatency();
|
||||
|
||||
// Ignore latency stuff if presentation failed
|
||||
DxvkLatencyStats latencyStats = { };
|
||||
|
||||
if (hr == S_OK && m_latency) {
|
||||
latencyStats = m_latency->getStatistics(m_frameId);
|
||||
m_latency->sleepAndBeginFrame(m_frameId + 1, std::abs(m_targetFrameRate));
|
||||
}
|
||||
|
||||
if (m_latencyHud)
|
||||
m_latencyHud->accumulateStats(latencyStats);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
@ -301,7 +302,8 @@ namespace dxvk {
|
|||
DXGI_COLOR_SPACE_TYPE ColorSpace) {
|
||||
UINT supportFlags = 0;
|
||||
|
||||
const VkColorSpaceKHR vkColorSpace = ConvertColorSpace(ColorSpace);
|
||||
VkColorSpaceKHR vkColorSpace = ConvertColorSpace(ColorSpace);
|
||||
|
||||
if (m_presenter->supportsColorSpace(vkColorSpace))
|
||||
supportFlags |= DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT;
|
||||
|
||||
|
@ -311,13 +313,14 @@ namespace dxvk {
|
|||
|
||||
HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetColorSpace(
|
||||
DXGI_COLOR_SPACE_TYPE ColorSpace) {
|
||||
if (!(CheckColorSpaceSupport(ColorSpace) & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT))
|
||||
VkColorSpaceKHR colorSpace = ConvertColorSpace(ColorSpace);
|
||||
|
||||
if (!m_presenter->supportsColorSpace(colorSpace))
|
||||
return E_INVALIDARG;
|
||||
|
||||
const VkColorSpaceKHR vkColorSpace = ConvertColorSpace(ColorSpace);
|
||||
m_dirty |= vkColorSpace != m_colorspace;
|
||||
m_colorspace = vkColorSpace;
|
||||
m_colorSpace = colorSpace;
|
||||
|
||||
m_presenter->setSurfaceFormat(GetSurfaceFormat(m_desc.Format));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
@ -325,10 +328,8 @@ namespace dxvk {
|
|||
HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetHDRMetaData(
|
||||
const DXGI_VK_HDR_METADATA* pMetaData) {
|
||||
// For some reason this call always seems to succeed on Windows
|
||||
if (pMetaData->Type == DXGI_HDR_METADATA_TYPE_HDR10) {
|
||||
m_hdrMetadata = ConvertHDRMetadata(pMetaData->HDR10);
|
||||
m_dirtyHdrMetadata = true;
|
||||
}
|
||||
if (pMetaData->Type == DXGI_HDR_METADATA_TYPE_HDR10)
|
||||
m_presenter->setHdrMetadata(ConvertHDRMetadata(pMetaData->HDR10));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -378,86 +379,95 @@ namespace dxvk {
|
|||
auto immediateContext = m_parent->GetContext();
|
||||
auto immediateContextLock = immediateContext->LockContext();
|
||||
|
||||
immediateContext->EndFrame();
|
||||
immediateContext->Flush();
|
||||
immediateContext->EndFrame(m_latency);
|
||||
immediateContext->ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, true);
|
||||
|
||||
SynchronizePresent();
|
||||
|
||||
if (!m_presenter->hasSwapChain())
|
||||
return DXGI_STATUS_OCCLUDED;
|
||||
m_presenter->setSyncInterval(SyncInterval);
|
||||
|
||||
// Presentation semaphores and WSI swap chain image
|
||||
PresenterInfo info = m_presenter->info();
|
||||
if (m_latency)
|
||||
m_latency->notifyCpuPresentBegin(m_frameId + 1u);
|
||||
|
||||
PresenterSync sync;
|
||||
Rc<DxvkImage> backBuffer;
|
||||
|
||||
uint32_t imageIndex = 0;
|
||||
VkResult status = m_presenter->acquireNextImage(sync, backBuffer);
|
||||
|
||||
VkResult status = m_presenter->acquireNextImage(sync, imageIndex);
|
||||
if (status != VK_SUCCESS && m_latency)
|
||||
m_latency->discardTimings();
|
||||
|
||||
while (status != VK_SUCCESS) {
|
||||
RecreateSwapChain();
|
||||
if (status < 0)
|
||||
return E_FAIL;
|
||||
|
||||
if (!m_presenter->hasSwapChain())
|
||||
if (status == VK_NOT_READY)
|
||||
return DXGI_STATUS_OCCLUDED;
|
||||
|
||||
info = m_presenter->info();
|
||||
status = m_presenter->acquireNextImage(sync, imageIndex);
|
||||
|
||||
if (status == VK_SUBOPTIMAL_KHR)
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_hdrMetadata && m_dirtyHdrMetadata) {
|
||||
m_presenter->setHdrMetadata(*m_hdrMetadata);
|
||||
m_dirtyHdrMetadata = false;
|
||||
}
|
||||
|
||||
m_frameId += 1;
|
||||
|
||||
// Present from CS thread so that we don't
|
||||
// have to synchronize with it first.
|
||||
m_presentStatus.result = VK_NOT_READY;
|
||||
DxvkImageViewKey viewInfo = { };
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
viewInfo.format = backBuffer->info().format;
|
||||
viewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
viewInfo.mipIndex = 0u;
|
||||
viewInfo.mipCount = 1u;
|
||||
viewInfo.layerIndex = 0u;
|
||||
viewInfo.layerCount = 1u;
|
||||
|
||||
immediateContext->EmitCs([
|
||||
cPresentStatus = &m_presentStatus,
|
||||
cDevice = m_device,
|
||||
cBlitter = m_blitter,
|
||||
cBackBuffer = m_imageViews.at(imageIndex),
|
||||
cBackBuffer = backBuffer->createView(viewInfo),
|
||||
cSwapImage = GetBackBufferView(),
|
||||
cSync = sync,
|
||||
cHud = m_hud,
|
||||
cPresenter = m_presenter,
|
||||
cColorSpace = m_colorspace,
|
||||
cLatency = m_latency,
|
||||
cColorSpace = m_colorSpace,
|
||||
cFrameId = m_frameId
|
||||
] (DxvkContext* ctx) {
|
||||
// Update back buffer color space as necessary
|
||||
if (cSwapImage->image()->info().colorSpace != cColorSpace) {
|
||||
DxvkImageUsageInfo usage = { };
|
||||
usage.colorSpace = cColorSpace;
|
||||
|
||||
ctx->ensureImageCompatibility(cSwapImage->image(), usage);
|
||||
}
|
||||
|
||||
// Blit the D3D back buffer onto the actual Vulkan
|
||||
// swap chain and render the HUD if we have one.
|
||||
auto contextObjects = ctx->beginExternalRendering();
|
||||
|
||||
cBlitter->beginPresent(contextObjects,
|
||||
cBackBuffer, cColorSpace, VkRect2D(),
|
||||
cSwapImage, cColorSpace, VkRect2D());
|
||||
|
||||
if (cHud != nullptr) {
|
||||
cHud->update();
|
||||
cHud->render(contextObjects, cBackBuffer, cColorSpace);
|
||||
}
|
||||
|
||||
cBlitter->endPresent(contextObjects, cBackBuffer, cColorSpace);
|
||||
cBlitter->present(contextObjects,
|
||||
cBackBuffer, VkRect2D(),
|
||||
cSwapImage, VkRect2D());
|
||||
|
||||
// Submit current command list and present
|
||||
ctx->synchronizeWsi(cSync);
|
||||
ctx->flushCommandList(nullptr);
|
||||
ctx->flushCommandList(nullptr, nullptr);
|
||||
|
||||
cDevice->presentImage(cPresenter,
|
||||
cPresenter->info().presentMode,
|
||||
cFrameId, cPresentStatus);
|
||||
cDevice->presentImage(cPresenter, cLatency, cFrameId, nullptr);
|
||||
});
|
||||
|
||||
if (m_backBuffers.size() > 1u)
|
||||
RotateBackBuffers(immediateContext);
|
||||
|
||||
immediateContext->FlushCsChunk();
|
||||
|
||||
if (m_latency) {
|
||||
m_latency->notifyCpuPresentEnd(m_frameId);
|
||||
|
||||
if (m_latency->needsAutoMarkers()) {
|
||||
immediateContext->EmitCs([
|
||||
cLatency = m_latency,
|
||||
cFrameId = m_frameId
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->beginLatencyTracking(cLatency, cFrameId + 1u);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
@ -481,48 +491,6 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void D3D11SwapChain::SynchronizePresent() {
|
||||
// Recreate swap chain if the previous present call failed
|
||||
VkResult status = m_device->waitForSubmission(&m_presentStatus);
|
||||
|
||||
if (status != VK_SUCCESS)
|
||||
RecreateSwapChain();
|
||||
}
|
||||
|
||||
|
||||
void D3D11SwapChain::RecreateSwapChain() {
|
||||
// Ensure that we can safely destroy the swap chain
|
||||
m_device->waitForSubmission(&m_presentStatus);
|
||||
m_device->waitForIdle();
|
||||
|
||||
m_presentStatus.result = VK_SUCCESS;
|
||||
m_dirtyHdrMetadata = true;
|
||||
|
||||
PresenterDesc presenterDesc;
|
||||
presenterDesc.imageExtent = { m_desc.Width, m_desc.Height };
|
||||
presenterDesc.imageCount = PickImageCount(m_desc.BufferCount + 1);
|
||||
presenterDesc.numFormats = PickFormats(m_desc.Format, presenterDesc.formats);
|
||||
|
||||
VkResult vr = m_presenter->recreateSwapChain(presenterDesc);
|
||||
|
||||
if (vr == VK_ERROR_SURFACE_LOST_KHR) {
|
||||
vr = m_presenter->recreateSurface([this] (VkSurfaceKHR* surface) {
|
||||
return CreateSurface(surface);
|
||||
});
|
||||
|
||||
if (vr)
|
||||
throw DxvkError(str::format("D3D11SwapChain: Failed to recreate surface: ", vr));
|
||||
|
||||
vr = m_presenter->recreateSwapChain(presenterDesc);
|
||||
}
|
||||
|
||||
if (vr)
|
||||
throw DxvkError(str::format("D3D11SwapChain: Failed to recreate swap chain: ", vr));
|
||||
|
||||
CreateRenderTargetViews();
|
||||
}
|
||||
|
||||
|
||||
void D3D11SwapChain::CreateFrameLatencyEvent() {
|
||||
m_frameLatencySignal = new sync::CallbackFence(m_frameId);
|
||||
|
||||
|
@ -532,63 +500,26 @@ namespace dxvk {
|
|||
|
||||
|
||||
void D3D11SwapChain::CreatePresenter() {
|
||||
PresenterDesc presenterDesc;
|
||||
presenterDesc.imageExtent = { m_desc.Width, m_desc.Height };
|
||||
presenterDesc.imageCount = PickImageCount(m_desc.BufferCount + 1);
|
||||
presenterDesc.numFormats = PickFormats(m_desc.Format, presenterDesc.formats);
|
||||
PresenterDesc presenterDesc = { };
|
||||
presenterDesc.deferSurfaceCreation = m_parent->GetOptions()->deferSurfaceCreation;
|
||||
|
||||
m_presenter = new Presenter(m_device, m_frameLatencySignal, presenterDesc);
|
||||
m_presenter = new Presenter(m_device, m_frameLatencySignal, presenterDesc, [
|
||||
cAdapter = m_device->adapter(),
|
||||
cFactory = m_surfaceFactory
|
||||
] (VkSurfaceKHR* surface) {
|
||||
return cFactory->CreateSurface(
|
||||
cAdapter->vki()->instance(),
|
||||
cAdapter->handle(), surface);
|
||||
});
|
||||
|
||||
m_presenter->setSurfaceFormat(GetSurfaceFormat(m_desc.Format));
|
||||
m_presenter->setSurfaceExtent({ m_desc.Width, m_desc.Height });
|
||||
m_presenter->setFrameRateLimit(m_targetFrameRate, GetActualFrameLatency());
|
||||
}
|
||||
|
||||
m_latency = m_device->createLatencyTracker(m_presenter);
|
||||
|
||||
VkResult D3D11SwapChain::CreateSurface(VkSurfaceKHR* pSurface) {
|
||||
Rc<DxvkAdapter> adapter = m_device->adapter();
|
||||
|
||||
return m_surfaceFactory->CreateSurface(
|
||||
adapter->vki()->instance(),
|
||||
adapter->handle(), pSurface);
|
||||
}
|
||||
|
||||
|
||||
void D3D11SwapChain::CreateRenderTargetViews() {
|
||||
PresenterInfo info = m_presenter->info();
|
||||
|
||||
m_imageViews.clear();
|
||||
m_imageViews.resize(info.imageCount);
|
||||
|
||||
DxvkImageCreateInfo imageInfo;
|
||||
imageInfo.type = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.format = info.format.format;
|
||||
imageInfo.flags = 0;
|
||||
imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageInfo.extent = { info.imageExtent.width, info.imageExtent.height, 1 };
|
||||
imageInfo.numLayers = 1;
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
imageInfo.stages = 0;
|
||||
imageInfo.access = 0;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
imageInfo.shared = VK_TRUE;
|
||||
|
||||
DxvkImageViewKey viewInfo;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = info.format.format;
|
||||
viewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
viewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
viewInfo.mipIndex = 0;
|
||||
viewInfo.mipCount = 1;
|
||||
viewInfo.layerIndex = 0;
|
||||
viewInfo.layerCount = 1;
|
||||
|
||||
for (uint32_t i = 0; i < info.imageCount; i++) {
|
||||
VkImage imageHandle = m_presenter->getImage(i).image;
|
||||
|
||||
Rc<DxvkImage> image = m_device->importImage(imageInfo,
|
||||
imageHandle, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
m_imageViews[i] = image->createView(viewInfo);
|
||||
}
|
||||
Com<D3D11ReflexDevice> reflex = GetReflexDevice();
|
||||
reflex->RegisterLatencyTracker(m_latency);
|
||||
}
|
||||
|
||||
|
||||
|
@ -648,10 +579,12 @@ namespace dxvk {
|
|||
|
||||
// Initialize images so that we can use them. Clearing
|
||||
// to black prevents garbled output for the first frame.
|
||||
m_parent->GetContext()->InjectCs([
|
||||
m_parent->GetContext()->InjectCs(DxvkCsQueue::HighPriority, [
|
||||
cImages = std::move(images)
|
||||
] (DxvkContext* ctx) {
|
||||
for (size_t i = 0; i < cImages.size(); i++) {
|
||||
ctx->setDebugName(cImages[i], str::format("Back buffer ", i).c_str());
|
||||
|
||||
ctx->initImage(cImages[i],
|
||||
cImages[i]->getAvailableSubresources(),
|
||||
VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
|
@ -661,15 +594,16 @@ namespace dxvk {
|
|||
|
||||
|
||||
void D3D11SwapChain::CreateBlitter() {
|
||||
m_blitter = new DxvkSwapchainBlitter(m_device);
|
||||
Rc<hud::Hud> hud = hud::Hud::createHud(m_device);
|
||||
|
||||
if (hud) {
|
||||
hud->addItem<hud::HudClientApiItem>("api", 1, GetApiName());
|
||||
|
||||
if (m_latency)
|
||||
m_latencyHud = hud->addItem<hud::HudLatencyItem>("latency", 4);
|
||||
}
|
||||
|
||||
|
||||
void D3D11SwapChain::CreateHud() {
|
||||
m_hud = hud::Hud::createHud(m_device);
|
||||
|
||||
if (m_hud != nullptr)
|
||||
m_hud->addItem<hud::HudClientApiItem>("api", 1, GetApiName());
|
||||
m_blitter = new DxvkSwapchainBlitter(m_device, std::move(hud));
|
||||
}
|
||||
|
||||
|
||||
|
@ -678,6 +612,20 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void D3D11SwapChain::DestroyLatencyTracker() {
|
||||
// Need to make sure the context stops using
|
||||
// the tracker for submissions
|
||||
m_parent->GetContext()->InjectCs(DxvkCsQueue::Ordered, [
|
||||
cLatency = m_latency
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->endLatencyTracking(cLatency);
|
||||
});
|
||||
|
||||
Com<D3D11ReflexDevice> reflex = GetReflexDevice();
|
||||
reflex->UnregisterLatencyTracker(m_latency);
|
||||
}
|
||||
|
||||
|
||||
void D3D11SwapChain::SyncFrameLatency() {
|
||||
// Wait for the sync event so that we respect the maximum frame latency
|
||||
m_frameLatencySignal->wait(m_frameId - GetActualFrameLatency());
|
||||
|
@ -713,46 +661,34 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
uint32_t D3D11SwapChain::PickFormats(
|
||||
DXGI_FORMAT Format,
|
||||
VkSurfaceFormatKHR* pDstFormats) {
|
||||
uint32_t n = 0;
|
||||
|
||||
VkSurfaceFormatKHR D3D11SwapChain::GetSurfaceFormat(DXGI_FORMAT Format) {
|
||||
switch (Format) {
|
||||
default:
|
||||
Logger::warn(str::format("D3D11SwapChain: Unexpected format: ", m_desc.Format));
|
||||
[[fallthrough]];
|
||||
|
||||
case DXGI_FORMAT_R8G8B8A8_UNORM:
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM: {
|
||||
pDstFormats[n++] = { VK_FORMAT_R8G8B8A8_UNORM, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_B8G8R8A8_UNORM, m_colorspace };
|
||||
} break;
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM:
|
||||
return { VK_FORMAT_R8G8B8A8_UNORM, m_colorSpace };
|
||||
|
||||
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: {
|
||||
pDstFormats[n++] = { VK_FORMAT_R8G8B8A8_SRGB, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_B8G8R8A8_SRGB, m_colorspace };
|
||||
} break;
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
|
||||
return { VK_FORMAT_R8G8B8A8_SRGB, m_colorSpace };
|
||||
|
||||
case DXGI_FORMAT_R10G10B10A2_UNORM: {
|
||||
pDstFormats[n++] = { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_A2R10G10B10_UNORM_PACK32, m_colorspace };
|
||||
} break;
|
||||
case DXGI_FORMAT_R10G10B10A2_UNORM:
|
||||
return { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorSpace };
|
||||
|
||||
case DXGI_FORMAT_R16G16B16A16_FLOAT: {
|
||||
pDstFormats[n++] = { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorspace };
|
||||
} break;
|
||||
case DXGI_FORMAT_R16G16B16A16_FLOAT:
|
||||
return { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorSpace };
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
uint32_t D3D11SwapChain::PickImageCount(
|
||||
UINT Preferred) {
|
||||
int32_t option = m_parent->GetOptions()->numBackBuffers;
|
||||
return option > 0 ? uint32_t(option) : uint32_t(Preferred);
|
||||
Com<D3D11ReflexDevice> D3D11SwapChain::GetReflexDevice() {
|
||||
Com<ID3DLowLatencyDevice> llDevice;
|
||||
m_parent->QueryInterface(__uuidof(ID3DLowLatencyDevice), reinterpret_cast<void**>(&llDevice));
|
||||
|
||||
return static_cast<D3D11ReflexDevice*>(llDevice.ptr());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "../dxvk/hud/dxvk_hud.h"
|
||||
|
||||
#include "../dxvk/dxvk_latency.h"
|
||||
#include "../dxvk/dxvk_swapchain_blitter.h"
|
||||
|
||||
#include "../util/sync/sync_signal.h"
|
||||
|
@ -107,13 +108,9 @@ namespace dxvk {
|
|||
Rc<Presenter> m_presenter;
|
||||
|
||||
Rc<DxvkSwapchainBlitter> m_blitter;
|
||||
|
||||
Rc<hud::Hud> m_hud;
|
||||
Rc<DxvkLatencyTracker> m_latency;
|
||||
|
||||
small_vector<Com<D3D11Texture2D, false>, 4> m_backBuffers;
|
||||
DxvkSubmitStatus m_presentStatus;
|
||||
|
||||
std::vector<Rc<DxvkImageView>> m_imageViews;
|
||||
|
||||
uint64_t m_frameId = DXGI_MAX_SWAP_CHAIN_BUFFERS;
|
||||
uint32_t m_frameLatency = DefaultFrameLatency;
|
||||
|
@ -121,54 +118,40 @@ namespace dxvk {
|
|||
HANDLE m_frameLatencyEvent = nullptr;
|
||||
Rc<sync::CallbackFence> m_frameLatencySignal;
|
||||
|
||||
bool m_dirty = true;
|
||||
|
||||
VkColorSpaceKHR m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
|
||||
std::optional<VkHdrMetadataEXT> m_hdrMetadata;
|
||||
bool m_dirtyHdrMetadata = true;
|
||||
VkColorSpaceKHR m_colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
|
||||
double m_targetFrameRate = 0.0;
|
||||
|
||||
dxvk::mutex m_frameStatisticsLock;
|
||||
DXGI_VK_FRAME_STATISTICS m_frameStatistics = { };
|
||||
|
||||
Rc<hud::HudLatencyItem> m_latencyHud;
|
||||
|
||||
Rc<DxvkImageView> GetBackBufferView();
|
||||
|
||||
HRESULT PresentImage(UINT SyncInterval);
|
||||
|
||||
void RotateBackBuffers(D3D11ImmediateContext* ctx);
|
||||
|
||||
void SynchronizePresent();
|
||||
|
||||
void RecreateSwapChain();
|
||||
|
||||
void CreateFrameLatencyEvent();
|
||||
|
||||
void CreatePresenter();
|
||||
|
||||
VkResult CreateSurface(VkSurfaceKHR* pSurface);
|
||||
|
||||
void CreateRenderTargetViews();
|
||||
|
||||
void CreateBackBuffers();
|
||||
|
||||
void CreateBlitter();
|
||||
|
||||
void CreateHud();
|
||||
|
||||
void DestroyFrameLatencyEvent();
|
||||
|
||||
void DestroyLatencyTracker();
|
||||
|
||||
void SyncFrameLatency();
|
||||
|
||||
uint32_t GetActualFrameLatency();
|
||||
|
||||
uint32_t PickFormats(
|
||||
DXGI_FORMAT Format,
|
||||
VkSurfaceFormatKHR* pDstFormats);
|
||||
VkSurfaceFormatKHR GetSurfaceFormat(DXGI_FORMAT Format);
|
||||
|
||||
uint32_t PickImageCount(
|
||||
UINT Preferred);
|
||||
Com<D3D11ReflexDevice> GetReflexDevice();
|
||||
|
||||
std::string GetApiName() const;
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "d3d11_device.h"
|
||||
#include "d3d11_context_imm.h"
|
||||
#include "d3d11_gdi.h"
|
||||
#include "d3d11_texture.h"
|
||||
|
||||
|
@ -374,6 +375,29 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void D3D11CommonTexture::SetDebugName(const char* pName) {
|
||||
if (m_image) {
|
||||
m_device->GetContext()->InjectCs(DxvkCsQueue::HighPriority, [
|
||||
cImage = m_image,
|
||||
cName = std::string(pName ? pName : "")
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->setDebugName(cImage, cName.c_str());
|
||||
});
|
||||
}
|
||||
|
||||
if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING) {
|
||||
for (uint32_t i = 0; i < m_buffers.size(); i++) {
|
||||
m_device->GetContext()->InjectCs(DxvkCsQueue::HighPriority, [
|
||||
cBuffer = m_buffers[i].buffer,
|
||||
cName = std::string(pName ? pName : "")
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->setDebugName(cBuffer, cName.c_str());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HRESULT D3D11CommonTexture::NormalizeTextureProperties(D3D11_COMMON_TEXTURE_DESC* pDesc) {
|
||||
if (pDesc->Width == 0 || pDesc->Height == 0 || pDesc->Depth == 0 || pDesc->ArraySize == 0)
|
||||
return E_INVALIDARG;
|
||||
|
@ -754,6 +778,7 @@ namespace dxvk {
|
|||
| VK_ACCESS_TRANSFER_WRITE_BIT
|
||||
| VK_ACCESS_SHADER_READ_BIT
|
||||
| VK_ACCESS_SHADER_WRITE_BIT;
|
||||
info.debugName = "Image buffer";
|
||||
|
||||
// We may read mapped buffers even if it is
|
||||
// marked as CPU write-only on the D3D11 side.
|
||||
|
@ -1195,6 +1220,11 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void STDMETHODCALLTYPE D3D11Texture1D::SetDebugName(const char* pName) {
|
||||
m_texture.SetDebugName(pName);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////
|
||||
// D 3 D 1 1 T E X T U R E 2 D
|
||||
D3D11Texture2D::D3D11Texture2D(
|
||||
|
@ -1376,6 +1406,11 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void STDMETHODCALLTYPE D3D11Texture2D::SetDebugName(const char* pName) {
|
||||
m_texture.SetDebugName(pName);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////
|
||||
// D 3 D 1 1 T E X T U R E 3 D
|
||||
D3D11Texture3D::D3D11Texture3D(
|
||||
|
@ -1487,6 +1522,11 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void STDMETHODCALLTYPE D3D11Texture3D::SetDebugName(const char* pName) {
|
||||
m_texture.SetDebugName(pName);
|
||||
}
|
||||
|
||||
|
||||
D3D11CommonTexture* GetCommonTexture(ID3D11Resource* pResource) {
|
||||
D3D11_RESOURCE_DIMENSION dimension = D3D11_RESOURCE_DIMENSION_UNKNOWN;
|
||||
pResource->GetType(&dimension);
|
||||
|
|
|
@ -525,6 +525,14 @@ namespace dxvk {
|
|||
return m_11on12;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets debug name for texture
|
||||
*
|
||||
* Passes the given name to the backing image or buffer.
|
||||
* \param [in] name Debug name
|
||||
*/
|
||||
void SetDebugName(const char* pName);
|
||||
|
||||
/**
|
||||
* \brief Normalizes and validates texture description
|
||||
*
|
||||
|
@ -759,6 +767,8 @@ namespace dxvk {
|
|||
void STDMETHODCALLTYPE GetDesc(
|
||||
D3D11_TEXTURE1D_DESC *pDesc) final;
|
||||
|
||||
void STDMETHODCALLTYPE SetDebugName(const char* pName) final;
|
||||
|
||||
D3D11CommonTexture* GetCommonTexture() {
|
||||
return &m_texture;
|
||||
}
|
||||
|
@ -825,6 +835,8 @@ namespace dxvk {
|
|||
void STDMETHODCALLTYPE GetDesc1(
|
||||
D3D11_TEXTURE2D_DESC1* pDesc) final;
|
||||
|
||||
void STDMETHODCALLTYPE SetDebugName(const char* pName) final;
|
||||
|
||||
D3D11CommonTexture* GetCommonTexture() {
|
||||
return &m_texture;
|
||||
}
|
||||
|
@ -875,6 +887,8 @@ namespace dxvk {
|
|||
void STDMETHODCALLTYPE GetDesc1(
|
||||
D3D11_TEXTURE3D_DESC1* pDesc) final;
|
||||
|
||||
void STDMETHODCALLTYPE SetDebugName(const char* pName) final;
|
||||
|
||||
D3D11CommonTexture* GetCommonTexture() {
|
||||
return &m_texture;
|
||||
}
|
||||
|
|
|
@ -52,15 +52,15 @@ namespace dxvk {
|
|||
* \returns Corresponding Vulkan shader stage
|
||||
*/
|
||||
constexpr VkShaderStageFlagBits GetShaderStage(DxbcProgramType ProgramType) {
|
||||
switch (ProgramType) {
|
||||
case DxbcProgramType::VertexShader: return VK_SHADER_STAGE_VERTEX_BIT;
|
||||
case DxbcProgramType::HullShader: return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
|
||||
case DxbcProgramType::DomainShader: return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
|
||||
case DxbcProgramType::GeometryShader: return VK_SHADER_STAGE_GEOMETRY_BIT;
|
||||
case DxbcProgramType::PixelShader: return VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
case DxbcProgramType::ComputeShader: return VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
default: return VkShaderStageFlagBits(0);
|
||||
}
|
||||
constexpr uint64_t lut
|
||||
= (uint64_t(VK_SHADER_STAGE_VERTEX_BIT) << (8u * uint32_t(DxbcProgramType::VertexShader)))
|
||||
| (uint64_t(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) << (8u * uint32_t(DxbcProgramType::HullShader)))
|
||||
| (uint64_t(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) << (8u * uint32_t(DxbcProgramType::DomainShader)))
|
||||
| (uint64_t(VK_SHADER_STAGE_GEOMETRY_BIT) << (8u * uint32_t(DxbcProgramType::GeometryShader)))
|
||||
| (uint64_t(VK_SHADER_STAGE_FRAGMENT_BIT) << (8u * uint32_t(DxbcProgramType::PixelShader)))
|
||||
| (uint64_t(VK_SHADER_STAGE_COMPUTE_BIT) << (8u * uint32_t(DxbcProgramType::ComputeShader)));
|
||||
|
||||
return VkShaderStageFlagBits((lut >> (8u * uint32_t(ProgramType))) & 0xff);
|
||||
}
|
||||
|
||||
}
|
|
@ -173,18 +173,6 @@ namespace dxvk {
|
|||
|
||||
Rc<DxvkImage> dxvkImage = GetCommonTexture(pResource)->GetImage();
|
||||
|
||||
if (!(dxvkImage->info().usage & VK_IMAGE_USAGE_SAMPLED_BIT)) {
|
||||
DxvkImageCreateInfo info = dxvkImage->info();
|
||||
info.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
|
||||
info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
info.access = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;
|
||||
info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
info.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
info.shared = VK_FALSE;
|
||||
dxvkImage = m_copy = pDevice->GetDXVKDevice()->createImage(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
}
|
||||
|
||||
DXGI_VK_FORMAT_INFO formatInfo = pDevice->LookupFormat(resourceDesc.Format, DXGI_VK_FORMAT_MODE_COLOR);
|
||||
DXGI_VK_FORMAT_FAMILY formatFamily = pDevice->LookupFamily(resourceDesc.Format, DXGI_VK_FORMAT_MODE_COLOR);
|
||||
|
||||
|
@ -200,7 +188,7 @@ namespace dxvk {
|
|||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.mipIndex = m_desc.Texture2D.MipSlice;
|
||||
viewInfo.mipCount = 1;
|
||||
viewInfo.layerIndex = 0;
|
||||
viewInfo.layerIndex = m_desc.Texture2D.ArraySlice;
|
||||
viewInfo.layerCount = 1;
|
||||
break;
|
||||
|
||||
|
@ -1025,6 +1013,10 @@ namespace dxvk {
|
|||
const D3D11_VIDEO_PROCESSOR_STREAM* pStreams) {
|
||||
D3D10DeviceLock lock = m_ctx->LockContext();
|
||||
|
||||
m_ctx->EmitCs([] (DxvkContext* ctx) {
|
||||
ctx->beginDebugLabel(vk::makeLabel(0x59eaff, "Video blit"));
|
||||
});
|
||||
|
||||
auto videoProcessor = static_cast<D3D11VideoProcessor*>(pVideoProcessor);
|
||||
bool hasStreamsEnabled = false;
|
||||
|
||||
|
@ -1037,7 +1029,9 @@ namespace dxvk {
|
|||
continue;
|
||||
|
||||
if (!hasStreamsEnabled) {
|
||||
m_ctx->ResetDirtyTracking();
|
||||
m_ctx->ResetCommandListState();
|
||||
|
||||
BindOutputView(pOutputView);
|
||||
hasStreamsEnabled = true;
|
||||
}
|
||||
|
@ -1047,9 +1041,14 @@ namespace dxvk {
|
|||
|
||||
if (hasStreamsEnabled) {
|
||||
UnbindResources();
|
||||
|
||||
m_ctx->RestoreCommandListState();
|
||||
}
|
||||
|
||||
m_ctx->EmitCs([] (DxvkContext* ctx) {
|
||||
ctx->endDebugLabel();
|
||||
});
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
@ -1184,6 +1183,13 @@ namespace dxvk {
|
|||
auto dxvkView = static_cast<D3D11VideoProcessorOutputView*>(pOutputView)->GetView();
|
||||
|
||||
m_ctx->EmitCs([this, cView = dxvkView] (DxvkContext* ctx) {
|
||||
DxvkImageUsageInfo usage = { };
|
||||
usage.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
usage.stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
usage.access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
ctx->ensureImageCompatibility(cView->image(), usage);
|
||||
|
||||
DxvkRenderTargets rt;
|
||||
rt.color[0].view = cView;
|
||||
rt.color[0].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
@ -1218,30 +1224,19 @@ namespace dxvk {
|
|||
|
||||
auto view = static_cast<D3D11VideoProcessorInputView*>(pStream->pInputSurface);
|
||||
|
||||
if (view->NeedsCopy()) {
|
||||
m_ctx->EmitCs([
|
||||
cDstImage = view->GetShadowCopy(),
|
||||
cSrcImage = view->GetImage(),
|
||||
cSrcLayers = view->GetImageSubresources()
|
||||
] (DxvkContext* ctx) {
|
||||
VkImageSubresourceLayers cDstLayers;
|
||||
cDstLayers.aspectMask = cSrcLayers.aspectMask;
|
||||
cDstLayers.baseArrayLayer = 0;
|
||||
cDstLayers.layerCount = cSrcLayers.layerCount;
|
||||
cDstLayers.mipLevel = cSrcLayers.mipLevel;
|
||||
|
||||
ctx->copyImage(
|
||||
cDstImage, cDstLayers, VkOffset3D(),
|
||||
cSrcImage, cSrcLayers, VkOffset3D(),
|
||||
cDstImage->info().extent);
|
||||
});
|
||||
}
|
||||
|
||||
m_ctx->EmitCs([this,
|
||||
cStreamState = *pStreamState,
|
||||
cImage = view->GetImage(),
|
||||
cViews = view->GetViews(),
|
||||
cIsYCbCr = view->IsYCbCr()
|
||||
] (DxvkContext* ctx) {
|
||||
DxvkImageUsageInfo usage = { };
|
||||
usage.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
usage.stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
usage.access = VK_ACCESS_SHADER_READ_BIT;
|
||||
|
||||
ctx->ensureImageCompatibility(cImage, usage);
|
||||
|
||||
VkViewport viewport;
|
||||
viewport.x = 0.0f;
|
||||
viewport.y = 0.0f;
|
||||
|
@ -1309,7 +1304,11 @@ namespace dxvk {
|
|||
for (uint32_t i = 0; i < cViews.size(); i++)
|
||||
ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 1 + i, Rc<DxvkImageView>(cViews[i]));
|
||||
|
||||
ctx->draw(3, 1, 0, 0);
|
||||
VkDrawIndirectCommand draw = { };
|
||||
draw.vertexCount = 3u;
|
||||
draw.instanceCount = 1u;
|
||||
|
||||
ctx->draw(1, &draw);
|
||||
|
||||
for (uint32_t i = 0; i < cViews.size(); i++)
|
||||
ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 1 + i, nullptr);
|
||||
|
@ -1323,6 +1322,8 @@ namespace dxvk {
|
|||
bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
||||
bufferInfo.stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
bufferInfo.access = VK_ACCESS_UNIFORM_READ_BIT;
|
||||
bufferInfo.debugName = "Video blit parameters";
|
||||
|
||||
m_ubo = m_device->createBuffer(bufferInfo, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
}
|
||||
|
||||
|
|
|
@ -138,10 +138,6 @@ namespace dxvk {
|
|||
return m_isYCbCr;
|
||||
}
|
||||
|
||||
bool NeedsCopy() const {
|
||||
return m_copy != nullptr;
|
||||
}
|
||||
|
||||
Rc<DxvkImage> GetImage() const {
|
||||
return GetCommonTexture(m_resource.ptr())->GetImage();
|
||||
}
|
||||
|
@ -150,10 +146,6 @@ namespace dxvk {
|
|||
return m_subresources;
|
||||
}
|
||||
|
||||
Rc<DxvkImage> GetShadowCopy() const {
|
||||
return m_copy;
|
||||
}
|
||||
|
||||
std::array<Rc<DxvkImageView>, 2> GetViews() const {
|
||||
return m_views;
|
||||
}
|
||||
|
@ -163,7 +155,6 @@ namespace dxvk {
|
|||
Com<ID3D11Resource> m_resource;
|
||||
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC m_desc;
|
||||
VkImageSubresourceLayers m_subresources;
|
||||
Rc<DxvkImage> m_copy;
|
||||
std::array<Rc<DxvkImageView>, 2> m_views;
|
||||
bool m_isYCbCr = false;
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace dxvk {
|
|||
* \param [in] b Second view to check
|
||||
* \returns \c true if the views overlap
|
||||
*/
|
||||
inline bool CheckViewOverlap(const D3D11_VK_VIEW_INFO& a, const D3D11_VK_VIEW_INFO b) {
|
||||
inline bool CheckViewOverlap(const D3D11_VK_VIEW_INFO& a, const D3D11_VK_VIEW_INFO& b) {
|
||||
if (likely(a.pResource != b.pResource))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -450,6 +450,7 @@ namespace dxvk {
|
|||
| VK_ACCESS_TRANSFER_READ_BIT
|
||||
| VK_ACCESS_SHADER_WRITE_BIT
|
||||
| VK_ACCESS_SHADER_READ_BIT;
|
||||
info.debugName = "UAV counter";
|
||||
|
||||
Rc<DxvkBuffer> buffer = device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
|
||||
|
|
|
@ -43,6 +43,10 @@ namespace dxvk {
|
|||
return m_info.BindFlags & Flags;
|
||||
}
|
||||
|
||||
BOOL HasCounter() const {
|
||||
return m_counterView != nullptr;
|
||||
}
|
||||
|
||||
D3D11_RESOURCE_DIMENSION GetResourceType() const {
|
||||
D3D11_RESOURCE_DIMENSION type;
|
||||
m_resource->GetType(&type);
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <climits>
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
|
@ -16,7 +15,9 @@ namespace dxvk {
|
|||
// Vertex buffer that can handle many tiny locks while
|
||||
// still maintaing the lock ordering of direct-mapped buffers.
|
||||
class D3D8BatchBuffer final : public D3D8VertexBuffer {
|
||||
|
||||
public:
|
||||
|
||||
D3D8BatchBuffer(
|
||||
D3D8Device* pDevice,
|
||||
D3DPOOL Pool,
|
||||
|
@ -67,8 +68,10 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
private:
|
||||
|
||||
std::vector<BYTE> m_data;
|
||||
DWORD m_fvf;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -79,13 +82,14 @@ namespace dxvk {
|
|||
D3DPRIMITIVETYPE PrimitiveType = D3DPT_INVALID;
|
||||
std::vector<uint16_t> Indices;
|
||||
UINT Offset = 0;
|
||||
UINT MinVertex = UINT_MAX;
|
||||
UINT MinVertex = std::numeric_limits<uint32_t>::max();
|
||||
UINT MaxVertex = 0;
|
||||
UINT PrimitiveCount = 0;
|
||||
UINT DrawCallCount = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
D3D8Batcher(D3D8Device* pDevice8, Com<d3d9::IDirect3DDevice9>&& pDevice9)
|
||||
: m_device8(pDevice8)
|
||||
, m_device(std::move(pDevice9)) {
|
||||
|
@ -121,7 +125,7 @@ namespace dxvk {
|
|||
|
||||
draw.PrimitiveType = D3DPRIMITIVETYPE(0);
|
||||
draw.Offset = 0;
|
||||
draw.MinVertex = UINT_MAX;
|
||||
draw.MinVertex = std::numeric_limits<uint32_t>::max();
|
||||
draw.MaxVertex = 0;
|
||||
draw.PrimitiveCount = 0;
|
||||
draw.DrawCallCount = 0;
|
||||
|
@ -231,6 +235,7 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
private:
|
||||
|
||||
D3D8Device* m_device8;
|
||||
Com<d3d9::IDirect3DDevice9> m_device;
|
||||
|
||||
|
@ -239,5 +244,7 @@ namespace dxvk {
|
|||
D3D8IndexBuffer* m_indices = nullptr;
|
||||
INT m_baseVertexIndex = 0;
|
||||
std::array<Batch, D3DPT_COUNT> m_batches;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
38
src/d3d8/d3d8_buffer.cpp
Normal file
38
src/d3d8/d3d8_buffer.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include "d3d8_buffer.h"
|
||||
#include "d3d8_device.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
// D3D8VertexBuffer
|
||||
|
||||
D3D8VertexBuffer::D3D8VertexBuffer(
|
||||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DVertexBuffer9>&& pBuffer,
|
||||
D3DPOOL Pool,
|
||||
DWORD Usage)
|
||||
: D3D8VertexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) {
|
||||
}
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE D3D8VertexBuffer::GetType() { return D3DRTYPE_VERTEXBUFFER; }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8VertexBuffer::GetDesc(D3DVERTEXBUFFER_DESC* pDesc) {
|
||||
return GetD3D9()->GetDesc(reinterpret_cast<d3d9::D3DVERTEXBUFFER_DESC*>(pDesc));
|
||||
}
|
||||
|
||||
// D3D8IndexBuffer
|
||||
|
||||
D3D8IndexBuffer::D3D8IndexBuffer(
|
||||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DIndexBuffer9>&& pBuffer,
|
||||
D3DPOOL Pool,
|
||||
DWORD Usage)
|
||||
: D3D8IndexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) {
|
||||
}
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE D3D8IndexBuffer::GetType() { return D3DRTYPE_INDEXBUFFER; }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8IndexBuffer::GetDesc(D3DINDEXBUFFER_DESC* pDesc) {
|
||||
return GetD3D9()->GetDesc(reinterpret_cast<d3d9::D3DINDEXBUFFER_DESC*>(pDesc));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "d3d8_include.h"
|
||||
#include "d3d8_options.h"
|
||||
#include "d3d8_resource.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
@ -15,8 +16,7 @@ namespace dxvk {
|
|||
Com<D3D9>&& pBuffer,
|
||||
D3DPOOL Pool,
|
||||
DWORD Usage)
|
||||
: D3D8Resource<D3D9, D3D8> (pDevice, std::move(pBuffer))
|
||||
, m_pool (Pool)
|
||||
: D3D8Resource<D3D9, D3D8> (pDevice, Pool, std::move(pBuffer))
|
||||
, m_usage (Usage) {
|
||||
m_options = this->GetParent()->GetOptions();
|
||||
}
|
||||
|
@ -49,9 +49,10 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
protected:
|
||||
|
||||
const D3D8Options* m_options;
|
||||
const D3DPOOL m_pool;
|
||||
const DWORD m_usage;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -64,15 +65,11 @@ namespace dxvk {
|
|||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DVertexBuffer9>&& pBuffer,
|
||||
D3DPOOL Pool,
|
||||
DWORD Usage)
|
||||
: D3D8VertexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) {
|
||||
}
|
||||
DWORD Usage);
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_VERTEXBUFFER; }
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc) {
|
||||
return GetD3D9()->GetDesc(reinterpret_cast<d3d9::D3DVERTEXBUFFER_DESC*>(pDesc));
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc);
|
||||
|
||||
};
|
||||
|
||||
|
@ -85,15 +82,12 @@ namespace dxvk {
|
|||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DIndexBuffer9>&& pBuffer,
|
||||
D3DPOOL Pool,
|
||||
DWORD Usage)
|
||||
: D3D8IndexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) {
|
||||
}
|
||||
DWORD Usage);
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_INDEXBUFFER; }
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDesc(D3DINDEXBUFFER_DESC* pDesc) final {
|
||||
return GetD3D9()->GetDesc(reinterpret_cast<d3d9::D3DINDEXBUFFER_DESC*>(pDesc));
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE GetDesc(D3DINDEXBUFFER_DESC* pDesc) final;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -19,16 +19,13 @@ namespace dxvk {
|
|||
|
||||
// Max supported shader model is PS 1.4 and VS 1.1
|
||||
pCaps8->VertexShaderVersion = D3DVS_VERSION(1, 1);
|
||||
// Late fixed-function capable hardware will advertise VS 1.1
|
||||
// support, but will not advertise any support for PS
|
||||
if (likely(caps9.PixelShaderVersion != D3DPS_VERSION(0, 0)))
|
||||
pCaps8->PixelShaderVersion = D3DPS_VERSION(1, 4);
|
||||
|
||||
// This was removed by D3D9. We can probably render windowed.
|
||||
pCaps8->Caps2 |= D3DCAPS2_CANRENDERWINDOWED;
|
||||
|
||||
// Replaced by D3DPRASTERCAPS_DEPTHBIAS in D3D9
|
||||
pCaps8->RasterCaps |= D3DPRASTERCAPS_ZBIAS;
|
||||
|
||||
|
||||
// Remove D3D9-specific caps:
|
||||
|
||||
pCaps8->Caps2 &= ~D3DCAPS2_CANAUTOGENMIPMAP;
|
||||
|
||||
pCaps8->Caps3 &= ~D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION
|
||||
|
@ -58,6 +55,34 @@ namespace dxvk {
|
|||
pCaps8->StencilCaps &= ~D3DSTENCILCAPS_TWOSIDED;
|
||||
|
||||
pCaps8->VertexProcessingCaps &= ~D3DVTXPCAPS_TEXGEN_SPHEREMAP;
|
||||
|
||||
// Add D3D8-specific caps:
|
||||
|
||||
// Removed in D3D9, since it can always render windowed
|
||||
pCaps8->Caps2 |= D3DCAPS2_CANRENDERWINDOWED
|
||||
// A remnant from a bygone age of ddraw interop most likely
|
||||
/* | D3DCAPS2_NO2DDURING3DSCENE*/;
|
||||
|
||||
// Used in conjunction with D3DPRASTERCAPS_PAT, but generally unadvertised
|
||||
/*pCaps8->PrimitiveMiscCaps |= D3DPMISCCAPS_LINEPATTERNREP;*/
|
||||
|
||||
// Replaced by D3DPRASTERCAPS_DEPTHBIAS in D3D9
|
||||
pCaps8->RasterCaps |= D3DPRASTERCAPS_ZBIAS
|
||||
// Advertised on Nvidia cards by modern drivers, but not on AMD or Intel
|
||||
/* | D3DPRASTERCAPS_ANTIALIASEDGES*/
|
||||
// Advertised on Nvidia cards, but not on AMD or Intel
|
||||
/* | D3DPRASTERCAPS_STRETCHBLTMULTISAMPLE*/
|
||||
// TODO: Implement D3DRS_LINEPATTERN - vkCmdSetLineRasterizationModeEXT
|
||||
/* | D3DPRASTERCAPS_PAT*/;
|
||||
|
||||
// MAG only filter caps, generally unsupported
|
||||
/*pCaps8->TextureFilterCaps |= D3DPTFILTERCAPS_MAGFAFLATCUBIC*/
|
||||
/* | D3DPTFILTERCAPS_MAGFGAUSSIANCUBIC;*/
|
||||
/*pCaps8->CubeTextureFilterCaps = pCaps8->TextureFilterCaps;*/
|
||||
/*pCaps8->VolumeTextureFilterCaps = pCaps8->TextureFilterCaps;*/
|
||||
|
||||
// Not advertised on any modern hardware
|
||||
/*pCaps8->VertexProcessingCaps |= D3DVTXPCAPS_NO_VSDT_UBYTE4;*/
|
||||
}
|
||||
|
||||
// (9<-8) D3DD3DPRESENT_PARAMETERS: Returns D3D9's params given an input for D3D8
|
||||
|
@ -75,35 +100,28 @@ namespace dxvk {
|
|||
params.BackBufferCount = pParams->BackBufferCount;
|
||||
|
||||
params.MultiSampleType = d3d9::D3DMULTISAMPLE_TYPE(pParams->MultiSampleType);
|
||||
params.MultiSampleQuality = 0; // (D3D8: no MultiSampleQuality), TODO: get a value for this
|
||||
// MultiSampleQuality is only used with D3DMULTISAMPLE_NONMASKABLE, which is not available in D3D8
|
||||
params.MultiSampleQuality = 0;
|
||||
|
||||
// If an application passes multiple D3DPRESENT_INTERVAL flags, this will be
|
||||
// validated appropriately by D3D9. Simply copy the values here.
|
||||
UINT PresentationInterval = pParams->FullScreen_PresentationInterval;
|
||||
|
||||
if (pParams->Windowed) {
|
||||
|
||||
if (unlikely(PresentationInterval != D3DPRESENT_INTERVAL_DEFAULT)) {
|
||||
// TODO: what does dx8 do if windowed app sets FullScreen_PresentationInterval?
|
||||
Logger::warn(str::format(
|
||||
"D3D8: Application is windowed yet requested FullScreen_PresentationInterval ", PresentationInterval,
|
||||
" (should be D3DPRESENT_INTERVAL_DEFAULT). This will be ignored."));
|
||||
}
|
||||
|
||||
// D3D8: For windowed swap chain, the back buffer is copied to the window immediately.
|
||||
PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
||||
}
|
||||
|
||||
D3DSWAPEFFECT SwapEffect = pParams->SwapEffect;
|
||||
|
||||
// D3DSWAPEFFECT_COPY_VSYNC has been removed
|
||||
if (SwapEffect == D3DSWAPEFFECT_COPY_VSYNC) {
|
||||
|
||||
// D3DSWAPEFFECT_COPY_VSYNC has been removed from D3D9, use D3DSWAPEFFECT_COPY
|
||||
SwapEffect = D3DSWAPEFFECT_COPY;
|
||||
|
||||
// D3D8: In windowed mode, D3DSWAPEFFECT_COPY_VSYNC enables VSYNC.
|
||||
// In fullscreen, D3DPRESENT_INTERVAL_IMMEDIATE is meaningless.
|
||||
if (pParams->Windowed || (PresentationInterval & D3DPRESENT_INTERVAL_IMMEDIATE) != 0) {
|
||||
if (pParams->Windowed || PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE) {
|
||||
PresentationInterval = D3DPRESENT_INTERVAL_ONE;
|
||||
// TODO: what does dx8 do if multiple D3DPRESENT_INTERVAL flags are set?
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,5 +190,5 @@ namespace dxvk {
|
|||
default: return d3d9::D3DSAMPLERSTATETYPE(-1u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,13 +51,17 @@ namespace dxvk {
|
|||
throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!");
|
||||
}
|
||||
|
||||
m_bridge->SetAPIName("D3D8");
|
||||
|
||||
ResetState();
|
||||
RecreateBackBuffersAndAutoDepthStencil();
|
||||
|
||||
if (m_d3d8Options.batching)
|
||||
m_batcher = new D3D8Batcher(this, GetD3D9());
|
||||
|
||||
d3d9::D3DCAPS9 caps9;
|
||||
HRESULT res = GetD3D9()->GetDeviceCaps(&caps9);
|
||||
|
||||
if (unlikely(SUCCEEDED(res) && caps9.PixelShaderVersion == D3DPS_VERSION(0, 0)))
|
||||
m_isFixedFunctionOnly = true;
|
||||
}
|
||||
|
||||
D3D8Device::~D3D8Device() {
|
||||
|
@ -131,6 +135,7 @@ namespace dxvk {
|
|||
// Immediately issue the query. D3D9 will begin it automatically before ending.
|
||||
pQuery->Issue(D3DISSUE_END);
|
||||
// TODO: Will immediately issuing the query actually yield meaingful results?
|
||||
//
|
||||
// Only relevant once RESOURCEMANAGER or VERTEXSTATS are implemented by D9VK,
|
||||
// since VCACHE queries will immediately return data during this call.
|
||||
res = pQuery->GetData(pDevInfoStruct, DevInfoStructSize, D3DGETDATA_FLUSH);
|
||||
|
@ -165,7 +170,7 @@ namespace dxvk {
|
|||
HRESULT res = GetD3D9()->GetDeviceCaps(&caps9);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
dxvk::ConvertCaps8(caps9, pCaps);
|
||||
ConvertCaps8(caps9, pCaps);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -236,6 +241,12 @@ namespace dxvk {
|
|||
&& pPresentationParameters->BackBufferCount > 1))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// In D3D8 nothing except D3DPRESENT_INTERVAL_DEFAULT can be used
|
||||
// as a flag for windowed presentation.
|
||||
if (unlikely(pPresentationParameters->Windowed
|
||||
&& pPresentationParameters->FullScreen_PresentationInterval != D3DPRESENT_INTERVAL_DEFAULT))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
m_presentParams = *pPresentationParameters;
|
||||
ResetState();
|
||||
|
||||
|
@ -276,7 +287,7 @@ namespace dxvk {
|
|||
HRESULT res = GetD3D9()->GetBackBuffer(0, iBackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9);
|
||||
|
||||
if (likely(SUCCEEDED(res))) {
|
||||
m_backBuffers[iBackBuffer] = new D3D8Surface(this, std::move(pSurface9));
|
||||
m_backBuffers[iBackBuffer] = new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pSurface9));
|
||||
*ppBackBuffer = m_backBuffers[iBackBuffer].ref();
|
||||
}
|
||||
|
||||
|
@ -336,7 +347,7 @@ namespace dxvk {
|
|||
NULL);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
*ppTexture = ref(new D3D8Texture2D(this, std::move(pTex9)));
|
||||
*ppTexture = ref(new D3D8Texture2D(this, Pool, std::move(pTex9)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -370,7 +381,7 @@ namespace dxvk {
|
|||
NULL);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
*ppVolumeTexture = ref(new D3D8Texture3D(this, std::move(pVolume9)));
|
||||
*ppVolumeTexture = ref(new D3D8Texture3D(this, Pool, std::move(pVolume9)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -403,7 +414,7 @@ namespace dxvk {
|
|||
NULL);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
*ppCubeTexture = ref(new D3D8TextureCube(this, std::move(pCube9)));
|
||||
*ppCubeTexture = ref(new D3D8TextureCube(this, Pool, std::move(pCube9)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -476,13 +487,13 @@ namespace dxvk {
|
|||
Height,
|
||||
d3d9::D3DFORMAT(Format),
|
||||
d3d9::D3DMULTISAMPLE_TYPE(MultiSample),
|
||||
0, // TODO: CreateRenderTarget MultisampleQuality
|
||||
0,
|
||||
Lockable,
|
||||
&pSurf9,
|
||||
NULL);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
*ppSurface = ref(new D3D8Surface(this, std::move(pSurf9)));
|
||||
*ppSurface = ref(new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pSurf9)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -509,13 +520,13 @@ namespace dxvk {
|
|||
Height,
|
||||
d3d9::D3DFORMAT(Format),
|
||||
d3d9::D3DMULTISAMPLE_TYPE(MultiSample),
|
||||
0, // TODO: CreateDepthStencilSurface MultisampleQuality
|
||||
true, // TODO: CreateDepthStencilSurface Discard
|
||||
0,
|
||||
FALSE, // z-buffer discarding is not used in D3D8
|
||||
&pSurf9,
|
||||
NULL);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
*ppSurface = ref(new D3D8Surface(this, std::move(pSurf9)));
|
||||
*ppSurface = ref(new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pSurf9)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -535,7 +546,6 @@ namespace dxvk {
|
|||
if (unlikely(ppSurface == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// FIXME: Handle D3DPOOL_SCRATCH in CopyRects
|
||||
D3DPOOL pool = isUnsupportedSurfaceFormat(Format) ? D3DPOOL_SCRATCH : D3DPOOL_SYSTEMMEM;
|
||||
|
||||
Com<d3d9::IDirect3DSurface9> pSurf = nullptr;
|
||||
|
@ -548,7 +558,7 @@ namespace dxvk {
|
|||
NULL);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
*ppSurface = ref(new D3D8Surface(this, std::move(pSurf)));
|
||||
*ppSurface = ref(new D3D8Surface(this, pool, std::move(pSurf)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -596,9 +606,8 @@ namespace dxvk {
|
|||
auto amplitude = cols * bpp;
|
||||
|
||||
// Handle DXT compressed textures.
|
||||
// TODO: Are rects always 4x4 aligned?
|
||||
if (compressed) {
|
||||
// Assume that DXT blocks are 4x4 pixels.
|
||||
// DXT blocks are always 4x4 pixels.
|
||||
constexpr UINT blockWidth = 4;
|
||||
constexpr UINT blockHeight = 4;
|
||||
|
||||
|
@ -640,14 +649,14 @@ namespace dxvk {
|
|||
* The following table shows the possible combinations of source
|
||||
* and destination surface pools, and how we handle each of them.
|
||||
*
|
||||
* ┌────────────┬───────────────────────────┬───────────────────────┬───────────────────────┬──────────┐
|
||||
* ┌────────────┬───────────────────────────┬───────────────────────┬───────────────────────┬──────────────────────┐
|
||||
* │ Src/Dst │ DEFAULT │ MANAGED │ SYSTEMMEM │ SCRATCH │
|
||||
* ├────────────┼───────────────────────────┼───────────────────────┼───────────────────────┼──────────┤
|
||||
* │ DEFAULT │ StretchRect │ GetRenderTargetData │ GetRenderTargetData │ - │
|
||||
* │ MANAGED │ UpdateTextureFromBuffer │ memcpy │ memcpy │ - │
|
||||
* │ SYSTEMMEM │ UpdateSurface │ memcpy │ memcpy │ - │
|
||||
* │ SCRATCH │ - │ - │ - │ - │
|
||||
* └────────────┴───────────────────────────┴───────────────────────┴───────────────────────┴──────────┘
|
||||
* ├────────────┼───────────────────────────┼───────────────────────┼───────────────────────┼──────────────────────┤
|
||||
* │ DEFAULT │ StretchRect │ GetRenderTargetData │ GetRenderTargetData │ GetRenderTargetData │
|
||||
* │ MANAGED │ UpdateTextureFromBuffer │ memcpy │ memcpy │ memcpy │
|
||||
* │ SYSTEMMEM │ UpdateSurface │ memcpy │ memcpy │ memcpy │
|
||||
* │ SCRATCH │ memcpy + UpdateSurface │ memcpy │ memcpy │ memcpy │
|
||||
* └────────────┴───────────────────────────┴───────────────────────┴───────────────────────┴──────────────────────┘
|
||||
*/
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::CopyRects(
|
||||
IDirect3DSurface8* pSourceSurface,
|
||||
|
@ -729,8 +738,8 @@ namespace dxvk {
|
|||
|
||||
POINT dstPt = { dstRect.left, dstRect.top };
|
||||
|
||||
auto unhandled = [&] {
|
||||
Logger::warn(str::format("D3D8Device::CopyRects: Unhandled case from src pool ", srcDesc.Pool, " to dst pool ", dstDesc.Pool));
|
||||
auto unsupported = [&] {
|
||||
Logger::err(str::format("D3D8Device::CopyRects: Unsupported case from src pool ", srcDesc.Pool, " to dst pool ", dstDesc.Pool));
|
||||
return D3DERR_INVALIDCALL;
|
||||
};
|
||||
|
||||
|
@ -776,19 +785,52 @@ namespace dxvk {
|
|||
&dstPt
|
||||
));
|
||||
}
|
||||
case d3d9::D3DPOOL_SCRATCH:
|
||||
case d3d9::D3DPOOL_SCRATCH: {
|
||||
// SCRATCH -> DEFAULT: memcpy to a SYSTEMMEM temporary buffer and use UpdateSurface
|
||||
|
||||
// UpdateSurface will not work on surface formats unsupported by D3DPOOL_DEFAULT
|
||||
if (unlikely(isUnsupportedSurfaceFormat(D3DFORMAT(srcDesc.Format)))) {
|
||||
return logError(D3DERR_INVALIDCALL);
|
||||
}
|
||||
|
||||
Com<IDirect3DSurface8> pTempImageSurface;
|
||||
// The temporary image surface is guaranteed to end up in SYSTEMMEM for supported formats
|
||||
HRESULT res = CreateImageSurface(
|
||||
srcDesc.Width,
|
||||
srcDesc.Height,
|
||||
D3DFORMAT(srcDesc.Format),
|
||||
&pTempImageSurface
|
||||
);
|
||||
|
||||
if (FAILED(res)) {
|
||||
return logError(res);
|
||||
}
|
||||
|
||||
Com<D3D8Surface> pBlitImage = static_cast<D3D8Surface*>(pTempImageSurface.ptr());
|
||||
// Temporary image surface dimensions are identical, so we can reuse srcDesc/Rect
|
||||
res = copyTextureBuffers(src.ptr(), pBlitImage.ptr(), srcDesc, srcDesc, srcRect, srcRect);
|
||||
|
||||
if (FAILED(res)) {
|
||||
return logError(res);
|
||||
}
|
||||
|
||||
return logError(GetD3D9()->UpdateSurface(
|
||||
pBlitImage->GetD3D9(),
|
||||
&srcRect,
|
||||
dst->GetD3D9(),
|
||||
&dstPt
|
||||
));
|
||||
}
|
||||
default: {
|
||||
// TODO: Unhandled case.
|
||||
return unhandled();
|
||||
return unsupported();
|
||||
}
|
||||
} break;
|
||||
|
||||
// Dest: MANAGED
|
||||
case d3d9::D3DPOOL_MANAGED:
|
||||
switch (srcDesc.Pool) {
|
||||
case d3d9::D3DPOOL_DEFAULT: {
|
||||
// TODO: Copy on GPU (handle MANAGED similarly to SYSTEMMEM for now)
|
||||
|
||||
case d3d9::D3DPOOL_DEFAULT: {
|
||||
// Get temporary off-screen surface for stretching.
|
||||
Com<d3d9::IDirect3DSurface9> pBlitImage = dst->GetBlitImage();
|
||||
|
||||
|
@ -808,8 +850,9 @@ namespace dxvk {
|
|||
return logError(GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9()));
|
||||
}
|
||||
case d3d9::D3DPOOL_MANAGED:
|
||||
case d3d9::D3DPOOL_SYSTEMMEM: {
|
||||
// SYSTEMMEM -> MANAGED: LockRect / memcpy
|
||||
case d3d9::D3DPOOL_SYSTEMMEM:
|
||||
case d3d9::D3DPOOL_SCRATCH: {
|
||||
// MANAGED/SYSMEM/SCRATCH -> MANAGED: LockRect / memcpy
|
||||
|
||||
if (stretch) {
|
||||
return logError(D3DERR_INVALIDCALL);
|
||||
|
@ -817,10 +860,8 @@ namespace dxvk {
|
|||
|
||||
return logError(copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect));
|
||||
}
|
||||
case d3d9::D3DPOOL_SCRATCH:
|
||||
default: {
|
||||
// TODO: Unhandled case.
|
||||
return unhandled();
|
||||
return unsupported();
|
||||
}
|
||||
} break;
|
||||
|
||||
|
@ -852,6 +893,7 @@ namespace dxvk {
|
|||
pBlitImage.ptr(),
|
||||
&dstRect,
|
||||
d3d9::D3DTEXF_NONE);
|
||||
|
||||
if (FAILED(res)) {
|
||||
return logError(res);
|
||||
}
|
||||
|
@ -859,29 +901,75 @@ namespace dxvk {
|
|||
// Now sync the rendertarget data into main memory.
|
||||
return logError(GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9()));
|
||||
}
|
||||
|
||||
// SYSMEM/MANAGED -> SYSMEM: LockRect / memcpy
|
||||
// MANAGED/SYSMEM/SCRATCH -> SYSMEM: LockRect / memcpy
|
||||
case d3d9::D3DPOOL_MANAGED:
|
||||
case d3d9::D3DPOOL_SYSTEMMEM: {
|
||||
case d3d9::D3DPOOL_SYSTEMMEM:
|
||||
case d3d9::D3DPOOL_SCRATCH: {
|
||||
if (stretch) {
|
||||
return logError(D3DERR_INVALIDCALL);
|
||||
}
|
||||
|
||||
return logError(copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect));
|
||||
}
|
||||
case d3d9::D3DPOOL_SCRATCH:
|
||||
default: {
|
||||
// TODO: Unhandled case.
|
||||
return unhandled();
|
||||
return unsupported();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
// DEST: SCRATCH
|
||||
case d3d9::D3DPOOL_SCRATCH:
|
||||
case d3d9::D3DPOOL_SCRATCH: {
|
||||
|
||||
// RT (DEFAULT) -> SCRATCH: Use GetRenderTargetData as fast path if possible
|
||||
if ((srcDesc.Usage & D3DUSAGE_RENDERTARGET || m_renderTarget == src.ptr())) {
|
||||
|
||||
// GetRenderTargetData works if the formats and sizes match
|
||||
if (srcDesc.MultiSampleType == d3d9::D3DMULTISAMPLE_NONE
|
||||
&& srcDesc.Width == dstDesc.Width
|
||||
&& srcDesc.Height == dstDesc.Height
|
||||
&& srcDesc.Format == dstDesc.Format
|
||||
&& !asymmetric) {
|
||||
return logError(GetD3D9()->GetRenderTargetData(src->GetD3D9(), dst->GetD3D9()));
|
||||
}
|
||||
}
|
||||
|
||||
switch (srcDesc.Pool) {
|
||||
case d3d9::D3DPOOL_DEFAULT: {
|
||||
// Get temporary off-screen surface for stretching.
|
||||
Com<d3d9::IDirect3DSurface9> pBlitImage = dst->GetBlitImage();
|
||||
|
||||
// Stretch the source RT to the temporary surface.
|
||||
HRESULT res = GetD3D9()->StretchRect(
|
||||
src->GetD3D9(),
|
||||
&srcRect,
|
||||
pBlitImage.ptr(),
|
||||
&dstRect,
|
||||
d3d9::D3DTEXF_NONE);
|
||||
|
||||
if (FAILED(res)) {
|
||||
return logError(res);
|
||||
}
|
||||
|
||||
// Now sync the rendertarget data into main memory.
|
||||
return logError(GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9()));
|
||||
}
|
||||
// MANAGED/SYSMEM/SCRATCH -> SCRATCH: LockRect / memcpy
|
||||
case d3d9::D3DPOOL_MANAGED:
|
||||
case d3d9::D3DPOOL_SYSTEMMEM:
|
||||
case d3d9::D3DPOOL_SCRATCH: {
|
||||
if (stretch) {
|
||||
return logError(D3DERR_INVALIDCALL);
|
||||
}
|
||||
|
||||
return logError(copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect));
|
||||
}
|
||||
default: {
|
||||
// TODO: Unhandled case.
|
||||
return unhandled();
|
||||
return unsupported();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
default: {
|
||||
return unsupported();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -976,7 +1064,7 @@ namespace dxvk {
|
|||
HRESULT res = GetD3D9()->GetRenderTarget(0, &pRT9); // use RT index 0
|
||||
|
||||
if (likely(SUCCEEDED(res))) {
|
||||
m_renderTarget = new D3D8Surface(this, std::move(pRT9));
|
||||
m_renderTarget = new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pRT9));
|
||||
*ppRenderTarget = m_renderTarget.ref();
|
||||
}
|
||||
|
||||
|
@ -1000,7 +1088,7 @@ namespace dxvk {
|
|||
HRESULT res = GetD3D9()->GetDepthStencilSurface(&pStencil9);
|
||||
|
||||
if (likely(SUCCEEDED(res))) {
|
||||
m_depthStencil = new D3D8Surface(this, std::move(pStencil9));
|
||||
m_depthStencil = new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pStencil9));
|
||||
*ppZStencilSurface = m_depthStencil.ref();
|
||||
}
|
||||
|
||||
|
@ -1280,6 +1368,27 @@ namespace dxvk {
|
|||
|
||||
D3D8Texture2D* tex = static_cast<D3D8Texture2D*>(pTexture);
|
||||
|
||||
// Splinter Cell: Force perspective divide when a shadow map is bound to slot 0
|
||||
if (unlikely(m_d3d8Options.shadowPerspectiveDivide && Stage == 0)) {
|
||||
if (tex) {
|
||||
D3DSURFACE_DESC surf;
|
||||
tex->GetLevelDesc(0, &surf);
|
||||
if (isDepthStencilFormat(surf.Format)) {
|
||||
// If we bound a depth texture to stage 0 then we need to set the projected flag for stage 0 and 1
|
||||
// Stage 1 is a non-depth light cookie texture but still requires perspective divide to work
|
||||
GetD3D9()->SetTextureStageState(0, d3d9::D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_PROJECTED);
|
||||
GetD3D9()->SetTextureStageState(1, d3d9::D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_PROJECTED);
|
||||
m_shadowPerspectiveDivide = true;
|
||||
} else if (m_shadowPerspectiveDivide) {
|
||||
// Non-depth texture bound. Game will reset the transform flags to 0 on its own
|
||||
m_shadowPerspectiveDivide = false;
|
||||
}
|
||||
} else if (m_shadowPerspectiveDivide) {
|
||||
// Texture unbound. Game will reset the transform flags to 0 on its own
|
||||
m_shadowPerspectiveDivide = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(m_textures[Stage] == tex))
|
||||
return D3D_OK;
|
||||
|
||||
|
@ -1313,6 +1422,13 @@ namespace dxvk {
|
|||
DWORD Value) {
|
||||
d3d9::D3DSAMPLERSTATETYPE stateType = GetSamplerStateType9(Type);
|
||||
|
||||
if (unlikely(m_d3d8Options.shadowPerspectiveDivide && Type == D3DTSS_TEXTURETRANSFORMFLAGS)) {
|
||||
// Splinter Cell: Ignore requests to change texture transform flags
|
||||
// to 0 while shadow mapping perspective divide mode is enabled
|
||||
if (m_shadowPerspectiveDivide && (Stage == 0 || Stage == 1))
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
StateChange();
|
||||
if (stateType != -1u) {
|
||||
// if the type has been remapped to a sampler state type:
|
||||
|
@ -1365,7 +1481,7 @@ namespace dxvk {
|
|||
|
||||
return GetD3D9()->DrawIndexedPrimitive(
|
||||
d3d9::D3DPRIMITIVETYPE(PrimitiveType),
|
||||
static_cast<INT>(std::min(m_baseVertexIndex, static_cast<UINT>(INT_MAX))), // set by SetIndices
|
||||
static_cast<INT>(std::min(m_baseVertexIndex, static_cast<UINT>(std::numeric_limits<int32_t>::max()))), // set by SetIndices
|
||||
MinVertexIndex,
|
||||
NumVertices,
|
||||
StartIndex,
|
||||
|
@ -1505,7 +1621,7 @@ namespace dxvk {
|
|||
if (unlikely(ShouldRecord()))
|
||||
return m_recorder->SetIndices(pIndexData, BaseVertexIndex);
|
||||
|
||||
if (unlikely(BaseVertexIndex > INT_MAX))
|
||||
if (unlikely(BaseVertexIndex > std::numeric_limits<int32_t>::max()))
|
||||
Logger::warn("D3D8Device::SetIndices: BaseVertexIndex exceeds INT_MAX");
|
||||
|
||||
// used by DrawIndexedPrimitive
|
||||
|
@ -1573,7 +1689,7 @@ namespace dxvk {
|
|||
|
||||
// Render States //
|
||||
|
||||
// ZBIAS can be an integer from 0 to 1 and needs to be remapped to float
|
||||
// ZBIAS can be an integer from 0 to 16 and needs to be remapped to float
|
||||
static constexpr float ZBIAS_SCALE = -0.000005f;
|
||||
static constexpr float ZBIAS_SCALE_INV = 1 / ZBIAS_SCALE;
|
||||
|
||||
|
@ -1588,26 +1704,26 @@ namespace dxvk {
|
|||
default:
|
||||
break;
|
||||
|
||||
// TODO: D3DRS_LINEPATTERN - vkCmdSetLineRasterizationModeEXT
|
||||
case D3DRS_LINEPATTERN: {
|
||||
[[maybe_unused]]
|
||||
D3DLINEPATTERN pattern = bit::cast<D3DLINEPATTERN>(Value);
|
||||
stateChange = false;
|
||||
} break;
|
||||
// TODO: Implement D3DRS_LINEPATTERN - vkCmdSetLineRasterizationModeEXT
|
||||
// and advertise support with D3DPRASTERCAPS_PAT once that is done
|
||||
case D3DRS_LINEPATTERN:
|
||||
Logger::warn("D3D8Device::SetRenderState: Unimplemented render state D3DRS_LINEPATTERN");
|
||||
m_linePattern = bit::cast<D3DLINEPATTERN>(Value);
|
||||
return D3D_OK;
|
||||
|
||||
// Not supported by D3D8.
|
||||
// Not supported by D3D8, but its value is stored.
|
||||
case D3DRS_ZVISIBLE:
|
||||
stateChange = false;
|
||||
break;
|
||||
m_zVisible = Value;
|
||||
return D3D_OK;
|
||||
|
||||
// TODO: Not implemented by D9VK. Try anyway.
|
||||
// TODO: Implement D3DRS_ANTIALIASEDLINEENABLE in D9VK.
|
||||
case D3DRS_EDGEANTIALIAS:
|
||||
State9 = d3d9::D3DRS_ANTIALIASEDLINEENABLE;
|
||||
break;
|
||||
|
||||
case D3DRS_ZBIAS:
|
||||
State9 = d3d9::D3DRS_DEPTHBIAS;
|
||||
Value = bit::cast<DWORD>(float(Value) * ZBIAS_SCALE);
|
||||
Value = bit::cast<DWORD>(static_cast<float>(Value) * ZBIAS_SCALE);
|
||||
break;
|
||||
|
||||
case D3DRS_SOFTWAREVERTEXPROCESSING:
|
||||
|
@ -1621,10 +1737,11 @@ namespace dxvk {
|
|||
|
||||
return GetD3D9()->SetSoftwareVertexProcessing(Value);
|
||||
|
||||
// TODO: D3DRS_PATCHSEGMENTS
|
||||
// TODO: Implement D3DRS_PATCHSEGMENTS
|
||||
case D3DRS_PATCHSEGMENTS:
|
||||
stateChange = false;
|
||||
break;
|
||||
Logger::warn("D3D8Device::SetRenderState: Unimplemented render state D3DRS_PATCHSEGMENTS");
|
||||
m_patchSegments = bit::cast<float>(Value);
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
if (stateChange) {
|
||||
|
@ -1653,31 +1770,33 @@ namespace dxvk {
|
|||
default:
|
||||
break;
|
||||
|
||||
// TODO: D3DRS_LINEPATTERN
|
||||
case D3DRS_LINEPATTERN:
|
||||
break;
|
||||
*pValue = bit::cast<DWORD>(m_linePattern);
|
||||
return D3D_OK;
|
||||
|
||||
// Not supported by D3D8.
|
||||
// Not supported by D3D8, but its value is stored.
|
||||
case D3DRS_ZVISIBLE:
|
||||
break;
|
||||
*pValue = m_zVisible;
|
||||
return D3D_OK;
|
||||
|
||||
case D3DRS_EDGEANTIALIAS:
|
||||
State9 = d3d9::D3DRS_ANTIALIASEDLINEENABLE;
|
||||
break;
|
||||
|
||||
case D3DRS_ZBIAS: {
|
||||
float bias = 0;
|
||||
HRESULT res = GetD3D9()->GetRenderState(d3d9::D3DRS_DEPTHBIAS, (DWORD*)&bias);
|
||||
*pValue = bit::cast<DWORD>(bias * ZBIAS_SCALE_INV);
|
||||
DWORD bias = 0;
|
||||
HRESULT res = GetD3D9()->GetRenderState(d3d9::D3DRS_DEPTHBIAS, &bias);
|
||||
*pValue = static_cast<DWORD>(bit::cast<float>(bias) * ZBIAS_SCALE_INV);
|
||||
return res;
|
||||
} break;
|
||||
|
||||
case D3DRS_SOFTWAREVERTEXPROCESSING:
|
||||
return GetD3D9()->GetSoftwareVertexProcessing();
|
||||
*pValue = GetD3D9()->GetSoftwareVertexProcessing();
|
||||
return D3D_OK;
|
||||
|
||||
// TODO: D3DRS_PATCHSEGMENTS
|
||||
case D3DRS_PATCHSEGMENTS:
|
||||
break;
|
||||
*pValue = bit::cast<DWORD>(m_patchSegments);
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
// This call will never fail
|
||||
|
@ -1698,8 +1817,8 @@ namespace dxvk {
|
|||
|
||||
// Validate VS version for non-FF shaders
|
||||
if (pFunction != nullptr) {
|
||||
uint32_t majorVersion = (pFunction[0] >> 8) & 0xff;
|
||||
uint32_t minorVersion = pFunction[0] & 0xff;
|
||||
const uint32_t majorVersion = D3DSHADER_VERSION_MAJOR(pFunction[0]);
|
||||
const uint32_t minorVersion = D3DSHADER_VERSION_MINOR(pFunction[0]);
|
||||
|
||||
if (unlikely(majorVersion != 1 || minorVersion > 1)) {
|
||||
Logger::err(str::format("D3D8Device::CreateVertexShader: Unsupported VS version ", majorVersion, ".", minorVersion));
|
||||
|
@ -1937,10 +2056,10 @@ namespace dxvk {
|
|||
if (unlikely(pFunction == nullptr || pHandle == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
uint32_t majorVersion = (pFunction[0] >> 8) & 0xff;
|
||||
uint32_t minorVersion = pFunction[0] & 0xff;
|
||||
const uint32_t majorVersion = D3DSHADER_VERSION_MAJOR(pFunction[0]);
|
||||
const uint32_t minorVersion = D3DSHADER_VERSION_MINOR(pFunction[0]);
|
||||
|
||||
if (unlikely(majorVersion != 1 || minorVersion > 4)) {
|
||||
if (unlikely(m_isFixedFunctionOnly || majorVersion != 1 || minorVersion > 4)) {
|
||||
Logger::err(str::format("D3D8Device::CreatePixelShader: Unsupported PS version ", majorVersion, ".", minorVersion));
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace dxvk {
|
|||
class D3D8Device final : public D3D8DeviceBase {
|
||||
|
||||
friend class D3D8StateBlock;
|
||||
|
||||
public:
|
||||
|
||||
D3D8Device(
|
||||
|
@ -356,8 +357,6 @@ namespace dxvk {
|
|||
|
||||
HRESULT STDMETHODCALLTYPE DeletePatch(UINT Handle);
|
||||
|
||||
public: // Internal Methods //
|
||||
|
||||
const D3D8Options* GetOptions() const {
|
||||
return &m_d3d8Options;
|
||||
}
|
||||
|
@ -384,8 +383,6 @@ namespace dxvk {
|
|||
m_presentParams.BackBufferCount = std::max(m_presentParams.BackBufferCount, 1u);
|
||||
|
||||
// Purge cached objects
|
||||
// TODO: Some functions may need to be called here (e.g. SetTexture, etc.)
|
||||
// in case Reset can be recorded by state blocks and other things.
|
||||
m_textures.fill(nullptr);
|
||||
m_streams.fill(D3D8VBO());
|
||||
m_indices = nullptr;
|
||||
|
@ -396,20 +393,22 @@ namespace dxvk {
|
|||
m_backBuffers.resize(m_presentParams.BackBufferCount);
|
||||
|
||||
m_autoDepthStencil = nullptr;
|
||||
|
||||
m_shadowPerspectiveDivide = false;
|
||||
}
|
||||
|
||||
inline void RecreateBackBuffersAndAutoDepthStencil() {
|
||||
for (UINT i = 0; i < m_presentParams.BackBufferCount; i++) {
|
||||
Com<d3d9::IDirect3DSurface9> pSurface9;
|
||||
GetD3D9()->GetBackBuffer(0, i, d3d9::D3DBACKBUFFER_TYPE_MONO, &pSurface9);
|
||||
m_backBuffers[i] = new D3D8Surface(this, std::move(pSurface9));
|
||||
m_backBuffers[i] = new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pSurface9));
|
||||
}
|
||||
|
||||
Com<d3d9::IDirect3DSurface9> pStencil9;
|
||||
// This call will fail if the D3D9 device is created without
|
||||
// the EnableAutoDepthStencil presentation parameter set to TRUE.
|
||||
HRESULT res = GetD3D9()->GetDepthStencilSurface(&pStencil9);
|
||||
m_autoDepthStencil = FAILED(res) ? nullptr : new D3D8Surface(this, std::move(pStencil9));
|
||||
m_autoDepthStencil = FAILED(res) ? nullptr : new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pStencil9));
|
||||
|
||||
m_renderTarget = m_backBuffers[0];
|
||||
m_depthStencil = m_autoDepthStencil;
|
||||
|
@ -427,6 +426,18 @@ namespace dxvk {
|
|||
|
||||
D3DPRESENT_PARAMETERS m_presentParams;
|
||||
|
||||
// Value of D3DRS_LINEPATTERN
|
||||
D3DLINEPATTERN m_linePattern = {};
|
||||
// Value of D3DRS_ZVISIBLE (although the RS is not supported, its value is stored)
|
||||
DWORD m_zVisible = 0;
|
||||
// Value of D3DRS_PATCHSEGMENTS
|
||||
float m_patchSegments = 1.0f;
|
||||
|
||||
// Controls fixed-function exclusive mode (no PS support)
|
||||
bool m_isFixedFunctionOnly = false;
|
||||
|
||||
bool m_shadowPerspectiveDivide = false;
|
||||
|
||||
D3D8StateBlock* m_recorder = nullptr;
|
||||
DWORD m_recorderToken = 0;
|
||||
DWORD m_token = 0;
|
||||
|
@ -438,14 +449,12 @@ namespace dxvk {
|
|||
UINT stride = 0;
|
||||
};
|
||||
|
||||
// Remember to fill() these in the constructor!
|
||||
std::array<Com<D3D8Texture2D, false>, d8caps::MAX_TEXTURE_STAGES> m_textures;
|
||||
std::array<D3D8VBO, d8caps::MAX_STREAMS> m_streams;
|
||||
|
||||
Com<D3D8IndexBuffer, false> m_indices;
|
||||
UINT m_baseVertexIndex = 0;
|
||||
|
||||
// TODO: Which of these should be a private ref
|
||||
std::vector<Com<D3D8Surface, false>> m_backBuffers;
|
||||
Com<D3D8Surface, false> m_autoDepthStencil;
|
||||
|
||||
|
|
|
@ -31,16 +31,28 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release() {
|
||||
// ignore Release calls on objects with 0 refCount
|
||||
if(unlikely(!this->m_refCount))
|
||||
return this->m_refCount;
|
||||
uint32_t oldRefCount, refCount;
|
||||
|
||||
do {
|
||||
oldRefCount = this->m_refCount.load(std::memory_order_acquire);
|
||||
|
||||
// clamp value to 0 to prevent underruns
|
||||
if (unlikely(!oldRefCount))
|
||||
return 0;
|
||||
|
||||
refCount = oldRefCount - 1;
|
||||
|
||||
} while (!this->m_refCount.compare_exchange_weak(oldRefCount,
|
||||
refCount,
|
||||
std::memory_order_release,
|
||||
std::memory_order_acquire));
|
||||
|
||||
uint32_t refCount = --this->m_refCount;
|
||||
if (unlikely(!refCount)) {
|
||||
auto* pDevice = GetDevice();
|
||||
this->ReleasePrivate();
|
||||
pDevice->Release();
|
||||
}
|
||||
|
||||
return refCount;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "d3d8_include.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
constexpr bool isDXT(D3DFORMAT fmt) {
|
||||
return fmt == D3DFMT_DXT1
|
||||
|| fmt == D3DFMT_DXT2
|
||||
|
@ -208,7 +209,6 @@ namespace dxvk {
|
|||
return size;
|
||||
}
|
||||
|
||||
|
||||
constexpr UINT getSurfaceSize(D3DFORMAT Format, UINT Width, UINT Height) {
|
||||
if (isDXT(Format)) {
|
||||
Width = ((Width + 3) >> 2);
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
|
||||
#include <cstring>
|
||||
|
||||
namespace dxvk
|
||||
{
|
||||
namespace dxvk {
|
||||
|
||||
D3D8Interface::D3D8Interface() {
|
||||
m_d3d9 = d3d9::Direct3DCreate9(D3D_SDK_VERSION);
|
||||
|
||||
// Get the bridge interface to D3D9.
|
||||
if (FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), (void**)&m_bridge))) {
|
||||
throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!");
|
||||
throw DxvkError("D3D8Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!");
|
||||
}
|
||||
|
||||
m_bridge->SetD3D8CompatibilityMode(true);
|
||||
|
@ -129,6 +129,12 @@ namespace dxvk
|
|||
&& pPresentationParameters->BackBufferCount > 1))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// In D3D8 nothing except D3DPRESENT_INTERVAL_DEFAULT can be used
|
||||
// as a flag for windowed presentation.
|
||||
if (unlikely(pPresentationParameters->Windowed
|
||||
&& pPresentationParameters->FullScreen_PresentationInterval != D3DPRESENT_INTERVAL_DEFAULT))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
Com<d3d9::IDirect3DDevice9> pDevice9 = nullptr;
|
||||
d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters);
|
||||
HRESULT res = m_d3d9->CreateDevice(
|
||||
|
|
|
@ -138,7 +138,7 @@ namespace dxvk {
|
|||
HRESULT res = m_d3d9->GetDeviceCaps(Adapter, (d3d9::D3DDEVTYPE)DeviceType, &caps9);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
dxvk::ConvertCaps8(caps9, pCaps);
|
||||
ConvertCaps8(caps9, pCaps);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "d3d8_interface.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
Logger Logger::s_instance("d3d8.log");
|
||||
|
||||
HRESULT CreateD3D8(IDirect3D8** ppDirect3D8) {
|
||||
|
@ -10,6 +11,7 @@ namespace dxvk {
|
|||
*ppDirect3D8 = ref(new D3D8Interface());
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
@ -17,29 +19,40 @@ extern "C" {
|
|||
DLLEXPORT HRESULT __stdcall ValidatePixelShader(
|
||||
const DWORD* pPixelShader,
|
||||
const D3DCAPS8* pCaps,
|
||||
BOOL errorReturn,
|
||||
BOOL ErrorReturn,
|
||||
char** pErrorString) {
|
||||
HRESULT res = S_OK;
|
||||
std::string errorMessage = "";
|
||||
|
||||
// ValidatePixelShader returns immediately in case of a NULL pPixelShader
|
||||
if (unlikely(pPixelShader == nullptr)) {
|
||||
errorMessage = "D3D8: ValidatePixelShader: Null pPixelShader";
|
||||
dxvk::Logger::warn("D3D8: ValidatePixelShader: Null pPixelShader");
|
||||
return E_FAIL;
|
||||
} else {
|
||||
uint32_t majorVersion = (pPixelShader[0] >> 8) & 0xff;
|
||||
uint32_t minorVersion = pPixelShader[0] & 0xff;
|
||||
const uint32_t majorVersion = D3DSHADER_VERSION_MAJOR(pPixelShader[0]);
|
||||
const uint32_t minorVersion = D3DSHADER_VERSION_MINOR(pPixelShader[0]);
|
||||
|
||||
if (unlikely(majorVersion != 1 || minorVersion > 4)) {
|
||||
errorMessage = dxvk::str::format("D3D8: ValidatePixelShader: Unsupported PS version ",
|
||||
majorVersion, ".", minorVersion);
|
||||
res = E_FAIL;
|
||||
} else if (unlikely(pCaps && pPixelShader[0] > pCaps->PixelShaderVersion)) {
|
||||
errorMessage = dxvk::str::format("D3D8: ValidatePixelShader: Caps: Unsupported PS version ",
|
||||
majorVersion, ".", minorVersion);
|
||||
res = E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t errorMessageSize = errorMessage.size() + 1;
|
||||
if (unlikely(res != S_OK)) {
|
||||
dxvk::Logger::warn(errorMessage);
|
||||
|
||||
if (!ErrorReturn)
|
||||
errorMessage = "";
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (pErrorString != nullptr && errorReturn) {
|
||||
if (pErrorString != nullptr) {
|
||||
const size_t errorMessageSize = errorMessage.size() + 1;
|
||||
// Wine tests call HeapFree() on the returned error string,
|
||||
// so the expectation is for it to be allocated on the heap.
|
||||
*pErrorString = (char*) HeapAlloc(GetProcessHeap(), 0, errorMessageSize);
|
||||
|
@ -48,41 +61,46 @@ extern "C" {
|
|||
}
|
||||
#endif
|
||||
|
||||
if (errorMessageSize > 1) {
|
||||
dxvk::Logger::warn(errorMessage);
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
return res;
|
||||
}
|
||||
|
||||
DLLEXPORT HRESULT __stdcall ValidateVertexShader(
|
||||
const DWORD* pVertexShader,
|
||||
const DWORD* pVertexDecl,
|
||||
const D3DCAPS8* pCaps,
|
||||
BOOL errorReturn,
|
||||
BOOL ErrorReturn,
|
||||
char** pErrorString) {
|
||||
HRESULT res = S_OK;
|
||||
std::string errorMessage = "";
|
||||
|
||||
if (unlikely(pVertexShader == nullptr)) {
|
||||
errorMessage = "D3D8: ValidateVertexShader: Null pVertexShader";
|
||||
res = E_FAIL;
|
||||
} else {
|
||||
uint32_t majorVersion = (pVertexShader[0] >> 8) & 0xff;
|
||||
uint32_t minorVersion = pVertexShader[0] & 0xff;
|
||||
const uint32_t majorVersion = D3DSHADER_VERSION_MAJOR(pVertexShader[0]);
|
||||
const uint32_t minorVersion = D3DSHADER_VERSION_MINOR(pVertexShader[0]);
|
||||
|
||||
if (unlikely(majorVersion != 1 || minorVersion > 1)) {
|
||||
errorMessage = dxvk::str::format("D3D8: ValidateVertexShader: Unsupported VS version ",
|
||||
majorVersion, ".", minorVersion);
|
||||
res = E_FAIL;
|
||||
} else if (unlikely(pCaps && pVertexShader[0] > pCaps->VertexShaderVersion)) {
|
||||
errorMessage = dxvk::str::format("D3D8: ValidateVertexShader: Caps: Unsupported VS version ",
|
||||
majorVersion, ".", minorVersion);
|
||||
res = E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t errorMessageSize = errorMessage.size() + 1;
|
||||
if (unlikely(res != S_OK)) {
|
||||
dxvk::Logger::warn(errorMessage);
|
||||
|
||||
if (!ErrorReturn)
|
||||
errorMessage = "";
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (pErrorString != nullptr && errorReturn) {
|
||||
if (pErrorString != nullptr) {
|
||||
const size_t errorMessageSize = errorMessage.size() + 1;
|
||||
// Wine tests call HeapFree() on the returned error string,
|
||||
// so the expectation is for it to be allocated on the heap.
|
||||
*pErrorString = (char*) HeapAlloc(GetProcessHeap(), 0, errorMessageSize);
|
||||
|
@ -91,12 +109,7 @@ extern "C" {
|
|||
}
|
||||
#endif
|
||||
|
||||
if (errorMessageSize > 1) {
|
||||
dxvk::Logger::warn(errorMessage);
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
return res;
|
||||
}
|
||||
|
||||
DLLEXPORT void __stdcall DebugSetMute() {}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "d3d8_options.h"
|
||||
|
||||
#include "../d3d9/d3d9_bridge.h"
|
||||
#include "../util/config/config.h"
|
||||
#include "../util/util_string.h"
|
||||
|
@ -6,8 +7,9 @@
|
|||
#include <charconv>
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
static inline uint32_t parseDword(std::string_view str) {
|
||||
uint32_t value = UINT32_MAX;
|
||||
uint32_t value = std::numeric_limits<uint32_t>::max();
|
||||
std::from_chars(str.data(), str.data() + str.size(), value);
|
||||
return value;
|
||||
}
|
||||
|
@ -50,6 +52,6 @@ namespace dxvk {
|
|||
|
||||
forceVsDecl.emplace_back(D3DVSDE_REGISTER(reg), D3DVSDT_TYPE(type));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "../util/config/config.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
struct D3D8Options {
|
||||
|
||||
/// Some games rely on undefined behavior by using undeclared vertex shader inputs.
|
||||
|
@ -41,16 +42,24 @@ namespace dxvk {
|
|||
/// it was brought in line with standard D3D9 behavior.
|
||||
bool forceLegacyDiscard = false;
|
||||
|
||||
/// Splinter Cell expects shadow map texture coordinates to be perspective divided
|
||||
/// even though D3DTTFF_PROJECTED is never set for any texture coordinates. This flag
|
||||
/// forces that flag for the necessary stages when a depth texture is bound to slot 0
|
||||
bool shadowPerspectiveDivide = false;
|
||||
|
||||
D3D8Options() {}
|
||||
|
||||
D3D8Options(const Config& config) {
|
||||
auto forceVsDeclStr = config.getOption<std::string>("d3d8.forceVsDecl", "");
|
||||
batching = config.getOption<bool> ("d3d8.batching", batching);
|
||||
placeP8InScratch = config.getOption<bool> ("d3d8.placeP8InScratch", placeP8InScratch);
|
||||
forceLegacyDiscard = config.getOption<bool> ("d3d8.forceLegacyDiscard", forceLegacyDiscard);
|
||||
shadowPerspectiveDivide = config.getOption<bool> ("d3d8.shadowPerspectiveDivide", shadowPerspectiveDivide);
|
||||
|
||||
parseVsDecl(forceVsDeclStr);
|
||||
}
|
||||
|
||||
void parseVsDecl(const std::string& decl);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -18,8 +18,9 @@ namespace dxvk {
|
|||
|
||||
public:
|
||||
|
||||
D3D8Resource(D3D8Device* pDevice, Com<D3D9>&& Object)
|
||||
D3D8Resource(D3D8Device* pDevice, D3DPOOL Pool, Com<D3D9>&& Object)
|
||||
: D3D8DeviceChild<D3D9, D3D8>(pDevice, std::move(Object))
|
||||
, m_pool ( Pool )
|
||||
, m_priority ( 0 ) { }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetPrivateData(
|
||||
|
@ -79,11 +80,16 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE SetPriority(DWORD PriorityNew) {
|
||||
// Priority can only be set for D3DPOOL_MANAGED resources
|
||||
if (likely(m_pool == D3DPOOL_MANAGED)) {
|
||||
DWORD oldPriority = m_priority;
|
||||
m_priority = PriorityNew;
|
||||
return oldPriority;
|
||||
}
|
||||
|
||||
return m_priority;
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE GetPriority() {
|
||||
return m_priority;
|
||||
}
|
||||
|
@ -99,6 +105,7 @@ namespace dxvk {
|
|||
|
||||
protected:
|
||||
|
||||
const D3DPOOL m_pool;
|
||||
DWORD m_priority;
|
||||
|
||||
private:
|
||||
|
@ -107,5 +114,4 @@ namespace dxvk {
|
|||
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
#include "d3d8_shader.h"
|
||||
|
||||
#define VSD_SHIFT_MASK(token, field) ((token & field ## MASK) >> field ## SHIFT)
|
||||
|
@ -313,8 +312,10 @@ namespace dxvk {
|
|||
// Instructions
|
||||
if ((token & VS_BIT_PARAM) == 0) {
|
||||
|
||||
// RSQ swizzle fixup
|
||||
if (opcode == D3DSIO_RSQ) {
|
||||
// Swizzle fixup for opcodes that require explicit use of a replicate swizzle.
|
||||
if (opcode == D3DSIO_RSQ || opcode == D3DSIO_RCP
|
||||
|| opcode == D3DSIO_EXP || opcode == D3DSIO_LOG
|
||||
|| opcode == D3DSIO_EXPP || opcode == D3DSIO_LOGP) {
|
||||
tokens.push_back(token); // instr
|
||||
tokens.push_back(token = pFunction[i++]); // dest
|
||||
token = pFunction[i++]; // src0
|
||||
|
@ -333,4 +334,5 @@ namespace dxvk {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,53 @@
|
|||
#include "d3d8_device.h"
|
||||
#include "d3d8_state_block.h"
|
||||
|
||||
HRESULT dxvk::D3D8StateBlock::Capture() {
|
||||
namespace dxvk {
|
||||
|
||||
D3D8StateBlock::D3D8StateBlock(
|
||||
D3D8Device* pDevice,
|
||||
D3DSTATEBLOCKTYPE Type,
|
||||
Com<d3d9::IDirect3DStateBlock9>&& pStateBlock)
|
||||
: m_device(pDevice)
|
||||
, m_stateBlock(std::move(pStateBlock))
|
||||
, m_type(Type)
|
||||
, m_isSWVP(pDevice->GetD3D9()->GetSoftwareVertexProcessing()) {
|
||||
if (Type == D3DSBT_VERTEXSTATE || Type == D3DSBT_ALL) {
|
||||
// Lights, D3DTSS_TEXCOORDINDEX and D3DTSS_TEXTURETRANSFORMFLAGS,
|
||||
// vertex shader, VS constants, and various render states.
|
||||
m_capture.vs = true;
|
||||
}
|
||||
|
||||
if (Type == D3DSBT_PIXELSTATE || Type == D3DSBT_ALL) {
|
||||
// Pixel shader, PS constants, and various RS/TSS states.
|
||||
m_capture.ps = true;
|
||||
}
|
||||
|
||||
if (Type == D3DSBT_ALL) {
|
||||
m_capture.indices = true;
|
||||
m_capture.swvp = true;
|
||||
m_capture.textures.setAll();
|
||||
m_capture.streams.setAll();
|
||||
}
|
||||
|
||||
m_textures.fill(nullptr);
|
||||
m_streams.fill(D3D8VBOP());
|
||||
}
|
||||
|
||||
// Construct a state block without a D3D9 object
|
||||
D3D8StateBlock::D3D8StateBlock(D3D8Device* pDevice)
|
||||
: D3D8StateBlock(pDevice, D3DSTATEBLOCKTYPE(0), nullptr) {
|
||||
}
|
||||
|
||||
// Attach a D3D9 object to a state block that doesn't have one yet
|
||||
void D3D8StateBlock::SetD3D9(Com<d3d9::IDirect3DStateBlock9>&& pStateBlock) {
|
||||
if (likely(m_stateBlock == nullptr)) {
|
||||
m_stateBlock = std::move(pStateBlock);
|
||||
} else {
|
||||
Logger::err("D3D8StateBlock::SetD3D9: m_stateBlock has already been initialized");
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT D3D8StateBlock::Capture() {
|
||||
if (unlikely(m_stateBlock == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
|
@ -29,9 +75,9 @@ HRESULT dxvk::D3D8StateBlock::Capture() {
|
|||
m_device->GetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, (DWORD*)&m_isSWVP);
|
||||
|
||||
return m_stateBlock->Capture();
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT dxvk::D3D8StateBlock::Apply() {
|
||||
HRESULT D3D8StateBlock::Apply() {
|
||||
if (unlikely(m_stateBlock == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
|
@ -58,4 +104,6 @@ HRESULT dxvk::D3D8StateBlock::Apply() {
|
|||
m_device->SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, m_isSWVP);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,47 +39,11 @@ namespace dxvk {
|
|||
D3D8StateBlock(
|
||||
D3D8Device* pDevice,
|
||||
D3DSTATEBLOCKTYPE Type,
|
||||
Com<d3d9::IDirect3DStateBlock9>&& pStateBlock)
|
||||
: m_device(pDevice)
|
||||
, m_stateBlock(std::move(pStateBlock))
|
||||
, m_type(Type) {
|
||||
if (Type == D3DSBT_VERTEXSTATE || Type == D3DSBT_ALL) {
|
||||
// Lights, D3DTSS_TEXCOORDINDEX and D3DTSS_TEXTURETRANSFORMFLAGS,
|
||||
// vertex shader, VS constants, and various render states.
|
||||
m_capture.vs = true;
|
||||
}
|
||||
Com<d3d9::IDirect3DStateBlock9>&& pStateBlock);
|
||||
|
||||
if (Type == D3DSBT_PIXELSTATE || Type == D3DSBT_ALL) {
|
||||
// Pixel shader, PS constants, and various RS/TSS states.
|
||||
m_capture.ps = true;
|
||||
}
|
||||
D3D8StateBlock(D3D8Device* pDevice);
|
||||
|
||||
if (Type == D3DSBT_ALL) {
|
||||
m_capture.indices = true;
|
||||
m_capture.swvp = true;
|
||||
m_capture.textures.setAll();
|
||||
m_capture.streams.setAll();
|
||||
}
|
||||
|
||||
m_textures.fill(nullptr);
|
||||
m_streams.fill(D3D8VBOP());
|
||||
}
|
||||
|
||||
~D3D8StateBlock() {}
|
||||
|
||||
// Construct a state block without a D3D9 object
|
||||
D3D8StateBlock(D3D8Device* pDevice)
|
||||
: D3D8StateBlock(pDevice, D3DSTATEBLOCKTYPE(0), nullptr) {
|
||||
}
|
||||
|
||||
// Attach a D3D9 object to a state block that doesn't have one yet
|
||||
void SetD3D9(Com<d3d9::IDirect3DStateBlock9>&& pStateBlock) {
|
||||
if (likely(m_stateBlock == nullptr)) {
|
||||
m_stateBlock = std::move(pStateBlock);
|
||||
} else {
|
||||
Logger::err("D3D8StateBlock::SetD3D9: m_stateBlock has already been initialized");
|
||||
}
|
||||
}
|
||||
void SetD3D9(Com<d3d9::IDirect3DStateBlock9>&& pStateBlock);
|
||||
|
||||
HRESULT Capture();
|
||||
|
||||
|
@ -126,6 +90,7 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
private:
|
||||
|
||||
D3D8Device* m_device;
|
||||
Com<d3d9::IDirect3DStateBlock9> m_stateBlock;
|
||||
D3DSTATEBLOCKTYPE m_type;
|
||||
|
@ -135,21 +100,21 @@ namespace dxvk {
|
|||
UINT stride = 0;
|
||||
};
|
||||
|
||||
private: // State Data //
|
||||
// State Data //
|
||||
|
||||
D3D8StateCapture m_capture;
|
||||
|
||||
DWORD m_vertexShader; // vs
|
||||
DWORD m_pixelShader; // ps
|
||||
DWORD m_vertexShader = 0;
|
||||
DWORD m_pixelShader = 0;
|
||||
|
||||
std::array<IDirect3DBaseTexture8*, d8caps::MAX_TEXTURE_STAGES> m_textures; // textures
|
||||
std::array<D3D8VBOP, d8caps::MAX_STREAMS> m_streams; // stream data
|
||||
std::array<IDirect3DBaseTexture8*, d8caps::MAX_TEXTURE_STAGES> m_textures;
|
||||
std::array<D3D8VBOP, d8caps::MAX_STREAMS> m_streams;
|
||||
|
||||
IDirect3DIndexBuffer8* m_indices = nullptr; // indices
|
||||
UINT m_baseVertexIndex; // indices
|
||||
IDirect3DIndexBuffer8* m_indices = nullptr;
|
||||
UINT m_baseVertexIndex = 0;
|
||||
|
||||
bool m_isSWVP; // D3DRS_SOFTWAREVERTEXPROCESSING
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -16,15 +16,13 @@ namespace dxvk {
|
|||
|
||||
D3D8Subresource(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
Com<D3D9>&& Object,
|
||||
IDirect3DBaseTexture8* pBaseTexture)
|
||||
: Resource(pDevice, std::move(Object)),
|
||||
: Resource(pDevice, Pool, std::move(Object)),
|
||||
m_container(pBaseTexture) {
|
||||
}
|
||||
|
||||
~D3D8Subresource() {
|
||||
}
|
||||
|
||||
// Refing subresources implicitly refs the container texture,
|
||||
ULONG STDMETHODCALLTYPE AddRef() final {
|
||||
if (m_container != nullptr)
|
||||
|
@ -56,6 +54,7 @@ namespace dxvk {
|
|||
protected:
|
||||
|
||||
IDirect3DBaseTexture8* m_container;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -1,9 +1,60 @@
|
|||
|
||||
#include "d3d8_surface.h"
|
||||
#include "d3d8_device.h"
|
||||
|
||||
#include "d3d8_d3d9_util.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
D3D8Surface::D3D8Surface(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
IDirect3DBaseTexture8* pTexture,
|
||||
Com<d3d9::IDirect3DSurface9>&& pSurface)
|
||||
: D3D8SurfaceBase (pDevice, Pool, std::move(pSurface), pTexture) {
|
||||
}
|
||||
|
||||
// A surface does not need to be attached to a texture
|
||||
D3D8Surface::D3D8Surface(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
Com<d3d9::IDirect3DSurface9>&& pSurface)
|
||||
: D3D8Surface (pDevice, Pool, nullptr, std::move(pSurface)) {
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Surface::GetDesc(D3DSURFACE_DESC* pDesc) {
|
||||
if (unlikely(pDesc == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
d3d9::D3DSURFACE_DESC desc;
|
||||
HRESULT res = GetD3D9()->GetDesc(&desc);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
ConvertSurfaceDesc8(&desc, pDesc);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Surface::LockRect(
|
||||
D3DLOCKED_RECT* pLockedRect,
|
||||
CONST RECT* pRect,
|
||||
DWORD Flags) {
|
||||
return GetD3D9()->LockRect((d3d9::D3DLOCKED_RECT*)pLockedRect, pRect, Flags);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Surface::UnlockRect() {
|
||||
return GetD3D9()->UnlockRect();
|
||||
}
|
||||
|
||||
// TODO: Consider creating only one texture to
|
||||
// encompass all surface levels of a texture.
|
||||
Com<d3d9::IDirect3DSurface9> D3D8Surface::GetBlitImage() {
|
||||
if (unlikely(m_blitImage == nullptr)) {
|
||||
m_blitImage = CreateBlitImage();
|
||||
}
|
||||
|
||||
return m_blitImage;
|
||||
}
|
||||
|
||||
Com<d3d9::IDirect3DSurface9> D3D8Surface::CreateBlitImage() {
|
||||
d3d9::D3DSURFACE_DESC desc;
|
||||
GetD3D9()->GetDesc(&desc);
|
||||
|
@ -23,4 +74,5 @@ namespace dxvk {
|
|||
|
||||
return image;
|
||||
}
|
||||
|
||||
}
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
#include "d3d8_include.h"
|
||||
#include "d3d8_subresource.h"
|
||||
#include "d3d8_d3d9_util.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
// TODO: all inherited methods in D3D8Surface should be final like in d9vk
|
||||
|
||||
// Note: IDirect3DSurface8 does not actually inherit from IDirect3DResource8,
|
||||
// however it does expose serveral of the methods typically found part of
|
||||
// IDirect3DResource8, such as Set/Get/FreePrivateData, so model it as such.
|
||||
using D3D8SurfaceBase = D3D8Subresource<d3d9::IDirect3DSurface9, IDirect3DSurface8>;
|
||||
class D3D8Surface final : public D3D8SurfaceBase {
|
||||
|
||||
|
@ -15,73 +15,36 @@ namespace dxvk {
|
|||
|
||||
D3D8Surface(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
IDirect3DBaseTexture8* pTexture,
|
||||
Com<d3d9::IDirect3DSurface9>&& pSurface)
|
||||
: D3D8SurfaceBase (pDevice, std::move(pSurface), pTexture) {
|
||||
}
|
||||
Com<d3d9::IDirect3DSurface9>&& pSurface);
|
||||
|
||||
// A surface does not need to be attached to a texture
|
||||
D3D8Surface(
|
||||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DSurface9>&& pSurface)
|
||||
: D3D8Surface (pDevice, nullptr, std::move(pSurface)) {
|
||||
}
|
||||
const D3DPOOL Pool,
|
||||
Com<d3d9::IDirect3DSurface9>&& pSurface);
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() {
|
||||
return D3DRESOURCETYPE(GetD3D9()->GetType());
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE GetDesc(D3DSURFACE_DESC* pDesc) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDesc(D3DSURFACE_DESC* pDesc) {
|
||||
if (unlikely(pDesc == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
HRESULT STDMETHODCALLTYPE LockRect(
|
||||
D3DLOCKED_RECT* pLockedRect,
|
||||
CONST RECT* pRect,
|
||||
DWORD Flags) final;
|
||||
|
||||
d3d9::D3DSURFACE_DESC desc;
|
||||
HRESULT res = GetD3D9()->GetDesc(&desc);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
ConvertSurfaceDesc8(&desc, pDesc);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE LockRect(D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
|
||||
return GetD3D9()->LockRect((d3d9::D3DLOCKED_RECT*)pLockedRect, pRect, Flags);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE UnlockRect() {
|
||||
return GetD3D9()->UnlockRect();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDC(HDC* phDC) {
|
||||
return GetD3D9()->GetDC(phDC);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE ReleaseDC(HDC hDC) {
|
||||
return GetD3D9()->ReleaseDC(hDC);
|
||||
}
|
||||
|
||||
public:
|
||||
HRESULT STDMETHODCALLTYPE UnlockRect() final;
|
||||
|
||||
/**
|
||||
* \brief Allocate or reuse an image of the same size
|
||||
* as this texture for performing blit into system mem.
|
||||
*
|
||||
* TODO: Consider creating only one texture to
|
||||
* encompass all surface levels of a texture.
|
||||
*/
|
||||
Com<d3d9::IDirect3DSurface9> GetBlitImage() {
|
||||
if (unlikely(m_blitImage == nullptr)) {
|
||||
m_blitImage = CreateBlitImage();
|
||||
}
|
||||
|
||||
return m_blitImage;
|
||||
}
|
||||
|
||||
Com<d3d9::IDirect3DSurface9> GetBlitImage();
|
||||
|
||||
private:
|
||||
|
||||
Com<d3d9::IDirect3DSurface9> CreateBlitImage();
|
||||
|
||||
Com<d3d9::IDirect3DSurface9> m_blitImage = nullptr;
|
||||
Com<d3d9::IDirect3DSurface9> m_blitImage;
|
||||
|
||||
};
|
||||
|
||||
}
|
41
src/d3d8/d3d8_swapchain.cpp
Normal file
41
src/d3d8/d3d8_swapchain.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include "d3d8_swapchain.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
D3D8SwapChain::D3D8SwapChain(
|
||||
D3D8Device* pDevice,
|
||||
D3DPRESENT_PARAMETERS* pPresentationParameters,
|
||||
Com<d3d9::IDirect3DSwapChain9>&& pSwapChain)
|
||||
: D3D8SwapChainBase(pDevice, std::move(pSwapChain)) {
|
||||
m_backBuffers.resize(pPresentationParameters->BackBufferCount);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8SwapChain::Present(const RECT *src, const RECT *dst, HWND hWnd, const RGNDATA *dirtyRegion) {
|
||||
return GetD3D9()->Present(src, dst, hWnd, dirtyRegion, 0);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8SwapChain::GetBackBuffer(
|
||||
UINT BackBuffer,
|
||||
D3DBACKBUFFER_TYPE Type,
|
||||
IDirect3DSurface8** ppBackBuffer) {
|
||||
if (unlikely(ppBackBuffer == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// Same logic as in D3D8Device::GetBackBuffer
|
||||
if (BackBuffer >= m_backBuffers.size() || m_backBuffers[BackBuffer] == nullptr) {
|
||||
Com<d3d9::IDirect3DSurface9> pSurface9;
|
||||
HRESULT res = GetD3D9()->GetBackBuffer(BackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9);
|
||||
|
||||
if (likely(SUCCEEDED(res))) {
|
||||
m_backBuffers[BackBuffer] = new D3D8Surface(GetParent(), D3DPOOL_DEFAULT, std::move(pSurface9));
|
||||
*ppBackBuffer = m_backBuffers[BackBuffer].ref();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
*ppBackBuffer = m_backBuffers[BackBuffer].ref();
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include "d3d8_device_child.h"
|
||||
#include "d3d8_surface.h"
|
||||
#include "d3d8_d3d9_util.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
|
@ -14,37 +13,17 @@ namespace dxvk {
|
|||
D3D8SwapChain(
|
||||
D3D8Device* pDevice,
|
||||
D3DPRESENT_PARAMETERS* pPresentationParameters,
|
||||
Com<d3d9::IDirect3DSwapChain9>&& pSwapChain)
|
||||
: D3D8SwapChainBase(pDevice, std::move(pSwapChain)) {
|
||||
m_backBuffers.resize(pPresentationParameters->BackBufferCount);
|
||||
}
|
||||
Com<d3d9::IDirect3DSwapChain9>&& pSwapChain);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Present(const RECT *src, const RECT *dst, HWND hWnd, const RGNDATA *dirtyRegion) final {
|
||||
return GetD3D9()->Present(src, dst, hWnd, dirtyRegion, 0);
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE Present(const RECT *src, const RECT *dst, HWND hWnd, const RGNDATA *dirtyRegion) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetBackBuffer(UINT BackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface8** ppBackBuffer) final {
|
||||
if (unlikely(ppBackBuffer == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// Same logic as in D3D8Device::GetBackBuffer
|
||||
if (BackBuffer >= m_backBuffers.size() || m_backBuffers[BackBuffer] == nullptr) {
|
||||
Com<d3d9::IDirect3DSurface9> pSurface9;
|
||||
HRESULT res = GetD3D9()->GetBackBuffer(BackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9);
|
||||
|
||||
if (likely(SUCCEEDED(res))) {
|
||||
m_backBuffers[BackBuffer] = new D3D8Surface(GetParent(), std::move(pSurface9));
|
||||
*ppBackBuffer = m_backBuffers[BackBuffer].ref();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
*ppBackBuffer = m_backBuffers[BackBuffer].ref();
|
||||
return D3D_OK;
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE GetBackBuffer(
|
||||
UINT BackBuffer,
|
||||
D3DBACKBUFFER_TYPE Type,
|
||||
IDirect3DSurface8** ppBackBuffer) final;
|
||||
|
||||
private:
|
||||
|
||||
std::vector<Com<D3D8Surface, false>> m_backBuffers;
|
||||
|
||||
};
|
||||
|
|
152
src/d3d8/d3d8_texture.cpp
Normal file
152
src/d3d8/d3d8_texture.cpp
Normal file
|
@ -0,0 +1,152 @@
|
|||
#include "d3d8_texture.h"
|
||||
|
||||
#include "d3d8_d3d9_util.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
// D3D8Texture2D
|
||||
|
||||
D3D8Texture2D::D3D8Texture2D(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
Com<d3d9::IDirect3DTexture9>&& pTexture)
|
||||
: D3D8Texture2DBase(pDevice, Pool, std::move(pTexture), pTexture->GetLevelCount()) {
|
||||
}
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE D3D8Texture2D::GetType() { return D3DRTYPE_TEXTURE; }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Texture2D::GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) {
|
||||
if (unlikely(pDesc == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
d3d9::D3DSURFACE_DESC surf;
|
||||
HRESULT res = GetD3D9()->GetLevelDesc(Level, &surf);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
ConvertSurfaceDesc8(&surf, pDesc);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Texture2D::GetSurfaceLevel(UINT Level, IDirect3DSurface8** ppSurfaceLevel) {
|
||||
return GetSubresource(Level, ppSurfaceLevel);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Texture2D::LockRect(
|
||||
UINT Level,
|
||||
D3DLOCKED_RECT* pLockedRect,
|
||||
CONST RECT* pRect,
|
||||
DWORD Flags) {
|
||||
return GetD3D9()->LockRect(Level, reinterpret_cast<d3d9::D3DLOCKED_RECT*>(pLockedRect), pRect, Flags);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Texture2D::UnlockRect(UINT Level) {
|
||||
return GetD3D9()->UnlockRect(Level);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Texture2D::AddDirtyRect(CONST RECT* pDirtyRect) {
|
||||
return GetD3D9()->AddDirtyRect(pDirtyRect);
|
||||
}
|
||||
|
||||
// D3D8Texture3D
|
||||
|
||||
D3D8Texture3D::D3D8Texture3D(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
Com<d3d9::IDirect3DVolumeTexture9>&& pVolumeTexture)
|
||||
: D3D8Texture3DBase(pDevice, Pool, std::move(pVolumeTexture), pVolumeTexture->GetLevelCount()) {}
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE D3D8Texture3D::GetType() { return D3DRTYPE_VOLUMETEXTURE; }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Texture3D::GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc) {
|
||||
if (unlikely(pDesc == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
d3d9::D3DVOLUME_DESC vol;
|
||||
HRESULT res = GetD3D9()->GetLevelDesc(Level, &vol);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
ConvertVolumeDesc8(&vol, pDesc);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Texture3D::GetVolumeLevel(UINT Level, IDirect3DVolume8** ppVolumeLevel) {
|
||||
return GetSubresource(Level, ppVolumeLevel);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Texture3D::LockBox(
|
||||
UINT Level,
|
||||
D3DLOCKED_BOX* pLockedBox,
|
||||
CONST D3DBOX* pBox,
|
||||
DWORD Flags) {
|
||||
return GetD3D9()->LockBox(
|
||||
Level,
|
||||
reinterpret_cast<d3d9::D3DLOCKED_BOX*>(pLockedBox),
|
||||
reinterpret_cast<const d3d9::D3DBOX*>(pBox),
|
||||
Flags
|
||||
);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Texture3D::UnlockBox(UINT Level) {
|
||||
return GetD3D9()->UnlockBox(Level);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Texture3D::AddDirtyBox(CONST D3DBOX* pDirtyBox) {
|
||||
return GetD3D9()->AddDirtyBox(reinterpret_cast<const d3d9::D3DBOX*>(pDirtyBox));
|
||||
}
|
||||
|
||||
// D3D8TextureCube
|
||||
|
||||
D3D8TextureCube::D3D8TextureCube(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
Com<d3d9::IDirect3DCubeTexture9>&& pTexture)
|
||||
: D3D8TextureCubeBase(pDevice, Pool, std::move(pTexture), pTexture->GetLevelCount() * CUBE_FACES) {
|
||||
}
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE D3D8TextureCube::GetType() { return D3DRTYPE_CUBETEXTURE; }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8TextureCube::GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) {
|
||||
if (unlikely(pDesc == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
d3d9::D3DSURFACE_DESC surf;
|
||||
HRESULT res = GetD3D9()->GetLevelDesc(Level, &surf);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
ConvertSurfaceDesc8(&surf, pDesc);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8TextureCube::GetCubeMapSurface(
|
||||
D3DCUBEMAP_FACES Face,
|
||||
UINT Level,
|
||||
IDirect3DSurface8** ppSurfaceLevel) {
|
||||
return GetSubresource((Level * CUBE_FACES) + Face, ppSurfaceLevel);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8TextureCube::LockRect(
|
||||
D3DCUBEMAP_FACES Face,
|
||||
UINT Level,
|
||||
D3DLOCKED_RECT* pLockedRect,
|
||||
const RECT* pRect,
|
||||
DWORD Flags) {
|
||||
return GetD3D9()->LockRect(
|
||||
d3d9::D3DCUBEMAP_FACES(Face),
|
||||
Level,
|
||||
reinterpret_cast<d3d9::D3DLOCKED_RECT*>(pLockedRect),
|
||||
pRect,
|
||||
Flags);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8TextureCube::UnlockRect(D3DCUBEMAP_FACES Face, UINT Level) {
|
||||
return GetD3D9()->UnlockRect(d3d9::D3DCUBEMAP_FACES(Face), Level);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8TextureCube::AddDirtyRect(D3DCUBEMAP_FACES Face, const RECT* pDirtyRect) {
|
||||
return GetD3D9()->AddDirtyRect(d3d9::D3DCUBEMAP_FACES(Face), pDirtyRect);
|
||||
}
|
||||
|
||||
}
|
|
@ -4,8 +4,6 @@
|
|||
#include "d3d8_surface.h"
|
||||
#include "d3d8_volume.h"
|
||||
|
||||
#include "d3d8_d3d9_util.h"
|
||||
|
||||
#include <vector>
|
||||
#include <new>
|
||||
|
||||
|
@ -23,9 +21,10 @@ namespace dxvk {
|
|||
|
||||
D3D8BaseTexture(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
Com<D3D9>&& pBaseTexture,
|
||||
UINT SubresourceCount)
|
||||
: D3D8Resource<D3D9, D3D8> ( pDevice, std::move(pBaseTexture) ) {
|
||||
: D3D8Resource<D3D9, D3D8> ( pDevice, Pool, std::move(pBaseTexture) ) {
|
||||
m_subresources.resize(SubresourceCount, nullptr);
|
||||
}
|
||||
|
||||
|
@ -76,7 +75,7 @@ namespace dxvk {
|
|||
Com<SubresourceType9> subresource = LookupSubresource(Index);
|
||||
|
||||
// Cache the subresource
|
||||
m_subresources[Index] = new SubresourceType(this->m_parent, this, std::move(subresource));
|
||||
m_subresources[Index] = new SubresourceType(this->m_parent, this->m_pool, this, std::move(subresource));
|
||||
} catch (HRESULT res) {
|
||||
return res;
|
||||
}
|
||||
|
@ -114,40 +113,24 @@ namespace dxvk {
|
|||
|
||||
D3D8Texture2D(
|
||||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DTexture9>&& pTexture)
|
||||
: D3D8Texture2DBase(pDevice, std::move(pTexture), pTexture->GetLevelCount()) {
|
||||
}
|
||||
const D3DPOOL Pool,
|
||||
Com<d3d9::IDirect3DTexture9>&& pTexture);
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_TEXTURE; }
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) {
|
||||
if (unlikely(pDesc == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc);
|
||||
|
||||
d3d9::D3DSURFACE_DESC surf;
|
||||
HRESULT res = GetD3D9()->GetLevelDesc(Level, &surf);
|
||||
HRESULT STDMETHODCALLTYPE GetSurfaceLevel(UINT Level, IDirect3DSurface8** ppSurfaceLevel);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
ConvertSurfaceDesc8(&surf, pDesc);
|
||||
HRESULT STDMETHODCALLTYPE LockRect(
|
||||
UINT Level,
|
||||
D3DLOCKED_RECT* pLockedRect,
|
||||
CONST RECT* pRect,
|
||||
DWORD Flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE UnlockRect(UINT Level);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetSurfaceLevel(UINT Level, IDirect3DSurface8** ppSurfaceLevel) {
|
||||
return GetSubresource(Level, ppSurfaceLevel);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE LockRect(UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
|
||||
return GetD3D9()->LockRect(Level, reinterpret_cast<d3d9::D3DLOCKED_RECT*>(pLockedRect), pRect, Flags);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE UnlockRect(UINT Level) {
|
||||
return GetD3D9()->UnlockRect(Level);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AddDirtyRect(CONST RECT* pDirtyRect) {
|
||||
return GetD3D9()->AddDirtyRect(pDirtyRect);
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE AddDirtyRect(CONST RECT* pDirtyRect);
|
||||
|
||||
};
|
||||
|
||||
|
@ -158,44 +141,24 @@ namespace dxvk {
|
|||
|
||||
D3D8Texture3D(
|
||||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DVolumeTexture9>&& pVolumeTexture)
|
||||
: D3D8Texture3DBase(pDevice, std::move(pVolumeTexture), pVolumeTexture->GetLevelCount()) {}
|
||||
const D3DPOOL Pool,
|
||||
Com<d3d9::IDirect3DVolumeTexture9>&& pVolumeTexture);
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_VOLUMETEXTURE; }
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc) {
|
||||
if (unlikely(pDesc == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc);
|
||||
|
||||
d3d9::D3DVOLUME_DESC vol;
|
||||
HRESULT res = GetD3D9()->GetLevelDesc(Level, &vol);
|
||||
HRESULT STDMETHODCALLTYPE GetVolumeLevel(UINT Level, IDirect3DVolume8** ppVolumeLevel);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
ConvertVolumeDesc8(&vol, pDesc);
|
||||
HRESULT STDMETHODCALLTYPE LockBox(
|
||||
UINT Level,
|
||||
D3DLOCKED_BOX* pLockedBox,
|
||||
CONST D3DBOX* pBox,
|
||||
DWORD Flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE UnlockBox(UINT Level);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetVolumeLevel(UINT Level, IDirect3DVolume8** ppVolumeLevel) {
|
||||
return GetSubresource(Level, ppVolumeLevel);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE LockBox(UINT Level, D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) {
|
||||
return GetD3D9()->LockBox(
|
||||
Level,
|
||||
reinterpret_cast<d3d9::D3DLOCKED_BOX*>(pLockedBox),
|
||||
reinterpret_cast<const d3d9::D3DBOX*>(pBox),
|
||||
Flags
|
||||
);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE UnlockBox(UINT Level) {
|
||||
return GetD3D9()->UnlockBox(Level);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AddDirtyBox(CONST D3DBOX* pDirtyBox) {
|
||||
return GetD3D9()->AddDirtyBox(reinterpret_cast<const d3d9::D3DBOX*>(pDirtyBox));
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE AddDirtyBox(CONST D3DBOX* pDirtyBox);
|
||||
|
||||
};
|
||||
|
||||
|
@ -206,49 +169,29 @@ namespace dxvk {
|
|||
|
||||
D3D8TextureCube(
|
||||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DCubeTexture9>&& pTexture)
|
||||
: D3D8TextureCubeBase(pDevice, std::move(pTexture), pTexture->GetLevelCount() * CUBE_FACES) {
|
||||
}
|
||||
const D3DPOOL Pool,
|
||||
Com<d3d9::IDirect3DCubeTexture9>&& pTexture);
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_CUBETEXTURE; }
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) {
|
||||
if (unlikely(pDesc == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc);
|
||||
|
||||
d3d9::D3DSURFACE_DESC surf;
|
||||
HRESULT res = GetD3D9()->GetLevelDesc(Level, &surf);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
ConvertSurfaceDesc8(&surf, pDesc);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetCubeMapSurface(D3DCUBEMAP_FACES Face, UINT Level, IDirect3DSurface8** ppSurfaceLevel) {
|
||||
return GetSubresource((Level * CUBE_FACES) + Face, ppSurfaceLevel);
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE GetCubeMapSurface(
|
||||
D3DCUBEMAP_FACES Face,
|
||||
UINT Level,
|
||||
IDirect3DSurface8** ppSurfaceLevel);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE LockRect(
|
||||
D3DCUBEMAP_FACES Face,
|
||||
UINT Level,
|
||||
D3DLOCKED_RECT* pLockedRect,
|
||||
const RECT* pRect,
|
||||
DWORD Flags) {
|
||||
return GetD3D9()->LockRect(
|
||||
d3d9::D3DCUBEMAP_FACES(Face),
|
||||
Level,
|
||||
reinterpret_cast<d3d9::D3DLOCKED_RECT*>(pLockedRect),
|
||||
pRect,
|
||||
Flags);
|
||||
}
|
||||
DWORD Flags);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE UnlockRect(D3DCUBEMAP_FACES Face, UINT Level) {
|
||||
return GetD3D9()->UnlockRect(d3d9::D3DCUBEMAP_FACES(Face), Level);
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE UnlockRect(D3DCUBEMAP_FACES Face, UINT Level);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AddDirtyRect(D3DCUBEMAP_FACES Face, const RECT* pDirtyRect);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AddDirtyRect(D3DCUBEMAP_FACES Face, const RECT* pDirtyRect) {
|
||||
return GetD3D9()->AddDirtyRect(d3d9::D3DCUBEMAP_FACES(Face), pDirtyRect);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
40
src/d3d8/d3d8_volume.cpp
Normal file
40
src/d3d8/d3d8_volume.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include "d3d8_volume.h"
|
||||
|
||||
#include "d3d8_d3d9_util.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
D3D8Volume::D3D8Volume(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
IDirect3DVolumeTexture8* pTexture,
|
||||
Com<d3d9::IDirect3DVolume9>&& pVolume)
|
||||
: D3D8VolumeBase(pDevice, Pool, std::move(pVolume), pTexture) {
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Volume::GetDesc(D3DVOLUME_DESC* pDesc) {
|
||||
if (unlikely(pDesc == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
d3d9::D3DVOLUME_DESC desc;
|
||||
HRESULT res = GetD3D9()->GetDesc(&desc);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
ConvertVolumeDesc8(&desc, pDesc);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Volume::LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) {
|
||||
return GetD3D9()->LockBox(
|
||||
reinterpret_cast<d3d9::D3DLOCKED_BOX*>(pLockedBox),
|
||||
reinterpret_cast<const d3d9::D3DBOX*>(pBox),
|
||||
Flags
|
||||
);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Volume::UnlockBox() {
|
||||
return GetD3D9()->UnlockBox();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "d3d8_subresource.h"
|
||||
#include "d3d8_d3d9_util.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
|
@ -12,34 +11,15 @@ namespace dxvk {
|
|||
|
||||
D3D8Volume(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
IDirect3DVolumeTexture8* pTexture,
|
||||
Com<d3d9::IDirect3DVolume9>&& pVolume)
|
||||
: D3D8VolumeBase(pDevice, std::move(pVolume), pTexture) {}
|
||||
Com<d3d9::IDirect3DVolume9>&& pVolume);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDesc(D3DVOLUME_DESC* pDesc) {
|
||||
if (unlikely(pDesc == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
HRESULT STDMETHODCALLTYPE GetDesc(D3DVOLUME_DESC* pDesc);
|
||||
|
||||
d3d9::D3DVOLUME_DESC desc;
|
||||
HRESULT res = GetD3D9()->GetDesc(&desc);
|
||||
HRESULT STDMETHODCALLTYPE LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) final;
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
ConvertVolumeDesc8(&desc, pDesc);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) final {
|
||||
return GetD3D9()->LockBox(
|
||||
reinterpret_cast<d3d9::D3DLOCKED_BOX*>(pLockedBox),
|
||||
reinterpret_cast<const d3d9::D3DBOX*>(pBox),
|
||||
Flags
|
||||
);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE UnlockBox() final {
|
||||
return GetD3D9()->UnlockBox();
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE UnlockBox() final;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@ namespace dxvk {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
Com<D3D9> m_d3d9;
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
d3d8_res = wrc_generator.process('version.rc')
|
||||
|
||||
d3d8_src = [
|
||||
'd3d8_main.cpp',
|
||||
'd3d8_interface.cpp',
|
||||
'd3d8_buffer.cpp',
|
||||
'd3d8_device.cpp',
|
||||
'd3d8_interface.cpp',
|
||||
'd3d8_main.cpp',
|
||||
'd3d8_multithread.cpp',
|
||||
'd3d8_options.cpp',
|
||||
'd3d8_surface.cpp',
|
||||
'd3d8_shader.cpp',
|
||||
'd3d8_state_block.cpp',
|
||||
'd3d8_shader.cpp'
|
||||
'd3d8_surface.cpp',
|
||||
'd3d8_swapchain.cpp',
|
||||
'd3d8_texture.cpp',
|
||||
'd3d8_volume.cpp'
|
||||
]
|
||||
|
||||
d3d8_ld_args = []
|
||||
|
|
|
@ -573,17 +573,22 @@ namespace dxvk {
|
|||
// Max Stream Stride
|
||||
pCaps->MaxStreamStride = 508; // bytes
|
||||
|
||||
const uint32_t majorVersion = options.shaderModel;
|
||||
const uint32_t minorVersion = options.shaderModel != 1 ? 0 : 4;
|
||||
// Late fixed-function capable cards, such as the GeForce 4 MX series,
|
||||
// expose support for VS 1.1, while not advertising any PS support
|
||||
const uint32_t majorVersionVS = options.shaderModel == 0 ? 1 : options.shaderModel;
|
||||
const uint32_t majorVersionPS = options.shaderModel;
|
||||
// Max supported SM1 is VS 1.1 and PS 1.4
|
||||
const uint32_t minorVersionVS = majorVersionVS != 1 ? 0 : 1;
|
||||
const uint32_t minorVersionPS = majorVersionPS != 1 ? 0 : 4;
|
||||
|
||||
// Shader Versions
|
||||
pCaps->VertexShaderVersion = D3DVS_VERSION(majorVersion, minorVersion);
|
||||
pCaps->PixelShaderVersion = D3DPS_VERSION(majorVersion, minorVersion);
|
||||
pCaps->VertexShaderVersion = D3DVS_VERSION(majorVersionVS, minorVersionVS);
|
||||
pCaps->PixelShaderVersion = D3DPS_VERSION(majorVersionPS, minorVersionPS);
|
||||
|
||||
// Max Vertex Shader Const
|
||||
pCaps->MaxVertexShaderConst = MaxFloatConstantsVS;
|
||||
// Max PS1 Value
|
||||
pCaps->PixelShader1xMaxValue = FLT_MAX;
|
||||
pCaps->PixelShader1xMaxValue = options.shaderModel > 0 ? std::numeric_limits<float>::max() : 0.0f;
|
||||
// Dev Caps 2
|
||||
pCaps->DevCaps2 = D3DDEVCAPS2_STREAMOFFSET
|
||||
/* | D3DDEVCAPS2_DMAPNPATCH */
|
||||
|
@ -630,24 +635,38 @@ namespace dxvk {
|
|||
/* | D3DPTFILTERCAPS_MAGFPYRAMIDALQUAD */
|
||||
/* | D3DPTFILTERCAPS_MAGFGAUSSIANQUAD */;
|
||||
|
||||
// Not too bothered about doing these longhand
|
||||
// We should match whatever my AMD hardware reports here
|
||||
// methinks for the best chance of stuff working.
|
||||
pCaps->VS20Caps.Caps = 1;
|
||||
pCaps->VS20Caps.DynamicFlowControlDepth = 24;
|
||||
pCaps->VS20Caps.NumTemps = 32;
|
||||
pCaps->VS20Caps.StaticFlowControlDepth = 4;
|
||||
pCaps->VS20Caps.Caps = options.shaderModel >= 2 ? D3DVS20CAPS_PREDICATION : 0;
|
||||
pCaps->VS20Caps.DynamicFlowControlDepth = options.shaderModel >= 2 ? D3DVS20_MAX_DYNAMICFLOWCONTROLDEPTH : 0;
|
||||
pCaps->VS20Caps.NumTemps = options.shaderModel >= 2 ? D3DVS20_MAX_NUMTEMPS : 0;
|
||||
pCaps->VS20Caps.StaticFlowControlDepth = options.shaderModel >= 2 ? D3DVS20_MAX_STATICFLOWCONTROLDEPTH : 0;
|
||||
|
||||
pCaps->PS20Caps.Caps = 31;
|
||||
pCaps->PS20Caps.DynamicFlowControlDepth = 24;
|
||||
pCaps->PS20Caps.NumTemps = 32;
|
||||
pCaps->PS20Caps.StaticFlowControlDepth = 4;
|
||||
pCaps->PS20Caps.Caps = options.shaderModel >= 2 ? D3DPS20CAPS_ARBITRARYSWIZZLE
|
||||
| D3DPS20CAPS_GRADIENTINSTRUCTIONS
|
||||
| D3DPS20CAPS_PREDICATION
|
||||
| D3DPS20CAPS_NODEPENDENTREADLIMIT
|
||||
| D3DPS20CAPS_NOTEXINSTRUCTIONLIMIT : 0;
|
||||
pCaps->PS20Caps.DynamicFlowControlDepth = options.shaderModel >= 2 ? D3DPS20_MAX_DYNAMICFLOWCONTROLDEPTH : 0;
|
||||
pCaps->PS20Caps.NumTemps = options.shaderModel >= 2 ? D3DPS20_MAX_NUMTEMPS : 0;
|
||||
pCaps->PS20Caps.StaticFlowControlDepth = options.shaderModel >= 2 ? D3DPS20_MAX_STATICFLOWCONTROLDEPTH : 0;
|
||||
pCaps->PS20Caps.NumInstructionSlots = options.shaderModel >= 2 ? D3DPS20_MAX_NUMINSTRUCTIONSLOTS : 0;
|
||||
|
||||
pCaps->PS20Caps.NumInstructionSlots = options.shaderModel >= 2 ? 512 : 256;
|
||||
// Vertex texture samplers are only available as part of SM3, the caps are 0 otherwise.
|
||||
pCaps->VertexTextureFilterCaps = options.shaderModel == 3 ? D3DPTFILTERCAPS_MINFPOINT
|
||||
| D3DPTFILTERCAPS_MINFLINEAR
|
||||
/* | D3DPTFILTERCAPS_MINFANISOTROPIC */
|
||||
/* | D3DPTFILTERCAPS_MINFPYRAMIDALQUAD */
|
||||
/* | D3DPTFILTERCAPS_MINFGAUSSIANQUAD */
|
||||
/* | D3DPTFILTERCAPS_MIPFPOINT */
|
||||
/* | D3DPTFILTERCAPS_MIPFLINEAR */
|
||||
/* | D3DPTFILTERCAPS_CONVOLUTIONMONO */
|
||||
| D3DPTFILTERCAPS_MAGFPOINT
|
||||
| D3DPTFILTERCAPS_MAGFLINEAR
|
||||
/* | D3DPTFILTERCAPS_MAGFANISOTROPIC */
|
||||
/* | D3DPTFILTERCAPS_MAGFPYRAMIDALQUAD */
|
||||
/* | D3DPTFILTERCAPS_MAGFGAUSSIANQUAD */ : 0;
|
||||
|
||||
pCaps->VertexTextureFilterCaps = 50332416;
|
||||
pCaps->MaxVShaderInstructionsExecuted = 4294967295;
|
||||
pCaps->MaxPShaderInstructionsExecuted = 4294967295;
|
||||
pCaps->MaxVShaderInstructionsExecuted = options.shaderModel >= 2 ? 4294967295 : 0;
|
||||
pCaps->MaxPShaderInstructionsExecuted = options.shaderModel >= 2 ? 4294967295 : 0;
|
||||
|
||||
pCaps->MaxVertexShader30InstructionSlots = options.shaderModel == 3 ? 32768 : 0;
|
||||
pCaps->MaxPixelShader30InstructionSlots = options.shaderModel == 3 ? 32768 : 0;
|
||||
|
|
|
@ -135,7 +135,7 @@ namespace dxvk {
|
|||
label.pLabelName = labelName.c_str();
|
||||
DecodeD3DCOLOR(color, label.color);
|
||||
|
||||
ctx->beginDebugLabel(&label);
|
||||
ctx->beginDebugLabel(label);
|
||||
});
|
||||
|
||||
// Handled by the global list.
|
||||
|
@ -165,7 +165,7 @@ namespace dxvk {
|
|||
label.pLabelName = labelName.c_str();
|
||||
DecodeD3DCOLOR(color, label.color);
|
||||
|
||||
ctx->insertDebugLabel(&label);
|
||||
ctx->insertDebugLabel(label);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -28,10 +28,6 @@ namespace dxvk {
|
|||
return m_device->QueryInterface(riid, ppvObject);
|
||||
}
|
||||
|
||||
void DxvkD3D8Bridge::SetAPIName(const char* name) {
|
||||
m_device->m_implicitSwapchain->SetApiName(name);
|
||||
}
|
||||
|
||||
HRESULT DxvkD3D8Bridge::UpdateTextureFromBuffer(
|
||||
IDirect3DSurface9* pDestSurface,
|
||||
IDirect3DSurface9* pSrcSurface,
|
||||
|
@ -45,6 +41,26 @@ namespace dxvk {
|
|||
if (unlikely(dst == nullptr || src == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// CopyRects will not pass a null pSrcRect, but check anyway
|
||||
if (unlikely(pSrcRect == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// validate dimensions to ensure we calculate a meaningful srcOffset & extent
|
||||
if (unlikely(pSrcRect->left < 0
|
||||
|| pSrcRect->top < 0
|
||||
|| pSrcRect->right <= pSrcRect->left
|
||||
|| pSrcRect->bottom <= pSrcRect->top))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// CopyRects will not pass a null pDestPoint, but check anyway
|
||||
if (unlikely(pDestPoint == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// validate dimensions to ensure we caculate a meaningful dstOffset
|
||||
if (unlikely(pDestPoint->x < 0
|
||||
|| pDestPoint->y < 0))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
D3D9CommonTexture* srcTextureInfo = src->GetCommonTexture();
|
||||
D3D9CommonTexture* dstTextureInfo = dst->GetCommonTexture();
|
||||
|
||||
|
@ -59,13 +75,10 @@ namespace dxvk {
|
|||
|
||||
extent = { uint32_t(pSrcRect->right - pSrcRect->left), uint32_t(pSrcRect->bottom - pSrcRect->top), 1 };
|
||||
|
||||
// TODO: Validate extents like in D3D9DeviceEx::UpdateSurface
|
||||
|
||||
dstOffset = { pDestPoint->x,
|
||||
pDestPoint->y,
|
||||
0u };
|
||||
|
||||
|
||||
m_device->UpdateTextureFromBuffer(
|
||||
srcTextureInfo, dstTextureInfo,
|
||||
src->GetSubresource(), dst->GetSubresource(),
|
||||
|
@ -108,4 +121,5 @@ namespace dxvk {
|
|||
const Config* DxvkD3D8InterfaceBridge::GetConfig() const {
|
||||
return &m_interface->GetInstance()->config();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,13 +21,6 @@ IDxvkD3D8Bridge : public IUnknown {
|
|||
using IDirect3DSurface9 = d3d9::IDirect3DSurface9;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Changes the API name displayed on the HUD
|
||||
*
|
||||
* \param [in] name The new API name
|
||||
*/
|
||||
virtual void SetAPIName(const char* name) = 0;
|
||||
|
||||
/**
|
||||
* \brief Updates a D3D9 surface from a D3D9 buffer
|
||||
*
|
||||
|
@ -74,8 +67,11 @@ namespace dxvk {
|
|||
class D3D9InterfaceEx;
|
||||
|
||||
class DxvkD3D8Bridge : public IDxvkD3D8Bridge {
|
||||
|
||||
public:
|
||||
|
||||
DxvkD3D8Bridge(D3D9DeviceEx* pDevice);
|
||||
|
||||
~DxvkD3D8Bridge();
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef();
|
||||
|
@ -84,8 +80,6 @@ namespace dxvk {
|
|||
REFIID riid,
|
||||
void** ppvObject);
|
||||
|
||||
void SetAPIName(const char* name);
|
||||
|
||||
HRESULT UpdateTextureFromBuffer(
|
||||
IDirect3DSurface9* pDestSurface,
|
||||
IDirect3DSurface9* pSrcSurface,
|
||||
|
@ -93,12 +87,17 @@ namespace dxvk {
|
|||
const POINT* pDestPoint);
|
||||
|
||||
private:
|
||||
|
||||
D3D9DeviceEx* m_device;
|
||||
|
||||
};
|
||||
|
||||
class DxvkD3D8InterfaceBridge : public IDxvkD3D8InterfaceBridge {
|
||||
|
||||
public:
|
||||
|
||||
DxvkD3D8InterfaceBridge(D3D9InterfaceEx* pObject);
|
||||
|
||||
~DxvkD3D8InterfaceBridge();
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef();
|
||||
|
@ -112,6 +111,9 @@ namespace dxvk {
|
|||
const Config* GetConfig() const;
|
||||
|
||||
protected:
|
||||
|
||||
D3D9InterfaceEx* m_interface;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -8,8 +8,9 @@ namespace dxvk {
|
|||
|
||||
D3D9VertexBuffer::D3D9VertexBuffer(
|
||||
D3D9DeviceEx* pDevice,
|
||||
const D3D9_BUFFER_DESC* pDesc)
|
||||
: D3D9VertexBufferBase(pDevice, pDesc) {
|
||||
const D3D9_BUFFER_DESC* pDesc,
|
||||
const bool Extended)
|
||||
: D3D9VertexBufferBase(pDevice, pDesc, Extended) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -68,8 +69,9 @@ namespace dxvk {
|
|||
|
||||
D3D9IndexBuffer::D3D9IndexBuffer(
|
||||
D3D9DeviceEx* pDevice,
|
||||
const D3D9_BUFFER_DESC* pDesc)
|
||||
: D3D9IndexBufferBase(pDevice, pDesc) {
|
||||
const D3D9_BUFFER_DESC* pDesc,
|
||||
const bool Extended)
|
||||
: D3D9IndexBufferBase(pDevice, pDesc, Extended) {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,9 @@ namespace dxvk {
|
|||
|
||||
D3D9Buffer(
|
||||
D3D9DeviceEx* pDevice,
|
||||
const D3D9_BUFFER_DESC* pDesc)
|
||||
: D3D9Resource<Type...> (pDevice),
|
||||
const D3D9_BUFFER_DESC* pDesc,
|
||||
const bool Extended)
|
||||
: D3D9Resource<Type...> (pDevice, pDesc->Pool, Extended ),
|
||||
m_buffer (pDevice, pDesc) {
|
||||
|
||||
}
|
||||
|
@ -57,7 +58,8 @@ namespace dxvk {
|
|||
|
||||
D3D9VertexBuffer(
|
||||
D3D9DeviceEx* pDevice,
|
||||
const D3D9_BUFFER_DESC* pDesc);
|
||||
const D3D9_BUFFER_DESC* pDesc,
|
||||
const bool Extended);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(
|
||||
REFIID riid,
|
||||
|
@ -76,7 +78,8 @@ namespace dxvk {
|
|||
|
||||
D3D9IndexBuffer(
|
||||
D3D9DeviceEx* pDevice,
|
||||
const D3D9_BUFFER_DESC* pDesc);
|
||||
const D3D9_BUFFER_DESC* pDesc,
|
||||
const bool Extended);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(
|
||||
REFIID riid,
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace dxvk {
|
|||
|
||||
public:
|
||||
|
||||
static constexpr UINT AllLayers = UINT32_MAX;
|
||||
static constexpr UINT AllLayers = std::numeric_limits<uint32_t>::max();
|
||||
|
||||
D3D9CommonTexture(
|
||||
D3D9DeviceEx* pDevice,
|
||||
|
@ -325,6 +325,8 @@ namespace dxvk {
|
|||
|
||||
uint32_t GetPlaneCount() const;
|
||||
|
||||
D3DPOOL GetPool() const { return m_desc.Pool; }
|
||||
|
||||
const D3D9_VK_FORMAT_MAPPING& GetMapping() { return m_mapping; }
|
||||
|
||||
void SetLocked(UINT Subresource, bool value) { m_locked.set(Subresource, value); }
|
||||
|
|
|
@ -101,6 +101,7 @@ namespace dxvk {
|
|||
bufferInfo.usage = m_usage;
|
||||
bufferInfo.access = 0;
|
||||
bufferInfo.stages = util::pipelineStages(m_stages);
|
||||
bufferInfo.debugName = "Constant buffer";
|
||||
|
||||
if (m_usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)
|
||||
bufferInfo.access |= VK_ACCESS_UNIFORM_READ_BIT;
|
||||
|
|
|
@ -29,6 +29,15 @@ namespace dxvk {
|
|||
|
||||
|
||||
void D3D9Cursor::UpdateCursor(int X, int Y) {
|
||||
// SetCursorPosition is used to directly update the position of software cursors,
|
||||
// but keep track of the cursor position even when using hardware cursors, in order
|
||||
// to ensure a smooth transition/overlap from one type to the other.
|
||||
m_sCursor.X = X;
|
||||
m_sCursor.Y = Y;
|
||||
|
||||
if (unlikely(m_sCursor.Width > 0 && m_sCursor.Height > 0))
|
||||
return;
|
||||
|
||||
POINT currentPos = { };
|
||||
if (::GetCursorPos(¤tPos) && currentPos == POINT{ X, Y })
|
||||
return;
|
||||
|
@ -37,15 +46,6 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void D3D9Cursor::RefreshSoftwareCursorPosition() {
|
||||
POINT currentPos = { };
|
||||
::GetCursorPos(¤tPos);
|
||||
|
||||
m_sCursor.X = static_cast<int32_t>(currentPos.x) - m_sCursor.XHotSpot;
|
||||
m_sCursor.Y = static_cast<int32_t>(currentPos.y) - m_sCursor.YHotSpot;
|
||||
}
|
||||
|
||||
|
||||
BOOL D3D9Cursor::ShowCursor(BOOL bShow) {
|
||||
// Cursor visibility remains unchanged (typically FALSE) if the cursor isn't set.
|
||||
if (unlikely(m_hCursor == nullptr && !IsSoftwareCursor()))
|
||||
|
@ -127,11 +127,6 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void D3D9Cursor::RefreshSoftwareCursorPosition() {
|
||||
Logger::warn("D3D9Cursor::RefreshSoftwareCursorPosition: Not supported on current platform.");
|
||||
}
|
||||
|
||||
|
||||
BOOL D3D9Cursor::ShowCursor(BOOL bShow) {
|
||||
Logger::warn("D3D9Cursor::ShowCursor: Not supported on current platform.");
|
||||
return std::exchange(m_visible, bShow);
|
||||
|
|
|
@ -47,8 +47,6 @@ namespace dxvk {
|
|||
|
||||
void UpdateCursor(int X, int Y);
|
||||
|
||||
void RefreshSoftwareCursorPosition();
|
||||
|
||||
BOOL ShowCursor(BOOL bShow);
|
||||
|
||||
HRESULT SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap);
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace dxvk {
|
|||
, m_csThread ( dxvkDevice, dxvkDevice->createContext() )
|
||||
, m_csChunk ( AllocCsChunk() )
|
||||
, m_submissionFence ( new sync::Fence() )
|
||||
, m_flushTracker ( m_d3d9Options.reproducibleCommandStream )
|
||||
, m_flushTracker ( GetMaxFlushType() )
|
||||
, m_d3d9Interop ( this )
|
||||
, m_d3d9On12 ( this )
|
||||
, m_d3d8Bridge ( this ) {
|
||||
|
@ -70,7 +70,7 @@ namespace dxvk {
|
|||
if (canSWVP)
|
||||
Logger::info("D3D9DeviceEx: Using extended constant set for software vertex processing.");
|
||||
|
||||
if (m_dxvkDevice->instance()->extensions().extDebugUtils)
|
||||
if (m_dxvkDevice->debugFlags().test(DxvkDebugFlag::Markers))
|
||||
m_annotation = new D3D9UserDefinedAnnotation(this);
|
||||
|
||||
m_initializer = new D3D9Initializer(this);
|
||||
|
@ -539,7 +539,7 @@ namespace dxvk {
|
|||
SynchronizeCsThread(DxvkCsThread::SynchronizeAll);
|
||||
|
||||
if (m_d3d9Options.deferSurfaceCreation)
|
||||
m_deviceHasBeenReset = true;
|
||||
m_resetCtr++;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
@ -657,7 +657,7 @@ namespace dxvk {
|
|||
if (pSharedHandle != nullptr && Pool != D3DPOOL_DEFAULT)
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
const Com<D3D9Texture2D> texture = new D3D9Texture2D(this, &desc, pSharedHandle);
|
||||
const Com<D3D9Texture2D> texture = new D3D9Texture2D(this, &desc, IsExtended(), pSharedHandle);
|
||||
|
||||
m_initializer->InitTexture(texture->GetCommonTexture(), initialData);
|
||||
*ppTexture = texture.ref();
|
||||
|
@ -717,7 +717,7 @@ namespace dxvk {
|
|||
return D3DERR_INVALIDCALL;
|
||||
|
||||
try {
|
||||
const Com<D3D9Texture3D> texture = new D3D9Texture3D(this, &desc);
|
||||
const Com<D3D9Texture3D> texture = new D3D9Texture3D(this, &desc, IsExtended());
|
||||
m_initializer->InitTexture(texture->GetCommonTexture());
|
||||
*ppVolumeTexture = texture.ref();
|
||||
|
||||
|
@ -775,7 +775,7 @@ namespace dxvk {
|
|||
return D3DERR_INVALIDCALL;
|
||||
|
||||
try {
|
||||
const Com<D3D9TextureCube> texture = new D3D9TextureCube(this, &desc);
|
||||
const Com<D3D9TextureCube> texture = new D3D9TextureCube(this, &desc, IsExtended());
|
||||
m_initializer->InitTexture(texture->GetCommonTexture());
|
||||
*ppCubeTexture = texture.ref();
|
||||
|
||||
|
@ -819,7 +819,7 @@ namespace dxvk {
|
|||
return D3DERR_INVALIDCALL;
|
||||
|
||||
try {
|
||||
const Com<D3D9VertexBuffer> buffer = new D3D9VertexBuffer(this, &desc);
|
||||
const Com<D3D9VertexBuffer> buffer = new D3D9VertexBuffer(this, &desc, IsExtended());
|
||||
m_initializer->InitBuffer(buffer->GetCommonBuffer());
|
||||
*ppVertexBuffer = buffer.ref();
|
||||
|
||||
|
@ -862,7 +862,7 @@ namespace dxvk {
|
|||
return D3DERR_INVALIDCALL;
|
||||
|
||||
try {
|
||||
const Com<D3D9IndexBuffer> buffer = new D3D9IndexBuffer(this, &desc);
|
||||
const Com<D3D9IndexBuffer> buffer = new D3D9IndexBuffer(this, &desc, IsExtended());
|
||||
m_initializer->InitBuffer(buffer->GetCommonBuffer());
|
||||
*ppIndexBuffer = buffer.ref();
|
||||
|
||||
|
@ -1760,7 +1760,7 @@ namespace dxvk {
|
|||
|
||||
m_state.depthStencil = ds;
|
||||
|
||||
UpdateActiveHazardsDS(UINT32_MAX);
|
||||
UpdateActiveHazardsDS(std::numeric_limits<uint32_t>::max());
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
@ -2157,7 +2157,7 @@ namespace dxvk {
|
|||
if (m_state.IsLightEnabled(Index) == !!Enable)
|
||||
return D3D_OK;
|
||||
|
||||
uint32_t searchIndex = UINT32_MAX;
|
||||
uint32_t searchIndex = std::numeric_limits<uint32_t>::max();
|
||||
uint32_t setIndex = Index;
|
||||
|
||||
if (!Enable)
|
||||
|
@ -2384,7 +2384,7 @@ namespace dxvk {
|
|||
|
||||
case D3DRS_ZWRITEENABLE:
|
||||
if (likely(!old != !Value))
|
||||
UpdateActiveHazardsDS(UINT32_MAX);
|
||||
UpdateActiveHazardsDS(std::numeric_limits<uint32_t>::max());
|
||||
[[fallthrough]];
|
||||
case D3DRS_STENCILENABLE:
|
||||
case D3DRS_ZENABLE:
|
||||
|
@ -2890,9 +2890,12 @@ namespace dxvk {
|
|||
|
||||
// Tests on Windows show that D3D9 does not do non-indexed instanced draws.
|
||||
|
||||
ctx->draw(
|
||||
vertexCount, 1,
|
||||
cStartVertex, 0);
|
||||
VkDrawIndirectCommand draw = { };
|
||||
draw.vertexCount = vertexCount;
|
||||
draw.instanceCount = 1u;
|
||||
draw.firstVertex = cStartVertex;
|
||||
|
||||
ctx->draw(1u, &draw);
|
||||
});
|
||||
|
||||
return D3D_OK;
|
||||
|
@ -2939,10 +2942,13 @@ namespace dxvk {
|
|||
|
||||
ApplyPrimitiveType(ctx, cPrimType);
|
||||
|
||||
ctx->drawIndexed(
|
||||
drawInfo.vertexCount, drawInfo.instanceCount,
|
||||
cStartIndex,
|
||||
cBaseVertexIndex, 0);
|
||||
VkDrawIndexedIndirectCommand draw = { };
|
||||
draw.indexCount = drawInfo.vertexCount;
|
||||
draw.instanceCount = drawInfo.instanceCount;
|
||||
draw.firstIndex = cStartIndex;
|
||||
draw.vertexOffset = cBaseVertexIndex;
|
||||
|
||||
ctx->drawIndexed(1u, &draw);
|
||||
});
|
||||
|
||||
return D3D_OK;
|
||||
|
@ -2981,11 +2987,12 @@ namespace dxvk {
|
|||
ApplyPrimitiveType(ctx, cPrimType);
|
||||
|
||||
// Tests on Windows show that D3D9 does not do non-indexed instanced draws.
|
||||
VkDrawIndirectCommand draw = { };
|
||||
draw.vertexCount = cVertexCount;
|
||||
draw.instanceCount = 1u;
|
||||
|
||||
ctx->bindVertexBuffer(0, std::move(cBufferSlice), cStride);
|
||||
ctx->draw(
|
||||
cVertexCount, 1,
|
||||
0, 0);
|
||||
ctx->draw(1u, &draw);
|
||||
ctx->bindVertexBuffer(0, DxvkBufferSlice(), 0);
|
||||
});
|
||||
|
||||
|
@ -3045,12 +3052,13 @@ namespace dxvk {
|
|||
|
||||
ApplyPrimitiveType(ctx, cPrimType);
|
||||
|
||||
VkDrawIndexedIndirectCommand draw = { };
|
||||
draw.indexCount = drawInfo.vertexCount;
|
||||
draw.instanceCount = drawInfo.instanceCount;
|
||||
|
||||
ctx->bindVertexBuffer(0, cBufferSlice.subSlice(0, cVertexSize), cStride);
|
||||
ctx->bindIndexBuffer(cBufferSlice.subSlice(cVertexSize, cBufferSlice.length() - cVertexSize), cIndexType);
|
||||
ctx->drawIndexed(
|
||||
drawInfo.vertexCount, drawInfo.instanceCount,
|
||||
0,
|
||||
0, 0);
|
||||
ctx->drawIndexed(1u, &draw);
|
||||
ctx->bindVertexBuffer(0, DxvkBufferSlice(), 0);
|
||||
ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32);
|
||||
});
|
||||
|
@ -3162,11 +3170,14 @@ namespace dxvk {
|
|||
// to avoid val errors / UB.
|
||||
ctx->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(nullptr);
|
||||
|
||||
VkDrawIndirectCommand draw = { };
|
||||
draw.vertexCount = drawInfo.vertexCount;
|
||||
draw.instanceCount = drawInfo.instanceCount;
|
||||
draw.firstVertex = cStartIndex;
|
||||
|
||||
ctx->bindShader<VK_SHADER_STAGE_GEOMETRY_BIT>(std::move(shader));
|
||||
ctx->bindUniformBuffer(VK_SHADER_STAGE_GEOMETRY_BIT, getSWVPBufferSlot(), std::move(cBufferSlice));
|
||||
ctx->draw(
|
||||
drawInfo.vertexCount, drawInfo.instanceCount,
|
||||
cStartIndex, 0);
|
||||
ctx->draw(1u, &draw);
|
||||
ctx->bindUniformBuffer(VK_SHADER_STAGE_GEOMETRY_BIT, getSWVPBufferSlot(), DxvkBufferSlice());
|
||||
ctx->bindShader<VK_SHADER_STAGE_GEOMETRY_BIT>(nullptr);
|
||||
});
|
||||
|
@ -3314,6 +3325,20 @@ namespace dxvk {
|
|||
if (unlikely(ppShader == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
const uint32_t majorVersion = D3DSHADER_VERSION_MAJOR(pFunction[0]);
|
||||
const uint32_t minorVersion = D3DSHADER_VERSION_MINOR(pFunction[0]);
|
||||
|
||||
// Late fixed-function capable hardware exposed support for VS 1.1
|
||||
const uint32_t shaderModelVS = m_d3d9Options.shaderModel == 0 ? 1 : m_d3d9Options.shaderModel;
|
||||
|
||||
if (unlikely(majorVersion > shaderModelVS
|
||||
|| (majorVersion == 1 && minorVersion > 1)
|
||||
// Skip checking the SM2 minor version, as it has a 2_x mode apparently
|
||||
|| (majorVersion == 3 && minorVersion != 0))) {
|
||||
Logger::err(str::format("D3D9DeviceEx::CreateVertexShader: Unsupported VS version ", majorVersion, ".", minorVersion));
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
DxsoModuleInfo moduleInfo;
|
||||
moduleInfo.options = m_dxsoOptions;
|
||||
|
||||
|
@ -3373,13 +3398,13 @@ namespace dxvk {
|
|||
BindShader<DxsoProgramTypes::VertexShader>(GetCommonShader(shader));
|
||||
m_vsShaderMasks = newShader->GetShaderMask();
|
||||
|
||||
UpdateTextureTypeMismatchesForShader(newShader, m_vsShaderMasks.samplerMask, caps::MaxTexturesPS + 1);
|
||||
UpdateTextureTypeMismatchesForShader(newShader, m_vsShaderMasks.samplerMask, FirstVSSamplerSlot);
|
||||
}
|
||||
else {
|
||||
m_vsShaderMasks = D3D9ShaderMasks();
|
||||
|
||||
// Fixed function vertex shaders don't support sampling textures.
|
||||
m_dirtyTextures = m_vsShaderMasks.samplerMask & m_mismatchingTextureTypes;
|
||||
m_dirtyTextures |= m_vsShaderMasks.samplerMask & m_mismatchingTextureTypes;
|
||||
m_mismatchingTextureTypes &= ~m_vsShaderMasks.samplerMask;
|
||||
}
|
||||
|
||||
|
@ -3678,6 +3703,17 @@ namespace dxvk {
|
|||
if (unlikely(ppShader == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
const uint32_t majorVersion = D3DSHADER_VERSION_MAJOR(pFunction[0]);
|
||||
const uint32_t minorVersion = D3DSHADER_VERSION_MINOR(pFunction[0]);
|
||||
|
||||
if (unlikely(majorVersion > m_d3d9Options.shaderModel
|
||||
|| (majorVersion == 1 && minorVersion > 4)
|
||||
// Skip checking the SM2 minor version, as it has a 2_x mode apparently
|
||||
|| (majorVersion == 3 && minorVersion != 0))) {
|
||||
Logger::err(str::format("D3D9DeviceEx::CreatePixelShader: Unsupported PS version ", majorVersion, ".", minorVersion));
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
DxsoModuleInfo moduleInfo;
|
||||
moduleInfo.options = m_dxsoOptions;
|
||||
|
||||
|
@ -3748,7 +3784,7 @@ namespace dxvk {
|
|||
newShaderMasks = FixedFunctionMask;
|
||||
|
||||
// Fixed function always uses spec constants to decide the texture type.
|
||||
m_dirtyTextures = newShaderMasks.samplerMask & m_mismatchingTextureTypes;
|
||||
m_dirtyTextures |= newShaderMasks.samplerMask & m_mismatchingTextureTypes;
|
||||
m_mismatchingTextureTypes &= ~newShaderMasks.samplerMask;
|
||||
}
|
||||
|
||||
|
@ -3770,8 +3806,8 @@ namespace dxvk {
|
|||
if (m_psShaderMasks.samplerMask != newShaderMasks.samplerMask ||
|
||||
m_psShaderMasks.rtMask != newShaderMasks.rtMask) {
|
||||
m_psShaderMasks = newShaderMasks;
|
||||
UpdateActiveHazardsRT(UINT32_MAX);
|
||||
UpdateActiveHazardsDS(UINT32_MAX);
|
||||
UpdateActiveHazardsRT(std::numeric_limits<uint32_t>::max());
|
||||
UpdateActiveHazardsDS(std::numeric_limits<uint32_t>::max());
|
||||
}
|
||||
|
||||
return D3D_OK;
|
||||
|
@ -4027,14 +4063,13 @@ namespace dxvk {
|
|||
DWORD dwFlags) {
|
||||
|
||||
if (m_cursor.IsSoftwareCursor()) {
|
||||
m_cursor.RefreshSoftwareCursorPosition();
|
||||
|
||||
D3D9_SOFTWARE_CURSOR* pSoftwareCursor = m_cursor.GetSoftwareCursor();
|
||||
|
||||
UINT cursorWidth = pSoftwareCursor->DrawCursor ? pSoftwareCursor->Width : 0;
|
||||
UINT cursorHeight = pSoftwareCursor->DrawCursor ? pSoftwareCursor->Height : 0;
|
||||
|
||||
m_implicitSwapchain->SetCursorPosition(pSoftwareCursor->X, pSoftwareCursor->Y,
|
||||
m_implicitSwapchain->SetCursorPosition(pSoftwareCursor->X - pSoftwareCursor->XHotSpot,
|
||||
pSoftwareCursor->Y - pSoftwareCursor->YHotSpot,
|
||||
cursorWidth, cursorHeight);
|
||||
|
||||
// Once a hardware cursor has been set or the device has been reset,
|
||||
|
@ -4045,8 +4080,6 @@ namespace dxvk {
|
|||
pSoftwareCursor->Height = 0;
|
||||
pSoftwareCursor->XHotSpot = 0;
|
||||
pSoftwareCursor->YHotSpot = 0;
|
||||
pSoftwareCursor->X = 0;
|
||||
pSoftwareCursor->Y = 0;
|
||||
pSoftwareCursor->ResetCursor = false;
|
||||
}
|
||||
}
|
||||
|
@ -4104,7 +4137,7 @@ namespace dxvk {
|
|||
return D3DERR_INVALIDCALL;
|
||||
|
||||
try {
|
||||
const Com<D3D9Surface> surface = new D3D9Surface(this, &desc, nullptr, pSharedHandle);
|
||||
const Com<D3D9Surface> surface = new D3D9Surface(this, &desc, IsExtended(), nullptr, pSharedHandle);
|
||||
m_initializer->InitTexture(surface->GetCommonTexture());
|
||||
*ppSurface = surface.ref();
|
||||
m_losableResourceCounter++;
|
||||
|
@ -4155,7 +4188,7 @@ namespace dxvk {
|
|||
return D3DERR_INVALIDCALL;
|
||||
|
||||
try {
|
||||
const Com<D3D9Surface> surface = new D3D9Surface(this, &desc, nullptr, pSharedHandle);
|
||||
const Com<D3D9Surface> surface = new D3D9Surface(this, &desc, IsExtended(), nullptr, pSharedHandle);
|
||||
m_initializer->InitTexture(surface->GetCommonTexture());
|
||||
*ppSurface = surface.ref();
|
||||
|
||||
|
@ -4206,7 +4239,7 @@ namespace dxvk {
|
|||
return D3DERR_INVALIDCALL;
|
||||
|
||||
try {
|
||||
const Com<D3D9Surface> surface = new D3D9Surface(this, &desc, nullptr, pSharedHandle);
|
||||
const Com<D3D9Surface> surface = new D3D9Surface(this, &desc, IsExtended(), nullptr, pSharedHandle);
|
||||
m_initializer->InitTexture(surface->GetCommonTexture());
|
||||
*ppSurface = surface.ref();
|
||||
m_losableResourceCounter++;
|
||||
|
@ -4270,7 +4303,7 @@ namespace dxvk {
|
|||
m_implicitSwapchain->Invalidate(pPresentationParameters->hDeviceWindow);
|
||||
|
||||
try {
|
||||
auto* swapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode);
|
||||
auto* swapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode, false);
|
||||
*ppSwapChain = ref(swapchain);
|
||||
m_losableResourceCounter++;
|
||||
}
|
||||
|
@ -4576,6 +4609,7 @@ namespace dxvk {
|
|||
info.access = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT
|
||||
| VK_ACCESS_INDEX_READ_BIT;
|
||||
info.stages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
|
||||
info.debugName = "UP buffer";
|
||||
|
||||
Rc<DxvkBuffer> buffer = m_dxvkDevice->createBuffer(info, memoryFlags);
|
||||
void* mapPtr = buffer->mapPtr(0);
|
||||
|
@ -5648,7 +5682,7 @@ namespace dxvk {
|
|||
void D3D9DeviceEx::InjectCsChunk(
|
||||
DxvkCsChunkRef&& Chunk,
|
||||
bool Synchronize) {
|
||||
m_csThread.injectChunk(std::move(Chunk), Synchronize);
|
||||
m_csThread.injectChunk(DxvkCsQueue::HighPriority, std::move(Chunk), Synchronize);
|
||||
}
|
||||
|
||||
|
||||
|
@ -6068,7 +6102,7 @@ namespace dxvk {
|
|||
] (DxvkContext* ctx) {
|
||||
ctx->signal(cSubmissionFence, cSubmissionId);
|
||||
ctx->signal(cStagingBufferFence, cStagingBufferAllocated);
|
||||
ctx->flushCommandList(cSubmissionStatus);
|
||||
ctx->flushCommandList(nullptr, cSubmissionStatus);
|
||||
});
|
||||
|
||||
FlushCsChunk();
|
||||
|
@ -6097,11 +6131,29 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void D3D9DeviceEx::EndFrame() {
|
||||
void D3D9DeviceEx::BeginFrame(Rc<DxvkLatencyTracker> LatencyTracker, uint64_t FrameId) {
|
||||
D3D9DeviceLock lock = LockDevice();
|
||||
|
||||
EmitCs<false>([] (DxvkContext* ctx) {
|
||||
EmitCs<false>([
|
||||
cTracker = std::move(LatencyTracker),
|
||||
cFrameId = FrameId
|
||||
] (DxvkContext* ctx) {
|
||||
if (cTracker && cTracker->needsAutoMarkers())
|
||||
ctx->beginLatencyTracking(cTracker, cFrameId);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void D3D9DeviceEx::EndFrame(Rc<DxvkLatencyTracker> LatencyTracker) {
|
||||
D3D9DeviceLock lock = LockDevice();
|
||||
|
||||
EmitCs<false>([
|
||||
cTracker = std::move(LatencyTracker)
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->endFrame();
|
||||
|
||||
if (cTracker && cTracker->needsAutoMarkers())
|
||||
ctx->endLatencyTracking(cTracker);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -6339,15 +6391,16 @@ namespace dxvk {
|
|||
|
||||
|
||||
void D3D9DeviceEx::UpdateTextureTypeMismatchesForShader(const D3D9CommonShader* shader, uint32_t shaderSamplerMask, uint32_t shaderSamplerOffset) {
|
||||
if (unlikely(shader->GetInfo().majorVersion() < 2)) {
|
||||
const uint32_t stageCorrectedShaderSamplerMask = shaderSamplerMask << shaderSamplerOffset;
|
||||
if (unlikely(shader->GetInfo().majorVersion() < 2 || m_d3d9Options.forceSamplerTypeSpecConstants)) {
|
||||
// SM 1 shaders don't define the texture type in the shader.
|
||||
// We always use spec constants for those.
|
||||
m_dirtyTextures = shaderSamplerMask & m_mismatchingTextureTypes;
|
||||
m_mismatchingTextureTypes &= ~shaderSamplerMask;
|
||||
m_dirtyTextures |= stageCorrectedShaderSamplerMask & m_mismatchingTextureTypes;
|
||||
m_mismatchingTextureTypes &= ~stageCorrectedShaderSamplerMask;
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i : bit::BitMask(shaderSamplerMask)) {
|
||||
for (const uint32_t i : bit::BitMask(stageCorrectedShaderSamplerMask)) {
|
||||
const D3D9CommonTexture* texture = GetCommonTexture(m_state.textures[i]);
|
||||
if (unlikely(texture == nullptr)) {
|
||||
// Unbound textures are not mismatching texture types
|
||||
|
@ -6373,15 +6426,20 @@ namespace dxvk {
|
|||
void D3D9DeviceEx::UpdateTextureTypeMismatchesForTexture(uint32_t stateSampler) {
|
||||
uint32_t shaderTextureIndex;
|
||||
const D3D9CommonShader* shader;
|
||||
if (unlikely(stateSampler > caps::MaxTexturesPS + 1)) {
|
||||
if (likely(IsPSSampler(stateSampler))) {
|
||||
shader = GetCommonShader(m_state.pixelShader);
|
||||
shaderTextureIndex = stateSampler;
|
||||
} else if (unlikely(IsVSSampler(stateSampler))) {
|
||||
shader = GetCommonShader(m_state.vertexShader);
|
||||
shaderTextureIndex = stateSampler - caps::MaxTexturesPS - 1;
|
||||
} else {
|
||||
shader = GetCommonShader(m_state.pixelShader);
|
||||
shaderTextureIndex = stateSampler;
|
||||
// Do not type check the fixed function displacement map texture.
|
||||
return;
|
||||
}
|
||||
|
||||
if (unlikely(shader == nullptr || shader->GetInfo().majorVersion() < 2)) {
|
||||
if (unlikely(shader == nullptr || shader->GetInfo().majorVersion() < 2 || m_d3d9Options.forceSamplerTypeSpecConstants)) {
|
||||
// This function only gets called by UpdateTextureBitmasks
|
||||
// which clears the dirty and mismatching bits for the texture before anyway.
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -6393,7 +6451,7 @@ namespace dxvk {
|
|||
bool shaderUsesTexture = shaderViewType != VkImageViewType(0);
|
||||
if (unlikely(boundViewType != shaderViewType && shaderUsesTexture)) {
|
||||
const uint32_t samplerBit = 1u << stateSampler;
|
||||
m_mismatchingTextureTypes |= 1 << samplerBit;
|
||||
m_mismatchingTextureTypes |= samplerBit;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7649,6 +7707,10 @@ namespace dxvk {
|
|||
const uint32_t regCountHardware = DetermineHardwareRegCount<ProgramType, ConstantType>();
|
||||
constexpr uint32_t regCountSoftware = DetermineSoftwareRegCount<ProgramType, ConstantType>();
|
||||
|
||||
// Error out in case of StartRegister + Count overflow
|
||||
if (unlikely(StartRegister > std::numeric_limits<uint32_t>::max() - Count))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
if (unlikely(StartRegister + Count > regCountSoftware))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
|
@ -7767,7 +7829,7 @@ namespace dxvk {
|
|||
|
||||
if (key.Data.Contents.UseLighting) {
|
||||
for (uint32_t i = 0; i < caps::MaxEnabledLights; i++) {
|
||||
if (m_state.enabledLightIndices[i] != UINT32_MAX)
|
||||
if (m_state.enabledLightIndices[i] != std::numeric_limits<uint32_t>::max())
|
||||
lightCount++;
|
||||
}
|
||||
}
|
||||
|
@ -7864,7 +7926,7 @@ namespace dxvk {
|
|||
uint32_t lightIdx = 0;
|
||||
for (uint32_t i = 0; i < caps::MaxEnabledLights; i++) {
|
||||
auto idx = m_state.enabledLightIndices[i];
|
||||
if (idx == UINT32_MAX)
|
||||
if (idx == std::numeric_limits<uint32_t>::max())
|
||||
continue;
|
||||
|
||||
data->Lights[lightIdx++] = D3D9Light(m_state.lights[idx].value(), m_state.transforms[GetTransformIndex(D3DTS_VIEW)]);
|
||||
|
@ -8442,7 +8504,7 @@ namespace dxvk {
|
|||
return hr;
|
||||
}
|
||||
else {
|
||||
m_implicitSwapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode);
|
||||
m_implicitSwapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode, true);
|
||||
m_mostRecentlyUsedSwapchain = m_implicitSwapchain.ptr();
|
||||
}
|
||||
|
||||
|
@ -8466,7 +8528,7 @@ namespace dxvk {
|
|||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_SURFACE, &desc)))
|
||||
return D3DERR_NOTAVAILABLE;
|
||||
|
||||
m_autoDepthStencil = new D3D9Surface(this, &desc, nullptr, nullptr);
|
||||
m_autoDepthStencil = new D3D9Surface(this, &desc, IsExtended(), nullptr, nullptr);
|
||||
m_initializer->InitTexture(m_autoDepthStencil->GetCommonTexture());
|
||||
SetDepthStencilSurface(m_autoDepthStencil.ptr());
|
||||
m_losableResourceCounter++;
|
||||
|
@ -8688,4 +8750,14 @@ namespace dxvk {
|
|||
m_flags.clr(D3D9DeviceFlag::DirtySpecializationEntries);
|
||||
}
|
||||
|
||||
|
||||
GpuFlushType D3D9DeviceEx::GetMaxFlushType() const {
|
||||
if (m_d3d9Options.reproducibleCommandStream)
|
||||
return GpuFlushType::ExplicitFlush;
|
||||
else if (m_dxvkDevice->perfHints().preferRenderPassOps)
|
||||
return GpuFlushType::ImplicitStrongHint;
|
||||
else
|
||||
return GpuFlushType::ImplicitWeakHint;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -653,15 +653,41 @@ namespace dxvk {
|
|||
const D3DDISPLAYMODEEX* pFullscreenDisplayMode,
|
||||
IDirect3DSwapChain9** ppSwapChain);
|
||||
|
||||
/**
|
||||
* @brief Sets the given sampler state
|
||||
*
|
||||
* @param StateSampler Sampler index (according to our internal way of storing samplers)
|
||||
* @param Type Sampler state type to change
|
||||
* @param Value State value
|
||||
*/
|
||||
HRESULT SetStateSamplerState(
|
||||
DWORD StateSampler,
|
||||
D3DSAMPLERSTATETYPE Type,
|
||||
DWORD Value);
|
||||
|
||||
/**
|
||||
* @brief Sets the given sampler texture
|
||||
*
|
||||
* @param StateSampler Sampler index (according to our internal way of storing samplers)
|
||||
* @param pTexture Texture to use
|
||||
*/
|
||||
HRESULT SetStateTexture(DWORD StateSampler, IDirect3DBaseTexture9* pTexture);
|
||||
|
||||
/**
|
||||
* @brief Sets the transform for the given sampler
|
||||
*
|
||||
* @param idx Sampler index (according to our internal way of storing samplers)
|
||||
* @param pMatrix Transform matrix
|
||||
*/
|
||||
HRESULT SetStateTransform(uint32_t idx, const D3DMATRIX* pMatrix);
|
||||
|
||||
/**
|
||||
* @brief Sets the fixed function texture processing state
|
||||
*
|
||||
* @param Stage Sampler index (according to our internal way of storing samplers)
|
||||
* @param Type Fixed function texture stage type
|
||||
* @param Value Value for the state
|
||||
*/
|
||||
HRESULT SetStateTextureStageState(
|
||||
DWORD Stage,
|
||||
D3D9TextureStageStateTypes Type,
|
||||
|
@ -800,7 +826,8 @@ namespace dxvk {
|
|||
void Flush();
|
||||
void FlushAndSync9On12();
|
||||
|
||||
void EndFrame();
|
||||
void BeginFrame(Rc<DxvkLatencyTracker> LatencyTracker, uint64_t FrameId);
|
||||
void EndFrame(Rc<DxvkLatencyTracker> LatencyTracker);
|
||||
|
||||
void UpdateActiveRTs(uint32_t index);
|
||||
|
||||
|
@ -817,8 +844,40 @@ namespace dxvk {
|
|||
|
||||
void UpdateActiveFetch4(uint32_t stateSampler);
|
||||
|
||||
/**
|
||||
* @brief Sets the mismatching texture type bits for all samplers if necessary.
|
||||
*
|
||||
* This function will check all samplers the shader uses and set the set the mismatching texture type bit for the given sampler if it does not
|
||||
* match the texture type expected by the respective shader.
|
||||
*
|
||||
* It will *not* unset the bit if the texture type does match.
|
||||
*
|
||||
* @param stateSampler Sampler index (according to our internal way of storing samplers)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Sets the mismatching texture type bits for all samplers if necessary.
|
||||
*
|
||||
* This function will check all samplers the shader uses and set the set the mismatching texture type bit for the given sampler if it does not
|
||||
* match the texture type expected by the shader.
|
||||
*
|
||||
* @param shader The shader
|
||||
* @param shaderSamplerMask Mask of all samplers that the shader uses (according to our internal way of storing samplers)
|
||||
* @param shaderSamplerOffset First index of the shader's samplers according to our internal way of storing samplers.
|
||||
* Used to transform the sampler indices that are relative to the entire pipeline to ones relative to the shader.
|
||||
*/
|
||||
void UpdateTextureTypeMismatchesForShader(const D3D9CommonShader* shader, uint32_t shaderSamplerMask, uint32_t shaderSamplerOffset);
|
||||
|
||||
/**
|
||||
* @brief Sets the mismatching texture type bit for the given sampler.
|
||||
*
|
||||
* This function will set the mismatching texture type bit for the given sampler if it does not
|
||||
* match the texture type expected by the respective shader.
|
||||
*
|
||||
* It will *not* unset the bit if the texture type does match.
|
||||
*
|
||||
* @param stateSampler Sampler index (according to our internal way of storing samplers)
|
||||
*/
|
||||
void UpdateTextureTypeMismatchesForTexture(uint32_t stateSampler);
|
||||
|
||||
void UploadManagedTexture(D3D9CommonTexture* pResource);
|
||||
|
@ -1143,12 +1202,12 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
/**
|
||||
* \brief Returns whether the device has been reset and marks it as true.
|
||||
* \brief Queries current reset counter
|
||||
* Used for the deferred surface creation workaround.
|
||||
* (Device Reset detection for D3D9SwapChainEx::Present)
|
||||
*/
|
||||
bool IsDeviceReset() {
|
||||
return std::exchange(m_deviceHasBeenReset, false);
|
||||
uint32_t GetResetCounter() {
|
||||
return m_resetCtr;
|
||||
}
|
||||
|
||||
template <bool Synchronize9On12>
|
||||
|
@ -1396,6 +1455,8 @@ namespace dxvk {
|
|||
&& !m_state.renderTargets[Index]->IsNull();
|
||||
}
|
||||
|
||||
GpuFlushType GetMaxFlushType() const;
|
||||
|
||||
Com<D3D9InterfaceEx> m_parent;
|
||||
D3DDEVTYPE m_deviceType;
|
||||
HWND m_window;
|
||||
|
@ -1513,7 +1574,7 @@ namespace dxvk {
|
|||
VkImageLayout m_hazardLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
|
||||
bool m_usingGraphicsPipelines = false;
|
||||
bool m_deviceHasBeenReset = false;
|
||||
uint32_t m_resetCtr = 0u;
|
||||
|
||||
DxvkDepthBiasRepresentation m_depthBiasRepresentation = { VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT, false };
|
||||
float m_depthBiasScale = 0.0f;
|
||||
|
|
|
@ -25,12 +25,28 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release() {
|
||||
uint32_t refCount = --this->m_refCount;
|
||||
uint32_t oldRefCount, refCount;
|
||||
|
||||
do {
|
||||
oldRefCount = this->m_refCount.load(std::memory_order_acquire);
|
||||
|
||||
// clamp value to 0 to prevent underruns
|
||||
if (unlikely(!oldRefCount))
|
||||
return 0;
|
||||
|
||||
refCount = oldRefCount - 1;
|
||||
|
||||
} while (!this->m_refCount.compare_exchange_weak(oldRefCount,
|
||||
refCount,
|
||||
std::memory_order_release,
|
||||
std::memory_order_acquire));
|
||||
|
||||
if (unlikely(!refCount)) {
|
||||
auto* pDevice = GetDevice();
|
||||
this->ReleasePrivate();
|
||||
pDevice->Release();
|
||||
}
|
||||
|
||||
return refCount;
|
||||
}
|
||||
|
||||
|
|
|
@ -1297,7 +1297,7 @@ namespace dxvk {
|
|||
uint32_t atten = m_module.opFFma (m_floatType, d, atten2, atten1);
|
||||
atten = m_module.opFFma (m_floatType, d, atten, atten0);
|
||||
atten = m_module.opFDiv (m_floatType, m_module.constf32(1.0f), atten);
|
||||
atten = m_module.opNMin (m_floatType, atten, m_module.constf32(FLT_MAX));
|
||||
atten = m_module.opNMin (m_floatType, atten, m_module.constf32(std::numeric_limits<float>::max()));
|
||||
|
||||
atten = m_module.opSelect(m_floatType, m_module.opFOrdGreaterThan(bool_t, d, range), m_module.constf32(0.0f), atten);
|
||||
atten = m_module.opSelect(m_floatType, isDirectional, m_module.constf32(1.0f), atten);
|
||||
|
|
|
@ -162,7 +162,7 @@ namespace dxvk {
|
|||
|
||||
void D3D9Initializer::ExecuteFlushLocked() {
|
||||
EmitCs([] (DxvkContext* ctx) {
|
||||
ctx->flushCommandList(nullptr);
|
||||
ctx->flushCommandList(nullptr, nullptr);
|
||||
});
|
||||
|
||||
FlushCsChunk();
|
||||
|
|
|
@ -67,6 +67,9 @@ namespace dxvk {
|
|||
SetProcessDPIAware();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (unlikely(m_d3d9Options.shaderModel == 0))
|
||||
Logger::warn("D3D9InterfaceEx: WARNING! Fixed-function exclusive mode is enabled.");
|
||||
}
|
||||
|
||||
|
||||
|
@ -457,6 +460,13 @@ namespace dxvk {
|
|||
|| pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// In windowed mode, only a subset of the presentation interval flags can be used.
|
||||
if (unlikely(pPresentationParameters->Windowed
|
||||
&& !(pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_DEFAULT
|
||||
|| pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_ONE
|
||||
|| pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -349,7 +349,7 @@ namespace dxvk {
|
|||
const D3D9_COMMON_TEXTURE_DESC& desc,
|
||||
IDirect3DResource9** ppResult) {
|
||||
try {
|
||||
const Com<ResourceType> texture = new ResourceType(m_device, &desc);
|
||||
const Com<ResourceType> texture = new ResourceType(m_device, &desc, m_device->IsExtended());
|
||||
m_device->m_initializer->InitTexture(texture->GetCommonTexture());
|
||||
*ppResult = texture.ref();
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace dxvk {
|
|||
SYSTEM_INFO sysInfo;
|
||||
GetSystemInfo(&sysInfo);
|
||||
m_allocationGranularity = sysInfo.dwAllocationGranularity;
|
||||
m_mappingGranularity = m_allocationGranularity * 16;
|
||||
}
|
||||
|
||||
D3D9Memory D3D9MemoryAllocator::Alloc(uint32_t Size) {
|
||||
|
@ -26,7 +27,7 @@ namespace dxvk {
|
|||
|
||||
uint32_t alignedSize = align(Size, CACHE_LINE_SIZE);
|
||||
for (auto& chunk : m_chunks) {
|
||||
D3D9Memory memory = chunk->Alloc(alignedSize);
|
||||
D3D9Memory memory = chunk->AllocLocked(alignedSize);
|
||||
if (memory) {
|
||||
m_usedMemory += memory.GetSize();
|
||||
return memory;
|
||||
|
@ -38,16 +39,26 @@ namespace dxvk {
|
|||
|
||||
D3D9MemoryChunk* chunk = new D3D9MemoryChunk(this, chunkSize);
|
||||
std::unique_ptr<D3D9MemoryChunk> uniqueChunk(chunk);
|
||||
D3D9Memory memory = uniqueChunk->Alloc(alignedSize);
|
||||
D3D9Memory memory = uniqueChunk->AllocLocked(alignedSize);
|
||||
m_usedMemory += memory.GetSize();
|
||||
|
||||
m_chunks.push_back(std::move(uniqueChunk));
|
||||
return memory;
|
||||
}
|
||||
|
||||
void D3D9MemoryAllocator::FreeChunk(D3D9MemoryChunk *Chunk) {
|
||||
void D3D9MemoryAllocator::Free(D3D9Memory *Memory) {
|
||||
std::lock_guard<dxvk::mutex> lock(m_mutex);
|
||||
|
||||
D3D9MemoryChunk* chunk = Memory->GetChunk();
|
||||
chunk->FreeLocked(Memory);
|
||||
m_usedMemory -= Memory->GetSize();
|
||||
if (chunk->IsEmpty())
|
||||
FreeChunk(chunk);
|
||||
}
|
||||
|
||||
void D3D9MemoryAllocator::FreeChunk(D3D9MemoryChunk *Chunk) {
|
||||
// Has to be called in the lock
|
||||
|
||||
m_allocatedMemory -= Chunk->Size();
|
||||
|
||||
m_chunks.erase(std::remove_if(m_chunks.begin(), m_chunks.end(), [&](auto& item) {
|
||||
|
@ -55,57 +66,66 @@ namespace dxvk {
|
|||
}), m_chunks.end());
|
||||
}
|
||||
|
||||
void D3D9MemoryAllocator::NotifyMapped(uint32_t Size) {
|
||||
m_mappedMemory += Size;
|
||||
void* D3D9MemoryAllocator::Map(D3D9Memory* Memory) {
|
||||
std::lock_guard<dxvk::mutex> lock(m_mutex);
|
||||
|
||||
D3D9MemoryChunk* chunk = Memory->GetChunk();
|
||||
uint32_t memoryMapped;
|
||||
void* ptr = chunk->MapLocked(Memory, memoryMapped);
|
||||
m_mappedMemory += memoryMapped;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void D3D9MemoryAllocator::NotifyUnmapped(uint32_t Size) {
|
||||
m_mappedMemory -= Size;
|
||||
void D3D9MemoryAllocator::Unmap(D3D9Memory* Memory) {
|
||||
std::lock_guard<dxvk::mutex> lock(m_mutex);
|
||||
|
||||
D3D9MemoryChunk* chunk = Memory->GetChunk();
|
||||
m_mappedMemory -= chunk->UnmapLocked(Memory);
|
||||
}
|
||||
|
||||
void D3D9MemoryAllocator::NotifyFreed(uint32_t Size) {
|
||||
m_usedMemory -= Size;
|
||||
}
|
||||
|
||||
uint32_t D3D9MemoryAllocator::MappedMemory() {
|
||||
uint32_t D3D9MemoryAllocator::MappedMemory() const {
|
||||
return m_mappedMemory.load();
|
||||
}
|
||||
|
||||
uint32_t D3D9MemoryAllocator::UsedMemory() {
|
||||
uint32_t D3D9MemoryAllocator::UsedMemory() const {
|
||||
return m_usedMemory.load();
|
||||
}
|
||||
|
||||
uint32_t D3D9MemoryAllocator::AllocatedMemory() {
|
||||
uint32_t D3D9MemoryAllocator::AllocatedMemory() const {
|
||||
return m_allocatedMemory.load();
|
||||
}
|
||||
|
||||
D3D9MemoryChunk::D3D9MemoryChunk(D3D9MemoryAllocator* Allocator, uint32_t Size)
|
||||
: m_allocator(Allocator), m_size(Size), m_mappingGranularity(m_allocator->MemoryGranularity() * 16) {
|
||||
: m_allocator(Allocator), m_size(Size) {
|
||||
m_mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE | SEC_COMMIT, 0, Size, nullptr);
|
||||
m_freeRanges.push_back({ 0, Size });
|
||||
m_mappingRanges.resize(((Size + m_mappingGranularity - 1) / m_mappingGranularity));
|
||||
uint32_t mappingGranularity = Allocator->MappingGranularity();
|
||||
m_mappingRanges.resize(((Size + mappingGranularity - 1) / mappingGranularity));
|
||||
}
|
||||
|
||||
D3D9MemoryChunk::~D3D9MemoryChunk() {
|
||||
std::lock_guard<dxvk::mutex> lock(m_mutex);
|
||||
// Has to be protected by the allocator lock
|
||||
|
||||
CloseHandle(m_mapping);
|
||||
}
|
||||
|
||||
void* D3D9MemoryChunk::Map(D3D9Memory* memory) {
|
||||
std::lock_guard<dxvk::mutex> lock(m_mutex);
|
||||
void* D3D9MemoryChunk::MapLocked(D3D9Memory* Memory, uint32_t& mappedSize) {
|
||||
// Has to be protected by the allocator lock
|
||||
|
||||
uint32_t alignedOffset = alignDown(memory->GetOffset(), m_mappingGranularity);
|
||||
uint32_t alignmentDelta = memory->GetOffset() - alignedOffset;
|
||||
uint32_t alignedSize = memory->GetSize() + alignmentDelta;
|
||||
if (alignedSize > m_mappingGranularity) {
|
||||
mappedSize = 0;
|
||||
uint32_t mappingGranularity = m_allocator->MappingGranularity();
|
||||
|
||||
uint32_t alignedOffset = alignDown(Memory->GetOffset(), mappingGranularity);
|
||||
uint32_t alignmentDelta = Memory->GetOffset() - alignedOffset;
|
||||
uint32_t alignedSize = Memory->GetSize() + alignmentDelta;
|
||||
if (alignedSize > mappingGranularity) {
|
||||
// The allocation crosses the boundary of the internal mapping page it's a part of
|
||||
// so we map it on it's own.
|
||||
alignedOffset = alignDown(memory->GetOffset(), m_allocator->MemoryGranularity());
|
||||
alignmentDelta = memory->GetOffset() - alignedOffset;
|
||||
alignedSize = memory->GetSize() + alignmentDelta;
|
||||
alignedOffset = alignDown(Memory->GetOffset(), m_allocator->AllocationGranularity());
|
||||
alignmentDelta = Memory->GetOffset() - alignedOffset;
|
||||
alignedSize = Memory->GetSize() + alignmentDelta;
|
||||
|
||||
m_allocator->NotifyMapped(alignedSize);
|
||||
mappedSize = alignedSize;
|
||||
uint8_t* basePtr = static_cast<uint8_t*>(MapViewOfFile(m_mapping, FILE_MAP_ALL_ACCESS, 0, alignedOffset, alignedSize));
|
||||
if (unlikely(basePtr == nullptr)) {
|
||||
DWORD error = GetLastError();
|
||||
|
@ -117,10 +137,10 @@ namespace dxvk {
|
|||
|
||||
// For small allocations we map the entire mapping page to minimize the overhead from having the align the offset to 65k bytes.
|
||||
// This should hopefully also reduce the amount of MapViewOfFile calls we do for tiny allocations.
|
||||
auto& mappingRange = m_mappingRanges[memory->GetOffset() / m_mappingGranularity];
|
||||
auto& mappingRange = m_mappingRanges[Memory->GetOffset() / mappingGranularity];
|
||||
if (unlikely(mappingRange.refCount == 0)) {
|
||||
m_allocator->NotifyMapped(m_mappingGranularity);
|
||||
mappingRange.ptr = static_cast<uint8_t*>(MapViewOfFile(m_mapping, FILE_MAP_ALL_ACCESS, 0, alignedOffset, m_mappingGranularity));
|
||||
mappedSize = mappingGranularity;
|
||||
mappingRange.ptr = static_cast<uint8_t*>(MapViewOfFile(m_mapping, FILE_MAP_ALL_ACCESS, 0, alignedOffset, m_allocator->MappingGranularity()));
|
||||
if (unlikely(mappingRange.ptr == nullptr)) {
|
||||
DWORD error = GetLastError();
|
||||
LPTSTR buffer = nullptr;
|
||||
|
@ -136,34 +156,36 @@ namespace dxvk {
|
|||
return basePtr + alignmentDelta;
|
||||
}
|
||||
|
||||
void D3D9MemoryChunk::Unmap(D3D9Memory* memory) {
|
||||
std::lock_guard<dxvk::mutex> lock(m_mutex);
|
||||
uint32_t D3D9MemoryChunk::UnmapLocked(D3D9Memory* Memory) {
|
||||
// Has to be protected by the allocator lock
|
||||
|
||||
uint32_t alignedOffset = alignDown(memory->GetOffset(), m_mappingGranularity);
|
||||
uint32_t alignmentDelta = memory->GetOffset() - alignedOffset;
|
||||
uint32_t alignedSize = memory->GetSize() + alignmentDelta;
|
||||
if (alignedSize > m_mappingGranularity) {
|
||||
uint32_t mappingGranularity = m_allocator->MappingGranularity();
|
||||
|
||||
uint32_t alignedOffset = alignDown(Memory->GetOffset(), mappingGranularity);
|
||||
uint32_t alignmentDelta = Memory->GetOffset() - alignedOffset;
|
||||
uint32_t alignedSize = Memory->GetSize() + alignmentDelta;
|
||||
if (alignedSize > mappingGranularity) {
|
||||
// Single use mapping
|
||||
alignedOffset = alignDown(memory->GetOffset(), m_allocator->MemoryGranularity());
|
||||
alignmentDelta = memory->GetOffset() - alignedOffset;
|
||||
alignedSize = memory->GetSize() + alignmentDelta;
|
||||
alignedOffset = alignDown(Memory->GetOffset(), m_allocator->AllocationGranularity());
|
||||
alignmentDelta = Memory->GetOffset() - alignedOffset;
|
||||
alignedSize = Memory->GetSize() + alignmentDelta;
|
||||
|
||||
uint8_t* basePtr = static_cast<uint8_t*>(memory->Ptr()) - alignmentDelta;
|
||||
uint8_t* basePtr = static_cast<uint8_t*>(Memory->Ptr()) - alignmentDelta;
|
||||
UnmapViewOfFile(basePtr);
|
||||
m_allocator->NotifyUnmapped(alignedSize);
|
||||
return;
|
||||
return alignedSize;
|
||||
}
|
||||
auto& mappingRange = m_mappingRanges[memory->GetOffset() / m_mappingGranularity];
|
||||
auto& mappingRange = m_mappingRanges[Memory->GetOffset() / mappingGranularity];
|
||||
mappingRange.refCount--;
|
||||
if (unlikely(mappingRange.refCount == 0)) {
|
||||
UnmapViewOfFile(mappingRange.ptr);
|
||||
mappingRange.ptr = nullptr;
|
||||
m_allocator->NotifyUnmapped(m_mappingGranularity);
|
||||
return mappingGranularity;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
D3D9Memory D3D9MemoryChunk::Alloc(uint32_t Size) {
|
||||
std::lock_guard<dxvk::mutex> lock(m_mutex);
|
||||
D3D9Memory D3D9MemoryChunk::AllocLocked(uint32_t Size) {
|
||||
// Has to be protected by the allocator lock
|
||||
|
||||
uint32_t offset = 0;
|
||||
uint32_t size = 0;
|
||||
|
@ -188,8 +210,8 @@ namespace dxvk {
|
|||
return {};
|
||||
}
|
||||
|
||||
void D3D9MemoryChunk::Free(D3D9Memory *Memory) {
|
||||
std::lock_guard<dxvk::mutex> lock(m_mutex);
|
||||
void D3D9MemoryChunk::FreeLocked(D3D9Memory *Memory) {
|
||||
// Has to be protected by the allocator lock
|
||||
|
||||
uint32_t offset = Memory->GetOffset();
|
||||
uint32_t size = Memory->GetSize();
|
||||
|
@ -211,11 +233,10 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
m_freeRanges.push_back({ offset, size });
|
||||
m_allocator->NotifyFreed(Memory->GetSize());
|
||||
}
|
||||
|
||||
bool D3D9MemoryChunk::IsEmpty() {
|
||||
std::lock_guard<dxvk::mutex> lock(m_mutex);
|
||||
bool D3D9MemoryChunk::IsEmpty() const {
|
||||
// Has to be protected by the allocator lock
|
||||
|
||||
return m_freeRanges.size() == 1
|
||||
&& m_freeRanges[0].length == m_size;
|
||||
|
@ -225,10 +246,6 @@ namespace dxvk {
|
|||
return m_allocator;
|
||||
}
|
||||
|
||||
HANDLE D3D9MemoryChunk::FileHandle() const {
|
||||
return m_mapping;
|
||||
}
|
||||
|
||||
|
||||
D3D9Memory::D3D9Memory(D3D9MemoryChunk* Chunk, size_t Offset, size_t Size)
|
||||
: m_chunk(Chunk), m_offset(Offset), m_size(Size) {}
|
||||
|
@ -260,11 +277,7 @@ namespace dxvk {
|
|||
if (m_ptr != nullptr)
|
||||
Unmap();
|
||||
|
||||
m_chunk->Free(this);
|
||||
if (m_chunk->IsEmpty()) {
|
||||
D3D9MemoryAllocator* allocator = m_chunk->Allocator();
|
||||
allocator->FreeChunk(m_chunk);
|
||||
}
|
||||
m_chunk->Allocator()->Free(this);
|
||||
m_chunk = nullptr;
|
||||
}
|
||||
|
||||
|
@ -275,14 +288,14 @@ namespace dxvk {
|
|||
if (unlikely(m_chunk == nullptr))
|
||||
return;
|
||||
|
||||
m_ptr = m_chunk->Map(this);
|
||||
m_ptr = m_chunk->Allocator()->Map(this);
|
||||
}
|
||||
|
||||
void D3D9Memory::Unmap() {
|
||||
if (unlikely(m_ptr == nullptr))
|
||||
return;
|
||||
|
||||
m_chunk->Unmap(this);
|
||||
m_chunk->Allocator()->Unmap(this);
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
|
||||
|
@ -298,15 +311,15 @@ namespace dxvk {
|
|||
return memory;
|
||||
}
|
||||
|
||||
uint32_t D3D9MemoryAllocator::MappedMemory() {
|
||||
uint32_t D3D9MemoryAllocator::MappedMemory() const {
|
||||
return m_allocatedMemory.load();
|
||||
}
|
||||
|
||||
uint32_t D3D9MemoryAllocator::UsedMemory() {
|
||||
uint32_t D3D9MemoryAllocator::UsedMemory() const {
|
||||
return m_allocatedMemory.load();
|
||||
}
|
||||
|
||||
uint32_t D3D9MemoryAllocator::AllocatedMemory() {
|
||||
uint32_t D3D9MemoryAllocator::AllocatedMemory() const {
|
||||
return m_allocatedMemory.load();
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ namespace dxvk {
|
|||
friend D3D9MemoryAllocator;
|
||||
|
||||
public:
|
||||
D3D9MemoryChunk(D3D9MemoryAllocator* Allocator, uint32_t Size);
|
||||
~D3D9MemoryChunk();
|
||||
|
||||
D3D9MemoryChunk (const D3D9MemoryChunk&) = delete;
|
||||
|
@ -47,29 +48,27 @@ namespace dxvk {
|
|||
D3D9MemoryChunk (D3D9MemoryChunk&& other) = delete;
|
||||
D3D9MemoryChunk& operator = (D3D9MemoryChunk&& other) = delete;
|
||||
|
||||
D3D9Memory Alloc(uint32_t Size);
|
||||
void Free(D3D9Memory* Memory);
|
||||
bool IsEmpty();
|
||||
uint32_t Size() const { return m_size; }
|
||||
D3D9MemoryAllocator* Allocator() const;
|
||||
HANDLE FileHandle() const;
|
||||
void* Map(D3D9Memory* memory);
|
||||
void Unmap(D3D9Memory* memory);
|
||||
|
||||
private:
|
||||
D3D9MemoryChunk(D3D9MemoryAllocator* Allocator, uint32_t Size);
|
||||
bool IsEmpty() const;
|
||||
uint32_t Size() const { return m_size; }
|
||||
|
||||
D3D9Memory AllocLocked(uint32_t Size);
|
||||
void FreeLocked(D3D9Memory* Memory);
|
||||
void* MapLocked(D3D9Memory* memory, uint32_t& mappedSize);
|
||||
uint32_t UnmapLocked(D3D9Memory* memory);
|
||||
|
||||
dxvk::mutex m_mutex;
|
||||
D3D9MemoryAllocator* m_allocator;
|
||||
HANDLE m_mapping;
|
||||
uint32_t m_size;
|
||||
uint32_t m_mappingGranularity;
|
||||
std::vector<D3D9MemoryRange> m_freeRanges;
|
||||
std::vector<D3D9MappingRange> m_mappingRanges;
|
||||
};
|
||||
|
||||
class D3D9Memory {
|
||||
friend D3D9MemoryChunk;
|
||||
friend D3D9MemoryAllocator;
|
||||
|
||||
public:
|
||||
D3D9Memory() {}
|
||||
|
@ -86,13 +85,13 @@ namespace dxvk {
|
|||
void Map();
|
||||
void Unmap();
|
||||
void* Ptr();
|
||||
D3D9MemoryChunk* GetChunk() const { return m_chunk; }
|
||||
size_t GetOffset() const { return m_offset; }
|
||||
size_t GetSize() const { return m_size; }
|
||||
|
||||
private:
|
||||
D3D9Memory(D3D9MemoryChunk* Chunk, size_t Offset, size_t Size);
|
||||
void Free();
|
||||
D3D9MemoryChunk* GetChunk() const { return m_chunk; }
|
||||
size_t GetOffset() const { return m_offset; }
|
||||
size_t GetSize() const { return m_size; }
|
||||
|
||||
D3D9MemoryChunk* m_chunk = nullptr;
|
||||
void* m_ptr = nullptr;
|
||||
|
@ -107,22 +106,26 @@ namespace dxvk {
|
|||
D3D9MemoryAllocator();
|
||||
~D3D9MemoryAllocator() = default;
|
||||
D3D9Memory Alloc(uint32_t Size);
|
||||
void FreeChunk(D3D9MemoryChunk* Chunk);
|
||||
void NotifyMapped(uint32_t Size);
|
||||
void NotifyUnmapped(uint32_t Size);
|
||||
void NotifyFreed(uint32_t Size);
|
||||
uint32_t MappedMemory();
|
||||
uint32_t UsedMemory();
|
||||
uint32_t AllocatedMemory();
|
||||
uint32_t MemoryGranularity() { return m_allocationGranularity; }
|
||||
D3D9Memory AllocFromChunk(D3D9MemoryChunk* Chunk, uint32_t Size);
|
||||
void Free(D3D9Memory* Memory);
|
||||
void* Map(D3D9Memory* Memory);
|
||||
void Unmap(D3D9Memory* Memory);
|
||||
uint32_t MappedMemory() const;
|
||||
uint32_t UsedMemory() const;
|
||||
uint32_t AllocatedMemory() const;
|
||||
uint32_t AllocationGranularity() const { return m_allocationGranularity; }
|
||||
uint32_t MappingGranularity() const { return m_mappingGranularity; }
|
||||
|
||||
private:
|
||||
void FreeChunk(D3D9MemoryChunk* Chunk);
|
||||
|
||||
dxvk::mutex m_mutex;
|
||||
std::vector<std::unique_ptr<D3D9MemoryChunk>> m_chunks;
|
||||
std::atomic<size_t> m_mappedMemory = 0;
|
||||
std::atomic<size_t> m_allocatedMemory = 0;
|
||||
std::atomic<size_t> m_usedMemory = 0;
|
||||
uint32_t m_allocationGranularity;
|
||||
uint32_t m_mappingGranularity;
|
||||
};
|
||||
|
||||
#else
|
||||
|
@ -144,7 +147,6 @@ namespace dxvk {
|
|||
void Map() {}
|
||||
void Unmap() {}
|
||||
void* Ptr() { return m_ptr; }
|
||||
size_t GetSize() const { return m_size; }
|
||||
|
||||
private:
|
||||
D3D9Memory(D3D9MemoryAllocator* pAllocator, size_t Size);
|
||||
|
@ -159,9 +161,9 @@ namespace dxvk {
|
|||
|
||||
public:
|
||||
D3D9Memory Alloc(uint32_t Size);
|
||||
uint32_t MappedMemory();
|
||||
uint32_t UsedMemory();
|
||||
uint32_t AllocatedMemory();
|
||||
uint32_t MappedMemory() const;
|
||||
uint32_t UsedMemory() const;
|
||||
uint32_t AllocatedMemory() const;
|
||||
void NotifyFreed(uint32_t Size) {
|
||||
m_allocatedMemory -= Size;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,6 @@ namespace dxvk {
|
|||
this->strictConstantCopies = config.getOption<bool> ("d3d9.strictConstantCopies", false);
|
||||
this->strictPow = config.getOption<bool> ("d3d9.strictPow", true);
|
||||
this->lenientClear = config.getOption<bool> ("d3d9.lenientClear", false);
|
||||
this->numBackBuffers = config.getOption<int32_t> ("d3d9.numBackBuffers", 0);
|
||||
this->deferSurfaceCreation = config.getOption<bool> ("d3d9.deferSurfaceCreation", false);
|
||||
this->samplerAnisotropy = config.getOption<int32_t> ("d3d9.samplerAnisotropy", -1);
|
||||
this->maxAvailableMemory = config.getOption<int32_t> ("d3d9.maxAvailableMemory", 4096);
|
||||
|
@ -80,6 +79,8 @@ namespace dxvk {
|
|||
// D3D8 options
|
||||
this->drefScaling = config.getOption<int32_t> ("d3d8.scaleDref", 0);
|
||||
|
||||
// Clamp the shader model value between 0 and 3
|
||||
this->shaderModel = dxvk::clamp(this->shaderModel, 0u, 3u);
|
||||
// Clamp LOD bias so that people don't abuse this in unintended ways
|
||||
this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f);
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace dxvk {
|
|||
int32_t maxFrameRate;
|
||||
|
||||
/// Set the max shader model the device can support in the caps.
|
||||
int32_t shaderModel;
|
||||
uint32_t shaderModel;
|
||||
|
||||
/// Whether or not to set the process as DPI aware in Windows when the API interface is created.
|
||||
bool dpiAware;
|
||||
|
@ -50,10 +50,6 @@ namespace dxvk {
|
|||
/// Whether or not to do a fast path clear if we're close enough to the whole render target.
|
||||
bool lenientClear;
|
||||
|
||||
/// Back buffer count for the Vulkan swap chain.
|
||||
/// Overrides buffer count in present parameters.
|
||||
int32_t numBackBuffers;
|
||||
|
||||
/// Defer surface creation
|
||||
bool deferSurfaceCreation;
|
||||
|
||||
|
|
|
@ -11,9 +11,11 @@ namespace dxvk {
|
|||
|
||||
public:
|
||||
|
||||
D3D9Resource(D3D9DeviceEx* pDevice)
|
||||
D3D9Resource(D3D9DeviceEx* pDevice, D3DPOOL Pool, bool Extended)
|
||||
: D3D9DeviceChild<Type...>(pDevice)
|
||||
, m_priority ( 0 ) { }
|
||||
, m_pool ( Pool )
|
||||
, m_priority ( 0 )
|
||||
, m_isExtended ( Extended ) { }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetPrivateData(
|
||||
REFGUID refguid,
|
||||
|
@ -72,11 +74,18 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE SetPriority(DWORD PriorityNew) {
|
||||
// Priority can only be set for D3DPOOL_MANAGED resources on
|
||||
// D3D9 interfaces, and for D3DPOOL_DEFAULT on D3D9Ex interfaces
|
||||
if (likely((m_pool == D3DPOOL_MANAGED && !m_isExtended)
|
||||
|| (m_pool == D3DPOOL_DEFAULT && m_isExtended))) {
|
||||
DWORD oldPriority = m_priority;
|
||||
m_priority = PriorityNew;
|
||||
return oldPriority;
|
||||
}
|
||||
|
||||
return m_priority;
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE GetPriority() {
|
||||
return m_priority;
|
||||
}
|
||||
|
@ -84,10 +93,12 @@ namespace dxvk {
|
|||
|
||||
protected:
|
||||
|
||||
const D3DPOOL m_pool;
|
||||
DWORD m_priority;
|
||||
|
||||
private:
|
||||
|
||||
const bool m_isExtended;
|
||||
ComPrivateData m_privateData;
|
||||
|
||||
};
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace dxvk {
|
|||
// do an or per-draw in the device.
|
||||
// We shift by 17 because 16 ps samplers + 1 dmap (tess)
|
||||
if (ShaderStage == VK_SHADER_STAGE_VERTEX_BIT)
|
||||
m_usedSamplers <<= caps::MaxTexturesPS + 1;
|
||||
m_usedSamplers <<= FirstVSSamplerSlot;
|
||||
|
||||
m_usedRTs = pModule->usedRTs();
|
||||
|
||||
|
@ -98,9 +98,6 @@ namespace dxvk {
|
|||
|
||||
DxsoModule module(reader);
|
||||
|
||||
if (module.info().majorVersion() > pDxbcModuleInfo->options.shaderModel)
|
||||
throw DxvkError("GetShaderModule: Out of range of supported shader model");
|
||||
|
||||
if (module.info().shaderStage() != ShaderStage)
|
||||
throw DxvkError("GetShaderModule: Bytecode does not match shader stage");
|
||||
|
||||
|
|
218
src/d3d9/d3d9_shader_validator.cpp
Normal file
218
src/d3d9/d3d9_shader_validator.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
#include "d3d9_shader_validator.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D9ShaderValidator::QueryInterface(REFIID riid, void** ppvObject) {
|
||||
if (ppvObject == nullptr)
|
||||
return E_POINTER;
|
||||
|
||||
*ppvObject = ref(this);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D9ShaderValidator::Begin(
|
||||
D3D9ShaderValidatorCallback pCallback,
|
||||
void* pUserData,
|
||||
DWORD Unknown) {
|
||||
if (unlikely(m_state != D3D9ShaderValidatorState::Begin)) {
|
||||
return ErrorCallback(nullptr, -1, 0, nullptr, 0,
|
||||
D3D9ShaderValidatorMessage::BeginOutOfOrder,
|
||||
"IDirect3DShaderValidator9::Begin called out of order. ::End must be called first.");
|
||||
}
|
||||
|
||||
m_callback = pCallback;
|
||||
m_userData = pUserData;
|
||||
m_state = D3D9ShaderValidatorState::ValidatingHeader;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D9ShaderValidator::Instruction(
|
||||
const char* pFile,
|
||||
UINT Line,
|
||||
const DWORD* pdwInst,
|
||||
DWORD cdw) {
|
||||
if (unlikely(pdwInst == nullptr || !cdw)) {
|
||||
return ErrorCallback(pFile, Line, 0, pdwInst, cdw,
|
||||
D3D9ShaderValidatorMessage::InstructionNullArgs,
|
||||
"IDirect3DShaderValidator9::Instruction called with NULL == pdwInst or 0 == cdw.");
|
||||
}
|
||||
|
||||
if (unlikely(m_state == D3D9ShaderValidatorState::Begin)) {
|
||||
return ErrorCallback(pFile, Line, 0, pdwInst, cdw,
|
||||
D3D9ShaderValidatorMessage::InstructionOutOfOrder,
|
||||
"IDirect3DShaderValidator9::Instruction called out of order. ::Begin must be called first.");
|
||||
} else if (unlikely(m_state == D3D9ShaderValidatorState::EndOfShader)) {
|
||||
return ErrorCallback(pFile, Line, 0, pdwInst, cdw,
|
||||
D3D9ShaderValidatorMessage::InstructionEndOfShader,
|
||||
"IDirect3DShaderValidator9::Instruction called out of order. After end token there should be no more instructions. Call ::End next.");
|
||||
} else if (unlikely(m_state == D3D9ShaderValidatorState::Error)) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if (m_state == D3D9ShaderValidatorState::ValidatingHeader)
|
||||
return ValidateHeader(pFile, Line, pdwInst, cdw);
|
||||
|
||||
DxsoCodeIter pdwInstIter{ reinterpret_cast<const uint32_t*>(pdwInst) };
|
||||
bool isEndToken = !m_ctx->decodeInstruction(pdwInstIter);
|
||||
const DxsoInstructionContext instContext = m_ctx->getInstructionContext();
|
||||
|
||||
if (isEndToken)
|
||||
return ValidateEndToken(pFile, Line, pdwInst, cdw);
|
||||
|
||||
// TODO: DxsoDecodeContext::decodeInstructionLength() does not currently appear
|
||||
// to return the correct token length in many cases, and as such dwordLength
|
||||
// will not be equal to cdw in many situations that are expected to pass validation
|
||||
//
|
||||
/*Logger::debug(str::format("IDirect3DShaderValidator9::Instruction: opcode ", instContext.instruction.opcode));
|
||||
// + 1 to account for the opcode...
|
||||
uint32_t dwordLength = instContext.instruction.tokenLength + 1;
|
||||
Logger::debug(str::format("IDirect3DShaderValidator9::Instruction: cdw ", cdw));
|
||||
Logger::debug(str::format("IDirect3DShaderValidator9::Instruction: dwordLength ", dwordLength));
|
||||
if (unlikely(cdw != dwordLength)) {
|
||||
return ErrorCallback(pFile, Line, 0x2,
|
||||
D3D9ShaderValidatorMessage::BadInstructionLength,
|
||||
str::format("Instruction length specified for instruction (", cdw, ") does not match the token count encountered (", dwordLength, "). Aborting validation."));
|
||||
}*/
|
||||
|
||||
// a maximum of 10 inputs are supported with PS 3.0 (validation required by The Void)
|
||||
if (m_isPixelShader && m_majorVersion == 3) {
|
||||
switch (instContext.instruction.opcode) {
|
||||
case DxsoOpcode::Comment:
|
||||
case DxsoOpcode::Def:
|
||||
case DxsoOpcode::DefB:
|
||||
case DxsoOpcode::DefI:
|
||||
break;
|
||||
|
||||
default:
|
||||
// Iterate over register tokens. Bit 31 of register tokens is always 1.
|
||||
for (uint32_t instNum = 1; instNum < cdw && (pdwInst[instNum] >> 31); instNum++) {
|
||||
DWORD regType = ((pdwInst[instNum] & D3DSP_REGTYPE_MASK) >> D3DSP_REGTYPE_SHIFT)
|
||||
| ((pdwInst[instNum] & D3DSP_REGTYPE_MASK2) >> D3DSP_REGTYPE_SHIFT2);
|
||||
DWORD regIndex = pdwInst[instNum] & D3DSP_REGNUM_MASK;
|
||||
|
||||
if (unlikely(regType == static_cast<DWORD>(DxsoRegisterType::Input) && regIndex >= 10)) {
|
||||
return ErrorCallback(pFile, Line, 0x2, pdwInst, cdw,
|
||||
instContext.instruction.opcode == DxsoOpcode::Dcl ?
|
||||
D3D9ShaderValidatorMessage::BadInputRegisterDeclaration :
|
||||
D3D9ShaderValidatorMessage::BadInputRegister,
|
||||
str::format("IDirect3DShaderValidator9::Instruction: PS input registers index #", regIndex, " not valid for operand ", instNum, "."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D9ShaderValidator::End() {
|
||||
if (unlikely(m_state == D3D9ShaderValidatorState::Error)) {
|
||||
return E_FAIL;
|
||||
} else if (unlikely(m_state == D3D9ShaderValidatorState::Begin)) {
|
||||
return ErrorCallback(nullptr, 0, 0, nullptr, 0,
|
||||
D3D9ShaderValidatorMessage::EndOutOfOrder,
|
||||
"IDirect3DShaderValidator9::End called out of order. Call to ::Begin, followed by calls to ::Instruction must occur first.");
|
||||
} else if (unlikely(m_state != D3D9ShaderValidatorState::EndOfShader)) {
|
||||
return ErrorCallback(nullptr, 0, 0, nullptr, 0,
|
||||
D3D9ShaderValidatorMessage::MissingEndToken,
|
||||
"IDirect3DShaderValidator9::End: Shader missing end token.");
|
||||
}
|
||||
|
||||
m_state = D3D9ShaderValidatorState::Begin;
|
||||
m_isPixelShader = false;
|
||||
m_majorVersion = 0;
|
||||
m_minorVersion = 0;
|
||||
m_callback = nullptr;
|
||||
m_userData = nullptr;
|
||||
m_ctx = nullptr;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT D3D9ShaderValidator::ValidateHeader(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw) {
|
||||
if (unlikely(cdw != 1)) {
|
||||
return ErrorCallback(pFile, Line, 0x6, pdwInst, cdw,
|
||||
D3D9ShaderValidatorMessage::BadVersionTokenLength,
|
||||
"IDirect3DShaderValidator9::Instruction: Bad version token. DWORD count > 1 given. Expected DWORD count to be 1 for version token.");
|
||||
}
|
||||
|
||||
DxsoReader reader = { reinterpret_cast<const char*>(pdwInst) };
|
||||
uint32_t headerToken = reader.readu32();
|
||||
uint32_t shaderType = headerToken & 0xffff0000;
|
||||
DxsoProgramType programType;
|
||||
|
||||
if (shaderType == 0xffff0000) { // Pixel Shader
|
||||
programType = DxsoProgramTypes::PixelShader;
|
||||
m_isPixelShader = true;
|
||||
} else if (shaderType == 0xfffe0000) { // Vertex Shader
|
||||
programType = DxsoProgramTypes::VertexShader;
|
||||
m_isPixelShader = false;
|
||||
} else {
|
||||
return ErrorCallback(pFile, Line, 0x6, pdwInst, cdw,
|
||||
D3D9ShaderValidatorMessage::BadVersionTokenType,
|
||||
"IDirect3DShaderValidator9::Instruction: Bad version token. It indicates neither a pixel shader nor a vertex shader.");
|
||||
}
|
||||
|
||||
m_majorVersion = D3DSHADER_VERSION_MAJOR(headerToken);
|
||||
m_minorVersion = D3DSHADER_VERSION_MINOR(headerToken);
|
||||
m_ctx = std::make_unique<DxsoDecodeContext>(DxsoProgramInfo{ programType, m_minorVersion, m_majorVersion });
|
||||
m_state = D3D9ShaderValidatorState::ValidatingInstructions;
|
||||
|
||||
const char* shaderTypeOutput = m_isPixelShader ? "PS" : "VS";
|
||||
Logger::debug(str::format("IDirect3DShaderValidator9::Instruction: Validating ",
|
||||
shaderTypeOutput, " version ", m_majorVersion, ".", m_minorVersion));
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT D3D9ShaderValidator::ValidateEndToken(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw) {
|
||||
// Reached the end token.
|
||||
if (unlikely(cdw != 1)) {
|
||||
return ErrorCallback(pFile, Line, 0x6, pdwInst, cdw,
|
||||
D3D9ShaderValidatorMessage::BadEndToken,
|
||||
"IDirect3DShaderValidator9::Instruction: Bad end token. DWORD count > 1 given. Expected DWORD count to be 1 for end token.");
|
||||
}
|
||||
|
||||
m_state = D3D9ShaderValidatorState::EndOfShader;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT D3D9ShaderValidator::ErrorCallback(
|
||||
const char* pFile,
|
||||
UINT Line,
|
||||
DWORD Unknown,
|
||||
const DWORD* pInstr,
|
||||
DWORD InstrLength,
|
||||
D3D9ShaderValidatorMessage MessageID,
|
||||
std::string Message) {
|
||||
if (m_callback)
|
||||
m_callback(pFile, Line, Unknown, MessageID, Message.c_str(), m_userData);
|
||||
|
||||
// TODO: Consider switching this to debug, once we're
|
||||
// confident the implementation doesn't cause any issues
|
||||
Logger::warn(Message);
|
||||
|
||||
// Log instruction that caused the error as raw bytecode
|
||||
if (Logger::logLevel() <= LogLevel::Debug && pInstr && InstrLength) {
|
||||
std::stringstream instMsg;
|
||||
|
||||
for (uint32_t i = 0; i < InstrLength; i++) {
|
||||
instMsg << (i ? "," : " [");
|
||||
instMsg << std::hex << std::setfill('0') << std::setw(8) << pInstr[i];
|
||||
instMsg << (i + 1 == InstrLength ? "]" : "");
|
||||
}
|
||||
|
||||
Logger::debug(instMsg.str());
|
||||
}
|
||||
|
||||
m_state = D3D9ShaderValidatorState::Error;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
}
|
|
@ -61,204 +61,35 @@ namespace dxvk {
|
|||
|
||||
public:
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) {
|
||||
if (ppvObject == nullptr)
|
||||
return E_POINTER;
|
||||
|
||||
*ppvObject = ref(this);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Begin(
|
||||
D3D9ShaderValidatorCallback pCallback,
|
||||
void* pUserData,
|
||||
DWORD Unknown) {
|
||||
if (unlikely(m_state != D3D9ShaderValidatorState::Begin)) {
|
||||
return ErrorCallback(nullptr, -1, 0,
|
||||
D3D9ShaderValidatorMessage::BeginOutOfOrder,
|
||||
"IDirect3DShaderValidator9::Begin called out of order. ::End must be called first.");
|
||||
}
|
||||
|
||||
m_callback = pCallback;
|
||||
m_userData = pUserData;
|
||||
m_state = D3D9ShaderValidatorState::ValidatingHeader;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
DWORD Unknown);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Instruction(
|
||||
const char* pFile,
|
||||
UINT Line,
|
||||
const DWORD* pdwInst,
|
||||
DWORD cdw) {
|
||||
if (unlikely(pdwInst == nullptr || !cdw)) {
|
||||
return ErrorCallback(pFile, Line, 0,
|
||||
D3D9ShaderValidatorMessage::InstructionNullArgs,
|
||||
"IDirect3DShaderValidator9::Instruction called with NULL == pdwInst or 0 == cdw.");
|
||||
}
|
||||
DWORD cdw);
|
||||
|
||||
if (unlikely(m_state == D3D9ShaderValidatorState::Begin)) {
|
||||
return ErrorCallback(pFile, Line, 0,
|
||||
D3D9ShaderValidatorMessage::InstructionOutOfOrder,
|
||||
"IDirect3DShaderValidator9::Instruction called out of order. ::Begin must be called first.");
|
||||
} else if (unlikely(m_state == D3D9ShaderValidatorState::EndOfShader)) {
|
||||
return ErrorCallback(pFile, Line, 0,
|
||||
D3D9ShaderValidatorMessage::InstructionEndOfShader,
|
||||
"IDirect3DShaderValidator9::Instruction called out of order. After end token there should be no more instructions. Call ::End next.");
|
||||
} else if (unlikely(m_state == D3D9ShaderValidatorState::Error)) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if (m_state == D3D9ShaderValidatorState::ValidatingHeader)
|
||||
return ValidateHeader(pFile, Line, pdwInst, cdw);
|
||||
|
||||
DxsoCodeIter pdwInstIter{ reinterpret_cast<const uint32_t*>(pdwInst) };
|
||||
bool isEndToken = !m_ctx->decodeInstruction(pdwInstIter);
|
||||
const DxsoInstructionContext instContext = m_ctx->getInstructionContext();
|
||||
|
||||
if (isEndToken)
|
||||
return ValidateEndToken(pFile, Line, pdwInst, cdw);
|
||||
|
||||
// TODO: DxsoDecodeContext::decodeInstructionLength() does not currently appear
|
||||
// to return the correct token length in many cases, and as such dwordLength
|
||||
// will not be equal to cdw in many situations that are expected to pass validation
|
||||
//
|
||||
/*Logger::debug(str::format("IDirect3DShaderValidator9::Instruction: opcode ", instContext.instruction.opcode));
|
||||
// + 1 to account for the opcode...
|
||||
uint32_t dwordLength = instContext.instruction.tokenLength + 1;
|
||||
Logger::debug(str::format("IDirect3DShaderValidator9::Instruction: cdw ", cdw));
|
||||
Logger::debug(str::format("IDirect3DShaderValidator9::Instruction: dwordLength ", dwordLength));
|
||||
if (unlikely(cdw != dwordLength)) {
|
||||
return ErrorCallback(pFile, Line, 0x2,
|
||||
D3D9ShaderValidatorMessage::BadInstructionLength,
|
||||
str::format("Instruction length specified for instruction (", cdw, ") does not match the token count encountered (", dwordLength, "). Aborting validation."));
|
||||
}*/
|
||||
|
||||
// a maximum of 10 inputs are supported with PS 3.0 (validation required by The Void)
|
||||
if (m_isPixelShader && m_majorVersion == 3) {
|
||||
switch (instContext.instruction.opcode) {
|
||||
case DxsoOpcode::Comment:
|
||||
case DxsoOpcode::Def:
|
||||
case DxsoOpcode::DefB:
|
||||
case DxsoOpcode::DefI:
|
||||
break;
|
||||
|
||||
default:
|
||||
// Iterate over register tokens. Bit 31 of register tokens is always 1.
|
||||
for (uint32_t instNum = 1; pdwInst[instNum] & 0x80000000; instNum++) {
|
||||
DWORD regType = ((pdwInst[instNum] & D3DSP_REGTYPE_MASK) >> D3DSP_REGTYPE_SHIFT)
|
||||
| ((pdwInst[instNum] & D3DSP_REGTYPE_MASK2) >> D3DSP_REGTYPE_SHIFT2);
|
||||
DWORD regIndex = pdwInst[instNum] & D3DSP_REGNUM_MASK;
|
||||
|
||||
if (unlikely(regType == static_cast<DWORD>(DxsoRegisterType::Input) && regIndex >= 10)) {
|
||||
return ErrorCallback(pFile, Line, 0x2,
|
||||
instContext.instruction.opcode == DxsoOpcode::Dcl ?
|
||||
D3D9ShaderValidatorMessage::BadInputRegisterDeclaration :
|
||||
D3D9ShaderValidatorMessage::BadInputRegister,
|
||||
"IDirect3DShaderValidator9::Instruction: Invalid number of PS input registers specified. Aborting validation.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE End() {
|
||||
if (unlikely(m_state == D3D9ShaderValidatorState::Error)) {
|
||||
return E_FAIL;
|
||||
} else if (unlikely(m_state == D3D9ShaderValidatorState::Begin)) {
|
||||
return ErrorCallback(nullptr, 0, 0,
|
||||
D3D9ShaderValidatorMessage::EndOutOfOrder,
|
||||
"IDirect3DShaderValidator9::End called out of order. Call to ::Begin, followed by calls to ::Instruction must occur first.");
|
||||
} else if (unlikely(m_state != D3D9ShaderValidatorState::EndOfShader)) {
|
||||
return ErrorCallback(nullptr, 0, 0,
|
||||
D3D9ShaderValidatorMessage::MissingEndToken,
|
||||
"IDirect3DShaderValidator9::End: Shader missing end token.");
|
||||
}
|
||||
|
||||
m_state = D3D9ShaderValidatorState::Begin;
|
||||
m_isPixelShader = false;
|
||||
m_majorVersion = 0;
|
||||
m_minorVersion = 0;
|
||||
m_callback = nullptr;
|
||||
m_userData = nullptr;
|
||||
m_ctx = nullptr;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE End();
|
||||
|
||||
private:
|
||||
|
||||
HRESULT ValidateHeader(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw) {
|
||||
if (unlikely(cdw != 1)) {
|
||||
return ErrorCallback(pFile, Line, 0x6,
|
||||
D3D9ShaderValidatorMessage::BadVersionTokenLength,
|
||||
"IDirect3DShaderValidator9::Instruction: Bad version token. DWORD count > 1 given. Expected DWORD count to be 1 for version token.");
|
||||
}
|
||||
HRESULT ValidateHeader(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw);
|
||||
|
||||
DxsoReader reader = { reinterpret_cast<const char*>(pdwInst) };
|
||||
uint32_t headerToken = reader.readu32();
|
||||
uint32_t shaderType = headerToken & 0xffff0000;
|
||||
DxsoProgramType programType;
|
||||
|
||||
if (shaderType == 0xffff0000) { // Pixel Shader
|
||||
programType = DxsoProgramTypes::PixelShader;
|
||||
m_isPixelShader = true;
|
||||
} else if (shaderType == 0xfffe0000) { // Vertex Shader
|
||||
programType = DxsoProgramTypes::VertexShader;
|
||||
m_isPixelShader = false;
|
||||
} else {
|
||||
return ErrorCallback(pFile, Line, 0x6,
|
||||
D3D9ShaderValidatorMessage::BadVersionTokenType,
|
||||
"IDirect3DShaderValidator9::Instruction: Bad version token. It indicates neither a pixel shader nor a vertex shader.");
|
||||
}
|
||||
|
||||
m_majorVersion = (headerToken >> 8) & 0xff;
|
||||
m_minorVersion = headerToken & 0xff;
|
||||
m_ctx = std::make_unique<DxsoDecodeContext>(DxsoProgramInfo{ programType, m_minorVersion, m_majorVersion });
|
||||
m_state = D3D9ShaderValidatorState::ValidatingInstructions;
|
||||
|
||||
const char* shaderTypeOutput = m_isPixelShader ? "PS" : "VS";
|
||||
Logger::debug(str::format("IDirect3DShaderValidator9::Instruction: Validating ",
|
||||
shaderTypeOutput, " version ", m_majorVersion, ".", m_minorVersion));
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT ValidateEndToken(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw) {
|
||||
// Reached the end token.
|
||||
if (unlikely(cdw != 1)) {
|
||||
return ErrorCallback(pFile, Line, 0x6,
|
||||
D3D9ShaderValidatorMessage::BadEndToken,
|
||||
"IDirect3DShaderValidator9::Instruction: Bad end token. DWORD count > 1 given. Expected DWORD count to be 1 for end token.");
|
||||
}
|
||||
|
||||
m_state = D3D9ShaderValidatorState::EndOfShader;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
HRESULT ValidateEndToken(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw);
|
||||
|
||||
HRESULT ErrorCallback(
|
||||
const char* pFile,
|
||||
UINT Line,
|
||||
DWORD Unknown,
|
||||
const DWORD* pInstr,
|
||||
DWORD InstrLength,
|
||||
D3D9ShaderValidatorMessage MessageID,
|
||||
const std::string& Message) {
|
||||
if (m_callback)
|
||||
m_callback(pFile, Line, Unknown, MessageID, Message.c_str(), m_userData);
|
||||
|
||||
// TODO: Consider switching this to debug, once we're
|
||||
// confident the implementation doesn't cause any issues
|
||||
Logger::warn(Message);
|
||||
|
||||
m_state = D3D9ShaderValidatorState::Error;
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
std::string Message);
|
||||
|
||||
bool m_isPixelShader = false;
|
||||
uint32_t m_majorVersion = 0;
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace dxvk {
|
|||
streamFreq[i] = 1;
|
||||
|
||||
for (uint32_t i = 0; i < enabledLightIndices.size(); i++)
|
||||
enabledLightIndices[i] = UINT32_MAX;
|
||||
enabledLightIndices[i] = std::numeric_limits<uint32_t>::max();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -204,7 +204,7 @@ namespace dxvk {
|
|||
if (m_state.IsLightEnabled(Index) == !!Enable)
|
||||
return D3D_OK;
|
||||
|
||||
uint32_t searchIndex = UINT32_MAX;
|
||||
uint32_t searchIndex = std::numeric_limits<uint32_t>::max();
|
||||
uint32_t setIndex = Index;
|
||||
|
||||
if (!Enable)
|
||||
|
@ -436,7 +436,7 @@ namespace dxvk {
|
|||
void D3D9StateBlock::CapturePixelSamplerStates() {
|
||||
m_captures.flags.set(D3D9CapturedStateFlag::SamplerStates);
|
||||
|
||||
for (uint32_t i = 0; i < caps::MaxTexturesPS + 1; i++) {
|
||||
for (uint32_t i = 0; i < FirstVSSamplerSlot; i++) {
|
||||
m_captures.samplers.set(i, true);
|
||||
|
||||
m_captures.samplerStates[i].set(D3DSAMP_ADDRESSU, true);
|
||||
|
@ -519,7 +519,7 @@ namespace dxvk {
|
|||
void D3D9StateBlock::CaptureVertexSamplerStates() {
|
||||
m_captures.flags.set(D3D9CapturedStateFlag::SamplerStates);
|
||||
|
||||
for (uint32_t i = caps::MaxTexturesPS + 1; i < SamplerCount; i++) {
|
||||
for (uint32_t i = FirstVSSamplerSlot; i < SamplerCount; i++) {
|
||||
m_captures.samplers.set(i, true);
|
||||
m_captures.samplerStates[i].set(D3DSAMP_DMAPOFFSET, true);
|
||||
}
|
||||
|
|
|
@ -12,12 +12,13 @@ namespace dxvk {
|
|||
|
||||
D3D9Subresource(
|
||||
D3D9DeviceEx* pDevice,
|
||||
const bool Extended,
|
||||
D3D9CommonTexture* pTexture,
|
||||
UINT Face,
|
||||
UINT MipLevel,
|
||||
IDirect3DBaseTexture9* pBaseTexture,
|
||||
IUnknown* pContainer)
|
||||
: D3D9Resource<Type...>(pDevice),
|
||||
: D3D9Resource<Type...>(pDevice, pTexture->GetPool(), Extended),
|
||||
m_container (pContainer),
|
||||
m_baseTexture (pBaseTexture),
|
||||
m_texture (pTexture),
|
||||
|
|
|
@ -11,10 +11,12 @@ namespace dxvk {
|
|||
D3D9Surface::D3D9Surface(
|
||||
D3D9DeviceEx* pDevice,
|
||||
const D3D9_COMMON_TEXTURE_DESC* pDesc,
|
||||
const bool Extended,
|
||||
IUnknown* pContainer,
|
||||
HANDLE* pSharedHandle)
|
||||
: D3D9SurfaceBase(
|
||||
pDevice,
|
||||
Extended,
|
||||
new D3D9CommonTexture( pDevice, this, pDesc, D3DRTYPE_SURFACE, pSharedHandle),
|
||||
0, 0,
|
||||
nullptr,
|
||||
|
@ -22,21 +24,25 @@ namespace dxvk {
|
|||
|
||||
D3D9Surface::D3D9Surface(
|
||||
D3D9DeviceEx* pDevice,
|
||||
const D3D9_COMMON_TEXTURE_DESC* pDesc)
|
||||
const D3D9_COMMON_TEXTURE_DESC* pDesc,
|
||||
const bool Extended)
|
||||
: D3D9Surface(
|
||||
pDevice,
|
||||
pDesc,
|
||||
Extended,
|
||||
nullptr,
|
||||
nullptr) { }
|
||||
|
||||
D3D9Surface::D3D9Surface(
|
||||
D3D9DeviceEx* pDevice,
|
||||
const bool Extended,
|
||||
D3D9CommonTexture* pTexture,
|
||||
UINT Face,
|
||||
UINT MipLevel,
|
||||
IDirect3DBaseTexture9* pBaseTexture)
|
||||
: D3D9SurfaceBase(
|
||||
pDevice,
|
||||
Extended,
|
||||
pTexture,
|
||||
Face, MipLevel,
|
||||
pBaseTexture,
|
||||
|
@ -124,17 +130,19 @@ namespace dxvk {
|
|||
return D3DERR_INVALIDCALL;
|
||||
|
||||
D3DBOX box;
|
||||
auto& desc = *(m_texture->Desc());
|
||||
D3DRESOURCETYPE type = m_texture->GetType();
|
||||
|
||||
if (m_texture->Device()->IsD3D8Compatible() && type != D3DRTYPE_TEXTURE) {
|
||||
// D3D8 LockRect clears any existing content present in
|
||||
// pLockedRect for anything beside D3DRTYPE_TEXTURE surfaces
|
||||
// LockRect clears any existing content present in pLockedRect,
|
||||
// for surfaces in D3DPOOL_DEFAULT. D3D8 additionally clears the content
|
||||
// for non-D3DPOOL_DEFAULT surfaces if their type is not D3DRTYPE_TEXTURE.
|
||||
if (desc.Pool == D3DPOOL_DEFAULT
|
||||
|| (m_texture->Device()->IsD3D8Compatible() && type != D3DRTYPE_TEXTURE)) {
|
||||
pLockedRect->pBits = nullptr;
|
||||
pLockedRect->Pitch = 0;
|
||||
}
|
||||
|
||||
if (unlikely(pRect != nullptr)) {
|
||||
auto& desc = *(m_texture->Desc());
|
||||
D3D9_FORMAT_BLOCK_SIZE blockSize = GetFormatAlignedBlockSize(desc.Format);
|
||||
|
||||
bool isBlockAlignedFormat = blockSize.Width > 0 && blockSize.Height > 0;
|
||||
|
|
|
@ -20,15 +20,18 @@ namespace dxvk {
|
|||
D3D9Surface(
|
||||
D3D9DeviceEx* pDevice,
|
||||
const D3D9_COMMON_TEXTURE_DESC* pDesc,
|
||||
const bool Extended,
|
||||
IUnknown* pContainer,
|
||||
HANDLE* pSharedHandle);
|
||||
|
||||
D3D9Surface(
|
||||
D3D9DeviceEx* pDevice,
|
||||
const D3D9_COMMON_TEXTURE_DESC* pDesc);
|
||||
const D3D9_COMMON_TEXTURE_DESC* pDesc,
|
||||
const bool Extended);
|
||||
|
||||
D3D9Surface(
|
||||
D3D9DeviceEx* pDevice,
|
||||
const bool Extended,
|
||||
D3D9CommonTexture* pTexture,
|
||||
UINT Face,
|
||||
UINT MipLevel,
|
||||
|
|
|
@ -23,10 +23,12 @@ namespace dxvk {
|
|||
D3D9SwapChainEx::D3D9SwapChainEx(
|
||||
D3D9DeviceEx* pDevice,
|
||||
D3DPRESENT_PARAMETERS* pPresentParams,
|
||||
const D3DDISPLAYMODEEX* pFullscreenDisplayMode)
|
||||
const D3DDISPLAYMODEEX* pFullscreenDisplayMode,
|
||||
bool EnableLatencyTracking)
|
||||
: D3D9SwapChainExBase(pDevice)
|
||||
, m_device (pDevice->GetDXVKDevice())
|
||||
, m_frameLatencyCap (pDevice->GetOptions()->maxFrameLatency)
|
||||
, m_latencyTracking (EnableLatencyTracking)
|
||||
, m_swapchainExt (this) {
|
||||
this->NormalizePresentParameters(pPresentParams);
|
||||
m_presentParams = *pPresentParams;
|
||||
|
@ -36,18 +38,10 @@ namespace dxvk {
|
|||
|
||||
UpdatePresentRegion(nullptr, nullptr);
|
||||
|
||||
if (m_window) {
|
||||
CreatePresenter();
|
||||
|
||||
if (!pDevice->GetOptions()->deferSurfaceCreation)
|
||||
RecreateSwapChain();
|
||||
}
|
||||
|
||||
if (FAILED(CreateBackBuffers(m_presentParams.BackBufferCount, m_presentParams.Flags)))
|
||||
throw DxvkError("D3D9: Failed to create swapchain backbuffers");
|
||||
|
||||
CreateBlitter();
|
||||
CreateHud();
|
||||
|
||||
InitRamp();
|
||||
|
||||
|
@ -78,8 +72,12 @@ namespace dxvk {
|
|||
ResetWindowProc(m_window);
|
||||
RestoreDisplayMode(m_monitor);
|
||||
|
||||
m_device->waitForSubmission(&m_presentStatus);
|
||||
m_device->waitForIdle();
|
||||
for (auto& p : m_presenters) {
|
||||
if (p.second.presenter) {
|
||||
p.second.presenter->destroyResources();
|
||||
p.second.presenter = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
m_parent->DecrementLosableCounter();
|
||||
}
|
||||
|
@ -148,27 +146,26 @@ namespace dxvk {
|
|||
if (options->presentInterval >= 0)
|
||||
presentInterval = options->presentInterval;
|
||||
|
||||
m_window = m_presentParams.hDeviceWindow;
|
||||
HWND window = m_presentParams.hDeviceWindow;
|
||||
|
||||
if (hDestWindowOverride != nullptr)
|
||||
m_window = hDestWindowOverride;
|
||||
window = hDestWindowOverride;
|
||||
|
||||
if (m_window == nullptr)
|
||||
return D3D_OK;
|
||||
|
||||
UpdateWindowCtx();
|
||||
|
||||
bool recreate = false;
|
||||
recreate |= m_wctx->presenter == nullptr;
|
||||
if (options->deferSurfaceCreation)
|
||||
recreate |= m_parent->IsDeviceReset();
|
||||
|
||||
if (m_wctx->presenter != nullptr) {
|
||||
m_dirty |= m_wctx->presenter->setSyncInterval(presentInterval) != VK_SUCCESS;
|
||||
m_dirty |= !m_wctx->presenter->hasSwapChain();
|
||||
if (m_window != window) {
|
||||
m_window = window;
|
||||
m_displayRefreshRateDirty = true;
|
||||
}
|
||||
|
||||
m_dirty |= UpdatePresentRegion(pSourceRect, pDestRect);
|
||||
m_dirty |= recreate;
|
||||
if (!UpdateWindowCtx())
|
||||
return D3D_OK;
|
||||
|
||||
if (options->deferSurfaceCreation && IsDeviceReset(m_wctx))
|
||||
m_wctx->presenter->invalidateSurface();
|
||||
|
||||
m_wctx->presenter->setSyncInterval(presentInterval);
|
||||
|
||||
UpdatePresentRegion(pSourceRect, pDestRect);
|
||||
UpdatePresentParameters();
|
||||
|
||||
#ifdef _WIN32
|
||||
const bool useGDIFallback = m_partialCopy && !HasFrontBuffer();
|
||||
|
@ -177,18 +174,10 @@ namespace dxvk {
|
|||
#endif
|
||||
|
||||
try {
|
||||
if (recreate)
|
||||
CreatePresenter();
|
||||
|
||||
if (std::exchange(m_dirty, false))
|
||||
RecreateSwapChain();
|
||||
|
||||
// We aren't going to device loss simply because
|
||||
// 99% of D3D9 games don't handle this properly and
|
||||
// just end up crashing (like with alt-tab loss)
|
||||
if (!m_wctx->presenter->hasSwapChain())
|
||||
return D3D_OK;
|
||||
|
||||
UpdateWindowedRefreshRate();
|
||||
UpdateTargetFrameRate(presentInterval);
|
||||
PresentImage(presentInterval);
|
||||
return D3D_OK;
|
||||
|
@ -206,7 +195,7 @@ namespace dxvk {
|
|||
#define DCX_USESTYLE 0x00010000
|
||||
|
||||
HRESULT D3D9SwapChainEx::PresentImageGDI(HWND Window) {
|
||||
m_parent->EndFrame();
|
||||
m_parent->EndFrame(nullptr);
|
||||
m_parent->Flush();
|
||||
|
||||
if (!std::exchange(m_warnedAboutGDIFallback, true))
|
||||
|
@ -607,12 +596,8 @@ namespace dxvk {
|
|||
|
||||
HRESULT hr = D3D_OK;
|
||||
|
||||
this->SynchronizePresent();
|
||||
this->NormalizePresentParameters(pPresentParams);
|
||||
|
||||
m_dirty |= m_presentParams.BackBufferFormat != pPresentParams->BackBufferFormat
|
||||
|| m_presentParams.BackBufferCount != pPresentParams->BackBufferCount;
|
||||
|
||||
bool changeFullscreen = m_presentParams.Windowed != pPresentParams->Windowed;
|
||||
|
||||
if (pPresentParams->Windowed) {
|
||||
|
@ -644,6 +629,8 @@ namespace dxvk {
|
|||
if (changeFullscreen)
|
||||
SetGammaRamp(0, &m_ramp);
|
||||
|
||||
UpdatePresentParameters();
|
||||
|
||||
hr = CreateBackBuffers(m_presentParams.BackBufferCount, m_presentParams.Flags);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
@ -663,17 +650,17 @@ namespace dxvk {
|
|||
|
||||
static bool validateGammaRamp(const WORD (&ramp)[256]) {
|
||||
if (ramp[0] >= ramp[std::size(ramp) - 1]) {
|
||||
Logger::err("validateGammaRamp: ramp inverted or flat");
|
||||
Logger::warn("validateGammaRamp: ramp inverted or flat");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < std::size(ramp); i++) {
|
||||
if (ramp[i] < ramp[i - 1]) {
|
||||
Logger::err("validateGammaRamp: ramp not monotonically increasing");
|
||||
Logger::warn("validateGammaRamp: ramp not monotonically increasing");
|
||||
return false;
|
||||
}
|
||||
if (ramp[i] - ramp[i - 1] >= UINT16_MAX / 2) {
|
||||
Logger::err("validateGammaRamp: huuuge jump");
|
||||
Logger::warn("validateGammaRamp: huuuge jump");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -730,16 +717,24 @@ namespace dxvk {
|
|||
|
||||
|
||||
void D3D9SwapChainEx::Invalidate(HWND hWindow) {
|
||||
if (hWindow == nullptr)
|
||||
if (!hWindow)
|
||||
hWindow = m_parent->GetWindow();
|
||||
|
||||
if (m_presenters.count(hWindow)) {
|
||||
if (m_wctx == &m_presenters[hWindow])
|
||||
m_wctx = nullptr;
|
||||
m_presenters.erase(hWindow);
|
||||
auto entry = m_presenters.find(hWindow);
|
||||
|
||||
m_device->waitForSubmission(&m_presentStatus);
|
||||
m_device->waitForIdle();
|
||||
if (entry != m_presenters.end()) {
|
||||
if (entry->second.presenter) {
|
||||
entry->second.presenter->destroyResources();
|
||||
entry->second.presenter = nullptr;
|
||||
|
||||
if (m_presentParams.hDeviceWindow == hWindow)
|
||||
DestroyLatencyTracker();
|
||||
}
|
||||
|
||||
if (m_wctx == &entry->second)
|
||||
m_wctx = nullptr;
|
||||
|
||||
m_presenters.erase(entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -749,7 +744,7 @@ namespace dxvk {
|
|||
|
||||
m_blitter->setCursorTexture(
|
||||
cursorSize,
|
||||
VK_FORMAT_B8G8R8A8_UNORM,
|
||||
VK_FORMAT_B8G8R8A8_SRGB,
|
||||
(void *) pCursorBitmap);
|
||||
}
|
||||
|
||||
|
@ -819,39 +814,30 @@ namespace dxvk {
|
|||
|
||||
|
||||
void D3D9SwapChainEx::PresentImage(UINT SyncInterval) {
|
||||
m_parent->EndFrame();
|
||||
m_parent->EndFrame(m_latencyTracker);
|
||||
m_parent->Flush();
|
||||
|
||||
if (m_latencyTracker)
|
||||
m_latencyTracker->notifyCpuPresentBegin(m_wctx->frameId + 1u);
|
||||
|
||||
// Retrieve the image and image view to present
|
||||
VkResult status = VK_SUCCESS;
|
||||
|
||||
Rc<DxvkImage> swapImage = m_backBuffers[0]->GetCommonTexture()->GetImage();
|
||||
Rc<DxvkImageView> swapImageView = m_backBuffers[0]->GetImageView(false);
|
||||
|
||||
for (uint32_t i = 0; i < SyncInterval || i < 1; i++) {
|
||||
SynchronizePresent();
|
||||
|
||||
// Presentation semaphores and WSI swap chain image
|
||||
PresenterInfo info = m_wctx->presenter->info();
|
||||
PresenterSync sync = { };
|
||||
Rc<DxvkImage> backBuffer;
|
||||
|
||||
uint32_t imageIndex = 0;
|
||||
status = m_wctx->presenter->acquireNextImage(sync, backBuffer);
|
||||
|
||||
VkResult status = m_wctx->presenter->acquireNextImage(sync, imageIndex);
|
||||
|
||||
while (status != VK_SUCCESS) {
|
||||
RecreateSwapChain();
|
||||
|
||||
info = m_wctx->presenter->info();
|
||||
status = m_wctx->presenter->acquireNextImage(sync, imageIndex);
|
||||
|
||||
if (status == VK_SUBOPTIMAL_KHR)
|
||||
if (status < 0 || status == VK_NOT_READY) {
|
||||
status = i ? VK_SUCCESS : status;
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_hdrMetadata && m_dirtyHdrMetadata) {
|
||||
m_wctx->presenter->setHdrMetadata(*m_hdrMetadata);
|
||||
m_dirtyHdrMetadata = false;
|
||||
}
|
||||
|
||||
VkRect2D srcRect = {
|
||||
{ int32_t(m_srcRect.left), int32_t(m_srcRect.top) },
|
||||
{ uint32_t(m_srcRect.right - m_srcRect.left), uint32_t(m_srcRect.bottom - m_srcRect.top) } };
|
||||
|
@ -866,55 +852,77 @@ namespace dxvk {
|
|||
|
||||
// Present from CS thread so that we don't
|
||||
// have to synchronize with it first.
|
||||
m_presentStatus.result = VK_NOT_READY;
|
||||
DxvkImageViewKey viewInfo;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
viewInfo.format = backBuffer->info().format;
|
||||
viewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
viewInfo.mipIndex = 0u;
|
||||
viewInfo.mipCount = 1u;
|
||||
viewInfo.layerIndex = 0u;
|
||||
viewInfo.layerCount = 1u;
|
||||
|
||||
m_parent->EmitCs([
|
||||
cPresentStatus = &m_presentStatus,
|
||||
cDevice = m_device,
|
||||
cPresenter = m_wctx->presenter,
|
||||
cBlitter = m_blitter,
|
||||
cColorSpace = m_colorspace,
|
||||
cSrcView = swapImageView,
|
||||
cSrcRect = srcRect,
|
||||
cDstView = m_wctx->imageViews.at(imageIndex),
|
||||
cDstView = backBuffer->createView(viewInfo),
|
||||
cDstRect = dstRect,
|
||||
cRepeat = i,
|
||||
cSync = sync,
|
||||
cHud = m_hud,
|
||||
cFrameId = m_wctx->frameId
|
||||
cFrameId = m_wctx->frameId,
|
||||
cLatency = m_latencyTracker
|
||||
] (DxvkContext* ctx) {
|
||||
// Update back buffer color space as necessary
|
||||
if (cSrcView->image()->info().colorSpace != cColorSpace) {
|
||||
DxvkImageUsageInfo usage = { };
|
||||
usage.colorSpace = cColorSpace;
|
||||
|
||||
ctx->ensureImageCompatibility(cSrcView->image(), usage);
|
||||
}
|
||||
|
||||
// Blit back buffer onto Vulkan swap chain
|
||||
auto contextObjects = ctx->beginExternalRendering();
|
||||
|
||||
cBlitter->beginPresent(contextObjects,
|
||||
cDstView, cColorSpace, cDstRect,
|
||||
cSrcView, cColorSpace, cSrcRect);
|
||||
|
||||
if (cHud) {
|
||||
if (!cRepeat)
|
||||
cHud->update();
|
||||
|
||||
cHud->render(contextObjects, cDstView, cColorSpace);
|
||||
}
|
||||
|
||||
cBlitter->endPresent(contextObjects, cDstView, cColorSpace);
|
||||
cBlitter->present(contextObjects,
|
||||
cDstView, cDstRect, cSrcView, cSrcRect);
|
||||
|
||||
// Submit command list and present
|
||||
ctx->synchronizeWsi(cSync);
|
||||
ctx->flushCommandList(nullptr);
|
||||
ctx->flushCommandList(nullptr, nullptr);
|
||||
|
||||
uint64_t frameId = cRepeat ? 0 : cFrameId;
|
||||
|
||||
cDevice->presentImage(cPresenter,
|
||||
cPresenter->info().presentMode,
|
||||
frameId, cPresentStatus);
|
||||
cDevice->presentImage(cPresenter, cLatency, frameId, nullptr);
|
||||
});
|
||||
|
||||
m_parent->FlushCsChunk();
|
||||
}
|
||||
|
||||
if (m_latencyTracker) {
|
||||
if (status == VK_SUCCESS)
|
||||
m_latencyTracker->notifyCpuPresentEnd(m_wctx->frameId);
|
||||
else
|
||||
m_latencyTracker->discardTimings();
|
||||
}
|
||||
|
||||
SyncFrameLatency();
|
||||
|
||||
DxvkLatencyStats latencyStats = { };
|
||||
|
||||
if (m_latencyTracker && status == VK_SUCCESS) {
|
||||
latencyStats = m_latencyTracker->getStatistics(m_wctx->frameId);
|
||||
m_latencyTracker->sleepAndBeginFrame(m_wctx->frameId + 1, std::abs(m_targetFrameRate));
|
||||
|
||||
m_parent->BeginFrame(m_latencyTracker, m_wctx->frameId + 1u);
|
||||
}
|
||||
|
||||
if (m_latencyHud)
|
||||
m_latencyHud->accumulateStats(latencyStats);
|
||||
|
||||
// Rotate swap chain buffers so that the back
|
||||
// buffer at index 0 becomes the front buffer.
|
||||
for (uint32_t i = 1; i < m_backBuffers.size(); i++)
|
||||
|
@ -924,110 +932,29 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void D3D9SwapChainEx::SynchronizePresent() {
|
||||
// Recreate swap chain if the previous present call failed
|
||||
VkResult status = m_device->waitForSubmission(&m_presentStatus);
|
||||
|
||||
if (status != VK_SUCCESS)
|
||||
RecreateSwapChain();
|
||||
}
|
||||
|
||||
void D3D9SwapChainEx::RecreateSwapChain() {
|
||||
// Ensure that we can safely destroy the swap chain
|
||||
m_device->waitForSubmission(&m_presentStatus);
|
||||
m_device->waitForIdle();
|
||||
|
||||
m_presentStatus.result = VK_SUCCESS;
|
||||
|
||||
Rc<Presenter> D3D9SwapChainEx::CreatePresenter(HWND Window, Rc<sync::Signal> Signal) {
|
||||
PresenterDesc presenterDesc;
|
||||
presenterDesc.imageExtent = GetPresentExtent();
|
||||
presenterDesc.imageCount = PickImageCount(m_presentParams.BackBufferCount + 1);
|
||||
presenterDesc.numFormats = PickFormats(EnumerateFormat(m_presentParams.BackBufferFormat), presenterDesc.formats);
|
||||
presenterDesc.deferSurfaceCreation = m_parent->GetOptions()->deferSurfaceCreation;
|
||||
|
||||
VkResult vr = m_wctx->presenter->recreateSwapChain(presenterDesc);
|
||||
Rc<Presenter> presenter = new Presenter(m_device, Signal, presenterDesc, [
|
||||
cDevice = m_device,
|
||||
cWindow = Window
|
||||
] (VkSurfaceKHR* surface) {
|
||||
auto vki = cDevice->adapter()->vki();
|
||||
|
||||
if (vr == VK_ERROR_SURFACE_LOST_KHR) {
|
||||
vr = m_wctx->presenter->recreateSurface([this] (VkSurfaceKHR* surface) {
|
||||
return CreateSurface(surface);
|
||||
});
|
||||
|
||||
if (vr)
|
||||
throw DxvkError(str::format("D3D9SwapChainEx: Failed to recreate surface: ", vr));
|
||||
|
||||
vr = m_wctx->presenter->recreateSwapChain(presenterDesc);
|
||||
}
|
||||
|
||||
if (vr)
|
||||
throw DxvkError(str::format("D3D9SwapChainEx: Failed to recreate swap chain: ", vr));
|
||||
|
||||
CreateRenderTargetViews();
|
||||
}
|
||||
|
||||
|
||||
void D3D9SwapChainEx::CreatePresenter() {
|
||||
// Ensure that we can safely destroy the swap chain
|
||||
m_device->waitForSubmission(&m_presentStatus);
|
||||
m_device->waitForIdle();
|
||||
|
||||
m_presentStatus.result = VK_SUCCESS;
|
||||
|
||||
PresenterDesc presenterDesc;
|
||||
presenterDesc.imageExtent = GetPresentExtent();
|
||||
presenterDesc.imageCount = PickImageCount(m_presentParams.BackBufferCount + 1);
|
||||
presenterDesc.numFormats = PickFormats(EnumerateFormat(m_presentParams.BackBufferFormat), presenterDesc.formats);
|
||||
|
||||
m_wctx->presenter = new Presenter(m_device, m_wctx->frameLatencySignal, presenterDesc);
|
||||
}
|
||||
|
||||
|
||||
VkResult D3D9SwapChainEx::CreateSurface(VkSurfaceKHR* pSurface) {
|
||||
auto vki = m_device->adapter()->vki();
|
||||
|
||||
return wsi::createSurface(m_window,
|
||||
return wsi::createSurface(cWindow,
|
||||
vki->getLoaderProc(),
|
||||
vki->instance(),
|
||||
pSurface);
|
||||
}
|
||||
surface);
|
||||
});
|
||||
|
||||
presenter->setSurfaceExtent(m_swapchainExtent);
|
||||
presenter->setSurfaceFormat(GetSurfaceFormat());
|
||||
|
||||
void D3D9SwapChainEx::CreateRenderTargetViews() {
|
||||
PresenterInfo info = m_wctx->presenter->info();
|
||||
if (m_hdrMetadata)
|
||||
presenter->setHdrMetadata(*m_hdrMetadata);
|
||||
|
||||
m_wctx->imageViews.clear();
|
||||
m_wctx->imageViews.resize(info.imageCount);
|
||||
|
||||
DxvkImageCreateInfo imageInfo;
|
||||
imageInfo.type = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.format = info.format.format;
|
||||
imageInfo.flags = 0;
|
||||
imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageInfo.extent = { info.imageExtent.width, info.imageExtent.height, 1 };
|
||||
imageInfo.numLayers = 1;
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
imageInfo.stages = 0;
|
||||
imageInfo.access = 0;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
imageInfo.shared = VK_TRUE;
|
||||
|
||||
DxvkImageViewKey viewInfo;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = info.format.format;
|
||||
viewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
viewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
viewInfo.mipIndex = 0;
|
||||
viewInfo.mipCount = 1;
|
||||
viewInfo.layerIndex = 0;
|
||||
viewInfo.layerCount = 1;
|
||||
|
||||
for (uint32_t i = 0; i < info.imageCount; i++) {
|
||||
VkImage imageHandle = m_wctx->presenter->getImage(i).image;
|
||||
|
||||
Rc<DxvkImage> image = m_device->importImage(imageInfo,
|
||||
imageHandle, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
m_wctx->imageViews[i] = image->createView(viewInfo);
|
||||
}
|
||||
return presenter;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1039,20 +966,27 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void D3D9SwapChainEx::UpdateWindowCtx() {
|
||||
if (m_window == nullptr)
|
||||
return;
|
||||
bool D3D9SwapChainEx::UpdateWindowCtx() {
|
||||
if (!m_window)
|
||||
return false;
|
||||
|
||||
if (!m_presenters.count(m_window)) {
|
||||
auto res = m_presenters.emplace(
|
||||
auto entry = m_presenters.find(m_window);
|
||||
|
||||
if (entry == m_presenters.end()) {
|
||||
entry = m_presenters.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(m_window),
|
||||
std::forward_as_tuple());
|
||||
std::forward_as_tuple()).first;
|
||||
|
||||
auto& wctx = res.first->second;
|
||||
wctx.frameLatencySignal = new sync::Fence(wctx.frameId);
|
||||
entry->second.frameLatencySignal = new sync::Fence(entry->second.frameId);
|
||||
entry->second.presenter = CreatePresenter(m_window, entry->second.frameLatencySignal);
|
||||
|
||||
if (m_presentParams.hDeviceWindow == m_window && m_latencyTracking)
|
||||
m_latencyTracker = m_device->createLatencyTracker(entry->second.presenter);
|
||||
}
|
||||
m_wctx = &m_presenters[m_window];
|
||||
|
||||
m_wctx = &entry->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1088,7 +1022,7 @@ namespace dxvk {
|
|||
for (uint32_t i = 0; i < NumBuffers; i++) {
|
||||
D3D9Surface* surface;
|
||||
try {
|
||||
surface = new D3D9Surface(m_parent, &desc, this, nullptr);
|
||||
surface = new D3D9Surface(m_parent, &desc, m_parent->IsExtended(), this, nullptr);
|
||||
m_parent->IncrementLosableCounter();
|
||||
} catch (const DxvkError& e) {
|
||||
DestroyBackBuffers();
|
||||
|
@ -1121,23 +1055,24 @@ namespace dxvk {
|
|||
|
||||
|
||||
void D3D9SwapChainEx::CreateBlitter() {
|
||||
m_blitter = new DxvkSwapchainBlitter(m_device);
|
||||
}
|
||||
Rc<hud::Hud> hud = hud::Hud::createHud(m_device);
|
||||
|
||||
if (hud) {
|
||||
m_apiHud = hud->addItem<hud::HudClientApiItem>("api", 1, GetApiName());
|
||||
|
||||
void D3D9SwapChainEx::CreateHud() {
|
||||
m_hud = hud::Hud::createHud(m_device);
|
||||
if (m_latencyTracking)
|
||||
m_latencyHud = hud->addItem<hud::HudLatencyItem>("latency", 4);
|
||||
|
||||
if (m_hud != nullptr) {
|
||||
m_hud->addItem<hud::HudClientApiItem>("api", 1, GetApiName());
|
||||
m_hud->addItem<hud::HudSamplerCount>("samplers", -1, m_parent);
|
||||
m_hud->addItem<hud::HudFixedFunctionShaders>("ffshaders", -1, m_parent);
|
||||
m_hud->addItem<hud::HudSWVPState>("swvp", -1, m_parent);
|
||||
hud->addItem<hud::HudSamplerCount>("samplers", -1, m_parent);
|
||||
hud->addItem<hud::HudFixedFunctionShaders>("ffshaders", -1, m_parent);
|
||||
hud->addItem<hud::HudSWVPState>("swvp", -1, m_parent);
|
||||
|
||||
#ifdef D3D9_ALLOW_UNMAPPING
|
||||
m_hud->addItem<hud::HudTextureMemory>("memory", -1, m_parent);
|
||||
hud->addItem<hud::HudTextureMemory>("memory", -1, m_parent);
|
||||
#endif
|
||||
}
|
||||
|
||||
m_blitter = new DxvkSwapchainBlitter(m_device, std::move(hud));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1152,14 +1087,32 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void D3D9SwapChainEx::DestroyLatencyTracker() {
|
||||
if (!m_latencyTracker)
|
||||
return;
|
||||
|
||||
m_parent->InjectCs([
|
||||
cTracker = std::move(m_latencyTracker)
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->endLatencyTracking(cTracker);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void D3D9SwapChainEx::UpdateTargetFrameRate(uint32_t SyncInterval) {
|
||||
double frameRateOption = double(m_parent->GetOptions()->maxFrameRate);
|
||||
double frameRate = std::max(frameRateOption, 0.0);
|
||||
|
||||
if (SyncInterval && frameRateOption == 0.0)
|
||||
if (frameRateOption == 0.0 && SyncInterval) {
|
||||
bool engageLimiter = SyncInterval > 1u || m_monitor ||
|
||||
m_device->config().latencySleep == Tristate::True;
|
||||
|
||||
if (engageLimiter)
|
||||
frameRate = -m_displayRefreshRate / double(SyncInterval);
|
||||
}
|
||||
|
||||
m_wctx->presenter->setFrameRateLimit(frameRate, GetActualFrameLatency());
|
||||
m_targetFrameRate = frameRate;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1168,11 +1121,6 @@ namespace dxvk {
|
|||
m_wctx->frameLatencySignal->wait(m_wctx->frameId - GetActualFrameLatency());
|
||||
}
|
||||
|
||||
void D3D9SwapChainEx::SetApiName(const char* name) {
|
||||
m_apiName = name;
|
||||
CreateHud();
|
||||
}
|
||||
|
||||
uint32_t D3D9SwapChainEx::GetActualFrameLatency() {
|
||||
uint32_t maxFrameLatency = m_parent->GetFrameLatency();
|
||||
|
||||
|
@ -1184,66 +1132,70 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
uint32_t D3D9SwapChainEx::PickFormats(
|
||||
D3D9Format Format,
|
||||
VkSurfaceFormatKHR* pDstFormats) {
|
||||
uint32_t n = 0;
|
||||
VkSurfaceFormatKHR D3D9SwapChainEx::GetSurfaceFormat() {
|
||||
D3D9Format format = EnumerateFormat(m_presentParams.BackBufferFormat);
|
||||
|
||||
switch (Format) {
|
||||
switch (format) {
|
||||
default:
|
||||
Logger::warn(str::format("D3D9SwapChainEx: Unexpected format: ", Format));
|
||||
Logger::warn(str::format("D3D9SwapChainEx: Unexpected format: ", format));
|
||||
[[fallthrough]];
|
||||
|
||||
case D3D9Format::A8R8G8B8:
|
||||
case D3D9Format::X8R8G8B8:
|
||||
return { VK_FORMAT_B8G8R8A8_UNORM, m_colorspace };
|
||||
|
||||
case D3D9Format::A8B8G8R8:
|
||||
case D3D9Format::X8B8G8R8: {
|
||||
pDstFormats[n++] = { VK_FORMAT_R8G8B8A8_UNORM, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_B8G8R8A8_UNORM, m_colorspace };
|
||||
} break;
|
||||
case D3D9Format::X8B8G8R8:
|
||||
return { VK_FORMAT_R8G8B8A8_UNORM, m_colorspace };
|
||||
|
||||
case D3D9Format::A2R10G10B10:
|
||||
case D3D9Format::A2B10G10R10: {
|
||||
pDstFormats[n++] = { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_A2R10G10B10_UNORM_PACK32, m_colorspace };
|
||||
} break;
|
||||
return { VK_FORMAT_A2R10G10B10_UNORM_PACK32, m_colorspace };
|
||||
|
||||
case D3D9Format::A2B10G10R10:
|
||||
return { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorspace };
|
||||
|
||||
case D3D9Format::X1R5G5B5:
|
||||
case D3D9Format::A1R5G5B5: {
|
||||
pDstFormats[n++] = { VK_FORMAT_B5G5R5A1_UNORM_PACK16, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_R5G5B5A1_UNORM_PACK16, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_A1R5G5B5_UNORM_PACK16, m_colorspace };
|
||||
} break;
|
||||
case D3D9Format::A1R5G5B5:
|
||||
return { VK_FORMAT_B5G5R5A1_UNORM_PACK16, m_colorspace };
|
||||
|
||||
case D3D9Format::R5G6B5: {
|
||||
pDstFormats[n++] = { VK_FORMAT_B5G6R5_UNORM_PACK16, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_R5G6B5_UNORM_PACK16, m_colorspace };
|
||||
} break;
|
||||
case D3D9Format::R5G6B5:
|
||||
return { VK_FORMAT_B5G6R5_UNORM_PACK16, m_colorspace };
|
||||
|
||||
case D3D9Format::A16B16G16R16F: {
|
||||
if (m_unlockAdditionalFormats) {
|
||||
pDstFormats[n++] = { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorspace };
|
||||
} else {
|
||||
Logger::warn(str::format("D3D9SwapChainEx: Unexpected format: ", Format));
|
||||
if (!m_unlockAdditionalFormats) {
|
||||
Logger::warn(str::format("D3D9SwapChainEx: Unexpected format: ", format));
|
||||
return VkSurfaceFormatKHR { };
|
||||
}
|
||||
|
||||
return { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorspace };
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
|
||||
void D3D9SwapChainEx::UpdateWindowedRefreshRate() {
|
||||
// Ignore call if we are in fullscreen mode and
|
||||
// know the active display mode already anyway
|
||||
if (!m_displayRefreshRateDirty || m_monitor)
|
||||
return;
|
||||
|
||||
m_displayRefreshRate = 0.0;
|
||||
m_displayRefreshRateDirty = false;
|
||||
|
||||
HMONITOR monitor = wsi::getWindowMonitor(m_window);
|
||||
|
||||
if (!monitor)
|
||||
return;
|
||||
|
||||
wsi::WsiMode mode = { };
|
||||
|
||||
if (!wsi::getCurrentDisplayMode(monitor, &mode))
|
||||
return;
|
||||
|
||||
if (mode.refreshRate.denominator) {
|
||||
m_displayRefreshRate = double(mode.refreshRate.numerator)
|
||||
/ double(mode.refreshRate.denominator);
|
||||
}
|
||||
|
||||
|
||||
uint32_t D3D9SwapChainEx::PickImageCount(
|
||||
UINT Preferred) {
|
||||
int32_t option = m_parent->GetOptions()->numBackBuffers;
|
||||
return option > 0 ? uint32_t(option) : uint32_t(Preferred);
|
||||
}
|
||||
|
||||
|
||||
void D3D9SwapChainEx::NotifyDisplayRefreshRate(
|
||||
double RefreshRate) {
|
||||
m_displayRefreshRate = RefreshRate;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1323,11 +1275,14 @@ namespace dxvk {
|
|||
if (!wsi::setWindowMode(monitor, m_window, &m_windowState, wsiMode))
|
||||
return D3DERR_NOTAVAILABLE;
|
||||
|
||||
if (wsi::getCurrentDisplayMode(monitor, &wsiMode))
|
||||
NotifyDisplayRefreshRate(double(wsiMode.refreshRate.numerator) / double(wsiMode.refreshRate.denominator));
|
||||
else
|
||||
NotifyDisplayRefreshRate(0.0);
|
||||
m_displayRefreshRate = 0.0;
|
||||
|
||||
if (wsi::getCurrentDisplayMode(monitor, &wsiMode)) {
|
||||
m_displayRefreshRate = double(wsiMode.refreshRate.numerator)
|
||||
/ double(wsiMode.refreshRate.denominator);
|
||||
}
|
||||
|
||||
m_displayRefreshRateDirty = false;
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
|
@ -1339,11 +1294,11 @@ namespace dxvk {
|
|||
if (!wsi::restoreDisplayMode())
|
||||
return D3DERR_NOTAVAILABLE;
|
||||
|
||||
NotifyDisplayRefreshRate(0.0);
|
||||
m_displayRefreshRateDirty = true;
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
bool D3D9SwapChainEx::UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect) {
|
||||
void D3D9SwapChainEx::UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect) {
|
||||
const bool isWindowed = m_presentParams.Windowed;
|
||||
|
||||
// Tests show that present regions are ignored in fullscreen
|
||||
|
@ -1379,15 +1334,15 @@ namespace dxvk {
|
|||
|| dstRect.right - dstRect.left != LONG(width)
|
||||
|| dstRect.bottom - dstRect.top != LONG(height);
|
||||
|
||||
bool recreate = m_wctx != nullptr
|
||||
&& (m_wctx->presenter == nullptr
|
||||
|| m_wctx->presenter->info().imageExtent.width != width
|
||||
|| m_wctx->presenter->info().imageExtent.height != height);
|
||||
|
||||
m_swapchainExtent = { width, height };
|
||||
m_dstRect = dstRect;
|
||||
}
|
||||
|
||||
return recreate;
|
||||
void D3D9SwapChainEx::UpdatePresentParameters() {
|
||||
if (m_wctx) {
|
||||
m_wctx->presenter->setSurfaceExtent(m_swapchainExtent);
|
||||
m_wctx->presenter->setSurfaceFormat(GetSurfaceFormat());
|
||||
}
|
||||
}
|
||||
|
||||
VkExtent2D D3D9SwapChainEx::GetPresentExtent() {
|
||||
|
@ -1396,13 +1351,22 @@ namespace dxvk {
|
|||
|
||||
|
||||
std::string D3D9SwapChainEx::GetApiName() {
|
||||
if (m_apiName == nullptr) {
|
||||
return this->GetParent()->IsExtended() ? "D3D9Ex" : "D3D9";
|
||||
} else {
|
||||
return m_apiName;
|
||||
return this->GetParent()->IsD3D8Compatible() ? "D3D8" :
|
||||
this->GetParent()->IsExtended() ? "D3D9Ex" : "D3D9";
|
||||
}
|
||||
|
||||
|
||||
bool D3D9SwapChainEx::IsDeviceReset(D3D9WindowContext* wctx) {
|
||||
uint32_t counter = m_parent->GetResetCounter();
|
||||
|
||||
if (counter == wctx->deviceResetCounter)
|
||||
return false;
|
||||
|
||||
wctx->deviceResetCounter = counter;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
D3D9VkExtSwapchain::D3D9VkExtSwapchain(D3D9SwapChainEx *pSwapChain)
|
||||
: m_swapchain(pSwapChain) {
|
||||
|
||||
|
@ -1432,9 +1396,11 @@ namespace dxvk {
|
|||
if (!CheckColorSpaceSupport(ColorSpace))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
m_swapchain->m_dirty |= ColorSpace != m_swapchain->m_colorspace;
|
||||
m_swapchain->m_colorspace = ColorSpace;
|
||||
|
||||
if (m_swapchain->m_wctx)
|
||||
m_swapchain->m_wctx->presenter->setSurfaceFormat(m_swapchain->GetSurfaceFormat());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
@ -1444,7 +1410,9 @@ namespace dxvk {
|
|||
return D3DERR_INVALIDCALL;
|
||||
|
||||
m_swapchain->m_hdrMetadata = *pHDRMetadata;
|
||||
m_swapchain->m_dirtyHdrMetadata = true;
|
||||
|
||||
if (m_swapchain->m_wctx)
|
||||
m_swapchain->m_wctx->presenter->setHdrMetadata(*pHDRMetadata);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
|
|
@ -52,10 +52,11 @@ namespace dxvk {
|
|||
|
||||
struct D3D9WindowContext {
|
||||
Rc<Presenter> presenter;
|
||||
std::vector<Rc<DxvkImageView>> imageViews;
|
||||
|
||||
uint64_t frameId = D3D9DeviceEx::MaxFrameLatency;
|
||||
Rc<sync::Fence> frameLatencySignal;
|
||||
|
||||
uint32_t deviceResetCounter = 0u;
|
||||
};
|
||||
|
||||
using D3D9SwapChainExBase = D3D9DeviceChild<IDirect3DSwapChain9Ex>;
|
||||
|
@ -68,7 +69,8 @@ namespace dxvk {
|
|||
D3D9SwapChainEx(
|
||||
D3D9DeviceEx* pDevice,
|
||||
D3DPRESENT_PARAMETERS* pPresentParams,
|
||||
const D3DDISPLAYMODEEX* pFullscreenDisplayMode);
|
||||
const D3DDISPLAYMODEEX* pFullscreenDisplayMode,
|
||||
bool EnableLatencyTracking);
|
||||
|
||||
~D3D9SwapChainEx();
|
||||
|
||||
|
@ -134,9 +136,7 @@ namespace dxvk {
|
|||
|
||||
void DestroyBackBuffers();
|
||||
|
||||
void SetApiName(const char* name);
|
||||
|
||||
void UpdateWindowCtx();
|
||||
bool UpdateWindowCtx();
|
||||
|
||||
private:
|
||||
|
||||
|
@ -157,8 +157,6 @@ namespace dxvk {
|
|||
|
||||
D3D9WindowContext* m_wctx = nullptr;
|
||||
|
||||
Rc<hud::Hud> m_hud;
|
||||
|
||||
std::vector<Com<D3D9Surface, false>> m_backBuffers;
|
||||
|
||||
RECT m_srcRect;
|
||||
|
@ -166,42 +164,38 @@ namespace dxvk {
|
|||
VkExtent2D m_swapchainExtent = { 0u, 0u };
|
||||
bool m_partialCopy = false;
|
||||
|
||||
DxvkSubmitStatus m_presentStatus;
|
||||
|
||||
uint32_t m_frameLatencyCap = 0;
|
||||
|
||||
bool m_dirty = true;
|
||||
|
||||
HWND m_window = nullptr;
|
||||
HMONITOR m_monitor = nullptr;
|
||||
|
||||
wsi::DxvkWindowState m_windowState;
|
||||
|
||||
double m_displayRefreshRate = 0.0;
|
||||
double m_targetFrameRate = 0.0;
|
||||
|
||||
const char* m_apiName = nullptr;
|
||||
double m_displayRefreshRate = 0.0;
|
||||
bool m_displayRefreshRateDirty = true;
|
||||
|
||||
bool m_warnedAboutGDIFallback = false;
|
||||
|
||||
VkColorSpaceKHR m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
|
||||
bool m_latencyTracking = false;
|
||||
Rc<DxvkLatencyTracker> m_latencyTracker = nullptr;
|
||||
|
||||
Rc<hud::HudClientApiItem> m_apiHud;
|
||||
Rc<hud::HudLatencyItem> m_latencyHud;
|
||||
|
||||
std::optional<VkHdrMetadataEXT> m_hdrMetadata;
|
||||
bool m_dirtyHdrMetadata = true;
|
||||
bool m_unlockAdditionalFormats = false;
|
||||
|
||||
D3D9VkExtSwapchain m_swapchainExt;
|
||||
|
||||
void PresentImage(UINT PresentInterval);
|
||||
|
||||
void SynchronizePresent();
|
||||
|
||||
void RecreateSwapChain();
|
||||
|
||||
void CreatePresenter();
|
||||
|
||||
VkResult CreateSurface(VkSurfaceKHR* pSurface);
|
||||
|
||||
void CreateRenderTargetViews();
|
||||
Rc<Presenter> CreatePresenter(
|
||||
HWND Window,
|
||||
Rc<sync::Signal> Signal);
|
||||
|
||||
HRESULT CreateBackBuffers(
|
||||
uint32_t NumBackBuffers,
|
||||
|
@ -209,7 +203,7 @@ namespace dxvk {
|
|||
|
||||
void CreateBlitter();
|
||||
|
||||
void CreateHud();
|
||||
void DestroyLatencyTracker();
|
||||
|
||||
void InitRamp();
|
||||
|
||||
|
@ -217,17 +211,11 @@ namespace dxvk {
|
|||
|
||||
uint32_t GetActualFrameLatency();
|
||||
|
||||
uint32_t PickFormats(
|
||||
D3D9Format Format,
|
||||
VkSurfaceFormatKHR* pDstFormats);
|
||||
|
||||
uint32_t PickImageCount(
|
||||
UINT Preferred);
|
||||
VkSurfaceFormatKHR GetSurfaceFormat();
|
||||
|
||||
void NormalizePresentParameters(D3DPRESENT_PARAMETERS* pPresentParams);
|
||||
|
||||
void NotifyDisplayRefreshRate(
|
||||
double RefreshRate);
|
||||
void UpdateWindowedRefreshRate();
|
||||
|
||||
HRESULT EnterFullscreenMode(
|
||||
D3DPRESENT_PARAMETERS* pPresentParams,
|
||||
|
@ -241,12 +229,16 @@ namespace dxvk {
|
|||
|
||||
HRESULT RestoreDisplayMode(HMONITOR hMonitor);
|
||||
|
||||
bool UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect);
|
||||
void UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect);
|
||||
|
||||
void UpdatePresentParameters();
|
||||
|
||||
VkExtent2D GetPresentExtent();
|
||||
|
||||
std::string GetApiName();
|
||||
|
||||
bool IsDeviceReset(D3D9WindowContext* wctx);
|
||||
|
||||
const Com<D3D9Surface, false>& GetFrontBuffer() const {
|
||||
return m_backBuffers.back();
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue