Compare commits

...

357 commits
v2.5 ... master

Author SHA1 Message Date
Philip Rebohle
a08579e555 [dxvk] Fix clear after late resolve 2025-03-06 09:54:34 +01:00
Philip Rebohle
45ec01a0a1 [d3d11] Add TRANSFER_SRC usage to icb
Needed for defrag.
2025-03-06 09:54:34 +01:00
Philip Rebohle
542e0d2ab0 [dxvk,d3d9,d3d11] Decouple ID3DUserDefinedAnnotation from internal markers
Some games nope out when we expose debug markers, so add a debug mode
that doesn't while still providing internal markers and debug names.
2025-03-06 09:54:34 +01:00
Philip Rebohle
91b48dd31d [d3d11] Make user defined annotation more robust 2025-03-06 09:54:34 +01:00
GranMinigun
d8eb4d0d66 [util] Spoof vendor ID for CivCity: Rome 2025-03-06 01:25:30 +01:00
Philip Rebohle
f161d9bc7b [dxvk] Fix synchronization on swapchain tear-down 2025-03-05 23:16:28 +01:00
Philip Rebohle
634f38b38d [dxvk] Enable sparse binding features if supported 2025-03-05 23:15:47 +01:00
Philip Rebohle
ff8378be19 [dxvk] Fix uploadImage barriers 2025-03-05 23:15:47 +01:00
Philip Rebohle
ad75fb17cd [dxvk] Fix uploadBuffer barriers 2025-03-05 23:15:47 +01:00
Philip Rebohle
76244812fb [dxvk] Move compressed image initialization to transfer queue
This is relatively common.
2025-03-05 23:15:47 +01:00
Philip Rebohle
9c0dec9f58 [dxvk] Move buffer initialization to transfer queue 2025-03-05 23:15:47 +01:00
Philip Rebohle
a4a5bf5d63 [dxvk] Add helpers for transfer queue release barriers 2025-03-05 23:15:47 +01:00
Philip Rebohle
49f0968f57 [dxvk] Use sparse residency for zero buffer
Saves a small amount of memory since reading from an unbound
buffer will return zero anyway.
2025-03-05 23:15:47 +01:00
Philip Rebohle
ec0deb73da [dxvk] Fix missing zero buffer tracking
Scary.
2025-03-05 23:15:47 +01:00
WinterSnowfall
fed51e6c92 [dxso] Fix uninitialized m_maxDefinedConstant variable 2025-03-05 15:02:11 +01:00
Philip Rebohle
196258111c [dxvk] Be more conservative with CS data alignment
Fixes #4734. Apparently, weak alignment hints are an error rather
than getting ignored.
2025-03-05 13:12:23 +01:00
Philip Rebohle
d04fe1cdc0 [meta] Add some missing documentation to dxvk.conf 2025-03-04 21:49:39 +01:00
Philip Rebohle
3cf453160c [d3d11] Ensure required image usage flags for video blits
Fixes some validation errors.
2025-03-04 21:05:59 +01:00
Philip Rebohle
9769df9dd8 [d3d11] Implement ID3D11On12Device1 2025-03-02 19:21:39 +01:00
Philip Rebohle
106032fa65 [dxvk] Use granular image access tracking in copyImageHw 2025-03-02 14:01:09 +01:00
Philip Rebohle
d2b53b76de [dxvk] Use granular image access tracking in copyBufferToImageHw 2025-03-02 14:01:09 +01:00
Philip Rebohle
02dc403074 [dxvk] Use granular image access tracking in copyImageToBufferCs 2025-03-02 14:01:09 +01:00
Philip Rebohle
420d083677 [dxvk] Use granular image access tracking in copyImageToBufferHw 2025-03-02 14:01:09 +01:00
Philip Rebohle
cd29f0feaa [dxvk] Add barrier helpers for granular image access tracking 2025-03-02 14:01:09 +01:00
Philip Rebohle
baaf72a373 [dxvk] Add helpers to compute more granular image address ranges 2025-03-02 14:01:09 +01:00
Philip Rebohle
1f0ad760e1 [dxvk] Refactor address range for barrier tracking
Allows us to bump the size and offset to 64 bits without increasing the
total size of the node.
2025-03-02 14:01:09 +01:00
Philip Rebohle
fe58b393d4 [util] Add helper for 2D and 3D morton codes 2025-03-02 14:01:09 +01:00
Philip Rebohle
c21f4e0190 [util] Add 48-bit integer type 2025-03-02 14:01:09 +01:00
Philip Rebohle
416f9c5a4a [d3d11] Embed UpdateBuffer data in CS chunk
Tiny optimization that gets rid of a copy and also lets us use chunk
memory more efficiently.
2025-03-02 13:29:48 +01:00
Philip Rebohle
5b68884fd9 [d3d11] Fix confusing debug color for GPU synchronization
Purple was already used for various other things.
2025-03-01 16:37:39 +01:00
Robin Kertels
8f84085370 [dxvk] Add CS thread load to the HUD 2025-03-01 14:23:22 +01:00
Philip Rebohle
b35e69b467 [d3d11] Skip empty draws
Watch Dogs 2 likes to do this with graphics UAVs bound, which
introduces quite a bit of unnecessary GPU synchronization.
2025-03-01 11:30:38 +01:00
Philip Rebohle
11dc0e7ce8 [util] Enable compute UAV barriers for Watch Dogs 2 2025-02-28 23:57:26 +01:00
Philip Rebohle
6b5d595b3e [d3d11] Add option to force compute shader UAV synchronization 2025-02-28 23:57:26 +01:00
Philip Rebohle
396a4e0235 [dxbc] Add option to implicitly synchronize UAV accesses 2025-02-28 23:57:26 +01:00
Philip Rebohle
c04410ca00 [dxbc] Increase maximum size of embedded icbs to 256 bytes
Gives drivers more info, while still avoiding duplication of large ICBs
when compiling the same shader into a large number of pipelines.
2025-02-28 12:07:49 +01:00
Philip Rebohle
fcbdff3b72 [d3d9] Set correct input topology for SWVP emulation
Otherwise we'll run the input patch pass, even if it doesn't do anything.
2025-02-26 20:58:47 +01:00
Philip Rebohle
0359a3521d [dxvk] Patch geometry shader if input topology does not match pipeline 2025-02-26 20:53:43 +01:00
Philip Rebohle
b41253e18d [dxvk] Add pass to patch GS input topology if necessary 2025-02-26 20:53:43 +01:00
Philip Rebohle
96a260e94e [dxbc] Write back GS input topology to shader info 2025-02-26 20:53:43 +01:00
Philip Rebohle
2cb9d40f88 [dxvk] Add input topology field to shader info 2025-02-26 20:53:43 +01:00
Philip Rebohle
2cd305e606 [dxvk] Enable uniformBufferStandardLayout feature
Core and required in Vulkan 1.2, but keep it optional anyway for now
since we're not really doing anything critical with it.
2025-02-26 20:52:25 +01:00
Philip Rebohle
ec5bd04378 [dxvk] Remove uniform data from shaders
No longer used
2025-02-26 20:52:25 +01:00
Philip Rebohle
43f360d20c [d3d11] Move immediate constant buffers to non-mapped VRAM
This has the advantage that icbs can be defragmented and don't
take up space in HVV chunks that would potentially go unused.
2025-02-26 20:52:25 +01:00
Philip Rebohle
6f59124e9a [dxbc] Retrieve icb data directly from shader module 2025-02-26 20:52:25 +01:00
Philip Rebohle
d6d13edb7a [dxbc] Use smallest aligned vector type for buffer-backed ubo 2025-02-26 20:52:25 +01:00
Philip Rebohle
31192b6d3f [dxbc] Rework embedded immediate constant buffers
Considerably reduces the size of immediate constant buffer arrays when
not all vector components are used. Also adds bound-checking.
2025-02-26 20:52:25 +01:00
WinterSnowfall
31a4679960 [dxvk] Don't skip CPU devices when a device filter is set 2025-02-25 21:39:22 +01:00
Philip Rebohle
13bd234cea [dxvk] Actually fix multidraw workaround
Otherwise we'll end up using the feature anyway.
2025-02-25 14:18:05 +01:00
Philip Rebohle
dafb71b18e [d3d11] Add debug labels for submissions 2025-02-25 00:17:35 +01:00
Philip Rebohle
a0c8bbaf10 [dxvk] Add method to retrieve resource debug names 2025-02-25 00:17:31 +01:00
Philip Rebohle
9c395fd60d [dxvk] Disable VK_EXT_multi_draw on qcom drivers 2025-02-24 22:16:37 +01:00
Philip Rebohle
866228a37d [dxvk] Fix memory type selection for non-device local property flags
Fixes potential memory allocation issues on unified memory setups
that have unique memory types for different resources.
2025-02-24 22:16:37 +01:00
Philip Rebohle
60bf1f9ec4 [dxvk] Enable tiler mode on proprietary drivers as well
Not like it really hurts anyone.
2025-02-24 16:32:32 +01:00
Philip Rebohle
95e12decf1 [dxbc] Remove spammy debug logging 2025-02-24 15:40:52 +01:00
Philip Rebohle
e3f047a96a [d3d11] Remove d3d11.dcSingleUseMode option 2025-02-24 15:34:30 +01:00
Philip Rebohle
7208d06548 [dxvk] Clean up indirect draw unrolling
Also don't report explicit multidraw as merged draws, to be more
consistent with reporting in games that also use IndirectCount.
2025-02-23 18:58:19 +01:00
Philip Rebohle
fc3cf3e822 [d3d11] Properly initialize minUav counter
And also use it when resetting UAV bindings. Fixes a small oversight
that didn't affect correctness.
2025-02-23 13:34:11 +01:00
Philip Rebohle
c69dbc4490 [dxvk] Move draw and dispatch stat counters back to the context
Otherwise we'll count the HUD by accident. Only keep the barrier counter
since there are so many different places where we issue pipeline barriers,
and they are interesting anyway.
2025-02-23 13:24:28 +01:00
Philip Rebohle
a0ea29a2fa [hud] Display number of merged draws, if any 2025-02-23 13:24:28 +01:00
Philip Rebohle
9b37ba679a [dxvk] Add stat counter for merged draws 2025-02-23 13:24:28 +01:00
Philip Rebohle
016f05a770 [dxvk] Implement draw batching via VK_EXT_multi_draw 2025-02-23 13:24:28 +01:00
Philip Rebohle
7e503fa053 [dxvk] Enable VK_EXT_multi_draw if supported 2025-02-23 13:24:28 +01:00
Philip Rebohle
7815b6942d [d3d11] Implement direct draw batching
Not super useful without backend support though.
2025-02-23 13:24:28 +01:00
Philip Rebohle
fc3d3ae331 [dxvk,d3d11] Refactor CS command data allocation
Allows us to allocate a (potentially growing) array of
arbitrary data structures for a CS command.
2025-02-23 13:24:28 +01:00
Philip Rebohle
20dc389ab7 [d3d11] Skip unnecessary iterations when binding graphics UAVs
Some games will unconditionally use a high index for UAVStartSlot.
2025-02-23 12:06:51 +01:00
Philip Rebohle
9f9d51d52c [d3d11] Lazy-bind pixel shader UAVs
Moderately cursed because PS UAVs are also available to other graphics
stages.
2025-02-23 12:06:51 +01:00
Philip Rebohle
9389456d20 [d3d11] Lazy-bind compute shader UAVs
And factor UAV counter updates out of binding.
2025-02-23 12:06:51 +01:00
Philip Rebohle
69171873fa [d3d11] Add compile-time debug flag for lazy binding 2025-02-23 12:06:51 +01:00
Philip Rebohle
c0983a32be [d3d11] Reset dirty bindings on command submission 2025-02-23 12:06:51 +01:00
Philip Rebohle
3e6dfcfb15 [d3d11] Reset dirty tracking when re-applying context state 2025-02-23 12:06:51 +01:00
Philip Rebohle
9d890c75ac [d3d11] Don't template methods that restore shader bindings
This was only needed because Bind* methods were also templated.
2025-02-23 12:06:51 +01:00
Philip Rebohle
771f14c466 [d3d11] Refactor BindUnorderedAccessView
We won't do lazy bindings for UAVs, but at least bring this function
in line with the rest of the binding functions.
2025-02-23 12:06:51 +01:00
Philip Rebohle
b0d881046f [d3d11] Lazy-bind samplers 2025-02-23 12:06:51 +01:00
Philip Rebohle
f2ab76c8db [d3d11] Lazy-bind shader resources 2025-02-23 12:06:51 +01:00
Philip Rebohle
4fdbfffdcc [d3d11] Lazy-bind constant buffers 2025-02-23 12:06:51 +01:00
Philip Rebohle
a61c114519 [d3d11] Change AllowFlush behaviour
No functional change, just makes it less annoying to use in methods that
can be called from both immediate and deferred contexts-
2025-02-23 12:06:51 +01:00
Philip Rebohle
41ec7b6a02 [d3d11] Track shader stages with dirty bindings as well as used bindings 2025-02-23 12:06:51 +01:00
Philip Rebohle
be61341178 [d3d11] Rework DXBC shader stage to Vulkan shader stage mapping
We're going to have to do this at runtime, so this needs to be fast.
2025-02-23 12:06:51 +01:00
Philip Rebohle
6080e6d24d [d3d11] Store used binding mask inside shader objects 2025-02-23 12:06:51 +01:00
Philip Rebohle
75599780f2 [dxbc] Gather binding info during shader compilation 2025-02-23 12:06:51 +01:00
Philip Rebohle
5f16faaae0 [util] Generalize bit mask iterator 2025-02-23 12:06:51 +01:00
Robin Kertels
94b48c1633 [d3d9] Slightly clean up sampler slot handling 2025-02-21 13:25:23 +01:00
Robin Kertels
f7d56886c5 [d3d9] Fix sampler slot correction not respecting dmap texture 2025-02-21 13:25:23 +01:00
Philip Rebohle
0691a7fc46 [dxvk] De-duplicate drawIndirectCount implementations
No functional change, just some code cleanup.
2025-02-20 13:30:31 +01:00
Philip Rebohle
a135e01f89 [dxvk] Unroll merged indirect draws as necessary 2025-02-20 13:30:31 +01:00
Philip Rebohle
1d8fb818fc [dxvk] Check whether pipeline has potentially hazardous stores 2025-02-20 13:30:31 +01:00
Philip Rebohle
6f7a468174 [dxvk] Fix global render pass barrier
Only need to deal with common write-after-read scenarios, we can
ignore writes since those will add extra barriers anyway. Also
move this work out of the somewhat hot pipeline bind function.
2025-02-20 13:30:31 +01:00
Philip Rebohle
dd1ca4ce59 [dxvk] Track order-invariant access ops in barrier tracker
This elides barriers between draws or dispatches if we can prove
order-invariance through atomic operations.
2025-02-20 13:30:31 +01:00
Philip Rebohle
c475960754 [dxvk] Pass store op around for barrier tracking 2025-02-20 13:30:31 +01:00
Philip Rebohle
e01a6eec3e [dxbc] Track bindings with order-invariant atomic stores 2025-02-20 13:30:31 +01:00
Philip Rebohle
d94e3633dc [dxvk] Introduce concept of order-invariant atomic stores 2025-02-20 13:30:31 +01:00
Philip Rebohle
8c7da07085 [util] Fix enum declaration 2025-02-20 13:30:31 +01:00
WinterSnowfall
22052106d8 [d3d9] Relax logging level on validateGammaRamp 2025-02-20 00:01:10 +01:00
WinterSnowfall
3716d48c89 [d3d8/9] Use numeric_limits globally 2025-02-20 00:01:10 +01:00
WinterSnowfall
317607e192 [d3d8/9] Prevent device child ref underruns on release 2025-02-20 00:01:10 +01:00
Philip Rebohle
19361c962c [dxvk] Add debug region for barrier control 2025-02-19 19:34:52 +01:00
Philip Rebohle
636669e1a5 [dxvk] Improve handling of nested debug regions 2025-02-19 19:34:52 +01:00
Philip Rebohle
b03d457ffb [dxvk,d3d11] Improve explicit UAV overlap behaviour
If the app explicitly enables UAV overlap, don't synchronize
back-to-back read-modify-write operations to the same UAV either.
2025-02-19 19:34:52 +01:00
Philip Rebohle
a2c9c0f740 [d3d11] Use resource cookies for draw buffer tracking
Avoids keeping draw buffers alive when the app stops using indirect
draws. Unlikely to have caused issues in practice, but draw buffers
are not part of the API state to begin with.
2025-02-19 19:34:52 +01:00
Philip Rebohle
07f7ccdc96 [dxvk,d3d11] Fix draw buffer tracking for DrawAuto
Not like anybody uses this feature, but we need to both check for
hazards and make sure the SO counter actually gets tracked. Use
the existing draw buffer mechanism for this.
2025-02-19 19:34:52 +01:00
Philip Rebohle
d37a13847a [dxvk] Rename and repurpose ignoreGraphicsBarriers option
Less nuclear approach that hopefully works just as well in practice.
2025-02-19 19:34:52 +01:00
Philip Rebohle
18e5c12b6d [dxvk] Fix resource hazard checks
Turns out we've had broken write-after-read checks for a while.
2025-02-19 19:34:52 +01:00
Philip Rebohle
48d145fff6 [dxvk] Change debug color for pipelines with side effects 2025-02-19 19:34:52 +01:00
Philip Rebohle
04d2609a91 [dxvk] Clean up shader resource hazard checking 2025-02-19 19:34:52 +01:00
Philip Rebohle
3bbae86ec9 [dxvk] Rework compute barrier tracking 2025-02-19 19:34:52 +01:00
Philip Rebohle
53b076be61 [dxvk] Rework graphics barrier tracking
Avoids having to insert redundant barriers when the app does UAV rendering.
2025-02-19 19:34:52 +01:00
Philip Rebohle
009f8ee356 [dxvk] Emit barriers when updating shader resources 2025-02-19 19:34:52 +01:00
Philip Rebohle
be9391ded5 [dxvk] Track transform feedback buffer access 2025-02-19 19:34:52 +01:00
Philip Rebohle
96337f11d4 [dxvk] Track vertex buffer access 2025-02-19 19:34:52 +01:00
Philip Rebohle
24b58e5858 [dxvk] Track index buffer access 2025-02-19 19:34:52 +01:00
Philip Rebohle
21eb682b39 [dxvk] Track indirect draw buffer access 2025-02-19 19:34:52 +01:00
Philip Rebohle
a7c1e7a2a0 [dxvk] Add resource flag to track graphics pipeline side effects 2025-02-19 19:34:52 +01:00
Philip Rebohle
978d7cb65b [dxvk] Add more convenience methods to track buffer barriers 2025-02-19 19:34:52 +01:00
Philip Rebohle
23067c48c7 [dxvk] Clean up CS chunk allocation
This also makes it more robust w.r.t. alignment.
2025-02-18 15:37:11 +01:00
Philip Rebohle
4c8ee300b5 [util] Add more optimal popcnt implementation 2025-02-18 15:28:41 +01:00
Robin Kertels
4282829f38 [d3d9] Route operations on unmappable memory through allocator
Fixes an extremely race condition that can happen when freeing a chunk.
2025-02-17 19:23:15 +01:00
WinterSnowfall
92523fc0dd [d3d9] Fix x64 crash on SetVertexShaderConstantF index overflow 2025-02-17 15:11:05 +01:00
WinterSnowfall
4a89b75bb7 [d3d8] Fix x64 crash on shader validation 2025-02-17 15:11:05 +01:00
WinterSnowfall
274c590ad6 [d3d9] Set priority only for D3DPOOL_MANAGED/DEFAULT resources 2025-02-17 15:11:05 +01:00
WinterSnowfall
84b2ac3f97 [d3d8] Set priority only for D3DPOOL_MANAGED resources 2025-02-17 15:11:05 +01:00
WinterSnowfall
8efa3ed84a [d3d8] A few minor formatting adjustments 2025-02-17 15:11:05 +01:00
Robin Kertels
3269f92138 [d3d9] Fix mismatching texture type mask updates 2025-02-17 14:32:34 +01:00
Jeff
0aebedae16 [d3d8] Add shadow perspective divide hack for Splinter Cell 2025-02-17 14:10:54 +01:00
Philip Rebohle
f2802dd2ff [dxvk] Fix some potential image layout bugs 2025-02-17 01:26:03 +01:00
Philip Rebohle
9389a0ca96 [dxvk] Don't transition image layout in clearImageViewCs unless necessary 2025-02-17 01:26:03 +01:00
Philip Rebohle
6d9e0baa27 [dxvk] Don't hold queue lock when invoking periodic memory tasks 2025-02-14 14:34:33 +01:00
Philip Rebohle
9573c389de [dxvk] Don't create GPL pipeline if we need to patch the vertex shader
Works around a game bug in Kingdom Come Deliverance and silences a validation
error.
2025-02-13 18:46:49 +01:00
Philip Rebohle
3453732bba [dxvk] Fix clear optimization edge case with multi-planar images 2025-02-12 21:42:27 +01:00
Philip Rebohle
0a84dbb787 [dxvk] Implement resolves with a render target clear if possible
Same idea as for copies, if the last use of the source image was a clear
then we can simply clear the destination image instead of performing a
much more expensive resolve operation.
2025-02-12 21:42:27 +01:00
Philip Rebohle
f5a5ec7c88 [dxvk] Factor out method to find pending deferred clears 2025-02-12 21:42:27 +01:00
Philip Rebohle
1cabbee2bb [dxvk] Rework render pass resolves
In an attempt to also support batching layered resolves.
2025-02-12 21:42:27 +01:00
Philip Rebohle
563129c863 [dxvk] Fix potential resolve attachment invalidation bug 2025-02-12 14:44:54 +01:00
Philip Rebohle
c5a3aa73a0 [util] Enable dummy composition swap chain for an upcoming game 2025-02-12 01:36:10 +01:00
Philip Rebohle
80bfd2ed97 [util] Add functionality to support hashed app profiles 2025-02-12 01:34:40 +01:00
Philip Rebohle
813ae020b6 [d3d11] Flush more around non-multisampled render passes 2025-02-11 16:14:53 +01:00
Philip Rebohle
7e3c6f819a [util] Add new flush type for render pass boundaries 2025-02-11 16:14:53 +01:00
Philip Rebohle
94b5cfc3d8 [dxvk] Don't use secondary command buffers for certain render passes
If we can't use any store/resolve op optimizations on a render pass, there
is no reason to pay for the overhead of secondary command buffers.
2025-02-11 16:14:53 +01:00
Philip Rebohle
beaf6dd2a6 [dxvk] Add context flag for active secondary command buffers
Makes some checks more explicit.
2025-02-11 16:14:53 +01:00
Philip Rebohle
cf9ccf711b [dxvk] Add perf hint to avoid secondary command buffers 2025-02-11 16:14:53 +01:00
Philip Rebohle
94254f556e [dxvk] Fix stencil resolve mode 2025-02-11 15:26:08 +01:00
Philip Rebohle
70492b2954 [dxvk] Avoid locking built-in latency tracker when calling into presenter
Same reason as for the Reflex one.
2025-02-11 15:12:13 +01:00
Philip Rebohle
b1174a1bdf [dxvk] Avoid locking Reflex latency tracker when calling into presenter
There are complex deadlock conditions during swap chain destruction.
2025-02-11 15:12:13 +01:00
Philip Rebohle
efeb15edbd [dxvk] Fix lack of forward progress guarantee in presenter
Turns out that relying on two threads to unblock another was a bad idea.
Fixes a potential deadlock w.r.t. swapchain recreation with Reflex.
2025-02-11 15:12:13 +01:00
Philip Rebohle
ac17d5f9b1 [dxvk] Enable tiler mode for more drivers 2025-02-10 18:36:01 +01:00
Philip Rebohle
11ec65d516 [include] Update Vulkan headers 2025-02-10 17:44:36 +01:00
Neal Gompa
4d3b35ae53 meson: Fix version construct by dropping the "v" prefix
The "v" prefix was causing the version to be incorrectly set for
generated files (such as pc files for pkg-config).

Dropping the prefix fixes the issue.
2025-02-10 16:38:03 +01:00
Philip Rebohle
3aeedb9c98 [d3d11] Forward DiscardResource calls to backend for images 2025-02-10 16:13:31 +01:00
Philip Rebohle
9b5499caf2 [dxvk] Ignore deferred discards that we can't fold into load ops 2025-02-10 16:13:31 +01:00
Philip Rebohle
fcabffc1e5 [dxvk] Add store op optimization for image discards in tiler mode 2025-02-10 16:13:31 +01:00
Philip Rebohle
821386aeff [dxvk] Add config option to toggle tiler optimizations 2025-02-10 16:13:31 +01:00
Philip Rebohle
fa2d791d79 [dxvk] Use resolve attachments if possible 2025-02-10 16:13:31 +01:00
Philip Rebohle
80fb7e2294 [d3d9,d3d11] Adjust flush heuristic on tiling GPUs
Probably needs work, just disable most of the heuristic for the time being.
2025-02-10 16:13:31 +01:00
Philip Rebohle
00299b264b [dxvk] Use secondary command buffers for rendering on tiling GPUs 2025-02-10 16:13:31 +01:00
Philip Rebohle
4a92b92f58 [dxvk] Add perf hint for tiling GPUs 2025-02-10 16:13:31 +01:00
Philip Rebohle
6c1bc35264 [dxvk] Add functionality to use secondary command buffers 2025-02-10 16:13:31 +01:00
Philip Rebohle
7d35afdc37 [dxvk] Add functionality to allocate secondary command buffer 2025-02-10 16:13:31 +01:00
Philip Rebohle
f615594f22 [dxgi] Create dummy window for composition swap chains
Obviously not a valid implementation, but allows composition swap chains
to exist with some basic functionality.
2025-02-10 14:23:12 +01:00
Philip Rebohle
76f9bec7cc [dxgi] Add option to expose composition swapchains 2025-02-10 14:14:38 +01:00
WinterSnowfall
0a19e5b6db [d3d8] Store the value of D3DRS_ZVISIBLE 2025-02-10 11:25:40 +01:00
WinterSnowfall
946419cda2 [d3d8] Fix invalid casting in GetRenderState 2025-02-10 11:25:40 +01:00
WinterSnowfall
13554f18bd [d3d9] Update software cursor position using SetCursorPosition 2025-02-08 22:28:48 +01:00
Blisto91
54a26dde3d [util] Disable dcSingleUseMode for Cardfight!! Vanguard Dear Days 2 2025-02-07 19:53:45 +01:00
Philip Rebohle
357484bd4b [d3d11] Don't call Flush from swap chain code
Use internal function instead.
2025-02-06 23:26:05 +01:00
Philip Rebohle
9664e0b850 [dxvk] Fix potential race conditions w.r.t. swapchain destruction
The signaling thread can queue up a wait while the swapchain is being
destroyed, which blows up. This also fixes a related race condition
where we would queue up a wait using the wrong present mode.

This is somewhat fragile in the sense that the queue worker *must* call
signalFrame for each and every present, or this will fall apart.
2025-02-06 20:25:32 +01:00
Philip Rebohle
e5a81f8c7e [dxvk] Don't probe buffer compatibility on image-only memory types
Silences a validation error.
2025-02-06 15:29:24 +01:00
Philip Rebohle
215cebc019 [dxvk] Remove superfluous function declaration 2025-02-06 14:29:19 +01:00
Philip Rebohle
a413eb0843 [dxvk] Only end render pass in invalidateImage if currently bound
Not really needed otherwise.
2025-02-05 22:49:25 +01:00
Philip Rebohle
0434b23167 [dxvk] Fix initializing buffers with non-dword sizes
Fixes #4641. This should be very rare in general though.
2025-02-05 18:36:01 +01:00
Philip Rebohle
cf946eb981 [dxvk] Add setupapi as a Windows WSI dependency 2025-02-02 21:22:19 +01:00
Philip Rebohle
3a34dae722 [dxvk] Work around locale-dependent regex parsing crash 2025-02-02 21:21:58 +01:00
WinterSnowfall
bd69d1efdc [d3d8/9] Use shader version macros where applicable 2025-02-02 18:17:10 +01:00
WinterSnowfall
e3f1d4c021 [d3d8] Fix up swizzle for all opcodes requiring a replicate swizzle 2025-02-02 18:17:10 +01:00
Philip Rebohle
5569274a97 [d3d9] Do not assume 16-byte alignment in replaceNaN
Stack alignment is 4 bytes on 32-bit, and we cannot align variables
on the stack, so this is technically broken.
2025-02-02 10:50:33 +01:00
WinterSnowfall
dd5b28e557 [d3d8] Use D3D8 compatibilty mode to set HUD API level 2025-02-02 10:47:32 +01:00
HunterCZ122
8860bde34b [util] Fix black screen after alt-tab for Trainz 2025-01-31 22:47:37 +01:00
Blisto91
8776cf2ce6 [util] Cleanup some deviceLossOnFocusLoss configs
These game work fine out of the box now
missed
2025-01-30 18:22:18 +01:00
Autumn Ashton
0cad97d5ec [util] Add scaleDref to SplinterCell2 2025-01-30 02:58:15 +00:00
Jeff
06bdc88eb6 [d3d8] Restore config for Splinter Cell 2025-01-30 01:59:22 +00:00
WinterSnowfall
1d90772eb2 [d3d9] Report vertex texture filtering support only for SM3 2025-01-27 18:53:10 +01:00
VPInventions
3d35925a05 Assign viewInfo.minLayer for D3D11VideoProcessorInputView 2025-01-27 18:48:46 +01:00
WinterSnowfall
5b21286377 [d3d9] Adjust SM2 VS/PS caps 2025-01-26 21:52:18 +01:00
WinterSnowfall
ad0920fe1e [d3d9] Relax SM2 minor version checks on shader creation 2025-01-26 20:02:45 +01:00
WinterSnowfall
c6dc7e0939 [d3d8/9] Proper (and age accurate) handling of d3d9.shaderModel = 0 2025-01-26 13:56:27 +01:00
WinterSnowfall
48d24893d4 [d3d8] Clean up and document D3D8-specific caps 2025-01-26 13:50:41 +01:00
WinterSnowfall
753c11d144 [d3d8] Implement SCRATCH pool copies with CopyRects 2025-01-26 13:50:41 +01:00
WinterSnowfall
7482e45534 [d3d8] Remove several empty explicit destructors 2025-01-26 13:50:41 +01:00
WinterSnowfall
e757170211 [d3d8] Remove superfluous methods from D3D8Surface 2025-01-26 13:50:41 +01:00
WinterSnowfall
e2a078d534 [d3d8] Properly return the value of D3DRS_SOFTWAREVERTEXPROCESSING 2025-01-26 13:50:41 +01:00
WinterSnowfall
d846e89a4b [d3d8] Improve handling of FullScreen_PresentationInterval 2025-01-26 13:50:41 +01:00
WinterSnowfall
e40f03b96a [d3d8] General TODO and comment cleanup 2025-01-26 13:50:41 +01:00
WinterSnowfall
088915e24c [d3d8] Don't forward D3DRS_ZVISIBLE changes to d3d9 2025-01-26 13:50:41 +01:00
WinterSnowfall
470764e24c [d3d8] Don't forward D3DRS_PATCHSEGMENTS changes to d3d9 2025-01-26 13:50:41 +01:00
WinterSnowfall
0983ab1f39 [d3d8] Don't forward D3DRS_LINEPATTERN changes to d3d9 2025-01-26 13:50:41 +01:00
WinterSnowfall
20b047600f [d3d8] Disable z-buffer discarding for depth stencil surfaces 2025-01-26 13:50:41 +01:00
WinterSnowfall
b61ea1db76 [d3d8] Address the MultiSampleQuality usage 2025-01-26 13:50:41 +01:00
WinterSnowfall
4184452f49 [d3d8] Validate srcRect/destPoint dimensions in UpdateTextureFromBuffer 2025-01-26 13:50:41 +01:00
Philip Rebohle
736ee20eb5 [d3d11] Implement ID3DLowLatencyDevice 2025-01-25 18:02:20 +01:00
Philip Rebohle
4f9b106d40 [d3d11] Stub out ID3DLowLatencyDevice for D3D11 2025-01-25 18:02:20 +01:00
Philip Rebohle
86c317fbbc [d3d11] Add ID3DLowLatencyDevice definitions 2025-01-25 18:02:20 +01:00
Philip Rebohle
59e53c1863 [d3d11] Add queue parameter to CS chunk injection 2025-01-25 18:02:20 +01:00
Philip Rebohle
539da5abdf [dxvk] Refactor CS chunk queues
Introduces two queues and allows us to dispatch chunks to the ordered
queue without disrupting the sequence number.
2025-01-25 18:02:20 +01:00
Philip Rebohle
0e9efa5183 [dxvk] Implement Reflex latency tracker 2025-01-25 18:02:20 +01:00
Philip Rebohle
4b009237d7 [dxvk] Add NV_low_latency2 path to latency tracker 2025-01-25 18:02:20 +01:00
Philip Rebohle
66dc0a9383 [dxvk] Pass present IDs to command submissions as necessary 2025-01-25 18:02:20 +01:00
Philip Rebohle
4c2990f199 [dxvk] Implement NV_low_latency2 functionality in presenter 2025-01-25 18:02:20 +01:00
Philip Rebohle
c700d76477 [dxvk] Add option to disable VK_NV_low_latency2 2025-01-25 18:02:20 +01:00
Philip Rebohle
859117797f [dxvk] Enable VK_NV_low_latency2 if supported 2025-01-25 18:02:20 +01:00
Philip Rebohle
4fcbf52752 [d3d9] Always limit to display refresh in low latency mode 2025-01-25 18:02:20 +01:00
Philip Rebohle
0e3ed707ba [d3d9] Implement latency tracking 2025-01-25 18:02:20 +01:00
Philip Rebohle
50375ee1ee [dxgi] Pass display refresh in windowed mode if latency sleep is enabled 2025-01-25 18:02:20 +01:00
Philip Rebohle
21f69eceaa [d3d11] Implement latency tracking 2025-01-25 18:02:20 +01:00
Philip Rebohle
23f08eaedd [hud] Add frame latency item 2025-01-25 18:02:20 +01:00
Philip Rebohle
2ee598b4af [dxvk] Add latency tracker to context 2025-01-25 18:02:20 +01:00
Philip Rebohle
95b8dc59fb [dxvk] Add latency tracker to presenter 2025-01-25 18:02:20 +01:00
Philip Rebohle
273e4abb14 [dxvk] Add latency tracker to queue submissions 2025-01-25 18:02:20 +01:00
Philip Rebohle
552470de63 [dxvk] Add latency tracker
Implements a basic latency sleep solution that is intended to work
without requiring games to support any related vendor features.

This alone is not enough to expose the Reflex API to applications via
dxvk-nvapi, but since that relies on NV_low_latency2 specifics anyway,
we are going to add an implementation based on that extension later
with an extended interface.
2025-01-25 18:02:20 +01:00
Philip Rebohle
55639889c7 [util] Expose frame rate environment variable
We'll need this in more places.
2025-01-25 18:02:20 +01:00
Philip Rebohle
53e5c4875f [util] Add parameter to initialize small_vector with a given size
Matches std::vector.
2025-01-25 18:02:20 +01:00
Blisto91
b73f9d8ecb [util] Extend Battlefield 2/2142 config to Project Reality 2025-01-22 19:56:41 +01:00
Vasiliy Stelmachenok
84ccad3528 [build] Use -mpreferred-stack-boundary=2 on 32-bit x86
Signed-off-by: Vasiliy Stelmachenok <ventureo@cachyos.org>
2025-01-21 21:17:28 +01:00
Blisto91
a3ba8fb4dc [util] Add configs for Star Wars Empire at War
In both the base game and expansion the Water and Shader Detail options are locked at their lowest value when they see Intel.
In the base game (not expansion) the AA option disappears for some reason whenit sees vram at 2075MB and above.
2025-01-20 23:01:25 +01:00
Vasiliy Stelmachenok
685126482d [d3d11] Fix missing argument reference
Signed-off-by: Vasiliy Stelmachenok <ventureo@cachyos.org>
2025-01-20 22:59:54 +01:00
Philip Rebohle
863591275c [dxvk] Error out on surface creation again
Unless it's NATIVE_WINDOW_IN_USE. Should fix a regression where
D3D swapchain creation would succeed on out-of-memory errors.
2025-01-20 18:20:14 +01:00
Philip Rebohle
c52a68a5be [dxvk] Fix typo that breaks HDR metadata 2025-01-16 17:47:56 +01:00
Philip Rebohle
5ed2d990af [d3d9] Fix software cursor texture format
The new code expects texture reads to be in linear space, so we need
to use an sRGB format here.
2025-01-16 12:05:17 +01:00
Philip Rebohle
3dbe8ad43c [d3d9] Rework device reset detection
More robust.
2025-01-16 11:38:39 +01:00
Philip Rebohle
f9c3dd1f5f [d3d9] Remove explicit swapchain synchronization
No longer necessary.
2025-01-16 11:38:39 +01:00
Philip Rebohle
c43cf6895b [d3d9] Refactor presenter management
There's basically no reason to ever recreate a presenter for an already
managed window with the new code.
2025-01-16 11:38:39 +01:00
Philip Rebohle
785649f3b8 [d3d11] Remove explicit swapchain synchronization 2025-01-16 11:38:39 +01:00
Philip Rebohle
42adc4ac11 [dxvk] Perform acquire and present synchronization in the backend
And add method to explicitly destroy relevant Vulkan objects.
2025-01-16 11:38:39 +01:00
Philip Rebohle
b03ff775ce [dxvk] Do not initialize present status from presentImage
Meaningless, this needs to be done elsewhere anyway.
2025-01-16 11:38:39 +01:00
Philip Rebohle
81c3242b6d [dxvk] Dirty swapchain if present returns SUBOPTIMAL
May fix some issues in case the WSI implementation can return SUBOPTIMAL
from present but not acquire. Also ensure to keep state consistent.
2025-01-16 11:38:39 +01:00
Philip Rebohle
816f8e9c87 [dxvk] Improve swapchain debug logging 2025-01-16 11:38:39 +01:00
Philip Rebohle
0f9245ff33 [d3d9,d3d11] Remove numBackBuffers option
Useless.
2025-01-16 11:38:39 +01:00
Philip Rebohle
cb42ea21be [dxvk] Adjust swapchain format preferences in various cases
For sRGB, using a native sRGB format allows the swapchain blitter
to use a more efficient code path for drawing the HUD.

Also allow RGB9E5 for sRGB and HDR10.
2025-01-16 11:38:39 +01:00
Philip Rebohle
d200184306 [dxvk] Rework swapchain composition
Also adds a separate set of shaders for the software cursor, provided it
can be drawn directly with alpha blending.
2025-01-16 11:38:39 +01:00
Philip Rebohle
a09d372caf [dxvk] Prepare swapchain blitter for compositing HUD and cursor
This will be necessary for non-linear color spaces to get proper blending.
2025-01-16 11:38:39 +01:00
Philip Rebohle
8be30d7d5a [hud] Add method to check whether HUD is empty 2025-01-16 11:38:39 +01:00
Philip Rebohle
5134a4b3c5 [dxvk] Move HUD update and rendering into swapchain blitter 2025-01-16 11:38:39 +01:00
Philip Rebohle
c1ed8cd1f3 [hud] Add function to change API name dynamically
Needed for D3D8 due to implicit swapchain shenanigans.
2025-01-16 11:38:39 +01:00
Philip Rebohle
0eeedf9259 [hud] Allow returning HUD item objects to the caller 2025-01-16 11:38:39 +01:00
Philip Rebohle
d5744e5a81 [dxvk] Enable automatic fallbacks for HDR color spaces
The swap chain blitter can deal with the conversion.
2025-01-16 11:38:39 +01:00
Philip Rebohle
d85d07c0ec [dxvk] Infer swap chain color space from image views 2025-01-16 11:38:39 +01:00
Philip Rebohle
1d790970d5 [d3d9] Set color space for swap chain image 2025-01-16 11:38:39 +01:00
Philip Rebohle
b586294e29 [d3d11] Set color space for swap chain image 2025-01-16 11:38:39 +01:00
Philip Rebohle
ed3c02906c [dxvk] Set color space for swap chain back buffers 2025-01-16 11:38:39 +01:00
Philip Rebohle
84317f913a [dxvk] Don't redundantly update image debug names 2025-01-16 11:38:39 +01:00
Philip Rebohle
ff41a5ab12 [dxvk] Add color space property to image description 2025-01-16 11:38:39 +01:00
Philip Rebohle
ad11509e83 [dxvk] Soften error reporting from the presenter 2025-01-16 11:38:39 +01:00
Philip Rebohle
43838d3df8 [dxvk] Move Vulkan swapchain management to backend
Massive cleanup reduce code duplication between D3D11 and D3D9,
and introduce a sane path to pass data around. Implicit swap
chain recreation is now entirely transparent to the frontends.
2025-01-16 11:38:39 +01:00
Philip Rebohle
06b44c6237 [dxvk] Rework HDR metadata updates 2025-01-16 11:38:39 +01:00
Philip Rebohle
63e88debee [dxvk] Move deferSurfaceCreation handling to backend 2025-01-16 11:38:39 +01:00
Philip Rebohle
ab9646551f [dxvk] Import swap chain images from the back-end 2025-01-16 11:38:39 +01:00
Philip Rebohle
073806df7c [dxvk] Move surface creation to the backend 2025-01-16 11:38:39 +01:00
Philip Rebohle
c707d9026f [meta] Release 2.5.3 2025-01-13 16:20:20 +01:00
Blisto91
3d401690cb [util] Enable Strict floatEmulation for Max Payne 3
Visual issues such as white, black or rainbow colored objects and effect on some driver such as ANV (and amdvlk when set to true)
2025-01-13 16:17:36 +01:00
Philip Rebohle
5f5c9a4cdd [d3d9] Improve logging for PS input register indices 2025-01-13 15:38:25 +01:00
Philip Rebohle
af45295a2f [d3d9] Log instruction stream in validator 2025-01-13 15:38:25 +01:00
Jeff
5b7726cf6f [d3d9] Fix invalid strings returned by GetInstanceExtensions 2025-01-13 11:08:45 +01:00
Philip Rebohle
d5832c3075 [dxbc] Improve code gen for zeroing workgroup memory 2025-01-12 14:10:47 +01:00
Philip Rebohle
f17317061c [dxbc] Bound-check dynamically indexed input registers
Halo MCC reads undefined PS inputs otherwise.
2025-01-12 14:10:47 +01:00
Blisto91
7f4e25267c [util] Enable hideIntegratedGraphics for Bright memory
The game will prefer other vendors over Intel even if the latter is the dGPU in the system. It likely assumes that Intel is always a iGPU.
This also happens with the native game on Windows and so isn't a Linux exclusive. Both in d3d11 and d3d12 mode.
2025-01-11 16:55:00 +01:00
Blisto91
07397305f1 [util] Enable zeroInitWorkgroupMemory for Far Cry 5 and New Dawn 2025-01-11 16:45:06 +01:00
Philip Rebohle
efb9d444c1 [dxvk] Introduce config option to enable FSE on Windows 2025-01-10 14:07:55 +01:00
Philip Rebohle
071dec7148 [hud] Add debug label for HUD rendering 2025-01-09 16:25:46 +01:00
Philip Rebohle
279c6f150a [dxvk] Add debug label for swap chain blitter 2025-01-09 16:25:46 +01:00
Philip Rebohle
50f9630250 [dxvk] Add debug regions for internal operations 2025-01-09 16:25:46 +01:00
Philip Rebohle
485090d039 [dxvk] Add debug region for mip generation 2025-01-09 16:25:46 +01:00
Philip Rebohle
498ecca81d [dxvk] Add debug region for memory defragmentation 2025-01-09 16:25:46 +01:00
Philip Rebohle
9bb06baaaa [dxvk] Add debug labels for pipeline binding 2025-01-09 16:25:46 +01:00
Philip Rebohle
ca2afb0b8b [dxvk] Add debug label for render target clears 2025-01-09 16:25:46 +01:00
Philip Rebohle
1b9ea8c6e3 [dxvk] Add debug regions for render passes 2025-01-09 16:25:46 +01:00
Philip Rebohle
d9b5f09239 [dxvk] Add debug region stack
Ensures that we correctly begin and end app-provided regions within
each command buffer.
2025-01-09 16:25:46 +01:00
Philip Rebohle
71bd780340 [dxvk] Add debug regions to command buffers 2025-01-09 16:25:46 +01:00
Philip Rebohle
b62a8c78b4 [dxvk] Add context feature flag for debug utils support 2025-01-09 16:25:46 +01:00
Philip Rebohle
8c3d7a1979 [d3d11] Support debug names for buffers and textures
Co-authored-by: Aaron Leiby <aleiby@gmail.com>
2025-01-09 16:25:46 +01:00
Philip Rebohle
4970dc3358 [d3d11] Set debug names for internal buffers 2025-01-09 16:25:46 +01:00
Philip Rebohle
1721be4973 [d3d9] Set debug names for internal buffers 2025-01-09 16:25:46 +01:00
Philip Rebohle
d2d46be8da [dxvk] Set debug names for memory allocations
Makes it easier to work out what is allocated where.
2025-01-09 16:25:46 +01:00
Philip Rebohle
3339b165cd [dxvk] Set debug names for internal buffers 2025-01-09 16:25:46 +01:00
Philip Rebohle
3e77893ef7 [dxvk] Set debug names for swap chain blitter resources 2025-01-09 16:25:46 +01:00
Philip Rebohle
098d5adca5 [hud] Set debug names for HUD resources 2025-01-09 16:25:46 +01:00
Philip Rebohle
2507820339 [dxvk] Add support for resource debug names 2025-01-09 16:25:46 +01:00
Philip Rebohle
7f4f927980 [dxvk] Add command buffer parameter to debug label functions 2025-01-09 16:25:46 +01:00
Philip Rebohle
5487f8b9a0 [dxvk] Add convenience methods to check for debug utils support 2025-01-09 16:25:46 +01:00
WinterSnowfall
4151d35e8e [d3d8] Alignment and formatting improvements 2025-01-09 15:50:46 +01:00
WinterSnowfall
639f815432 [d3d8] Refactor D3D8Vertex/IndexBuffer implementation 2025-01-09 15:50:46 +01:00
WinterSnowfall
13ec120289 [d3d8] Refactor D3D8Surface implementation 2025-01-09 15:50:46 +01:00
WinterSnowfall
9d37e4abb4 [d3d8] Refactor D3D8Volume implementation 2025-01-09 15:50:46 +01:00
WinterSnowfall
7bb8819fbc [d3d8] Refactor D3D8Texture2D/3D/Cube implementation 2025-01-09 15:50:46 +01:00
WinterSnowfall
8017607fe7 [d3d8] Refactor D3D8SwapChain implementation 2025-01-09 15:50:46 +01:00
WinterSnowfall
84ad2ea261 [d3d8] Properly initialize state block data 2025-01-09 15:50:46 +01:00
WinterSnowfall
8ead28e481 [d3d8] Unify source file endline delimiters 2025-01-09 15:50:46 +01:00
WinterSnowfall
475bf4e9c1 [d3d9] Use cdw to iterate over register tokens in D3D9ShaderValidator 2025-01-07 17:49:18 +01:00
WinterSnowfall
150cb0d4c7 [d3d9] Refactor the D3D9ShaderValidator implementation 2025-01-06 18:55:37 +01:00
WinterSnowfall
28a08cae6d [d3d8/9] Clear pLockedRect/Box contents universally on lock 2025-01-06 15:52:42 +01:00
Philip Rebohle
9a244e8951 [dxbc] Use NClamp for tess factors and depth clamp
This flushes NaN tess factors to 0, which should match D3D behaviour
for both outer and inner tess factors. The legacy code hasn't been
touched in 7 years.
2025-01-04 17:41:50 +01:00
Robin Kertels
24f98c5835 [d3d9] Skip texture type check when forceSpecConst option is active 2025-01-04 02:11:58 +00:00
esullivan
33498eb512 [spirv] Emit the grad and const offset image ops in the correct order
Currently the grad and const offset image operand ids are emitted in the
incorrect order. This causes incorrect code gen if both the grad and const
offset image operands are used.

