nestopia/source/win32/NstDirect2D.hpp

791 lines
15 KiB
C++

////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2003-2008 Martin Freij
//
// This file is part of Nestopia.
//
// Nestopia is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Nestopia is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Nestopia; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
////////////////////////////////////////////////////////////////////////////////////////
#ifndef NST_DIRECTX_DIRECT2D_H
#define NST_DIRECTX_DIRECT2D_H
#pragma once
#include <set>
#include <vector>
#include "NstWindowRect.hpp"
#include "NstDirectX.hpp"
#ifdef NST_DEBUG
#define D3D_DEBUG_INFO
#endif
#include <d3d9.h>
#include <d3dx9.h>
namespace Nestopia
{
namespace DirectX
{
class Direct2D
{
public:
explicit Direct2D(HWND);
~Direct2D();
typedef Window::Rect Rect;
typedef Window::Point Point;
enum
{
MAX_SCANLINES = 100
};
struct Mode
{
typedef std::set<uchar> Rates;
explicit Mode(uint=0,uint=0,uint=0);
enum
{
MIN_WIDTH = 256,
MIN_HEIGHT = 240,
DEFAULT_RATE = 60,
MAX_RATE = 120
};
bool operator == (const Mode&) const;
bool operator < (const Mode&) const;
uint width, height, bpp;
Rates rates;
bool operator != (const Mode& mode) const
{
return !(*this == mode);
}
Point Size() const
{
return Point( width, height );
}
};
struct Adapter : BaseAdapter
{
typedef std::set<Mode> Modes;
enum DeviceType
{
DEVICE_HAL,
DEVICE_HEL
};
enum Filter
{
FILTER_NONE,
FILTER_BILINEAR
};
/** used to identify multiple monitors on the same video adapter */
uint guidIndex;
uint ordinal;
DeviceType deviceType;
Point maxScreenSize;
bool videoMemScreen;
bool anyTextureSize;
bool canDoScanlineEffect;
bool intervalTwo;
bool intervalThree;
bool intervalFour;
bool modern;
uint filters;
Modes modes;
};
typedef std::vector<Adapter> Adapters;
enum
{
RENDER_PICTURE = 0x01,
RENDER_FPS = 0x04,
RENDER_MSG = 0x08,
RENDER_NFO = 0x10
};
void SelectAdapter(const Adapters::const_iterator);
void RenderScreen(uint);
bool CanSwitchFullscreen(const Adapter::Modes::const_iterator) const;
bool SwitchFullscreen(const Adapter::Modes::const_iterator);
bool SwitchWindowed();
void UpdateWindowView();
void UpdateWindowView(const Point&,const Rect&,uint,int,Adapter::Filter,bool);
void UpdateFullscreenView(const Rect&,const Point&,const Rect&,uint,int,Adapter::Filter,bool);
void UpdateFrameRate(uint,bool,bool);
void EnableDialogBoxMode(bool);
bool Repair();
enum ScreenShotResult
{
SCREENSHOT_OK,
SCREENSHOT_UNSUPPORTED,
SCREENSHOT_ERROR
};
ScreenShotResult SaveScreenShot(wcstring,uint) const;
private:
enum
{
INVALID_RECT = MAKE_HRESULT(SEVERITY_ERROR,0x123,2),
TSL_PATCHES = 32
};
void FlushObjects();
void InvalidateObjects();
void ValidateObjects();
class Base
{
public:
Base();
~Base();
inline operator IDirect3D9& () const;
static uint FormatToBpp(D3DFORMAT);
static void FormatToMask(D3DFORMAT,ulong&,ulong&,ulong&);
private:
static IDirect3D9& Create();
static const Adapters EnumerateAdapters(IDirect3D9&);
IDirect3D9& com;
const Adapters adapters;
public:
const Adapters& GetAdapters() const
{
return adapters;
}
const Adapter& GetAdapter(uint i) const
{
NST_ASSERT( i < adapters.size() );
return adapters[i];
}
};
class Device
{
public:
Device(HWND,const Base&);
void Create(IDirect3D9&,const Adapter&);
bool CanSwitchFullscreen(const Mode&) const;
bool CanToggleDialogBoxMode(bool) const;
bool ResetFrameRate(uint,bool,bool,const Base&);
uint GetMaxMessageLength() const;
void SwitchFullscreen(const Mode&);
void SwitchWindowed();
NST_SINGLE_CALL HRESULT RenderScreen(uint,uint,uint) const;
HRESULT ResetWindowClient(const Point&,HRESULT);
HRESULT ToggleDialogBoxMode();
HRESULT Repair(HRESULT);
HRESULT Reset();
inline operator IDirect3DDevice9& () const;
private:
void Prepare() const;
void LogDisplaySwitch() const;
uint GetRefreshRate() const;
DWORD GetPresentationFlags() const;
D3DSWAPEFFECT GetSwapEffect() const;
uint GetDesiredPresentationRate(const Mode&) const;
DWORD GetDesiredPresentationInterval(uint) const;
DWORD GetDesiredPresentationInterval() const;
bool GetDisplayMode(D3DDISPLAYMODE&) const;
struct Timing
{
Timing();
bool autoHz;
bool vsync;
bool tripleBuffering;
uchar frameRate;
};
class Fonts
{
public:
Fonts();
void Create(const Device&);
void Destroy(bool);
void OnReset() const;
void OnLost() const;
NST_SINGLE_CALL void Render(const D3DPRESENT_PARAMETERS&,uint) const;
private:
class Font
{
public:
void Create(const Device&);
void Destroy();
void Update(const GenericString&);
uint Width() const;
void OnReset() const;
void OnLost() const;
inline bool CanDraw() const;
inline void Draw(D3DCOLOR,DWORD,Rect) const;
private:
ComInterface<ID3DXFont> com;
HeapString string;
uint length;
public:
void Clear()
{
length = 0;
}
};
Font fps;
Font msg;
Font nfo;
uint width;
public:
void UpdateFps(const GenericString& string)
{
fps.Update( string );
}
void ClearFps()
{
fps.Clear();
}
void UpdateMsg(const GenericString& string)
{
msg.Update( string );
}
void ClearMsg()
{
msg.Clear();
}
void UpdateNfo(const GenericString& string)
{
nfo.Update( string );
}
void ClearNfo()
{
nfo.Clear();
}
uint Width() const
{
return width;
}
};
ComInterface<IDirect3DDevice9> com;
Timing timing;
Fonts fonts;
uchar ordinal;
uchar intervalTwo;
uchar intervalThree;
uchar intervalFour;
D3DPRESENT_PARAMETERS presentation;
bool dialogBoxMode;
public:
uint GetOrdinal() const
{
return ordinal;
}
HRESULT ClearScreen() const
{
return com->Clear( 0, NULL, D3DCLEAR_TARGET, 0, 1.f, 0 );
}
HRESULT PresentScreen() const
{
return com->Present( NULL, NULL, NULL, NULL );
}
const D3DPRESENT_PARAMETERS& GetPresentation() const
{
return presentation;
}
void DrawFps(const GenericString& string)
{
fonts.UpdateFps( string );
}
void ClearFps()
{
fonts.ClearFps();
}
void DrawMsg(const GenericString& string)
{
fonts.UpdateMsg( string );
}
void ClearMsg()
{
fonts.ClearMsg();
}
void DrawNfo(const GenericString& string)
{
fonts.UpdateNfo( string );
}
void ClearNfo()
{
fonts.ClearNfo();
}
void EnableAutoFrequency(bool enable)
{
timing.autoHz = enable;
}
bool ThrottleRequired(uint speed) const
{
return
(
presentation.PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE ||
speed != timing.frameRate
);
}
bool SmoothFrameRate() const
{
return presentation.PresentationInterval != D3DPRESENT_INTERVAL_IMMEDIATE;
}
};
class Textures
{
public:
explicit Textures(D3DFORMAT);
void Update(const Adapter&,const Point&,uint,Adapter::Filter,bool);
HRESULT Validate(IDirect3DDevice9&,const Adapter&,D3DFORMAT);
void Invalidate();
void Flush();
bool SaveToFile(wcstring,D3DXIMAGE_FILEFORMAT) const;
inline double GetScreenLeftU(uint) const;
inline double GetScreenRightU(uint) const;
inline double GetScreenTopV(uint) const;
inline double GetScreenBottomV(uint) const;
inline double GetEffectLeftU(uint) const;
inline double GetEffectRightU(uint) const;
inline double GetEffectTopV(uint) const;
inline double GetEffectBottomV(uint) const;
private:
class Texture : public ImplicitBool<Texture>
{
public:
void Invalidate();
protected:
explicit Texture(uint,D3DFORMAT);
~Texture();
bool Validate(IDirect3DDevice9&,D3DFORMAT,bool);
static uint GetSquared(const Point&);
ComInterface<IDirect3DTexture9> com;
Object::Pod<D3DSURFACE_DESC> desc;
Point size;
const uint stage;
public:
bool operator ! () const
{
return !com;
}
void GetBitMask(ulong& r,ulong& g,ulong& b) const
{
Base::FormatToMask( desc.Format, r, g, b );
}
uint GetBitsPerPixel() const
{
return Base::FormatToBpp( desc.Format );
}
NST_FORCE_INLINE HRESULT Lock(D3DLOCKED_RECT& lockedRect) const
{
const RECT rect = {0,0,size.x,size.y};
if (com)
{
return com->LockRect
(
0,
&lockedRect,
(desc.Usage & D3DUSAGE_DYNAMIC) ? NULL : &rect,
(desc.Usage & D3DUSAGE_DYNAMIC) ? (D3DLOCK_DISCARD|D3DLOCK_NOSYSLOCK) : D3DLOCK_NOSYSLOCK
);
}
else
{
return D3DERR_DEVICELOST;
}
}
NST_FORCE_INLINE void Unlock() const
{
com->UnlockRect( 0 );
}
};
class ScreenTexture : public Texture
{
public:
explicit ScreenTexture(D3DFORMAT);
void Update(const Adapter&,Point,bool);
void Flush();
bool Validate(IDirect3DDevice9&,D3DFORMAT,const Adapter&);
bool SaveToFile(wcstring,D3DXIMAGE_FILEFORMAT) const;
inline double GetLeftU(uint) const;
inline double GetRightU(uint) const;
inline double GetTopV(uint) const;
inline double GetBottomV(uint) const;
private:
bool useVidMem;
};
class EffectTexture : public Texture
{
public:
explicit EffectTexture(D3DFORMAT);
void Update(const Adapter&,Point,uint);
bool Validate(IDirect3DDevice9&,D3DFORMAT);
inline double GetLeftU(uint) const;
inline double GetRightU(uint) const;
inline double GetTopV(uint) const;
inline double GetBottomV(uint) const;
private:
uint scanlines;
bool dirty;
};
ScreenTexture screenTexture;
EffectTexture effectTexture;
Adapter::Filter filter;
public:
NST_FORCE_INLINE HRESULT LockScreen(void*& data,long& pitch) const
{
D3DLOCKED_RECT lockedRect;
const HRESULT hResult = screenTexture.Lock( lockedRect );
if (SUCCEEDED(hResult))
{
data = lockedRect.pBits;
pitch = lockedRect.Pitch;
}
return hResult;
}
NST_FORCE_INLINE void UnlockScreen() const
{
screenTexture.Unlock();
}
void GetScreenBitMask(ulong& r,ulong& g,ulong& b) const
{
screenTexture.GetBitMask( r, g, b );
}
uint GetScreenBitsPerPixel() const
{
return screenTexture.GetBitsPerPixel();
}
};
class VertexBuffer
{
public:
VertexBuffer();
~VertexBuffer();
void Update(const Rect&,const Rect&,int);
HRESULT Validate(IDirect3DDevice9&,const Textures&);
void Invalidate();
inline uint NumVertices() const;
private:
enum
{
FVF = D3DFVF_XYZRHW|D3DFVF_TEX2
};
#pragma pack(push,1)
struct Vertex
{
float x,y,z,rhw,u0,v0,u1,v1;
};
#pragma pack(pop)
NST_COMPILE_ASSERT( sizeof(Vertex) == 32 );
ComInterface<IDirect3DVertexBuffer9> com;
Rect rect;
Rect clip;
uint numVertices;
int screenCurvature;
bool dirty;
public:
const Rect& GetRect() const
{
return rect;
}
};
class IndexBuffer
{
public:
IndexBuffer();
~IndexBuffer();
void Update(bool);
HRESULT Validate(IDirect3DDevice9&);
void Invalidate();
inline uint NumStrips() const;
private:
ComInterface<IDirect3DIndexBuffer9> com;
uint numStrips;
};
Base base;
Device device;
Textures textures;
VertexBuffer vertexBuffer;
IndexBuffer indexBuffer;
HRESULT lastResult;
public:
bool ValidScreen() const
{
return SUCCEEDED(lastResult);
}
bool Windowed() const
{
return device.GetPresentation().Windowed;
}
bool ThrottleRequired(uint speed) const
{
return device.ThrottleRequired( speed ) || FAILED(lastResult);
}
const Adapters& GetAdapters() const
{
return base.GetAdapters();
}
const Adapter& GetAdapter() const
{
return base.GetAdapter( device.GetOrdinal() );
}
uint GetBitsPerPixel() const
{
return textures.GetScreenBitsPerPixel();
}
void GetBitMask(ulong& r,ulong& g,ulong& b) const
{
textures.GetScreenBitMask( r, g, b );
}
const Rect& GetScreenRect() const
{
return vertexBuffer.GetRect();
}
NST_FORCE_INLINE bool LockScreen(void*& data,long& pitch)
{
if (SUCCEEDED(lastResult))
lastResult = textures.LockScreen( data, pitch );
return SUCCEEDED(lastResult);
}
NST_FORCE_INLINE void UnlockScreen() const
{
NST_VERIFY( SUCCEEDED(lastResult) );
textures.UnlockScreen();
}
bool ClearScreen()
{
if (SUCCEEDED(lastResult))
{
lastResult = device.ClearScreen();
return SUCCEEDED(lastResult);
}
else
{
return lastResult == INVALID_RECT;
}
}
bool PresentScreen()
{
if (SUCCEEDED(lastResult))
{
lastResult = device.PresentScreen();
return SUCCEEDED(lastResult);
}
else
{
return lastResult == INVALID_RECT;
}
}
void DrawFps(const GenericString& string)
{
device.DrawFps( string );
}
void ClearFps()
{
device.ClearFps();
}
void DrawMsg(const GenericString& string)
{
device.DrawMsg( string );
}
void ClearMsg()
{
device.ClearMsg();
}
void DrawNfo(const GenericString& string)
{
device.DrawNfo( string );
}
void ClearNfo()
{
device.ClearNfo();
}
void EnableAutoFrequency(bool enable)
{
device.EnableAutoFrequency( enable );
}
uint GetMaxMessageLength() const
{
return device.GetMaxMessageLength();
}
const Point GetFullscreenDisplayMode() const
{
return Point( device.GetPresentation().BackBufferWidth, device.GetPresentation().BackBufferHeight );
}
bool ModernGPU() const
{
return base.GetAdapters()[device.GetOrdinal()].modern;
}
bool SmoothFrameRate() const
{
return device.SmoothFrameRate();
}
};
}
}
#endif