Reduce latency with DXGI 1.3 GetFrameLatencyWaitableObject (#6435)
This pull request reduces input lag, especially with selection, by using `IDXGISwapChain2::GetFrameLatencyWaitableObject`. This is based on the [DXGI 1.3 documentation]. Excerpt from the [DXGI 1.3 improvement list]: > The following functionality has been added in Microsoft DirectX Graphics Infrastructure (DXGI) 1.3, which is included starting in Windows 8.1. Before, during rendering: 1. render frame 2. call `Present` on swap chain: 2.a. blocks until it can present 2.b. meanwhile, selection/text in terminal might have changed, but we're still using the frame that we rendered before blocking 2.c. presents After, during rendering: 1. block until we can present 2. render frame with latest data 3. call `Present` on swap chain: 3.a. present without blocking [DXGI 1.3 documentation]: https://docs.microsoft.com/en-us/windows/uwp/gaming/reduce-latency-with-dxgi-1-3-swap-chains [DXGI 1.3 improvement list]: https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/dxgi-1-3-improvements:
This commit is contained in:
parent
0c93b2ebbe
commit
a921bbfebb
|
@ -40,3 +40,10 @@ HRESULT RenderEngineBase::PrepareRenderInfo(const RenderFrameInfo& /*info*/) noe
|
|||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Blocks until the engine is able to render without blocking.
|
||||
void RenderEngineBase::WaitUntilCanRender() noexcept
|
||||
{
|
||||
// do nothing by default
|
||||
}
|
||||
|
|
|
@ -1200,3 +1200,13 @@ void Renderer::ResetErrorStateAndResume()
|
|||
// because we're not stateful (we could be in the future), all we want to do is reenable painting.
|
||||
EnablePainting();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Blocks until the engines are able to render without blocking.
|
||||
void Renderer::WaitUntilCanRender()
|
||||
{
|
||||
for (const auto pEngine : _rgpEngines)
|
||||
{
|
||||
pEngine->WaitUntilCanRender();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ namespace Microsoft::Console::Render
|
|||
|
||||
void EnablePainting() override;
|
||||
void WaitForPaintCompletionAndDisable(const DWORD dwTimeoutMs) override;
|
||||
void WaitUntilCanRender() override;
|
||||
|
||||
void AddRenderEngine(_In_ IRenderEngine* const pEngine) override;
|
||||
|
||||
|
|
|
@ -201,6 +201,7 @@ DWORD WINAPI RenderThread::_ThreadProc()
|
|||
|
||||
ResetEvent(_hPaintCompletedEvent);
|
||||
|
||||
_pRenderer->WaitUntilCanRender();
|
||||
LOG_IF_FAILED(_pRenderer->PaintFrame());
|
||||
|
||||
SetEvent(_hPaintCompletedEvent);
|
||||
|
|
|
@ -83,6 +83,7 @@ DxEngine::DxEngine() :
|
|||
_glyphCell{},
|
||||
_boxDrawingEffect{},
|
||||
_haveDeviceResources{ false },
|
||||
_swapChainFrameLatencyWaitableObject{ INVALID_HANDLE_VALUE },
|
||||
_retroTerminalEffects{ false },
|
||||
_forceFullRepaintRendering{ false },
|
||||
_softwareRendering{ false },
|
||||
|
@ -425,6 +426,11 @@ try
|
|||
|
||||
if (createSwapChain)
|
||||
{
|
||||
_swapChainFlags = 0;
|
||||
|
||||
// requires DXGI 1.3 which was introduced in Windows 8.1
|
||||
WI_SetFlagIf(_swapChainFlags, DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT, IsWindows8Point1OrGreater());
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 SwapChainDesc = { 0 };
|
||||
SwapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
|
@ -433,6 +439,7 @@ try
|
|||
SwapChainDesc.SampleDesc.Count = 1;
|
||||
SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
|
||||
SwapChainDesc.Scaling = DXGI_SCALING_NONE;
|
||||
SwapChainDesc.Flags = _swapChainFlags;
|
||||
|
||||
switch (_chainMode)
|
||||
{
|
||||
|
@ -487,6 +494,20 @@ try
|
|||
THROW_HR(E_NOTIMPL);
|
||||
}
|
||||
|
||||
if (IsWindows8Point1OrGreater())
|
||||
{
|
||||
::Microsoft::WRL::ComPtr<IDXGISwapChain2> swapChain2;
|
||||
const HRESULT asResult = _dxgiSwapChain.As(&swapChain2);
|
||||
if (SUCCEEDED(asResult))
|
||||
{
|
||||
_swapChainFrameLatencyWaitableObject = wil::unique_handle{ swapChain2->GetFrameLatencyWaitableObject() };
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_HR_MSG(asResult, "Failed to obtain IDXGISwapChain2 from swap chain");
|
||||
}
|
||||
}
|
||||
|
||||
if (_retroTerminalEffects)
|
||||
{
|
||||
const HRESULT hr = _SetupTerminalEffects();
|
||||
|
@ -612,6 +633,7 @@ void DxEngine::_ReleaseDeviceResources() noexcept
|
|||
|
||||
_dxgiSurface.Reset();
|
||||
_dxgiSwapChain.Reset();
|
||||
_swapChainFrameLatencyWaitableObject.reset();
|
||||
|
||||
if (nullptr != _d3dDeviceContext.Get())
|
||||
{
|
||||
|
@ -960,7 +982,7 @@ try
|
|||
_d2dRenderTarget.Reset();
|
||||
|
||||
// Change the buffer size and recreate the render target (and surface)
|
||||
RETURN_IF_FAILED(_dxgiSwapChain->ResizeBuffers(2, clientSize.width<UINT>(), clientSize.height<UINT>(), DXGI_FORMAT_B8G8R8A8_UNORM, 0));
|
||||
RETURN_IF_FAILED(_dxgiSwapChain->ResizeBuffers(2, clientSize.width<UINT>(), clientSize.height<UINT>(), DXGI_FORMAT_B8G8R8A8_UNORM, _swapChainFlags));
|
||||
RETURN_IF_FAILED(_PrepareRenderTarget());
|
||||
|
||||
// OK we made it past the parts that can cause errors. We can release our failure handler.
|
||||
|
@ -1085,6 +1107,26 @@ CATCH_RETURN()
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Blocks until the engine is able to render without blocking.
|
||||
// - See https://docs.microsoft.com/en-us/windows/uwp/gaming/reduce-latency-with-dxgi-1-3-swap-chains.
|
||||
void DxEngine::WaitUntilCanRender() noexcept
|
||||
{
|
||||
if (!_swapChainFrameLatencyWaitableObject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto ret = WaitForSingleObjectEx(
|
||||
_swapChainFrameLatencyWaitableObject.get(),
|
||||
1000, // 1 second timeout (shouldn't ever occur)
|
||||
true);
|
||||
if (ret != WAIT_OBJECT_0)
|
||||
{
|
||||
LOG_WIN32_MSG(ret, "Waiting for swap chain frame latency waitable object returned error or timeout.");
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Takes queued drawing information and presents it to the screen.
|
||||
// - This is separated out so it can be done outside the lock as it's expensive.
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <dxgi.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <dxgi1_3.h>
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <d2d1.h>
|
||||
|
@ -73,6 +74,8 @@ namespace Microsoft::Console::Render
|
|||
|
||||
[[nodiscard]] HRESULT StartPaint() noexcept override;
|
||||
[[nodiscard]] HRESULT EndPaint() noexcept override;
|
||||
|
||||
void WaitUntilCanRender() noexcept override;
|
||||
[[nodiscard]] HRESULT Present() noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT ScrollFrame() noexcept override;
|
||||
|
@ -180,7 +183,9 @@ namespace Microsoft::Console::Render
|
|||
::Microsoft::WRL::ComPtr<ID2D1RenderTarget> _d2dRenderTarget;
|
||||
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> _d2dBrushForeground;
|
||||
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> _d2dBrushBackground;
|
||||
UINT _swapChainFlags;
|
||||
::Microsoft::WRL::ComPtr<IDXGISwapChain1> _dxgiSwapChain;
|
||||
wil::unique_handle _swapChainFrameLatencyWaitableObject;
|
||||
|
||||
// Terminal effects resources.
|
||||
bool _retroTerminalEffects;
|
||||
|
|
|
@ -49,6 +49,8 @@ namespace Microsoft::Console::Render
|
|||
public:
|
||||
[[nodiscard]] virtual HRESULT StartPaint() noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT EndPaint() noexcept = 0;
|
||||
|
||||
virtual void WaitUntilCanRender() noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT Present() noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept = 0;
|
||||
|
|
|
@ -58,6 +58,7 @@ namespace Microsoft::Console::Render
|
|||
|
||||
virtual void EnablePainting() = 0;
|
||||
virtual void WaitForPaintCompletionAndDisable(const DWORD dwTimeoutMs) = 0;
|
||||
virtual void WaitUntilCanRender() = 0;
|
||||
|
||||
virtual void AddRenderEngine(_In_ IRenderEngine* const pEngine) = 0;
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@ namespace Microsoft::Console::Render
|
|||
|
||||
[[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override;
|
||||
|
||||
void WaitUntilCanRender() noexcept override;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] virtual HRESULT _DoUpdateTitle(const std::wstring& newTitle) noexcept = 0;
|
||||
|
||||
|
|
Loading…
Reference in a new issue