1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of OGRE
4 (Object-oriented Graphics Rendering Engine)
5 For the latest info, see http://www.ogre3d.org/
6 
7 Copyright (c) 2000-2013 Torus Knot Software Ltd
8 
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15 
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 -----------------------------------------------------------------------------
27 */
28 #include "OgreD3D9DeviceManager.h"
29 #include "OgreD3D9Device.h"
30 #include "OgreD3D9RenderSystem.h"
31 #include "OgreD3D9RenderWindow.h"
32 #include "OgreD3D9Driver.h"
33 #include "OgreD3D9DriverList.h"
34 #include "OgreRoot.h"
35 
36 namespace Ogre
37 {
38 	//---------------------------------------------------------------------
D3D9DeviceManager()39 	D3D9DeviceManager::D3D9DeviceManager()
40 	{
41 		mActiveDevice = NULL;
42 		mActiveRenderWindowDevice = NULL;
43 	}
44 
45 	//---------------------------------------------------------------------
~D3D9DeviceManager()46 	D3D9DeviceManager::~D3D9DeviceManager()
47 	{
48 		DeviceIterator itDevice = mRenderDevices.begin();
49 		while (mRenderDevices.size() > 0)
50 		{
51 			mRenderDevices[0]->destroy();
52 		}
53 
54 		mActiveDevice = NULL;
55 		mActiveRenderWindowDevice = NULL;
56 	}
57 
58 	//---------------------------------------------------------------------
setActiveDevice(D3D9Device * device)59 	void D3D9DeviceManager::setActiveDevice(D3D9Device* device)
60 	{
61 		if (mActiveDevice != device)
62 		{
63 			mActiveDevice = device;
64 
65 			D3D9RenderSystem*	renderSystem = static_cast<D3D9RenderSystem*>(Root::getSingleton().getRenderSystem());
66 			D3D9DriverList*		driverList	 = renderSystem->getDirect3DDrivers();
67 
68 			// Update the active driver member.
69 			for (uint i=0; i < driverList->count(); ++i)
70 			{
71 				D3D9Driver* currDriver = driverList->item(i);
72 				if (currDriver->getAdapterNumber() == mActiveDevice->getAdapterNumber())
73 				{
74 					renderSystem->mActiveD3DDriver = currDriver;
75 					break;
76 				}
77 			}
78 
79 			// Invalidate active view port.
80 			renderSystem->mActiveViewport = NULL;
81 		}
82 	}
83 
84 	//---------------------------------------------------------------------
getActiveDevice()85 	D3D9Device* D3D9DeviceManager::getActiveDevice()
86 	{
87 		if (mActiveDevice == NULL)
88 		{
89 			OGRE_EXCEPT( Exception::ERR_INVALIDPARAMS,
90 				"Current active device is NULL !!!",
91 				"D3D9RenderSystem::getActiveDevice" );
92 		}
93 
94 		return mActiveDevice;
95 	}
96 
97 	//---------------------------------------------------------------------
setActiveRenderTargetDevice(D3D9Device * device)98 	void D3D9DeviceManager::setActiveRenderTargetDevice(D3D9Device* device)
99 	{
100 		mActiveRenderWindowDevice = device;
101 		if (mActiveRenderWindowDevice != NULL)
102 			setActiveDevice(mActiveRenderWindowDevice);
103 	}
104 
105 	//---------------------------------------------------------------------
getActiveRenderTargetDevice()106 	D3D9Device* D3D9DeviceManager::getActiveRenderTargetDevice()
107 	{
108 		return mActiveRenderWindowDevice;
109 	}
110 
111 	//---------------------------------------------------------------------
getDeviceCount()112 	UINT D3D9DeviceManager::getDeviceCount()
113 	{
114 		return static_cast<UINT>(mRenderDevices.size());
115 	}
116 
117 	//---------------------------------------------------------------------
getDevice(UINT index)118 	D3D9Device* D3D9DeviceManager::getDevice(UINT index)
119 	{
120 		return mRenderDevices[index];
121 	}
122 
123 	//---------------------------------------------------------------------
linkRenderWindow(D3D9RenderWindow * renderWindow)124 	void D3D9DeviceManager::linkRenderWindow(D3D9RenderWindow* renderWindow)
125 	{
126 		D3D9Device* renderDevice;
127 
128 		// Detach from previous device.
129 		renderDevice = renderWindow->getDevice();
130 		if (renderDevice != NULL)
131 			renderDevice->detachRenderWindow(renderWindow);
132 
133 		D3D9RenderWindowList renderWindowsGroup;
134 
135 		// Select new device for this window.
136 		renderDevice = selectDevice(renderWindow, renderWindowsGroup);
137 
138 		// Link the windows group to the new device.
139 		for (uint i = 0; i < renderWindowsGroup.size(); ++i)
140 		{
141 			D3D9RenderWindow* currWindow = renderWindowsGroup[i];
142 
143 			currWindow->setDevice(renderDevice);
144 			renderDevice->attachRenderWindow(currWindow);
145 			renderDevice->setAdapterOrdinalIndex(currWindow, i);
146 		}
147 
148 		renderDevice->acquire();
149 		if (mActiveDevice == NULL)
150 			setActiveDevice(renderDevice);
151 	}
152 
153 	//---------------------------------------------------------------------
selectDevice(D3D9RenderWindow * renderWindow,D3D9RenderWindowList & renderWindowsGroup)154 	D3D9Device* D3D9DeviceManager::selectDevice(D3D9RenderWindow* renderWindow, D3D9RenderWindowList& renderWindowsGroup)
155 	{
156 		D3D9RenderSystem*		renderSystem	 = static_cast<D3D9RenderSystem*>(Root::getSingleton().getRenderSystem());
157 		D3D9Device*				renderDevice	 = NULL;
158 		IDirect3D9*				direct3D9	     = D3D9RenderSystem::getDirect3D9();
159 		UINT					nAdapterOrdinal  = D3DADAPTER_DEFAULT;
160 		D3DDEVTYPE				devType			 = D3DDEVTYPE_HAL;
161 		DWORD					extraFlags		 = 0;
162 		D3D9DriverList*			driverList = renderSystem->getDirect3DDrivers();
163 		bool					nvAdapterFound = false;
164 
165 
166 		// Default group includes at least the given render window.
167 		renderWindowsGroup.push_back(renderWindow);
168 
169 		// Case we use nvidia performance HUD, override the device settings.
170 		if (renderWindow->isNvPerfHUDEnable())
171 		{
172 			// Look for 'NVIDIA NVPerfHUD' adapter (<= v4)
173 			// or 'NVIDIA PerfHUD' (v5)
174 			// If it is present, override default settings
175 			for (UINT adapter=0; adapter < direct3D9->GetAdapterCount(); ++adapter)
176 			{
177 				D3D9Driver* currDriver = driverList->item(adapter);
178 				const D3DADAPTER_IDENTIFIER9& currAdapterIdentifier = currDriver->getAdapterIdentifier();
179 
180 				if(strstr(currAdapterIdentifier.Description, "PerfHUD") != NULL)
181 				{
182 					renderDevice = NULL;
183 					nAdapterOrdinal = adapter;
184 					renderSystem->mActiveD3DDriver = currDriver;
185 					devType = D3DDEVTYPE_REF;
186 					nvAdapterFound = true;
187 					break;
188 				}
189 			}
190 		}
191 
192 		// No special adapter should be used.
193 		if (nvAdapterFound == false)
194 		{
195 			renderSystem->mActiveD3DDriver = findDriver(renderWindow);
196 			nAdapterOrdinal = renderSystem->mActiveD3DDriver->getAdapterNumber();
197 
198 			bool bTryUsingMultiheadDevice = false;
199 
200 			if (renderWindow->isFullScreen())
201 			{
202 				bTryUsingMultiheadDevice = true;
203 				if (renderSystem->getMultiheadUse() == D3D9RenderSystem::mutAuto)
204 				{
205 					OSVERSIONINFO osVersionInfo;
206 
207 					osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
208 
209 					// Case version info failed -> assume we run on XP.
210 					if (FALSE == GetVersionEx(&osVersionInfo))
211 					{
212 						osVersionInfo.dwMajorVersion = 5;
213 					}
214 
215 					// XP and below - multi-head will cause artifacts when vsync is on.
216 					if (osVersionInfo.dwMajorVersion <= 5 && renderWindow->isVSync())
217 					{
218 						bTryUsingMultiheadDevice = false;
219 						LogManager::getSingleton().logMessage("D3D9 : Multi head disabled. It causes horizontal line when used in XP + VSync combination");
220 					}
221 
222 					// Vista and SP1 or SP2 - multi-head device can not be reset - it causes memory corruption.
223 					if (osVersionInfo.dwMajorVersion == 6 &&
224 						(_stricmp(osVersionInfo.szCSDVersion, "Service Pack 1") == 0 ||
225 						 _stricmp(osVersionInfo.szCSDVersion, "Service Pack 2") == 0))
226 
227 					{
228 						bTryUsingMultiheadDevice = false;
229 						LogManager::getSingleton().logMessage("D3D9 : Multi head disabled. It causes application run time crashes when used in Vista + SP 1 or 2 combination");
230 					}
231 				}
232 				else
233 				{
234 					bTryUsingMultiheadDevice = renderSystem->getMultiheadUse() == D3D9RenderSystem::mutYes ? true : false;
235 				}
236 			}
237 
238 
239 			// Check if we can create a group of render windows
240 			// on the same device using the multi-head feature.
241 			if (bTryUsingMultiheadDevice)
242 			{
243 				const D3DCAPS9& targetAdapterCaps = renderSystem->mActiveD3DDriver->getD3D9DeviceCaps();
244 				D3DCAPS9        masterAdapterCaps;
245 
246 				// Find the master device caps.
247 				if (targetAdapterCaps.MasterAdapterOrdinal == targetAdapterCaps.AdapterOrdinal)
248 				{
249 					masterAdapterCaps = targetAdapterCaps;
250 				}
251 				else
252 				{
253 					for (uint i = 0; i < driverList->count(); ++i)
254 					{
255 						D3D9Driver* currDriver = driverList->item(i);
256 						const D3DCAPS9& currDeviceCaps = currDriver->getD3D9DeviceCaps();
257 
258 						if (currDeviceCaps.AdapterOrdinal == targetAdapterCaps.MasterAdapterOrdinal)
259 						{
260 							masterAdapterCaps = currDeviceCaps;
261 							break;
262 						}
263 					}
264 				}
265 
266 				// Case the master adapter can handle multiple adapters.
267 				if (masterAdapterCaps.NumberOfAdaptersInGroup > 1)
268 				{
269 					// Create empty list of render windows composing this group.
270 					renderWindowsGroup.resize(masterAdapterCaps.NumberOfAdaptersInGroup);
271 					for (uint i = 0; i < renderWindowsGroup.size(); ++i)
272 						renderWindowsGroup[i] = NULL;
273 
274 
275 					// Assign the current render window to it's place in the group.
276 					renderWindowsGroup[targetAdapterCaps.AdapterOrdinalInGroup] = renderWindow;
277 
278 
279 					// For each existing window - check if it belongs to the group.
280 					for (uint i = 0; i < renderSystem->mRenderWindows.size(); ++i)
281 					{
282 						D3D9RenderWindow* currRenderWindow = renderSystem->mRenderWindows[i];
283 
284 						if (currRenderWindow->isFullScreen())
285 						{
286 							D3D9Driver* currDriver = findDriver(currRenderWindow);
287 							const D3DCAPS9& currDeviceCaps = currDriver->getD3D9DeviceCaps();
288 
289 							if (currDeviceCaps.MasterAdapterOrdinal == masterAdapterCaps.AdapterOrdinal)
290 							{
291 								renderWindowsGroup[currDeviceCaps.AdapterOrdinalInGroup] = currRenderWindow;
292 								break;
293 							}
294 						}
295 					}
296 
297 					bool bDeviceGroupFull = true;
298 
299 
300 					// Check if render windows group is full and ready to be driven by
301 					// the master device.
302 					for (uint i = 0; i < renderWindowsGroup.size(); ++i)
303 					{
304 						// This group misses required window -> go back to default.
305 						if (renderWindowsGroup[i] == NULL)
306 						{
307 							bDeviceGroupFull = false;
308 							renderWindowsGroup.clear();
309 							renderWindowsGroup.push_back(renderWindow);
310 							break;
311 						}
312 					}
313 
314 					// Case device group is full -> we can use multi head device.
315 					if (bDeviceGroupFull)
316 					{
317 						bool validateAllDevices = false;
318 
319 						for (uint i = 0; i < renderWindowsGroup.size(); ++i)
320 						{
321 							D3D9RenderWindow* currRenderWindow = renderWindowsGroup[i];
322 							D3D9Device* currDevice = currRenderWindow->getDevice();
323 
324 							// This is the master window
325 							if (i == 0)
326 							{
327 								// If master device exists - just release it.
328 								if (currDevice != NULL)
329 								{
330 									renderDevice = currDevice;
331 									renderDevice->release();
332 								}
333 							}
334 
335 							// This is subordinate window.
336 							else
337 							{
338 								// If subordinate device exists - destroy it.
339 								if (currDevice != NULL)
340 								{
341 									currDevice->destroy();
342 									validateAllDevices = true;
343 								}
344 							}
345 						}
346 
347 						// In case some device was destroyed - make sure all other devices are valid.
348 						// A possible scenario is that full screen window has been destroyed and it's handle
349 						// was used and the shared focus handle. All other devices used this handle and must be
350 						// recreated using other handles otherwise create device will fail.
351 						if (validateAllDevices)
352 						{
353 							for (uint i = 0; i < mRenderDevices.size(); ++i)
354 								mRenderDevices[i]->validateFocusWindow();
355 						}
356 					}
357 				}
358 			}
359 		}
360 
361 
362 
363 		// Do we want to preserve the FPU mode? Might be useful for scientific apps
364 		ConfigOptionMap& options = renderSystem->getConfigOptions();
365 		ConfigOptionMap::iterator opti = options.find("Floating-point mode");
366 		if (opti != options.end() && opti->second.currentValue == "Consistent")
367 			extraFlags |= D3DCREATE_FPU_PRESERVE;
368 
369 #if OGRE_THREAD_SUPPORT == 1
370 		extraFlags |= D3DCREATE_MULTITHREADED;
371 #endif
372 
373 
374 		// Try to find a matching device from current device list.
375 		if (renderDevice == NULL)
376 		{
377 			for (uint i = 0; i < mRenderDevices.size(); ++i)
378 			{
379 				D3D9Device* currDevice = mRenderDevices[i];
380 
381 				if (currDevice->getAdapterNumber() == nAdapterOrdinal &&
382 					currDevice->getDeviceType() == devType &&
383 					currDevice->isFullScreen() == renderWindow->isFullScreen())
384 				{
385 					renderDevice = currDevice;
386 					break;
387 				}
388 			}
389 		}
390 
391 		// No matching device found -> try reference device type (might have been
392 		// previously created as a fallback, but don't change devType because HAL
393 		// should be preferred on creation)
394 		if (renderDevice == NULL)
395 		{
396 			for (uint i = 0; i < mRenderDevices.size(); ++i)
397 			{
398 				D3D9Device* currDevice = mRenderDevices[i];
399 
400 				if (currDevice->getAdapterNumber() == nAdapterOrdinal &&
401 					currDevice->getDeviceType() == D3DDEVTYPE_REF)
402 				{
403 					renderDevice = currDevice;
404 					break;
405 				}
406 			}
407 		}
408 
409 
410 		// No matching device found -> create new one.
411 		if (renderDevice == NULL)
412 		{
413 			renderDevice = OGRE_NEW D3D9Device(this, nAdapterOrdinal, direct3D9->GetAdapterMonitor(nAdapterOrdinal), devType, extraFlags);
414 			mRenderDevices.push_back(renderDevice);
415 			if (mActiveDevice == NULL)
416 				setActiveDevice(renderDevice);
417 		}
418 
419 		return renderDevice;
420 	}
421 
422 	//-----------------------------------------------------------------------
findDriver(D3D9RenderWindow * renderWindow)423 	D3D9Driver* D3D9DeviceManager::findDriver(D3D9RenderWindow* renderWindow)
424 	{
425 		D3D9RenderSystem*		renderSystem	 = static_cast<D3D9RenderSystem*>(Root::getSingleton().getRenderSystem());
426 		IDirect3D9*				direct3D9	     = D3D9RenderSystem::getDirect3D9();
427 		UINT					nAdapterOrdinal  = D3DADAPTER_DEFAULT;
428 		HMONITOR				hRenderWindowMonitor = NULL;
429 		D3D9DriverList*			driverList = renderSystem->getDirect3DDrivers();
430 
431 		// Find the monitor this render window belongs to.
432 		hRenderWindowMonitor = MonitorFromWindow(renderWindow->getWindowHandle(), MONITOR_DEFAULTTONEAREST);
433 
434 
435 		// Find the matching driver using window monitor handle.
436 		for (uint i = 0; i < driverList->count(); ++i)
437 		{
438 			D3D9Driver* currDriver       = driverList->item(i);
439 			HMONITOR hCurrAdpaterMonitor = direct3D9->GetAdapterMonitor(currDriver->getAdapterNumber());
440 
441 			if (hCurrAdpaterMonitor == hRenderWindowMonitor)
442 			{
443 				return currDriver;
444 			}
445 		}
446 
447 		return NULL;
448 	}
449 
450 	//-----------------------------------------------------------------------
notifyOnDeviceDestroy(D3D9Device * device)451 	void D3D9DeviceManager::notifyOnDeviceDestroy(D3D9Device* device)
452 	{
453 		if (device != NULL)
454 		{
455 			if (device == mActiveDevice)
456 				mActiveDevice = NULL;
457 
458 			DeviceIterator itDevice = mRenderDevices.begin();
459 			while (itDevice != mRenderDevices.end())
460 			{
461 				if (*itDevice == device)
462 				{
463 					OGRE_DELETE device;
464 					mRenderDevices.erase(itDevice);
465 					break;
466 				}
467 				++itDevice;
468 			}
469 
470 			if (mActiveDevice == NULL)
471 			{
472 				DeviceIterator itDevice = mRenderDevices.begin();
473 				if (itDevice != mRenderDevices.end())
474 					mActiveDevice = (*itDevice);
475 			}
476 		}
477 	}
478 
479 	//---------------------------------------------------------------------
getDeviceFromD3D9Device(IDirect3DDevice9 * d3d9Device)480 	D3D9Device*	D3D9DeviceManager::getDeviceFromD3D9Device(IDirect3DDevice9* d3d9Device)
481 	{
482 		DeviceIterator itDevice = mRenderDevices.begin();
483 		while (itDevice != mRenderDevices.end())
484 		{
485 			if ((*itDevice)->getD3D9Device() == d3d9Device)
486 			{
487 				return *itDevice;
488 			}
489 			++itDevice;
490 		}
491 
492 		return NULL;
493 	}
494 
495 	//---------------------------------------------------------------------
destroyInactiveRenderDevices()496 	void D3D9DeviceManager::destroyInactiveRenderDevices()
497 	{
498 		DeviceIterator itDevice = mRenderDevices.begin();
499 		while (itDevice != mRenderDevices.end())
500 		{
501 			if ((*itDevice)->getRenderWindowCount() == 0 &&
502 				(*itDevice)->getLastPresentFrame() + 1 < Root::getSingleton().getNextFrameNumber())
503 			{
504 				if (*itDevice == mActiveRenderWindowDevice)
505 					setActiveRenderTargetDevice(NULL);
506 				(*itDevice)->destroy();
507 				break;
508 			}
509 			++itDevice;
510 		}
511 	}
512 
513 }
514