This fixes the compilation error found when running TopSpin 2k25 through
DXVK using the NVIDIA proprietary Vulkan driver.
2025-01-03 23:30:56 +01:00
Blisto91
8b9b46dfff [util] Cap fps in The hurricane of the Varstray 2025-01-01 23:42:56 +01:00
Blisto91
7425a33dd7 [util] Merge the Arcana Heart 3 entries 2025-01-01 23:42:56 +01:00
spiffeeroo
39f4d804a2 [util] Limit frame rate to 60 fps for Arcana Heart 3 Love Max!!!!! and Arcana Heart 3 Love Max Six Stars!!!!!! Xtend
[Arcana Heart 3 Love Max!!!!!](https://store.steampowered.com/app/370460) runs too fast when frame rate is above 60 fps (like on high refresh rate monitor). Limit frame rate to 60 fps so game runs at correct speed.

[Arcana Heart 3 Love Max Six Stars!!!!!! Xtend](https://store.steampowered.com/app/661990) also suffers from the game speed being too fast while in windowed mode. Limit frame rate to 60 fps to fix game speed.
2025-01-01 19:46:37 +01:00
Robin Kertels
1eb0c687a6 [d3d9] Fix messing up the dirty textures bitmask 2024-12-20 18:26:02 +01:00
Philip Rebohle
b4faf0bb3e [meta] Release 2.5.2 2024-12-20 13:37:07 +01:00
Robin Kertels
acf93aa70a [util] Remove Alpha Protocol config
The texture type check makes this redundant.
2024-12-17 14:49:52 +01:00
Robin Kertels
65843aa016 [d3d9] Don't bind textures if texture type doesnt match 2024-12-17 14:49:52 +01:00
Tiagoquix
67f4ec73aa [util] Adjust "Earth Defense Force 5" position
Game uses D3D11 according to https://www.pcgamingwiki.com/wiki/Earth_Defense_Force_5#API. Currently it's listed in the D3D9 section.
2024-12-16 23:05:33 +01:00
Philip Rebohle
d1789c4d85 [dxvk] Always consider env var configuration as active
Fixes #4529.
2024-12-15 16:51:32 +01:00
Blisto91
fb8f79e4a7 [util] Add Sims 2 Content Manager & HomeCrafter Plus 2024-12-12 14:02:00 +01:00
Blisto91
0997719a73 [util] Set textureMemory to 0 for Dark Sector
Works around some crashes
2024-12-12 14:01:40 +01:00
Philip Rebohle
5868b067e4 [dxgi] Use VK_FORMAT_A8_UNORM if supported 2024-12-10 01:16:44 +01:00
Philip Rebohle
32399461da [dxvk] Properly encode KHR_maintenance5 formats 2024-12-10 01:16:44 +01:00
WinterSnowfall
4dd0afe121 [d3d9] Validate depth and stencil clears 2024-12-10 01:16:24 +01:00
Jeff
d1abce3be2 [d3d9] Add GetInstanceExtensions to interop API 2024-12-09 18:11:46 +01:00
Philip Rebohle
45da7d6678 [d3d11] Fix rectangle check in ClearView
It is legal to pass a potentially invalid pointer if NumRects is 0.
Fixes validation errors in TopSpin 2k25.
2024-12-09 17:43:53 +01:00
WinterSnowfall
b1ad43145b [d3d8] Add missing include to d3d8_state_block.h 2024-12-05 23:43:23 +01:00
Tiagoquix
a78c1bd6dd [util] Separate Borderlands 2 fixes from Borderlands: The Pre-Sequel 2024-12-05 15:00:19 +01:00
WinterSnowfall
d094053018 [d3d9] Implement several IDirect3DShaderValidator9 validations
Co-Authored-By: Joshua Ashton <joshua@froggi.es>
Co-Authored-By: Paul Gofman <gofmanp@gmail.com>
2024-12-05 14:59:56 +01:00
Autumn Ashton
d956b188ed [d3d9] Clip plane compaction
Compact clip planes to the smallest amount that are enabled.

Signed-off-by: Autumn Ashton <misyl@froggi.es>
2024-12-05 06:08:34 +00:00
Autumn Ashton
8c4c814fb7 [d3d9] Spec-constant out writes to clip distances when disabled
Add a new spec constant with a mask of the enabled clip planes such that they can be optimized out to improve performance.

For GPL shaders, override what we return here so it's always true and don't bother putting the mask in the UBO.

Signed-off-by: Autumn Ashton <misyl@froggi.es>
2024-12-05 06:08:34 +00:00
WinterSnowfall
027fe5963a [d3d9] Fixes for state block specific behavior 2024-12-04 13:06:28 +01:00
WinterSnowfall
17b85accfb [d3d9] Adjust device reset failure error codes 2024-12-04 13:06:28 +01:00
WinterSnowfall
f23c74949a [d3d9] Validate supported formats in D3D9Surface::GetDC 2024-12-04 13:06:28 +01:00
WinterSnowfall
fb56668dc0 [d3d9] Adjust out of bounds clip plane index handling 2024-12-04 13:06:28 +01:00
WinterSnowfall
9c012c8e75 [d3d9] Skip recording MultiplyTransform calls in state blocks 2024-12-04 13:06:28 +01:00
WinterSnowfall
640a266158 [d3d9] Only advertise SRCCOLOR2 & INVSRCCOLOR2 with 9Ex 2024-12-04 13:06:28 +01:00
WinterSnowfall
93d2715b7a [build] Suppress gcc misleading indentation warnings 2024-12-04 10:00:15 +01:00
WinterSnowfall
218b67dd0c [d3d9] Use header back buffer limits during validation
Co-authored-by: Blisto91 <47954800+Blisto91@users.noreply.github.com>
2024-11-28 15:44:00 +01:00
Jeff
4e07302cbf [d3d9] Fix race condition in PIX events 2024-11-28 07:24:12 +00:00
WinterSnowfall
b8ce414820 [util] Enable deferSurfaceCreation for the Codename Panzer series 2024-11-27 16:51:54 +01:00
Philip Rebohle
d7bd3cd58e [d3d9] Remove d3d9.enableDialogMode option 2024-11-25 11:54:55 +01:00
Philip Rebohle
15db475edf [dxvk] Always disable exclusive fullscreen 2024-11-25 11:54:55 +01:00
WinterSnowfall
9e1bc1db23 [d3d9] Expand NormalizeTextureProperties validations 2024-11-23 16:58:25 +01:00
WinterSnowfall
97ad37e409 [d3d9] Validate RT parent device during SetRenderTarget 2024-11-23 16:58:25 +01:00
WinterSnowfall
77020760f1 [d3d9] Fix behavior on multiple image unlocks 2024-11-23 16:58:25 +01:00
WinterSnowfall
bb227fa850 [d3d8] Partially implement ValidateVertexShader/ValidatePixelShader 2024-11-23 16:58:25 +01:00
WinterSnowfall
34165f3814 [d3d8] Validate PS/VS version on creation 2024-11-23 16:58:25 +01:00
WinterSnowfall
423c86bf5e [d3d8] Validate DS dimensions on SetRenderTarget 2024-11-23 16:58:25 +01:00
WinterSnowfall
dd183b4a53 [d3d8] Adjust function, declaration and delete behavior for VS/PS 2024-11-23 16:58:25 +01:00
WinterSnowfall
4f98844f47 [d3d9] Skip some validations for D3DDEVTYPE_NULLREF devices 2024-11-23 16:58:25 +01:00
Blisto91
43b79bcb23 [util] Set Borderlands 2 floatEmulation to Strict
Missing lava in Vault of the Warrior
2024-11-23 02:28:03 +01:00
Blisto91
dd15328ccf [build] Remove old d3dcompiler_47 dependency
Leftover from when tests were in the main repository
2024-11-19 10:50:09 +01:00
Blisto91
7f1e52703d [d3d9] Change correctness factor to 0.5f
The original issue that 0.5f - (1.0f / 128.0f) fixed is a game bug that also happens on Windows and the version of the addon used back then didn't fix correctly. With the latest version of the addon it looks correct.
2024-11-18 13:03:58 +01:00
Philip Rebohle
b276c60f49 [meta] Release 2.5.1 2024-11-18 12:32:41 +01:00
Philip Rebohle
7ffd6dae2c [meta] Document dxgi.enableUe4Workarounds option 2024-11-18 12:32:41 +01:00
Blisto91
987c132df2 [util] Don't include original WC3 in Reforged config
Reforged sits in a folder called x86_64 which should filter out the original Warcraft 3
2024-11-17 20:02:04 +01:00
Blisto91
fefa4ee1f6 [util] Hide iGPU for WoW and WC3 Reforged
Currently with Proton/Wine these games will prefer to use the iGPU. On systems usng the Nvidia proprietary driver this can make them bug out so they don't start or show a gray screen.

It has to be figured and fixed why they even want to use the iGPU, but in the meantime we might as well work around the issue.
2024-11-17 16:12:17 +01:00
WinterSnowfall
ce1b06d0c6 [d3d8] Use unsigned values to identify remapped sampler states 2024-11-16 17:56:57 +01:00
Philip Rebohle
482e758aee [dxgi] Introduce dxgi.enableUe4Workarounds option 2024-11-16 17:26:33 +01:00
Philip Rebohle
1dcc9f3b6a [dxvk] Fix struct <-> class warning 2024-11-16 16:58:17 +01:00
Philip Rebohle
4fb6403b8f [d3d11] Store map mode as raw integer
Fixes some annoying error with clang, hopefully.
2024-11-16 16:58:17 +01:00
60IQ
12c2b2f81b add --64-only and --32-only build options from package-native.sh 2024-11-15 21:14:51 +01:00
Philip Rebohle
4c09b006a5 [d3d9] Actually use correct sampler state for anisotropy 2024-11-13 14:35:59 +01:00
Blisto91
2dadad57f0 [util] Hide amd for GTA The Definitive Edition trilogy
Crashes because of static ags on amd systems which report HDR support.
2024-11-13 12:17:43 +01:00
Randy Eckenrode
8dc1fe1262 [build] Fix native GLFW build
Fixes an undefined reference to `glfwGetWindowAttrib` when building with
GLFW support enabled.
2024-11-11 22:33:52 +01:00
Philip Rebohle
e6209d28cd [build] Fix project version for native builds
Derp, wasn't tested.
2024-11-11 19:59:39 +01:00
229 changed files with 14593 additions and 5398 deletions

View file

@ -1 +1 @@
2.5
2.5.3

148
dxvk.conf
View file

@ -18,6 +18,39 @@
# 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
# AMDAGS.
#
# Supported values: True, False
# dxgi.enableUe4Workarounds = False
# Create the VkSurface on the first call to IDXGISwapChain::Present,
# rather than when creating the swap chain. Some games that start
# rendering with a different graphics API may require this option,
@ -54,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.
#
@ -118,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
@ -138,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,
@ -154,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
@ -183,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
@ -267,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.
@ -411,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
@ -490,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

View file

@ -1,4 +1,4 @@
project('dxvk', ['c', 'cpp'], version : 'v2.5', 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')

View file

@ -25,6 +25,8 @@ shift 2
opt_nopackage=0
opt_devbuild=0
opt_buildid=false
opt_64_only=0
opt_32_only=0
crossfile="build-win"
@ -40,6 +42,12 @@ while [ $# -gt 0 ]; do
"--build-id")
opt_buildid=true
;;
"--64-only")
opt_64_only=1
;;
"--32-only")
opt_32_only=1
;;
*)
echo "Unrecognized option: $1" >&2
exit 1
@ -84,8 +92,12 @@ function package {
rm -R "dxvk-$DXVK_VERSION"
}
build_arch 64
build_arch 32
if [ $opt_32_only -eq 0 ]; then
build_arch 64
fi
if [ $opt_64_only -eq 0 ]; then
build_arch 32
fi
if [ $opt_nopackage -eq 0 ]; then
package

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
@ -625,7 +646,7 @@ namespace dxvk {
// If the texture has an image as well as a staging buffer,
// upload the written buffer data to the image
bool needsUpload = mapType != D3D11_MAP_READ
bool needsUpload = mapType != uint32_t(D3D11_MAP_READ)
&& (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER || mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC);
if (needsUpload) {
@ -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;
}
}

