Share via

Correct usage of DisplayTask::SetSignal

Peter Kinsey-James 0 Reputation points
2025-12-20T10:27:58.7433333+00:00

I can't get Windows.Display.Devices.Core DisplayTask::SetSignal to work as documented

Sample application compiles, runs and presents content to my monitor

I have created a DisplayFence by opening a shared handle to a D3D11Fence and passed that in as the parameter to SetSignal for both DisplayTaskSignalKind enums - but in both cases, calling GetCompletedValue or waiting on an Event from the fence fail to ever change the CompletedValue or be signalled

Frame pace my application based on presentation success and I want to be able to predict the next frame time. How is this meant to work?

https://learn.microsoft.com/en-us/windows-hardware/drivers/display/specialized-monitors-compositor

https://learn.microsoft.com/en-us/uwp/api/windows.devices.display.core.displaytask.setsignal?view=winrt-26100#windows-devices-display-core-displaytask-setsignal(windows-devices-display-core-displaytasksignalkind-windows-devices-display-core-displayfence)

Things I have tried:

  • Multiple machines both running Win 11 (24H2 26100.7462)
  • Different GPUs (NVidia RTX 4000 Ada & Nvidia Gefore RTX 4060 Laptop)
  • Latest Nvidia driver
  • Passing in a nullptr DisplayFence
  • Tried both by removing display from desktop in Windows on Enterprise & using a vendor specific block in my device EDID
  • Tried having a separate DX12 Device creating D3D12Fence1 instances and using those as the backing for the DisplayFence
  • Neither OnPresentFlipAway or OnPresentFlipTo ever seem to signal the underlying D3D fence
Windows development | Windows API - Win32
{count} votes

2 answers

Sort by: Most helpful
  1. Peter Kinsey-James 0 Reputation points
    2026-03-13T11:59:30.19+00:00

    I managed to figure this one out eventually.

    Seemingly on nvidia GPU systems the SetSignal API doesn't do anything and instead a bunch of what is done via the WinRT APIs is in fact done by NvAPI. Testing on an intel system the APIs worked as I would have expected

    It does however require a pro-range GPU that supports their PresentBarrier feature

    Rough steps:

    1. Remove the display from desktop into a "Managed" state using https://www.nvidia.com/en-gb/drivers/ddisplay-utility/
    2. Sample App needs to be updated to use Dx12 / 11on12
    3. Enumerate Dedicated displays using NvAPI_DISP_GetNvManagedDedicatedDisplays
    4. Acquire displays NvAPI_DISP_AcquireDedicatedDisplay
    5. Open the shared handle from acquiring the display as IDisplayDeviceInterop and then convert to a winrt::DisplaySource
    6. Get display info for acquired display using NvAPI_Disp_GetDisplayIdInfo
    7. Find the matching winrt Target of that display by using winrt::DisplayManager::GetCurrentTargets() and comparing the NV_DISPLAY_ID_INFO_DATA adapterId, targetId with the DisplayTarget adapter LUID & AdapterRelativeId
    8. Create Primary instances using matched DisplayTarget
    9. Create scanout instances using interopped DisplaySource
    10. Create a present barrier using NvAPI_D3D12_CreateDDisplayPresentBarrierClient DisplaySource::SourceId()
    11. Create D3D12 fences and pass them to NvAPI_D3D12_RegisterPresentBarrierResources
    12. Join the present barrier NvAPI_JoinPresentBarrier
    13. Those fences can now be waited on using SetEventOnCompletion
    14. When the app is done - make sure you release the display NvAPI_DISP_ReleaseDedicatedDisplay

    Microsoft support were unable to give any further answers as to whether this is expected as the D3D team did not respond to their enquiries - but seemingly this is the way it's done on nvidia

    0 comments No comments

  2. Michael Le (WICLOUD CORPORATION) 11,085 Reputation points Microsoft External Staff Moderator
    2025-12-22T09:31:44.1333333+00:00

    Hello @Peter Kinsey-James ,

    The documentation states that the signaled fence value is DisplayTaskResult.PresentId.

    The fence value that is signaled is the value that's returned from DisplayTaskPool.TryExecuteTask in the DisplayTaskResult.PresentId property (it's very probable that it will increase by 1 with each Present).

    SetSignal does not signal a value you choose. If you wait for your own counter (or 1), the fence can look like it never signals.

    Use TryExecuteTask, read PresentId, and wait for that exact value on the underlying D3D fence.

    I hope this clarifies, and happy holidays.

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.