1 #include "ppsspp_config.h"
2
3 #include "Common/CommonWindows.h"
4 #include <d3d11.h>
5 #include <WinError.h>
6
7 #include "Common/Log.h"
8 #include "Common/System/Display.h"
9 #include "Common/Data/Encoding/Utf8.h"
10 #include "Common/Data/Text/I18n.h"
11
12 #include "Core/Config.h"
13 #include "Core/ConfigValues.h"
14 #include "Core/Reporting.h"
15 #include "Core/System.h"
16 #include "Windows/GPU/D3D11Context.h"
17 #include "Windows/W32Util/Misc.h"
18 #include "Common/GPU/thin3d.h"
19 #include "Common/GPU/thin3d_create.h"
20 #include "Common/GPU/D3D11/D3D11Loader.h"
21
22 #ifdef __MINGW32__
23 #undef __uuidof
24 #define __uuidof(type) IID_##type
25 #endif
26
27 #ifndef DXGI_ERROR_NOT_FOUND
28 #define _FACDXGI 0x87a
29 #define MAKE_DXGI_HRESULT(code) MAKE_HRESULT(1, _FACDXGI, code)
30 #define DXGI_ERROR_NOT_FOUND MAKE_DXGI_HRESULT(2)
31 #endif
32
33 #if PPSSPP_PLATFORM(UWP)
34 #error This file should not be compiled for UWP.
35 #endif
36
D3D11Context()37 D3D11Context::D3D11Context() : draw_(nullptr), adapterId(-1), hDC(nullptr), hWnd_(nullptr), hD3D11(nullptr) {
38 }
39
~D3D11Context()40 D3D11Context::~D3D11Context() {
41 }
42
SwapBuffers()43 void D3D11Context::SwapBuffers() {
44 swapChain_->Present(swapInterval_, 0);
45 draw_->HandleEvent(Draw::Event::PRESENTED, 0, 0, nullptr, nullptr);
46 }
47
SwapInterval(int interval)48 void D3D11Context::SwapInterval(int interval) {
49 swapInterval_ = interval;
50 }
51
CreateTheDevice(IDXGIAdapter * adapter)52 HRESULT D3D11Context::CreateTheDevice(IDXGIAdapter *adapter) {
53 bool windowed = true;
54 // D3D11 has no need for display rotation.
55 g_display_rotation = DisplayRotation::ROTATE_0;
56 g_display_rot_matrix.setIdentity();
57 #if defined(_DEBUG) && !PPSSPP_ARCH(ARM) && !PPSSPP_ARCH(ARM64)
58 UINT createDeviceFlags = D3D11_CREATE_DEVICE_DEBUG;
59 #else
60 UINT createDeviceFlags = 0;
61 #endif
62
63 static const D3D_DRIVER_TYPE driverTypes[] = {
64 D3D_DRIVER_TYPE_HARDWARE,
65 D3D_DRIVER_TYPE_WARP,
66 D3D_DRIVER_TYPE_REFERENCE,
67 };
68 const UINT numDriverTypes = ARRAYSIZE(driverTypes);
69
70 static const D3D_FEATURE_LEVEL featureLevels[] = {
71 D3D_FEATURE_LEVEL_12_1,
72 D3D_FEATURE_LEVEL_12_0,
73 D3D_FEATURE_LEVEL_11_1,
74 D3D_FEATURE_LEVEL_11_0,
75 D3D_FEATURE_LEVEL_10_1,
76 D3D_FEATURE_LEVEL_10_0,
77 };
78 const UINT numFeatureLevels = ARRAYSIZE(featureLevels);
79
80 HRESULT hr = S_OK;
81 D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_UNKNOWN;
82 hr = ptr_D3D11CreateDevice(adapter, driverType, nullptr, createDeviceFlags, (D3D_FEATURE_LEVEL *)featureLevels, numFeatureLevels,
83 D3D11_SDK_VERSION, &device_, &featureLevel_, &context_);
84 if (hr == E_INVALIDARG) {
85 // DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1 so we need to retry without it
86 hr = ptr_D3D11CreateDevice(adapter, driverType, nullptr, createDeviceFlags, (D3D_FEATURE_LEVEL *)&featureLevels[3], numFeatureLevels - 3,
87 D3D11_SDK_VERSION, &device_, &featureLevel_, &context_);
88 }
89 return hr;
90 }
91
GetRes(HWND hWnd,int & xres,int & yres)92 static void GetRes(HWND hWnd, int &xres, int &yres) {
93 RECT rc;
94 GetClientRect(hWnd, &rc);
95 xres = rc.right - rc.left;
96 yres = rc.bottom - rc.top;
97 }
98
Init(HINSTANCE hInst,HWND wnd,std::string * error_message)99 bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) {
100 hWnd_ = wnd;
101 LoadD3D11Error result = LoadD3D11();
102
103 HRESULT hr = E_FAIL;
104 std::vector<std::string> adapterNames;
105 std::string chosenAdapterName;
106 if (result == LoadD3D11Error::SUCCESS) {
107 std::vector<IDXGIAdapter *> adapters;
108 int chosenAdapter = 0;
109 IDXGIFactory* pFactory = nullptr;
110
111 hr = ptr_CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&pFactory);
112 if (SUCCEEDED(hr)) {
113 IDXGIAdapter* pAdapter;
114 for (UINT i = 0; pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; i++) {
115 adapters.push_back(pAdapter);
116 DXGI_ADAPTER_DESC desc;
117 pAdapter->GetDesc(&desc);
118 std::string str = ConvertWStringToUTF8(desc.Description);
119 adapterNames.push_back(str);
120 if (str == g_Config.sD3D11Device) {
121 chosenAdapter = i;
122 }
123 }
124 if (!adapters.empty()) {
125 chosenAdapterName = adapterNames[chosenAdapter];
126 hr = CreateTheDevice(adapters[chosenAdapter]);
127 for (int i = 0; i < (int)adapters.size(); i++) {
128 adapters[i]->Release();
129 }
130 } else {
131 // No adapters found. Trip the error path below.
132 hr = E_FAIL;
133 }
134 pFactory->Release();
135 }
136 }
137
138 if (FAILED(hr)) {
139 const char *defaultError = "Your GPU does not appear to support Direct3D 11.\n\nWould you like to try again using Direct3D 9 instead?";
140 auto err = GetI18NCategory("Error");
141
142 std::wstring error;
143
144 if (result == LoadD3D11Error::FAIL_NO_COMPILER) {
145 error = ConvertUTF8ToWString(err->T("D3D11CompilerMissing", "D3DCompiler_47.dll not found. Please install. Or press Yes to try again using Direct3D9 instead."));
146 } else if (result == LoadD3D11Error::FAIL_NO_D3D11) {
147 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."));
148 }
149
150 error = ConvertUTF8ToWString(err->T("D3D11NotSupported", defaultError));
151 std::wstring title = ConvertUTF8ToWString(err->T("D3D11InitializationError", "Direct3D 11 initialization error"));
152 bool yes = IDYES == MessageBox(hWnd_, error.c_str(), title.c_str(), MB_ICONERROR | MB_YESNO);
153 if (yes) {
154 // Change the config to D3D9 and restart.
155 g_Config.iGPUBackend = (int)GPUBackend::DIRECT3D9;
156 g_Config.sFailedGPUBackends.clear();
157 g_Config.Save("save_d3d9_fallback");
158
159 W32Util::ExitAndRestart();
160 }
161 return false;
162 }
163
164 if (FAILED(device_->QueryInterface(__uuidof (ID3D11Device1), (void **)&device1_))) {
165 device1_ = nullptr;
166 }
167
168 if (FAILED(context_->QueryInterface(__uuidof (ID3D11DeviceContext1), (void **)&context1_))) {
169 context1_ = nullptr;
170 }
171
172 #ifdef _DEBUG
173 if (SUCCEEDED(device_->QueryInterface(__uuidof(ID3D11Debug), (void**)&d3dDebug_))) {
174 if (SUCCEEDED(d3dDebug_->QueryInterface(__uuidof(ID3D11InfoQueue), (void**)&d3dInfoQueue_))) {
175 d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true);
176 d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true);
177 d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, true);
178 }
179 }
180 #endif
181
182 draw_ = Draw::T3DCreateD3D11Context(device_, context_, device1_, context1_, featureLevel_, hWnd_, adapterNames);
183 SetGPUBackend(GPUBackend::DIRECT3D11, chosenAdapterName);
184 bool success = draw_->CreatePresets(); // If we can run D3D11, there's a compiler installed. I think.
185 _assert_msg_(success, "Failed to compile preset shaders");
186
187 int width;
188 int height;
189 GetRes(hWnd_, width, height);
190
191 // Obtain DXGI factory from device (since we used nullptr for pAdapter above)
192 IDXGIFactory1* dxgiFactory = nullptr;
193 IDXGIDevice* dxgiDevice = nullptr;
194 IDXGIAdapter* adapter = nullptr;
195 hr = device_->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgiDevice));
196 if (SUCCEEDED(hr)) {
197 hr = dxgiDevice->GetAdapter(&adapter);
198 if (SUCCEEDED(hr)) {
199 hr = adapter->GetParent(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&dxgiFactory));
200 DXGI_ADAPTER_DESC desc;
201 adapter->GetDesc(&desc);
202 adapter->Release();
203 }
204 dxgiDevice->Release();
205 }
206
207 // DirectX 11.0 systems
208 DXGI_SWAP_CHAIN_DESC sd;
209 ZeroMemory(&sd, sizeof(sd));
210 sd.BufferCount = 1;
211 sd.BufferDesc.Width = width;
212 sd.BufferDesc.Height = height;
213 sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
214 sd.BufferDesc.RefreshRate.Numerator = 60;
215 sd.BufferDesc.RefreshRate.Denominator = 1;
216 sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
217 sd.OutputWindow = hWnd_;
218 sd.SampleDesc.Count = 1;
219 sd.SampleDesc.Quality = 0;
220 sd.Windowed = TRUE;
221
222 hr = dxgiFactory->CreateSwapChain(device_, &sd, &swapChain_);
223 dxgiFactory->MakeWindowAssociation(hWnd_, DXGI_MWA_NO_ALT_ENTER);
224 dxgiFactory->Release();
225
226 GotBackbuffer();
227 return true;
228 }
229
LostBackbuffer()230 void D3D11Context::LostBackbuffer() {
231 draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, width, height, nullptr);
232 bbRenderTargetTex_->Release();
233 bbRenderTargetTex_ = nullptr;
234 bbRenderTargetView_->Release();
235 bbRenderTargetView_ = nullptr;
236 }
237
GotBackbuffer()238 void D3D11Context::GotBackbuffer() {
239 // Create a render target view
240 ID3D11Texture2D* pBackBuffer = nullptr;
241 HRESULT hr = swapChain_->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&bbRenderTargetTex_));
242 if (FAILED(hr))
243 return;
244
245 D3D11_TEXTURE2D_DESC bbDesc{};
246 bbRenderTargetTex_->GetDesc(&bbDesc);
247 width = bbDesc.Width;
248 height = bbDesc.Height;
249
250 hr = device_->CreateRenderTargetView(bbRenderTargetTex_, nullptr, &bbRenderTargetView_);
251 if (FAILED(hr))
252 return;
253 draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, width, height, bbRenderTargetView_, bbRenderTargetTex_);
254 }
255
Resize()256 void D3D11Context::Resize() {
257 LostBackbuffer();
258 int width;
259 int height;
260 GetRes(hWnd_, width, height);
261 swapChain_->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0);
262 GotBackbuffer();
263 }
264
Shutdown()265 void D3D11Context::Shutdown() {
266 LostBackbuffer();
267
268 delete draw_;
269 draw_ = nullptr;
270
271 swapChain_->Release();
272 swapChain_ = nullptr;
273 if (context1_)
274 context1_->Release();
275 if (device1_)
276 device1_->Release();
277 device1_ = nullptr;
278 device_->Release();
279 device_ = nullptr;
280
281 context_->ClearState();
282 context_->Flush();
283 context_->Release();
284 context_ = nullptr;
285
286 #ifdef _DEBUG
287 if (d3dInfoQueue_) {
288 d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, false);
289 d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, false);
290 d3dInfoQueue_->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, false);
291 }
292 if (d3dDebug_) {
293 d3dDebug_->ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY | D3D11_RLDO_DETAIL);
294 d3dDebug_->Release();
295 d3dDebug_ = nullptr;
296 }
297 if (d3dInfoQueue_) {
298 d3dInfoQueue_->Release();
299 d3dInfoQueue_ = nullptr;
300 }
301 #endif
302
303 hWnd_ = nullptr;
304 UnloadD3D11();
305 }
306