1 #include "ppsspp_config.h"
2 
3 #include "Common/CommonWindows.h"
4 #include <d3d9.h>
5 
6 #include "Common/GPU/D3D9/D3D9StateCache.h"
7 
8 #include "Common/System/Display.h"
9 #include "Common/Data/Encoding/Utf8.h"
10 #include "Common/Data/Text/I18n.h"
11 #include "Common/Log.h"
12 #include "Common/OSVersion.h"
13 
14 #include "Core/Config.h"
15 #include "Core/ConfigValues.h"
16 #include "Core/Reporting.h"
17 #include "Core/System.h"
18 #include "Common/OSVersion.h"
19 #include "Windows/GPU/D3D9Context.h"
20 #include "Windows/W32Util/Misc.h"
21 #include "Common/GPU/thin3d.h"
22 #include "Common/GPU/thin3d_create.h"
23 #include "Common/GPU/D3D9/D3DCompilerLoader.h"
24 
SwapBuffers()25 void D3D9Context::SwapBuffers() {
26 	if (has9Ex_) {
27 		deviceEx_->EndScene();
28 		deviceEx_->PresentEx(NULL, NULL, NULL, NULL, 0);
29 		deviceEx_->BeginScene();
30 	} else {
31 		device_->EndScene();
32 		device_->Present(NULL, NULL, NULL, NULL);
33 		device_->BeginScene();
34 	}
35 }
36 
37 typedef HRESULT (__stdcall *DIRECT3DCREATE9EX)(UINT, IDirect3D9Ex**);
38 
GetRes(HWND hWnd,int & xres,int & yres)39 static void GetRes(HWND hWnd, int &xres, int &yres) {
40 	RECT rc;
41 	GetClientRect(hWnd, &rc);
42 	xres = rc.right - rc.left;
43 	yres = rc.bottom - rc.top;
44 }
45 
SwapInterval(int interval)46 void D3D9Context::SwapInterval(int interval) {
47 	swapInterval_ = interval;
48 }
49 
Init(HINSTANCE hInst,HWND wnd,std::string * error_message)50 bool D3D9Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) {
51 	bool windowed = true;
52 	hWnd_ = wnd;
53 
54 	// D3D9 has no need for display rotation.
55 	g_display_rotation = DisplayRotation::ROTATE_0;
56 	g_display_rot_matrix.setIdentity();
57 
58 	DIRECT3DCREATE9EX g_pfnCreate9ex;
59 
60 	hD3D9_ = LoadLibrary(TEXT("d3d9.dll"));
61 	if (!hD3D9_) {
62 		ERROR_LOG(G3D, "Missing d3d9.dll");
63 		*error_message = "D3D9.dll missing - try reinstalling DirectX.";
64 		return false;
65 	}
66 
67 	bool result = LoadD3DCompilerDynamic();
68 	if (!result) {
69 		*error_message = "D3DCompiler not found! Try reinstalling DirectX.";
70 		return false;
71 	}
72 
73 	g_pfnCreate9ex = (DIRECT3DCREATE9EX)GetProcAddress(hD3D9_, "Direct3DCreate9Ex");
74 	has9Ex_ = (g_pfnCreate9ex != NULL) && IsVistaOrHigher();
75 
76 	if (has9Ex_) {
77 		HRESULT result = g_pfnCreate9ex(D3D_SDK_VERSION, &d3dEx_);
78 		d3d_ = d3dEx_;
79 		if (FAILED(result)) {
80 			FreeLibrary(hD3D9_);
81 			*error_message = "D3D9Ex available but context creation failed. Try reinstalling DirectX.";
82 			return false;
83 		}
84 	} else {
85 		d3d_ = Direct3DCreate9(D3D_SDK_VERSION);
86 		if (!d3d_) {
87 			FreeLibrary(hD3D9_);
88 			*error_message = "Failed to create D3D9 context. Try reinstalling DirectX.";
89 			return false;
90 		}
91 	}
92 	adapterId_ = D3DADAPTER_DEFAULT;
93 
94 	D3DCAPS9 d3dCaps;
95 
96 	D3DDISPLAYMODE d3ddm;
97 	if (FAILED(d3d_->GetAdapterDisplayMode(adapterId_, &d3ddm))) {
98 		*error_message = "GetAdapterDisplayMode failed";
99 		d3d_->Release();
100 		return false;
101 	}
102 
103 	if (FAILED(d3d_->GetDeviceCaps(adapterId_, D3DDEVTYPE_HAL, &d3dCaps))) {
104 		*error_message = "GetDeviceCaps failed (?)";
105 		d3d_->Release();
106 		return false;
107 	}
108 
109 	HRESULT hr;
110 	if (FAILED(hr = d3d_->CheckDeviceFormat(adapterId_,
111 		D3DDEVTYPE_HAL,
112 		d3ddm.Format,
113 		D3DUSAGE_DEPTHSTENCIL,
114 		D3DRTYPE_SURFACE,
115 		D3DFMT_D24S8))) {
116 		if (hr == D3DERR_NOTAVAILABLE) {
117 			*error_message = "D24S8 depth/stencil not available";
118 			d3d_->Release();
119 			return false;
120 		}
121 	}
122 
123 	DWORD dwBehaviorFlags = D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE;
124 	if (d3dCaps.VertexProcessingCaps != 0)
125 		dwBehaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
126 	else
127 		dwBehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
128 
129 	int xres, yres;
130 	GetRes(hWnd_, xres, yres);
131 
132 	presentParams_ = {};
133 	presentParams_.BackBufferWidth = xres;
134 	presentParams_.BackBufferHeight = yres;
135 	presentParams_.BackBufferFormat = d3ddm.Format;
136 	presentParams_.MultiSampleType = D3DMULTISAMPLE_NONE;
137 	presentParams_.SwapEffect = D3DSWAPEFFECT_DISCARD;
138 	presentParams_.Windowed = windowed;
139 	presentParams_.hDeviceWindow = wnd;
140 	presentParams_.EnableAutoDepthStencil = true;
141 	presentParams_.AutoDepthStencilFormat = D3DFMT_D24S8;
142 	presentParams_.PresentationInterval = swapInterval_ == 1 ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
143 
144 	if (has9Ex_) {
145 		if (windowed && IsWin7OrHigher()) {
146 			// This "new" flip mode should give higher performance but doesn't.
147 			//pp.BackBufferCount = 2;
148 			//pp.SwapEffect = D3DSWAPEFFECT_FLIPEX;
149 		}
150 		hr = d3dEx_->CreateDeviceEx(adapterId_, D3DDEVTYPE_HAL, wnd, dwBehaviorFlags, &presentParams_, NULL, &deviceEx_);
151 		device_ = deviceEx_;
152 	} else {
153 		hr = d3d_->CreateDevice(adapterId_, D3DDEVTYPE_HAL, wnd, dwBehaviorFlags, &presentParams_, &device_);
154 	}
155 
156 	if (FAILED(hr)) {
157 		*error_message = "Failed to create D3D device";
158 		d3d_->Release();
159 		return false;
160 	}
161 
162 	device_->BeginScene();
163 	DX9::pD3Ddevice = device_;
164 	DX9::pD3DdeviceEx = deviceEx_;
165 
166 	if (deviceEx_ && IsWin7OrHigher()) {
167 		// TODO: This makes it slower?
168 		//deviceEx->SetMaximumFrameLatency(1);
169 	}
170 	draw_ = Draw::T3DCreateDX9Context(d3d_, d3dEx_, adapterId_, device_, deviceEx_);
171 	SetGPUBackend(GPUBackend::DIRECT3D9);
172 	if (!draw_->CreatePresets()) {
173 		// Shader compiler not installed? Return an error so we can fall back to GL.
174 		device_->Release();
175 		d3d_->Release();
176 		*error_message = "DirectX9 runtime not correctly installed. Please install.";
177 		return false;
178 	}
179 	if (draw_)
180 		draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, 0, 0, nullptr);
181 	return true;
182 }
183 
Resize()184 void D3D9Context::Resize() {
185 	// This should only be called from the emu thread.
186 	int xres, yres;
187 	GetRes(hWnd_, xres, yres);
188 	uint32_t newInterval = swapInterval_ == 1 ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;;
189 	bool w_changed = presentParams_.BackBufferWidth != xres;
190 	bool h_changed = presentParams_.BackBufferHeight != yres;
191 	bool i_changed = presentParams_.PresentationInterval != newInterval;
192 
193 	if (device_ && (w_changed || h_changed || i_changed)) {
194 		draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, 0, 0, nullptr);
195 		presentParams_.BackBufferWidth = xres;
196 		presentParams_.BackBufferHeight = yres;
197 		presentParams_.PresentationInterval = newInterval;
198 		HRESULT hr = device_->Reset(&presentParams_);
199 		if (FAILED(hr)) {
200 			// Had to remove DXGetErrorStringA calls here because dxerr.lib is deprecated and will not link with VS 2015.
201 			_assert_msg_(false, "Unable to reset D3D9 device");
202 		}
203 		draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, 0, 0, nullptr);
204 	}
205 }
206 
Shutdown()207 void D3D9Context::Shutdown() {
208 	draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, 0, 0, nullptr);
209 	delete draw_;
210 	draw_ = nullptr;
211 	device_->EndScene();
212 	device_->Release();
213 	d3d_->Release();
214 	UnloadD3DCompiler();
215 	DX9::pD3Ddevice = nullptr;
216 	DX9::pD3DdeviceEx = nullptr;
217 	device_ = nullptr;
218 	hWnd_ = nullptr;
219 	FreeLibrary(hD3D9_);
220 	hD3D9_ = nullptr;
221 }
222