View file

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

View file

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

View file

@ -1452,10 +1452,10 @@ namespace dxvk {
|| texture->CountSubresources() <= SrcSubresource)
return;
D3D11_MAP map = texture->GetMapType(SrcSubresource);
uint32_t map = texture->GetMapType(SrcSubresource);
if (map != D3D11_MAP_READ
&& map != D3D11_MAP_READ_WRITE)
if (map != uint32_t(D3D11_MAP_READ)
&& map != uint32_t(D3D11_MAP_READ_WRITE))
return;
CopySubresourceData(
@ -1481,11 +1481,11 @@ namespace dxvk {
|| texture->CountSubresources() <= DstSubresource)
return;
D3D11_MAP map = texture->GetMapType(DstSubresource);
uint32_t map = texture->GetMapType(DstSubresource);
if (map != D3D11_MAP_WRITE
&& map != D3D11_MAP_WRITE_NO_OVERWRITE
&& map != D3D11_MAP_READ_WRITE)
if (map != uint32_t(D3D11_MAP_WRITE)
&& map != uint32_t(D3D11_MAP_WRITE_NO_OVERWRITE)
&& map != uint32_t(D3D11_MAP_READ_WRITE))
return;
CopySubresourceData(
@ -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;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -147,4 +147,11 @@ namespace dxvk {
}
}
HRESULT STDMETHODCALLTYPE D3D11on12Device::GetD3D12Device(
REFIID riid,
void** ppvDevice) {
return m_d3d12Queue->GetDevice(riid, ppvDevice);
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -85,7 +85,7 @@ namespace dxvk {
public:
static constexpr D3D11_MAP UnmappedSubresource = D3D11_MAP(-1u);
static constexpr uint32_t UnmappedSubresource = ~0u;
D3D11CommonTexture(
ID3D11Resource* pInterface,
@ -200,10 +200,10 @@ namespace dxvk {
* \param [in] Subresource Subresource index
* \returns Current map mode of that subresource
*/
D3D11_MAP GetMapType(UINT Subresource) const {
uint32_t GetMapType(UINT Subresource) const {
return Subresource < m_mapInfo.size()
? D3D11_MAP(m_mapInfo[Subresource].mapType)
: D3D11_MAP(~0u);
? m_mapInfo[Subresource].mapType
: UnmappedSubresource;
}
/**
@ -216,7 +216,7 @@ namespace dxvk {
*/
void NotifyMap(UINT Subresource, D3D11_MAP MapType) {
if (likely(Subresource < m_mapInfo.size())) {
m_mapInfo[Subresource].mapType = MapType;
m_mapInfo[Subresource].mapType = uint32_t(MapType);
if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC)
CreateMappedBuffer(Subresource);
@ -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
*
@ -561,7 +569,7 @@ namespace dxvk {
struct MappedInfo {
D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT layout = { };
D3D11_MAP mapType = UnmappedSubresource;
uint32_t mapType = UnmappedSubresource;
uint64_t seq = 0u;
};
@ -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;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 -1
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_MIPFILTER;
case D3DTSS_MAXANISOTROPY: return d3d9::D3DSAMP_MAXANISOTROPY;
// 25:
case D3DTSS_ADDRESSW: return d3d9::D3DSAMP_ADDRESSW;
default: return d3d9::D3DSAMPLERSTATETYPE(-1);
}
}
}
#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);
}
}
}

View file

@ -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;
@ -1282,7 +1407,7 @@ namespace dxvk {
DWORD* pValue) {
d3d9::D3DSAMPLERSTATETYPE stateType = GetSamplerStateType9(Type);
if (stateType != -1) {
if (stateType != -1u) {
// if the type has been remapped to a sampler state type:
return GetD3D9()->GetSamplerState(Stage, stateType, pValue);
}
@ -1297,8 +1422,15 @@ 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 != -1) {
if (stateType != -1u) {
// if the type has been remapped to a sampler state type:
return GetD3D9()->SetSamplerState(Stage, stateType, Value);
} else {
@ -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);

View file

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

View file

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

View file

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

View file

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

View file

@ -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,
&params,
&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,
&params,
&pDevice9
);
if (likely(SUCCEEDED(res)))
*ppReturnedDeviceInterface = ref(new D3D8Device(
this, std::move(pDevice9),
DeviceType, hFocusWindow, BehaviorFlags,
pPresentationParameters
));
return res;
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 = []

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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(&currentPos) && currentPos == POINT{ X, Y })
return;
@ -37,15 +46,6 @@ namespace dxvk {
}
void D3D9Cursor::RefreshSoftwareCursorPosition() {
POINT currentPos = { };
::GetCursorPos(&currentPos);
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);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -162,7 +162,7 @@ namespace dxvk {
void D3D9Initializer::ExecuteFlushLocked() {
EmitCs([] (DxvkContext* ctx) {
ctx->flushCommandList(nullptr);
ctx->flushCommandList(nullptr, nullptr);
});
FlushCsChunk();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

Some files were not shown because too many files have changed in this diff Show more