Proxy vkd3d device extension interfaces - #435
Conversation
|
Using this PR + jp7677/dxvk-nvapi#391 + a recent master build of vkd3d-proton with VKD3D_CONFIG=descriptor_heap, I finally have Cyberpunk running stable with Reshade + DLSS 4.5 + FG. It took all of those together to make a difference. I can't say it's 100% stable (the game has never been 100% stable, often mod-related issues), but I was able to go from 1-2 minutes before a crash, to well over 1hr+ of gameplay with no Reshade-related crashes. I haven't tried it with DLSS 5. NVIDIA driver 610.57.04 |
|
Independent confirmation, from the other side of the same bug: I spent two days tracking this down under Linux/Proton before finding this PR, and both failure modes you describe are exactly what I measured. How the native handles get in. Control experiment. I clobbered one byte of the
Why a blanket answer is wrong, which is what makes this PR's shape the right one. Four runs, same bench, only the add-on differs:
Both sorts of handle reach the same slot: Your second failure mode (apparent index hits a live heap, corrupted descriptor goes through instead of crashing) matches a class of community reports that read as "it runs fine but the pass does nothing visible", so this PR probably fixes more than the crashes. Same crash independently reported on: FFXIV, D3D11, RTX 4080 (NIGos/dlss5-bridge#22); LOTRO and Galactic Civilizations IV, D3D11, RTX 4080; DLSS5-Feeder on D3D11; my own bench on an RTX 5070, driver 610.57.04. I would happily test this on Blackwell and on both add-ons, but there is no build to test: CI here is |
|
Hi @flshy1337 , please find attached the built x64 Release ReShade64.dll renamed to |
|
|
|
@JoeyDelp thanks for the build, and sorry for the delay. Ran the four-way matrix against it on the same bench as before (RTX 5070, driver 610.57.04, Proton Experimental, vkd3d-proton, dlss5-bridge synthetic contract with three display-mode changes). Only
So the build fixes both failure modes at once — the consumer that needs its handles converted and the one whose handles must be left alone — which is the result a per-handle decision predicts, and which neither the stock build nor my earlier "never convert" byte patch could give. Logs from all four runs are available if wanted. On @crosire's point: agreed that the same outcome is reachable without a check in the hot path — proxying the |
|
Thanks @crosire and @flshy1337 for the feedback and additional validation. I agree that proxying I’ll rework the PR in that direction and use the four-way matrix above as the acceptance test. Thanks again for taking the time to test and clarify the constraints. |
858670d to
4eb9056
Compare
|
Reworked the PR following the feedback from @crosire and @flshy1337. The shared Extension calls made through the ReShade device proxy still get descriptor conversion, while callers that obtain the native device first and query the VKD3D extension interfaces bypass ReShade entirely. I reran the local matrix:
I also rebuilt and retested the change against current upstream @flshy1337, if you have time to rerun your four-way matrix against this version, that would be useful as an independent validation. |
Updated:
ReShade currently hooks the shared vtable of VKD3D-Proton's
ID3D12DeviceExt*interfaces in order to translate ReShade virtual CPU descriptor handles before forwarding CUDA descriptor operations.Since this modifies the native VKD3D extension object's vtable, the hooks also affect callers that intentionally use the original D3D12 device, for example add-ons using
get_native(). Those callers create and pass native descriptor handles, which must not be passed throughconvert_to_original_cpu_descriptor_handle().This can result in either an invalid descriptor heap dereference inside ReShade or a native descriptor being translated through an unrelated ReShade descriptor heap before being passed to VKD3D.
This change replaces the shared
ID3D12DeviceExt*vtable hooks with an extension proxy returned when the interface is queried through ReShade'sD3D12Deviceproxy.The proxy forwards the VKD3D extension interfaces through
ID3D12DeviceExt5and only converts descriptor handles for the descriptor-bearing methods:GetCudaTextureObjectGetCudaSurfaceObjectGetCudaMergedTextureSamplerObjectGetCudaIndependentDescriptorObjectAll other extension methods are forwarded unchanged.
Calls that query an extension interface through ReShade's D3D12 device therefore continue to receive descriptor translation. Calls that obtain the native device first and query the extension interface from that device receive VKD3D's native extension object directly and bypass ReShade.
convert_to_original_cpu_descriptor_handle()remains unchanged.Testing
Tested with a full add-on Release build on D3D12 through VKD3D-Proton using stock DXVK-NVAPI.
Kingdom Come: Deliverance II was tested with four configurations:
EXCEPTION_ACCESS_VIOLATIONduring the NR path.The proxy implementation was also rebuilt and retested against current ReShade
mainwithout regression.ReShade currently assumes that every `D3D12_CPU_DESCRIPTOR_HANDLE` passed to `convert_to_original_cpu_descriptor_handle()` is one of its own virtual descriptor handles.- If the apparent heap index does not refer to a live ReShade heap, ReShade can dereference an invalid/null heap entry.
- If it happens to refer to a live heap, the native descriptor can instead be incorrectly translated and the corrupted descriptor is then passed to the underlying D3D12 implementation.
- The encoded heap index refers to a live ReShade heap.
- The lower metadata bits match that heap's virtual base handle.
- The encoded descriptor offset is within the descriptor heap.
This is not necessarily true for private D3D12 extension interfaces. In particular, the VKD3D-Proton CUDA descriptor extension functions may pass native CPU descriptor handles through ReShade's hooks.
A native descriptor can coincidentally contain bits that decode to an existing ReShade descriptor heap index. The current implementation then treats it as a ReShade virtual handle and translates it using the wrong descriptor heap.
I observed two failure modes while debugging this:
ReShade's virtual CPU descriptor handles already encode enough information to distinguish them more reliably: the heap type/flags are stored in the lower three bits, and the descriptor offset must fall within the referenced heap.
This change therefore only converts a handle when:
Otherwise the original handle is returned unchanged.
Testing
Tested with a full add-on Release build on D3D12 through VKD3D-Proton using DXVK-NVAPI.
The reproducer was a ReShade add-on using VKD3D's
ID3D12DeviceExt2CUDA descriptor interfaces. Before this change, enabling the relevant rendering path reliably caused an access violation. With this change and otherwise stock DXVK-NVAPI, the same path initializes and renders correctly.Existing ReShade virtual descriptor handling also remains functional in the tested workload.