mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-03-06 20:58:37 +01:00
Compare commits
344 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 | ||
|
b4faf0bb3e | ||
|
acf93aa70a | ||
|
65843aa016 | ||
|
67f4ec73aa | ||
|
d1789c4d85 | ||
|
fb8f79e4a7 | ||
|
0997719a73 | ||
|
5868b067e4 | ||
|
32399461da | ||
|
4dd0afe121 | ||
|
d1abce3be2 | ||
|
45da7d6678 | ||
|
b1ad43145b | ||
|
a78c1bd6dd | ||
|
d094053018 | ||
|
d956b188ed | ||
|
8c4c814fb7 | ||
|
027fe5963a | ||
|
17b85accfb | ||
|
f23c74949a | ||
|
fb56668dc0 | ||
|
9c012c8e75 | ||
|
640a266158 | ||
|
93d2715b7a | ||
|
218b67dd0c | ||
|
4e07302cbf | ||
|
b8ce414820 | ||
|
d7bd3cd58e | ||
|
15db475edf | ||
|
9e1bc1db23 | ||
|
97ad37e409 | ||
|
77020760f1 | ||
|
bb227fa850 | ||
|
34165f3814 | ||
|
423c86bf5e | ||
|
dd183b4a53 | ||
|
4f98844f47 | ||
|
43b79bcb23 | ||
|
dd15328ccf | ||
|
7f1e52703d |
227 changed files with 14528 additions and 5374 deletions
2
RELEASE
2
RELEASE
|
@ -1 +1 @@
|
|||
2.5.1
|
||||
2.5.3
|
||||
|
|
138
dxvk.conf
138
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
|
||||
|
@ -193,23 +246,25 @@
|
|||
|
||||
|
||||
# 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
|
||||
|
@ -500,16 +576,6 @@
|
|||
# d3d9.floatEmulation = Auto
|
||||
|
||||
|
||||
# Enable dialog box mode
|
||||
#
|
||||
# Changes the default state of dialog box mode.
|
||||
# *Disables* exclusive fullscreen when enabled.
|
||||
#
|
||||
# Supported values:
|
||||
# - True, False: Always enable / disable
|
||||
|
||||
# d3d9.enableDialogMode = False
|
||||
|
||||
# Overrides the application's MSAA level on the swapchain
|
||||
#
|
||||
# Supported values: -1 (application) and 0 to 16 (user override)
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 46dc0f6e514f5730784bb2cac2a7c731636839e8
|
||||
Subproject commit 234c4b7370a8ea3239a214c9e871e4b17c89f4ab
|
15
meson.build
15
meson.build
|
@ -1,4 +1,4 @@
|
|||
project('dxvk', ['c', 'cpp'], version : 'v2.5.1', 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()
|
||||
|
@ -18,6 +18,7 @@ compiler_args = [
|
|||
# gcc
|
||||
'-Wno-missing-field-initializers',
|
||||
'-Wno-unused-parameter',
|
||||
'-Wno-misleading-indentation',
|
||||
'-Wno-cast-function-type', # Needed for GetProcAddress.
|
||||
# clang
|
||||
'-Wno-unused-private-field',
|
||||
|
@ -79,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
|
||||
|
@ -102,12 +107,6 @@ if platform == 'windows'
|
|||
lib_d3d11 = cpp.find_library('d3d11')
|
||||
lib_dxgi = cpp.find_library('dxgi')
|
||||
|
||||
if dxvk_is_msvc
|
||||
lib_d3dcompiler_47 = cpp.find_library('d3dcompiler')
|
||||
else
|
||||
lib_d3dcompiler_47 = cpp.find_library('d3dcompiler_47')
|
||||
endif
|
||||
|
||||
if dxvk_is_msvc
|
||||
res_ext = '.res'
|
||||
wrc = find_program('rc')
|
||||
|
|
|
@ -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,10 +26,10 @@ 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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -150,15 +146,5 @@ namespace dxvk {
|
|||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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,14 +55,9 @@ 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,18 +1040,18 @@ namespace dxvk {
|
|||
|
||||
void RestoreCommandListState();
|
||||
|
||||
template<DxbcProgramType Stage>
|
||||
void RestoreConstantBuffers();
|
||||
|
||||
template<DxbcProgramType Stage>
|
||||
void RestoreSamplers();
|
||||
|
||||
template<DxbcProgramType Stage>
|
||||
void RestoreShaderResources();
|
||||
|
||||
template<DxbcProgramType Stage>
|
||||
void RestoreUnorderedAccessViews();
|
||||
void RestoreConstantBuffers(
|
||||
DxbcProgramType Stage);
|
||||
|
||||
void RestoreSamplers(
|
||||
DxbcProgramType Stage);
|
||||
|
||||
void RestoreShaderResources(
|
||||
DxbcProgramType Stage);
|
||||
|
||||
void RestoreUnorderedAccessViews(
|
||||
DxbcProgramType Stage);
|
||||
|
||||
template<DxbcProgramType ShaderStage>
|
||||
void SetConstantBuffers(
|
||||
UINT StartSlot,
|
||||
|
@ -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([
|
||||
|
@ -218,7 +234,12 @@ namespace dxvk {
|
|||
D3D10DeviceLock lock = LockContext();
|
||||
|
||||
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;
|
||||
}
|
||||
// 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);
|
||||
// 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);
|
||||
}
|
||||
m_device->waitForResource(Resource, access);
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -302,6 +304,32 @@ namespace dxvk {
|
|||
predicateValue = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \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,9 +372,9 @@ 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;
|
||||
|
||||
|
|
|
@ -19,24 +19,28 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetPrivateData(
|
||||
REFGUID guid,
|
||||
UINT *pDataSize,
|
||||
void *pData) final {
|
||||
REFGUID guid,
|
||||
UINT* pDataSize,
|
||||
void* pData) final {
|
||||
return m_privateData.getData(
|
||||
guid, pDataSize, pData);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetPrivateData(
|
||||
REFGUID guid,
|
||||
UINT DataSize,
|
||||
const void *pData) final {
|
||||
REFGUID guid,
|
||||
UINT DataSize,
|
||||
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 {
|
||||
REFGUID guid,
|
||||
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;
|
||||
|
@ -117,4 +124,4 @@ namespace dxvk {
|
|||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -62,4 +61,4 @@ namespace dxvk {
|
|||
this->shaderDumpPath = env::getEnvVar("DXVK_SHADER_DUMP_PATH");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -118,4 +114,4 @@ namespace dxvk {
|
|||
std::string shaderDumpPath;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
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, memFlags);
|
||||
std::memcpy(m_buffer->mapPtr(0), shaderInfo.uniformData, shaderInfo.uniformSize);
|
||||
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";
|
||||
|
||||
m_buffer = pDevice->GetDXVKDevice()->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
namespace dxvk {
|
||||
|
||||
class D3D11Device;
|
||||
|
||||
|
||||
/**
|
||||
* \brief Common shader object
|
||||
*
|
||||
|
@ -52,12 +52,18 @@ namespace dxvk {
|
|||
std::string GetName() const {
|
||||
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)
|
||||
return hr;
|
||||
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())
|
||||
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;
|
||||
}
|
||||
if (status == VK_NOT_READY)
|
||||
return DXGI_STATUS_OCCLUDED;
|
||||
|
||||
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,49 +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);
|
||||
presenterDesc.fullScreenExclusive = PickFullscreenMode();
|
||||
|
||||
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);
|
||||
|
||||
|
@ -533,64 +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.fullScreenExclusive = PickFullscreenMode();
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -650,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);
|
||||
|
@ -663,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());
|
||||
|
||||
void D3D11SwapChain::CreateHud() {
|
||||
m_hud = hud::Hud::createHud(m_device);
|
||||
if (m_latency)
|
||||
m_latencyHud = hud->addItem<hud::HudLatencyItem>("latency", 4);
|
||||
}
|
||||
|
||||
if (m_hud != nullptr)
|
||||
m_hud->addItem<hud::HudClientApiItem>("api", 1, GetApiName());
|
||||
m_blitter = new DxvkSwapchainBlitter(m_device, std::move(hud));
|
||||
}
|
||||
|
||||
|
||||
|
@ -680,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());
|
||||
|
@ -715,53 +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]];
|
||||
|
||||
[[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_R10G10B10A2_UNORM: {
|
||||
pDstFormats[n++] = { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_A2R10G10B10_UNORM_PACK32, m_colorspace };
|
||||
} break;
|
||||
|
||||
case DXGI_FORMAT_R16G16B16A16_FLOAT: {
|
||||
pDstFormats[n++] = { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorspace };
|
||||
} break;
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
|
||||
return { VK_FORMAT_R8G8B8A8_SRGB, m_colorSpace };
|
||||
|
||||
case DXGI_FORMAT_R10G10B10A2_UNORM:
|
||||
return { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorSpace };
|
||||
|
||||
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));
|
||||
|
||||
|
||||
VkFullScreenExclusiveEXT D3D11SwapChain::PickFullscreenMode() {
|
||||
return m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH
|
||||
? VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT
|
||||
: VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT;
|
||||
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,56 +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);
|
||||
|
||||
uint32_t PickImageCount(
|
||||
UINT Preferred);
|
||||
|
||||
VkFullScreenExclusiveEXT PickFullscreenMode();
|
||||
|
||||
VkSurfaceFormatKHR GetSurfaceFormat(DXGI_FORMAT Format);
|
||||
|
||||
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"
|
||||
|
||||
|
@ -372,8 +373,31 @@ namespace dxvk {
|
|||
return viewFormat.Format == baseFormat.Format && planeCount == 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
@ -1193,8 +1218,13 @@ namespace dxvk {
|
|||
pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags;
|
||||
pDesc->MiscFlags = m_texture.Desc()->MiscFlags;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
|
@ -78,4 +78,4 @@ namespace dxvk {
|
|||
return a && b && CheckViewOverlap(a->GetViewInfo(), b->GetViewInfo());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,11 +68,13 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
private:
|
||||
|
||||
std::vector<BYTE> m_data;
|
||||
DWORD m_fvf;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Main handler for batching D3D8 draw calls.
|
||||
class D3D8Batcher {
|
||||
|
||||
|
@ -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)) {
|
||||
|
@ -115,13 +119,13 @@ namespace dxvk {
|
|||
d3d9::D3DFMT_INDEX16,
|
||||
m_stream->GetPtr(draw.MinVertex * m_stride),
|
||||
m_stride);
|
||||
|
||||
|
||||
m_device->SetStreamSource(0, D3D8VertexBuffer::GetD3D9Nullable(m_stream), 0, m_stride);
|
||||
m_device->SetIndices(D3D8IndexBuffer::GetD3D9Nullable(m_indices));
|
||||
|
||||
|
||||
draw.PrimitiveType = D3DPRIMITIVETYPE(0);
|
||||
draw.Offset = 0;
|
||||
draw.MinVertex = UINT_MAX;
|
||||
draw.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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -61,18 +62,14 @@ namespace dxvk {
|
|||
public:
|
||||
|
||||
D3D8VertexBuffer(
|
||||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DVertexBuffer9>&& pBuffer,
|
||||
D3DPOOL Pool,
|
||||
DWORD Usage)
|
||||
: D3D8VertexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) {
|
||||
}
|
||||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DVertexBuffer9>&& pBuffer,
|
||||
D3DPOOL Pool,
|
||||
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);
|
||||
|
||||
};
|
||||
|
||||
|
@ -82,18 +79,15 @@ namespace dxvk {
|
|||
public:
|
||||
|
||||
D3D8IndexBuffer(
|
||||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DIndexBuffer9>&& pBuffer,
|
||||
D3DPOOL Pool,
|
||||
DWORD Usage)
|
||||
: D3D8IndexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) {
|
||||
}
|
||||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DIndexBuffer9>&& pBuffer,
|
||||
D3DPOOL Pool,
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
namespace dxvk::d8caps {
|
||||
|
||||
inline constexpr uint32_t MAX_TEXTURE_STAGES = 8;
|
||||
inline constexpr uint32_t MAX_STREAMS = 16;
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace dxvk::d8caps {
|
||||
|
||||
inline constexpr uint32_t MAX_TEXTURE_STAGES = 8;
|
||||
inline constexpr uint32_t MAX_STREAMS = 16;
|
||||
|
||||
}
|
|
@ -1,180 +1,194 @@
|
|||
#pragma once
|
||||
|
||||
// Utility functions for converting
|
||||
// between DirectX8 and DirectX9 types.
|
||||
|
||||
#include "d3d8_include.h"
|
||||
#include "d3d8_format.h"
|
||||
#include "d3d8_options.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
// (8<-9) D3DCAPSX: Writes to D3DCAPS8 from D3DCAPS9
|
||||
inline void ConvertCaps8(const d3d9::D3DCAPS9& caps9, D3DCAPS8* pCaps8) {
|
||||
|
||||
// should be aligned
|
||||
std::memcpy(pCaps8, &caps9, sizeof(D3DCAPS8));
|
||||
|
||||
// Max supported shader model is PS 1.4 and VS 1.1
|
||||
pCaps8->VertexShaderVersion = D3DVS_VERSION(1, 1);
|
||||
pCaps8->PixelShaderVersion = D3DPS_VERSION(1, 4);
|
||||
|
||||
// This was removed by D3D9. We can probably render windowed.
|
||||
pCaps8->Caps2 |= D3DCAPS2_CANRENDERWINDOWED;
|
||||
|
||||
// Replaced by D3DPRASTERCAPS_DEPTHBIAS in D3D9
|
||||
pCaps8->RasterCaps |= D3DPRASTERCAPS_ZBIAS;
|
||||
|
||||
|
||||
// Remove D3D9-specific caps:
|
||||
pCaps8->Caps2 &= ~D3DCAPS2_CANAUTOGENMIPMAP;
|
||||
|
||||
pCaps8->Caps3 &= ~D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION
|
||||
& ~D3DCAPS3_COPY_TO_VIDMEM
|
||||
& ~D3DCAPS3_COPY_TO_SYSTEMMEM;
|
||||
|
||||
pCaps8->PrimitiveMiscCaps &= ~D3DPMISCCAPS_INDEPENDENTWRITEMASKS
|
||||
& ~D3DPMISCCAPS_PERSTAGECONSTANT
|
||||
& ~D3DPMISCCAPS_FOGANDSPECULARALPHA
|
||||
& ~D3DPMISCCAPS_SEPARATEALPHABLEND
|
||||
& ~D3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS
|
||||
& ~D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING
|
||||
& ~D3DPMISCCAPS_FOGVERTEXCLAMPED
|
||||
& ~D3DPMISCCAPS_POSTBLENDSRGBCONVERT;
|
||||
|
||||
pCaps8->RasterCaps &= ~D3DPRASTERCAPS_SCISSORTEST
|
||||
& ~D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS
|
||||
& ~D3DPRASTERCAPS_DEPTHBIAS
|
||||
& ~D3DPRASTERCAPS_MULTISAMPLE_TOGGLE;
|
||||
|
||||
pCaps8->SrcBlendCaps &= ~D3DPBLENDCAPS_BLENDFACTOR
|
||||
& ~D3DPBLENDCAPS_INVSRCCOLOR2
|
||||
& ~D3DPBLENDCAPS_SRCCOLOR2;
|
||||
|
||||
pCaps8->DestBlendCaps &= ~D3DPBLENDCAPS_BLENDFACTOR
|
||||
& ~D3DPBLENDCAPS_INVSRCCOLOR2
|
||||
& ~D3DPBLENDCAPS_SRCCOLOR2;
|
||||
|
||||
pCaps8->LineCaps &= ~D3DLINECAPS_ANTIALIAS;
|
||||
|
||||
pCaps8->StencilCaps &= ~D3DSTENCILCAPS_TWOSIDED;
|
||||
|
||||
pCaps8->VertexProcessingCaps &= ~D3DVTXPCAPS_TEXGEN_SPHEREMAP;
|
||||
}
|
||||
|
||||
// (9<-8) D3DD3DPRESENT_PARAMETERS: Returns D3D9's params given an input for D3D8
|
||||
inline d3d9::D3DPRESENT_PARAMETERS ConvertPresentParameters9(D3DPRESENT_PARAMETERS* pParams) {
|
||||
// A 0 back buffer count needs to be corrected and made visible to the D3D8 application as well
|
||||
pParams->BackBufferCount = std::max(pParams->BackBufferCount, 1u);
|
||||
|
||||
if (pParams->BackBufferFormat == D3DFMT_UNKNOWN)
|
||||
pParams->BackBufferFormat = D3DFMT_X8R8G8B8;
|
||||
|
||||
d3d9::D3DPRESENT_PARAMETERS params;
|
||||
params.BackBufferWidth = pParams->BackBufferWidth;
|
||||
params.BackBufferHeight = pParams->BackBufferHeight;
|
||||
params.BackBufferFormat = d3d9::D3DFORMAT(pParams->BackBufferFormat);
|
||||
params.BackBufferCount = pParams->BackBufferCount;
|
||||
|
||||
params.MultiSampleType = d3d9::D3DMULTISAMPLE_TYPE(pParams->MultiSampleType);
|
||||
params.MultiSampleQuality = 0; // (D3D8: no MultiSampleQuality), TODO: get a value for this
|
||||
|
||||
UINT PresentationInterval = pParams->FullScreen_PresentationInterval;
|
||||
|
||||
if (pParams->Windowed) {
|
||||
|
||||
if (unlikely(PresentationInterval != D3DPRESENT_INTERVAL_DEFAULT)) {
|
||||
// TODO: what does dx8 do if windowed app sets FullScreen_PresentationInterval?
|
||||
Logger::warn(str::format(
|
||||
"D3D8: Application is windowed yet requested FullScreen_PresentationInterval ", PresentationInterval,
|
||||
" (should be D3DPRESENT_INTERVAL_DEFAULT). This will be ignored."));
|
||||
}
|
||||
|
||||
// D3D8: For windowed swap chain, the back buffer is copied to the window immediately.
|
||||
PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
||||
}
|
||||
|
||||
D3DSWAPEFFECT SwapEffect = pParams->SwapEffect;
|
||||
|
||||
// D3DSWAPEFFECT_COPY_VSYNC has been removed
|
||||
if (SwapEffect == D3DSWAPEFFECT_COPY_VSYNC) {
|
||||
|
||||
SwapEffect = D3DSWAPEFFECT_COPY;
|
||||
|
||||
// D3D8: In windowed mode, D3DSWAPEFFECT_COPY_VSYNC enables VSYNC.
|
||||
// In fullscreen, D3DPRESENT_INTERVAL_IMMEDIATE is meaningless.
|
||||
if (pParams->Windowed || (PresentationInterval & D3DPRESENT_INTERVAL_IMMEDIATE) != 0) {
|
||||
PresentationInterval = D3DPRESENT_INTERVAL_ONE;
|
||||
// TODO: what does dx8 do if multiple D3DPRESENT_INTERVAL flags are set?
|
||||
}
|
||||
}
|
||||
|
||||
params.SwapEffect = d3d9::D3DSWAPEFFECT(SwapEffect);
|
||||
params.hDeviceWindow = pParams->hDeviceWindow;
|
||||
params.Windowed = pParams->Windowed;
|
||||
params.EnableAutoDepthStencil = pParams->EnableAutoDepthStencil;
|
||||
params.AutoDepthStencilFormat = d3d9::D3DFORMAT(pParams->AutoDepthStencilFormat);
|
||||
params.Flags = pParams->Flags;
|
||||
|
||||
// D3DPRESENT_RATE_UNLIMITED is unsupported, use D3DPRESENT_RATE_DEFAULT (or 0)
|
||||
if (unlikely(pParams->FullScreen_RefreshRateInHz == D3DPRESENT_RATE_UNLIMITED)) {
|
||||
params.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
|
||||
} else {
|
||||
params.FullScreen_RefreshRateInHz = pParams->FullScreen_RefreshRateInHz;
|
||||
}
|
||||
|
||||
// FullScreen_PresentationInterval -> PresentationInterval
|
||||
params.PresentationInterval = PresentationInterval;
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
// (8<-9) Convert D3DSURFACE_DESC
|
||||
inline void ConvertSurfaceDesc8(const d3d9::D3DSURFACE_DESC* pSurf9, D3DSURFACE_DESC* pSurf8) {
|
||||
pSurf8->Format = D3DFORMAT(pSurf9->Format);
|
||||
pSurf8->Type = D3DRESOURCETYPE(pSurf9->Type);
|
||||
pSurf8->Usage = pSurf9->Usage;
|
||||
pSurf8->Pool = D3DPOOL(pSurf9->Pool);
|
||||
pSurf8->Size = getSurfaceSize(pSurf8->Format, pSurf9->Width, pSurf9->Height);
|
||||
|
||||
pSurf8->MultiSampleType = D3DMULTISAMPLE_TYPE(pSurf9->MultiSampleType);
|
||||
// DX8: No multisample quality
|
||||
pSurf8->Width = pSurf9->Width;
|
||||
pSurf8->Height = pSurf9->Height;
|
||||
}
|
||||
|
||||
// (8<-9) Convert D3DVOLUME_DESC
|
||||
inline void ConvertVolumeDesc8(const d3d9::D3DVOLUME_DESC* pVol9, D3DVOLUME_DESC* pVol8) {
|
||||
pVol8->Format = D3DFORMAT(pVol9->Format);
|
||||
pVol8->Type = D3DRESOURCETYPE(pVol9->Type);
|
||||
pVol8->Usage = pVol9->Usage;
|
||||
pVol8->Pool = D3DPOOL(pVol9->Pool);
|
||||
pVol8->Size = getSurfaceSize(pVol8->Format, pVol9->Width, pVol9->Height) * pVol9->Depth;
|
||||
pVol8->Width = pVol9->Width;
|
||||
pVol8->Height = pVol9->Height;
|
||||
pVol8->Depth = pVol9->Depth;
|
||||
}
|
||||
|
||||
// If this D3DTEXTURESTAGESTATETYPE has been remapped to a d3d9::D3DSAMPLERSTATETYPE
|
||||
// it will be returned, otherwise returns -1u
|
||||
inline d3d9::D3DSAMPLERSTATETYPE GetSamplerStateType9(const D3DTEXTURESTAGESTATETYPE StageType) {
|
||||
switch (StageType) {
|
||||
// 13-21:
|
||||
case D3DTSS_ADDRESSU: return d3d9::D3DSAMP_ADDRESSU;
|
||||
case D3DTSS_ADDRESSV: return d3d9::D3DSAMP_ADDRESSV;
|
||||
case D3DTSS_BORDERCOLOR: return d3d9::D3DSAMP_BORDERCOLOR;
|
||||
case D3DTSS_MAGFILTER: return d3d9::D3DSAMP_MAGFILTER;
|
||||
case D3DTSS_MINFILTER: return d3d9::D3DSAMP_MINFILTER;
|
||||
case D3DTSS_MIPFILTER: return d3d9::D3DSAMP_MIPFILTER;
|
||||
case D3DTSS_MIPMAPLODBIAS: return d3d9::D3DSAMP_MIPMAPLODBIAS;
|
||||
case D3DTSS_MAXMIPLEVEL: return d3d9::D3DSAMP_MAXMIPLEVEL;
|
||||
case D3DTSS_MAXANISOTROPY: return d3d9::D3DSAMP_MAXANISOTROPY;
|
||||
// 25:
|
||||
case D3DTSS_ADDRESSW: return d3d9::D3DSAMP_ADDRESSW;
|
||||
default: return d3d9::D3DSAMPLERSTATETYPE(-1u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma once
|
||||
|
||||
// Utility functions for converting
|
||||
// between DirectX8 and DirectX9 types.
|
||||
|
||||
#include "d3d8_include.h"
|
||||
#include "d3d8_format.h"
|
||||
#include "d3d8_options.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
// (8<-9) D3DCAPSX: Writes to D3DCAPS8 from D3DCAPS9
|
||||
inline void ConvertCaps8(const d3d9::D3DCAPS9& caps9, D3DCAPS8* pCaps8) {
|
||||
|
||||
// should be aligned
|
||||
std::memcpy(pCaps8, &caps9, sizeof(D3DCAPS8));
|
||||
|
||||
// Max supported shader model is PS 1.4 and VS 1.1
|
||||
pCaps8->VertexShaderVersion = D3DVS_VERSION(1, 1);
|
||||
// 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);
|
||||
|
||||
// Remove D3D9-specific caps:
|
||||
|
||||
pCaps8->Caps2 &= ~D3DCAPS2_CANAUTOGENMIPMAP;
|
||||
|
||||
pCaps8->Caps3 &= ~D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION
|
||||
& ~D3DCAPS3_COPY_TO_VIDMEM
|
||||
& ~D3DCAPS3_COPY_TO_SYSTEMMEM;
|
||||
|
||||
pCaps8->PrimitiveMiscCaps &= ~D3DPMISCCAPS_INDEPENDENTWRITEMASKS
|
||||
& ~D3DPMISCCAPS_PERSTAGECONSTANT
|
||||
& ~D3DPMISCCAPS_FOGANDSPECULARALPHA
|
||||
& ~D3DPMISCCAPS_SEPARATEALPHABLEND
|
||||
& ~D3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS
|
||||
& ~D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING
|
||||
& ~D3DPMISCCAPS_FOGVERTEXCLAMPED
|
||||
& ~D3DPMISCCAPS_POSTBLENDSRGBCONVERT;
|
||||
|
||||
pCaps8->RasterCaps &= ~D3DPRASTERCAPS_SCISSORTEST
|
||||
& ~D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS
|
||||
& ~D3DPRASTERCAPS_DEPTHBIAS
|
||||
& ~D3DPRASTERCAPS_MULTISAMPLE_TOGGLE;
|
||||
|
||||
pCaps8->SrcBlendCaps &= ~D3DPBLENDCAPS_BLENDFACTOR;
|
||||
|
||||
pCaps8->DestBlendCaps &= ~D3DPBLENDCAPS_BLENDFACTOR;
|
||||
|
||||
pCaps8->LineCaps &= ~D3DLINECAPS_ANTIALIAS;
|
||||
|
||||
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
|
||||
inline d3d9::D3DPRESENT_PARAMETERS ConvertPresentParameters9(D3DPRESENT_PARAMETERS* pParams) {
|
||||
// A 0 back buffer count needs to be corrected and made visible to the D3D8 application as well
|
||||
pParams->BackBufferCount = std::max(pParams->BackBufferCount, 1u);
|
||||
|
||||
if (pParams->BackBufferFormat == D3DFMT_UNKNOWN)
|
||||
pParams->BackBufferFormat = D3DFMT_X8R8G8B8;
|
||||
|
||||
d3d9::D3DPRESENT_PARAMETERS params;
|
||||
params.BackBufferWidth = pParams->BackBufferWidth;
|
||||
params.BackBufferHeight = pParams->BackBufferHeight;
|
||||
params.BackBufferFormat = d3d9::D3DFORMAT(pParams->BackBufferFormat);
|
||||
params.BackBufferCount = pParams->BackBufferCount;
|
||||
|
||||
params.MultiSampleType = d3d9::D3DMULTISAMPLE_TYPE(pParams->MultiSampleType);
|
||||
// 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) {
|
||||
// D3D8: For windowed swap chain, the back buffer is copied to the window immediately.
|
||||
PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
||||
}
|
||||
|
||||
D3DSWAPEFFECT SwapEffect = pParams->SwapEffect;
|
||||
|
||||
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) {
|
||||
PresentationInterval = D3DPRESENT_INTERVAL_ONE;
|
||||
}
|
||||
}
|
||||
|
||||
params.SwapEffect = d3d9::D3DSWAPEFFECT(SwapEffect);
|
||||
params.hDeviceWindow = pParams->hDeviceWindow;
|
||||
params.Windowed = pParams->Windowed;
|
||||
params.EnableAutoDepthStencil = pParams->EnableAutoDepthStencil;
|
||||
params.AutoDepthStencilFormat = d3d9::D3DFORMAT(pParams->AutoDepthStencilFormat);
|
||||
params.Flags = pParams->Flags;
|
||||
|
||||
// D3DPRESENT_RATE_UNLIMITED is unsupported, use D3DPRESENT_RATE_DEFAULT (or 0)
|
||||
if (unlikely(pParams->FullScreen_RefreshRateInHz == D3DPRESENT_RATE_UNLIMITED)) {
|
||||
params.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
|
||||
} else {
|
||||
params.FullScreen_RefreshRateInHz = pParams->FullScreen_RefreshRateInHz;
|
||||
}
|
||||
|
||||
// FullScreen_PresentationInterval -> PresentationInterval
|
||||
params.PresentationInterval = PresentationInterval;
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
// (8<-9) Convert D3DSURFACE_DESC
|
||||
inline void ConvertSurfaceDesc8(const d3d9::D3DSURFACE_DESC* pSurf9, D3DSURFACE_DESC* pSurf8) {
|
||||
pSurf8->Format = D3DFORMAT(pSurf9->Format);
|
||||
pSurf8->Type = D3DRESOURCETYPE(pSurf9->Type);
|
||||
pSurf8->Usage = pSurf9->Usage;
|
||||
pSurf8->Pool = D3DPOOL(pSurf9->Pool);
|
||||
pSurf8->Size = getSurfaceSize(pSurf8->Format, pSurf9->Width, pSurf9->Height);
|
||||
|
||||
pSurf8->MultiSampleType = D3DMULTISAMPLE_TYPE(pSurf9->MultiSampleType);
|
||||
// DX8: No multisample quality
|
||||
pSurf8->Width = pSurf9->Width;
|
||||
pSurf8->Height = pSurf9->Height;
|
||||
}
|
||||
|
||||
// (8<-9) Convert D3DVOLUME_DESC
|
||||
inline void ConvertVolumeDesc8(const d3d9::D3DVOLUME_DESC* pVol9, D3DVOLUME_DESC* pVol8) {
|
||||
pVol8->Format = D3DFORMAT(pVol9->Format);
|
||||
pVol8->Type = D3DRESOURCETYPE(pVol9->Type);
|
||||
pVol8->Usage = pVol9->Usage;
|
||||
pVol8->Pool = D3DPOOL(pVol9->Pool);
|
||||
pVol8->Size = getSurfaceSize(pVol8->Format, pVol9->Width, pVol9->Height) * pVol9->Depth;
|
||||
pVol8->Width = pVol9->Width;
|
||||
pVol8->Height = pVol9->Height;
|
||||
pVol8->Depth = pVol9->Depth;
|
||||
}
|
||||
|
||||
// If this D3DTEXTURESTAGESTATETYPE has been remapped to a d3d9::D3DSAMPLERSTATETYPE
|
||||
// it will be returned, otherwise returns -1u
|
||||
inline d3d9::D3DSAMPLERSTATETYPE GetSamplerStateType9(const D3DTEXTURESTAGESTATETYPE StageType) {
|
||||
switch (StageType) {
|
||||
// 13-21:
|
||||
case D3DTSS_ADDRESSU: return d3d9::D3DSAMP_ADDRESSU;
|
||||
case D3DTSS_ADDRESSV: return d3d9::D3DSAMP_ADDRESSV;
|
||||
case D3DTSS_BORDERCOLOR: return d3d9::D3DSAMP_BORDERCOLOR;
|
||||
case D3DTSS_MAGFILTER: return d3d9::D3DSAMP_MAGFILTER;
|
||||
case D3DTSS_MINFILTER: return d3d9::D3DSAMP_MINFILTER;
|
||||
case D3DTSS_MIPFILTER: return d3d9::D3DSAMP_MIPFILTER;
|
||||
case D3DTSS_MIPMAPLODBIAS: return d3d9::D3DSAMP_MIPMAPLODBIAS;
|
||||
case D3DTSS_MAXMIPLEVEL: return d3d9::D3DSAMP_MAXMIPLEVEL;
|
||||
case D3DTSS_MAXANISOTROPY: return d3d9::D3DSAMP_MAXANISOTROPY;
|
||||
// 25:
|
||||
case D3DTSS_ADDRESSW: return d3d9::D3DSAMP_ADDRESSW;
|
||||
default: return d3d9::D3DSAMPLERSTATETYPE(-1u);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,12 +32,12 @@ namespace dxvk {
|
|||
};
|
||||
|
||||
D3D8Device::D3D8Device(
|
||||
D3D8Interface* pParent,
|
||||
Com<d3d9::IDirect3DDevice9>&& pDevice,
|
||||
D3DDEVTYPE DeviceType,
|
||||
HWND hFocusWindow,
|
||||
DWORD BehaviorFlags,
|
||||
D3DPRESENT_PARAMETERS* pParams)
|
||||
D3D8Interface* pParent,
|
||||
Com<d3d9::IDirect3DDevice9>&& pDevice,
|
||||
D3DDEVTYPE DeviceType,
|
||||
HWND hFocusWindow,
|
||||
DWORD BehaviorFlags,
|
||||
D3DPRESENT_PARAMETERS* pParams)
|
||||
: D3D8DeviceBase(std::move(pDevice))
|
||||
, m_d3d8Options(pParent->GetOptions())
|
||||
, m_parent(pParent)
|
||||
|
@ -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() {
|
||||
|
@ -73,7 +77,7 @@ namespace dxvk {
|
|||
|
||||
HRESULT res;
|
||||
Com<d3d9::IDirect3DQuery9> pQuery;
|
||||
|
||||
|
||||
switch (DevInfoID) {
|
||||
// pre-D3D8 queries
|
||||
case 0:
|
||||
|
@ -81,7 +85,7 @@ namespace dxvk {
|
|||
case D3DDEVINFOID_D3DTEXTUREMANAGER:
|
||||
case D3DDEVINFOID_TEXTURING:
|
||||
return E_FAIL;
|
||||
|
||||
|
||||
case D3DDEVINFOID_VCACHE:
|
||||
// The query will return D3D_OK on Nvidia and D3DERR_NOTAVAILABLE on AMD/Intel
|
||||
// in D3D9, however in the case of the latter we'll need to return a
|
||||
|
@ -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);
|
||||
|
@ -148,7 +153,7 @@ namespace dxvk {
|
|||
return GetD3D9()->GetAvailableTextureMem();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::ResourceManagerDiscardBytes(DWORD bytes) {
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::ResourceManagerDiscardBytes(DWORD bytes) {
|
||||
return GetD3D9()->EvictManagedResources();
|
||||
}
|
||||
|
||||
|
@ -165,7 +170,7 @@ namespace dxvk {
|
|||
HRESULT res = GetD3D9()->GetDeviceCaps(&caps9);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
dxvk::ConvertCaps8(caps9, pCaps);
|
||||
ConvertCaps8(caps9, pCaps);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -207,7 +212,7 @@ namespace dxvk {
|
|||
|
||||
if (unlikely(pPresentationParameters == nullptr || ppSwapChain == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
|
||||
Com<d3d9::IDirect3DSwapChain9> pSwapChain9;
|
||||
d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters);
|
||||
HRESULT res = GetD3D9()->CreateAdditionalSwapChain(
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -429,7 +440,7 @@ namespace dxvk {
|
|||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
*ppVertexBuffer = ref(new D3D8VertexBuffer(this, std::move(pVertexBuffer9), Pool, Usage));
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -446,10 +457,10 @@ namespace dxvk {
|
|||
|
||||
Com<d3d9::IDirect3DIndexBuffer9> pIndexBuffer9 = nullptr;
|
||||
HRESULT res = GetD3D9()->CreateIndexBuffer(Length, Usage, d3d9::D3DFORMAT(Format), d3d9::D3DPOOL(Pool), &pIndexBuffer9, NULL);
|
||||
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
*ppIndexBuffer = ref(new D3D8IndexBuffer(this, std::move(pIndexBuffer9), Pool, Usage));
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -570,7 +580,7 @@ namespace dxvk {
|
|||
res = src->LockRect(&srcLocked, &srcRect, D3DLOCK_READONLY);
|
||||
if (FAILED(res))
|
||||
return res;
|
||||
|
||||
|
||||
res = dst->LockRect(&dstLocked, &dstRect, 0);
|
||||
if (FAILED(res)) {
|
||||
src->UnlockRect();
|
||||
|
@ -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 │ - │ - │ - │ - │
|
||||
* └────────────┴───────────────────────────┴───────────────────────┴───────────────────────┴──────────┘
|
||||
* ┌────────────┬───────────────────────────┬───────────────────────┬───────────────────────┬──────────────────────┐
|
||||
* │ Src/Dst │ DEFAULT │ MANAGED │ SYSTEMMEM │ 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) {
|
||||
// TODO: Copy on GPU (handle MANAGED similarly to SYSTEMMEM for now)
|
||||
case d3d9::D3DPOOL_DEFAULT: {
|
||||
// TODO: Copy on GPU (handle MANAGED similarly to SYSTEMMEM for now)
|
||||
|
||||
// Get temporary off-screen surface for stretching.
|
||||
Com<d3d9::IDirect3DSurface9> pBlitImage = dst->GetBlitImage();
|
||||
|
||||
|
@ -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: {
|
||||
return unsupported();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
default: {
|
||||
// TODO: Unhandled case.
|
||||
return unhandled();
|
||||
return unsupported();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -935,15 +1023,31 @@ namespace dxvk {
|
|||
// SetDepthStencilSurface is a separate call
|
||||
D3D8Surface* zStencil = static_cast<D3D8Surface*>(pNewZStencil);
|
||||
|
||||
if (likely(m_depthStencil != zStencil)) {
|
||||
StateChange();
|
||||
res = GetD3D9()->SetDepthStencilSurface(D3D8Surface::GetD3D9Nullable(zStencil));
|
||||
// Depth stencil dimensions can not be lower than
|
||||
// those of the currently set render target.
|
||||
if (m_renderTarget != nullptr && zStencil != nullptr) {
|
||||
D3DSURFACE_DESC rtDesc;
|
||||
res = m_renderTarget->GetDesc(&rtDesc);
|
||||
|
||||
if (unlikely(FAILED(res))) return res;
|
||||
|
||||
m_depthStencil = zStencil;
|
||||
D3DSURFACE_DESC dsDesc;
|
||||
res = zStencil->GetDesc(&dsDesc);
|
||||
|
||||
if (unlikely(FAILED(res))) return res;
|
||||
|
||||
if (unlikely(dsDesc.Width < rtDesc.Width
|
||||
|| dsDesc.Height < rtDesc.Height))
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
StateChange();
|
||||
res = GetD3D9()->SetDepthStencilSurface(D3D8Surface::GetD3D9Nullable(zStencil));
|
||||
|
||||
if (unlikely(FAILED(res))) return res;
|
||||
|
||||
m_depthStencil = zStencil;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
|
@ -960,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();
|
||||
}
|
||||
|
||||
|
@ -984,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();
|
||||
}
|
||||
|
||||
|
@ -996,7 +1100,7 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::BeginScene() { return GetD3D9()->BeginScene(); }
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::EndScene() { StateChange(); return GetD3D9()->EndScene(); }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::Clear(
|
||||
|
@ -1099,7 +1203,7 @@ namespace dxvk {
|
|||
HRESULT STDMETHODCALLTYPE D3D8Device::GetClipPlane(DWORD Index, float* pPlane) {
|
||||
return GetD3D9()->GetClipPlane(Index, pPlane);
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::CreateStateBlock(
|
||||
D3DSTATEBLOCKTYPE Type,
|
||||
DWORD* pToken) {
|
||||
|
@ -1264,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;
|
||||
|
||||
|
@ -1297,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:
|
||||
|
@ -1349,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,
|
||||
|
@ -1388,7 +1520,7 @@ namespace dxvk {
|
|||
m_streams[0] = D3D8VBO {nullptr, 0};
|
||||
m_indices = nullptr;
|
||||
m_baseVertexIndex = 0;
|
||||
|
||||
|
||||
return GetD3D9()->DrawIndexedPrimitiveUP(
|
||||
d3d9::D3DPRIMITIVETYPE(PrimitiveType),
|
||||
MinVertexIndex,
|
||||
|
@ -1474,12 +1606,12 @@ namespace dxvk {
|
|||
|
||||
if (unlikely(StreamNumber >= d8caps::MAX_STREAMS))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
|
||||
const D3D8VBO& vbo = m_streams[StreamNumber];
|
||||
|
||||
*ppStreamData = vbo.buffer.ref();
|
||||
*pStride = vbo.stride;
|
||||
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
|
@ -1489,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
|
||||
|
@ -1557,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;
|
||||
|
||||
|
@ -1572,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:
|
||||
|
@ -1605,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) {
|
||||
|
@ -1637,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
|
||||
|
@ -1680,17 +1815,30 @@ namespace dxvk {
|
|||
if (unlikely(pDeclaration == nullptr || pHandle == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// Validate VS version for non-FF shaders
|
||||
if (pFunction != nullptr) {
|
||||
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));
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
}
|
||||
|
||||
D3D8VertexShaderInfo& info = m_vertexShaders.emplace_back();
|
||||
|
||||
// Store D3D8 bytecodes in the shader info
|
||||
if (pDeclaration != nullptr)
|
||||
for (UINT i = 0; pDeclaration[i+1] != D3DVSD_END(); i++)
|
||||
info.declaration.push_back(pDeclaration[i]);
|
||||
for (UINT i = 0; pDeclaration[i] != D3DVSD_END(); i++)
|
||||
info.declaration.push_back(pDeclaration[i]);
|
||||
info.declaration.push_back(D3DVSD_END());
|
||||
|
||||
if (pFunction != nullptr)
|
||||
for (UINT i = 0; pFunction[i+1] != D3DVS_END(); i++)
|
||||
if (pFunction != nullptr) {
|
||||
for (UINT i = 0; pFunction[i] != D3DVS_END(); i++)
|
||||
info.function.push_back(pFunction[i]);
|
||||
|
||||
info.function.push_back(D3DVS_END());
|
||||
}
|
||||
|
||||
D3D9VertexShaderCode result = TranslateVertexShader8(pDeclaration, pFunction, m_d3d8Options);
|
||||
|
||||
// Create vertex declaration
|
||||
|
@ -1792,7 +1940,7 @@ namespace dxvk {
|
|||
|
||||
/*
|
||||
// Slow path. Use to debug cached shader validation. //
|
||||
|
||||
|
||||
d3d9::IDirect3DVertexShader9* pVertexShader;
|
||||
HRESULT res = GetD3D9()->GetVertexShader(&pVertexShader);
|
||||
|
||||
|
@ -1829,6 +1977,9 @@ namespace dxvk {
|
|||
|
||||
info->declaration.clear();
|
||||
info->function.clear();
|
||||
|
||||
if (m_currentVertexShader == Handle)
|
||||
m_currentVertexShader = 0;
|
||||
}
|
||||
|
||||
return D3D_OK;
|
||||
|
@ -1841,12 +1992,12 @@ namespace dxvk {
|
|||
|
||||
if (unlikely(!pInfo))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
|
||||
UINT SizeOfData = *pSizeOfData;
|
||||
|
||||
|
||||
// Get actual size
|
||||
UINT ActualSize = pInfo->declaration.size() * sizeof(DWORD);
|
||||
|
||||
|
||||
if (pData == nullptr) {
|
||||
*pSizeOfData = ActualSize;
|
||||
return D3D_OK;
|
||||
|
@ -1854,8 +2005,10 @@ namespace dxvk {
|
|||
|
||||
// D3D8-specific behavior
|
||||
if (SizeOfData < ActualSize) {
|
||||
*pSizeOfData = ActualSize;
|
||||
return D3DERR_MOREDATA;
|
||||
// D3DERR_MOREDATA should be returned according to the D3D8 documentation,
|
||||
// along with a correction to the ActualSize, however tests have shown that
|
||||
// D3DERR_INVALIDCALL is returned and no size correction is performed.
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
memcpy(pData, pInfo->declaration.data(), ActualSize);
|
||||
|
@ -1869,12 +2022,12 @@ namespace dxvk {
|
|||
|
||||
if (unlikely(!pInfo))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
|
||||
UINT SizeOfData = *pSizeOfData;
|
||||
|
||||
|
||||
// Get actual size
|
||||
UINT ActualSize = pInfo->function.size() * sizeof(DWORD);
|
||||
|
||||
|
||||
if (pData == nullptr) {
|
||||
*pSizeOfData = ActualSize;
|
||||
return D3D_OK;
|
||||
|
@ -1882,8 +2035,10 @@ namespace dxvk {
|
|||
|
||||
// D3D8-specific behavior
|
||||
if (SizeOfData < ActualSize) {
|
||||
*pSizeOfData = ActualSize;
|
||||
return D3DERR_MOREDATA;
|
||||
// D3DERR_MOREDATA should be returned according to the D3D8 documentation,
|
||||
// along with a correction to the ActualSize, however tests have shown that
|
||||
// D3DERR_INVALIDCALL is returned and no size correction is performed.
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
memcpy(pData, pInfo->function.data(), ActualSize);
|
||||
|
@ -1901,8 +2056,16 @@ namespace dxvk {
|
|||
if (unlikely(pFunction == nullptr || pHandle == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
const uint32_t majorVersion = D3DSHADER_VERSION_MAJOR(pFunction[0]);
|
||||
const uint32_t minorVersion = D3DSHADER_VERSION_MINOR(pFunction[0]);
|
||||
|
||||
if (unlikely(m_isFixedFunctionOnly || majorVersion != 1 || minorVersion > 4)) {
|
||||
Logger::err(str::format("D3D8Device::CreatePixelShader: Unsupported PS version ", majorVersion, ".", minorVersion));
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
d3d9::IDirect3DPixelShader9* pPixelShader;
|
||||
|
||||
|
||||
HRESULT res = GetD3D9()->CreatePixelShader(pFunction, &pPixelShader);
|
||||
|
||||
if (likely(SUCCEEDED(res))) {
|
||||
|
@ -1915,7 +2078,7 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
inline d3d9::IDirect3DPixelShader9* getPixelShaderPtr(D3D8Device* device, DWORD Handle) {
|
||||
|
||||
|
||||
Handle = getShaderIndex(Handle);
|
||||
|
||||
if (unlikely(Handle >= device->m_pixelShaders.size())) {
|
||||
|
@ -1986,6 +2149,9 @@ namespace dxvk {
|
|||
|
||||
m_pixelShaders[getShaderIndex(Handle)] = nullptr;
|
||||
|
||||
if (m_currentPixelShader == Handle)
|
||||
m_currentPixelShader = 0;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
|
@ -1998,11 +2164,11 @@ namespace dxvk {
|
|||
return D3DERR_INVALIDCALL;
|
||||
|
||||
UINT SizeOfData = *pSizeOfData;
|
||||
|
||||
|
||||
// Get actual size
|
||||
UINT ActualSize = 0;
|
||||
pPixelShader->GetFunction(nullptr, &ActualSize);
|
||||
|
||||
|
||||
if (pData == nullptr) {
|
||||
*pSizeOfData = ActualSize;
|
||||
return D3D_OK;
|
||||
|
@ -2010,8 +2176,10 @@ namespace dxvk {
|
|||
|
||||
// D3D8-specific behavior
|
||||
if (SizeOfData < ActualSize) {
|
||||
*pSizeOfData = ActualSize;
|
||||
return D3DERR_MOREDATA;
|
||||
// D3DERR_MOREDATA should be returned according to the D3D8 documentation,
|
||||
// along with a correction to the ActualSize, however tests have shown that
|
||||
// D3DERR_INVALIDCALL is returned and no size correction is performed.
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
return pPixelShader->GetFunction(pData, &SizeOfData);
|
||||
|
|
|
@ -27,18 +27,19 @@ namespace dxvk {
|
|||
class D3D8Device final : public D3D8DeviceBase {
|
||||
|
||||
friend class D3D8StateBlock;
|
||||
|
||||
public:
|
||||
|
||||
D3D8Device(
|
||||
D3D8Interface* pParent,
|
||||
Com<d3d9::IDirect3DDevice9>&& pDevice,
|
||||
D3DDEVTYPE DeviceType,
|
||||
HWND hFocusWindow,
|
||||
DWORD BehaviorFlags,
|
||||
D3DPRESENT_PARAMETERS* pParams);
|
||||
D3D8Interface* pParent,
|
||||
Com<d3d9::IDirect3DDevice9>&& pDevice,
|
||||
D3DDEVTYPE DeviceType,
|
||||
HWND hFocusWindow,
|
||||
DWORD BehaviorFlags,
|
||||
D3DPRESENT_PARAMETERS* pParams);
|
||||
|
||||
~D3D8Device();
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE TestCooperativeLevel();
|
||||
|
||||
UINT STDMETHODCALLTYPE GetAvailableTextureMem();
|
||||
|
@ -206,7 +207,7 @@ namespace dxvk {
|
|||
HRESULT STDMETHODCALLTYPE SetRenderState(D3DRENDERSTATETYPE State, DWORD Value);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue);
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CreateStateBlock(
|
||||
D3DSTATEBLOCKTYPE Type,
|
||||
DWORD* pToken);
|
||||
|
@ -306,7 +307,7 @@ namespace dxvk {
|
|||
HRESULT STDMETHODCALLTYPE GetVertexShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetVertexShaderDeclaration(DWORD Handle, void* pData, DWORD* pSizeOfData);
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetVertexShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetStreamSource(
|
||||
|
@ -326,7 +327,7 @@ namespace dxvk {
|
|||
UINT* pBaseVertexIndex);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CreatePixelShader(
|
||||
const DWORD* pFunction,
|
||||
const DWORD* pFunction,
|
||||
DWORD* pHandle);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetPixelShader(DWORD Handle);
|
||||
|
@ -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;
|
||||
|
@ -394,22 +391,24 @@ namespace dxvk {
|
|||
|
||||
m_backBuffers.clear();
|
||||
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;
|
||||
|
@ -426,6 +425,18 @@ namespace dxvk {
|
|||
Com<D3D8Interface> m_parent;
|
||||
|
||||
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;
|
||||
|
@ -437,15 +448,13 @@ namespace dxvk {
|
|||
Com<D3D8VertexBuffer, false> buffer = nullptr;
|
||||
UINT stride = 0;
|
||||
};
|
||||
|
||||
// Remember to fill() these in the constructor!
|
||||
|
||||
std::array<Com<D3D8Texture2D, false>, d8caps::MAX_TEXTURE_STAGES> m_textures;
|
||||
std::array<D3D8VBO, d8caps::MAX_STREAMS> m_streams;
|
||||
|
||||
Com<D3D8IndexBuffer, false> m_indices;
|
||||
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;
|
||||
|
||||
|
|
|
@ -1,71 +1,83 @@
|
|||
#pragma once
|
||||
|
||||
// Common methods for device-tied objects.
|
||||
// - AddRef, Release from IUnknown
|
||||
// - GetDevice from various classes including IDirect3DResource8
|
||||
|
||||
#include "d3d8_include.h"
|
||||
#include "d3d8_wrapped_object.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
class D3D8Device;
|
||||
|
||||
template <typename D3D9, typename D3D8>
|
||||
class D3D8DeviceChild : public D3D8WrappedObject<D3D9, D3D8> {
|
||||
|
||||
public:
|
||||
|
||||
D3D8DeviceChild(D3D8Device* pDevice, Com<D3D9>&& Object)
|
||||
: D3D8WrappedObject<D3D9, D3D8>(std::move(Object))
|
||||
, m_parent( pDevice ) { }
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef() {
|
||||
uint32_t refCount = this->m_refCount++;
|
||||
if (unlikely(!refCount)) {
|
||||
this->AddRefPrivate();
|
||||
GetDevice()->AddRef();
|
||||
}
|
||||
|
||||
return refCount + 1;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release() {
|
||||
// ignore Release calls on objects with 0 refCount
|
||||
if(unlikely(!this->m_refCount))
|
||||
return this->m_refCount;
|
||||
|
||||
uint32_t refCount = --this->m_refCount;
|
||||
if (unlikely(!refCount)) {
|
||||
auto* pDevice = GetDevice();
|
||||
this->ReleasePrivate();
|
||||
pDevice->Release();
|
||||
}
|
||||
return refCount;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDevice(IDirect3DDevice8** ppDevice) {
|
||||
InitReturnPtr(ppDevice);
|
||||
|
||||
if (ppDevice == nullptr)
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
*ppDevice = ref(GetDevice());
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
IDirect3DDevice8* GetDevice() {
|
||||
return reinterpret_cast<IDirect3DDevice8*>(m_parent);
|
||||
}
|
||||
|
||||
D3D8Device* GetParent() {
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
D3D8Device* m_parent;
|
||||
|
||||
};
|
||||
|
||||
#pragma once
|
||||
|
||||
// Common methods for device-tied objects.
|
||||
// - AddRef, Release from IUnknown
|
||||
// - GetDevice from various classes including IDirect3DResource8
|
||||
|
||||
#include "d3d8_include.h"
|
||||
#include "d3d8_wrapped_object.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
class D3D8Device;
|
||||
|
||||
template <typename D3D9, typename D3D8>
|
||||
class D3D8DeviceChild : public D3D8WrappedObject<D3D9, D3D8> {
|
||||
|
||||
public:
|
||||
|
||||
D3D8DeviceChild(D3D8Device* pDevice, Com<D3D9>&& Object)
|
||||
: D3D8WrappedObject<D3D9, D3D8>(std::move(Object))
|
||||
, m_parent( pDevice ) { }
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef() {
|
||||
uint32_t refCount = this->m_refCount++;
|
||||
if (unlikely(!refCount)) {
|
||||
this->AddRefPrivate();
|
||||
GetDevice()->AddRef();
|
||||
}
|
||||
|
||||
return refCount + 1;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release() {
|
||||
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;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDevice(IDirect3DDevice8** ppDevice) {
|
||||
InitReturnPtr(ppDevice);
|
||||
|
||||
if (ppDevice == nullptr)
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
*ppDevice = ref(GetDevice());
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
IDirect3DDevice8* GetDevice() {
|
||||
return reinterpret_cast<IDirect3DDevice8*>(m_parent);
|
||||
}
|
||||
|
||||
D3D8Device* GetParent() {
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
D3D8Device* m_parent;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -63,21 +63,21 @@ interface DECLSPEC_UUID("4B8AAAFA-140F-42BA-9131-597EAFAA2EAD") IDirect3DVolumeT
|
|||
#endif
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#define __CRT_UUID_DECL(type,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \
|
||||
} \
|
||||
#define __CRT_UUID_DECL(type,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \
|
||||
} \
|
||||
extern "C++" template<> struct __mingw_uuidof_s<d3d9::type> { static constexpr IID __uuid_inst = {l,w1,w2, {b1,b2,b3,b4,b5,b6,b7,b8}}; }; \
|
||||
extern "C++" template<> constexpr const GUID &__mingw_uuidof<d3d9::type>() { return __mingw_uuidof_s<d3d9::type>::__uuid_inst; } \
|
||||
extern "C++" template<> constexpr const GUID &__mingw_uuidof<d3d9::type*>() { return __mingw_uuidof_s<d3d9::type>::__uuid_inst; } \
|
||||
namespace d3d9 {
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
#define __CRT_UUID_DECL(type, a, b, c, d, e, f, g, h, i, j, k) \
|
||||
} \
|
||||
extern "C++" { template <> constexpr GUID __uuidof_helper<d3d9::type>() { return GUID{a,b,c,{d,e,f,g,h,i,j,k}}; } } \
|
||||
extern "C++" { template <> constexpr GUID __uuidof_helper<d3d9::type*>() { return __uuidof_helper<d3d9::type>(); } } \
|
||||
extern "C++" { template <> constexpr GUID __uuidof_helper<const d3d9::type*>() { return __uuidof_helper<d3d9::type>(); } } \
|
||||
extern "C++" { template <> constexpr GUID __uuidof_helper<d3d9::type&>() { return __uuidof_helper<d3d9::type>(); } } \
|
||||
extern "C++" { template <> constexpr GUID __uuidof_helper<const d3d9::type&>() { return __uuidof_helper<d3d9::type>(); } } \
|
||||
#define __CRT_UUID_DECL(type, a, b, c, d, e, f, g, h, i, j, k) \
|
||||
} \
|
||||
extern "C++" { template <> constexpr GUID __uuidof_helper<d3d9::type>() { return GUID{a,b,c,{d,e,f,g,h,i,j,k}}; } } \
|
||||
extern "C++" { template <> constexpr GUID __uuidof_helper<d3d9::type*>() { return __uuidof_helper<d3d9::type>(); } } \
|
||||
extern "C++" { template <> constexpr GUID __uuidof_helper<const d3d9::type*>() { return __uuidof_helper<d3d9::type>(); } } \
|
||||
extern "C++" { template <> constexpr GUID __uuidof_helper<d3d9::type&>() { return __uuidof_helper<d3d9::type>(); } } \
|
||||
extern "C++" { template <> constexpr GUID __uuidof_helper<const d3d9::type&>() { return __uuidof_helper<d3d9::type>(); } } \
|
||||
namespace d3d9 {
|
||||
#endif
|
||||
|
||||
|
@ -86,7 +86,7 @@ namespace d3d9 {
|
|||
|
||||
/**
|
||||
* \brief Direct3D 9
|
||||
*
|
||||
*
|
||||
* All D3D9 interfaces are included within
|
||||
* a namespace, so as not to collide with
|
||||
* D3D8 interfaces.
|
||||
|
@ -126,27 +126,27 @@ namespace d3d9 {
|
|||
// Missed definitions in Wine/MinGW.
|
||||
|
||||
#ifndef D3DPRESENT_BACK_BUFFERS_MAX_EX
|
||||
#define D3DPRESENT_BACK_BUFFERS_MAX_EX 30
|
||||
#define D3DPRESENT_BACK_BUFFERS_MAX_EX 30
|
||||
#endif
|
||||
|
||||
#ifndef D3DSI_OPCODE_MASK
|
||||
#define D3DSI_OPCODE_MASK 0x0000FFFF
|
||||
#define D3DSI_OPCODE_MASK 0x0000FFFF
|
||||
#endif
|
||||
|
||||
#ifndef D3DSP_TEXTURETYPE_MASK
|
||||
#define D3DSP_TEXTURETYPE_MASK 0x78000000
|
||||
#define D3DSP_TEXTURETYPE_MASK 0x78000000
|
||||
#endif
|
||||
|
||||
#ifndef D3DUSAGE_AUTOGENMIPMAP
|
||||
#define D3DUSAGE_AUTOGENMIPMAP 0x00000400L
|
||||
#define D3DUSAGE_AUTOGENMIPMAP 0x00000400L
|
||||
#endif
|
||||
|
||||
#ifndef D3DSP_DCL_USAGE_MASK
|
||||
#define D3DSP_DCL_USAGE_MASK 0x0000000f
|
||||
#define D3DSP_DCL_USAGE_MASK 0x0000000f
|
||||
#endif
|
||||
|
||||
#ifndef D3DSP_OPCODESPECIFICCONTROL_MASK
|
||||
#define D3DSP_OPCODESPECIFICCONTROL_MASK 0x00ff0000
|
||||
#define D3DSP_OPCODESPECIFICCONTROL_MASK 0x00ff0000
|
||||
#endif
|
||||
|
||||
#ifndef D3DSP_OPCODESPECIFICCONTROL_SHIFT
|
||||
|
@ -154,31 +154,31 @@ namespace d3d9 {
|
|||
#endif
|
||||
|
||||
#ifndef D3DCURSOR_IMMEDIATE_UPDATE
|
||||
#define D3DCURSOR_IMMEDIATE_UPDATE 0x00000001L
|
||||
#define D3DCURSOR_IMMEDIATE_UPDATE 0x00000001L
|
||||
#endif
|
||||
|
||||
#ifndef D3DPRESENT_FORCEIMMEDIATE
|
||||
#define D3DPRESENT_FORCEIMMEDIATE 0x00000100L
|
||||
#define D3DPRESENT_FORCEIMMEDIATE 0x00000100L
|
||||
#endif
|
||||
|
||||
// From d3dtypes.h
|
||||
|
||||
#ifndef D3DDEVINFOID_TEXTUREMANAGER
|
||||
#define D3DDEVINFOID_TEXTUREMANAGER 1
|
||||
#define D3DDEVINFOID_TEXTUREMANAGER 1
|
||||
#endif
|
||||
|
||||
#ifndef D3DDEVINFOID_D3DTEXTUREMANAGER
|
||||
#define D3DDEVINFOID_D3DTEXTUREMANAGER 2
|
||||
#define D3DDEVINFOID_D3DTEXTUREMANAGER 2
|
||||
#endif
|
||||
|
||||
#ifndef D3DDEVINFOID_TEXTURING
|
||||
#define D3DDEVINFOID_TEXTURING 3
|
||||
#define D3DDEVINFOID_TEXTURING 3
|
||||
#endif
|
||||
|
||||
// From d3dhal.h
|
||||
|
||||
#ifndef D3DDEVINFOID_VCACHE
|
||||
#define D3DDEVINFOID_VCACHE 4
|
||||
#define D3DDEVINFOID_VCACHE 4
|
||||
#endif
|
||||
|
||||
// MinGW headers are broken. Who'dve guessed?
|
||||
|
@ -186,21 +186,21 @@ namespace d3d9 {
|
|||
|
||||
// Missing from d3d8types.h
|
||||
#ifndef D3DDEVINFOID_RESOURCEMANAGER
|
||||
#define D3DDEVINFOID_RESOURCEMANAGER 5
|
||||
#define D3DDEVINFOID_RESOURCEMANAGER 5
|
||||
#endif
|
||||
|
||||
#ifndef D3DDEVINFOID_VERTEXSTATS
|
||||
#define D3DDEVINFOID_VERTEXSTATS 6 // Aka D3DDEVINFOID_D3DVERTEXSTATS
|
||||
#define D3DDEVINFOID_VERTEXSTATS 6 // Aka D3DDEVINFOID_D3DVERTEXSTATS
|
||||
#endif
|
||||
|
||||
#ifndef D3DPRESENT_RATE_UNLIMITED
|
||||
#define D3DPRESENT_RATE_UNLIMITED 0x7FFFFFFF
|
||||
#define D3DPRESENT_RATE_UNLIMITED 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
#else // _MSC_VER
|
||||
|
||||
// These are enum typedefs in the MinGW headers, but not defined by Microsoft
|
||||
#define D3DVSDT_TYPE DWORD
|
||||
#define D3DVSDE_REGISTER DWORD
|
||||
#define D3DVSDT_TYPE DWORD
|
||||
#define D3DVSDE_REGISTER DWORD
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,153 +1,159 @@
|
|||
#include "d3d8_interface.h"
|
||||
|
||||
#include "d3d8_device.h"
|
||||
#include "d3d8_texture.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace dxvk
|
||||
{
|
||||
D3D8Interface::D3D8Interface() {
|
||||
m_d3d9 = d3d9::Direct3DCreate9(D3D_SDK_VERSION);
|
||||
|
||||
// Get the bridge interface to D3D9.
|
||||
if (FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), (void**)&m_bridge))) {
|
||||
throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!");
|
||||
}
|
||||
|
||||
m_bridge->SetD3D8CompatibilityMode(true);
|
||||
|
||||
m_d3d8Options = D3D8Options(*m_bridge->GetConfig());
|
||||
|
||||
m_adapterCount = m_d3d9->GetAdapterCount();
|
||||
m_adapterModeCounts.resize(m_adapterCount);
|
||||
m_adapterModes.reserve(m_adapterCount);
|
||||
|
||||
for (UINT adapter = 0; adapter < m_adapterCount; adapter++) {
|
||||
m_adapterModes.emplace_back();
|
||||
|
||||
// cache adapter modes and mode counts for each d3d9 format
|
||||
for (d3d9::D3DFORMAT fmt : ADAPTER_FORMATS) {
|
||||
|
||||
const UINT modeCount = m_d3d9->GetAdapterModeCount(adapter, fmt);
|
||||
for (UINT mode = 0; mode < modeCount; mode++) {
|
||||
|
||||
m_adapterModes[adapter].emplace_back();
|
||||
m_d3d9->EnumAdapterModes(adapter, fmt, mode, &(m_adapterModes[adapter].back()));
|
||||
|
||||
// can't use modeCount as it's only for one fmt
|
||||
m_adapterModeCounts[adapter]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Interface::QueryInterface(REFIID riid, void** ppvObject) {
|
||||
if (ppvObject == nullptr)
|
||||
return E_POINTER;
|
||||
|
||||
*ppvObject = nullptr;
|
||||
|
||||
if (riid == __uuidof(IUnknown)
|
||||
|| riid == __uuidof(IDirect3D8)) {
|
||||
*ppvObject = ref(this);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
Logger::warn("D3D8Interface::QueryInterface: Unknown interface query");
|
||||
Logger::warn(str::format(riid));
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Interface::GetAdapterIdentifier(
|
||||
UINT Adapter,
|
||||
DWORD Flags,
|
||||
D3DADAPTER_IDENTIFIER8* pIdentifier) {
|
||||
if (unlikely(pIdentifier == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// This flag now has the opposite effect.
|
||||
// Either way, WHQLevel will be 1 with Direct3D9Ex
|
||||
if (Flags & D3DENUM_NO_WHQL_LEVEL)
|
||||
Flags &= ~D3DENUM_WHQL_LEVEL;
|
||||
else
|
||||
Flags |= D3DENUM_WHQL_LEVEL;
|
||||
|
||||
d3d9::D3DADAPTER_IDENTIFIER9 identifier9;
|
||||
HRESULT res = m_d3d9->GetAdapterIdentifier(Adapter, Flags, &identifier9);
|
||||
|
||||
if (likely(SUCCEEDED(res))) {
|
||||
strncpy(pIdentifier->Driver, identifier9.Driver, MAX_DEVICE_IDENTIFIER_STRING);
|
||||
strncpy(pIdentifier->Description, identifier9.Description, MAX_DEVICE_IDENTIFIER_STRING);
|
||||
|
||||
pIdentifier->DriverVersion = identifier9.DriverVersion;
|
||||
pIdentifier->VendorId = identifier9.VendorId;
|
||||
pIdentifier->DeviceId = identifier9.DeviceId;
|
||||
pIdentifier->SubSysId = identifier9.SubSysId;
|
||||
pIdentifier->Revision = identifier9.Revision;
|
||||
pIdentifier->DeviceIdentifier = identifier9.DeviceIdentifier;
|
||||
|
||||
pIdentifier->WHQLLevel = identifier9.WHQLLevel;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT __stdcall D3D8Interface::EnumAdapterModes(
|
||||
UINT Adapter,
|
||||
UINT Mode,
|
||||
D3DDISPLAYMODE* pMode) {
|
||||
if (Adapter >= m_adapterCount || Mode >= m_adapterModeCounts[Adapter] || pMode == nullptr) {
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
pMode->Width = m_adapterModes[Adapter][Mode].Width;
|
||||
pMode->Height = m_adapterModes[Adapter][Mode].Height;
|
||||
pMode->RefreshRate = m_adapterModes[Adapter][Mode].RefreshRate;
|
||||
pMode->Format = D3DFORMAT(m_adapterModes[Adapter][Mode].Format);
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall D3D8Interface::CreateDevice(
|
||||
UINT Adapter,
|
||||
D3DDEVTYPE DeviceType,
|
||||
HWND hFocusWindow,
|
||||
DWORD BehaviorFlags,
|
||||
D3DPRESENT_PARAMETERS* pPresentationParameters,
|
||||
IDirect3DDevice8** ppReturnedDeviceInterface) {
|
||||
InitReturnPtr(ppReturnedDeviceInterface);
|
||||
|
||||
if (unlikely(pPresentationParameters == nullptr ||
|
||||
ppReturnedDeviceInterface == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// D3DSWAPEFFECT_COPY can not be used with more than one back buffer.
|
||||
// This is also technically true for D3DSWAPEFFECT_COPY_VSYNC, however
|
||||
// RC Cars depends on it not being rejected.
|
||||
if (unlikely(pPresentationParameters->SwapEffect == D3DSWAPEFFECT_COPY
|
||||
&& pPresentationParameters->BackBufferCount > 1))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
Com<d3d9::IDirect3DDevice9> pDevice9 = nullptr;
|
||||
d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters);
|
||||
HRESULT res = m_d3d9->CreateDevice(
|
||||
Adapter,
|
||||
(d3d9::D3DDEVTYPE)DeviceType,
|
||||
hFocusWindow,
|
||||
BehaviorFlags,
|
||||
¶ms,
|
||||
&pDevice9
|
||||
);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
*ppReturnedDeviceInterface = ref(new D3D8Device(
|
||||
this, std::move(pDevice9),
|
||||
DeviceType, hFocusWindow, BehaviorFlags,
|
||||
pPresentationParameters
|
||||
));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#include "d3d8_interface.h"
|
||||
|
||||
#include "d3d8_device.h"
|
||||
#include "d3d8_texture.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
D3D8Interface::D3D8Interface() {
|
||||
m_d3d9 = d3d9::Direct3DCreate9(D3D_SDK_VERSION);
|
||||
|
||||
// Get the bridge interface to D3D9.
|
||||
if (FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), (void**)&m_bridge))) {
|
||||
throw DxvkError("D3D8Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!");
|
||||
}
|
||||
|
||||
m_bridge->SetD3D8CompatibilityMode(true);
|
||||
|
||||
m_d3d8Options = D3D8Options(*m_bridge->GetConfig());
|
||||
|
||||
m_adapterCount = m_d3d9->GetAdapterCount();
|
||||
m_adapterModeCounts.resize(m_adapterCount);
|
||||
m_adapterModes.reserve(m_adapterCount);
|
||||
|
||||
for (UINT adapter = 0; adapter < m_adapterCount; adapter++) {
|
||||
m_adapterModes.emplace_back();
|
||||
|
||||
// cache adapter modes and mode counts for each d3d9 format
|
||||
for (d3d9::D3DFORMAT fmt : ADAPTER_FORMATS) {
|
||||
|
||||
const UINT modeCount = m_d3d9->GetAdapterModeCount(adapter, fmt);
|
||||
for (UINT mode = 0; mode < modeCount; mode++) {
|
||||
|
||||
m_adapterModes[adapter].emplace_back();
|
||||
m_d3d9->EnumAdapterModes(adapter, fmt, mode, &(m_adapterModes[adapter].back()));
|
||||
|
||||
// can't use modeCount as it's only for one fmt
|
||||
m_adapterModeCounts[adapter]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Interface::QueryInterface(REFIID riid, void** ppvObject) {
|
||||
if (ppvObject == nullptr)
|
||||
return E_POINTER;
|
||||
|
||||
*ppvObject = nullptr;
|
||||
|
||||
if (riid == __uuidof(IUnknown)
|
||||
|| riid == __uuidof(IDirect3D8)) {
|
||||
*ppvObject = ref(this);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
Logger::warn("D3D8Interface::QueryInterface: Unknown interface query");
|
||||
Logger::warn(str::format(riid));
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Interface::GetAdapterIdentifier(
|
||||
UINT Adapter,
|
||||
DWORD Flags,
|
||||
D3DADAPTER_IDENTIFIER8* pIdentifier) {
|
||||
if (unlikely(pIdentifier == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// This flag now has the opposite effect.
|
||||
// Either way, WHQLevel will be 1 with Direct3D9Ex
|
||||
if (Flags & D3DENUM_NO_WHQL_LEVEL)
|
||||
Flags &= ~D3DENUM_WHQL_LEVEL;
|
||||
else
|
||||
Flags |= D3DENUM_WHQL_LEVEL;
|
||||
|
||||
d3d9::D3DADAPTER_IDENTIFIER9 identifier9;
|
||||
HRESULT res = m_d3d9->GetAdapterIdentifier(Adapter, Flags, &identifier9);
|
||||
|
||||
if (likely(SUCCEEDED(res))) {
|
||||
strncpy(pIdentifier->Driver, identifier9.Driver, MAX_DEVICE_IDENTIFIER_STRING);
|
||||
strncpy(pIdentifier->Description, identifier9.Description, MAX_DEVICE_IDENTIFIER_STRING);
|
||||
|
||||
pIdentifier->DriverVersion = identifier9.DriverVersion;
|
||||
pIdentifier->VendorId = identifier9.VendorId;
|
||||
pIdentifier->DeviceId = identifier9.DeviceId;
|
||||
pIdentifier->SubSysId = identifier9.SubSysId;
|
||||
pIdentifier->Revision = identifier9.Revision;
|
||||
pIdentifier->DeviceIdentifier = identifier9.DeviceIdentifier;
|
||||
|
||||
pIdentifier->WHQLLevel = identifier9.WHQLLevel;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT __stdcall D3D8Interface::EnumAdapterModes(
|
||||
UINT Adapter,
|
||||
UINT Mode,
|
||||
D3DDISPLAYMODE* pMode) {
|
||||
if (Adapter >= m_adapterCount || Mode >= m_adapterModeCounts[Adapter] || pMode == nullptr) {
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
pMode->Width = m_adapterModes[Adapter][Mode].Width;
|
||||
pMode->Height = m_adapterModes[Adapter][Mode].Height;
|
||||
pMode->RefreshRate = m_adapterModes[Adapter][Mode].RefreshRate;
|
||||
pMode->Format = D3DFORMAT(m_adapterModes[Adapter][Mode].Format);
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall D3D8Interface::CreateDevice(
|
||||
UINT Adapter,
|
||||
D3DDEVTYPE DeviceType,
|
||||
HWND hFocusWindow,
|
||||
DWORD BehaviorFlags,
|
||||
D3DPRESENT_PARAMETERS* pPresentationParameters,
|
||||
IDirect3DDevice8** ppReturnedDeviceInterface) {
|
||||
InitReturnPtr(ppReturnedDeviceInterface);
|
||||
|
||||
if (unlikely(pPresentationParameters == nullptr ||
|
||||
ppReturnedDeviceInterface == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// D3DSWAPEFFECT_COPY can not be used with more than one back buffer.
|
||||
// This is also technically true for D3DSWAPEFFECT_COPY_VSYNC, however
|
||||
// RC Cars depends on it not being rejected.
|
||||
if (unlikely(pPresentationParameters->SwapEffect == D3DSWAPEFFECT_COPY
|
||||
&& 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(
|
||||
Adapter,
|
||||
(d3d9::D3DDEVTYPE)DeviceType,
|
||||
hFocusWindow,
|
||||
BehaviorFlags,
|
||||
¶ms,
|
||||
&pDevice9
|
||||
);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
*ppReturnedDeviceInterface = ref(new D3D8Device(
|
||||
this, std::move(pDevice9),
|
||||
DeviceType, hFocusWindow, BehaviorFlags,
|
||||
pPresentationParameters
|
||||
));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,172 +1,172 @@
|
|||
#pragma once
|
||||
|
||||
#include "d3d8_include.h"
|
||||
#include "d3d8_d3d9_util.h"
|
||||
#include "d3d8_options.h"
|
||||
#include "d3d8_format.h"
|
||||
#include "../d3d9/d3d9_bridge.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
/**
|
||||
* \brief D3D8 interface implementation
|
||||
*
|
||||
* Implements the IDirect3DDevice8 interfaces
|
||||
* which provides the way to get adapters and create other objects such as \ref IDirect3DDevice8.
|
||||
* similar to \ref DxgiFactory but for D3D8.
|
||||
*/
|
||||
class D3D8Interface final : public ComObjectClamp<IDirect3D8> {
|
||||
|
||||
static constexpr d3d9::D3DFORMAT ADAPTER_FORMATS[] = {
|
||||
d3d9::D3DFMT_A1R5G5B5,
|
||||
//d3d9::D3DFMT_A2R10G10B10, (not in D3D8)
|
||||
d3d9::D3DFMT_A8R8G8B8,
|
||||
d3d9::D3DFMT_R5G6B5,
|
||||
d3d9::D3DFMT_X1R5G5B5,
|
||||
d3d9::D3DFMT_X8R8G8B8
|
||||
};
|
||||
|
||||
public:
|
||||
D3D8Interface();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE RegisterSoftwareDevice(void* pInitializeFunction) {
|
||||
return m_d3d9->RegisterSoftwareDevice(pInitializeFunction);
|
||||
}
|
||||
|
||||
UINT STDMETHODCALLTYPE GetAdapterCount() {
|
||||
return m_d3d9->GetAdapterCount();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetAdapterIdentifier(
|
||||
UINT Adapter,
|
||||
DWORD Flags,
|
||||
D3DADAPTER_IDENTIFIER8* pIdentifier);
|
||||
|
||||
UINT STDMETHODCALLTYPE GetAdapterModeCount(UINT Adapter) {
|
||||
return m_adapterModeCounts[Adapter];
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE EnumAdapterModes(
|
||||
UINT Adapter,
|
||||
UINT Mode,
|
||||
D3DDISPLAYMODE* pMode);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode) {
|
||||
return m_d3d9->GetAdapterDisplayMode(Adapter, (d3d9::D3DDISPLAYMODE*)pMode);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CheckDeviceType(
|
||||
UINT Adapter,
|
||||
D3DDEVTYPE DevType,
|
||||
D3DFORMAT AdapterFormat,
|
||||
D3DFORMAT BackBufferFormat,
|
||||
BOOL bWindowed) {
|
||||
// Ignore the bWindowed parameter when querying D3D9. D3D8 does
|
||||
// identical validations between windowed and fullscreen modes, adhering
|
||||
// to the stricter fullscreen adapter and back buffer format validations.
|
||||
return m_d3d9->CheckDeviceType(
|
||||
Adapter,
|
||||
(d3d9::D3DDEVTYPE)DevType,
|
||||
(d3d9::D3DFORMAT)AdapterFormat,
|
||||
(d3d9::D3DFORMAT)BackBufferFormat,
|
||||
FALSE
|
||||
);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CheckDeviceFormat(
|
||||
UINT Adapter,
|
||||
D3DDEVTYPE DeviceType,
|
||||
D3DFORMAT AdapterFormat,
|
||||
DWORD Usage,
|
||||
D3DRESOURCETYPE RType,
|
||||
D3DFORMAT CheckFormat) {
|
||||
return m_d3d9->CheckDeviceFormat(
|
||||
Adapter,
|
||||
(d3d9::D3DDEVTYPE)DeviceType,
|
||||
(d3d9::D3DFORMAT)AdapterFormat,
|
||||
Usage,
|
||||
(d3d9::D3DRESOURCETYPE)RType,
|
||||
(d3d9::D3DFORMAT)CheckFormat
|
||||
);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CheckDeviceMultiSampleType(
|
||||
UINT Adapter,
|
||||
D3DDEVTYPE DeviceType,
|
||||
D3DFORMAT SurfaceFormat,
|
||||
BOOL Windowed,
|
||||
D3DMULTISAMPLE_TYPE MultiSampleType) {
|
||||
DWORD* pQualityLevels = nullptr;
|
||||
return m_d3d9->CheckDeviceMultiSampleType(
|
||||
Adapter,
|
||||
(d3d9::D3DDEVTYPE)DeviceType,
|
||||
(d3d9::D3DFORMAT)SurfaceFormat,
|
||||
Windowed,
|
||||
(d3d9::D3DMULTISAMPLE_TYPE)MultiSampleType,
|
||||
pQualityLevels
|
||||
);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CheckDepthStencilMatch(
|
||||
UINT Adapter,
|
||||
D3DDEVTYPE DeviceType,
|
||||
D3DFORMAT AdapterFormat,
|
||||
D3DFORMAT RenderTargetFormat,
|
||||
D3DFORMAT DepthStencilFormat) {
|
||||
if (isSupportedDepthStencilFormat(DepthStencilFormat))
|
||||
return m_d3d9->CheckDepthStencilMatch(
|
||||
Adapter,
|
||||
(d3d9::D3DDEVTYPE)DeviceType,
|
||||
(d3d9::D3DFORMAT)AdapterFormat,
|
||||
(d3d9::D3DFORMAT)RenderTargetFormat,
|
||||
(d3d9::D3DFORMAT)DepthStencilFormat
|
||||
);
|
||||
|
||||
return D3DERR_NOTAVAILABLE;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDeviceCaps(
|
||||
UINT Adapter,
|
||||
D3DDEVTYPE DeviceType,
|
||||
D3DCAPS8* pCaps) {
|
||||
if (unlikely(pCaps == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
d3d9::D3DCAPS9 caps9;
|
||||
HRESULT res = m_d3d9->GetDeviceCaps(Adapter, (d3d9::D3DDEVTYPE)DeviceType, &caps9);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
dxvk::ConvertCaps8(caps9, pCaps);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HMONITOR STDMETHODCALLTYPE GetAdapterMonitor(UINT Adapter) {
|
||||
return m_d3d9->GetAdapterMonitor(Adapter);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CreateDevice(
|
||||
UINT Adapter,
|
||||
D3DDEVTYPE DeviceType,
|
||||
HWND hFocusWindow,
|
||||
DWORD BehaviorFlags,
|
||||
D3DPRESENT_PARAMETERS* pPresentationParameters,
|
||||
IDirect3DDevice8** ppReturnedDeviceInterface);
|
||||
|
||||
|
||||
const D3D8Options& GetOptions() { return m_d3d8Options; }
|
||||
|
||||
private:
|
||||
|
||||
UINT m_adapterCount;
|
||||
std::vector<UINT> m_adapterModeCounts;
|
||||
std::vector<std::vector<d3d9::D3DDISPLAYMODE>> m_adapterModes;
|
||||
|
||||
Com<d3d9::IDirect3D9> m_d3d9;
|
||||
Com<IDxvkD3D8InterfaceBridge> m_bridge;
|
||||
D3D8Options m_d3d8Options;
|
||||
};
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "d3d8_include.h"
|
||||
#include "d3d8_d3d9_util.h"
|
||||
#include "d3d8_options.h"
|
||||
#include "d3d8_format.h"
|
||||
#include "../d3d9/d3d9_bridge.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
/**
|
||||
* \brief D3D8 interface implementation
|
||||
*
|
||||
* Implements the IDirect3DDevice8 interfaces
|
||||
* which provides the way to get adapters and create other objects such as \ref IDirect3DDevice8.
|
||||
* similar to \ref DxgiFactory but for D3D8.
|
||||
*/
|
||||
class D3D8Interface final : public ComObjectClamp<IDirect3D8> {
|
||||
|
||||
static constexpr d3d9::D3DFORMAT ADAPTER_FORMATS[] = {
|
||||
d3d9::D3DFMT_A1R5G5B5,
|
||||
//d3d9::D3DFMT_A2R10G10B10, (not in D3D8)
|
||||
d3d9::D3DFMT_A8R8G8B8,
|
||||
d3d9::D3DFMT_R5G6B5,
|
||||
d3d9::D3DFMT_X1R5G5B5,
|
||||
d3d9::D3DFMT_X8R8G8B8
|
||||
};
|
||||
|
||||
public:
|
||||
D3D8Interface();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE RegisterSoftwareDevice(void* pInitializeFunction) {
|
||||
return m_d3d9->RegisterSoftwareDevice(pInitializeFunction);
|
||||
}
|
||||
|
||||
UINT STDMETHODCALLTYPE GetAdapterCount() {
|
||||
return m_d3d9->GetAdapterCount();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetAdapterIdentifier(
|
||||
UINT Adapter,
|
||||
DWORD Flags,
|
||||
D3DADAPTER_IDENTIFIER8* pIdentifier);
|
||||
|
||||
UINT STDMETHODCALLTYPE GetAdapterModeCount(UINT Adapter) {
|
||||
return m_adapterModeCounts[Adapter];
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE EnumAdapterModes(
|
||||
UINT Adapter,
|
||||
UINT Mode,
|
||||
D3DDISPLAYMODE* pMode);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode) {
|
||||
return m_d3d9->GetAdapterDisplayMode(Adapter, (d3d9::D3DDISPLAYMODE*)pMode);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CheckDeviceType(
|
||||
UINT Adapter,
|
||||
D3DDEVTYPE DevType,
|
||||
D3DFORMAT AdapterFormat,
|
||||
D3DFORMAT BackBufferFormat,
|
||||
BOOL bWindowed) {
|
||||
// Ignore the bWindowed parameter when querying D3D9. D3D8 does
|
||||
// identical validations between windowed and fullscreen modes, adhering
|
||||
// to the stricter fullscreen adapter and back buffer format validations.
|
||||
return m_d3d9->CheckDeviceType(
|
||||
Adapter,
|
||||
(d3d9::D3DDEVTYPE)DevType,
|
||||
(d3d9::D3DFORMAT)AdapterFormat,
|
||||
(d3d9::D3DFORMAT)BackBufferFormat,
|
||||
FALSE
|
||||
);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CheckDeviceFormat(
|
||||
UINT Adapter,
|
||||
D3DDEVTYPE DeviceType,
|
||||
D3DFORMAT AdapterFormat,
|
||||
DWORD Usage,
|
||||
D3DRESOURCETYPE RType,
|
||||
D3DFORMAT CheckFormat) {
|
||||
return m_d3d9->CheckDeviceFormat(
|
||||
Adapter,
|
||||
(d3d9::D3DDEVTYPE)DeviceType,
|
||||
(d3d9::D3DFORMAT)AdapterFormat,
|
||||
Usage,
|
||||
(d3d9::D3DRESOURCETYPE)RType,
|
||||
(d3d9::D3DFORMAT)CheckFormat
|
||||
);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CheckDeviceMultiSampleType(
|
||||
UINT Adapter,
|
||||
D3DDEVTYPE DeviceType,
|
||||
D3DFORMAT SurfaceFormat,
|
||||
BOOL Windowed,
|
||||
D3DMULTISAMPLE_TYPE MultiSampleType) {
|
||||
DWORD* pQualityLevels = nullptr;
|
||||
return m_d3d9->CheckDeviceMultiSampleType(
|
||||
Adapter,
|
||||
(d3d9::D3DDEVTYPE)DeviceType,
|
||||
(d3d9::D3DFORMAT)SurfaceFormat,
|
||||
Windowed,
|
||||
(d3d9::D3DMULTISAMPLE_TYPE)MultiSampleType,
|
||||
pQualityLevels
|
||||
);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CheckDepthStencilMatch(
|
||||
UINT Adapter,
|
||||
D3DDEVTYPE DeviceType,
|
||||
D3DFORMAT AdapterFormat,
|
||||
D3DFORMAT RenderTargetFormat,
|
||||
D3DFORMAT DepthStencilFormat) {
|
||||
if (isSupportedDepthStencilFormat(DepthStencilFormat))
|
||||
return m_d3d9->CheckDepthStencilMatch(
|
||||
Adapter,
|
||||
(d3d9::D3DDEVTYPE)DeviceType,
|
||||
(d3d9::D3DFORMAT)AdapterFormat,
|
||||
(d3d9::D3DFORMAT)RenderTargetFormat,
|
||||
(d3d9::D3DFORMAT)DepthStencilFormat
|
||||
);
|
||||
|
||||
return D3DERR_NOTAVAILABLE;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDeviceCaps(
|
||||
UINT Adapter,
|
||||
D3DDEVTYPE DeviceType,
|
||||
D3DCAPS8* pCaps) {
|
||||
if (unlikely(pCaps == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
d3d9::D3DCAPS9 caps9;
|
||||
HRESULT res = m_d3d9->GetDeviceCaps(Adapter, (d3d9::D3DDEVTYPE)DeviceType, &caps9);
|
||||
|
||||
if (likely(SUCCEEDED(res)))
|
||||
ConvertCaps8(caps9, pCaps);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HMONITOR STDMETHODCALLTYPE GetAdapterMonitor(UINT Adapter) {
|
||||
return m_d3d9->GetAdapterMonitor(Adapter);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CreateDevice(
|
||||
UINT Adapter,
|
||||
D3DDEVTYPE DeviceType,
|
||||
HWND hFocusWindow,
|
||||
DWORD BehaviorFlags,
|
||||
D3DPRESENT_PARAMETERS* pPresentationParameters,
|
||||
IDirect3DDevice8** ppReturnedDeviceInterface);
|
||||
|
||||
|
||||
const D3D8Options& GetOptions() { return m_d3d8Options; }
|
||||
|
||||
private:
|
||||
|
||||
UINT m_adapterCount;
|
||||
std::vector<UINT> m_adapterModeCounts;
|
||||
std::vector<std::vector<d3d9::D3DDISPLAYMODE>> m_adapterModes;
|
||||
|
||||
Com<d3d9::IDirect3D9> m_d3d9;
|
||||
Com<IDxvkD3D8InterfaceBridge> m_bridge;
|
||||
D3D8Options m_d3d8Options;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,65 +1,124 @@
|
|||
#include "d3d8_interface.h"
|
||||
|
||||
namespace dxvk {
|
||||
Logger Logger::s_instance("d3d8.log");
|
||||
|
||||
HRESULT CreateD3D8(IDirect3D8** ppDirect3D8) {
|
||||
if (!ppDirect3D8)
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
*ppDirect3D8 = ref(new D3D8Interface());
|
||||
return D3D_OK;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
DLLEXPORT HRESULT __stdcall ValidatePixelShader(
|
||||
const DWORD* pPixelShader,
|
||||
const D3DCAPS8* pCaps,
|
||||
BOOL errorReturn,
|
||||
char** pErrorString) {
|
||||
dxvk::Logger::warn("D3D8: ValidatePixelShader: Stub");
|
||||
|
||||
if (unlikely(pPixelShader == nullptr))
|
||||
return E_FAIL;
|
||||
|
||||
if (errorReturn && pErrorString != nullptr) {
|
||||
const char* errorMessage = "";
|
||||
*pErrorString = (char *) errorMessage;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
DLLEXPORT HRESULT __stdcall ValidateVertexShader(
|
||||
const DWORD* pVertexShader,
|
||||
const DWORD* pVertexDecl,
|
||||
const D3DCAPS8* pCaps,
|
||||
BOOL errorReturn,
|
||||
char** pErrorString) {
|
||||
dxvk::Logger::warn("D3D8: ValidateVertexShader: Stub");
|
||||
|
||||
if (unlikely(pVertexShader == nullptr))
|
||||
return E_FAIL;
|
||||
|
||||
if (errorReturn && pErrorString != nullptr) {
|
||||
const char* errorMessage = "";
|
||||
*pErrorString = (char *) errorMessage;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
DLLEXPORT void __stdcall DebugSetMute() {
|
||||
dxvk::Logger::debug("D3D8: DebugSetMute: Stub");
|
||||
}
|
||||
|
||||
DLLEXPORT IDirect3D8* __stdcall Direct3DCreate8(UINT nSDKVersion) {
|
||||
IDirect3D8* pDirect3D = nullptr;
|
||||
dxvk::CreateD3D8(&pDirect3D);
|
||||
|
||||
return pDirect3D;
|
||||
}
|
||||
|
||||
}
|
||||
#include "d3d8_interface.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
Logger Logger::s_instance("d3d8.log");
|
||||
|
||||
HRESULT CreateD3D8(IDirect3D8** ppDirect3D8) {
|
||||
if (!ppDirect3D8)
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
*ppDirect3D8 = ref(new D3D8Interface());
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
DLLEXPORT HRESULT __stdcall ValidatePixelShader(
|
||||
const DWORD* pPixelShader,
|
||||
const D3DCAPS8* pCaps,
|
||||
BOOL ErrorReturn,
|
||||
char** pErrorString) {
|
||||
HRESULT res = S_OK;
|
||||
std::string errorMessage = "";
|
||||
|
||||
// ValidatePixelShader returns immediately in case of a NULL pPixelShader
|
||||
if (unlikely(pPixelShader == nullptr)) {
|
||||
dxvk::Logger::warn("D3D8: ValidatePixelShader: Null pPixelShader");
|
||||
return E_FAIL;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(res != S_OK)) {
|
||||
dxvk::Logger::warn(errorMessage);
|
||||
|
||||
if (!ErrorReturn)
|
||||
errorMessage = "";
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
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);
|
||||
if (*pErrorString)
|
||||
memcpy(*pErrorString, errorMessage.c_str(), errorMessageSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
DLLEXPORT HRESULT __stdcall ValidateVertexShader(
|
||||
const DWORD* pVertexShader,
|
||||
const DWORD* pVertexDecl,
|
||||
const D3DCAPS8* pCaps,
|
||||
BOOL ErrorReturn,
|
||||
char** pErrorString) {
|
||||
HRESULT res = S_OK;
|
||||
std::string errorMessage = "";
|
||||
|
||||
if (unlikely(pVertexShader == nullptr)) {
|
||||
errorMessage = "D3D8: ValidateVertexShader: Null pVertexShader";
|
||||
res = E_FAIL;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(res != S_OK)) {
|
||||
dxvk::Logger::warn(errorMessage);
|
||||
|
||||
if (!ErrorReturn)
|
||||
errorMessage = "";
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
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);
|
||||
if (*pErrorString)
|
||||
memcpy(*pErrorString, errorMessage.c_str(), errorMessageSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
DLLEXPORT void __stdcall DebugSetMute() {}
|
||||
|
||||
DLLEXPORT IDirect3D8* __stdcall Direct3DCreate8(UINT nSDKVersion) {
|
||||
IDirect3D8* pDirect3D = nullptr;
|
||||
dxvk::CreateD3D8(&pDirect3D);
|
||||
|
||||
return pDirect3D;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,56 +1,65 @@
|
|||
#pragma once
|
||||
|
||||
#include "d3d8_include.h"
|
||||
#include "../d3d9/d3d9_bridge.h"
|
||||
#include "../util/config/config.h"
|
||||
|
||||
namespace dxvk {
|
||||
struct D3D8Options {
|
||||
|
||||
/// Some games rely on undefined behavior by using undeclared vertex shader inputs.
|
||||
/// The simplest way to fix them is to simply modify their vertex shader decl.
|
||||
///
|
||||
/// This option takes a comma-separated list of colon-separated number pairs, where
|
||||
/// the first number is a D3DVSDE_REGISTER value, the second is a D3DVSDT_TYPE value.
|
||||
/// e.g. "0:2,3:2,7:1" for float3 position : v0, float3 normal : v3, float2 uv : v7
|
||||
std::vector<std::pair<D3DVSDE_REGISTER, D3DVSDT_TYPE>> forceVsDecl;
|
||||
|
||||
/// Specialized drawcall batcher, typically for games that draw a lot of similar
|
||||
/// geometry in separate drawcalls (sometimes even one triangle at a time).
|
||||
///
|
||||
/// May hurt performance outside of specifc games that benefit from it.
|
||||
bool batching = false;
|
||||
|
||||
/// The Lord of the Rings: The Fellowship of the Ring tries to create a P8 texture
|
||||
/// in D3DPOOL_MANAGED on Nvidia and Intel, which fails, but has a separate code
|
||||
/// path for ATI/AMD that creates it in D3DPOOL_SCRATCH instead, which works.
|
||||
///
|
||||
/// The internal logic determining this path doesn't seem to be d3d-related, but
|
||||
/// the game works universally if we mimic its own ATI/AMD workaround during P8
|
||||
/// texture creation.
|
||||
///
|
||||
/// Early Nvidia GPUs, such as the GeForce 4 generation cards, included and exposed
|
||||
/// P8 texture support. However, it was no longer advertised with cards in the FX series
|
||||
/// and above. Most likely ATI/AMD drivers never supported P8 in the first place.
|
||||
bool placeP8InScratch = false;
|
||||
|
||||
/// Rayman 3 relies on D3DLOCK_DISCARD being ignored for everything except D3DUSAGE_DYNAMIC +
|
||||
/// D3DUSAGE_WRITEONLY buffers, however this approach incurs a performance penalty.
|
||||
///
|
||||
/// Some titles might abuse this early D3D8 quirk, however at some point in its history
|
||||
/// it was brought in line with standard D3D9 behavior.
|
||||
bool forceLegacyDiscard = 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);
|
||||
|
||||
parseVsDecl(forceVsDeclStr);
|
||||
}
|
||||
|
||||
void parseVsDecl(const std::string& decl);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "d3d8_include.h"
|
||||
#include "../d3d9/d3d9_bridge.h"
|
||||
#include "../util/config/config.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
struct D3D8Options {
|
||||
|
||||
/// Some games rely on undefined behavior by using undeclared vertex shader inputs.
|
||||
/// The simplest way to fix them is to simply modify their vertex shader decl.
|
||||
///
|
||||
/// This option takes a comma-separated list of colon-separated number pairs, where
|
||||
/// the first number is a D3DVSDE_REGISTER value, the second is a D3DVSDT_TYPE value.
|
||||
/// e.g. "0:2,3:2,7:1" for float3 position : v0, float3 normal : v3, float2 uv : v7
|
||||
std::vector<std::pair<D3DVSDE_REGISTER, D3DVSDT_TYPE>> forceVsDecl;
|
||||
|
||||
/// Specialized drawcall batcher, typically for games that draw a lot of similar
|
||||
/// geometry in separate drawcalls (sometimes even one triangle at a time).
|
||||
///
|
||||
/// May hurt performance outside of specifc games that benefit from it.
|
||||
bool batching = false;
|
||||
|
||||
/// The Lord of the Rings: The Fellowship of the Ring tries to create a P8 texture
|
||||
/// in D3DPOOL_MANAGED on Nvidia and Intel, which fails, but has a separate code
|
||||
/// path for ATI/AMD that creates it in D3DPOOL_SCRATCH instead, which works.
|
||||
///
|
||||
/// The internal logic determining this path doesn't seem to be d3d-related, but
|
||||
/// the game works universally if we mimic its own ATI/AMD workaround during P8
|
||||
/// texture creation.
|
||||
///
|
||||
/// Early Nvidia GPUs, such as the GeForce 4 generation cards, included and exposed
|
||||
/// P8 texture support. However, it was no longer advertised with cards in the FX series
|
||||
/// and above. Most likely ATI/AMD drivers never supported P8 in the first place.
|
||||
bool placeP8InScratch = false;
|
||||
|
||||
/// Rayman 3 relies on D3DLOCK_DISCARD being ignored for everything except D3DUSAGE_DYNAMIC +
|
||||
/// D3DUSAGE_WRITEONLY buffers, however this approach incurs a performance penalty.
|
||||
///
|
||||
/// Some titles might abuse this early D3D8 quirk, however at some point in its history
|
||||
/// 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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,111 +1,117 @@
|
|||
#pragma once
|
||||
|
||||
/** Implements IDirect3DResource8
|
||||
*
|
||||
* - SetPrivateData, GetPrivateData, FreePrivateData
|
||||
* - SetPriority, GetPriority
|
||||
*
|
||||
* - Subclasses provide: PreLoad, GetType
|
||||
*/
|
||||
|
||||
#include "d3d8_device_child.h"
|
||||
#include "../util/com/com_private_data.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
template <typename D3D9, typename D3D8>
|
||||
class D3D8Resource : public D3D8DeviceChild<D3D9, D3D8> {
|
||||
|
||||
public:
|
||||
|
||||
D3D8Resource(D3D8Device* pDevice, Com<D3D9>&& Object)
|
||||
: D3D8DeviceChild<D3D9, D3D8>(pDevice, std::move(Object))
|
||||
, m_priority ( 0 ) { }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetPrivateData(
|
||||
REFGUID refguid,
|
||||
const void* pData,
|
||||
DWORD SizeOfData,
|
||||
DWORD Flags) final {
|
||||
HRESULT hr;
|
||||
if (Flags & D3DSPD_IUNKNOWN) {
|
||||
if(unlikely(SizeOfData != sizeof(IUnknown*)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
IUnknown* unknown =
|
||||
const_cast<IUnknown*>(
|
||||
reinterpret_cast<const IUnknown*>(pData));
|
||||
hr = m_privateData.setInterface(
|
||||
refguid, unknown);
|
||||
}
|
||||
else
|
||||
hr = m_privateData.setData(
|
||||
refguid, SizeOfData, pData);
|
||||
|
||||
if (unlikely(FAILED(hr)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetPrivateData(
|
||||
REFGUID refguid,
|
||||
void* pData,
|
||||
DWORD* pSizeOfData) final {
|
||||
if (unlikely(pData == nullptr && pSizeOfData == nullptr))
|
||||
return D3DERR_NOTFOUND;
|
||||
|
||||
HRESULT hr = m_privateData.getData(
|
||||
refguid, reinterpret_cast<UINT*>(pSizeOfData), pData);
|
||||
|
||||
if (unlikely(FAILED(hr))) {
|
||||
if(hr == DXGI_ERROR_MORE_DATA)
|
||||
return D3DERR_MOREDATA;
|
||||
else if (hr == DXGI_ERROR_NOT_FOUND)
|
||||
return D3DERR_NOTFOUND;
|
||||
else
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE FreePrivateData(REFGUID refguid) final {
|
||||
HRESULT hr = m_privateData.setData(refguid, 0, nullptr);
|
||||
|
||||
if (unlikely(FAILED(hr)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE SetPriority(DWORD PriorityNew) {
|
||||
DWORD oldPriority = m_priority;
|
||||
m_priority = PriorityNew;
|
||||
return oldPriority;
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE GetPriority() {
|
||||
return m_priority;
|
||||
}
|
||||
|
||||
virtual IUnknown* GetInterface(REFIID riid) override try {
|
||||
return D3D8DeviceChild<D3D9, D3D8>::GetInterface(riid);
|
||||
} catch (HRESULT err) {
|
||||
if (riid == __uuidof(IDirect3DResource8))
|
||||
return this;
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
DWORD m_priority;
|
||||
|
||||
private:
|
||||
|
||||
ComPrivateData m_privateData;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
/** Implements IDirect3DResource8
|
||||
*
|
||||
* - SetPrivateData, GetPrivateData, FreePrivateData
|
||||
* - SetPriority, GetPriority
|
||||
*
|
||||
* - Subclasses provide: PreLoad, GetType
|
||||
*/
|
||||
|
||||
#include "d3d8_device_child.h"
|
||||
#include "../util/com/com_private_data.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
template <typename D3D9, typename D3D8>
|
||||
class D3D8Resource : public D3D8DeviceChild<D3D9, D3D8> {
|
||||
|
||||
public:
|
||||
|
||||
D3D8Resource(D3D8Device* pDevice, D3DPOOL Pool, Com<D3D9>&& Object)
|
||||
: D3D8DeviceChild<D3D9, D3D8>(pDevice, std::move(Object))
|
||||
, m_pool ( Pool )
|
||||
, m_priority ( 0 ) { }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetPrivateData(
|
||||
REFGUID refguid,
|
||||
const void* pData,
|
||||
DWORD SizeOfData,
|
||||
DWORD Flags) final {
|
||||
HRESULT hr;
|
||||
if (Flags & D3DSPD_IUNKNOWN) {
|
||||
if(unlikely(SizeOfData != sizeof(IUnknown*)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
IUnknown* unknown =
|
||||
const_cast<IUnknown*>(
|
||||
reinterpret_cast<const IUnknown*>(pData));
|
||||
hr = m_privateData.setInterface(
|
||||
refguid, unknown);
|
||||
}
|
||||
else
|
||||
hr = m_privateData.setData(
|
||||
refguid, SizeOfData, pData);
|
||||
|
||||
if (unlikely(FAILED(hr)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetPrivateData(
|
||||
REFGUID refguid,
|
||||
void* pData,
|
||||
DWORD* pSizeOfData) final {
|
||||
if (unlikely(pData == nullptr && pSizeOfData == nullptr))
|
||||
return D3DERR_NOTFOUND;
|
||||
|
||||
HRESULT hr = m_privateData.getData(
|
||||
refguid, reinterpret_cast<UINT*>(pSizeOfData), pData);
|
||||
|
||||
if (unlikely(FAILED(hr))) {
|
||||
if(hr == DXGI_ERROR_MORE_DATA)
|
||||
return D3DERR_MOREDATA;
|
||||
else if (hr == DXGI_ERROR_NOT_FOUND)
|
||||
return D3DERR_NOTFOUND;
|
||||
else
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE FreePrivateData(REFGUID refguid) final {
|
||||
HRESULT hr = m_privateData.setData(refguid, 0, nullptr);
|
||||
|
||||
if (unlikely(FAILED(hr)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
virtual IUnknown* GetInterface(REFIID riid) override try {
|
||||
return D3D8DeviceChild<D3D9, D3D8>::GetInterface(riid);
|
||||
} catch (HRESULT err) {
|
||||
if (riid == __uuidof(IDirect3DResource8))
|
||||
return this;
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
const D3DPOOL m_pool;
|
||||
DWORD m_priority;
|
||||
|
||||
private:
|
||||
|
||||
ComPrivateData m_privateData;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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,61 +1,109 @@
|
|||
#include "d3d8_device.h"
|
||||
#include "d3d8_state_block.h"
|
||||
|
||||
HRESULT dxvk::D3D8StateBlock::Capture() {
|
||||
if (unlikely(m_stateBlock == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
namespace dxvk {
|
||||
|
||||
if (m_capture.vs) m_device->GetVertexShader(&m_vertexShader);
|
||||
if (m_capture.ps) m_device->GetPixelShader(&m_pixelShader);
|
||||
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;
|
||||
}
|
||||
|
||||
for (DWORD stage = 0; stage < m_textures.size(); stage++) {
|
||||
if (m_capture.textures.get(stage))
|
||||
m_textures[stage] = m_device->m_textures[stage].ptr();
|
||||
if (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());
|
||||
}
|
||||
|
||||
for (DWORD stream = 0; stream < m_streams.size(); stream++) {
|
||||
if (m_capture.streams.get(stream)) {
|
||||
m_streams[stream].buffer = m_device->m_streams[stream].buffer.ptr();
|
||||
m_streams[stream].stride = m_device->m_streams[stream].stride;
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
if (m_capture.indices) {
|
||||
m_baseVertexIndex = m_device->m_baseVertexIndex;
|
||||
m_indices = m_device->m_indices.ptr();
|
||||
HRESULT D3D8StateBlock::Capture() {
|
||||
if (unlikely(m_stateBlock == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
if (m_capture.vs) m_device->GetVertexShader(&m_vertexShader);
|
||||
if (m_capture.ps) m_device->GetPixelShader(&m_pixelShader);
|
||||
|
||||
for (DWORD stage = 0; stage < m_textures.size(); stage++) {
|
||||
if (m_capture.textures.get(stage))
|
||||
m_textures[stage] = m_device->m_textures[stage].ptr();
|
||||
}
|
||||
|
||||
for (DWORD stream = 0; stream < m_streams.size(); stream++) {
|
||||
if (m_capture.streams.get(stream)) {
|
||||
m_streams[stream].buffer = m_device->m_streams[stream].buffer.ptr();
|
||||
m_streams[stream].stride = m_device->m_streams[stream].stride;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_capture.indices) {
|
||||
m_baseVertexIndex = m_device->m_baseVertexIndex;
|
||||
m_indices = m_device->m_indices.ptr();
|
||||
}
|
||||
|
||||
if (m_capture.swvp)
|
||||
m_device->GetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, (DWORD*)&m_isSWVP);
|
||||
|
||||
return m_stateBlock->Capture();
|
||||
}
|
||||
|
||||
if (m_capture.swvp)
|
||||
m_device->GetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, (DWORD*)&m_isSWVP);
|
||||
HRESULT D3D8StateBlock::Apply() {
|
||||
if (unlikely(m_stateBlock == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
HRESULT res = m_stateBlock->Apply();
|
||||
|
||||
if (m_capture.vs) m_device->SetVertexShader(m_vertexShader);
|
||||
if (m_capture.ps) m_device->SetPixelShader(m_pixelShader);
|
||||
|
||||
for (DWORD stage = 0; stage < m_textures.size(); stage++) {
|
||||
if (m_capture.textures.get(stage))
|
||||
m_device->SetTexture(stage, m_textures[stage]);
|
||||
}
|
||||
|
||||
for (DWORD stream = 0; stream < m_streams.size(); stream++) {
|
||||
if (m_capture.streams.get(stream))
|
||||
m_device->SetStreamSource(stream, m_streams[stream].buffer, m_streams[stream].stride);
|
||||
}
|
||||
|
||||
if (m_capture.indices)
|
||||
m_device->SetIndices(m_indices, m_baseVertexIndex);
|
||||
|
||||
// This was a very easy footgun for D3D8 applications.
|
||||
if (m_capture.swvp)
|
||||
m_device->SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, m_isSWVP);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return m_stateBlock->Capture();
|
||||
}
|
||||
|
||||
HRESULT dxvk::D3D8StateBlock::Apply() {
|
||||
if (unlikely(m_stateBlock == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
HRESULT res = m_stateBlock->Apply();
|
||||
|
||||
if (m_capture.vs) m_device->SetVertexShader(m_vertexShader);
|
||||
if (m_capture.ps) m_device->SetPixelShader(m_pixelShader);
|
||||
|
||||
for (DWORD stage = 0; stage < m_textures.size(); stage++) {
|
||||
if (m_capture.textures.get(stage))
|
||||
m_device->SetTexture(stage, m_textures[stage]);
|
||||
}
|
||||
|
||||
for (DWORD stream = 0; stream < m_streams.size(); stream++) {
|
||||
if (m_capture.streams.get(stream))
|
||||
m_device->SetStreamSource(stream, m_streams[stream].buffer, m_streams[stream].stride);
|
||||
}
|
||||
|
||||
if (m_capture.indices)
|
||||
m_device->SetIndices(m_indices, m_baseVertexIndex);
|
||||
|
||||
// This was a very easy footgun for D3D8 applications.
|
||||
if (m_capture.swvp)
|
||||
m_device->SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, m_isSWVP);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -1,153 +1,120 @@
|
|||
#pragma once
|
||||
|
||||
#include "d3d8_caps.h"
|
||||
#include "d3d8_include.h"
|
||||
#include "d3d8_device.h"
|
||||
#include "d3d8_device_child.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
struct D3D8StateCapture {
|
||||
bool vs : 1;
|
||||
bool ps : 1;
|
||||
bool indices : 1;
|
||||
bool swvp : 1;
|
||||
|
||||
bit::bitset<d8caps::MAX_TEXTURE_STAGES> textures;
|
||||
bit::bitset<d8caps::MAX_STREAMS> streams;
|
||||
|
||||
D3D8StateCapture()
|
||||
: vs(false)
|
||||
, ps(false)
|
||||
, indices(false)
|
||||
, swvp(false) {
|
||||
// Ensure all bits are initialized to false
|
||||
textures.clearAll();
|
||||
streams.clearAll();
|
||||
}
|
||||
};
|
||||
|
||||
// Wrapper class for D3D9 state blocks. Captures D3D8-specific state.
|
||||
class D3D8StateBlock {
|
||||
|
||||
public:
|
||||
|
||||
D3D8StateBlock(
|
||||
D3D8Device* pDevice,
|
||||
D3DSTATEBLOCKTYPE Type,
|
||||
Com<d3d9::IDirect3DStateBlock9>&& pStateBlock)
|
||||
: m_device(pDevice)
|
||||
, m_stateBlock(std::move(pStateBlock))
|
||||
, m_type(Type) {
|
||||
if (Type == D3DSBT_VERTEXSTATE || Type == D3DSBT_ALL) {
|
||||
// Lights, D3DTSS_TEXCOORDINDEX and D3DTSS_TEXTURETRANSFORMFLAGS,
|
||||
// vertex shader, VS constants, and various render states.
|
||||
m_capture.vs = true;
|
||||
}
|
||||
|
||||
if (Type == D3DSBT_PIXELSTATE || Type == D3DSBT_ALL) {
|
||||
// Pixel shader, PS constants, and various RS/TSS states.
|
||||
m_capture.ps = true;
|
||||
}
|
||||
|
||||
if (Type == D3DSBT_ALL) {
|
||||
m_capture.indices = true;
|
||||
m_capture.swvp = true;
|
||||
m_capture.textures.setAll();
|
||||
m_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");
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT Capture();
|
||||
|
||||
HRESULT Apply();
|
||||
|
||||
inline HRESULT SetVertexShader(DWORD Handle) {
|
||||
m_vertexShader = Handle;
|
||||
m_capture.vs = true;
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
inline HRESULT SetPixelShader(DWORD Handle) {
|
||||
m_pixelShader = Handle;
|
||||
m_capture.ps = true;
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
inline HRESULT SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) {
|
||||
m_textures[Stage] = pTexture;
|
||||
m_capture.textures.set(Stage, true);
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
inline HRESULT SetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer8* pStreamData, UINT Stride) {
|
||||
m_streams[StreamNumber].buffer = pStreamData;
|
||||
// The previous stride is preserved if pStreamData is NULL
|
||||
if (likely(pStreamData != nullptr))
|
||||
m_streams[StreamNumber].stride = Stride;
|
||||
m_capture.streams.set(StreamNumber, true);
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
inline HRESULT SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex) {
|
||||
m_indices = pIndexData;
|
||||
m_baseVertexIndex = BaseVertexIndex;
|
||||
m_capture.indices = true;
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
inline HRESULT SetSoftwareVertexProcessing(bool value) {
|
||||
m_isSWVP = value;
|
||||
m_capture.swvp = true;
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
D3D8Device* m_device;
|
||||
Com<d3d9::IDirect3DStateBlock9> m_stateBlock;
|
||||
D3DSTATEBLOCKTYPE m_type;
|
||||
|
||||
struct D3D8VBOP {
|
||||
IDirect3DVertexBuffer8* buffer = nullptr;
|
||||
UINT stride = 0;
|
||||
};
|
||||
|
||||
private: // State Data //
|
||||
|
||||
D3D8StateCapture m_capture;
|
||||
|
||||
DWORD m_vertexShader; // vs
|
||||
DWORD m_pixelShader; // ps
|
||||
|
||||
std::array<IDirect3DBaseTexture8*, d8caps::MAX_TEXTURE_STAGES> m_textures; // textures
|
||||
std::array<D3D8VBOP, d8caps::MAX_STREAMS> m_streams; // stream data
|
||||
|
||||
IDirect3DIndexBuffer8* m_indices = nullptr; // indices
|
||||
UINT m_baseVertexIndex; // indices
|
||||
|
||||
bool m_isSWVP; // D3DRS_SOFTWAREVERTEXPROCESSING
|
||||
};
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "d3d8_caps.h"
|
||||
#include "d3d8_include.h"
|
||||
#include "d3d8_device.h"
|
||||
#include "d3d8_device_child.h"
|
||||
|
||||
#include "../util/util_bit.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
struct D3D8StateCapture {
|
||||
bool vs : 1;
|
||||
bool ps : 1;
|
||||
bool indices : 1;
|
||||
bool swvp : 1;
|
||||
|
||||
bit::bitset<d8caps::MAX_TEXTURE_STAGES> textures;
|
||||
bit::bitset<d8caps::MAX_STREAMS> streams;
|
||||
|
||||
D3D8StateCapture()
|
||||
: vs(false)
|
||||
, ps(false)
|
||||
, indices(false)
|
||||
, swvp(false) {
|
||||
// Ensure all bits are initialized to false
|
||||
textures.clearAll();
|
||||
streams.clearAll();
|
||||
}
|
||||
};
|
||||
|
||||
// Wrapper class for D3D9 state blocks. Captures D3D8-specific state.
|
||||
class D3D8StateBlock {
|
||||
|
||||
public:
|
||||
|
||||
D3D8StateBlock(
|
||||
D3D8Device* pDevice,
|
||||
D3DSTATEBLOCKTYPE Type,
|
||||
Com<d3d9::IDirect3DStateBlock9>&& pStateBlock);
|
||||
|
||||
D3D8StateBlock(D3D8Device* pDevice);
|
||||
|
||||
void SetD3D9(Com<d3d9::IDirect3DStateBlock9>&& pStateBlock);
|
||||
|
||||
HRESULT Capture();
|
||||
|
||||
HRESULT Apply();
|
||||
|
||||
inline HRESULT SetVertexShader(DWORD Handle) {
|
||||
m_vertexShader = Handle;
|
||||
m_capture.vs = true;
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
inline HRESULT SetPixelShader(DWORD Handle) {
|
||||
m_pixelShader = Handle;
|
||||
m_capture.ps = true;
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
inline HRESULT SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) {
|
||||
m_textures[Stage] = pTexture;
|
||||
m_capture.textures.set(Stage, true);
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
inline HRESULT SetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer8* pStreamData, UINT Stride) {
|
||||
m_streams[StreamNumber].buffer = pStreamData;
|
||||
// The previous stride is preserved if pStreamData is NULL
|
||||
if (likely(pStreamData != nullptr))
|
||||
m_streams[StreamNumber].stride = Stride;
|
||||
m_capture.streams.set(StreamNumber, true);
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
inline HRESULT SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex) {
|
||||
m_indices = pIndexData;
|
||||
m_baseVertexIndex = BaseVertexIndex;
|
||||
m_capture.indices = true;
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
inline HRESULT SetSoftwareVertexProcessing(bool value) {
|
||||
m_isSWVP = value;
|
||||
m_capture.swvp = true;
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
D3D8Device* m_device;
|
||||
Com<d3d9::IDirect3DStateBlock9> m_stateBlock;
|
||||
D3DSTATEBLOCKTYPE m_type;
|
||||
|
||||
struct D3D8VBOP {
|
||||
IDirect3DVertexBuffer8* buffer = nullptr;
|
||||
UINT stride = 0;
|
||||
};
|
||||
|
||||
// State Data //
|
||||
|
||||
D3D8StateCapture m_capture;
|
||||
|
||||
DWORD m_vertexShader = 0;
|
||||
DWORD m_pixelShader = 0;
|
||||
|
||||
std::array<IDirect3DBaseTexture8*, d8caps::MAX_TEXTURE_STAGES> m_textures;
|
||||
std::array<D3D8VBOP, d8caps::MAX_STREAMS> m_streams;
|
||||
|
||||
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,26 +1,78 @@
|
|||
|
||||
#include "d3d8_surface.h"
|
||||
#include "d3d8_device.h"
|
||||
|
||||
#include "d3d8_d3d9_util.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
Com<d3d9::IDirect3DSurface9> D3D8Surface::CreateBlitImage() {
|
||||
d3d9::D3DSURFACE_DESC desc;
|
||||
GetD3D9()->GetDesc(&desc);
|
||||
D3D8Surface::D3D8Surface(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
IDirect3DBaseTexture8* pTexture,
|
||||
Com<d3d9::IDirect3DSurface9>&& pSurface)
|
||||
: D3D8SurfaceBase (pDevice, Pool, std::move(pSurface), pTexture) {
|
||||
}
|
||||
|
||||
// NOTE: This adds a D3DPOOL_DEFAULT resource to the
|
||||
// device, which counts as losable during device reset
|
||||
Com<d3d9::IDirect3DSurface9> image = nullptr;
|
||||
HRESULT res = GetParent()->GetD3D9()->CreateRenderTarget(
|
||||
desc.Width, desc.Height, desc.Format,
|
||||
d3d9::D3DMULTISAMPLE_NONE, 0,
|
||||
FALSE,
|
||||
&image,
|
||||
NULL);
|
||||
|
||||
if (FAILED(res))
|
||||
throw new DxvkError("D3D8: Failed to create blit image");
|
||||
|
||||
return image;
|
||||
// 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);
|
||||
|
||||
// NOTE: This adds a D3DPOOL_DEFAULT resource to the
|
||||
// device, which counts as losable during device reset
|
||||
Com<d3d9::IDirect3DSurface9> image = nullptr;
|
||||
HRESULT res = GetParent()->GetD3D9()->CreateRenderTarget(
|
||||
desc.Width, desc.Height, desc.Format,
|
||||
d3d9::D3DMULTISAMPLE_NONE, 0,
|
||||
FALSE,
|
||||
&image,
|
||||
NULL);
|
||||
|
||||
if (FAILED(res))
|
||||
throw new DxvkError("D3D8: Failed to create blit image");
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,87 +1,50 @@
|
|||
#pragma once
|
||||
|
||||
#include "d3d8_include.h"
|
||||
#include "d3d8_subresource.h"
|
||||
#include "d3d8_d3d9_util.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
// TODO: all inherited methods in D3D8Surface should be final like in d9vk
|
||||
|
||||
using D3D8SurfaceBase = D3D8Subresource<d3d9::IDirect3DSurface9, IDirect3DSurface8>;
|
||||
class D3D8Surface final : public D3D8SurfaceBase {
|
||||
|
||||
public:
|
||||
|
||||
D3D8Surface(
|
||||
D3D8Device* pDevice,
|
||||
IDirect3DBaseTexture8* pTexture,
|
||||
Com<d3d9::IDirect3DSurface9>&& pSurface)
|
||||
: D3D8SurfaceBase (pDevice, std::move(pSurface), pTexture) {
|
||||
}
|
||||
|
||||
// A surface does not need to be attached to a texture
|
||||
D3D8Surface(
|
||||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DSurface9>&& pSurface)
|
||||
: D3D8Surface (pDevice, nullptr, std::move(pSurface)) {
|
||||
}
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() {
|
||||
return D3DRESOURCETYPE(GetD3D9()->GetType());
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDesc(D3DSURFACE_DESC* pDesc) {
|
||||
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 LockRect(D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
|
||||
return GetD3D9()->LockRect((d3d9::D3DLOCKED_RECT*)pLockedRect, pRect, Flags);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE UnlockRect() {
|
||||
return GetD3D9()->UnlockRect();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDC(HDC* phDC) {
|
||||
return GetD3D9()->GetDC(phDC);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE ReleaseDC(HDC hDC) {
|
||||
return GetD3D9()->ReleaseDC(hDC);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Allocate or reuse an image of the same size
|
||||
* as this texture for performing blit into system mem.
|
||||
*
|
||||
* TODO: Consider creating only one texture to
|
||||
* encompass all surface levels of a texture.
|
||||
*/
|
||||
Com<d3d9::IDirect3DSurface9> GetBlitImage() {
|
||||
if (unlikely(m_blitImage == nullptr)) {
|
||||
m_blitImage = CreateBlitImage();
|
||||
}
|
||||
|
||||
return m_blitImage;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
Com<d3d9::IDirect3DSurface9> CreateBlitImage();
|
||||
|
||||
Com<d3d9::IDirect3DSurface9> m_blitImage = nullptr;
|
||||
|
||||
};
|
||||
#pragma once
|
||||
|
||||
#include "d3d8_include.h"
|
||||
#include "d3d8_subresource.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
// 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 {
|
||||
|
||||
public:
|
||||
|
||||
D3D8Surface(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
IDirect3DBaseTexture8* pTexture,
|
||||
Com<d3d9::IDirect3DSurface9>&& pSurface);
|
||||
|
||||
D3D8Surface(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
Com<d3d9::IDirect3DSurface9>&& pSurface);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDesc(D3DSURFACE_DESC* pDesc) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE LockRect(
|
||||
D3DLOCKED_RECT* pLockedRect,
|
||||
CONST RECT* pRect,
|
||||
DWORD Flags) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE UnlockRect() final;
|
||||
|
||||
/**
|
||||
* \brief Allocate or reuse an image of the same size
|
||||
* as this texture for performing blit into system mem.
|
||||
*/
|
||||
Com<d3d9::IDirect3DSurface9> GetBlitImage();
|
||||
|
||||
private:
|
||||
|
||||
Com<d3d9::IDirect3DSurface9> CreateBlitImage();
|
||||
|
||||
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,49 +2,28 @@
|
|||
|
||||
#include "d3d8_device_child.h"
|
||||
#include "d3d8_surface.h"
|
||||
#include "d3d8_d3d9_util.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
|
||||
using D3D8SwapChainBase = D3D8DeviceChild<d3d9::IDirect3DSwapChain9, IDirect3DSwapChain8>;
|
||||
class D3D8SwapChain final : public D3D8SwapChainBase {
|
||||
|
||||
public:
|
||||
|
||||
D3D8SwapChain(
|
||||
D3D8Device* pDevice,
|
||||
D3DPRESENT_PARAMETERS* pPresentationParameters,
|
||||
Com<d3d9::IDirect3DSwapChain9>&& pSwapChain)
|
||||
: D3D8SwapChainBase(pDevice, std::move(pSwapChain)) {
|
||||
m_backBuffers.resize(pPresentationParameters->BackBufferCount);
|
||||
}
|
||||
D3D8Device* pDevice,
|
||||
D3DPRESENT_PARAMETERS* pPresentationParameters,
|
||||
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 GetBackBuffer(UINT BackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface8** ppBackBuffer) final {
|
||||
if (unlikely(ppBackBuffer == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
HRESULT STDMETHODCALLTYPE Present(const RECT *src, const RECT *dst, HWND hWnd, const RGNDATA *dirtyRegion) final;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,254 +1,197 @@
|
|||
#pragma once
|
||||
|
||||
#include "d3d8_resource.h"
|
||||
#include "d3d8_surface.h"
|
||||
#include "d3d8_volume.h"
|
||||
|
||||
#include "d3d8_d3d9_util.h"
|
||||
|
||||
#include <vector>
|
||||
#include <new>
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
template <typename SubresourceType, typename D3D9, typename D3D8>
|
||||
class D3D8BaseTexture : public D3D8Resource<D3D9, D3D8> {
|
||||
|
||||
public:
|
||||
|
||||
constexpr static UINT CUBE_FACES = 6;
|
||||
|
||||
using SubresourceType8 = typename SubresourceType::D3D8;
|
||||
using SubresourceType9 = typename SubresourceType::D3D9;
|
||||
|
||||
D3D8BaseTexture(
|
||||
D3D8Device* pDevice,
|
||||
Com<D3D9>&& pBaseTexture,
|
||||
UINT SubresourceCount)
|
||||
: D3D8Resource<D3D9, D3D8> ( pDevice, std::move(pBaseTexture) ) {
|
||||
m_subresources.resize(SubresourceCount, nullptr);
|
||||
}
|
||||
|
||||
~D3D8BaseTexture() {
|
||||
for (size_t i = 0; i < m_subresources.size(); i++)
|
||||
if (m_subresources[i] != nullptr)
|
||||
m_subresources[i] = nullptr;
|
||||
}
|
||||
|
||||
virtual IUnknown* GetInterface(REFIID riid) final override try {
|
||||
return D3D8Resource<D3D9, D3D8>::GetInterface(riid);
|
||||
} catch (HRESULT err) {
|
||||
if (riid == __uuidof(IDirect3DBaseTexture8))
|
||||
return this;
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
void STDMETHODCALLTYPE PreLoad() final {
|
||||
this->GetD3D9()->PreLoad();
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE SetLOD(DWORD LODNew) final {
|
||||
return this->GetD3D9()->SetLOD(LODNew);
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE GetLOD() final {
|
||||
return this->GetD3D9()->GetLOD();
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE GetLevelCount() final {
|
||||
return this->GetD3D9()->GetLevelCount();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetSubresource(UINT Index, SubresourceType8** ppSubresource) {
|
||||
InitReturnPtr(ppSubresource);
|
||||
|
||||
if (unlikely(ppSubresource == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
if (unlikely(Index >= m_subresources.size()))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
if (m_subresources[Index] == nullptr) {
|
||||
try {
|
||||
Com<SubresourceType9> subresource = LookupSubresource(Index);
|
||||
|
||||
// Cache the subresource
|
||||
m_subresources[Index] = new SubresourceType(this->m_parent, this, std::move(subresource));
|
||||
} catch (HRESULT res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
*ppSubresource = m_subresources[Index].ref();
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Com<SubresourceType9> LookupSubresource(UINT Index) {
|
||||
Com<SubresourceType9> ptr = nullptr;
|
||||
HRESULT res = D3DERR_INVALIDCALL;
|
||||
if constexpr (std::is_same_v<D3D8, IDirect3DTexture8>) {
|
||||
res = this->GetD3D9()->GetSurfaceLevel(Index, &ptr);
|
||||
} else if constexpr (std::is_same_v<D3D8, IDirect3DVolumeTexture8>) {
|
||||
res = this->GetD3D9()->GetVolumeLevel(Index, &ptr);
|
||||
} else if constexpr (std::is_same_v<D3D8, IDirect3DCubeTexture8>) {
|
||||
res = this->GetD3D9()->GetCubeMapSurface(d3d9::D3DCUBEMAP_FACES(Index % CUBE_FACES), Index / CUBE_FACES, &ptr);
|
||||
}
|
||||
if (FAILED(res))
|
||||
throw res;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
std::vector<Com<SubresourceType, false>> m_subresources;
|
||||
|
||||
};
|
||||
|
||||
using D3D8Texture2DBase = D3D8BaseTexture<D3D8Surface, d3d9::IDirect3DTexture9, IDirect3DTexture8>;
|
||||
class D3D8Texture2D final : public D3D8Texture2DBase {
|
||||
|
||||
public:
|
||||
|
||||
D3D8Texture2D(
|
||||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DTexture9>&& pTexture)
|
||||
: D3D8Texture2DBase(pDevice, std::move(pTexture), pTexture->GetLevelCount()) {
|
||||
}
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_TEXTURE; }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) {
|
||||
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 GetSurfaceLevel(UINT Level, IDirect3DSurface8** ppSurfaceLevel) {
|
||||
return GetSubresource(Level, ppSurfaceLevel);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE LockRect(UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
|
||||
return GetD3D9()->LockRect(Level, reinterpret_cast<d3d9::D3DLOCKED_RECT*>(pLockedRect), pRect, Flags);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE UnlockRect(UINT Level) {
|
||||
return GetD3D9()->UnlockRect(Level);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AddDirtyRect(CONST RECT* pDirtyRect) {
|
||||
return GetD3D9()->AddDirtyRect(pDirtyRect);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
using D3D8Texture3DBase = D3D8BaseTexture<D3D8Volume, d3d9::IDirect3DVolumeTexture9, IDirect3DVolumeTexture8>;
|
||||
class D3D8Texture3D final : public D3D8Texture3DBase {
|
||||
|
||||
public:
|
||||
|
||||
D3D8Texture3D(
|
||||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DVolumeTexture9>&& pVolumeTexture)
|
||||
: D3D8Texture3DBase(pDevice, std::move(pVolumeTexture), pVolumeTexture->GetLevelCount()) {}
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_VOLUMETEXTURE; }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc) {
|
||||
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 GetVolumeLevel(UINT Level, IDirect3DVolume8** ppVolumeLevel) {
|
||||
return GetSubresource(Level, ppVolumeLevel);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE LockBox(UINT Level, D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) {
|
||||
return GetD3D9()->LockBox(
|
||||
Level,
|
||||
reinterpret_cast<d3d9::D3DLOCKED_BOX*>(pLockedBox),
|
||||
reinterpret_cast<const d3d9::D3DBOX*>(pBox),
|
||||
Flags
|
||||
);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE UnlockBox(UINT Level) {
|
||||
return GetD3D9()->UnlockBox(Level);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AddDirtyBox(CONST D3DBOX* pDirtyBox) {
|
||||
return GetD3D9()->AddDirtyBox(reinterpret_cast<const d3d9::D3DBOX*>(pDirtyBox));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
using D3D8TextureCubeBase = D3D8BaseTexture<D3D8Surface, d3d9::IDirect3DCubeTexture9, IDirect3DCubeTexture8>;
|
||||
class D3D8TextureCube final : public D3D8TextureCubeBase {
|
||||
|
||||
public:
|
||||
|
||||
D3D8TextureCube(
|
||||
D3D8Device* pDevice,
|
||||
Com<d3d9::IDirect3DCubeTexture9>&& pTexture)
|
||||
: D3D8TextureCubeBase(pDevice, std::move(pTexture), pTexture->GetLevelCount() * CUBE_FACES) {
|
||||
}
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_CUBETEXTURE; }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) {
|
||||
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 GetCubeMapSurface(D3DCUBEMAP_FACES Face, UINT Level, IDirect3DSurface8** ppSurfaceLevel) {
|
||||
return GetSubresource((Level * CUBE_FACES) + Face, ppSurfaceLevel);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE LockRect(
|
||||
D3DCUBEMAP_FACES Face,
|
||||
UINT Level,
|
||||
D3DLOCKED_RECT* pLockedRect,
|
||||
const RECT* pRect,
|
||||
DWORD Flags) {
|
||||
return GetD3D9()->LockRect(
|
||||
d3d9::D3DCUBEMAP_FACES(Face),
|
||||
Level,
|
||||
reinterpret_cast<d3d9::D3DLOCKED_RECT*>(pLockedRect),
|
||||
pRect,
|
||||
Flags);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE UnlockRect(D3DCUBEMAP_FACES Face, UINT Level) {
|
||||
return GetD3D9()->UnlockRect(d3d9::D3DCUBEMAP_FACES(Face), Level);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AddDirtyRect(D3DCUBEMAP_FACES Face, const RECT* pDirtyRect) {
|
||||
return GetD3D9()->AddDirtyRect(d3d9::D3DCUBEMAP_FACES(Face), pDirtyRect);
|
||||
}
|
||||
};
|
||||
#pragma once
|
||||
|
||||
#include "d3d8_resource.h"
|
||||
#include "d3d8_surface.h"
|
||||
#include "d3d8_volume.h"
|
||||
|
||||
#include <vector>
|
||||
#include <new>
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
template <typename SubresourceType, typename D3D9, typename D3D8>
|
||||
class D3D8BaseTexture : public D3D8Resource<D3D9, D3D8> {
|
||||
|
||||
public:
|
||||
|
||||
constexpr static UINT CUBE_FACES = 6;
|
||||
|
||||
using SubresourceType8 = typename SubresourceType::D3D8;
|
||||
using SubresourceType9 = typename SubresourceType::D3D9;
|
||||
|
||||
D3D8BaseTexture(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
Com<D3D9>&& pBaseTexture,
|
||||
UINT SubresourceCount)
|
||||
: D3D8Resource<D3D9, D3D8> ( pDevice, Pool, std::move(pBaseTexture) ) {
|
||||
m_subresources.resize(SubresourceCount, nullptr);
|
||||
}
|
||||
|
||||
~D3D8BaseTexture() {
|
||||
for (size_t i = 0; i < m_subresources.size(); i++)
|
||||
if (m_subresources[i] != nullptr)
|
||||
m_subresources[i] = nullptr;
|
||||
}
|
||||
|
||||
virtual IUnknown* GetInterface(REFIID riid) final override try {
|
||||
return D3D8Resource<D3D9, D3D8>::GetInterface(riid);
|
||||
} catch (HRESULT err) {
|
||||
if (riid == __uuidof(IDirect3DBaseTexture8))
|
||||
return this;
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
void STDMETHODCALLTYPE PreLoad() final {
|
||||
this->GetD3D9()->PreLoad();
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE SetLOD(DWORD LODNew) final {
|
||||
return this->GetD3D9()->SetLOD(LODNew);
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE GetLOD() final {
|
||||
return this->GetD3D9()->GetLOD();
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE GetLevelCount() final {
|
||||
return this->GetD3D9()->GetLevelCount();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetSubresource(UINT Index, SubresourceType8** ppSubresource) {
|
||||
InitReturnPtr(ppSubresource);
|
||||
|
||||
if (unlikely(ppSubresource == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
if (unlikely(Index >= m_subresources.size()))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
if (m_subresources[Index] == nullptr) {
|
||||
try {
|
||||
Com<SubresourceType9> subresource = LookupSubresource(Index);
|
||||
|
||||
// Cache the subresource
|
||||
m_subresources[Index] = new SubresourceType(this->m_parent, this->m_pool, this, std::move(subresource));
|
||||
} catch (HRESULT res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
*ppSubresource = m_subresources[Index].ref();
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Com<SubresourceType9> LookupSubresource(UINT Index) {
|
||||
Com<SubresourceType9> ptr = nullptr;
|
||||
HRESULT res = D3DERR_INVALIDCALL;
|
||||
if constexpr (std::is_same_v<D3D8, IDirect3DTexture8>) {
|
||||
res = this->GetD3D9()->GetSurfaceLevel(Index, &ptr);
|
||||
} else if constexpr (std::is_same_v<D3D8, IDirect3DVolumeTexture8>) {
|
||||
res = this->GetD3D9()->GetVolumeLevel(Index, &ptr);
|
||||
} else if constexpr (std::is_same_v<D3D8, IDirect3DCubeTexture8>) {
|
||||
res = this->GetD3D9()->GetCubeMapSurface(d3d9::D3DCUBEMAP_FACES(Index % CUBE_FACES), Index / CUBE_FACES, &ptr);
|
||||
}
|
||||
if (FAILED(res))
|
||||
throw res;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
std::vector<Com<SubresourceType, false>> m_subresources;
|
||||
|
||||
};
|
||||
|
||||
using D3D8Texture2DBase = D3D8BaseTexture<D3D8Surface, d3d9::IDirect3DTexture9, IDirect3DTexture8>;
|
||||
class D3D8Texture2D final : public D3D8Texture2DBase {
|
||||
|
||||
public:
|
||||
|
||||
D3D8Texture2D(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
Com<d3d9::IDirect3DTexture9>&& pTexture);
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetSurfaceLevel(UINT Level, IDirect3DSurface8** ppSurfaceLevel);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE LockRect(
|
||||
UINT Level,
|
||||
D3DLOCKED_RECT* pLockedRect,
|
||||
CONST RECT* pRect,
|
||||
DWORD Flags);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE UnlockRect(UINT Level);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AddDirtyRect(CONST RECT* pDirtyRect);
|
||||
|
||||
};
|
||||
|
||||
using D3D8Texture3DBase = D3D8BaseTexture<D3D8Volume, d3d9::IDirect3DVolumeTexture9, IDirect3DVolumeTexture8>;
|
||||
class D3D8Texture3D final : public D3D8Texture3DBase {
|
||||
|
||||
public:
|
||||
|
||||
D3D8Texture3D(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
Com<d3d9::IDirect3DVolumeTexture9>&& pVolumeTexture);
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetVolumeLevel(UINT Level, IDirect3DVolume8** ppVolumeLevel);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE LockBox(
|
||||
UINT Level,
|
||||
D3DLOCKED_BOX* pLockedBox,
|
||||
CONST D3DBOX* pBox,
|
||||
DWORD Flags);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE UnlockBox(UINT Level);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AddDirtyBox(CONST D3DBOX* pDirtyBox);
|
||||
|
||||
};
|
||||
|
||||
using D3D8TextureCubeBase = D3D8BaseTexture<D3D8Surface, d3d9::IDirect3DCubeTexture9, IDirect3DCubeTexture8>;
|
||||
class D3D8TextureCube final : public D3D8TextureCubeBase {
|
||||
|
||||
public:
|
||||
|
||||
D3D8TextureCube(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
Com<d3d9::IDirect3DCubeTexture9>&& pTexture);
|
||||
|
||||
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc);
|
||||
|
||||
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);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE UnlockRect(D3DCUBEMAP_FACES Face, UINT Level);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AddDirtyRect(D3DCUBEMAP_FACES Face, const RECT* 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,46 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "d3d8_subresource.h"
|
||||
#include "d3d8_d3d9_util.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
using D3D8VolumeBase = D3D8Subresource<d3d9::IDirect3DVolume9, IDirect3DVolume8>;
|
||||
class D3D8Volume final : public D3D8VolumeBase {
|
||||
|
||||
public:
|
||||
|
||||
D3D8Volume(
|
||||
D3D8Device* pDevice,
|
||||
IDirect3DVolumeTexture8* pTexture,
|
||||
Com<d3d9::IDirect3DVolume9>&& pVolume)
|
||||
: D3D8VolumeBase(pDevice, std::move(pVolume), pTexture) {}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDesc(D3DVOLUME_DESC* pDesc) {
|
||||
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 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();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "d3d8_subresource.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
using D3D8VolumeBase = D3D8Subresource<d3d9::IDirect3DVolume9, IDirect3DVolume8>;
|
||||
class D3D8Volume final : public D3D8VolumeBase {
|
||||
|
||||
public:
|
||||
|
||||
D3D8Volume(
|
||||
D3D8Device* pDevice,
|
||||
const D3DPOOL Pool,
|
||||
IDirect3DVolumeTexture8* pTexture,
|
||||
Com<d3d9::IDirect3DVolume9>&& pVolume);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDesc(D3DVOLUME_DESC* pDesc);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE UnlockBox() final;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -38,7 +38,7 @@ namespace dxvk {
|
|||
return this;
|
||||
if (riid == __uuidof(D3D8))
|
||||
return this;
|
||||
|
||||
|
||||
throw E_NOINTERFACE;
|
||||
}
|
||||
|
||||
|
@ -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 = []
|
||||
|
|
|
@ -412,9 +412,13 @@ namespace dxvk {
|
|||
| D3DPBLENDCAPS_SRCALPHASAT
|
||||
| D3DPBLENDCAPS_BOTHSRCALPHA
|
||||
| D3DPBLENDCAPS_BOTHINVSRCALPHA
|
||||
| D3DPBLENDCAPS_BLENDFACTOR
|
||||
| D3DPBLENDCAPS_INVSRCCOLOR2
|
||||
| D3DPBLENDCAPS_SRCCOLOR2;
|
||||
| D3DPBLENDCAPS_BLENDFACTOR;
|
||||
|
||||
// Only 9Ex devices advertise D3DPBLENDCAPS_SRCCOLOR2 and D3DPBLENDCAPS_INVSRCCOLOR2
|
||||
if (m_parent->IsExtended())
|
||||
pCaps->SrcBlendCaps |= D3DPBLENDCAPS_SRCCOLOR2
|
||||
| D3DPBLENDCAPS_INVSRCCOLOR2;
|
||||
|
||||
// Destination Blend Caps
|
||||
pCaps->DestBlendCaps = pCaps->SrcBlendCaps;
|
||||
// Alpha Comparison Caps
|
||||
|
@ -569,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 */
|
||||
|
@ -626,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;
|
||||
|
|
|
@ -127,6 +127,7 @@ namespace dxvk {
|
|||
INT STDMETHODCALLTYPE D3D9UserDefinedAnnotation::BeginEvent(
|
||||
D3DCOLOR Color,
|
||||
LPCWSTR Name) {
|
||||
D3D9DeviceLock lock = m_container->LockDevice();
|
||||
m_container->EmitCs([color = Color, labelName = dxvk::str::fromws(Name)](DxvkContext *ctx) {
|
||||
VkDebugUtilsLabelEXT label;
|
||||
label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
|
||||
|
@ -134,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.
|
||||
|
@ -143,6 +144,7 @@ namespace dxvk {
|
|||
|
||||
|
||||
INT STDMETHODCALLTYPE D3D9UserDefinedAnnotation::EndEvent() {
|
||||
D3D9DeviceLock lock = m_container->LockDevice();
|
||||
m_container->EmitCs([](DxvkContext *ctx) {
|
||||
ctx->endDebugLabel();
|
||||
});
|
||||
|
@ -155,6 +157,7 @@ namespace dxvk {
|
|||
void STDMETHODCALLTYPE D3D9UserDefinedAnnotation::SetMarker(
|
||||
D3DCOLOR Color,
|
||||
LPCWSTR Name) {
|
||||
D3D9DeviceLock lock = m_container->LockDevice();
|
||||
m_container->EmitCs([color = Color, labelName = dxvk::str::fromws(Name)](DxvkContext *ctx) {
|
||||
VkDebugUtilsLabelEXT label;
|
||||
label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
|
||||
|
@ -162,7 +165,7 @@ namespace dxvk {
|
|||
label.pLabelName = labelName.c_str();
|
||||
DecodeD3DCOLOR(color, label.color);
|
||||
|
||||
ctx->insertDebugLabel(&label);
|
||||
ctx->insertDebugLabel(label);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,111 +1,125 @@
|
|||
|
||||
#include "d3d9_device.h"
|
||||
#include "d3d9_interface.h"
|
||||
#include "d3d9_bridge.h"
|
||||
#include "d3d9_swapchain.h"
|
||||
#include "d3d9_surface.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
DxvkD3D8Bridge::DxvkD3D8Bridge(D3D9DeviceEx* pDevice)
|
||||
: m_device(pDevice) {
|
||||
}
|
||||
|
||||
DxvkD3D8Bridge::~DxvkD3D8Bridge() {
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE DxvkD3D8Bridge::AddRef() {
|
||||
return m_device->AddRef();
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE DxvkD3D8Bridge::Release() {
|
||||
return m_device->Release();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxvkD3D8Bridge::QueryInterface(
|
||||
REFIID riid,
|
||||
void** ppvObject) {
|
||||
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,
|
||||
const RECT* pSrcRect,
|
||||
const POINT* pDestPoint) {
|
||||
auto lock = m_device->LockDevice();
|
||||
|
||||
D3D9Surface* dst = static_cast<D3D9Surface*>(pDestSurface);
|
||||
D3D9Surface* src = static_cast<D3D9Surface*>(pSrcSurface);
|
||||
|
||||
if (unlikely(dst == nullptr || src == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
D3D9CommonTexture* srcTextureInfo = src->GetCommonTexture();
|
||||
D3D9CommonTexture* dstTextureInfo = dst->GetCommonTexture();
|
||||
|
||||
VkOffset3D srcOffset = { 0u, 0u, 0u };
|
||||
VkOffset3D dstOffset = { 0u, 0u, 0u };
|
||||
VkExtent3D texLevelExtent = srcTextureInfo->GetExtentMip(src->GetSubresource());
|
||||
VkExtent3D extent = texLevelExtent;
|
||||
|
||||
srcOffset = { pSrcRect->left,
|
||||
pSrcRect->top,
|
||||
0u };
|
||||
|
||||
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(),
|
||||
srcOffset, extent, dstOffset
|
||||
);
|
||||
|
||||
dstTextureInfo->SetNeedsReadback(dst->GetSubresource(), true);
|
||||
|
||||
if (dstTextureInfo->IsAutomaticMip())
|
||||
m_device->MarkTextureMipsDirty(dstTextureInfo);
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
DxvkD3D8InterfaceBridge::DxvkD3D8InterfaceBridge(D3D9InterfaceEx* pObject)
|
||||
: m_interface(pObject) {
|
||||
}
|
||||
|
||||
DxvkD3D8InterfaceBridge::~DxvkD3D8InterfaceBridge() {
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE DxvkD3D8InterfaceBridge::AddRef() {
|
||||
return m_interface->AddRef();
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE DxvkD3D8InterfaceBridge::Release() {
|
||||
return m_interface->Release();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxvkD3D8InterfaceBridge::QueryInterface(
|
||||
REFIID riid,
|
||||
void** ppvObject) {
|
||||
return m_interface->QueryInterface(riid, ppvObject);
|
||||
}
|
||||
|
||||
void DxvkD3D8InterfaceBridge::SetD3D8CompatibilityMode(const bool compatMode) {
|
||||
m_interface->SetD3D8CompatibilityMode(compatMode);
|
||||
}
|
||||
|
||||
const Config* DxvkD3D8InterfaceBridge::GetConfig() const {
|
||||
return &m_interface->GetInstance()->config();
|
||||
}
|
||||
}
|
||||
|
||||
#include "d3d9_device.h"
|
||||
#include "d3d9_interface.h"
|
||||
#include "d3d9_bridge.h"
|
||||
#include "d3d9_swapchain.h"
|
||||
#include "d3d9_surface.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
DxvkD3D8Bridge::DxvkD3D8Bridge(D3D9DeviceEx* pDevice)
|
||||
: m_device(pDevice) {
|
||||
}
|
||||
|
||||
DxvkD3D8Bridge::~DxvkD3D8Bridge() {
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE DxvkD3D8Bridge::AddRef() {
|
||||
return m_device->AddRef();
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE DxvkD3D8Bridge::Release() {
|
||||
return m_device->Release();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxvkD3D8Bridge::QueryInterface(
|
||||
REFIID riid,
|
||||
void** ppvObject) {
|
||||
return m_device->QueryInterface(riid, ppvObject);
|
||||
}
|
||||
|
||||
HRESULT DxvkD3D8Bridge::UpdateTextureFromBuffer(
|
||||
IDirect3DSurface9* pDestSurface,
|
||||
IDirect3DSurface9* pSrcSurface,
|
||||
const RECT* pSrcRect,
|
||||
const POINT* pDestPoint) {
|
||||
auto lock = m_device->LockDevice();
|
||||
|
||||
D3D9Surface* dst = static_cast<D3D9Surface*>(pDestSurface);
|
||||
D3D9Surface* src = static_cast<D3D9Surface*>(pSrcSurface);
|
||||
|
||||
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();
|
||||
|
||||
VkOffset3D srcOffset = { 0u, 0u, 0u };
|
||||
VkOffset3D dstOffset = { 0u, 0u, 0u };
|
||||
VkExtent3D texLevelExtent = srcTextureInfo->GetExtentMip(src->GetSubresource());
|
||||
VkExtent3D extent = texLevelExtent;
|
||||
|
||||
srcOffset = { pSrcRect->left,
|
||||
pSrcRect->top,
|
||||
0u };
|
||||
|
||||
extent = { uint32_t(pSrcRect->right - pSrcRect->left), uint32_t(pSrcRect->bottom - pSrcRect->top), 1 };
|
||||
|
||||
dstOffset = { pDestPoint->x,
|
||||
pDestPoint->y,
|
||||
0u };
|
||||
|
||||
m_device->UpdateTextureFromBuffer(
|
||||
srcTextureInfo, dstTextureInfo,
|
||||
src->GetSubresource(), dst->GetSubresource(),
|
||||
srcOffset, extent, dstOffset
|
||||
);
|
||||
|
||||
dstTextureInfo->SetNeedsReadback(dst->GetSubresource(), true);
|
||||
|
||||
if (dstTextureInfo->IsAutomaticMip())
|
||||
m_device->MarkTextureMipsDirty(dstTextureInfo);
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
DxvkD3D8InterfaceBridge::DxvkD3D8InterfaceBridge(D3D9InterfaceEx* pObject)
|
||||
: m_interface(pObject) {
|
||||
}
|
||||
|
||||
DxvkD3D8InterfaceBridge::~DxvkD3D8InterfaceBridge() {
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE DxvkD3D8InterfaceBridge::AddRef() {
|
||||
return m_interface->AddRef();
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE DxvkD3D8InterfaceBridge::Release() {
|
||||
return m_interface->Release();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxvkD3D8InterfaceBridge::QueryInterface(
|
||||
REFIID riid,
|
||||
void** ppvObject) {
|
||||
return m_interface->QueryInterface(riid, ppvObject);
|
||||
}
|
||||
|
||||
void DxvkD3D8InterfaceBridge::SetD3D8CompatibilityMode(const bool compatMode) {
|
||||
m_interface->SetD3D8CompatibilityMode(compatMode);
|
||||
}
|
||||
|
||||
const Config* DxvkD3D8InterfaceBridge::GetConfig() const {
|
||||
return &m_interface->GetInstance()->config();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,117 +1,119 @@
|
|||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include "../util/config/config.h"
|
||||
|
||||
/**
|
||||
* The D3D9 bridge allows D3D8 to access DXVK internals.
|
||||
* For Vulkan interop without needing DXVK internals, see d3d9_interop.h.
|
||||
*
|
||||
* NOTE: You must include "d3d9_include.h" or "d3d8_include.h" before this header.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief D3D9 device interface for D3D8 interop
|
||||
*/
|
||||
MIDL_INTERFACE("D3D9D3D8-42A9-4C1E-AA97-BEEFCAFE2000")
|
||||
IDxvkD3D8Bridge : public IUnknown {
|
||||
|
||||
// D3D8 keeps D3D9 objects contained in a namespace.
|
||||
#ifdef DXVK_D3D9_NAMESPACE
|
||||
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
|
||||
*
|
||||
* \param [in] pDestSurface Destination surface (typically in VRAM)
|
||||
* \param [in] pSrcSurface Source surface (typically in system memory)
|
||||
* \param [in] pSrcRect Source rectangle
|
||||
* \param [in] pDestPoint Destination (top-left) point
|
||||
*/
|
||||
virtual HRESULT UpdateTextureFromBuffer(
|
||||
IDirect3DSurface9* pDestSurface,
|
||||
IDirect3DSurface9* pSrcSurface,
|
||||
const RECT* pSrcRect,
|
||||
const POINT* pDestPoint) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief D3D9 instance interface for D3D8 interop
|
||||
*/
|
||||
MIDL_INTERFACE("D3D9D3D8-A407-773E-18E9-CAFEBEEF3000")
|
||||
IDxvkD3D8InterfaceBridge : public IUnknown {
|
||||
/**
|
||||
* \brief Enables or disables D3D9-specific features and validations
|
||||
*
|
||||
* \param [in] compatMode Compatibility state
|
||||
*/
|
||||
virtual void SetD3D8CompatibilityMode(const bool compatMode) = 0;
|
||||
|
||||
/**
|
||||
* \brief Retrieves the DXVK configuration
|
||||
*
|
||||
* \returns The DXVK Config object
|
||||
*/
|
||||
virtual const dxvk::Config* GetConfig() const = 0;
|
||||
};
|
||||
|
||||
#ifndef _MSC_VER
|
||||
__CRT_UUID_DECL(IDxvkD3D8Bridge, 0xD3D9D3D8, 0x42A9, 0x4C1E, 0xAA, 0x97, 0xBE, 0xEF, 0xCA, 0xFE, 0x20, 0x00);
|
||||
__CRT_UUID_DECL(IDxvkD3D8InterfaceBridge, 0xD3D9D3D8, 0xA407, 0x773E, 0x18, 0xE9, 0xCA, 0xFE, 0xBE, 0xEF, 0x30, 0x00);
|
||||
#endif
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
class D3D9DeviceEx;
|
||||
class D3D9InterfaceEx;
|
||||
|
||||
class DxvkD3D8Bridge : public IDxvkD3D8Bridge {
|
||||
public:
|
||||
DxvkD3D8Bridge(D3D9DeviceEx* pDevice);
|
||||
~DxvkD3D8Bridge();
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef();
|
||||
ULONG STDMETHODCALLTYPE Release();
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(
|
||||
REFIID riid,
|
||||
void** ppvObject);
|
||||
|
||||
void SetAPIName(const char* name);
|
||||
|
||||
HRESULT UpdateTextureFromBuffer(
|
||||
IDirect3DSurface9* pDestSurface,
|
||||
IDirect3DSurface9* pSrcSurface,
|
||||
const RECT* pSrcRect,
|
||||
const POINT* pDestPoint);
|
||||
|
||||
private:
|
||||
D3D9DeviceEx* m_device;
|
||||
};
|
||||
|
||||
class DxvkD3D8InterfaceBridge : public IDxvkD3D8InterfaceBridge {
|
||||
public:
|
||||
DxvkD3D8InterfaceBridge(D3D9InterfaceEx* pObject);
|
||||
~DxvkD3D8InterfaceBridge();
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef();
|
||||
ULONG STDMETHODCALLTYPE Release();
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(
|
||||
REFIID riid,
|
||||
void** ppvObject);
|
||||
|
||||
void SetD3D8CompatibilityMode(const bool compatMode);
|
||||
|
||||
const Config* GetConfig() const;
|
||||
|
||||
protected:
|
||||
D3D9InterfaceEx* m_interface;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include "../util/config/config.h"
|
||||
|
||||
/**
|
||||
* The D3D9 bridge allows D3D8 to access DXVK internals.
|
||||
* For Vulkan interop without needing DXVK internals, see d3d9_interop.h.
|
||||
*
|
||||
* NOTE: You must include "d3d9_include.h" or "d3d8_include.h" before this header.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief D3D9 device interface for D3D8 interop
|
||||
*/
|
||||
MIDL_INTERFACE("D3D9D3D8-42A9-4C1E-AA97-BEEFCAFE2000")
|
||||
IDxvkD3D8Bridge : public IUnknown {
|
||||
|
||||
// D3D8 keeps D3D9 objects contained in a namespace.
|
||||
#ifdef DXVK_D3D9_NAMESPACE
|
||||
using IDirect3DSurface9 = d3d9::IDirect3DSurface9;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Updates a D3D9 surface from a D3D9 buffer
|
||||
*
|
||||
* \param [in] pDestSurface Destination surface (typically in VRAM)
|
||||
* \param [in] pSrcSurface Source surface (typically in system memory)
|
||||
* \param [in] pSrcRect Source rectangle
|
||||
* \param [in] pDestPoint Destination (top-left) point
|
||||
*/
|
||||
virtual HRESULT UpdateTextureFromBuffer(
|
||||
IDirect3DSurface9* pDestSurface,
|
||||
IDirect3DSurface9* pSrcSurface,
|
||||
const RECT* pSrcRect,
|
||||
const POINT* pDestPoint) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief D3D9 instance interface for D3D8 interop
|
||||
*/
|
||||
MIDL_INTERFACE("D3D9D3D8-A407-773E-18E9-CAFEBEEF3000")
|
||||
IDxvkD3D8InterfaceBridge : public IUnknown {
|
||||
/**
|
||||
* \brief Enables or disables D3D9-specific features and validations
|
||||
*
|
||||
* \param [in] compatMode Compatibility state
|
||||
*/
|
||||
virtual void SetD3D8CompatibilityMode(const bool compatMode) = 0;
|
||||
|
||||
/**
|
||||
* \brief Retrieves the DXVK configuration
|
||||
*
|
||||
* \returns The DXVK Config object
|
||||
*/
|
||||
virtual const dxvk::Config* GetConfig() const = 0;
|
||||
};
|
||||
|
||||
#ifndef _MSC_VER
|
||||
__CRT_UUID_DECL(IDxvkD3D8Bridge, 0xD3D9D3D8, 0x42A9, 0x4C1E, 0xAA, 0x97, 0xBE, 0xEF, 0xCA, 0xFE, 0x20, 0x00);
|
||||
__CRT_UUID_DECL(IDxvkD3D8InterfaceBridge, 0xD3D9D3D8, 0xA407, 0x773E, 0x18, 0xE9, 0xCA, 0xFE, 0xBE, 0xEF, 0x30, 0x00);
|
||||
#endif
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
class D3D9DeviceEx;
|
||||
class D3D9InterfaceEx;
|
||||
|
||||
class DxvkD3D8Bridge : public IDxvkD3D8Bridge {
|
||||
|
||||
public:
|
||||
|
||||
DxvkD3D8Bridge(D3D9DeviceEx* pDevice);
|
||||
|
||||
~DxvkD3D8Bridge();
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef();
|
||||
ULONG STDMETHODCALLTYPE Release();
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(
|
||||
REFIID riid,
|
||||
void** ppvObject);
|
||||
|
||||
HRESULT UpdateTextureFromBuffer(
|
||||
IDirect3DSurface9* pDestSurface,
|
||||
IDirect3DSurface9* pSrcSurface,
|
||||
const RECT* pSrcRect,
|
||||
const POINT* pDestPoint);
|
||||
|
||||
private:
|
||||
|
||||
D3D9DeviceEx* m_device;
|
||||
|
||||
};
|
||||
|
||||
class DxvkD3D8InterfaceBridge : public IDxvkD3D8InterfaceBridge {
|
||||
|
||||
public:
|
||||
|
||||
DxvkD3D8InterfaceBridge(D3D9InterfaceEx* pObject);
|
||||
|
||||
~DxvkD3D8InterfaceBridge();
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef();
|
||||
ULONG STDMETHODCALLTYPE Release();
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(
|
||||
REFIID riid,
|
||||
void** ppvObject);
|
||||
|
||||
void SetD3D8CompatibilityMode(const bool compatMode);
|
||||
|
||||
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,
|
||||
|
|
|
@ -178,9 +178,38 @@ namespace dxvk {
|
|||
if (pDesc->Usage & D3DUSAGE_WRITEONLY)
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
constexpr DWORD usageRTOrDS = D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL;
|
||||
|
||||
// RENDERTARGET and DEPTHSTENCIL must be default pool
|
||||
constexpr DWORD incompatibleUsages = D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL;
|
||||
if (pDesc->Pool != D3DPOOL_DEFAULT && (pDesc->Usage & incompatibleUsages))
|
||||
if (pDesc->Pool != D3DPOOL_DEFAULT && (pDesc->Usage & usageRTOrDS))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// RENDERTARGET and DEPTHSTENCIL in D3DPOOL_DEFAULT
|
||||
// can not also have DYNAMIC usage
|
||||
if (pDesc->Pool == D3DPOOL_DEFAULT &&
|
||||
(pDesc->Usage & usageRTOrDS) &&
|
||||
(pDesc->Usage & D3DUSAGE_DYNAMIC))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
const bool isPlainSurface = ResourceType == D3DRTYPE_SURFACE && !(pDesc->Usage & usageRTOrDS);
|
||||
const bool isDepthStencilFormat = IsDepthStencilFormat(pDesc->Format);
|
||||
|
||||
// With the exception of image surfaces (d3d8)
|
||||
// or plain offscreen surfaces (d3d9), depth stencil
|
||||
// formats can only be used in D3DPOOL_DEFAULT
|
||||
if (!isPlainSurface && pDesc->Pool != D3DPOOL_DEFAULT && isDepthStencilFormat)
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// Depth stencil formats can not have RENDERTARGET
|
||||
// usage, and nothing except depth stencil formats
|
||||
// can have DEPTHSTENCIL usage
|
||||
if (( isDepthStencilFormat && (pDesc->Usage & D3DUSAGE_RENDERTARGET)) ||
|
||||
(!isDepthStencilFormat && (pDesc->Usage & D3DUSAGE_DEPTHSTENCIL)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// Volume textures can not be used as render targets
|
||||
if (ResourceType == D3DRTYPE_VOLUMETEXTURE &&
|
||||
(pDesc->Usage & D3DUSAGE_RENDERTARGET))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// Volume textures in D3DPOOL_SCRATCH must not have DYNAMIC usage
|
||||
|
@ -202,13 +231,13 @@ namespace dxvk {
|
|||
pDesc->MipLevels = maxMipLevelCount;
|
||||
|
||||
if (unlikely(pDesc->Discard)) {
|
||||
if (!IsDepthStencilFormat(pDesc->Format))
|
||||
if (!isDepthStencilFormat)
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
if (pDesc->Format == D3D9Format::D32_LOCKABLE
|
||||
|| pDesc->Format == D3D9Format::D32F_LOCKABLE
|
||||
|| pDesc->Format == D3D9Format::D16_LOCKABLE
|
||||
|| pDesc->Format == D3D9Format::S8_LOCKABLE)
|
||||
|| pDesc->Format == D3D9Format::D32F_LOCKABLE
|
||||
|| pDesc->Format == D3D9Format::D16_LOCKABLE
|
||||
|| pDesc->Format == D3D9Format::S8_LOCKABLE)
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
@ -319,12 +319,14 @@ namespace dxvk {
|
|||
return std::exchange(m_transitionedToHazardLayout, true);
|
||||
}
|
||||
|
||||
D3DRESOURCETYPE GetType() {
|
||||
D3DRESOURCETYPE GetType() const {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
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); }
|
||||
|
@ -441,6 +443,10 @@ namespace dxvk {
|
|||
static VkImageType GetImageTypeFromResourceType(
|
||||
D3DRESOURCETYPE Dimension);
|
||||
|
||||
static VkImageViewType GetImageViewTypeFromResourceType(
|
||||
D3DRESOURCETYPE Dimension,
|
||||
UINT Layer);
|
||||
|
||||
/**
|
||||
* \brief Tracks sequence number for a given subresource
|
||||
*
|
||||
|
@ -553,10 +559,6 @@ namespace dxvk {
|
|||
|
||||
void ExportImageInfo();
|
||||
|
||||
static VkImageViewType GetImageViewTypeFromResourceType(
|
||||
D3DRESOURCETYPE Dimension,
|
||||
UINT Layer);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -476,10 +476,15 @@ namespace dxvk {
|
|||
Logger::info("Device reset");
|
||||
m_deviceLostState = D3D9DeviceLostState::Ok;
|
||||
|
||||
HRESULT hr = m_parent->ValidatePresentationParameters(pPresentationParameters);
|
||||
HRESULT hr;
|
||||
// Black Desert creates a D3DDEVTYPE_NULLREF device and
|
||||
// expects reset to work despite passing invalid parameters.
|
||||
if (likely(m_deviceType != D3DDEVTYPE_NULLREF)) {
|
||||
hr = m_parent->ValidatePresentationParameters(pPresentationParameters);
|
||||
|
||||
if (unlikely(FAILED(hr)))
|
||||
return hr;
|
||||
if (unlikely(FAILED(hr)))
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (!IsExtended()) {
|
||||
// The internal references are always cleared, regardless of whether the Reset call succeeds.
|
||||
|
@ -509,7 +514,8 @@ namespace dxvk {
|
|||
if (unlikely(m_losableResourceCounter.load() != 0 && !IsExtended() && m_d3d9Options.countLosableResources)) {
|
||||
Logger::warn(str::format("Device reset failed because device still has alive losable resources: Device not reset. Remaining resources: ", m_losableResourceCounter.load()));
|
||||
m_deviceLostState = D3D9DeviceLostState::NotReset;
|
||||
return D3DERR_DEVICELOST;
|
||||
// D3D8 returns D3DERR_DEVICELOST here, whereas D3D9 returns D3DERR_INVALIDCALL.
|
||||
return m_isD3D8Compatible ? D3DERR_DEVICELOST : D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
hr = ResetSwapChain(pPresentationParameters, nullptr);
|
||||
|
@ -533,7 +539,7 @@ namespace dxvk {
|
|||
SynchronizeCsThread(DxvkCsThread::SynchronizeAll);
|
||||
|
||||
if (m_d3d9Options.deferSurfaceCreation)
|
||||
m_deviceHasBeenReset = true;
|
||||
m_resetCtr++;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
@ -651,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();
|
||||
|
@ -711,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();
|
||||
|
||||
|
@ -769,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();
|
||||
|
||||
|
@ -813,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();
|
||||
|
||||
|
@ -856,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();
|
||||
|
||||
|
@ -1587,7 +1593,12 @@ namespace dxvk {
|
|||
IDirect3DSurface9* pRenderTarget) {
|
||||
D3D9DeviceLock lock = LockDevice();
|
||||
|
||||
if (unlikely((pRenderTarget == nullptr && RenderTargetIndex == 0)))
|
||||
if (unlikely(pRenderTarget == nullptr && RenderTargetIndex == 0))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// We need to make sure the render target was created using this device.
|
||||
D3D9Surface* rt = static_cast<D3D9Surface*>(pRenderTarget);
|
||||
if (unlikely(rt != nullptr && rt->GetDevice() != this))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
return SetRenderTargetInternal(RenderTargetIndex, pRenderTarget);
|
||||
|
@ -1749,7 +1760,7 @@ namespace dxvk {
|
|||
|
||||
m_state.depthStencil = ds;
|
||||
|
||||
UpdateActiveHazardsDS(UINT32_MAX);
|
||||
UpdateActiveHazardsDS(std::numeric_limits<uint32_t>::max());
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
@ -1829,6 +1840,12 @@ namespace dxvk {
|
|||
|
||||
D3D9DeviceLock lock = LockDevice();
|
||||
|
||||
// D3DCLEAR_ZBUFFER and D3DCLEAR_STENCIL are invalid flags
|
||||
// if there is no currently bound DS (which can be the autoDS)
|
||||
if (unlikely(m_state.depthStencil == nullptr
|
||||
&& (Flags & (D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL))))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
const auto& vp = m_state.viewport;
|
||||
const auto& sc = m_state.scissorRect;
|
||||
|
||||
|
@ -2015,10 +2032,6 @@ namespace dxvk {
|
|||
|
||||
const uint32_t idx = GetTransformIndex(TransformState);
|
||||
|
||||
// D3D8 state blocks ignore capturing calls to MultiplyTransform().
|
||||
if (unlikely(!m_isD3D8Compatible && ShouldRecord()))
|
||||
return m_recorder->MultiplyStateTransform(idx, pMatrix);
|
||||
|
||||
m_state.transforms[idx] = m_state.transforms[idx] * ConvertMatrix(pMatrix);
|
||||
|
||||
m_flags.set(D3D9DeviceFlag::DirtyFFVertexData);
|
||||
|
@ -2144,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)
|
||||
|
@ -2181,9 +2194,13 @@ namespace dxvk {
|
|||
HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetClipPlane(DWORD Index, const float* pPlane) {
|
||||
D3D9DeviceLock lock = LockDevice();
|
||||
|
||||
if (unlikely(Index >= caps::MaxClipPlanes || !pPlane))
|
||||
if (unlikely(!pPlane))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// Higher indexes will be capped to the last valid index
|
||||
if (unlikely(Index >= caps::MaxClipPlanes))
|
||||
Index = caps::MaxClipPlanes - 1;
|
||||
|
||||
if (unlikely(ShouldRecord()))
|
||||
return m_recorder->SetClipPlane(Index, pPlane);
|
||||
|
||||
|
@ -2207,9 +2224,13 @@ namespace dxvk {
|
|||
HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetClipPlane(DWORD Index, float* pPlane) {
|
||||
D3D9DeviceLock lock = LockDevice();
|
||||
|
||||
if (unlikely(Index >= caps::MaxClipPlanes || !pPlane))
|
||||
if (unlikely(!pPlane))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// Higher indexes will be capped to the last valid index
|
||||
if (unlikely(Index >= caps::MaxClipPlanes))
|
||||
Index = caps::MaxClipPlanes - 1;
|
||||
|
||||
for (uint32_t i = 0; i < 4; i++)
|
||||
pPlane[i] = m_state.clipPlanes[Index].coeff[i];
|
||||
|
||||
|
@ -2363,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:
|
||||
|
@ -2576,6 +2597,10 @@ namespace dxvk {
|
|||
IDirect3DStateBlock9** ppSB) {
|
||||
D3D9DeviceLock lock = LockDevice();
|
||||
|
||||
// A state block can not be created while another is being recorded.
|
||||
if (unlikely(ShouldRecord()))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
InitReturnPtr(ppSB);
|
||||
|
||||
if (unlikely(ppSB == nullptr))
|
||||
|
@ -2599,7 +2624,8 @@ namespace dxvk {
|
|||
HRESULT STDMETHODCALLTYPE D3D9DeviceEx::BeginStateBlock() {
|
||||
D3D9DeviceLock lock = LockDevice();
|
||||
|
||||
if (unlikely(m_recorder != nullptr))
|
||||
// Only one state block can be recorded at a given time.
|
||||
if (unlikely(ShouldRecord()))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
m_recorder = new D3D9StateBlock(this, D3D9StateBlockType::None);
|
||||
|
@ -2611,11 +2637,12 @@ namespace dxvk {
|
|||
HRESULT STDMETHODCALLTYPE D3D9DeviceEx::EndStateBlock(IDirect3DStateBlock9** ppSB) {
|
||||
D3D9DeviceLock lock = LockDevice();
|
||||
|
||||
InitReturnPtr(ppSB);
|
||||
|
||||
if (unlikely(ppSB == nullptr || m_recorder == nullptr))
|
||||
// Recording a state block can't end if recording hasn't been started.
|
||||
if (unlikely(ppSB == nullptr || !ShouldRecord()))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
InitReturnPtr(ppSB);
|
||||
|
||||
*ppSB = m_recorder.ref();
|
||||
if (!m_isD3D8Compatible)
|
||||
m_losableResourceCounter++;
|
||||
|
@ -2863,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;
|
||||
|
@ -2912,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;
|
||||
|
@ -2954,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);
|
||||
});
|
||||
|
||||
|
@ -3018,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);
|
||||
});
|
||||
|
@ -3135,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);
|
||||
});
|
||||
|
@ -3287,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;
|
||||
|
||||
|
@ -3345,10 +3397,17 @@ namespace dxvk {
|
|||
|
||||
BindShader<DxsoProgramTypes::VertexShader>(GetCommonShader(shader));
|
||||
m_vsShaderMasks = newShader->GetShaderMask();
|
||||
|
||||
UpdateTextureTypeMismatchesForShader(newShader, m_vsShaderMasks.samplerMask, FirstVSSamplerSlot);
|
||||
}
|
||||
else
|
||||
else {
|
||||
m_vsShaderMasks = D3D9ShaderMasks();
|
||||
|
||||
// Fixed function vertex shaders don't support sampling textures.
|
||||
m_dirtyTextures |= m_vsShaderMasks.samplerMask & m_mismatchingTextureTypes;
|
||||
m_mismatchingTextureTypes &= ~m_vsShaderMasks.samplerMask;
|
||||
}
|
||||
|
||||
m_flags.set(D3D9DeviceFlag::DirtyInputLayout);
|
||||
|
||||
return D3D_OK;
|
||||
|
@ -3644,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;
|
||||
|
||||
|
@ -3703,6 +3773,8 @@ namespace dxvk {
|
|||
|
||||
BindShader<DxsoProgramTypes::PixelShader>(newShader);
|
||||
newShaderMasks = newShader->GetShaderMask();
|
||||
|
||||
UpdateTextureTypeMismatchesForShader(newShader, newShaderMasks.samplerMask, 0);
|
||||
}
|
||||
else {
|
||||
// TODO: What fixed function textures are in use?
|
||||
|
@ -3710,6 +3782,10 @@ namespace dxvk {
|
|||
|
||||
// The RT output is always 0 for fixed function.
|
||||
newShaderMasks = FixedFunctionMask;
|
||||
|
||||
// Fixed function always uses spec constants to decide the texture type.
|
||||
m_dirtyTextures |= newShaderMasks.samplerMask & m_mismatchingTextureTypes;
|
||||
m_mismatchingTextureTypes &= ~newShaderMasks.samplerMask;
|
||||
}
|
||||
|
||||
// If we have any RTs we would have bound to the the FB
|
||||
|
@ -3730,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;
|
||||
|
@ -3987,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,
|
||||
|
@ -4005,8 +4080,6 @@ namespace dxvk {
|
|||
pSoftwareCursor->Height = 0;
|
||||
pSoftwareCursor->XHotSpot = 0;
|
||||
pSoftwareCursor->YHotSpot = 0;
|
||||
pSoftwareCursor->X = 0;
|
||||
pSoftwareCursor->Y = 0;
|
||||
pSoftwareCursor->ResetCursor = false;
|
||||
}
|
||||
}
|
||||
|
@ -4060,11 +4133,11 @@ namespace dxvk {
|
|||
desc.IsAttachmentOnly = TRUE;
|
||||
desc.IsLockable = Lockable;
|
||||
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc)))
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_SURFACE, &desc)))
|
||||
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++;
|
||||
|
@ -4108,14 +4181,14 @@ namespace dxvk {
|
|||
// Docs: Off-screen plain surfaces are always lockable, regardless of their pool types.
|
||||
desc.IsLockable = TRUE;
|
||||
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc)))
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_SURFACE, &desc)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
if (pSharedHandle != nullptr && Pool != D3DPOOL_DEFAULT)
|
||||
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();
|
||||
|
||||
|
@ -4162,11 +4235,11 @@ namespace dxvk {
|
|||
desc.IsAttachmentOnly = TRUE;
|
||||
desc.IsLockable = IsLockableDepthStencilFormat(desc.Format);
|
||||
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc)))
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_SURFACE, &desc)))
|
||||
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++;
|
||||
|
@ -4230,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++;
|
||||
}
|
||||
|
@ -4314,12 +4387,12 @@ namespace dxvk {
|
|||
const uint32_t textureType = newTexture != nullptr
|
||||
? uint32_t(newTexture->GetType() - D3DRTYPE_TEXTURE)
|
||||
: 0;
|
||||
// There are 4 texture types, so we need 2 bits.
|
||||
// There are 3 texture types, so we need 2 bits.
|
||||
const uint32_t offset = StateSampler * 2;
|
||||
const uint32_t textureBitMask = 0b11u << offset;
|
||||
const uint32_t textureBits = textureType << offset;
|
||||
|
||||
// In fixed function shaders and SM < 3 we put the type mask
|
||||
// In fixed function shaders and SM < 2 we put the type mask
|
||||
// into a spec constant to select the used sampler type.
|
||||
m_textureTypes &= ~textureBitMask;
|
||||
m_textureTypes |= textureBits;
|
||||
|
@ -4536,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);
|
||||
|
@ -4616,21 +4690,18 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
inline bool D3D9DeviceEx::ShouldRecord() {
|
||||
return m_recorder != nullptr && !m_recorder->IsApplying();
|
||||
}
|
||||
|
||||
|
||||
D3D9_VK_FORMAT_MAPPING D3D9DeviceEx::LookupFormat(
|
||||
D3D9Format Format) const {
|
||||
return m_adapter->GetFormatMapping(Format);
|
||||
}
|
||||
|
||||
|
||||
const DxvkFormatInfo* D3D9DeviceEx::UnsupportedFormatInfo(
|
||||
D3D9Format Format) const {
|
||||
return m_adapter->GetUnsupportedFormatInfo(Format);
|
||||
}
|
||||
|
||||
|
||||
bool D3D9DeviceEx::WaitForResource(
|
||||
const DxvkPagedResource& Resource,
|
||||
uint64_t SequenceNumber,
|
||||
|
@ -4975,9 +5046,13 @@ namespace dxvk {
|
|||
|
||||
UINT Subresource = pResource->CalcSubresource(Face, MipLevel);
|
||||
|
||||
// We weren't locked anyway!
|
||||
if (unlikely(!pResource->GetLocked(Subresource)))
|
||||
return D3D_OK;
|
||||
// Don't allow multiple unlockings, except for D3DRTYPE_TEXTURE
|
||||
if (unlikely(!pResource->GetLocked(Subresource))) {
|
||||
if (pResource->GetType() == D3DRTYPE_TEXTURE)
|
||||
return D3D_OK;
|
||||
else
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
MapTexture(pResource, Subresource); // Add it to the list of mapped resources
|
||||
pResource->SetLocked(Subresource, false);
|
||||
|
@ -5607,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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -5761,7 +5836,7 @@ namespace dxvk {
|
|||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
getSpecConstantBufferSlot(),
|
||||
sizeof(D3D9SpecializationInfo));
|
||||
D3D9SpecializationInfo::UBOSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5911,11 +5986,22 @@ namespace dxvk {
|
|||
auto mapPtr = m_vsClipPlanes.AllocSlice();
|
||||
auto dst = reinterpret_cast<D3D9ClipPlane*>(mapPtr);
|
||||
|
||||
uint32_t clipPlaneCount = 0u;
|
||||
for (uint32_t i = 0; i < caps::MaxClipPlanes; i++) {
|
||||
dst[i] = (m_state.renderStates[D3DRS_CLIPPLANEENABLE] & (1 << i))
|
||||
D3D9ClipPlane clipPlane = (m_state.renderStates[D3DRS_CLIPPLANEENABLE] & (1 << i))
|
||||
? m_state.clipPlanes[i]
|
||||
: D3D9ClipPlane();
|
||||
|
||||
if (clipPlane != D3D9ClipPlane())
|
||||
dst[clipPlaneCount++] = clipPlane;
|
||||
}
|
||||
|
||||
// Write the rest to 0 for GPL.
|
||||
for (uint32_t i = clipPlaneCount; i < caps::MaxClipPlanes; i++)
|
||||
dst[i] = D3D9ClipPlane();
|
||||
|
||||
if (m_specInfo.set<SpecClipPlaneCount>(clipPlaneCount))
|
||||
m_flags.set(D3D9DeviceFlag::DirtySpecializationEntries);
|
||||
}
|
||||
|
||||
|
||||
|
@ -6016,7 +6102,7 @@ namespace dxvk {
|
|||
] (DxvkContext* ctx) {
|
||||
ctx->signal(cSubmissionFence, cSubmissionId);
|
||||
ctx->signal(cStagingBufferFence, cStagingBufferAllocated);
|
||||
ctx->flushCommandList(cSubmissionStatus);
|
||||
ctx->flushCommandList(nullptr, cSubmissionStatus);
|
||||
});
|
||||
|
||||
FlushCsChunk();
|
||||
|
@ -6045,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);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -6084,11 +6188,12 @@ namespace dxvk {
|
|||
inline void D3D9DeviceEx::UpdateTextureBitmasks(uint32_t index, DWORD combinedUsage) {
|
||||
const uint32_t bit = 1 << index;
|
||||
|
||||
m_activeTextureRTs &= ~bit;
|
||||
m_activeTextureDSs &= ~bit;
|
||||
m_activeTextures &= ~bit;
|
||||
m_activeTexturesToUpload &= ~bit;
|
||||
m_activeTexturesToGen &= ~bit;
|
||||
m_activeTextureRTs &= ~bit;
|
||||
m_activeTextureDSs &= ~bit;
|
||||
m_activeTextures &= ~bit;
|
||||
m_activeTexturesToUpload &= ~bit;
|
||||
m_activeTexturesToGen &= ~bit;
|
||||
m_mismatchingTextureTypes &= ~bit;
|
||||
|
||||
auto tex = GetCommonTexture(m_state.textures[index]);
|
||||
if (tex != nullptr) {
|
||||
|
@ -6129,6 +6234,8 @@ namespace dxvk {
|
|||
|
||||
if (unlikely(m_fetch4Enabled & bit))
|
||||
UpdateActiveFetch4(index);
|
||||
|
||||
UpdateTextureTypeMismatchesForTexture(index);
|
||||
} else {
|
||||
if (unlikely(m_fetch4 & bit))
|
||||
UpdateActiveFetch4(index);
|
||||
|
@ -6283,6 +6390,72 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
void D3D9DeviceEx::UpdateTextureTypeMismatchesForShader(const D3D9CommonShader* shader, uint32_t shaderSamplerMask, uint32_t shaderSamplerOffset) {
|
||||
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 |= stageCorrectedShaderSamplerMask & m_mismatchingTextureTypes;
|
||||
m_mismatchingTextureTypes &= ~stageCorrectedShaderSamplerMask;
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
m_dirtyTextures |= m_mismatchingTextureTypes & (1 << i);
|
||||
m_mismatchingTextureTypes &= ~(1 << i);
|
||||
continue;
|
||||
}
|
||||
|
||||
VkImageViewType boundViewType = D3D9CommonTexture::GetImageViewTypeFromResourceType(texture->GetType(), D3D9CommonTexture::AllLayers);
|
||||
VkImageViewType shaderViewType = shader->GetImageViewType(i - shaderSamplerOffset);
|
||||
if (unlikely(boundViewType != shaderViewType)) {
|
||||
m_dirtyTextures |= 1 << i;
|
||||
m_mismatchingTextureTypes |= 1 << i;
|
||||
} else {
|
||||
// The texture type is no longer mismatching, make sure we bind the texture now.
|
||||
m_dirtyTextures |= m_mismatchingTextureTypes & (1 << i);
|
||||
m_mismatchingTextureTypes &= ~(1 << i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void D3D9DeviceEx::UpdateTextureTypeMismatchesForTexture(uint32_t stateSampler) {
|
||||
uint32_t shaderTextureIndex;
|
||||
const D3D9CommonShader* shader;
|
||||
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 {
|
||||
// Do not type check the fixed function displacement map texture.
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const D3D9CommonTexture* tex = GetCommonTexture(m_state.textures[stateSampler]);
|
||||
VkImageViewType boundViewType = D3D9CommonTexture::GetImageViewTypeFromResourceType(tex->GetType(), D3D9CommonTexture::AllLayers);
|
||||
VkImageViewType shaderViewType = shader->GetImageViewType(shaderTextureIndex);
|
||||
// D3D9 does not have 1D textures. The value of VIEW_TYPE_1D is 0
|
||||
// which is the default when there is no declaration for the type.
|
||||
bool shaderUsesTexture = shaderViewType != VkImageViewType(0);
|
||||
if (unlikely(boundViewType != shaderViewType && shaderUsesTexture)) {
|
||||
const uint32_t samplerBit = 1u << stateSampler;
|
||||
m_mismatchingTextureTypes |= samplerBit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void D3D9DeviceEx::GenerateTextureMips(uint32_t mask) {
|
||||
for (uint32_t texIdx : bit::BitMask(mask)) {
|
||||
// Guaranteed to not be nullptr...
|
||||
|
@ -6571,12 +6744,7 @@ namespace dxvk {
|
|||
const D3DVIEWPORT9& vp = m_state.viewport;
|
||||
|
||||
// Correctness Factor for 1/2 texel offset
|
||||
// We need to bias this slightly to make
|
||||
// imprecision in games happy.
|
||||
// Originally we did this only for powers of two
|
||||
// resolutions but since NEAREST filtering fixed to
|
||||
// truncate, we need to do this all the time now.
|
||||
constexpr float cf = 0.5f - (1.0f / 128.0f);
|
||||
constexpr float cf = 0.5f;
|
||||
|
||||
// How much to bias MinZ by to avoid a depth
|
||||
// degenerate viewport.
|
||||
|
@ -7022,8 +7190,8 @@ namespace dxvk {
|
|||
|
||||
|
||||
void D3D9DeviceEx::UndirtyTextures(uint32_t usedMask) {
|
||||
const uint32_t activeMask = usedMask & m_activeTextures;
|
||||
const uint32_t inactiveMask = usedMask & ~m_activeTextures;
|
||||
const uint32_t activeMask = usedMask & (m_activeTextures & ~m_mismatchingTextureTypes);
|
||||
const uint32_t inactiveMask = usedMask & (~m_activeTextures | m_mismatchingTextureTypes);
|
||||
|
||||
for (uint32_t i : bit::BitMask(activeMask))
|
||||
BindTexture(i);
|
||||
|
@ -7539,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;
|
||||
|
||||
|
@ -7657,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++;
|
||||
}
|
||||
}
|
||||
|
@ -7754,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)]);
|
||||
|
@ -8309,12 +8481,12 @@ namespace dxvk {
|
|||
" - Windowed: ", pPresentationParameters->Windowed ? "true" : "false", "\n",
|
||||
" - Swap effect: ", pPresentationParameters->SwapEffect, "\n"));
|
||||
|
||||
// Black Desert creates a device with a NULL hDeviceWindow and
|
||||
// seemingly expects this validation to not prevent a swapchain reset.
|
||||
if (pPresentationParameters->hDeviceWindow != nullptr &&
|
||||
!pPresentationParameters->Windowed &&
|
||||
(pPresentationParameters->BackBufferWidth == 0
|
||||
|| pPresentationParameters->BackBufferHeight == 0)) {
|
||||
// Black Desert creates a D3DDEVTYPE_NULLREF device and
|
||||
// expects this validation to not prevent a swapchain reset.
|
||||
if (likely(m_deviceType != D3DDEVTYPE_NULLREF) &&
|
||||
unlikely(!pPresentationParameters->Windowed &&
|
||||
(pPresentationParameters->BackBufferWidth == 0
|
||||
|| pPresentationParameters->BackBufferHeight == 0))) {
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
|
@ -8332,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();
|
||||
}
|
||||
|
||||
|
@ -8353,10 +8525,10 @@ namespace dxvk {
|
|||
desc.IsAttachmentOnly = TRUE;
|
||||
desc.IsLockable = IsLockableDepthStencilFormat(desc.Format);
|
||||
|
||||
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc)))
|
||||
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++;
|
||||
|
@ -8572,11 +8744,20 @@ namespace dxvk {
|
|||
if (m_usingGraphicsPipelines) {
|
||||
// TODO: Make uploading specialization information less naive.
|
||||
auto mapPtr = m_specBuffer.AllocSlice();
|
||||
auto dst = reinterpret_cast<D3D9SpecializationInfo*>(mapPtr);
|
||||
*dst = m_specInfo;
|
||||
memcpy(mapPtr, m_specInfo.data.data(), D3D9SpecializationInfo::UBOSize);
|
||||
}
|
||||
|
||||
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,6 +844,42 @@ 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);
|
||||
|
||||
void UploadManagedTextures(uint32_t mask);
|
||||
|
@ -1020,6 +1083,13 @@ namespace dxvk {
|
|||
*/
|
||||
void RemoveMappedTexture(D3D9CommonTexture* pTexture);
|
||||
|
||||
/**
|
||||
* \brief Returns whether the device is currently recording a StateBlock
|
||||
*/
|
||||
bool ShouldRecord() const {
|
||||
return m_recorder != nullptr;
|
||||
}
|
||||
|
||||
bool IsD3D8Compatible() const {
|
||||
return m_isD3D8Compatible;
|
||||
}
|
||||
|
@ -1132,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>
|
||||
|
@ -1160,11 +1230,6 @@ namespace dxvk {
|
|||
*/
|
||||
void WaitStagingBuffer();
|
||||
|
||||
/**
|
||||
* \brief Returns whether the device is currently recording a StateBlock
|
||||
*/
|
||||
inline bool ShouldRecord();
|
||||
|
||||
HRESULT CreateShaderModule(
|
||||
D3D9CommonShader* pShaderModule,
|
||||
uint32_t* pLength,
|
||||
|
@ -1390,6 +1455,8 @@ namespace dxvk {
|
|||
&& !m_state.renderTargets[Index]->IsNull();
|
||||
}
|
||||
|
||||
GpuFlushType GetMaxFlushType() const;
|
||||
|
||||
Com<D3D9InterfaceEx> m_parent;
|
||||
D3DDEVTYPE m_deviceType;
|
||||
HWND m_window;
|
||||
|
@ -1459,6 +1526,7 @@ namespace dxvk {
|
|||
uint32_t m_drefClamp = 0;
|
||||
uint32_t m_cubeTextures = 0;
|
||||
uint32_t m_textureTypes = 0;
|
||||
uint32_t m_mismatchingTextureTypes = 0;
|
||||
uint32_t m_projectionBitfield = 0;
|
||||
|
||||
uint32_t m_dirtySamplerStates = 0;
|
||||
|
@ -1506,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);
|
||||
|
@ -2366,6 +2366,7 @@ namespace dxvk {
|
|||
|
||||
uint32_t floatType = m_module.defFloatType(32);
|
||||
uint32_t vec4Type = m_module.defVectorType(floatType, 4);
|
||||
uint32_t boolType = m_module.defBoolType();
|
||||
|
||||
// Declare uniform buffer containing clip planes
|
||||
uint32_t clipPlaneArray = m_module.defArrayTypeUnique(vec4Type, clipPlaneCountId);
|
||||
|
@ -2406,6 +2407,9 @@ namespace dxvk {
|
|||
|
||||
m_module.decorateBuiltIn(clipDistArray, spv::BuiltInClipDistance);
|
||||
|
||||
// Always consider clip planes enabled when doing GPL by forcing 6 for the quick value.
|
||||
uint32_t clipPlaneCount = m_spec.get(m_module, m_specUbo, SpecClipPlaneCount, 0, 32, m_module.constu32(caps::MaxClipPlanes));
|
||||
|
||||
// Compute clip distances
|
||||
for (uint32_t i = 0; i < caps::MaxClipPlanes; i++) {
|
||||
std::array<uint32_t, 2> blockMembers = {{
|
||||
|
@ -2419,12 +2423,14 @@ namespace dxvk {
|
|||
clipPlaneBlock, blockMembers.size(), blockMembers.data()));
|
||||
|
||||
uint32_t distId = m_module.opDot(floatType, worldPos, planeId);
|
||||
|
||||
uint32_t clipPlaneEnabled = m_module.opULessThan(boolType, m_module.constu32(i), clipPlaneCount);
|
||||
|
||||
uint32_t value = m_module.opSelect(floatType, clipPlaneEnabled, distId, m_module.constf32(0.0f));
|
||||
|
||||
m_module.opStore(
|
||||
m_module.opAccessChain(
|
||||
m_module.defPointerType(floatType, spv::StorageClassOutput),
|
||||
clipDistArray, 1, &blockMembers[1]),
|
||||
distId);
|
||||
m_module.opStore(m_module.opAccessChain(
|
||||
m_module.defPointerType(floatType, spv::StorageClassOutput),
|
||||
clipDistArray, 1, &blockMembers[1]), value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -255,4 +255,16 @@ namespace dxvk {
|
|||
|| format == D3D9Format::DXT5;
|
||||
}
|
||||
|
||||
// D3D9 documentation says: IDirect3DSurface9::GetDC is valid on the following formats only:
|
||||
// D3DFMT_R5G6B5, D3DFMT_X1R5G5B5, D3DFMT_R8G8B8, and D3DFMT_X8R8G8B8. However,
|
||||
// the equivalent formats of D3DFMT_A1R5G5B5 and D3DFMT_A8R8G8B8 are also supported.
|
||||
inline bool IsSurfaceGetDCCompatibleFormat(D3D9Format format) {
|
||||
return format == D3D9Format::R5G6B5
|
||||
|| format == D3D9Format::X1R5G5B5
|
||||
|| format == D3D9Format::A1R5G5B5
|
||||
|| format == D3D9Format::R8G8B8
|
||||
|| format == D3D9Format::X8R8G8B8
|
||||
|| format == D3D9Format::A8R8G8B8;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
||||
|
@ -93,7 +96,8 @@ namespace dxvk {
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
if (riid == __uuidof(ID3D9VkInteropInterface)) {
|
||||
if (riid == __uuidof(ID3D9VkInteropInterface)
|
||||
|| riid == __uuidof(ID3D9VkInteropInterface1)) {
|
||||
*ppvObject = ref(&m_d3d9Interop);
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -360,10 +364,15 @@ namespace dxvk {
|
|||
!(BehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING)))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
HRESULT hr = ValidatePresentationParameters(pPresentationParameters);
|
||||
HRESULT hr;
|
||||
// Black Desert creates a D3DDEVTYPE_NULLREF device and
|
||||
// expects it be created despite passing invalid parameters.
|
||||
if (likely(DeviceType != D3DDEVTYPE_NULLREF)) {
|
||||
hr = ValidatePresentationParameters(pPresentationParameters);
|
||||
|
||||
if (unlikely(FAILED(hr)))
|
||||
return hr;
|
||||
if (unlikely(FAILED(hr)))
|
||||
return hr;
|
||||
}
|
||||
|
||||
auto* adapter = GetAdapter(Adapter);
|
||||
|
||||
|
@ -413,18 +422,23 @@ namespace dxvk {
|
|||
// can not be higher than D3DSWAPEFFECT_FLIPEX.
|
||||
if (unlikely(pPresentationParameters->SwapEffect > D3DSWAPEFFECT_FLIPEX))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// 30 is the highest supported back buffer count for Ex devices.
|
||||
if (unlikely(pPresentationParameters->BackBufferCount > D3DPRESENT_BACK_BUFFERS_MAX_EX))
|
||||
return D3DERR_INVALIDCALL;
|
||||
} else {
|
||||
// The swap effect value on a non-Ex D3D9 device
|
||||
// can not be higher than D3DSWAPEFFECT_COPY.
|
||||
if (unlikely(pPresentationParameters->SwapEffect > D3DSWAPEFFECT_COPY))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// 3 is the highest supported back buffer count for non-Ex devices.
|
||||
if (unlikely(pPresentationParameters->BackBufferCount > D3DPRESENT_BACK_BUFFERS_MAX))
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
// The swap effect value can not be 0.
|
||||
// Black Desert sets this to 0 with a NULL hDeviceWindow
|
||||
// and expects device creation to succeed.
|
||||
if (unlikely(pPresentationParameters->hDeviceWindow != nullptr
|
||||
&& !pPresentationParameters->SwapEffect))
|
||||
if (unlikely(!pPresentationParameters->SwapEffect))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// D3DSWAPEFFECT_COPY can not be used with more than one back buffer.
|
||||
|
@ -436,10 +450,6 @@ namespace dxvk {
|
|||
&& pPresentationParameters->BackBufferCount > 1))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// 3 is the highest supported back buffer count.
|
||||
if (unlikely(pPresentationParameters->BackBufferCount > 3))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
// Valid fullscreen presentation intervals must be known values.
|
||||
if (unlikely(!pPresentationParameters->Windowed
|
||||
&& !(pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_DEFAULT
|
||||
|
@ -450,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,26 @@ ID3D9VkInteropInterface : public IUnknown {
|
|||
VkPhysicalDevice* pPhysicalDevice) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief D3D9 interface for Vulkan interop - extended
|
||||
*
|
||||
* Provides access to the instance extension lists
|
||||
* and everything provided by ID3D9VkInteropInterface
|
||||
*/
|
||||
MIDL_INTERFACE("d6589ed4-7a37-4096-bac2-223b25ae31d2")
|
||||
ID3D9VkInteropInterface1 : public ID3D9VkInteropInterface {
|
||||
/**
|
||||
* \brief Gets a list of enabled instance extensions
|
||||
*
|
||||
* \param [out] pExtensionCount Number of extensions
|
||||
* \param [out] ppExtensions List of extension names
|
||||
* \returns D3DERR_MOREDATA if the list was truncated
|
||||
*/
|
||||
virtual HRESULT STDMETHODCALLTYPE GetInstanceExtensions(
|
||||
UINT* pExtensionCount,
|
||||
const char** ppExtensions) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief D3D9 texture interface for Vulkan interop
|
||||
*
|
||||
|
@ -263,6 +283,7 @@ ID3D9VkExtSwapchain : public IUnknown {
|
|||
|
||||
#ifndef _MSC_VER
|
||||
__CRT_UUID_DECL(ID3D9VkInteropInterface, 0x3461a81b,0xce41,0x485b,0xb6,0xb5,0xfc,0xf0,0x8b,0xa6,0xa6,0xbd);
|
||||
__CRT_UUID_DECL(ID3D9VkInteropInterface1, 0xd6589ed4,0x7a37,0x4096,0xba,0xc2,0x22,0x3b,0x25,0xae,0x31,0xd2);
|
||||
__CRT_UUID_DECL(ID3D9VkInteropTexture, 0xd56344f5,0x8d35,0x46fd,0x80,0x6d,0x94,0xc3,0x51,0xb4,0x72,0xc1);
|
||||
__CRT_UUID_DECL(ID3D9VkInteropDevice, 0x2eaa4b89,0x0107,0x4bdb,0x87,0xf7,0x0f,0x54,0x1c,0x49,0x3c,0xe0);
|
||||
__CRT_UUID_DECL(ID3D9VkExtSwapchain, 0x13776e93,0x4aa9,0x430a,0xa4,0xec,0xfe,0x9e,0x28,0x11,0x81,0xd5);
|
||||
|
|
|
@ -51,6 +51,31 @@ namespace dxvk {
|
|||
}
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D9VkInteropInterface::GetInstanceExtensions(
|
||||
UINT* pExtensionCount,
|
||||
const char** ppExtensions) {
|
||||
if (pExtensionCount == nullptr)
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
const DxvkNameList& extensions = m_interface->GetInstance()->extensionNameList();
|
||||
|
||||
if (ppExtensions == nullptr) {
|
||||
*pExtensionCount = extensions.count();
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
// Write
|
||||
UINT count = 0;
|
||||
UINT maxCount = *pExtensionCount;
|
||||
for (uint32_t i = 0; i < extensions.count() && i < maxCount; i++) {
|
||||
ppExtensions[i] = extensions.name(i);
|
||||
count++;
|
||||
}
|
||||
|
||||
*pExtensionCount = count;
|
||||
return (count < maxCount) ? D3DERR_MOREDATA : D3D_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Texture Interop
|
||||
///////////////////////////////
|
||||
|
@ -324,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();
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace dxvk {
|
|||
class D3D9DeviceEx;
|
||||
struct D3D9_COMMON_TEXTURE_DESC;
|
||||
|
||||
class D3D9VkInteropInterface final : public ID3D9VkInteropInterface {
|
||||
class D3D9VkInteropInterface final : public ID3D9VkInteropInterface1 {
|
||||
|
||||
public:
|
||||
|
||||
|
@ -34,6 +34,10 @@ namespace dxvk {
|
|||
UINT Adapter,
|
||||
VkPhysicalDevice* pPhysicalDevice);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetInstanceExtensions(
|
||||
UINT* pExtensionCount,
|
||||
const char** ppExtensions);
|
||||
|
||||
private:
|
||||
|
||||
D3D9InterfaceEx* m_interface;
|
||||
|
|
|
@ -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);
|
||||
|
@ -61,7 +60,6 @@ namespace dxvk {
|
|||
this->invariantPosition = config.getOption<bool> ("d3d9.invariantPosition", true);
|
||||
this->memoryTrackTest = config.getOption<bool> ("d3d9.memoryTrackTest", false);
|
||||
this->supportVCache = config.getOption<bool> ("d3d9.supportVCache", vendorId == uint32_t(DxvkGpuVendor::Nvidia));
|
||||
this->enableDialogMode = config.getOption<bool> ("d3d9.enableDialogMode", false);
|
||||
this->forceSamplerTypeSpecConstants = config.getOption<bool> ("d3d9.forceSamplerTypeSpecConstants", false);
|
||||
this->forceSwapchainMSAA = config.getOption<int32_t> ("d3d9.forceSwapchainMSAA", -1);
|
||||
this->forceSampleRateShading = config.getOption<bool> ("d3d9.forceSampleRateShading", false);
|
||||
|
@ -81,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;
|
||||
|
||||
|
@ -103,9 +99,6 @@ namespace dxvk {
|
|||
/// Forced aspect ratio, disable other modes
|
||||
std::string forceAspectRatio;
|
||||
|
||||
/// Enable dialog mode (ie. no exclusive fullscreen)
|
||||
bool enableDialogMode;
|
||||
|
||||
/// Always use a spec constant to determine sampler type (instead of just in PS 1.x)
|
||||
/// Works around a game bug in Halo CE where it gives cube textures to 2d/volume samplers
|
||||
bool forceSamplerTypeSpecConstants;
|
||||
|
|
|
@ -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,9 +74,16 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE SetPriority(DWORD PriorityNew) {
|
||||
DWORD oldPriority = m_priority;
|
||||
m_priority = PriorityNew;
|
||||
return oldPriority;
|
||||
// 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() {
|
||||
|
@ -84,11 +93,13 @@ namespace dxvk {
|
|||
|
||||
protected:
|
||||
|
||||
DWORD m_priority;
|
||||
const D3DPOOL m_pool;
|
||||
DWORD m_priority;
|
||||
|
||||
private:
|
||||
|
||||
ComPrivateData m_privateData;
|
||||
const bool m_isExtended;
|
||||
ComPrivateData m_privateData;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -57,12 +57,13 @@ namespace dxvk {
|
|||
m_shader = pModule->compile(*pDxsoModuleInfo, name, AnalysisInfo, constantLayout);
|
||||
m_isgn = pModule->isgn();
|
||||
m_usedSamplers = pModule->usedSamplers();
|
||||
m_textureTypes = pModule->textureTypes();
|
||||
|
||||
// Shift up these sampler bits so we can just
|
||||
// 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();
|
||||
|
||||
|
@ -97,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");
|
||||
|
||||
|
|
|
@ -54,11 +54,18 @@ namespace dxvk {
|
|||
|
||||
uint32_t GetMaxDefinedConstant() const { return m_maxDefinedConst; }
|
||||
|
||||
VkImageViewType GetImageViewType(uint32_t samplerSlot) const {
|
||||
const uint32_t offset = samplerSlot * 2;
|
||||
const uint32_t mask = 0b11;
|
||||
return static_cast<VkImageViewType>((m_textureTypes >> offset) & mask);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
DxsoIsgn m_isgn;
|
||||
uint32_t m_usedSamplers;
|
||||
uint32_t m_usedRTs;
|
||||
uint32_t m_textureTypes;
|
||||
|
||||
DxsoProgramInfo m_info;
|
||||
DxsoShaderMetaInfo m_meta;
|
||||
|
|
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -2,22 +2,56 @@
|
|||
|
||||
#include "d3d9_include.h"
|
||||
|
||||
#include "../dxso/dxso_header.h"
|
||||
#include "../dxso/dxso_decoder.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
enum class D3D9ShaderValidatorMessage : uint32_t {
|
||||
BeginOutOfOrder = 0xeb,
|
||||
InstructionOutOfOrder = 0xec,
|
||||
InstructionEndOfShader = 0xed,
|
||||
InstructionNullArgs = 0xee,
|
||||
BadVersionTokenLength = 0xef,
|
||||
BadVersionTokenType = 0xf0,
|
||||
BadEndToken = 0xf1,
|
||||
EndOutOfOrder = 0xf2,
|
||||
MissingEndToken = 0xf3,
|
||||
BadInputRegisterDeclaration = 0x12c,
|
||||
BadInputRegister = 0x167,
|
||||
BadInstructionLength = 0x21e,
|
||||
};
|
||||
|
||||
enum class D3D9ShaderValidatorState {
|
||||
Begin,
|
||||
ValidatingHeader,
|
||||
ValidatingInstructions,
|
||||
EndOfShader,
|
||||
Error,
|
||||
};
|
||||
|
||||
using D3D9ShaderValidatorCallback = HRESULT(STDMETHODCALLTYPE *)(
|
||||
const char* pFile,
|
||||
UINT Line,
|
||||
DWORD Unknown,
|
||||
D3D9ShaderValidatorMessage MessageID,
|
||||
const char* pMessage,
|
||||
void* pUserData);
|
||||
|
||||
class IDirect3DShaderValidator9 : public IUnknown {
|
||||
|
||||
public:
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE Begin(
|
||||
void* pCallback,
|
||||
void* pUserParam,
|
||||
DWORD Unknown) = 0;
|
||||
D3D9ShaderValidatorCallback pCallback,
|
||||
void* pUserParam,
|
||||
DWORD Unknown) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE Instruction(
|
||||
const char* pUnknown1,
|
||||
UINT Unknown2,
|
||||
const DWORD* pInstruction,
|
||||
DWORD InstructionLength) = 0;
|
||||
const char* pFile,
|
||||
UINT Line,
|
||||
const DWORD* pdwInst,
|
||||
DWORD cdw) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE End() = 0;
|
||||
|
||||
|
@ -27,42 +61,45 @@ 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(
|
||||
void* pCallback,
|
||||
void* pUserParam,
|
||||
DWORD Unknown) {
|
||||
Logger::debug("D3D9ShaderValidator::Begin: Stub");
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
D3D9ShaderValidatorCallback pCallback,
|
||||
void* pUserData,
|
||||
DWORD Unknown);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Instruction(
|
||||
const char* pUnknown1,
|
||||
UINT Unknown2,
|
||||
const DWORD* pInstruction,
|
||||
DWORD InstructionLength) {
|
||||
Logger::debug("D3D9ShaderValidator::Instruction: Stub");
|
||||
const char* pFile,
|
||||
UINT Line,
|
||||
const DWORD* pdwInst,
|
||||
DWORD cdw);
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE End();
|
||||
|
||||
private:
|
||||
|
||||
HRESULT STDMETHODCALLTYPE End() {
|
||||
Logger::debug("D3D9ShaderValidator::End: Stub");
|
||||
HRESULT ValidateHeader(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw);
|
||||
|
||||
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,
|
||||
std::string Message);
|
||||
|
||||
bool m_isPixelShader = false;
|
||||
uint32_t m_majorVersion = 0;
|
||||
uint32_t m_minorVersion = 0;
|
||||
|
||||
D3D9ShaderValidatorState m_state = D3D9ShaderValidatorState::Begin;
|
||||
D3D9ShaderValidatorCallback m_callback = nullptr;
|
||||
void* m_userData = nullptr;
|
||||
|
||||
std::unique_ptr<DxsoDecodeContext> m_ctx;
|
||||
};
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue