1 ////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // Nestopia - NES/Famicom emulator written in C++
4 //
5 // Copyright (C) 2003-2008 Martin Freij
6 //
7 // This file is part of Nestopia.
8 //
9 // Nestopia is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // Nestopia is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with Nestopia; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 //
23 ////////////////////////////////////////////////////////////////////////////////////////
24 
25 #include "language/resource.h"
26 #include "NstResourceString.hpp"
27 #include "NstIoLog.hpp"
28 #include "NstApplicationException.hpp"
29 #include "NstDirect2D.hpp"
30 #include "NstIoScreen.hpp"
31 
32 #if NST_MSVC
33 #pragma comment(lib,"d3d9")
34 #pragma comment(lib,"d3dx9")
35 #endif
36 
37 namespace Nestopia
38 {
39 	namespace DirectX
40 	{
41 		#ifdef NST_MSVC_OPTIMIZE
42 		#pragma optimize("t", on)
43 		#endif
44 
operator IDirect3D9&() const45 		inline Direct2D::Base::operator IDirect3D9& () const
46 		{
47 			return com;
48 		}
49 
operator IDirect3DDevice9&() const50 		inline Direct2D::Device::operator IDirect3DDevice9& () const
51 		{
52 			return **com;
53 		}
54 
CanDraw() const55 		inline bool Direct2D::Device::Fonts::Font::CanDraw() const
56 		{
57 			return length && com;
58 		}
59 
NumVertices() const60 		inline uint Direct2D::VertexBuffer::NumVertices() const
61 		{
62 			return numVertices;
63 		}
64 
NumStrips() const65 		inline uint Direct2D::IndexBuffer::NumStrips() const
66 		{
67 			return numStrips;
68 		}
69 
GetLeftU(uint x) const70 		inline double Direct2D::Textures::ScreenTexture::GetLeftU(uint x) const
71 		{
72 			return double(NST_MIN(size.x,x)) / desc.Width;
73 		}
74 
GetRightU(uint x) const75 		inline double Direct2D::Textures::ScreenTexture::GetRightU(uint x) const
76 		{
77 			return double(NST_MIN(size.x,x)) / desc.Width;
78 		}
79 
GetTopV(uint y) const80 		inline double Direct2D::Textures::ScreenTexture::GetTopV(uint y) const
81 		{
82 			return double(NST_MIN(size.y,y)) / desc.Height;
83 		}
84 
GetBottomV(uint y) const85 		inline double Direct2D::Textures::ScreenTexture::GetBottomV(uint y) const
86 		{
87 			return double(NST_MIN(size.y,y)) / desc.Height;
88 		}
89 
GetLeftU(uint) const90 		inline double Direct2D::Textures::EffectTexture::GetLeftU(uint) const
91 		{
92 			return 0.0;
93 		}
94 
GetRightU(uint) const95 		inline double Direct2D::Textures::EffectTexture::GetRightU(uint) const
96 		{
97 			return 1.0;
98 		}
99 
GetTopV(uint y) const100 		inline double Direct2D::Textures::EffectTexture::GetTopV(uint y) const
101 		{
102 			return double(NST_MIN(size.y,y)) / desc.Height * 2;
103 		}
104 
GetBottomV(uint y) const105 		inline double Direct2D::Textures::EffectTexture::GetBottomV(uint y) const
106 		{
107 			return double(NST_MIN(size.y,y)) / desc.Height * 2;
108 		}
109 
GetScreenLeftU(uint x) const110 		inline double Direct2D::Textures::GetScreenLeftU(uint x) const
111 		{
112 			return screenTexture.GetLeftU(x);
113 		}
114 
GetScreenRightU(uint x) const115 		inline double Direct2D::Textures::GetScreenRightU(uint x) const
116 		{
117 			return screenTexture.GetRightU(x);
118 		}
119 
GetScreenTopV(uint y) const120 		inline double Direct2D::Textures::GetScreenTopV(uint y) const
121 		{
122 			return screenTexture.GetTopV(y);
123 		}
124 
GetScreenBottomV(uint y) const125 		inline double Direct2D::Textures::GetScreenBottomV(uint y) const
126 		{
127 			return screenTexture.GetBottomV(y);
128 		}
129 
GetEffectLeftU(uint x) const130 		inline double Direct2D::Textures::GetEffectLeftU(uint x) const
131 		{
132 			return effectTexture.GetLeftU(x);
133 		}
134 
GetEffectRightU(uint x) const135 		inline double Direct2D::Textures::GetEffectRightU(uint x) const
136 		{
137 			return effectTexture.GetRightU(x);
138 		}
139 
GetEffectTopV(uint y) const140 		inline double Direct2D::Textures::GetEffectTopV(uint y) const
141 		{
142 			return effectTexture.GetTopV(y);
143 		}
144 
GetEffectBottomV(uint y) const145 		inline double Direct2D::Textures::GetEffectBottomV(uint y) const
146 		{
147 			return effectTexture.GetBottomV(y);
148 		}
149 
Draw(const D3DCOLOR color,const DWORD flags,Rect rect) const150 		inline void Direct2D::Device::Fonts::Font::Draw(const D3DCOLOR color,const DWORD flags,Rect rect) const
151 		{
152 			com->DrawText( NULL, string.Ptr(), length, &rect, flags, color );
153 		}
154 
Render(const D3DPRESENT_PARAMETERS & presentation,const uint state) const155 		NST_SINGLE_CALL void Direct2D::Device::Fonts::Render(const D3DPRESENT_PARAMETERS& presentation,const uint state) const
156 		{
157 			const uint width = presentation.BackBufferWidth;
158 			const uint height = presentation.BackBufferHeight;
159 
160 			if (!presentation.Windowed)
161 			{
162 				if ((state & RENDER_FPS) && fps.CanDraw())
163 				{
164 					for (uint i=0; i < 2; ++i)
165 					{
166 						fps.Draw
167 						(
168 							i ? D3DCOLOR_ARGB(0xFF,0xA5,0xB5,0x40) : D3DCOLOR_ARGB(0xFF,0x2A,0x35,0x10),
169 							DT_SINGLELINE|TA_BOTTOM|TA_RIGHT|DT_NOCLIP,
170 							Rect(width-31,height-31,width-i-3,height-i-3)
171 						);
172 					}
173 				}
174 
175 				if ((state & RENDER_MSG) && msg.CanDraw())
176 				{
177 					for (uint i=0; i < 2; ++i)
178 					{
179 						msg.Draw
180 						(
181 							i ? D3DCOLOR_ARGB(0xFF,0xFF,0x20,0x20) : D3DCOLOR_ARGB(0xFF,0x20,0x20,0xA0),
182 							DT_SINGLELINE|TA_BOTTOM|TA_LEFT|DT_NOCLIP,
183 							Rect(4-i,height-31,width,height-i-3)
184 						);
185 					}
186 				}
187 			}
188 
189 			if ((state & RENDER_NFO) && nfo.CanDraw())
190 			{
191 				for (uint i=0; i < 2; ++i)
192 				{
193 					nfo.Draw
194 					(
195 						i ? D3DCOLOR_ARGB(0xFF,0x20,0xFF,0x20) : D3DCOLOR_ARGB(0xFF,0x20,0x60,0x20),
196 						TA_TOP|TA_LEFT|DT_NOCLIP,
197 						Rect(16-i,16-i,width,height)
198 					);
199 				}
200 			}
201 		}
202 
RenderScreen(const uint state,const uint numIndexedStrips,const uint numVertices) const203 		NST_SINGLE_CALL HRESULT Direct2D::Device::RenderScreen(const uint state,const uint numIndexedStrips,const uint numVertices) const
204 		{
205 			HRESULT hResult = com->BeginScene();
206 
207 			if (SUCCEEDED(hResult))
208 			{
209 				if (state & RENDER_PICTURE)
210 				{
211 					if (numIndexedStrips)
212 						hResult = com->DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP, 0, 0, numVertices, 0, numIndexedStrips );
213 					else
214 						hResult = com->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );
215 				}
216 
217 				fonts.Render( presentation, state );
218 				com->EndScene();
219 			}
220 
221 			return hResult;
222 		}
223 
224 		#ifdef NST_MSVC_OPTIMIZE
225 		#pragma optimize("", on)
226 		#endif
227 
Direct2D(HWND hWnd)228 		Direct2D::Direct2D(HWND hWnd)
229 		:
230 		device     ( hWnd, base ),
231 		textures   ( device.GetPresentation().BackBufferFormat ),
232 		lastResult ( D3D_OK )
233 		{
234 			ValidateObjects();
235 		}
236 
~Direct2D()237 		Direct2D::~Direct2D()
238 		{
239 		}
240 
241 		#ifdef NST_MSVC_OPTIMIZE
242 		#pragma optimize("t", on)
243 		#endif
244 
InvalidateObjects()245 		void Direct2D::InvalidateObjects()
246 		{
247 			indexBuffer.Invalidate();
248 			vertexBuffer.Invalidate();
249 			textures.Invalidate();
250 		}
251 
FlushObjects()252 		void Direct2D::FlushObjects()
253 		{
254 			textures.Flush();
255 		}
256 
ValidateObjects()257 		void Direct2D::ValidateObjects()
258 		{
259 			if (SUCCEEDED(lastResult))
260 			{
261 				lastResult = textures.Validate( device, GetAdapter(), device.GetPresentation().BackBufferFormat );
262 
263 				if (SUCCEEDED(lastResult))
264 				{
265 					lastResult = indexBuffer.Validate( device );
266 
267 					if (SUCCEEDED(lastResult))
268 						lastResult = vertexBuffer.Validate( device, textures );
269 				}
270 			}
271 		}
272 
RenderScreen(uint state)273 		void Direct2D::RenderScreen(uint state)
274 		{
275 			if (SUCCEEDED(lastResult))
276 				lastResult = device.RenderScreen( state, indexBuffer.NumStrips(), vertexBuffer.NumVertices() );
277 		}
278 
279 		/**
280 		 * Selects a video adapter and creates a new video device. Selection is ignored if the adapter is already selected.
281 		 *
282 		 * @param adapter The video adapter to select.
283 		 * @throws Application::Exception If an error occured creating or resetting the new video device.
284 		 * @see Application::Exception
285 		 */
SelectAdapter(const Adapters::const_iterator adapter)286 		void Direct2D::SelectAdapter(const Adapters::const_iterator adapter)
287 		{
288 			if (device.GetOrdinal() != adapter->ordinal)
289 			{
290 				InvalidateObjects();
291 
292 				device.Create( base, *adapter );
293 				if (FAILED(device.Reset()))
294 					throw Application::Exception( L"Couldn't reset the new video device!" );
295 
296 				ValidateObjects();
297 				lastResult = D3D_OK;
298 				//lastResult = INVALID_RECT;	//removed when implementing support for multiple monitor adapters
299 			}
300 			else
301 			{
302 				Io::Log() << "Selection of video adapter with ordinal \"" << adapter->ordinal << "\" was ignored as it is already selected.\r\n";
303 			}
304 		}
305 
306 		/**
307 		 * Verifies if a switch to a fullscreen mode can be made or if the mode is already set to fullscreen.
308 		 *
309 		 * @param mode The video mode to set to fullscreen.
310 		 * @return True if switched to the fullscreen mode can be made. False if the mode is already set to fullscreen.
311 		 */
CanSwitchFullscreen(const Adapter::Modes::const_iterator mode) const312 		bool Direct2D::CanSwitchFullscreen(const Adapter::Modes::const_iterator mode) const
313 		{
314 			return device.CanSwitchFullscreen( *mode );
315 		}
316 
317 		/**
318 		 * Switches to a fullscreen mode. Switch is ignored if the specific mode is already set to fullscreen.
319 		 *
320 		 * @param mode The video mode to set to fullscreen.
321 		 * @return True if switched to fullscreen. False if the mode is already set in fullscreen.
322 		 */
SwitchFullscreen(const Adapter::Modes::const_iterator mode)323 		bool Direct2D::SwitchFullscreen(const Adapter::Modes::const_iterator mode)
324 		{
325 			if (CanSwitchFullscreen( mode ))
326 			{
327 				FlushObjects();
328 				device.SwitchFullscreen( *mode );
329 				lastResult = D3D_OK;
330 				ValidateObjects();
331 				return true;
332 			}
333 
334 			return false;
335 		}
336 
337 		/**
338 		 * Switches to window mode. Switch is ignored if already in window mode.
339 		 *
340 		 * @return True if switched to window mode. False if the mode is already in window mode.
341 		 */
SwitchWindowed()342 		bool Direct2D::SwitchWindowed()
343 		{
344 			if (!device.GetPresentation().Windowed)
345 			{
346 				FlushObjects();
347 				device.SwitchWindowed();
348 				lastResult = D3D_OK;
349 				ValidateObjects();
350 				return true;
351 			}
352 
353 			return false;
354 		}
355 
EnableDialogBoxMode(const bool enable)356 		void Direct2D::EnableDialogBoxMode(const bool enable)
357 		{
358 			if (device.CanToggleDialogBoxMode( enable ))
359 			{
360 				FlushObjects();
361 				lastResult = device.ToggleDialogBoxMode();
362 				ValidateObjects();
363 			}
364 		}
365 
Repair()366 		bool Direct2D::Repair()
367 		{
368 			if (FAILED(lastResult) && lastResult != INVALID_RECT)
369 			{
370 				FlushObjects();
371 				lastResult = device.Repair( lastResult );
372 				ValidateObjects();
373 			}
374 
375 			return SUCCEEDED(lastResult);
376 		}
377 
UpdateWindowView()378 		void Direct2D::UpdateWindowView()
379 		{
380 			const Point::Picture picture( device.GetPresentation().hDeviceWindow );
381 
382 			if (picture.x > 0 && picture.y > 0)
383 			{
384 				const Point::Client client( device.GetPresentation().hDeviceWindow );
385 				NST_ASSERT( client.x >= picture.x && client.y >= picture.y );
386 
387 				if
388 				(
389 					client.x != device.GetPresentation().BackBufferWidth ||
390 					client.y != device.GetPresentation().BackBufferHeight ||
391 					lastResult == INVALID_RECT
392 				)
393 				{
394 					FlushObjects();
395 					lastResult = device.ResetWindowClient( client, lastResult );
396 				}
397 
398 				ValidateObjects();
399 			}
400 			else
401 			{
402 				lastResult = INVALID_RECT;
403 			}
404 		}
405 
UpdateWindowView(const Point & screen,const Rect & clipping,const uint scanlines,const int screenCurvature,const Adapter::Filter filter,const bool useVidMem)406 		void Direct2D::UpdateWindowView
407 		(
408 			const Point& screen,
409 			const Rect& clipping,
410 			const uint scanlines,
411 			const int screenCurvature,
412 			const Adapter::Filter filter,
413 			const bool useVidMem
414 		)
415 		{
416 			const Point::Picture picture( device.GetPresentation().hDeviceWindow );
417 
418 			if (picture.x > 0 && picture.y > 0)
419 			{
420 				textures.Update( base.GetAdapter(device.GetOrdinal()), screen, scanlines, filter, useVidMem );
421 				vertexBuffer.Update( picture, clipping, screenCurvature );
422 				indexBuffer.Update( screenCurvature );
423 
424 				const Point::Client client( device.GetPresentation().hDeviceWindow );
425 				NST_ASSERT( client.x >= picture.x && client.y >= picture.y );
426 
427 				if
428 				(
429 					client.x != device.GetPresentation().BackBufferWidth ||
430 					client.y != device.GetPresentation().BackBufferHeight ||
431 					lastResult == INVALID_RECT
432 				)
433 				{
434 					FlushObjects();
435 					lastResult = device.ResetWindowClient( client, lastResult );
436 				}
437 
438 				ValidateObjects();
439 			}
440 			else
441 			{
442 				lastResult = INVALID_RECT;
443 			}
444 		}
445 
UpdateFullscreenView(const Rect & picture,const Point & screen,const Rect & clipping,const uint scanlines,const int screenCurvature,const Adapter::Filter filter,const bool useVidMem)446 		void Direct2D::UpdateFullscreenView
447 		(
448 			const Rect& picture,
449 			const Point& screen,
450 			const Rect& clipping,
451 			const uint scanlines,
452 			const int screenCurvature,
453 			const Adapter::Filter filter,
454 			const bool useVidMem
455 		)
456 		{
457 			NST_ASSERT( picture.Width() && picture.Height() );
458 
459 			textures.Update( base.GetAdapter(device.GetOrdinal()), screen, scanlines, filter, useVidMem );
460 			vertexBuffer.Update( picture, clipping, screenCurvature );
461 			indexBuffer.Update( screenCurvature );
462 			ValidateObjects();
463 		}
464 
UpdateFrameRate(const uint frameRate,const bool vsync,const bool tripleBuffering)465 		void Direct2D::UpdateFrameRate(const uint frameRate,const bool vsync,const bool tripleBuffering)
466 		{
467 			if (device.ResetFrameRate( frameRate, vsync, tripleBuffering, base ))
468 			{
469 				FlushObjects();
470 
471 				if (SUCCEEDED(lastResult))
472 				{
473 					lastResult = device.Reset();
474 					ValidateObjects();
475 				}
476 			}
477 		}
478 
SaveScreenShot(wcstring const file,const uint ext) const479 		Direct2D::ScreenShotResult Direct2D::SaveScreenShot(wcstring const file,const uint ext) const
480 		{
481 			NST_ASSERT( file && *file );
482 
483 			if (SUCCEEDED(lastResult))
484 			{
485 				D3DXIMAGE_FILEFORMAT format;
486 
487 				switch (ext)
488 				{
489 					case MAKEFOURCC('p','n','g','\0'): format = D3DXIFF_PNG; break;
490 					case MAKEFOURCC('j','p','g','\0'): format = D3DXIFF_JPG; break;
491 					case MAKEFOURCC('b','m','p','\0'): format = D3DXIFF_BMP; break;
492 					default: return SCREENSHOT_UNSUPPORTED;
493 				}
494 
495 				if (textures.SaveToFile( file, format ))
496 					return SCREENSHOT_OK;
497 			}
498 
499 			return SCREENSHOT_ERROR;
500 		}
501 
Mode(uint w,uint h,uint b)502 		Direct2D::Mode::Mode(uint w,uint h,uint b)
503 		: width(w), height(h), bpp(b) {}
504 
operator ==(const Mode & mode) const505 		bool Direct2D::Mode::operator == (const Mode& mode) const
506 		{
507 			return width == mode.width && height == mode.height && bpp == mode.bpp;
508 		}
509 
operator <(const Mode & mode) const510 		bool Direct2D::Mode::operator < (const Mode& mode) const
511 		{
512 			if ( width  < mode.width  ) return true;
513 			if ( width  > mode.width  ) return false;
514 			if ( height < mode.height ) return true;
515 			if ( height > mode.height ) return false;
516 			if ( bpp    < mode.bpp    ) return true;
517 			if ( bpp    > mode.bpp    ) return false;
518 
519 			return false;
520 		}
521 
522 		#ifdef NST_MSVC_OPTIMIZE
523 		#pragma optimize("", on)
524 		#endif
525 
Base()526 		Direct2D::Base::Base()
527 		: com(Create()), adapters(EnumerateAdapters(com)) {}
528 
~Base()529 		Direct2D::Base::~Base()
530 		{
531 			com.Release();
532 		}
533 
Create()534 		IDirect3D9& Direct2D::Base::Create()
535 		{
536 			IDirect3D9* com;
537 
538 			if (NULL != (com = ::Direct3DCreate9( D3D_SDK_VERSION )))
539 			{
540 				return *com;
541 			}
542 			else if (NULL != (com = ::Direct3DCreate9( D3D9b_SDK_VERSION ))) // unofficial, it may work, it may not work
543 			{
544 				return *com;
545 			}
546 			else
547 			{
548 				throw Application::Exception( IDS_ERR_D3D_FAILED );
549 			}
550 		}
551 
552 		/**
553 		 * Lists all video adapters and stores their information and properties.
554 		 *
555 		 * @param d3d The Direct3D9 object to fetch video adapters from.
556 		 */
EnumerateAdapters(IDirect3D9 & d3d)557 		const Direct2D::Adapters Direct2D::Base::EnumerateAdapters(IDirect3D9& d3d)
558 		{
559 			NST_COMPILE_ASSERT( D3DADAPTER_DEFAULT == 0 );
560 
561 			Io::Log() << "Direct3D: initializing..\r\n";
562 
563 			Adapters adapters;
564 			for (uint ordinal=0, numAdapters=d3d.GetAdapterCount(); ordinal < NST_MIN(numAdapters,255); ++ordinal)
565 			{
566 				D3DADAPTER_IDENTIFIER9 identifier;
567 
568 				if (SUCCEEDED(d3d.GetAdapterIdentifier( ordinal, 0, &identifier )))
569 				{
570 					uint guidIndex = 1;	//the index of adapters of the same GUID
571 					if (!adapters.empty())
572 					{
573 						for (Adapters::iterator adapterIterator = adapters.begin(); adapterIterator != adapters.end(); adapterIterator++)	//for each previous found adapter
574 						{
575 							if (adapterIterator->guid == identifier.DeviceIdentifier)	//if the adapter is of the same GUID
576 							{
577 								guidIndex++;
578 							}
579 						}
580 					}
581 
582 					Io::Log() << "Direct3D: enumerating device - name: "
583 										<< (*identifier.Description ? identifier.Description : "unknown")
584 										<< ", GUID: "
585 										<< System::Guid( identifier.DeviceIdentifier ).GetString()
586 										<< ", index: "
587 										<< guidIndex
588 										<< "\r\n";
589 
590 					Adapter::Modes modes;
591 
592 					for (uint format=0; format < 2; ++format)
593 					{
594 						const D3DFORMAT type = (format ? D3DFMT_X8R8G8B8 : D3DFMT_R5G6B5);
595 
596 						for (uint mode=0, numModes=d3d.GetAdapterModeCount( ordinal, type ); mode < numModes; ++mode)
597 						{
598 							D3DDISPLAYMODE display;
599 
600 							if (FAILED(d3d.EnumAdapterModes( ordinal, type, mode, &display )))
601 								continue;
602 
603 							if (display.Width < Mode::MIN_WIDTH || display.Height < Mode::MIN_HEIGHT || display.RefreshRate > Mode::MAX_RATE)
604 								continue;
605 
606 							// C++ standard vagueness, sometimes set::iterator == set::const_iterator
607 							const_cast<Mode::Rates&>(modes.insert(Mode( display.Width, display.Height, format ? 32 : 16 )).first->rates).insert( display.RefreshRate );
608 						}
609 					}
610 
611 					if (modes.empty())
612 					{
613 						Io::Log() << "Direct3D: found no valid display mode, continuing enumeration..\r\n";
614 					}
615 					else
616 					{
617 						D3DCAPS9 caps;
618 
619 						if (FAILED(d3d.GetDeviceCaps( ordinal, D3DDEVTYPE_HAL, &caps )))
620 						{
621 							if (FAILED(d3d.GetDeviceCaps( ordinal, D3DDEVTYPE_REF, &caps )))
622 							{
623 								Io::Log() << "Direct3D: warning, bogus device, continuing enumeration..\r\n";
624 								continue;
625 							}
626 							else
627 							{
628 								Io::Log() << "Direct3D: performance warning, this is a REF device only!\r\n";
629 							}
630 						}
631 
632 						adapters.push_back( Adapter() );
633 						Adapter& adapter = adapters.back();
634 
635 						adapter.guid                = identifier.DeviceIdentifier;
636 						adapter.guidIndex           = guidIndex;
637 						adapter.name                = (*identifier.Description ? identifier.Description : "Unknown");
638 						adapter.name.Trim();
639 						adapter.name                << Resource::String( IDS_DIALOG_VIDEO_ADAPTER_INDEX_SUFFIX ).Invoke( ValueString(guidIndex) );	//appends the GUID index suffix
640 						adapter.ordinal             = ordinal;
641 						adapter.deviceType          = (caps.DeviceType != D3DDEVTYPE_REF ? Adapter::DEVICE_HAL : Adapter::DEVICE_HEL);
642 						adapter.maxScreenSize       = Point(caps.MaxTextureWidth,caps.MaxTextureHeight);
643 						adapter.videoMemScreen      = caps.Caps2 & D3DCAPS2_DYNAMICTEXTURES;
644 						adapter.anyTextureSize      = (caps.TextureCaps & (D3DPTEXTURECAPS_SQUAREONLY|D3DPTEXTURECAPS_POW2|D3DPTEXTURECAPS_NONPOW2CONDITIONAL)) == 0;
645 						adapter.canDoScanlineEffect = (caps.MaxSimultaneousTextures >= 2) && (caps.TextureOpCaps & D3DTEXOPCAPS_MODULATE) && (caps.TextureAddressCaps & D3DPTADDRESSCAPS_WRAP);
646 						adapter.intervalTwo         = caps.PresentationIntervals & D3DPRESENT_INTERVAL_TWO;
647 						adapter.intervalThree       = caps.PresentationIntervals & D3DPRESENT_INTERVAL_THREE;
648 						adapter.intervalFour        = caps.PresentationIntervals & D3DPRESENT_INTERVAL_FOUR;
649 						adapter.filters             = 0;
650 						adapter.modern              = (caps.PixelShaderVersion >= D3DPS_VERSION(2,0));
651 						adapter.modes               = modes;
652 
653 						if ((caps.TextureFilterCaps & (D3DPTFILTERCAPS_MINFLINEAR|D3DPTFILTERCAPS_MAGFLINEAR)) == (D3DPTFILTERCAPS_MINFLINEAR|D3DPTFILTERCAPS_MAGFLINEAR))
654 							adapter.filters |= Adapter::FILTER_BILINEAR;
655 
656 						Io::Log log;
657 
658 						log << "Direct3D: dynamic textures: " << (adapter.videoMemScreen ? "supported\r\n" : "unsupported\r\n")
659 								<< "Direct3D: texture bilinear filtering: " << ((adapter.filters & Adapter::FILTER_BILINEAR) ? "supported\r\n" : "unsupported\r\n")
660 								<< "Direct3D: max texture dimensions: " << caps.MaxTextureWidth << 'x' << caps.MaxTextureHeight
661 								<< "\r\nDirect3D: scanline effect: " << (adapter.canDoScanlineEffect ? "supported\r\n" : "unsupported\r\n")
662 								<< "Direct3D: vsync on every second refresh: " << (adapter.intervalTwo ? "supported\r\n" : "unsupported\r\n")
663 								<< "Direct3D: vsync on every third refresh: " << (adapter.intervalThree ? "supported\r\n" : "unsupported\r\n")
664 								<< "Direct3D: found " << modes.size() << " display modes\r\n"
665 								<< "Direct3D: supported monitor frequencies: ";
666 
667 						Mode::Rates rates;
668 
669 						for (Adapter::Modes::const_iterator it(modes.begin()), end(modes.end()); it != end; ++it)
670 							rates.insert( it->rates.begin(), it->rates.end() );
671 
672 						for (Mode::Rates::const_iterator it(rates.begin()), end(rates.end());; )
673 						{
674 							log << uint(*it);
675 
676 							if (++it != end)
677 							{
678 								log << "hz, ";
679 							}
680 							else
681 							{
682 								log << "hz\r\n";
683 								break;
684 							}
685 						}
686 					}
687 				}
688 			}
689 
690 			if (adapters.empty())
691 				throw Application::Exception( L"Found no valid display adapter!" );
692 
693 			return adapters;
694 		}
695 
696 		#ifdef NST_MSVC_OPTIMIZE
697 		#pragma optimize("t", on)
698 		#endif
699 
FormatToBpp(const D3DFORMAT format)700 		uint Direct2D::Base::FormatToBpp(const D3DFORMAT format)
701 		{
702 			switch (format)
703 			{
704 				case D3DFMT_X8R8G8B8:
705 				case D3DFMT_X8B8G8R8:
706 				case D3DFMT_A8R8G8B8:
707 				case D3DFMT_A8B8G8R8:
708 				case D3DFMT_A2R10G10B10:
709 				case D3DFMT_A2B10G10R10:
710 					return 32;
711 
712 				case D3DFMT_R5G6B5:
713 				case D3DFMT_X1R5G5B5:
714 				case D3DFMT_X4R4G4B4:
715 				case D3DFMT_A1R5G5B5:
716 				case D3DFMT_A4R4G4B4:
717 				case D3DFMT_A8R3G3B2:
718 					return 16;
719 			}
720 
721 			return 0;
722 		}
723 
FormatToMask(const D3DFORMAT format,ulong & r,ulong & g,ulong & b)724 		void Direct2D::Base::FormatToMask(const D3DFORMAT format,ulong& r,ulong& g,ulong& b)
725 		{
726 			switch (format)
727 			{
728 				case D3DFMT_X8R8G8B8:
729 				case D3DFMT_A8R8G8B8:    r = 0x00FF0000; g = 0x0000FF00; b = 0x000000FF; break;
730 				case D3DFMT_X8B8G8R8:
731 				case D3DFMT_A8B8G8R8:    r = 0x000000FF; g = 0x0000FF00; b = 0x00FF0000; break;
732 				case D3DFMT_A2R10G10B10: r = 0x3FF00000; g = 0x000FFC00; b = 0x000003FF; break;
733 				case D3DFMT_A2B10G10R10: r = 0x000003FF; g = 0x000FFC00; b = 0x3FF00000; break;
734 				case D3DFMT_R5G6B5:      r = 0xF800;     g = 0x07E0;     b = 0x001F;     break;
735 				case D3DFMT_X1R5G5B5:
736 				case D3DFMT_A1R5G5B5:    r = 0x7C00;     g = 0x03E0;     b = 0x001F;     break;
737 				case D3DFMT_X4R4G4B4:
738 				case D3DFMT_A4R4G4B4:    r = 0x0F00;     g = 0x00F0;     b = 0x000F;     break;
739 				case D3DFMT_A8R3G3B2:    r = 0x00E0;     g = 0x001C;     b = 0x0003;     break;
740 				default:                 r = 0;          g = 0;          b = 0;          break;
741 			}
742 		}
743 
744 		#ifdef NST_MSVC_OPTIMIZE
745 		#pragma optimize("", on)
746 		#endif
747 
Device(HWND const hWnd,const Base & base)748 		Direct2D::Device::Device(HWND const hWnd,const Base& base)
749 		{
750 			NST_ASSERT( hWnd );
751 
752 			presentation.BackBufferWidth            = 0;
753 			presentation.BackBufferHeight           = 0;
754 			presentation.BackBufferFormat           = D3DFMT_UNKNOWN;
755 			presentation.BackBufferCount            = 1;
756 			presentation.MultiSampleType            = D3DMULTISAMPLE_NONE;
757 			presentation.MultiSampleQuality         = 0;
758 			presentation.SwapEffect                 = D3DSWAPEFFECT_DISCARD;
759 			presentation.hDeviceWindow              = hWnd;
760 			presentation.Windowed                   = true;
761 			presentation.EnableAutoDepthStencil     = false;
762 			presentation.AutoDepthStencilFormat     = D3DFMT_UNKNOWN;
763 			presentation.Flags                      = 0;
764 			presentation.FullScreen_RefreshRateInHz = 0;
765 			presentation.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;
766 
767 			dialogBoxMode = false;
768 
769 			Create( base, base.GetAdapter(0) );	//always connects to first video device at start up
770 		}
771 
Create(IDirect3D9 & d3d,const Adapter & adapter)772 		void Direct2D::Device::Create(IDirect3D9& d3d,const Adapter& adapter)
773 		{
774 			ordinal = adapter.ordinal;
775 			intervalTwo = adapter.intervalTwo;
776 			intervalThree = adapter.intervalThree;
777 			intervalFour = adapter.intervalFour;
778 
779 			fonts.Destroy( true );
780 			com.Release();
781 
782 			NST_VERIFY( !!Point::Client(presentation.hDeviceWindow) );
783 
784 			uint buffers = (timing.tripleBuffering ? 2 : 1);
785 			presentation.BackBufferCount = buffers;
786 			presentation.Flags = GetPresentationFlags();
787 			presentation.SwapEffect = GetSwapEffect();
788 			DWORD flags = D3DCREATE_PUREDEVICE|D3DCREATE_HARDWARE_VERTEXPROCESSING;
789 
790 			for (;;)
791 			{
792 				const HRESULT hResult = d3d.CreateDevice
793 				(
794 					adapter.ordinal,
795 					adapter.deviceType == Adapter::DEVICE_HAL ? D3DDEVTYPE_HAL : D3DDEVTYPE_REF,
796 					presentation.hDeviceWindow,
797 					flags,
798 					&presentation,
799 					&com
800 				);
801 
802 				if (SUCCEEDED(hResult))
803 				{
804 					break;
805 				}
806 				else if (hResult == D3DERR_DEVICELOST)
807 				{
808 					throw Application::Exception( L"Can't start! Direct3D is busy!" );
809 				}
810 				else if (buffers != presentation.BackBufferCount)
811 				{
812 					buffers = presentation.BackBufferCount;
813 					Io::Log() << "Direct3D: Warning! IDirect3D9::CreateDevice() failed, retrying with one back-buffer only..\r\n";
814 				}
815 				else if (flags == (D3DCREATE_PUREDEVICE|D3DCREATE_HARDWARE_VERTEXPROCESSING))
816 				{
817 					flags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
818 					Io::Log() << "Direct3D: Warning! IDirect3D9::CreateDevice() failed, retrying without a pure device..\r\n";
819 				}
820 				else if (flags == D3DCREATE_HARDWARE_VERTEXPROCESSING)
821 				{
822 					flags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
823 					Io::Log() << "Direct3D: Warning! IDirect3D9::CreateDevice() failed, retrying with software vertex processing mode..\r\n";
824 				}
825 				else
826 				{
827 					if (HDC const hdc = ::GetDC( NULL ))
828 					{
829 						const int bits = ::GetDeviceCaps( hdc, BITSPIXEL );
830 						::ReleaseDC( NULL, hdc );
831 
832 						if (bits && bits != 16 && bits != 32)
833 							throw Application::Exception( IDS_ERR_BAD_BPP );
834 					}
835 
836 					throw Application::Exception( IDS_ERR_D3D_DEVICE_FAILED );
837 				}
838 			}
839 
840 			Prepare();
841 			fonts.Create( *this );
842 
843 			Io::Log() << "Direct3D: creating "
844                       << (adapter.deviceType == Adapter::DEVICE_HAL ? "HAL device #" : "REF device #")
845                       << adapter.ordinal
846                       << "\r\n";
847 
848 			LogDisplaySwitch();
849 		}
850 
GetDisplayMode(D3DDISPLAYMODE & displayMode) const851 		bool Direct2D::Device::GetDisplayMode(D3DDISPLAYMODE& displayMode) const
852 		{
853 			IDirect3D9* base;
854 
855 			if (SUCCEEDED(com->GetDirect3D( &base )))
856 			{
857 				const HRESULT hResult = base->GetAdapterDisplayMode( ordinal, &displayMode );
858 				base->Release();
859 
860 				if (SUCCEEDED(hResult))
861 					return true;
862 			}
863 
864 			displayMode.Width = presentation.BackBufferWidth;
865 			displayMode.Height = presentation.BackBufferHeight;
866 
867 			return false;
868 		}
869 
870 		#ifdef NST_MSVC_OPTIMIZE
871 		#pragma optimize("t", on)
872 		#endif
873 
GetPresentationFlags() const874 		DWORD Direct2D::Device::GetPresentationFlags() const
875 		{
876 			return (dialogBoxMode && !presentation.Windowed) ? D3DPRESENTFLAG_LOCKABLE_BACKBUFFER : 0;
877 		}
878 
GetSwapEffect() const879 		D3DSWAPEFFECT Direct2D::Device::GetSwapEffect() const
880 		{
881 			return (dialogBoxMode && !presentation.Windowed) || (presentation.BackBufferCount > 1) ? D3DSWAPEFFECT_DISCARD : D3DSWAPEFFECT_COPY;
882 		}
883 
GetDesiredPresentationRate(const Mode & mode) const884 		uint Direct2D::Device::GetDesiredPresentationRate(const Mode& mode) const
885 		{
886 			if (presentation.Windowed)
887 			{
888 				return 0;
889 			}
890 			else if (timing.autoHz)
891 			{
892 				int match = INT_MAX;
893 				Mode::Rates::const_iterator close(mode.rates.begin());
894 
895 				for (Mode::Rates::const_iterator it(mode.rates.end()), begin(mode.rates.begin());; )
896 				{
897 					--it;
898 
899 					for (uint i=5; --i; )
900 					{
901 						int diff = int(timing.frameRate * i) - int(*it);
902 
903 						if (diff == 0)
904 							return *it;
905 
906 						if (diff < 0)
907 							diff = int(*it) - int(timing.frameRate * i);
908 
909 						if (match > diff)
910 						{
911 							match = diff;
912 							close = it;
913 						}
914 					}
915 
916 					if (it == begin)
917 						break;
918 				}
919 
920 				return *close;
921 			}
922 			else for (Mode::Rates::const_iterator it(mode.rates.begin()), end(mode.rates.end()); it != end; ++it)
923 			{
924 				if (*it == Mode::DEFAULT_RATE)
925 					return Mode::DEFAULT_RATE;
926 			}
927 
928 			return 0;
929 		}
930 
GetDesiredPresentationInterval(const uint rate) const931 		DWORD Direct2D::Device::GetDesiredPresentationInterval(const uint rate) const
932 		{
933 			if (!timing.vsync || rate % timing.frameRate)
934 			{
935 				return D3DPRESENT_INTERVAL_IMMEDIATE;
936 			}
937 			else if (!presentation.Windowed)
938 			{
939 				if (timing.frameRate * 4 == rate && intervalFour)
940 				{
941 					return D3DPRESENT_INTERVAL_FOUR;
942 				}
943 				else if (timing.frameRate * 3 == rate && intervalThree)
944 				{
945 					return D3DPRESENT_INTERVAL_THREE;
946 				}
947 				else if (timing.frameRate * 2 == rate && intervalTwo)
948 				{
949 					return D3DPRESENT_INTERVAL_TWO;
950 				}
951 			}
952 
953 			return timing.frameRate == rate ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
954 		}
955 
GetRefreshRate() const956 		uint Direct2D::Device::GetRefreshRate() const
957 		{
958 			if (presentation.Windowed)
959 			{
960 				D3DDISPLAYMODE mode;
961 				return GetDisplayMode( mode ) ? mode.RefreshRate : 0;
962 			}
963 			else
964 			{
965 				return presentation.FullScreen_RefreshRateInHz;
966 			}
967 		}
968 
GetDesiredPresentationInterval() const969 		DWORD Direct2D::Device::GetDesiredPresentationInterval() const
970 		{
971 			return GetDesiredPresentationInterval( GetRefreshRate() );
972 		}
973 
Reset()974 		HRESULT Direct2D::Device::Reset()
975 		{
976 			fonts.OnLost();
977 
978 			const uint oldInterval = presentation.PresentationInterval;
979 			presentation.PresentationInterval = GetDesiredPresentationInterval();
980 			uint buffers = timing.tripleBuffering ? 2 : 1;
981 			presentation.BackBufferCount = buffers;
982 			presentation.Flags = GetPresentationFlags();
983 			presentation.SwapEffect = GetSwapEffect();
984 
985 			for (;;)
986 			{
987 				const HRESULT hResult = com->Reset( &presentation );
988 
989 				if (SUCCEEDED(hResult))
990 				{
991 					break;
992 				}
993 				else if (hResult == D3DERR_DEVICELOST)
994 				{
995 					return D3DERR_DEVICELOST;
996 				}
997 				else if (buffers != presentation.BackBufferCount)
998 				{
999 					buffers = presentation.BackBufferCount;
1000 					Io::Log() << "Direct3D: Warning! IDirect3DDevice9::Reset() failed, retrying with one back-buffer only..\r\n";
1001 				}
1002 				else throw Application::Exception
1003 				(
1004 					IDS_ERR_FAILED,
1005 					hResult == D3DERR_INVALIDCALL         ? L"IDirect3DDevice9::Reset() (code: D3DERR_INVALIDCALL)"         :
1006 					hResult == D3DERR_OUTOFVIDEOMEMORY    ? L"IDirect3DDevice9::Reset() (code: D3DERR_OUTOFVIDEOMEMORY)"    :
1007 					hResult == D3DERR_DRIVERINTERNALERROR ? L"IDirect3DDevice9::Reset() (code: D3DERR_DRIVERINTERNALERROR)" :
1008 					hResult == E_OUTOFMEMORY              ? L"IDirect3DDevice9::Reset() (code: E_OUTOFMEMORY)"              :
1009 															L"IDirect3DDevice9::Reset()"
1010 				);
1011 			}
1012 
1013 			if (!presentation.Windowed && dialogBoxMode && FAILED(com->SetDialogBoxMode( true )))
1014 				throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::SetDialogBoxMode()" );
1015 
1016 			Prepare();
1017 			fonts.OnReset();
1018 
1019 			if (presentation.PresentationInterval != oldInterval)
1020 			{
1021 				Io::Log() <<
1022 				(
1023 					presentation.PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE ? "Direct3D: disabling VSYNC\r\n" :
1024 					presentation.PresentationInterval == D3DPRESENT_INTERVAL_TWO       ? "Direct3D: enabling VSYNC on second refresh\r\n" :
1025 					presentation.PresentationInterval == D3DPRESENT_INTERVAL_THREE     ? "Direct3D: enabling VSYNC on third refresh\r\n" :
1026                                                                                          "Direct3D: enabling VSYNC\r\n"
1027 				);
1028 			}
1029 
1030 			if (!presentation.Windowed && ::GetMenu( presentation.hDeviceWindow ))
1031 				::DrawMenuBar( presentation.hDeviceWindow );
1032 
1033 			return D3D_OK;
1034 		}
1035 
Prepare() const1036 		void Direct2D::Device::Prepare() const
1037 		{
1038 			com->SetRenderState( D3DRS_ZWRITEENABLE, false        );
1039 			com->SetRenderState( D3DRS_COLORVERTEX,  false        );
1040 			com->SetRenderState( D3DRS_CULLMODE,     D3DCULL_NONE );
1041 			com->SetRenderState( D3DRS_LIGHTING,     false        );
1042 		}
1043 
LogDisplaySwitch() const1044 		void Direct2D::Device::LogDisplaySwitch() const
1045 		{
1046 			Io::Log log;
1047 			log << "Direct3D: entering ";
1048 
1049 			D3DDISPLAYMODE mode;
1050 
1051 			if (GetDisplayMode( mode ))
1052 			{
1053 				log << mode.Width
1054 					<< 'x'
1055 					<< mode.Height
1056 					<< 'x'
1057 					<< Base::FormatToBpp(mode.Format)
1058 					<< ' '
1059 					<< mode.RefreshRate
1060 					<< "hz ";
1061 			}
1062 
1063 			log << (presentation.Windowed ? "window mode\r\n" : "full-screen mode\r\n");
1064 		}
1065 
GetMaxMessageLength() const1066 		uint Direct2D::Device::GetMaxMessageLength() const
1067 		{
1068 			return fonts.Width() ? (presentation.BackBufferWidth - fonts.Width() * 7) / fonts.Width() : 64;
1069 		}
1070 
1071 		#ifdef NST_MSVC_OPTIMIZE
1072 		#pragma optimize("", on)
1073 		#endif
1074 
Repair(const HRESULT lastError)1075 		HRESULT Direct2D::Device::Repair(const HRESULT lastError)
1076 		{
1077 			NST_ASSERT( FAILED(lastError) );
1078 
1079 			uint id = 0;
1080 			wcstring msg;
1081 
1082 			switch (lastError)
1083 			{
1084 				case D3DERR_DEVICELOST:
1085 				case D3DERR_DEVICENOTRESET:
1086 
1087 					switch (com->TestCooperativeLevel())
1088 					{
1089 						case D3DERR_DEVICELOST:
1090 
1091 							return D3DERR_DEVICELOST;
1092 
1093 						case D3DERR_DEVICENOTRESET:
1094 
1095 							return Reset();
1096 
1097 						case D3DERR_DRIVERINTERNALERROR:
1098 
1099 							msg = L"Internal video driver error! Try upgrading it!";
1100 							break;
1101 
1102 						default:
1103 
1104 							id = IDS_ERR_FAILED;
1105 							msg = L"IDirect3DDevice9::TestCooperativeLevel()";
1106 							break;
1107 					}
1108 
1109 				case D3DERR_DRIVERINTERNALERROR:
1110 
1111 					msg = L"Internal video driver error! Try upgrading it!";
1112 					break;
1113 
1114 				case E_OUTOFMEMORY:
1115 
1116 					msg = L"Out of memory!";
1117 					break;
1118 
1119 				case D3DERR_OUTOFVIDEOMEMORY:
1120 
1121 					msg = L"Out of video memory!";
1122 					break;
1123 
1124 				default:
1125 
1126 					msg = L"Unknown Direct3D error!";
1127 					break;
1128 			}
1129 
1130 			throw Application::Exception( id, msg );
1131 		}
1132 
1133 		#ifdef NST_MSVC_OPTIMIZE
1134 		#pragma optimize("t", on)
1135 		#endif
1136 
CanToggleDialogBoxMode(bool enable) const1137 		bool Direct2D::Device::CanToggleDialogBoxMode(bool enable) const
1138 		{
1139 			return !presentation.Windowed && dialogBoxMode != enable;
1140 		}
1141 
1142 		/**
1143 		 * Verifies if a switch to a fullscreen mode can be made or if the mode is already set to fullscreen.
1144 		 *
1145 		 * @param mode The video mode to set to fullscreen.
1146 		 * @return True if switched to the fullscreen mode can be made. False if the mode is already set to fullscreen.
1147 		 */
CanSwitchFullscreen(const Mode & mode) const1148 		bool Direct2D::Device::CanSwitchFullscreen(const Mode& mode) const
1149 		{
1150 			return
1151 			(
1152 				presentation.Windowed ||
1153 				presentation.BackBufferWidth != mode.width ||
1154 				presentation.BackBufferHeight != mode.height ||
1155 				presentation.BackBufferFormat != (mode.bpp == 16 ? D3DFMT_R5G6B5 : D3DFMT_X8R8G8B8) ||
1156 				presentation.FullScreen_RefreshRateInHz != GetDesiredPresentationRate( mode )
1157 			);
1158 		}
1159 
1160 		/**
1161 		 * Switches to fullscreen mode. Creates fonts to be displayed in fullscreen.
1162 		 *
1163 		 * @param mode The video mode to set to fullscreen.
1164 		 * @throws Application::Exception If failed switch to fullscreen.
1165 		 * @see Application::Exception
1166 		 */
SwitchFullscreen(const Mode & mode)1167 		void Direct2D::Device::SwitchFullscreen(const Mode& mode)
1168 		{
1169 			presentation.Windowed = false;
1170 			presentation.BackBufferWidth = mode.width;
1171 			presentation.BackBufferHeight = mode.height;
1172 			presentation.BackBufferFormat = (mode.bpp == 16 ? D3DFMT_R5G6B5 : D3DFMT_X8R8G8B8);
1173 			presentation.FullScreen_RefreshRateInHz = GetDesiredPresentationRate( mode );
1174 
1175 			if (FAILED(Reset()))
1176 				throw Application::Exception( L"Couldn't switch display mode!" );
1177 
1178 			fonts.Create( *this );
1179 			LogDisplaySwitch();
1180 		}
1181 
ToggleDialogBoxMode()1182 		HRESULT Direct2D::Device::ToggleDialogBoxMode()
1183 		{
1184 			NST_ASSERT( !presentation.Windowed );
1185 
1186 			if (dialogBoxMode)
1187 			{
1188 				dialogBoxMode = false;
1189 				com->SetDialogBoxMode( false );
1190 			}
1191 			else
1192 			{
1193 				dialogBoxMode = true;
1194 			}
1195 
1196 			return Reset();
1197 		}
1198 
1199 		/**
1200 		 * Switches to window mode. Destroys any fullscreen fonts and creates fonts to be displayed in status bar.
1201 		 *
1202 		 * @throws Application::Exception If failed switch to window mode.
1203 		 * @see Application::Exception
1204 		 */
SwitchWindowed()1205 		void Direct2D::Device::SwitchWindowed()
1206 		{
1207 			fonts.Destroy( false );
1208 
1209 			presentation.Windowed = true;
1210 			presentation.BackBufferWidth = 0;
1211 			presentation.BackBufferHeight = 0;
1212 			presentation.BackBufferFormat = D3DFMT_UNKNOWN;
1213 			presentation.FullScreen_RefreshRateInHz = 0;
1214 
1215 			if (dialogBoxMode)
1216 			{
1217 				dialogBoxMode = false;
1218 				com->SetDialogBoxMode( false );
1219 			}
1220 
1221 			if (FAILED(Reset()))
1222 				throw Application::Exception( L"Couldn't switch display mode!" );
1223 
1224 			fonts.Create( *this );
1225 			LogDisplaySwitch();
1226 		}
1227 
ResetWindowClient(const Point & client,HRESULT hResult)1228 		HRESULT Direct2D::Device::ResetWindowClient(const Point& client,HRESULT hResult)
1229 		{
1230 			NST_ASSERT( presentation.Windowed && client.x > 0 && client.y > 0 );
1231 
1232 			presentation.BackBufferWidth = client.x;
1233 			presentation.BackBufferHeight = client.y;
1234 
1235 			if (SUCCEEDED(hResult) || hResult == INVALID_RECT)
1236 				hResult = Reset();
1237 
1238 			return hResult;
1239 		}
1240 
ResetFrameRate(uint frameRate,bool vsync,bool tripleBuffering,const Base & base)1241 		bool Direct2D::Device::ResetFrameRate(uint frameRate,bool vsync,bool tripleBuffering,const Base& base)
1242 		{
1243 			timing.frameRate = frameRate;
1244 			timing.vsync = vsync;
1245 
1246 			bool update = false;
1247 
1248 			if (timing.tripleBuffering != tripleBuffering)
1249 			{
1250 				timing.tripleBuffering = tripleBuffering;
1251 				update = true;
1252 			}
1253 
1254 			if (!presentation.Windowed)
1255 			{
1256 				const Mode mode
1257 				(
1258 					presentation.BackBufferWidth,
1259 					presentation.BackBufferHeight,
1260 					presentation.BackBufferFormat == D3DFMT_X8R8G8B8 ? 32 : 16
1261 				);
1262 
1263 				frameRate = GetDesiredPresentationRate( *base.GetAdapter(ordinal).modes.find(mode) );
1264 
1265 				if (presentation.FullScreen_RefreshRateInHz != frameRate)
1266 				{
1267 					presentation.FullScreen_RefreshRateInHz = frameRate;
1268 					update = true;
1269 				}
1270 			}
1271 
1272 			return update || presentation.PresentationInterval != GetDesiredPresentationInterval();
1273 		}
1274 
Fonts()1275 		Direct2D::Device::Fonts::Fonts()
1276 		: width(0) {}
1277 
Create(const Device & device)1278 		void Direct2D::Device::Fonts::Font::Create(const Device& device)
1279 		{
1280 			wcstring fontName = L"System";
1281 			uint fontHeight = 12;
1282 
1283 			D3DDISPLAYMODE mode;
1284 			device.GetDisplayMode( mode );
1285 
1286 			if (mode.Width > 320 && mode.Height > 240)
1287 			{
1288 				fontHeight = mode.Height / (device.presentation.Windowed ? 32 : 16);
1289 
1290 				switch (PRIMARYLANGID(::GetUserDefaultLangID()))
1291 				{
1292 					case LANG_JAPANESE: fontName = L"MS Gothic"; break;
1293 					case LANG_CHINESE:  fontName = L"MS Hei";    break;
1294 					case LANG_KOREAN:   fontName = L"GulimChe";  break;
1295 					default:            fontName = L"Arial";     break;
1296 				}
1297 			}
1298 
1299 			if (com)
1300 			{
1301 				Object::Pod<D3DXFONT_DESC> desc;
1302 				com->GetDesc( &desc );
1303 
1304 				if (desc.Height == int(fontHeight) && bool(desc.Width > 320 && desc.Height > 240) == bool(mode.Width > 320 && mode.Height > 240))
1305 					return;
1306 
1307 				com.Release();
1308 			}
1309 
1310 			::D3DXCreateFont
1311 			(
1312 				*device.com,
1313 				fontHeight,
1314 				0,
1315 				FW_NORMAL,
1316 				1,
1317 				false,
1318 				DEFAULT_CHARSET,
1319 				OUT_DEFAULT_PRECIS,
1320 				DEFAULT_QUALITY,
1321 				DEFAULT_PITCH|FF_DONTCARE,
1322 				fontName,
1323 				&com
1324 			);
1325 		}
1326 
Width() const1327 		uint Direct2D::Device::Fonts::Font::Width() const
1328 		{
1329 			TEXTMETRIC metric;
1330 
1331 			if (com && SUCCEEDED(com->GetTextMetrics( &metric )))
1332 				return metric.tmAveCharWidth;
1333 			else
1334 				return 0;
1335 		}
1336 
Destroy()1337 		void Direct2D::Device::Fonts::Font::Destroy()
1338 		{
1339 			length = 0;
1340 			com.Release();
1341 		}
1342 
OnReset() const1343 		void Direct2D::Device::Fonts::Font::OnReset() const
1344 		{
1345 			if (com)
1346 				com->OnResetDevice();
1347 		}
1348 
OnLost() const1349 		void Direct2D::Device::Fonts::Font::OnLost() const
1350 		{
1351 			if (com)
1352 				com->OnLostDevice();
1353 		}
1354 
Update(const GenericString & newstring)1355 		void Direct2D::Device::Fonts::Font::Update(const GenericString& newstring)
1356 		{
1357 			string = newstring;
1358 			length = newstring.Length();
1359 
1360 			if (length && com)
1361 				com->PreloadText( string.Ptr(), string.Length() );
1362 		}
1363 
Create(const Device & device)1364 		void Direct2D::Device::Fonts::Create(const Device& device)
1365 		{
1366 			nfo.Create( device );
1367 
1368 			if (!device.presentation.Windowed)
1369 			{
1370 				fps.Create( device );
1371 				msg.Create( device );
1372 			}
1373 
1374 			width = nfo.Width();
1375 		}
1376 
Destroy(const bool newDevice)1377 		void Direct2D::Device::Fonts::Destroy(const bool newDevice)
1378 		{
1379 			width = 0;
1380 
1381 			fps.Destroy();
1382 			msg.Destroy();
1383 
1384 			if (newDevice)
1385 				nfo.Destroy();
1386 			else
1387 				nfo.OnReset();
1388 		}
1389 
OnReset() const1390 		void Direct2D::Device::Fonts::OnReset() const
1391 		{
1392 			fps.OnReset();
1393 			msg.OnReset();
1394 			nfo.OnReset();
1395 		}
1396 
OnLost() const1397 		void Direct2D::Device::Fonts::OnLost() const
1398 		{
1399 			fps.OnLost();
1400 			msg.OnLost();
1401 			nfo.OnLost();
1402 		}
1403 
Timing()1404 		Direct2D::Device::Timing::Timing()
1405 		:
1406 		autoHz          (false),
1407 		vsync           (false),
1408 		tripleBuffering (false),
1409 		frameRate       (Mode::DEFAULT_RATE)
1410 		{
1411 		}
1412 
VertexBuffer()1413 		Direct2D::VertexBuffer::VertexBuffer()
1414 		: numVertices(4), screenCurvature(0), dirty(false) {}
1415 
~VertexBuffer()1416 		Direct2D::VertexBuffer::~VertexBuffer()
1417 		{
1418 			Invalidate();
1419 		}
1420 
Update(const Rect & picture,const Rect & c,const int s)1421 		void Direct2D::VertexBuffer::Update(const Rect& picture,const Rect& c,const int s)
1422 		{
1423 			NST_ASSERT( picture.Width() > 0 && picture.Height() > 0 && c.Width() > 0 && c.Height() > 0 );
1424 
1425 			dirty = true;
1426 
1427 			rect = picture;
1428 			clip = c;
1429 			screenCurvature = s;
1430 
1431 			const uint n = (s ? (TSL_PATCHES+1) * (TSL_PATCHES+1) : 4);
1432 
1433 			if (numVertices != n)
1434 			{
1435 				numVertices = n;
1436 				Invalidate();
1437 			}
1438 		}
1439 
Invalidate()1440 		void Direct2D::VertexBuffer::Invalidate()
1441 		{
1442 			if (com)
1443 			{
1444 				IDirect3DDevice9* device;
1445 
1446 				if (SUCCEEDED(com->GetDevice( &device )))
1447 				{
1448 					device->SetStreamSource( 0, NULL, 0, 0 );
1449 					device->Release();
1450 				}
1451 
1452 				com.Release();
1453 			}
1454 		}
1455 
Validate(IDirect3DDevice9 & device,const Textures & textures)1456 		HRESULT Direct2D::VertexBuffer::Validate(IDirect3DDevice9& device,const Textures& textures)
1457 		{
1458 			if (!com)
1459 			{
1460 				const HRESULT hResult = device.CreateVertexBuffer
1461 				(
1462 					numVertices * sizeof(Vertex),
1463 					D3DUSAGE_WRITEONLY,
1464 					FVF,
1465 					D3DPOOL_MANAGED,
1466 					&com,
1467 					NULL
1468 				);
1469 
1470 				if (FAILED(hResult))
1471 				{
1472 					if (hResult == D3DERR_DEVICELOST)
1473 						return D3DERR_DEVICELOST;
1474 					else
1475 						throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::CreateVertexBuffer()" );
1476 				}
1477 
1478 				dirty = true;
1479 			}
1480 
1481 			if (dirty)
1482 			{
1483 				dirty = false;
1484 
1485 				void* ptr;
1486 				const HRESULT hResult = com->Lock( 0, 0, &ptr, D3DLOCK_NOSYSLOCK );
1487 
1488 				if (SUCCEEDED(hResult))
1489 				{
1490 					Vertex* NST_RESTRICT v = static_cast<Vertex*>(ptr);
1491 
1492 					dirty = true;
1493 
1494 					if (screenCurvature)
1495 					{
1496 						const float z = 1.f - screenCurvature / 40.f;
1497 						const D3DXVECTOR2 p0( rect.left - 0.499f, rect.top - 0.499f );
1498 						const D3DXVECTOR2 p1( rect.right - 0.499f, rect.bottom - 0.499f );
1499 						const D3DXVECTOR2 t( rect.Width() * z, rect.Height() * z );
1500 
1501 						for (uint y=0; y <= TSL_PATCHES; ++y)
1502 						{
1503 							D3DXVECTOR2 vy;
1504 
1505 							float weight = y / float(TSL_PATCHES);
1506 							::D3DXVec2Hermite( &vy, &p0, &t, &p1, &t, weight );
1507 
1508 							vy.x = textures.GetScreenBottomV(clip.top + clip.Height() * weight);
1509 							float x1 = textures.GetEffectBottomV(clip.top + clip.Height() * weight);
1510 
1511 							for (uint x=0; x <= TSL_PATCHES; ++x, ++v)
1512 							{
1513 								D3DXVECTOR2 vx;
1514 
1515 								weight = x / float(TSL_PATCHES);
1516 								::D3DXVec2Hermite( &vx, &p0, &t, &p1, &t, weight );
1517 
1518 								v->x   = vx.x;
1519 								v->y   = vy.y;
1520 								v->z   = 0.f;
1521 								v->rhw = 1.f;
1522 								v->u0  = textures.GetScreenLeftU(clip.left + clip.Width() * weight);
1523 								v->u1  = textures.GetEffectLeftU(clip.left + clip.Width() * weight);
1524 								v->v0  = vy.x;
1525 								v->v1  = x1;
1526 							}
1527 						}
1528 					}
1529 					else
1530 					{
1531 						v[0].x   = rect.left - 0.499f;
1532 						v[0].y   = rect.top - 0.499f;
1533 						v[0].z   = 0.f;
1534 						v[0].rhw = 1.f;
1535 						v[0].u0  = textures.GetScreenLeftU( clip.left );
1536 						v[0].u1  = textures.GetEffectLeftU( clip.left );
1537 						v[0].v0  = textures.GetScreenTopV( clip.top );
1538 						v[0].v1  = textures.GetEffectTopV( clip.top );
1539 						v[1].x   = rect.left - 0.499f;
1540 						v[1].y   = rect.bottom - 0.499f;
1541 						v[1].z   = 0.f;
1542 						v[1].rhw = 1.f;
1543 						v[1].u0  = textures.GetScreenLeftU( clip.left );
1544 						v[1].u1  = textures.GetEffectLeftU( clip.left );
1545 						v[1].v0  = textures.GetScreenBottomV( clip.bottom );
1546 						v[1].v1  = textures.GetEffectBottomV( clip.bottom );
1547 						v[2].x   = rect.right - 0.499f;
1548 						v[2].y   = rect.top - 0.499f;
1549 						v[2].z   = 0.f;
1550 						v[2].rhw = 1.f;
1551 						v[2].u0  = textures.GetScreenRightU( clip.right );
1552 						v[2].u1  = textures.GetEffectRightU( clip.right );
1553 						v[2].v0  = textures.GetScreenTopV( clip.top );
1554 						v[2].v1  = textures.GetEffectTopV( clip.top );
1555 						v[3].x   = rect.right - 0.499f;
1556 						v[3].y   = rect.bottom - 0.499f;
1557 						v[3].z   = 0.f;
1558 						v[3].rhw = 1.f;
1559 						v[3].u0  = textures.GetScreenRightU( clip.right );
1560 						v[3].u1  = textures.GetEffectRightU( clip.right );
1561 						v[3].v0  = textures.GetScreenBottomV( clip.bottom );
1562 						v[3].v1  = textures.GetEffectBottomV( clip.bottom );
1563 					}
1564 
1565 					com->Unlock();
1566 				}
1567 				else if (hResult == D3DERR_DEVICELOST)
1568 				{
1569 					return D3DERR_DEVICELOST;
1570 				}
1571 				else
1572 				{
1573 					throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DVertexBuffer9::Lock()" );
1574 				}
1575 
1576 				com->PreLoad();
1577 			}
1578 
1579 			if (FAILED(device.SetFVF( FVF )))
1580 				throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::SetFVF()" );
1581 
1582 			if (FAILED(device.SetStreamSource( 0, *com, 0, sizeof(Vertex) )))
1583 				throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::SetStreamSource()" );
1584 
1585 			return D3D_OK;
1586 		}
1587 
IndexBuffer()1588 		Direct2D::IndexBuffer::IndexBuffer()
1589 		: numStrips(0) {}
1590 
~IndexBuffer()1591 		Direct2D::IndexBuffer::~IndexBuffer()
1592 		{
1593 			Invalidate();
1594 		}
1595 
Update(bool s)1596 		void Direct2D::IndexBuffer::Update(bool s)
1597 		{
1598 			if (bool(numStrips) != s)
1599 			{
1600 				numStrips = (s ? (TSL_PATCHES * 2 + 1) * TSL_PATCHES - 1 : 0);
1601 				Invalidate();
1602 			}
1603 		}
1604 
Invalidate()1605 		void Direct2D::IndexBuffer::Invalidate()
1606 		{
1607 			if (com)
1608 			{
1609 				IDirect3DDevice9* device;
1610 
1611 				if (SUCCEEDED(com->GetDevice( &device )))
1612 				{
1613 					device->SetIndices( NULL );
1614 					device->Release();
1615 				}
1616 
1617 				com.Release();
1618 			}
1619 		}
1620 
Validate(IDirect3DDevice9 & device)1621 		HRESULT Direct2D::IndexBuffer::Validate(IDirect3DDevice9& device)
1622 		{
1623 			NST_ASSERT( !com || numStrips );
1624 
1625 			if (numStrips)
1626 			{
1627 				if (!com)
1628 				{
1629 					const HRESULT hResult = device.CreateIndexBuffer
1630 					(
1631 						(((TSL_PATCHES * 2 + 1) * TSL_PATCHES) + 1) * sizeof(WORD),
1632 						D3DUSAGE_WRITEONLY,
1633 						D3DFMT_INDEX16,
1634 						D3DPOOL_MANAGED,
1635 						&com,
1636 						NULL
1637 					);
1638 
1639 					if (SUCCEEDED(hResult))
1640 					{
1641 						void* ptr;
1642 						const HRESULT hResult = com->Lock( 0, 0, &ptr, D3DLOCK_NOSYSLOCK );
1643 
1644 						if (SUCCEEDED(hResult))
1645 						{
1646 							WORD* NST_RESTRICT data = static_cast<WORD*>(ptr);
1647 
1648 							for (uint p=0, i=0, n=TSL_PATCHES+1;;)
1649 							{
1650 								uint j = i + n;
1651 
1652 								do
1653 								{
1654 									*data++ = i;
1655 									*data++ = i++ + n;
1656 								}
1657 								while (i < j);
1658 
1659 								i += n-1;
1660 
1661 								do
1662 								{
1663 									*data++ = i-- + n;
1664 									*data++ = i;
1665 								}
1666 								while (i > j);
1667 
1668 								i += n;
1669 								p += 2;
1670 
1671 								if (p == n-1)
1672 								{
1673 									*data++ = i;
1674 									break;
1675 								}
1676 							}
1677 
1678 							com->Unlock();
1679 						}
1680 						else if (hResult == D3DERR_DEVICELOST)
1681 						{
1682 							return D3DERR_DEVICELOST;
1683 						}
1684 						else
1685 						{
1686 							throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DIndexBuffer9::Lock()" );
1687 						}
1688 					}
1689 					else if (hResult == D3DERR_DEVICELOST)
1690 					{
1691 						return D3DERR_DEVICELOST;
1692 					}
1693 					else
1694 					{
1695 						throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::CreateIndexBuffer()" );
1696 					}
1697 				}
1698 
1699 				com->PreLoad();
1700 
1701 				if (FAILED(device.SetIndices( *com )))
1702 					throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::SetIndices()" );
1703 			}
1704 
1705 			return D3D_OK;
1706 		}
1707 
Textures(D3DFORMAT backBufferFormat)1708 		Direct2D::Textures::Textures(D3DFORMAT backBufferFormat)
1709 		:
1710 		screenTexture (backBufferFormat),
1711 		effectTexture (backBufferFormat),
1712 		filter        (Adapter::FILTER_NONE)
1713 		{}
1714 
Update(const Adapter & adapter,const Point & screen,const uint scanlines,const Adapter::Filter f,const bool useVidMem)1715 		void Direct2D::Textures::Update(const Adapter& adapter,const Point& screen,const uint scanlines,const Adapter::Filter f,const bool useVidMem)
1716 		{
1717 			NST_ASSERT( screen.x > 0 && screen.y > 0 );
1718 
1719 			filter = f;
1720 
1721 			effectTexture.Update( adapter, screen, scanlines );
1722 			screenTexture.Update( adapter, screen, useVidMem );
1723 		}
1724 
Flush()1725 		void Direct2D::Textures::Flush()
1726 		{
1727 			screenTexture.Flush();
1728 		}
1729 
Invalidate()1730 		void Direct2D::Textures::Invalidate()
1731 		{
1732 			effectTexture.Invalidate();
1733 			screenTexture.Invalidate();
1734 		}
1735 
Validate(IDirect3DDevice9 & device,const Adapter & adapter,const D3DFORMAT backBufferFormat)1736 		HRESULT Direct2D::Textures::Validate(IDirect3DDevice9& device,const Adapter& adapter,const D3DFORMAT backBufferFormat)
1737 		{
1738 			if (screenTexture.Validate( device, backBufferFormat, adapter ) && effectTexture.Validate( device, backBufferFormat ))
1739 			{
1740 				const D3DTEXTUREFILTERTYPE type = (filter == Adapter::FILTER_NONE ? D3DTEXF_POINT : D3DTEXF_LINEAR);
1741 
1742 				for (uint i=0, n = (effectTexture ? 2 : 1); i < n; ++i)
1743 				{
1744 					device.SetSamplerState( i, D3DSAMP_MINFILTER, type );
1745 					device.SetSamplerState( i, D3DSAMP_MAGFILTER, type );
1746 				}
1747 
1748 				return D3D_OK;
1749 			}
1750 
1751 			return D3DERR_DEVICELOST;
1752 		}
1753 
SaveToFile(wcstring const file,const D3DXIMAGE_FILEFORMAT type) const1754 		bool Direct2D::Textures::SaveToFile(wcstring const file,const D3DXIMAGE_FILEFORMAT type) const
1755 		{
1756 			return screenTexture.SaveToFile( file, type );
1757 		}
1758 
Texture(uint s,D3DFORMAT f)1759 		Direct2D::Textures::Texture::Texture(uint s,D3DFORMAT f)
1760 		: size(256,256), stage(s)
1761 		{
1762 			desc.Width = size.x;
1763 			desc.Height = size.y;
1764 			desc.Format = f;
1765 		}
1766 
~Texture()1767 		Direct2D::Textures::Texture::~Texture()
1768 		{
1769 			Invalidate();
1770 		}
1771 
GetSquared(const Point & p)1772 		uint Direct2D::Textures::Texture::GetSquared(const Point& p)
1773 		{
1774 			uint squared = NST_MAX(p.x,p.y);
1775 
1776 			squared--;
1777 			squared |= squared >> 1;
1778 			squared |= squared >> 2;
1779 			squared |= squared >> 4;
1780 			squared |= squared >> 8;
1781 			squared |= squared >> 16;
1782 			squared++;
1783 
1784 			return squared;
1785 		}
1786 
Invalidate()1787 		void Direct2D::Textures::Texture::Invalidate()
1788 		{
1789 			if (com)
1790 			{
1791 				IDirect3DDevice9* device;
1792 
1793 				if (SUCCEEDED(com->GetDevice( &device )))
1794 				{
1795 					device->SetTextureStageState( stage, D3DTSS_COLOROP, D3DTOP_DISABLE );
1796 					device->SetTexture( stage, NULL );
1797 					device->Release();
1798 				}
1799 
1800 				com.Release();
1801 			}
1802 		}
1803 
Validate(IDirect3DDevice9 & device,const D3DFORMAT desiredFormat,const bool dynamicUsage)1804 		bool Direct2D::Textures::Texture::Validate(IDirect3DDevice9& device,const D3DFORMAT desiredFormat,const bool dynamicUsage)
1805 		{
1806 			if (!com)
1807 			{
1808 				const HRESULT hResult = ::D3DXCreateTexture
1809 				(
1810 					&device,
1811 					desc.Width,
1812 					desc.Height,
1813 					1,
1814 					dynamicUsage ? D3DUSAGE_DYNAMIC : 0,
1815 					desiredFormat,
1816 					dynamicUsage ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED,
1817 					&com
1818 				);
1819 
1820 				if (SUCCEEDED(hResult))
1821 				{
1822 					if (FAILED(com->GetLevelDesc( 0, &desc )))
1823 						throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::GetLevelDesc()" );
1824 
1825 					if (desc.Width >= size.x && desc.Height >= size.y)
1826 					{
1827 						if (!GetBitsPerPixel())
1828 							throw Application::Exception( L"Unsupported bits-per-pixel format!" );
1829 
1830 						return true;
1831 					}
1832 					else
1833 					{
1834 						throw Application::Exception( L"Maximum texture dimension too small!" );
1835 					}
1836 				}
1837 				else if (hResult == D3DERR_DEVICELOST)
1838 				{
1839 					return false;
1840 				}
1841 				else
1842 				{
1843 					throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::CreateTexture()" );
1844 				}
1845 			}
1846 
1847 			return false;
1848 		}
1849 
ScreenTexture(D3DFORMAT f)1850 		Direct2D::Textures::ScreenTexture::ScreenTexture(D3DFORMAT f)
1851 		: Texture(0,f), useVidMem(false) {}
1852 
Update(const Adapter & adapter,Point newSize,const bool wantVidMem)1853 		void Direct2D::Textures::ScreenTexture::Update(const Adapter& adapter,Point newSize,const bool wantVidMem)
1854 		{
1855 			size = newSize;
1856 
1857 			if (!adapter.anyTextureSize)
1858 				newSize = GetSquared(newSize);
1859 
1860 			if (desc.Width != newSize.x || desc.Height != newSize.y)
1861 			{
1862 				desc.Width = newSize.x;
1863 				desc.Height = newSize.y;
1864 				Invalidate();
1865 			}
1866 
1867 			if (useVidMem != wantVidMem)
1868 			{
1869 				useVidMem = wantVidMem;
1870 				Invalidate();
1871 			}
1872 		}
1873 
Flush()1874 		void Direct2D::Textures::ScreenTexture::Flush()
1875 		{
1876 			if (desc.Pool == D3DPOOL_DEFAULT)
1877 				Invalidate();
1878 		}
1879 
Validate(IDirect3DDevice9 & device,const D3DFORMAT desiredFormat,const Adapter & adapter)1880 		bool Direct2D::Textures::ScreenTexture::Validate(IDirect3DDevice9& device,const D3DFORMAT desiredFormat,const Adapter& adapter)
1881 		{
1882 			if (!Texture::Validate( device, desiredFormat, useVidMem && adapter.videoMemScreen ) && !com)
1883 				return false;
1884 
1885 			device.SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
1886 			device.SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
1887 			device.SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
1888 
1889 			device.SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
1890 			device.SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );
1891 
1892 			if (FAILED(device.SetTexture( 0, *com )))
1893 				throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::SetTexture()" );
1894 
1895 			return true;
1896 		}
1897 
SaveToFile(wcstring const file,const D3DXIMAGE_FILEFORMAT type) const1898 		bool Direct2D::Textures::ScreenTexture::SaveToFile(wcstring const file,const D3DXIMAGE_FILEFORMAT type) const
1899 		{
1900 			NST_ASSERT( file && *file && size.x && size.y );
1901 
1902 			if (!com)
1903 				return false;
1904 
1905 			ComInterface<IDirect3DSurface9> surface;
1906 
1907 			{
1908 				ComInterface<IDirect3DDevice9> device;
1909 
1910 				if (FAILED(com->GetDevice( &device )))
1911 					return false;
1912 
1913 				if (FAILED(device->CreateOffscreenPlainSurface( size.x, size.y, D3DFMT_R8G8B8, D3DPOOL_SCRATCH, &surface, NULL )))
1914 					return false;
1915 			}
1916 
1917 			ulong masks[3];
1918 			GetBitMask(masks[0],masks[1],masks[2]);
1919 
1920 			uint shifts[3];
1921 
1922 			for (uint i=0; i < 3; ++i)
1923 			{
1924 				NST_ASSERT( masks[i] );
1925 
1926 				for (shifts[i]=0; !(masks[i] & 0x1); ++shifts[i])
1927 					masks[i] >>= 1;
1928 			}
1929 
1930 			D3DLOCKED_RECT dstLock;
1931 
1932 			if (FAILED(surface->LockRect( &dstLock, NULL, D3DLOCK_NOSYSLOCK )))
1933 				return false;
1934 
1935 			D3DLOCKED_RECT srcLock;
1936 			const RECT rect = {0,0,size.x,size.y};
1937 
1938 			if (FAILED(com->LockRect( 0, &srcLock, &rect, D3DLOCK_READONLY|D3DLOCK_NOSYSLOCK )))
1939 			{
1940 				surface->UnlockRect();
1941 				return false;
1942 			}
1943 
1944 			BYTE* NST_RESTRICT dst = static_cast<BYTE*>(dstLock.pBits);
1945 			const uint dstPitch = dstLock.Pitch - size.x * 3;
1946 
1947 			if (GetBitsPerPixel() == 16)
1948 			{
1949 				const WORD* NST_RESTRICT src = static_cast<WORD*>(srcLock.pBits);
1950 				const uint srcPitch = srcLock.Pitch - size.x * sizeof(WORD);
1951 
1952 				for (uint y=size.y; y; --y)
1953 				{
1954 					for (const BYTE* const end=dst+size.x*3; dst != end; dst += 3)
1955 					{
1956 						const uint pixel = *src++;
1957 
1958 						dst[2] = ((pixel >> shifts[0] & masks[0]) * 0xFF + masks[0] / 2) / masks[0];
1959 						dst[1] = ((pixel >> shifts[1] & masks[1]) * 0xFF + masks[1] / 2) / masks[1];
1960 						dst[0] = ((pixel >> shifts[2] & masks[2]) * 0xFF + masks[2] / 2) / masks[2];
1961 					}
1962 
1963 					src = reinterpret_cast<const WORD*>(reinterpret_cast<const BYTE*>(src) + srcPitch);
1964 					dst += dstPitch;
1965 				}
1966 			}
1967 			else
1968 			{
1969 				const DWORD* NST_RESTRICT src = static_cast<DWORD*>(srcLock.pBits);
1970 				const uint srcPitch = srcLock.Pitch - size.x * sizeof(DWORD);
1971 
1972 				for (uint y=size.y; y; --y)
1973 				{
1974 					for (const BYTE* const end=dst+size.x*3; dst != end; dst += 3)
1975 					{
1976 						const uint pixel = *src++;
1977 
1978 						dst[2] = ((pixel >> shifts[0] & masks[0]) * 0xFF + masks[0] / 2) / masks[0];
1979 						dst[1] = ((pixel >> shifts[1] & masks[1]) * 0xFF + masks[1] / 2) / masks[1];
1980 						dst[0] = ((pixel >> shifts[2] & masks[2]) * 0xFF + masks[2] / 2) / masks[2];
1981 					}
1982 
1983 					src = reinterpret_cast<const DWORD*>(reinterpret_cast<const BYTE*>(src) + srcPitch);
1984 					dst += dstPitch;
1985 				}
1986 			}
1987 
1988 			com->UnlockRect( 0 );
1989 			surface->UnlockRect();
1990 
1991 			return SUCCEEDED(::D3DXSaveSurfaceToFile( file, type, *surface, NULL, &rect ));
1992 		}
1993 
EffectTexture(D3DFORMAT f)1994 		Direct2D::Textures::EffectTexture::EffectTexture(D3DFORMAT f)
1995 		: Texture(1,f), scanlines(0), dirty(false) {}
1996 
Update(const Adapter & adapter,Point newSize,const uint newScanlines)1997 		void Direct2D::Textures::EffectTexture::Update(const Adapter& adapter,Point newSize,const uint newScanlines)
1998 		{
1999 			NST_ASSERT( newScanlines <= MAX_SCANLINES );
2000 
2001 			newSize.x = 1;
2002 			size = newSize;
2003 
2004 			if (!adapter.anyTextureSize)
2005 				newSize = GetSquared(newSize);
2006 
2007 			if (desc.Width != newSize.x || desc.Height != newSize.y)
2008 			{
2009 				desc.Width = newSize.x;
2010 				desc.Height = newSize.y;
2011 				Invalidate();
2012 			}
2013 
2014 			if (scanlines != newScanlines)
2015 			{
2016 				dirty = true;
2017 
2018 				if ((scanlines > 0) != (newScanlines > 0))
2019 					Invalidate();
2020 
2021 				scanlines = newScanlines;
2022 			}
2023 		}
2024 
Validate(IDirect3DDevice9 & device,const D3DFORMAT desiredFormat)2025 		bool Direct2D::Textures::EffectTexture::Validate(IDirect3DDevice9& device,const D3DFORMAT desiredFormat)
2026 		{
2027 			NST_ASSERT( !com || scanlines );
2028 
2029 			if (scanlines)
2030 			{
2031 				if (Texture::Validate( device, desiredFormat, false ))
2032 				{
2033 					dirty = true;
2034 				}
2035 				else if (!com)
2036 				{
2037 					return false;
2038 				}
2039 
2040 				if (dirty)
2041 				{
2042 					dirty = false;
2043 
2044 					ulong mask[3];
2045 					GetBitMask(mask[0],mask[1],mask[2]);
2046 
2047 					const uint intensity[2] =
2048 					{
2049 						mask[0] | mask[1] | mask[2],
2050 						((mask[0] * (100 - scanlines) / 100) & mask[0]) |
2051 						((mask[1] * (100 - scanlines) / 100) & mask[1]) |
2052 						((mask[2] * (100 - scanlines) / 100) & mask[2])
2053 					};
2054 
2055 					D3DLOCKED_RECT lockedRect;
2056 					const RECT rect = {0,0,desc.Width,desc.Height};
2057 					const HRESULT hResult = com->LockRect( 0, &lockedRect, &rect, D3DLOCK_NOSYSLOCK );
2058 
2059 					if (SUCCEEDED(hResult))
2060 					{
2061 						if (GetBitsPerPixel() == 32)
2062 						{
2063 							DWORD* NST_RESTRICT dst = static_cast<DWORD*>(lockedRect.pBits);
2064 							const uint pitch = lockedRect.Pitch - desc.Width * sizeof(DWORD);
2065 
2066 							for (uint y=desc.Height/2; y; --y)
2067 							{
2068 								for (uint i=0; i < 2; ++i)
2069 								{
2070 									for (uint x=desc.Width; x; --x)
2071 										*dst++ = intensity[i];
2072 
2073 									dst = reinterpret_cast<DWORD*>(reinterpret_cast<BYTE*>(dst) + pitch);
2074 								}
2075 							}
2076 						}
2077 						else
2078 						{
2079 							WORD* NST_RESTRICT dst = static_cast<WORD*>(lockedRect.pBits);
2080 							const uint pitch = lockedRect.Pitch - desc.Width * sizeof(WORD);
2081 
2082 							for (uint y=desc.Height/2; y; --y)
2083 							{
2084 								for (uint i=0; i < 2; ++i)
2085 								{
2086 									for (uint x=desc.Width; x; --x)
2087 										*dst++ = intensity[i];
2088 
2089 									dst = reinterpret_cast<WORD*>(reinterpret_cast<BYTE*>(dst) + pitch);
2090 								}
2091 							}
2092 						}
2093 
2094 						com->UnlockRect(0);
2095 					}
2096 					else if (hResult == D3DERR_DEVICELOST)
2097 					{
2098 						return false;
2099 					}
2100 					else
2101 					{
2102 						throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DTexture9::LockRect()" );
2103 					}
2104 
2105 					com->PreLoad();
2106 				}
2107 
2108 				device.SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_MODULATE );
2109 				device.SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
2110 				device.SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
2111 
2112 				device.SetSamplerState( 1, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP );
2113 				device.SetSamplerState( 1, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP );
2114 
2115 				if (FAILED(device.SetTexture( 1, *com )))
2116 					throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::SetTexture()" );
2117 			}
2118 
2119 			return true;
2120 		}
2121 
2122 		#ifdef NST_MSVC_OPTIMIZE
2123 		#pragma optimize("", on)
2124 		#endif
2125 	}
2126 }
2127