Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
258 views
in Technique[技术] by (71.8m points)

graphics - What is the correct way to control the frequency of swapchain present call?

I'm new to graphics rendering, and I'm trying to write a win32 drawing app using D2D and D3D11. I use two overlapped offscreen D2D bitmaps to preserve the content of the canvas, the top-level bitmap is transparent.

Whenever a mouse message is received, a line from the last point to the current point will be rendered to the top-level bitmap. Then I will draw both bitmaps by Z-Order to the back buffer of the swap chain then call Present(0, 0).

As you may notice, the present call is event-driven in my design. If the mouse message is received every 1 ms, then in 1 second I will get 1000 polylines rendered on the top-level bitmap(which is good), then 1000 times to composite the two bitmaps, and 1000 times to call Present(which is really bad since I only need to present in, let's say, 60 fps). The redundant composition calls and present calls take the most resources of GPU, and finally Present(0, 0) blocks the UI thread, and the frequency of mouse messages reported is dramatically reduced.

int OnMouseMove(int x, int y)
{
    // ...

    // update top-level bitmap
    DrawLineTo(topLevelBitmap, x, y);

    // get back buffer
    CComPtr<IDXGISurface> dxgiBackBuffer;
    HRESULT hr = _dxgiSwapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer));

    // Draw two bitmaps to the back buffer
    ClearBackBuffer(dxgiBackBuffer);
    DrawBimap(dxgiBackBuffer, backgroundBitmap);
    DrawBimap(dxgiBackBuffer, topLevelBitmap);

    // Present
    DXGI_PRESENT_PARAMETERS parameters = { 0 };
    _dxgiSwapChain->Present1(0, 0, &parameters);

    // ...
}

I tried to find a callback that could be used to trigger present calls, like CVDisplayLink/CADisplayLink on macOS/iOS, or a higher priority timer that could generate reliable callbacks, but failed. (WM_TIMER has a rather low priority so I didn't even try)

Another thought is to create a new thread and call present in a while loop and sleep for 16ms after each present is called. However, I'm not sure if this is a standard way, and I also worry about thread safety.

// in UI thread
int OnMouseMove(int x, int y)
{
    // update top-level bitmap
    DrawLineTo(topLevelBitmap, x, y);
}


// in new thread
while(1)
{
    // get back buffer
    CComPtr<IDXGISurface> dxgiBackBuffer;
    HRESULT hr = _dxgiSwapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer));

    // Draw two bitmaps to the back buffer
    ClearBackBuffer(dxgiBackBuffer);
    DrawBimap(dxgiBackBuffer, backgroundBitmap);
    DrawBimap(dxgiBackBuffer, topLevelBitmap);

    // Present
    DXGI_PRESENT_PARAMETERS parameters = { 0 };
    _dxgiSwapChain->Present1(0, 0, &parameters);

    sleep(16);
}

So my question is what is the proper way to separate the off-screen rendering(draw line) and the on-screen rendering(draw bitmap & present)?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

What you should be doing here is to have a non-blocking event loop using PeekMessage, and then do your rendering on the same thread, after you've gotten all the window events. As for keeping your FPS locked to the monitor's refresh rate, Present's first argument is the sync interval, which should be set to 1, and it will block until the monitor is ready to show the next frame.

For example:

while (isOpen)
{
  // Message loop
  MSG message;
  while (PeekMessage(&message, m_WindowHandle, NULL, NULL, PM_REMOVE))
  {
      TranslateMessage(&message);
      DispatchMessage(&message);
  }

  // Render
  
  DXGI_PRESENT_PARAMETERS parameters = { 0 };
  _dxgiSwapChain->Present1(1, 0, &parameters);
}

Inside your window proc, you should just store the current position of the mouse so that you can then update it once in your render loop.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...