Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce latency with DXGI 1.3 GetFrameLatencyWaitableObject #6435

Merged
9 commits merged into from
Jun 10, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actions/spell-check/dictionary/microsoft.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ SACLs
tdbuildteamid
vcruntime
visualstudio
WINBLUE
beviu marked this conversation as resolved.
Show resolved Hide resolved
7 changes: 7 additions & 0 deletions src/renderer/base/RenderEngineBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
10 changes: 10 additions & 0 deletions src/renderer/base/renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1194,3 +1194,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();
}
}
1 change: 1 addition & 0 deletions src/renderer/base/renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions src/renderer/base/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ DWORD WINAPI RenderThread::_ThreadProc()

ResetEvent(_hPaintCompletedEvent);

_pRenderer->WaitUntilCanRender();
miniksa marked this conversation as resolved.
Show resolved Hide resolved
miniksa marked this conversation as resolved.
Show resolved Hide resolved
LOG_IF_FAILED(_pRenderer->PaintFrame());

SetEvent(_hPaintCompletedEvent);
Expand Down
41 changes: 40 additions & 1 deletion src/renderer/dx/DxRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ DxEngine::DxEngine() :
_glyphCell{},
_boxDrawingEffect{},
_haveDeviceResources{ false },
_swapChainFrameLatencyWaitableObject{ INVALID_HANDLE_VALUE },
_retroTerminalEffects{ false },
_forceFullRepaintRendering{ false },
_softwareRendering{ false },
Expand Down Expand Up @@ -434,6 +435,12 @@ try
SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
SwapChainDesc.Scaling = DXGI_SCALING_NONE;

// requires DXGI 1.3 which was introduced in Windows 8.1
if (IsWindows8Point1OrGreater())
{
SwapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
}

switch (_chainMode)
{
case SwapChainMode::ForHwnd:
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -612,6 +633,7 @@ void DxEngine::_ReleaseDeviceResources() noexcept

_dxgiSurface.Reset();
_dxgiSwapChain.Reset();
_swapChainFrameLatencyWaitableObject.reset();

if (nullptr != _d3dDeviceContext.Get())
{
Expand Down Expand Up @@ -960,7 +982,8 @@ 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));
const UINT swapChainFlags = IsWindows8Point1OrGreater() ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0;
beviu marked this conversation as resolved.
Show resolved Hide resolved
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.
Expand Down Expand Up @@ -1085,6 +1108,22 @@ 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;
}

WaitForSingleObjectEx(
miniksa marked this conversation as resolved.
Show resolved Hide resolved
_swapChainFrameLatencyWaitableObject.get(),
1000, // 1 second timeout (shouldn't ever occur)
true);
}

// 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.
Expand Down
4 changes: 4 additions & 0 deletions src/renderer/dx/DxRenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <dxgi.h>
#include <dxgi1_2.h>
beviu marked this conversation as resolved.
Show resolved Hide resolved
#include <dxgi1_3.h>

#include <d3d11.h>
#include <d2d1.h>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -181,6 +184,7 @@ namespace Microsoft::Console::Render
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> _d2dBrushForeground;
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> _d2dBrushBackground;
::Microsoft::WRL::ComPtr<IDXGISwapChain1> _dxgiSwapChain;
wil::unique_handle _swapChainFrameLatencyWaitableObject;

// Terminal effects resources.
bool _retroTerminalEffects;
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/inc/IRenderEngine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/renderer/inc/IRenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions src/renderer/inc/RenderEngineBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down