diff options
| author | Stenzek <stenzek@gmail.com> | 2019-03-09 23:31:35 +1000 |
|---|---|---|
| committer | Stenzek <stenzek@gmail.com> | 2019-03-29 19:52:38 +1000 |
| commit | ea15080d8f59a62ba347ff4c6037116035bc69b4 (patch) | |
| tree | 9f05be3e6fbafc9f5a7dadab45865f51eba034e5 /Source/Core/VideoBackends/D3DCommon/SwapChain.cpp | |
| parent | f6641b7e4fd666a620843f1a3b5d5117a1397f9c (diff) | |
Add D3DCommon (shared code between D3D11 and D3D12)
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 |
