Sharing a fence: a handle that knows when it is closed
D3D12 fences can now be shared through Windows handles inside one process, with the fence kept alive exactly as long as a handle or a fence object refers to it.
What changed
A fence is a counter that the GPU and the CPU use to wait for each other. D3D12 lets a program create a fence as "shared", turn it into a Windows handle with CreateSharedHandle, and turn that handle back into a fence with OpenSharedHandle. Until now the layer returned "not implemented", and one vkd3d-proton test crashed on the missing handle.
Each fence now points at a small reference-counted object holding its Metal counter (a MTLSharedEvent). The handle is a small anonymous or named shared-memory section (a Windows object that can hold a few bytes) recording a marker, the process id and which fence it names. Opening it gives a new fence object over the same counter, with its own reference count, which is what vkd3d-proton's test checks against Windows behaviour.
The interesting part
The hard part is lifetime, not the handle itself. D3D12 keeps the counter alive while any handle is open, even after the program has released every fence object, and it has to release anything waiting on the counter once the last fence and the last handle are gone. CloseHandle gives a DLL no callback. The layer keeps a handle of its own to each section and asks Wine how many handles the object has (NtQueryObject, which Wine answers from wineserver's own count). When the answer is 1, only our copy is left, so the layer drops it along with its reference.
I first looked at Wine's D3DKMT synchronization objects, which vkd3d-proton uses to share Vulkan semaphores. They give a proper shared handle, but their public API stores no data of ours, so a handle could not lead back to our fence. Sharing across processes is still missing: it would need Metal's cross-process event handle moved between two Unix processes, which wineserver cannot carry.
Numbers
- Full run: 783 pass, 26 fail, 104 skip of 913; 0 newly failing, 2 newly passing (api/shared_fence, test_fence_wait_robustness_shared).
- Previous run: 781 pass, 27 fail, 104 skip of 912.
- pass
- fail
- skip
suite · share of each row's total
- vkd3d-proton (+1)460 pass, 17 fail, 100 skip of 577
- vkd3d177 pass, 9 fail, 4 skip of 190
- apps77 pass, 0 fail, 0 skip of 77
- api (+1)65 pass, 0 fail, 0 skip of 65
- samples4 pass, 0 fail, 0 skip of 4