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 #ifndef NST_DIRECTX_DIRECT2D_H
26 #define NST_DIRECTX_DIRECT2D_H
27 
28 #pragma once
29 
30 #include <set>
31 #include <vector>
32 #include "NstWindowRect.hpp"
33 #include "NstDirectX.hpp"
34 
35 #ifdef NST_DEBUG
36 #define D3D_DEBUG_INFO
37 #endif
38 
39 #include <d3d9.h>
40 #include <d3dx9.h>
41 
42 namespace Nestopia
43 {
44 	namespace DirectX
45 	{
46 		class Direct2D
47 		{
48 		public:
49 
50 			explicit Direct2D(HWND);
51 			~Direct2D();
52 
53 			typedef Window::Rect Rect;
54 			typedef Window::Point Point;
55 
56 			enum
57 			{
58 				MAX_SCANLINES = 100
59 			};
60 
61 			struct Mode
62 			{
63 				typedef std::set<uchar> Rates;
64 
65 				explicit Mode(uint=0,uint=0,uint=0);
66 
67 				enum
68 				{
69 					MIN_WIDTH = 256,
70 					MIN_HEIGHT = 240,
71 					DEFAULT_RATE = 60,
72 					MAX_RATE = 120
73 				};
74 
75 				bool operator == (const Mode&) const;
76 				bool operator <  (const Mode&) const;
77 
78 				uint width, height, bpp;
79 				Rates rates;
80 
operator !=Nestopia::DirectX::Direct2D::Mode81 				bool operator != (const Mode& mode) const
82 				{
83 					return !(*this == mode);
84 				}
85 
SizeNestopia::DirectX::Direct2D::Mode86 				Point Size() const
87 				{
88 					return Point( width, height );
89 				}
90 			};
91 
92 			struct Adapter : BaseAdapter
93 			{
94 				typedef std::set<Mode> Modes;
95 
96 				enum DeviceType
97 				{
98 					DEVICE_HAL,
99 					DEVICE_HEL
100 				};
101 
102 				enum Filter
103 				{
104 					FILTER_NONE,
105 					FILTER_BILINEAR
106 				};
107 
108 				/** used to identify multiple monitors on the same video adapter */
109 				uint guidIndex;
110 				uint ordinal;
111 				DeviceType deviceType;
112 				Point maxScreenSize;
113 				bool videoMemScreen;
114 				bool anyTextureSize;
115 				bool canDoScanlineEffect;
116 				bool intervalTwo;
117 				bool intervalThree;
118 				bool intervalFour;
119 				bool modern;
120 				uint filters;
121 				Modes modes;
122 			};
123 
124 			typedef std::vector<Adapter> Adapters;
125 
126 			enum
127 			{
128 				RENDER_PICTURE = 0x01,
129 				RENDER_FPS     = 0x04,
130 				RENDER_MSG     = 0x08,
131 				RENDER_NFO     = 0x10
132 			};
133 
134 			void SelectAdapter(const Adapters::const_iterator);
135 			void RenderScreen(uint);
136 			bool CanSwitchFullscreen(const Adapter::Modes::const_iterator) const;
137 			bool SwitchFullscreen(const Adapter::Modes::const_iterator);
138 			bool SwitchWindowed();
139 			void UpdateWindowView();
140 			void UpdateWindowView(const Point&,const Rect&,uint,int,Adapter::Filter,bool);
141 			void UpdateFullscreenView(const Rect&,const Point&,const Rect&,uint,int,Adapter::Filter,bool);
142 			void UpdateFrameRate(uint,bool,bool);
143 			void EnableDialogBoxMode(bool);
144 			bool Repair();
145 
146 			enum ScreenShotResult
147 			{
148 				SCREENSHOT_OK,
149 				SCREENSHOT_UNSUPPORTED,
150 				SCREENSHOT_ERROR
151 			};
152 
153 			ScreenShotResult SaveScreenShot(wcstring,uint) const;
154 
155 		private:
156 
157 			enum
158 			{
159 				INVALID_RECT = MAKE_HRESULT(SEVERITY_ERROR,0x123,2),
160 				TSL_PATCHES = 32
161 			};
162 
163 			void FlushObjects();
164 			void InvalidateObjects();
165 			void ValidateObjects();
166 
167 			class Base
168 			{
169 			public:
170 
171 				Base();
172 				~Base();
173 
174 				inline operator IDirect3D9& () const;
175 
176 				static uint FormatToBpp(D3DFORMAT);
177 				static void FormatToMask(D3DFORMAT,ulong&,ulong&,ulong&);
178 
179 			private:
180 
181 				static IDirect3D9& Create();
182 				static const Adapters EnumerateAdapters(IDirect3D9&);
183 
184 				IDirect3D9& com;
185 				const Adapters adapters;
186 
187 			public:
188 
GetAdapters() const189 				const Adapters& GetAdapters() const
190 				{
191 					return adapters;
192 				}
193 
GetAdapter(uint i) const194 				const Adapter& GetAdapter(uint i) const
195 				{
196 					NST_ASSERT( i < adapters.size() );
197 					return adapters[i];
198 				}
199 			};
200 
201 			class Device
202 			{
203 			public:
204 
205 				Device(HWND,const Base&);
206 
207 				void Create(IDirect3D9&,const Adapter&);
208 
209 				bool CanSwitchFullscreen(const Mode&) const;
210 				bool CanToggleDialogBoxMode(bool) const;
211 				bool ResetFrameRate(uint,bool,bool,const Base&);
212 				uint GetMaxMessageLength() const;
213 
214 				void SwitchFullscreen(const Mode&);
215 				void SwitchWindowed();
216 
217 				NST_SINGLE_CALL HRESULT RenderScreen(uint,uint,uint) const;
218 
219 				HRESULT ResetWindowClient(const Point&,HRESULT);
220 				HRESULT ToggleDialogBoxMode();
221 				HRESULT Repair(HRESULT);
222 				HRESULT Reset();
223 
224 				inline operator IDirect3DDevice9& () const;
225 
226 			private:
227 
228 				void  Prepare() const;
229 				void  LogDisplaySwitch() const;
230 				uint  GetRefreshRate() const;
231 				DWORD GetPresentationFlags() const;
232 				D3DSWAPEFFECT GetSwapEffect() const;
233 				uint  GetDesiredPresentationRate(const Mode&) const;
234 				DWORD GetDesiredPresentationInterval(uint) const;
235 				DWORD GetDesiredPresentationInterval() const;
236 				bool  GetDisplayMode(D3DDISPLAYMODE&) const;
237 
238 				struct Timing
239 				{
240 					Timing();
241 
242 					bool autoHz;
243 					bool vsync;
244 					bool tripleBuffering;
245 					uchar frameRate;
246 				};
247 
248 				class Fonts
249 				{
250 				public:
251 
252 					Fonts();
253 
254 					void Create(const Device&);
255 					void Destroy(bool);
256 					void OnReset() const;
257 					void OnLost() const;
258 
259 					NST_SINGLE_CALL void Render(const D3DPRESENT_PARAMETERS&,uint) const;
260 
261 				private:
262 
263 					class Font
264 					{
265 					public:
266 
267 						void Create(const Device&);
268 						void Destroy();
269 						void Update(const GenericString&);
270 						uint Width() const;
271 						void OnReset() const;
272 						void OnLost() const;
273 
274 						inline bool CanDraw() const;
275 						inline void Draw(D3DCOLOR,DWORD,Rect) const;
276 
277 					private:
278 
279 						ComInterface<ID3DXFont> com;
280 						HeapString string;
281 						uint length;
282 
283 					public:
284 
Clear()285 						void Clear()
286 						{
287 							length = 0;
288 						}
289 					};
290 
291 					Font fps;
292 					Font msg;
293 					Font nfo;
294 					uint width;
295 
296 				public:
297 
UpdateFps(const GenericString & string)298 					void UpdateFps(const GenericString& string)
299 					{
300 						fps.Update( string );
301 					}
302 
ClearFps()303 					void ClearFps()
304 					{
305 						fps.Clear();
306 					}
307 
UpdateMsg(const GenericString & string)308 					void UpdateMsg(const GenericString& string)
309 					{
310 						msg.Update( string );
311 					}
312 
ClearMsg()313 					void ClearMsg()
314 					{
315 						msg.Clear();
316 					}
317 
UpdateNfo(const GenericString & string)318 					void UpdateNfo(const GenericString& string)
319 					{
320 						nfo.Update( string );
321 					}
322 
ClearNfo()323 					void ClearNfo()
324 					{
325 						nfo.Clear();
326 					}
327 
Width() const328 					uint Width() const
329 					{
330 						return width;
331 					}
332 				};
333 
334 				ComInterface<IDirect3DDevice9> com;
335 				Timing timing;
336 				Fonts fonts;
337 				uchar ordinal;
338 				uchar intervalTwo;
339 				uchar intervalThree;
340 				uchar intervalFour;
341 				D3DPRESENT_PARAMETERS presentation;
342 				bool dialogBoxMode;
343 
344 			public:
345 
GetOrdinal() const346 				uint GetOrdinal() const
347 				{
348 					return ordinal;
349 				}
350 
ClearScreen() const351 				HRESULT ClearScreen() const
352 				{
353 					return com->Clear( 0, NULL, D3DCLEAR_TARGET, 0, 1.f, 0 );
354 				}
355 
PresentScreen() const356 				HRESULT PresentScreen() const
357 				{
358 					return com->Present( NULL, NULL, NULL, NULL );
359 				}
360 
GetPresentation() const361 				const D3DPRESENT_PARAMETERS& GetPresentation() const
362 				{
363 					return presentation;
364 				}
365 
DrawFps(const GenericString & string)366 				void DrawFps(const GenericString& string)
367 				{
368 					fonts.UpdateFps( string );
369 				}
370 
ClearFps()371 				void ClearFps()
372 				{
373 					fonts.ClearFps();
374 				}
375 
DrawMsg(const GenericString & string)376 				void DrawMsg(const GenericString& string)
377 				{
378 					fonts.UpdateMsg( string );
379 				}
380 
ClearMsg()381 				void ClearMsg()
382 				{
383 					fonts.ClearMsg();
384 				}
385 
DrawNfo(const GenericString & string)386 				void DrawNfo(const GenericString& string)
387 				{
388 					fonts.UpdateNfo( string );
389 				}
390 
ClearNfo()391 				void ClearNfo()
392 				{
393 					fonts.ClearNfo();
394 				}
395 
EnableAutoFrequency(bool enable)396 				void EnableAutoFrequency(bool enable)
397 				{
398 					timing.autoHz = enable;
399 				}
400 
ThrottleRequired(uint speed) const401 				bool ThrottleRequired(uint speed) const
402 				{
403 					return
404 					(
405 						presentation.PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE ||
406 						speed != timing.frameRate
407 					);
408 				}
409 
SmoothFrameRate() const410 				bool SmoothFrameRate() const
411 				{
412 					return presentation.PresentationInterval != D3DPRESENT_INTERVAL_IMMEDIATE;
413 				}
414 			};
415 
416 			class Textures
417 			{
418 			public:
419 
420 				explicit Textures(D3DFORMAT);
421 
422 				void Update(const Adapter&,const Point&,uint,Adapter::Filter,bool);
423 				HRESULT Validate(IDirect3DDevice9&,const Adapter&,D3DFORMAT);
424 				void Invalidate();
425 				void Flush();
426 				bool SaveToFile(wcstring,D3DXIMAGE_FILEFORMAT) const;
427 
428 				inline double GetScreenLeftU(uint) const;
429 				inline double GetScreenRightU(uint) const;
430 				inline double GetScreenTopV(uint) const;
431 				inline double GetScreenBottomV(uint) const;
432 				inline double GetEffectLeftU(uint) const;
433 				inline double GetEffectRightU(uint) const;
434 				inline double GetEffectTopV(uint) const;
435 				inline double GetEffectBottomV(uint) const;
436 
437 			private:
438 
439 				class Texture : public ImplicitBool<Texture>
440 				{
441 				public:
442 
443 					void Invalidate();
444 
445 				protected:
446 
447 					explicit Texture(uint,D3DFORMAT);
448 					~Texture();
449 
450 					bool Validate(IDirect3DDevice9&,D3DFORMAT,bool);
451 
452 					static uint GetSquared(const Point&);
453 
454 					ComInterface<IDirect3DTexture9> com;
455 					Object::Pod<D3DSURFACE_DESC> desc;
456 					Point size;
457 					const uint stage;
458 
459 				public:
460 
operator !() const461 					bool operator ! () const
462 					{
463 						return !com;
464 					}
465 
GetBitMask(ulong & r,ulong & g,ulong & b) const466 					void GetBitMask(ulong& r,ulong& g,ulong& b) const
467 					{
468 						Base::FormatToMask( desc.Format, r, g, b );
469 					}
470 
GetBitsPerPixel() const471 					uint GetBitsPerPixel() const
472 					{
473 						return Base::FormatToBpp( desc.Format );
474 					}
475 
Lock(D3DLOCKED_RECT & lockedRect) const476 					NST_FORCE_INLINE HRESULT Lock(D3DLOCKED_RECT& lockedRect) const
477 					{
478 						const RECT rect = {0,0,size.x,size.y};
479 
480 						if (com)
481 						{
482 							return com->LockRect
483 							(
484 								0,
485 								&lockedRect,
486 								(desc.Usage & D3DUSAGE_DYNAMIC) ? NULL : &rect,
487 								(desc.Usage & D3DUSAGE_DYNAMIC) ? (D3DLOCK_DISCARD|D3DLOCK_NOSYSLOCK) : D3DLOCK_NOSYSLOCK
488 							);
489 						}
490 						else
491 						{
492 							return D3DERR_DEVICELOST;
493 						}
494 					}
495 
Unlock() const496 					NST_FORCE_INLINE void Unlock() const
497 					{
498 						com->UnlockRect( 0 );
499 					}
500 				};
501 
502 				class ScreenTexture : public Texture
503 				{
504 				public:
505 
506 					explicit ScreenTexture(D3DFORMAT);
507 
508 					void Update(const Adapter&,Point,bool);
509 					void Flush();
510 					bool Validate(IDirect3DDevice9&,D3DFORMAT,const Adapter&);
511 					bool SaveToFile(wcstring,D3DXIMAGE_FILEFORMAT) const;
512 
513 					inline double GetLeftU(uint) const;
514 					inline double GetRightU(uint) const;
515 					inline double GetTopV(uint) const;
516 					inline double GetBottomV(uint) const;
517 
518 				private:
519 
520 					bool useVidMem;
521 				};
522 
523 				class EffectTexture : public Texture
524 				{
525 				public:
526 
527 					explicit EffectTexture(D3DFORMAT);
528 
529 					void Update(const Adapter&,Point,uint);
530 					bool Validate(IDirect3DDevice9&,D3DFORMAT);
531 
532 					inline double GetLeftU(uint) const;
533 					inline double GetRightU(uint) const;
534 					inline double GetTopV(uint) const;
535 					inline double GetBottomV(uint) const;
536 
537 				private:
538 
539 					uint scanlines;
540 					bool dirty;
541 				};
542 
543 				ScreenTexture screenTexture;
544 				EffectTexture effectTexture;
545 				Adapter::Filter filter;
546 
547 			public:
548 
LockScreen(void * & data,long & pitch) const549 				NST_FORCE_INLINE HRESULT LockScreen(void*& data,long& pitch) const
550 				{
551 					D3DLOCKED_RECT lockedRect;
552 					const HRESULT hResult = screenTexture.Lock( lockedRect );
553 
554 					if (SUCCEEDED(hResult))
555 					{
556 						data = lockedRect.pBits;
557 						pitch = lockedRect.Pitch;
558 					}
559 
560 					return hResult;
561 				}
562 
UnlockScreen() const563 				NST_FORCE_INLINE void UnlockScreen() const
564 				{
565 					screenTexture.Unlock();
566 				}
567 
GetScreenBitMask(ulong & r,ulong & g,ulong & b) const568 				void GetScreenBitMask(ulong& r,ulong& g,ulong& b) const
569 				{
570 					screenTexture.GetBitMask( r, g, b );
571 				}
572 
GetScreenBitsPerPixel() const573 				uint GetScreenBitsPerPixel() const
574 				{
575 					return screenTexture.GetBitsPerPixel();
576 				}
577 			};
578 
579 			class VertexBuffer
580 			{
581 			public:
582 
583 				VertexBuffer();
584 				~VertexBuffer();
585 
586 				void Update(const Rect&,const Rect&,int);
587 				HRESULT Validate(IDirect3DDevice9&,const Textures&);
588 				void Invalidate();
589 
590 				inline uint NumVertices() const;
591 
592 			private:
593 
594 				enum
595 				{
596 					FVF = D3DFVF_XYZRHW|D3DFVF_TEX2
597 				};
598 
599 				#pragma pack(push,1)
600 
601 				struct Vertex
602 				{
603 					float x,y,z,rhw,u0,v0,u1,v1;
604 				};
605 
606 				#pragma pack(pop)
607 
608 				NST_COMPILE_ASSERT( sizeof(Vertex) == 32 );
609 
610 				ComInterface<IDirect3DVertexBuffer9> com;
611 				Rect rect;
612 				Rect clip;
613 				uint numVertices;
614 				int screenCurvature;
615 				bool dirty;
616 
617 			public:
618 
GetRect() const619 				const Rect& GetRect() const
620 				{
621 					return rect;
622 				}
623 			};
624 
625 			class IndexBuffer
626 			{
627 			public:
628 
629 				IndexBuffer();
630 				~IndexBuffer();
631 
632 				void Update(bool);
633 				HRESULT Validate(IDirect3DDevice9&);
634 				void Invalidate();
635 
636 				inline uint NumStrips() const;
637 
638 			private:
639 
640 				ComInterface<IDirect3DIndexBuffer9> com;
641 				uint numStrips;
642 			};
643 
644 			Base base;
645 			Device device;
646 			Textures textures;
647 			VertexBuffer vertexBuffer;
648 			IndexBuffer indexBuffer;
649 			HRESULT lastResult;
650 
651 		public:
652 
ValidScreen() const653 			bool ValidScreen() const
654 			{
655 				return SUCCEEDED(lastResult);
656 			}
657 
Windowed() const658 			bool Windowed() const
659 			{
660 				return device.GetPresentation().Windowed;
661 			}
662 
ThrottleRequired(uint speed) const663 			bool ThrottleRequired(uint speed) const
664 			{
665 				return device.ThrottleRequired( speed ) || FAILED(lastResult);
666 			}
667 
GetAdapters() const668 			const Adapters& GetAdapters() const
669 			{
670 				return base.GetAdapters();
671 			}
672 
GetAdapter() const673 			const Adapter& GetAdapter() const
674 			{
675 				return base.GetAdapter( device.GetOrdinal() );
676 			}
677 
GetBitsPerPixel() const678 			uint GetBitsPerPixel() const
679 			{
680 				return textures.GetScreenBitsPerPixel();
681 			}
682 
GetBitMask(ulong & r,ulong & g,ulong & b) const683 			void GetBitMask(ulong& r,ulong& g,ulong& b) const
684 			{
685 				textures.GetScreenBitMask( r, g, b );
686 			}
687 
GetScreenRect() const688 			const Rect& GetScreenRect() const
689 			{
690 				return vertexBuffer.GetRect();
691 			}
692 
LockScreen(void * & data,long & pitch)693 			NST_FORCE_INLINE bool LockScreen(void*& data,long& pitch)
694 			{
695 				if (SUCCEEDED(lastResult))
696 					lastResult = textures.LockScreen( data, pitch );
697 
698 				return SUCCEEDED(lastResult);
699 			}
700 
UnlockScreen() const701 			NST_FORCE_INLINE void UnlockScreen() const
702 			{
703 				NST_VERIFY( SUCCEEDED(lastResult) );
704 				textures.UnlockScreen();
705 			}
706 
ClearScreen()707 			bool ClearScreen()
708 			{
709 				if (SUCCEEDED(lastResult))
710 				{
711 					lastResult = device.ClearScreen();
712 					return SUCCEEDED(lastResult);
713 				}
714 				else
715 				{
716 					return lastResult == INVALID_RECT;
717 				}
718 			}
719 
PresentScreen()720 			bool PresentScreen()
721 			{
722 				if (SUCCEEDED(lastResult))
723 				{
724 					lastResult = device.PresentScreen();
725 					return SUCCEEDED(lastResult);
726 				}
727 				else
728 				{
729 					return lastResult == INVALID_RECT;
730 				}
731 			}
732 
DrawFps(const GenericString & string)733 			void DrawFps(const GenericString& string)
734 			{
735 				device.DrawFps( string );
736 			}
737 
ClearFps()738 			void ClearFps()
739 			{
740 				device.ClearFps();
741 			}
742 
DrawMsg(const GenericString & string)743 			void DrawMsg(const GenericString& string)
744 			{
745 				device.DrawMsg( string );
746 			}
747 
ClearMsg()748 			void ClearMsg()
749 			{
750 				device.ClearMsg();
751 			}
752 
DrawNfo(const GenericString & string)753 			void DrawNfo(const GenericString& string)
754 			{
755 				device.DrawNfo( string );
756 			}
757 
ClearNfo()758 			void ClearNfo()
759 			{
760 				device.ClearNfo();
761 			}
762 
EnableAutoFrequency(bool enable)763 			void EnableAutoFrequency(bool enable)
764 			{
765 				device.EnableAutoFrequency( enable );
766 			}
767 
GetMaxMessageLength() const768 			uint GetMaxMessageLength() const
769 			{
770 				return device.GetMaxMessageLength();
771 			}
772 
GetFullscreenDisplayMode() const773 			const Point GetFullscreenDisplayMode() const
774 			{
775 				return Point( device.GetPresentation().BackBufferWidth, device.GetPresentation().BackBufferHeight );
776 			}
777 
ModernGPU() const778 			bool ModernGPU() const
779 			{
780 				return base.GetAdapters()[device.GetOrdinal()].modern;
781 			}
782 
SmoothFrameRate() const783 			bool SmoothFrameRate() const
784 			{
785 				return device.SmoothFrameRate();
786 			}
787 		};
788 	}
789 }
790 
791 #endif
792