diff options
| author | Connor McLaughlin <stenzek@gmail.com> | 2019-03-29 20:40:06 +1000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-03-29 20:40:06 +1000 |
| commit | f3fadd730218429a5f6472283d16d30d7e276f8d (patch) | |
| tree | d1d6577cd52b2d3b3d7872aef4fac80dbd0ce563 /Source/Core/VideoBackends/D3DCommon/SwapChain.cpp | |
| parent | fbe57e8dbef17e583f0ba0208af7bf414a3716bf (diff) | |
| parent | d0d010f8549cc902829f567b3b5127d9a0556ca5 (diff) | |
Merge pull request #7869 from stenzek/d3dcommon
D3D: Move sharable D3D11/D3D12 code to common library
Diffstat (limited to 'Source/Core/VideoBackends/D3DCommon/SwapChain.cpp')
| -rw-r--r-- | Source/Core/VideoBackends/D3DCommon/SwapChain.cpp | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/Source/Core/VideoBackends/D3DCommon/SwapChain.cpp b/Source/Core/VideoBackends/D3DCommon/SwapChain.cpp new file mode 100644 index 0000000000..376f891a96 --- /dev/null +++ b/Source/Core/VideoBackends/D3DCommon/SwapChain.cpp @@ -0,0 +1,232 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "VideoBackends/D3DCommon/SwapChain.h" + +#include <algorithm> +#include <cstdint> + +#include "Common/Assert.h" +#include "Common/CommonFuncs.h" +#include "Common/Logging/Log.h" +#include "Common/MsgHandler.h" +#include "VideoCommon/VideoConfig.h" + +static bool IsTearingSupported(IDXGIFactory2* dxgi_factory) +{ + Microsoft::WRL::ComPtr<IDXGIFactory5> factory5; + if (FAILED(dxgi_factory->QueryInterface(IID_PPV_ARGS(&factory5)))) + return false; + + UINT allow_tearing = 0; + return SUCCEEDED(factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing, + sizeof(allow_tearing))) && + allow_tearing != 0; +} + +static bool GetFullscreenState(IDXGISwapChain1* swap_chain) +{ + BOOL fs = FALSE; + return SUCCEEDED(swap_chain->GetFullscreenState(&fs, nullptr)) && fs; +} + +namespace D3DCommon +{ +SwapChain::SwapChain(const WindowSystemInfo& wsi, IDXGIFactory2* dxgi_factory, IUnknown* d3d_device) + : m_wsi(wsi), m_dxgi_factory(dxgi_factory), m_d3d_device(d3d_device), + m_allow_tearing_supported(IsTearingSupported(dxgi_factory)) +{ +} + +SwapChain::~SwapChain() +{ + // Can't destroy swap chain while it's fullscreen. + if (m_swap_chain && GetFullscreenState(m_swap_chain.Get())) + m_swap_chain->SetFullscreenState(FALSE, nullptr); +} + +bool SwapChain::WantsStereo() +{ + return g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer; +} + +u32 SwapChain::GetSwapChainFlags() const +{ + // This flag is necessary if we want to use a flip-model swapchain without locking the framerate + return m_allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; +} + +bool SwapChain::CreateSwapChain(bool stereo) +{ + RECT client_rc; + if (GetClientRect(static_cast<HWND>(m_wsi.render_surface), &client_rc)) + { + m_width = client_rc.right - client_rc.left; + m_height = client_rc.bottom - client_rc.top; + } + + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {}; + swap_chain_desc.Width = m_width; + swap_chain_desc.Height = m_height; + swap_chain_desc.BufferCount = SWAP_CHAIN_BUFFER_COUNT; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.SampleDesc.Count = 1; + swap_chain_desc.SampleDesc.Quality = 0; + swap_chain_desc.Format = GetDXGIFormatForAbstractFormat(m_texture_format, false); + swap_chain_desc.Scaling = DXGI_SCALING_STRETCH; + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swap_chain_desc.Stereo = stereo; + swap_chain_desc.Flags = GetSwapChainFlags(); + + HRESULT hr = m_dxgi_factory->CreateSwapChainForHwnd( + m_d3d_device.Get(), static_cast<HWND>(m_wsi.render_surface), &swap_chain_desc, nullptr, + nullptr, &m_swap_chain); + if (FAILED(hr)) + { + // Flip-model discard swapchains aren't supported on Windows 8, so here we fall back to + // a sequential swapchain + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + hr = m_dxgi_factory->CreateSwapChainForHwnd(m_d3d_device.Get(), + static_cast<HWND>(m_wsi.render_surface), + &swap_chain_desc, nullptr, nullptr, &m_swap_chain); + } + + if (FAILED(hr)) + { + // Flip-model swapchains aren't supported on Windows 7, so here we fall back to a legacy + // BitBlt-model swapchain + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + hr = m_dxgi_factory->CreateSwapChainForHwnd(m_d3d_device.Get(), + static_cast<HWND>(m_wsi.render_surface), + &swap_chain_desc, nullptr, nullptr, &m_swap_chain); + } + + if (FAILED(hr)) + { + PanicAlert("Failed to create swap chain with HRESULT %08X", hr); + return false; + } + + // We handle fullscreen ourselves. + hr = m_dxgi_factory->MakeWindowAssociation(static_cast<HWND>(m_wsi.render_surface), + DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER); + if (FAILED(hr)) + WARN_LOG(VIDEO, "MakeWindowAssociation() failed with HRESULT %08X", hr); + + m_stereo = stereo; + if (!CreateSwapChainBuffers()) + { + PanicAlert("Failed to create swap chain buffers"); + DestroySwapChainBuffers(); + m_swap_chain.Reset(); + return false; + } + + return true; +} + +void SwapChain::DestroySwapChain() +{ + DestroySwapChainBuffers(); + + // Can't destroy swap chain while it's fullscreen. + if (m_swap_chain && GetFullscreenState(m_swap_chain.Get())) + m_swap_chain->SetFullscreenState(FALSE, nullptr); + + m_swap_chain.Reset(); +} + +bool SwapChain::ResizeSwapChain() +{ + DestroySwapChainBuffers(); + + HRESULT hr = m_swap_chain->ResizeBuffers(SWAP_CHAIN_BUFFER_COUNT, 0, 0, + GetDXGIFormatForAbstractFormat(m_texture_format, false), + GetSwapChainFlags()); + if (FAILED(hr)) + WARN_LOG(VIDEO, "ResizeBuffers() failed with HRESULT %08X", hr); + + DXGI_SWAP_CHAIN_DESC1 desc; + if (SUCCEEDED(m_swap_chain->GetDesc1(&desc))) + { + m_width = desc.Width; + m_height = desc.Height; + } + + return CreateSwapChainBuffers(); +} + +void SwapChain::SetStereo(bool stereo) +{ + if (m_stereo == stereo) + return; + + DestroySwapChain(); + if (!CreateSwapChain(stereo)) + { + PanicAlert("Failed to switch swap chain stereo mode"); + CreateSwapChain(false); + } +} + +bool SwapChain::GetFullscreen() const +{ + return GetFullscreenState(m_swap_chain.Get()); +} + +void SwapChain::SetFullscreen(bool request) +{ + m_swap_chain->SetFullscreenState(request, nullptr); +} + +bool SwapChain::CheckForFullscreenChange() +{ + if (m_fullscreen_request != m_has_fullscreen) + { + HRESULT hr = m_swap_chain->SetFullscreenState(m_fullscreen_request, nullptr); + if (SUCCEEDED(hr)) + { + m_has_fullscreen = m_fullscreen_request; + return true; + } + } + + const bool new_fullscreen_state = GetFullscreenState(m_swap_chain.Get()); + if (new_fullscreen_state != m_has_fullscreen) + { + m_has_fullscreen = new_fullscreen_state; + m_fullscreen_request = new_fullscreen_state; + return true; + } + + return false; +} + +bool SwapChain::Present() +{ + // When using sync interval 0, it is recommended to always pass the tearing flag when it is + // supported, even when presenting in windowed mode. However, this flag cannot be used if the app + // is in fullscreen mode as a result of calling SetFullscreenState. + UINT present_flags = 0; + if (m_allow_tearing_supported && !g_ActiveConfig.bVSyncActive && !m_has_fullscreen) + present_flags |= DXGI_PRESENT_ALLOW_TEARING; + + HRESULT hr = m_swap_chain->Present(static_cast<UINT>(g_ActiveConfig.bVSyncActive), present_flags); + if (FAILED(hr)) + { + WARN_LOG(VIDEO, "Swap chain present failed with HRESULT %08X", hr); + return false; + } + + return true; +} + +bool SwapChain::ChangeSurface(void* native_handle) +{ + DestroySwapChain(); + m_wsi.render_surface = native_handle; + return CreateSwapChain(m_stereo); +} + +} // namespace D3DCommon |
