Search code examples
direct3d11

How to understand the asynchrony of CopyResource?


        virtual void STDMETHODCALLTYPE CopyResource(
        /* [annotation] */
        _In_ ID3D11Resource *pDstResource,
        /* [annotation] */
        _In_ ID3D11Resource *pSrcResource);
  • I have questions about this function, especially when there are read and write operations immediately on pDstResource, such as the following code, because of asynchronous, Can I ensure that every time pDstResource is a newly rendered image instead of black_color?

  • This suspicion is because before using IDXGIOutputDuplication::AcquireNextFrame to get the first frame and calling CopyResource, it takes a short time to Sleep before you can see that pDstResource is copied (judging from the rendering output).

  • Refer to performance considerations, I still don’t know the principle here, whether it is when WinB wants to access pDstResource_shared, Direct3D will be synchronized internally (that is, executing the CopyResource command)? Worried about misunderstanding.

     for(;;)
     {
         1,  WinA_Clear(black_color)
         2,  WinA_DrawRedTriangleOnTexture(pSrcResource)
         3,   WinA_CopyResource(pDstResource_shared, pSrcResource)
         4,   WinB_Clear(white_color)
         5,   WinB_RenderTexture(pDstResource_shared)
     }
    

Solution

  • Can I ensure that every time pDstResource is a newly rendered image instead of black_color?

    Unlike lower-level APIs like Direct3D 12, in 10 and 11 the GPU driver handles that automatically. Usually does it pretty well.

    If you call CopyResource, and then update the source texture, the new changes won’t be visible in the copy. If you call CopyResource and then read from destination texture somehow (either render or move to system memory), the GPU driver will first wait for the copy to complete.

    However, there’re edge cases when this workflow breaks. One of them is DXGI surface sharing. If you use that mechanism to share textures across graphics APIs, or across processes, it’s possible you’ll get incomplete renders or other artifacts.

    Since one of your textures is named pDstResource_shared, I assume that’s what you’re doing.

    To workaround, wait for the copy to complete before passing the destination texture to someone else through surface sharing. Here’s how.

    1. On startup, create ID3D11Query object, of type D3D11_QUERY_EVENT

    2. After you called ID3D11DeviceContext.CopyResource, call ID3D11DeviceContext.Flush and then ID3D11DeviceContext.End, passing that query.

    3. You now need to wait for ID3D11DeviceContext.GetData method to return S_OK for that query. That happens when the GPU has completed all commands submitted before ID3D11DeviceContext.End, applying all pending updates to all resources.

    Eventually, MS implemented proper way to wait for GPU, use a fence instead of query, ID3D11Fence::SetEventOnCompletion can signal an event once the GPU did the job, then you can put a thread to sleep with WaitForSingleObject or similar. However, this requires recent enough version of Windows 10. Don’t remember which one but I think either Anniversary Update from 2016, or Creators Update from 2017. Queries are way more compatible, they work on all versions of Win10, and even on Windows 7 or 8.