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