-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
SwapChain.cpp
254 lines (214 loc) · 7.71 KB
/
SwapChain.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
// 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(IDXGISwapChain* swap_chain)
{
BOOL fs = FALSE;
return SUCCEEDED(swap_chain->GetFullscreenState(&fs, nullptr)) && fs;
}
namespace D3DCommon
{
SwapChain::SwapChain(const WindowSystemInfo& wsi, IDXGIFactory* dxgi_factory, IUnknown* d3d_device)
: m_wsi(wsi), m_dxgi_factory(dxgi_factory), m_d3d_device(d3d_device)
{
}
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;
}
// Try using the Win8 version if available.
Microsoft::WRL::ComPtr<IDXGIFactory2> dxgi_factory2;
HRESULT hr = m_dxgi_factory.As(&dxgi_factory2);
if (SUCCEEDED(hr))
{
m_allow_tearing_supported = IsTearingSupported(dxgi_factory2.Get());
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();
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain1;
hr = dxgi_factory2->CreateSwapChainForHwnd(m_d3d_device.Get(),
static_cast<HWND>(m_wsi.render_surface),
&swap_chain_desc, nullptr, nullptr, &swap_chain1);
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 = dxgi_factory2->CreateSwapChainForHwnd(m_d3d_device.Get(),
static_cast<HWND>(m_wsi.render_surface),
&swap_chain_desc, nullptr, nullptr, &swap_chain1);
}
m_swap_chain = swap_chain1;
}
// Flip-model swapchains aren't supported on Windows 7, so here we fall back to a legacy
// BitBlt-model swapchain. Note that this won't work for DX12, but systems which don't
// support the newer DXGI interface aren't going to support DX12 anyway.
if (FAILED(hr))
{
DXGI_SWAP_CHAIN_DESC desc = {};
desc.BufferDesc.Width = m_width;
desc.BufferDesc.Height = m_height;
desc.BufferDesc.Format = GetDXGIFormatForAbstractFormat(m_texture_format, false);
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = SWAP_CHAIN_BUFFER_COUNT;
desc.OutputWindow = static_cast<HWND>(m_wsi.render_surface);
desc.Windowed = TRUE;
desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
desc.Flags = 0;
m_allow_tearing_supported = false;
hr = m_dxgi_factory->CreateSwapChain(m_d3d_device.Get(), &desc, &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_DESC desc;
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)))
{
m_width = desc.BufferDesc.Width;
m_height = desc.BufferDesc.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