Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Windows/GPU/D3D11Context.cpp
5656 views
1
#include "ppsspp_config.h"
2
3
#include "Common/CommonWindows.h"
4
#ifndef __LIBRETRO__ // their build server uses an old SDK
5
#include <dxgi1_5.h>
6
#endif
7
#include <d3d11.h>
8
#include <WinError.h>
9
#include <wrl/client.h>
10
11
#include "Common/Log.h"
12
#include "Common/System/Display.h"
13
#include "Common/Data/Encoding/Utf8.h"
14
#include "Common/Data/Text/I18n.h"
15
#include "Common/OSVersion.h"
16
17
#include "Core/Config.h"
18
#include "Core/ConfigValues.h"
19
#include "Core/System.h"
20
#include "Windows/GPU/D3D11Context.h"
21
#include "Windows/W32Util/Misc.h"
22
#include "Common/GPU/thin3d.h"
23
#include "Common/GPU/thin3d_create.h"
24
#include "Common/GPU/D3D11/D3D11Loader.h"
25
26
#ifndef DXGI_ERROR_NOT_FOUND
27
#define _FACDXGI 0x87a
28
#define MAKE_DXGI_HRESULT(code) MAKE_HRESULT(1, _FACDXGI, code)
29
#define DXGI_ERROR_NOT_FOUND MAKE_DXGI_HRESULT(2)
30
#endif
31
32
#if PPSSPP_PLATFORM(UWP)
33
#error This file should not be compiled for UWP.
34
#endif
35
36
using Microsoft::WRL::ComPtr;
37
38
HRESULT D3D11Context::CreateTheDevice(IDXGIAdapter *adapter) {
39
bool windowed = true;
40
// D3D11 has no need for display rotation.
41
g_display.rotation = DisplayRotation::ROTATE_0;
42
g_display.rot_matrix.setIdentity();
43
#if defined(_DEBUG) && !PPSSPP_ARCH(ARM) && !PPSSPP_ARCH(ARM64)
44
UINT createDeviceFlags = D3D11_CREATE_DEVICE_DEBUG;
45
#else
46
UINT createDeviceFlags = 0;
47
#endif
48
49
static const D3D_FEATURE_LEVEL featureLevels[] = {
50
D3D_FEATURE_LEVEL_12_1,
51
D3D_FEATURE_LEVEL_12_0,
52
D3D_FEATURE_LEVEL_11_1,
53
D3D_FEATURE_LEVEL_11_0,
54
D3D_FEATURE_LEVEL_10_1,
55
D3D_FEATURE_LEVEL_10_0,
56
};
57
const UINT numFeatureLevels = ARRAYSIZE(featureLevels);
58
59
HRESULT hr = S_OK;
60
D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_UNKNOWN;
61
hr = ptr_D3D11CreateDevice(adapter, driverType, nullptr, createDeviceFlags, (D3D_FEATURE_LEVEL *)featureLevels, numFeatureLevels,
62
D3D11_SDK_VERSION, &device_, &featureLevel_, &context_);
63
if (hr == E_INVALIDARG) {
64
// DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1 so we need to retry without it
65
hr = ptr_D3D11CreateDevice(adapter, driverType, nullptr, createDeviceFlags, (D3D_FEATURE_LEVEL *)&featureLevels[3], numFeatureLevels - 3,
66
D3D11_SDK_VERSION, &device_, &featureLevel_, &context_);
67
} else if ((hr == DXGI_ERROR_SDK_COMPONENT_MISSING) && (createDeviceFlags & D3D11_CREATE_DEVICE_DEBUG)) {
68
// Likely no debug device available.
69
// This happens in debug builds if you don't install the Graphics Tools optional feature in Windows 10+.
70
// So, we just retry without the debug flag.
71
WARN_LOG(Log::G3D, "D3D11CreateDevice failed with DXGI_ERROR_SDK_COMPONENT_MISSING, retrying without debug flag.");
72
createDeviceFlags &= ~D3D11_CREATE_DEVICE_DEBUG;
73
hr = ptr_D3D11CreateDevice(adapter, driverType, nullptr, createDeviceFlags, (D3D_FEATURE_LEVEL *)featureLevels, numFeatureLevels,
74
D3D11_SDK_VERSION, &device_, &featureLevel_, &context_);
75
}
76
return hr;
77
}
78
79
bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) {
80
hWnd_ = wnd;
81
LoadD3D11Error result = LoadD3D11();
82
83
HRESULT hr = E_FAIL;
84
std::vector<std::string> adapterNames;
85
std::string chosenAdapterName;
86
if (result == LoadD3D11Error::SUCCESS) {
87
std::vector<ComPtr<IDXGIAdapter>> adapters;
88
int chosenAdapter = 0;
89
ComPtr<IDXGIFactory> pFactory;
90
91
hr = ptr_CreateDXGIFactory(IID_PPV_ARGS(&pFactory));
92
if (SUCCEEDED(hr)) {
93
ComPtr<IDXGIAdapter> pAdapter;
94
for (UINT i = 0; pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; i++) {
95
adapters.push_back(pAdapter);
96
DXGI_ADAPTER_DESC desc;
97
pAdapter->GetDesc(&desc);
98
std::string str = ConvertWStringToUTF8(desc.Description);
99
adapterNames.push_back(str);
100
if (str == g_Config.sD3D11Device) {
101
chosenAdapter = i;
102
}
103
}
104
if (!adapters.empty()) {
105
chosenAdapterName = adapterNames[chosenAdapter];
106
hr = CreateTheDevice(adapters[chosenAdapter].Get());
107
adapters.clear();
108
} else {
109
// No adapters found. Trip the error path below.
110
hr = E_FAIL;
111
}
112
}
113
}
114
115
if (FAILED(hr)) {
116
const char *defaultError = "Your GPU does not appear to support Direct3D 11.\n\nWould you like to try again using OpenGL instead?";
117
auto err = GetI18NCategory(I18NCat::ERRORS);
118
119
std::wstring error;
120
121
if (result == LoadD3D11Error::FAIL_NO_COMPILER) {
122
error = ConvertUTF8ToWString(err->T("D3D11CompilerMissing", "D3DCompiler_47.dll not found. Please install. Or press Yes to try again using OpenGL instead."));
123
} else if (result == LoadD3D11Error::FAIL_NO_D3D11) {
124
error = ConvertUTF8ToWString(err->T("D3D11Missing", "Your operating system version does not include D3D11. Please run Windows Update.\n\nPress Yes to try again using Direct3D9 instead."));
125
}
126
127
error = ConvertUTF8ToWString(err->T("D3D11NotSupported", defaultError));
128
std::wstring title = ConvertUTF8ToWString(err->T("D3D11InitializationError", "Direct3D 11 initialization error"));
129
bool yes = IDYES == MessageBox(hWnd_, error.c_str(), title.c_str(), MB_ICONERROR | MB_YESNO);
130
if (yes) {
131
// Change the config to OpenGL and restart.
132
g_Config.iGPUBackend = (int)GPUBackend::OPENGL;
133
g_Config.sFailedGPUBackends.clear();
134
g_Config.Save("save_d3d9_fallback");
135
136
W32Util::ExitAndRestart();
137
}
138
return false;
139
}
140
141
if (FAILED(device_.As(&device1_))) {
142
device1_ = nullptr;
143
}
144
145
if (FAILED(context_.As(&context1_))) {
146
context1_ = nullptr;
147
}
148
149
#ifdef _DEBUG
150
if (SUCCEEDED(device_.As(&d3dDebug_))) {
151
if (SUCCEEDED(d3dDebug_.As(&d3dInfoQueue_))) {
152
d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true);
153
d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true);
154
d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, true);
155
}
156
}
157
#endif
158
159
160
int width;
161
int height;
162
W32Util::GetWindowRes(hWnd_, &width, &height);
163
164
// Obtain DXGI factory from device (since we used nullptr for pAdapter above)
165
ComPtr<IDXGIFactory1> dxgiFactory;
166
ComPtr<IDXGIDevice> dxgiDevice;
167
hr = device_.As(&dxgiDevice);
168
if (SUCCEEDED(hr)) {
169
ComPtr<IDXGIAdapter> adapter;
170
hr = dxgiDevice->GetAdapter(&adapter);
171
if (SUCCEEDED(hr)) {
172
hr = adapter->GetParent(IID_PPV_ARGS(&dxgiFactory));
173
}
174
}
175
176
// Create the swap chain. Modern features (flip model, tearing) require Windows 10 (DXGI 1.5) or newer.
177
swapChainDesc_.BufferCount = 1;
178
swapChainDesc_.BufferDesc.Width = width;
179
swapChainDesc_.BufferDesc.Height = height;
180
swapChainDesc_.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
181
swapChainDesc_.BufferDesc.RefreshRate.Numerator = 60;
182
swapChainDesc_.BufferDesc.RefreshRate.Denominator = 1;
183
swapChainDesc_.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
184
swapChainDesc_.OutputWindow = hWnd_;
185
swapChainDesc_.SampleDesc.Count = 1;
186
swapChainDesc_.SampleDesc.Quality = 0;
187
swapChainDesc_.Windowed = TRUE;
188
swapChainDesc_.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
189
190
#ifndef __LIBRETRO__ // their build server uses an old SDK
191
// This Win10 check should not be needed, see issue #20877. But it can't hurt, plus the user did report that it helped (which makes little sense
192
// unless there's a broken driver).
193
if (IsWin10OrHigher()) {
194
ComPtr<IDXGIFactory5> dxgiFactory5;
195
hr = dxgiFactory.As(&dxgiFactory5);
196
if (SUCCEEDED(hr)) {
197
swapChainDesc_.BufferCount = 2;
198
swapChainDesc_.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
199
200
BOOL allowTearing = FALSE;
201
hr = dxgiFactory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing));
202
if (SUCCEEDED(hr) && allowTearing) {
203
swapChainDesc_.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
204
}
205
}
206
}
207
#endif
208
209
hr = dxgiFactory->CreateSwapChain(device_.Get(), &swapChainDesc_, &swapChain_);
210
dxgiFactory->MakeWindowAssociation(hWnd_, DXGI_MWA_NO_ALT_ENTER);
211
212
draw_ = Draw::T3DCreateD3D11Context(device_, context_, device1_, context1_, swapChain_, featureLevel_, hWnd_, adapterNames, g_Config.iInflightFrames);
213
SetGPUBackend(GPUBackend::DIRECT3D11, chosenAdapterName);
214
bool success = draw_->CreatePresets(); // If we can run D3D11, there's a compiler installed. I think.
215
_assert_msg_(success, "Failed to compile preset shaders");
216
217
GotBackbuffer();
218
return true;
219
}
220
221
void D3D11Context::LostBackbuffer() {
222
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, width, height, nullptr);
223
bbRenderTargetTex_.Reset();
224
bbRenderTargetView_.Reset();
225
}
226
227
void D3D11Context::GotBackbuffer() {
228
// Create a render target view
229
HRESULT hr = swapChain_->GetBuffer(0, IID_PPV_ARGS(&bbRenderTargetTex_));
230
if (FAILED(hr))
231
return;
232
233
D3D11_TEXTURE2D_DESC bbDesc{};
234
bbRenderTargetTex_->GetDesc(&bbDesc);
235
width = bbDesc.Width;
236
height = bbDesc.Height;
237
238
hr = device_->CreateRenderTargetView(bbRenderTargetTex_.Get(), nullptr, &bbRenderTargetView_);
239
if (FAILED(hr))
240
return;
241
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, width, height, bbRenderTargetView_.Get(), bbRenderTargetTex_.Get());
242
}
243
244
void D3D11Context::Resize() {
245
LostBackbuffer();
246
int width;
247
int height;
248
W32Util::GetWindowRes(hWnd_, &width, &height);
249
swapChain_->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, swapChainDesc_.Flags);
250
GotBackbuffer();
251
}
252
253
void D3D11Context::Shutdown() {
254
LostBackbuffer();
255
256
delete draw_;
257
draw_ = nullptr;
258
259
context_->ClearState();
260
context_->Flush();
261
#ifdef _DEBUG
262
if (d3dInfoQueue_) {
263
d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, false);
264
d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, false);
265
d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, false);
266
}
267
if (d3dDebug_) {
268
d3dDebug_->ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY | D3D11_RLDO_DETAIL);
269
}
270
#endif
271
272
#ifdef _DEBUG
273
d3dDebug_ = nullptr;
274
d3dInfoQueue_ = nullptr;
275
#endif
276
277
// Important that we release before we unload the DLL, otherwise we may crash on shutdown.
278
bbRenderTargetTex_ = nullptr;
279
bbRenderTargetView_ = nullptr;
280
swapChain_ = nullptr;
281
context1_ = nullptr;
282
context_ = nullptr;
283
device1_ = nullptr;
284
device_ = nullptr;
285
hWnd_ = nullptr;
286
287
UnloadD3D11();
288
}
289
290