mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
2126 lines
55 KiB
C++
2126 lines
55 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
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "language/resource.h"
|
|
#include "NstResourceString.hpp"
|
|
#include "NstIoLog.hpp"
|
|
#include "NstApplicationException.hpp"
|
|
#include "NstDirect2D.hpp"
|
|
#include "NstIoScreen.hpp"
|
|
|
|
#if NST_MSVC
|
|
#pragma comment(lib,"d3d9")
|
|
#pragma comment(lib,"d3dx9")
|
|
#endif
|
|
|
|
namespace Nestopia
|
|
{
|
|
namespace DirectX
|
|
{
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("t", on)
|
|
#endif
|
|
|
|
inline Direct2D::Base::operator IDirect3D9& () const
|
|
{
|
|
return com;
|
|
}
|
|
|
|
inline Direct2D::Device::operator IDirect3DDevice9& () const
|
|
{
|
|
return **com;
|
|
}
|
|
|
|
inline bool Direct2D::Device::Fonts::Font::CanDraw() const
|
|
{
|
|
return length && com;
|
|
}
|
|
|
|
inline uint Direct2D::VertexBuffer::NumVertices() const
|
|
{
|
|
return numVertices;
|
|
}
|
|
|
|
inline uint Direct2D::IndexBuffer::NumStrips() const
|
|
{
|
|
return numStrips;
|
|
}
|
|
|
|
inline double Direct2D::Textures::ScreenTexture::GetLeftU(uint x) const
|
|
{
|
|
return double(NST_MIN(size.x,x)) / desc.Width;
|
|
}
|
|
|
|
inline double Direct2D::Textures::ScreenTexture::GetRightU(uint x) const
|
|
{
|
|
return double(NST_MIN(size.x,x)) / desc.Width;
|
|
}
|
|
|
|
inline double Direct2D::Textures::ScreenTexture::GetTopV(uint y) const
|
|
{
|
|
return double(NST_MIN(size.y,y)) / desc.Height;
|
|
}
|
|
|
|
inline double Direct2D::Textures::ScreenTexture::GetBottomV(uint y) const
|
|
{
|
|
return double(NST_MIN(size.y,y)) / desc.Height;
|
|
}
|
|
|
|
inline double Direct2D::Textures::EffectTexture::GetLeftU(uint) const
|
|
{
|
|
return 0.0;
|
|
}
|
|
|
|
inline double Direct2D::Textures::EffectTexture::GetRightU(uint) const
|
|
{
|
|
return 1.0;
|
|
}
|
|
|
|
inline double Direct2D::Textures::EffectTexture::GetTopV(uint y) const
|
|
{
|
|
return double(NST_MIN(size.y,y)) / desc.Height * 2;
|
|
}
|
|
|
|
inline double Direct2D::Textures::EffectTexture::GetBottomV(uint y) const
|
|
{
|
|
return double(NST_MIN(size.y,y)) / desc.Height * 2;
|
|
}
|
|
|
|
inline double Direct2D::Textures::GetScreenLeftU(uint x) const
|
|
{
|
|
return screenTexture.GetLeftU(x);
|
|
}
|
|
|
|
inline double Direct2D::Textures::GetScreenRightU(uint x) const
|
|
{
|
|
return screenTexture.GetRightU(x);
|
|
}
|
|
|
|
inline double Direct2D::Textures::GetScreenTopV(uint y) const
|
|
{
|
|
return screenTexture.GetTopV(y);
|
|
}
|
|
|
|
inline double Direct2D::Textures::GetScreenBottomV(uint y) const
|
|
{
|
|
return screenTexture.GetBottomV(y);
|
|
}
|
|
|
|
inline double Direct2D::Textures::GetEffectLeftU(uint x) const
|
|
{
|
|
return effectTexture.GetLeftU(x);
|
|
}
|
|
|
|
inline double Direct2D::Textures::GetEffectRightU(uint x) const
|
|
{
|
|
return effectTexture.GetRightU(x);
|
|
}
|
|
|
|
inline double Direct2D::Textures::GetEffectTopV(uint y) const
|
|
{
|
|
return effectTexture.GetTopV(y);
|
|
}
|
|
|
|
inline double Direct2D::Textures::GetEffectBottomV(uint y) const
|
|
{
|
|
return effectTexture.GetBottomV(y);
|
|
}
|
|
|
|
inline void Direct2D::Device::Fonts::Font::Draw(const D3DCOLOR color,const DWORD flags,Rect rect) const
|
|
{
|
|
com->DrawText( NULL, string.Ptr(), length, &rect, flags, color );
|
|
}
|
|
|
|
NST_SINGLE_CALL void Direct2D::Device::Fonts::Render(const D3DPRESENT_PARAMETERS& presentation,const uint state) const
|
|
{
|
|
const uint width = presentation.BackBufferWidth;
|
|
const uint height = presentation.BackBufferHeight;
|
|
|
|
if (!presentation.Windowed)
|
|
{
|
|
if ((state & RENDER_FPS) && fps.CanDraw())
|
|
{
|
|
for (uint i=0; i < 2; ++i)
|
|
{
|
|
fps.Draw
|
|
(
|
|
i ? D3DCOLOR_ARGB(0xFF,0xA5,0xB5,0x40) : D3DCOLOR_ARGB(0xFF,0x2A,0x35,0x10),
|
|
DT_SINGLELINE|TA_BOTTOM|TA_RIGHT|DT_NOCLIP,
|
|
Rect(width-31,height-31,width-i-3,height-i-3)
|
|
);
|
|
}
|
|
}
|
|
|
|
if ((state & RENDER_MSG) && msg.CanDraw())
|
|
{
|
|
for (uint i=0; i < 2; ++i)
|
|
{
|
|
msg.Draw
|
|
(
|
|
i ? D3DCOLOR_ARGB(0xFF,0xFF,0x20,0x20) : D3DCOLOR_ARGB(0xFF,0x20,0x20,0xA0),
|
|
DT_SINGLELINE|TA_BOTTOM|TA_LEFT|DT_NOCLIP,
|
|
Rect(4-i,height-31,width,height-i-3)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((state & RENDER_NFO) && nfo.CanDraw())
|
|
{
|
|
for (uint i=0; i < 2; ++i)
|
|
{
|
|
nfo.Draw
|
|
(
|
|
i ? D3DCOLOR_ARGB(0xFF,0x20,0xFF,0x20) : D3DCOLOR_ARGB(0xFF,0x20,0x60,0x20),
|
|
TA_TOP|TA_LEFT|DT_NOCLIP,
|
|
Rect(16-i,16-i,width,height)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
NST_SINGLE_CALL HRESULT Direct2D::Device::RenderScreen(const uint state,const uint numIndexedStrips,const uint numVertices) const
|
|
{
|
|
HRESULT hResult = com->BeginScene();
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
if (state & RENDER_PICTURE)
|
|
{
|
|
if (numIndexedStrips)
|
|
hResult = com->DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP, 0, 0, numVertices, 0, numIndexedStrips );
|
|
else
|
|
hResult = com->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );
|
|
}
|
|
|
|
fonts.Render( presentation, state );
|
|
com->EndScene();
|
|
}
|
|
|
|
return hResult;
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("", on)
|
|
#endif
|
|
|
|
Direct2D::Direct2D(HWND hWnd)
|
|
:
|
|
device ( hWnd, base ),
|
|
textures ( device.GetPresentation().BackBufferFormat ),
|
|
lastResult ( D3D_OK )
|
|
{
|
|
ValidateObjects();
|
|
}
|
|
|
|
Direct2D::~Direct2D()
|
|
{
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("t", on)
|
|
#endif
|
|
|
|
void Direct2D::InvalidateObjects()
|
|
{
|
|
indexBuffer.Invalidate();
|
|
vertexBuffer.Invalidate();
|
|
textures.Invalidate();
|
|
}
|
|
|
|
void Direct2D::FlushObjects()
|
|
{
|
|
textures.Flush();
|
|
}
|
|
|
|
void Direct2D::ValidateObjects()
|
|
{
|
|
if (SUCCEEDED(lastResult))
|
|
{
|
|
lastResult = textures.Validate( device, GetAdapter(), device.GetPresentation().BackBufferFormat );
|
|
|
|
if (SUCCEEDED(lastResult))
|
|
{
|
|
lastResult = indexBuffer.Validate( device );
|
|
|
|
if (SUCCEEDED(lastResult))
|
|
lastResult = vertexBuffer.Validate( device, textures );
|
|
}
|
|
}
|
|
}
|
|
|
|
void Direct2D::RenderScreen(uint state)
|
|
{
|
|
if (SUCCEEDED(lastResult))
|
|
lastResult = device.RenderScreen( state, indexBuffer.NumStrips(), vertexBuffer.NumVertices() );
|
|
}
|
|
|
|
/**
|
|
* Selects a video adapter and creates a new video device. Selection is ignored if the adapter is already selected.
|
|
*
|
|
* @param adapter The video adapter to select.
|
|
* @throws Application::Exception If an error occured creating or resetting the new video device.
|
|
* @see Application::Exception
|
|
*/
|
|
void Direct2D::SelectAdapter(const Adapters::const_iterator adapter)
|
|
{
|
|
if (device.GetOrdinal() != adapter->ordinal)
|
|
{
|
|
InvalidateObjects();
|
|
|
|
device.Create( base, *adapter );
|
|
if (FAILED(device.Reset()))
|
|
throw Application::Exception( L"Couldn't reset the new video device!" );
|
|
|
|
ValidateObjects();
|
|
lastResult = D3D_OK;
|
|
//lastResult = INVALID_RECT; //removed when implementing support for multiple monitor adapters
|
|
}
|
|
else
|
|
{
|
|
Io::Log() << "Selection of video adapter with ordinal \"" << adapter->ordinal << "\" was ignored as it is already selected.\r\n";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verifies if a switch to a fullscreen mode can be made or if the mode is already set to fullscreen.
|
|
*
|
|
* @param mode The video mode to set to fullscreen.
|
|
* @return True if switched to the fullscreen mode can be made. False if the mode is already set to fullscreen.
|
|
*/
|
|
bool Direct2D::CanSwitchFullscreen(const Adapter::Modes::const_iterator mode) const
|
|
{
|
|
return device.CanSwitchFullscreen( *mode );
|
|
}
|
|
|
|
/**
|
|
* Switches to a fullscreen mode. Switch is ignored if the specific mode is already set to fullscreen.
|
|
*
|
|
* @param mode The video mode to set to fullscreen.
|
|
* @return True if switched to fullscreen. False if the mode is already set in fullscreen.
|
|
*/
|
|
bool Direct2D::SwitchFullscreen(const Adapter::Modes::const_iterator mode)
|
|
{
|
|
if (CanSwitchFullscreen( mode ))
|
|
{
|
|
FlushObjects();
|
|
device.SwitchFullscreen( *mode );
|
|
lastResult = D3D_OK;
|
|
ValidateObjects();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Switches to window mode. Switch is ignored if already in window mode.
|
|
*
|
|
* @return True if switched to window mode. False if the mode is already in window mode.
|
|
*/
|
|
bool Direct2D::SwitchWindowed()
|
|
{
|
|
if (!device.GetPresentation().Windowed)
|
|
{
|
|
FlushObjects();
|
|
device.SwitchWindowed();
|
|
lastResult = D3D_OK;
|
|
ValidateObjects();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Direct2D::EnableDialogBoxMode(const bool enable)
|
|
{
|
|
if (device.CanToggleDialogBoxMode( enable ))
|
|
{
|
|
FlushObjects();
|
|
lastResult = device.ToggleDialogBoxMode();
|
|
ValidateObjects();
|
|
}
|
|
}
|
|
|
|
bool Direct2D::Repair()
|
|
{
|
|
if (FAILED(lastResult) && lastResult != INVALID_RECT)
|
|
{
|
|
FlushObjects();
|
|
lastResult = device.Repair( lastResult );
|
|
ValidateObjects();
|
|
}
|
|
|
|
return SUCCEEDED(lastResult);
|
|
}
|
|
|
|
void Direct2D::UpdateWindowView()
|
|
{
|
|
const Point::Picture picture( device.GetPresentation().hDeviceWindow );
|
|
|
|
if (picture.x > 0 && picture.y > 0)
|
|
{
|
|
const Point::Client client( device.GetPresentation().hDeviceWindow );
|
|
NST_ASSERT( client.x >= picture.x && client.y >= picture.y );
|
|
|
|
if
|
|
(
|
|
client.x != device.GetPresentation().BackBufferWidth ||
|
|
client.y != device.GetPresentation().BackBufferHeight ||
|
|
lastResult == INVALID_RECT
|
|
)
|
|
{
|
|
FlushObjects();
|
|
lastResult = device.ResetWindowClient( client, lastResult );
|
|
}
|
|
|
|
ValidateObjects();
|
|
}
|
|
else
|
|
{
|
|
lastResult = INVALID_RECT;
|
|
}
|
|
}
|
|
|
|
void Direct2D::UpdateWindowView
|
|
(
|
|
const Point& screen,
|
|
const Rect& clipping,
|
|
const uint scanlines,
|
|
const int screenCurvature,
|
|
const Adapter::Filter filter,
|
|
const bool useVidMem
|
|
)
|
|
{
|
|
const Point::Picture picture( device.GetPresentation().hDeviceWindow );
|
|
|
|
if (picture.x > 0 && picture.y > 0)
|
|
{
|
|
textures.Update( base.GetAdapter(device.GetOrdinal()), screen, scanlines, filter, useVidMem );
|
|
vertexBuffer.Update( picture, clipping, screenCurvature );
|
|
indexBuffer.Update( screenCurvature );
|
|
|
|
const Point::Client client( device.GetPresentation().hDeviceWindow );
|
|
NST_ASSERT( client.x >= picture.x && client.y >= picture.y );
|
|
|
|
if
|
|
(
|
|
client.x != device.GetPresentation().BackBufferWidth ||
|
|
client.y != device.GetPresentation().BackBufferHeight ||
|
|
lastResult == INVALID_RECT
|
|
)
|
|
{
|
|
FlushObjects();
|
|
lastResult = device.ResetWindowClient( client, lastResult );
|
|
}
|
|
|
|
ValidateObjects();
|
|
}
|
|
else
|
|
{
|
|
lastResult = INVALID_RECT;
|
|
}
|
|
}
|
|
|
|
void Direct2D::UpdateFullscreenView
|
|
(
|
|
const Rect& picture,
|
|
const Point& screen,
|
|
const Rect& clipping,
|
|
const uint scanlines,
|
|
const int screenCurvature,
|
|
const Adapter::Filter filter,
|
|
const bool useVidMem
|
|
)
|
|
{
|
|
NST_ASSERT( picture.Width() && picture.Height() );
|
|
|
|
textures.Update( base.GetAdapter(device.GetOrdinal()), screen, scanlines, filter, useVidMem );
|
|
vertexBuffer.Update( picture, clipping, screenCurvature );
|
|
indexBuffer.Update( screenCurvature );
|
|
ValidateObjects();
|
|
}
|
|
|
|
void Direct2D::UpdateFrameRate(const uint frameRate,const bool vsync,const bool tripleBuffering)
|
|
{
|
|
if (device.ResetFrameRate( frameRate, vsync, tripleBuffering, base ))
|
|
{
|
|
FlushObjects();
|
|
|
|
if (SUCCEEDED(lastResult))
|
|
{
|
|
lastResult = device.Reset();
|
|
ValidateObjects();
|
|
}
|
|
}
|
|
}
|
|
|
|
Direct2D::ScreenShotResult Direct2D::SaveScreenShot(wcstring const file,const uint ext) const
|
|
{
|
|
NST_ASSERT( file && *file );
|
|
|
|
if (SUCCEEDED(lastResult))
|
|
{
|
|
D3DXIMAGE_FILEFORMAT format;
|
|
|
|
switch (ext)
|
|
{
|
|
case MAKEFOURCC('p','n','g','\0'): format = D3DXIFF_PNG; break;
|
|
case MAKEFOURCC('j','p','g','\0'): format = D3DXIFF_JPG; break;
|
|
case MAKEFOURCC('b','m','p','\0'): format = D3DXIFF_BMP; break;
|
|
default: return SCREENSHOT_UNSUPPORTED;
|
|
}
|
|
|
|
if (textures.SaveToFile( file, format ))
|
|
return SCREENSHOT_OK;
|
|
}
|
|
|
|
return SCREENSHOT_ERROR;
|
|
}
|
|
|
|
Direct2D::Mode::Mode(uint w,uint h,uint b)
|
|
: width(w), height(h), bpp(b) {}
|
|
|
|
bool Direct2D::Mode::operator == (const Mode& mode) const
|
|
{
|
|
return width == mode.width && height == mode.height && bpp == mode.bpp;
|
|
}
|
|
|
|
bool Direct2D::Mode::operator < (const Mode& mode) const
|
|
{
|
|
if ( width < mode.width ) return true;
|
|
if ( width > mode.width ) return false;
|
|
if ( height < mode.height ) return true;
|
|
if ( height > mode.height ) return false;
|
|
if ( bpp < mode.bpp ) return true;
|
|
if ( bpp > mode.bpp ) return false;
|
|
|
|
return false;
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("", on)
|
|
#endif
|
|
|
|
Direct2D::Base::Base()
|
|
: com(Create()), adapters(EnumerateAdapters(com)) {}
|
|
|
|
Direct2D::Base::~Base()
|
|
{
|
|
com.Release();
|
|
}
|
|
|
|
IDirect3D9& Direct2D::Base::Create()
|
|
{
|
|
IDirect3D9* com;
|
|
|
|
if (NULL != (com = ::Direct3DCreate9( D3D_SDK_VERSION )))
|
|
{
|
|
return *com;
|
|
}
|
|
else if (NULL != (com = ::Direct3DCreate9( D3D9b_SDK_VERSION ))) // unofficial, it may work, it may not work
|
|
{
|
|
return *com;
|
|
}
|
|
else
|
|
{
|
|
throw Application::Exception( IDS_ERR_D3D_FAILED );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lists all video adapters and stores their information and properties.
|
|
*
|
|
* @param d3d The Direct3D9 object to fetch video adapters from.
|
|
*/
|
|
const Direct2D::Adapters Direct2D::Base::EnumerateAdapters(IDirect3D9& d3d)
|
|
{
|
|
NST_COMPILE_ASSERT( D3DADAPTER_DEFAULT == 0 );
|
|
|
|
Io::Log() << "Direct3D: initializing..\r\n";
|
|
|
|
Adapters adapters;
|
|
for (uint ordinal=0, numAdapters=d3d.GetAdapterCount(); ordinal < NST_MIN(numAdapters,255); ++ordinal)
|
|
{
|
|
D3DADAPTER_IDENTIFIER9 identifier;
|
|
|
|
if (SUCCEEDED(d3d.GetAdapterIdentifier( ordinal, 0, &identifier )))
|
|
{
|
|
uint guidIndex = 1; //the index of adapters of the same GUID
|
|
if (!adapters.empty())
|
|
{
|
|
for (Adapters::iterator adapterIterator = adapters.begin(); adapterIterator != adapters.end(); adapterIterator++) //for each previous found adapter
|
|
{
|
|
if (adapterIterator->guid == identifier.DeviceIdentifier) //if the adapter is of the same GUID
|
|
{
|
|
guidIndex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Io::Log() << "Direct3D: enumerating device - name: "
|
|
<< (*identifier.Description ? identifier.Description : "unknown")
|
|
<< ", GUID: "
|
|
<< System::Guid( identifier.DeviceIdentifier ).GetString()
|
|
<< ", index: "
|
|
<< guidIndex
|
|
<< "\r\n";
|
|
|
|
Adapter::Modes modes;
|
|
|
|
for (uint format=0; format < 2; ++format)
|
|
{
|
|
const D3DFORMAT type = (format ? D3DFMT_X8R8G8B8 : D3DFMT_R5G6B5);
|
|
|
|
for (uint mode=0, numModes=d3d.GetAdapterModeCount( ordinal, type ); mode < numModes; ++mode)
|
|
{
|
|
D3DDISPLAYMODE display;
|
|
|
|
if (FAILED(d3d.EnumAdapterModes( ordinal, type, mode, &display )))
|
|
continue;
|
|
|
|
if (display.Width < Mode::MIN_WIDTH || display.Height < Mode::MIN_HEIGHT || display.RefreshRate > Mode::MAX_RATE)
|
|
continue;
|
|
|
|
// C++ standard vagueness, sometimes set::iterator == set::const_iterator
|
|
const_cast<Mode::Rates&>(modes.insert(Mode( display.Width, display.Height, format ? 32 : 16 )).first->rates).insert( display.RefreshRate );
|
|
}
|
|
}
|
|
|
|
if (modes.empty())
|
|
{
|
|
Io::Log() << "Direct3D: found no valid display mode, continuing enumeration..\r\n";
|
|
}
|
|
else
|
|
{
|
|
D3DCAPS9 caps;
|
|
|
|
if (FAILED(d3d.GetDeviceCaps( ordinal, D3DDEVTYPE_HAL, &caps )))
|
|
{
|
|
if (FAILED(d3d.GetDeviceCaps( ordinal, D3DDEVTYPE_REF, &caps )))
|
|
{
|
|
Io::Log() << "Direct3D: warning, bogus device, continuing enumeration..\r\n";
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
Io::Log() << "Direct3D: performance warning, this is a REF device only!\r\n";
|
|
}
|
|
}
|
|
|
|
adapters.push_back( Adapter() );
|
|
Adapter& adapter = adapters.back();
|
|
|
|
adapter.guid = identifier.DeviceIdentifier;
|
|
adapter.guidIndex = guidIndex;
|
|
adapter.name = (*identifier.Description ? identifier.Description : "Unknown");
|
|
adapter.name.Trim();
|
|
adapter.name << Resource::String( IDS_DIALOG_VIDEO_ADAPTER_INDEX_SUFFIX ).Invoke( ValueString(guidIndex) ); //appends the GUID index suffix
|
|
adapter.ordinal = ordinal;
|
|
adapter.deviceType = (caps.DeviceType != D3DDEVTYPE_REF ? Adapter::DEVICE_HAL : Adapter::DEVICE_HEL);
|
|
adapter.maxScreenSize = Point(caps.MaxTextureWidth,caps.MaxTextureHeight);
|
|
adapter.videoMemScreen = caps.Caps2 & D3DCAPS2_DYNAMICTEXTURES;
|
|
adapter.anyTextureSize = (caps.TextureCaps & (D3DPTEXTURECAPS_SQUAREONLY|D3DPTEXTURECAPS_POW2|D3DPTEXTURECAPS_NONPOW2CONDITIONAL)) == 0;
|
|
adapter.canDoScanlineEffect = (caps.MaxSimultaneousTextures >= 2) && (caps.TextureOpCaps & D3DTEXOPCAPS_MODULATE) && (caps.TextureAddressCaps & D3DPTADDRESSCAPS_WRAP);
|
|
adapter.intervalTwo = caps.PresentationIntervals & D3DPRESENT_INTERVAL_TWO;
|
|
adapter.intervalThree = caps.PresentationIntervals & D3DPRESENT_INTERVAL_THREE;
|
|
adapter.intervalFour = caps.PresentationIntervals & D3DPRESENT_INTERVAL_FOUR;
|
|
adapter.filters = 0;
|
|
adapter.modern = (caps.PixelShaderVersion >= D3DPS_VERSION(2,0));
|
|
adapter.modes = modes;
|
|
|
|
if ((caps.TextureFilterCaps & (D3DPTFILTERCAPS_MINFLINEAR|D3DPTFILTERCAPS_MAGFLINEAR)) == (D3DPTFILTERCAPS_MINFLINEAR|D3DPTFILTERCAPS_MAGFLINEAR))
|
|
adapter.filters |= Adapter::FILTER_BILINEAR;
|
|
|
|
Io::Log log;
|
|
|
|
log << "Direct3D: dynamic textures: " << (adapter.videoMemScreen ? "supported\r\n" : "unsupported\r\n")
|
|
<< "Direct3D: texture bilinear filtering: " << ((adapter.filters & Adapter::FILTER_BILINEAR) ? "supported\r\n" : "unsupported\r\n")
|
|
<< "Direct3D: max texture dimensions: " << caps.MaxTextureWidth << 'x' << caps.MaxTextureHeight
|
|
<< "\r\nDirect3D: scanline effect: " << (adapter.canDoScanlineEffect ? "supported\r\n" : "unsupported\r\n")
|
|
<< "Direct3D: vsync on every second refresh: " << (adapter.intervalTwo ? "supported\r\n" : "unsupported\r\n")
|
|
<< "Direct3D: vsync on every third refresh: " << (adapter.intervalThree ? "supported\r\n" : "unsupported\r\n")
|
|
<< "Direct3D: found " << modes.size() << " display modes\r\n"
|
|
<< "Direct3D: supported monitor frequencies: ";
|
|
|
|
Mode::Rates rates;
|
|
|
|
for (Adapter::Modes::const_iterator it(modes.begin()), end(modes.end()); it != end; ++it)
|
|
rates.insert( it->rates.begin(), it->rates.end() );
|
|
|
|
for (Mode::Rates::const_iterator it(rates.begin()), end(rates.end());; )
|
|
{
|
|
log << uint(*it);
|
|
|
|
if (++it != end)
|
|
{
|
|
log << "hz, ";
|
|
}
|
|
else
|
|
{
|
|
log << "hz\r\n";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (adapters.empty())
|
|
throw Application::Exception( L"Found no valid display adapter!" );
|
|
|
|
return adapters;
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("t", on)
|
|
#endif
|
|
|
|
uint Direct2D::Base::FormatToBpp(const D3DFORMAT format)
|
|
{
|
|
switch (format)
|
|
{
|
|
case D3DFMT_X8R8G8B8:
|
|
case D3DFMT_X8B8G8R8:
|
|
case D3DFMT_A8R8G8B8:
|
|
case D3DFMT_A8B8G8R8:
|
|
case D3DFMT_A2R10G10B10:
|
|
case D3DFMT_A2B10G10R10:
|
|
return 32;
|
|
|
|
case D3DFMT_R5G6B5:
|
|
case D3DFMT_X1R5G5B5:
|
|
case D3DFMT_X4R4G4B4:
|
|
case D3DFMT_A1R5G5B5:
|
|
case D3DFMT_A4R4G4B4:
|
|
case D3DFMT_A8R3G3B2:
|
|
return 16;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Direct2D::Base::FormatToMask(const D3DFORMAT format,ulong& r,ulong& g,ulong& b)
|
|
{
|
|
switch (format)
|
|
{
|
|
case D3DFMT_X8R8G8B8:
|
|
case D3DFMT_A8R8G8B8: r = 0x00FF0000; g = 0x0000FF00; b = 0x000000FF; break;
|
|
case D3DFMT_X8B8G8R8:
|
|
case D3DFMT_A8B8G8R8: r = 0x000000FF; g = 0x0000FF00; b = 0x00FF0000; break;
|
|
case D3DFMT_A2R10G10B10: r = 0x3FF00000; g = 0x000FFC00; b = 0x000003FF; break;
|
|
case D3DFMT_A2B10G10R10: r = 0x000003FF; g = 0x000FFC00; b = 0x3FF00000; break;
|
|
case D3DFMT_R5G6B5: r = 0xF800; g = 0x07E0; b = 0x001F; break;
|
|
case D3DFMT_X1R5G5B5:
|
|
case D3DFMT_A1R5G5B5: r = 0x7C00; g = 0x03E0; b = 0x001F; break;
|
|
case D3DFMT_X4R4G4B4:
|
|
case D3DFMT_A4R4G4B4: r = 0x0F00; g = 0x00F0; b = 0x000F; break;
|
|
case D3DFMT_A8R3G3B2: r = 0x00E0; g = 0x001C; b = 0x0003; break;
|
|
default: r = 0; g = 0; b = 0; break;
|
|
}
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("", on)
|
|
#endif
|
|
|
|
Direct2D::Device::Device(HWND const hWnd,const Base& base)
|
|
{
|
|
NST_ASSERT( hWnd );
|
|
|
|
presentation.BackBufferWidth = 0;
|
|
presentation.BackBufferHeight = 0;
|
|
presentation.BackBufferFormat = D3DFMT_UNKNOWN;
|
|
presentation.BackBufferCount = 1;
|
|
presentation.MultiSampleType = D3DMULTISAMPLE_NONE;
|
|
presentation.MultiSampleQuality = 0;
|
|
presentation.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
|
presentation.hDeviceWindow = hWnd;
|
|
presentation.Windowed = true;
|
|
presentation.EnableAutoDepthStencil = false;
|
|
presentation.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
|
|
presentation.Flags = 0;
|
|
presentation.FullScreen_RefreshRateInHz = 0;
|
|
presentation.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
|
|
|
dialogBoxMode = false;
|
|
|
|
Create( base, base.GetAdapter(0) ); //always connects to first video device at start up
|
|
}
|
|
|
|
void Direct2D::Device::Create(IDirect3D9& d3d,const Adapter& adapter)
|
|
{
|
|
ordinal = adapter.ordinal;
|
|
intervalTwo = adapter.intervalTwo;
|
|
intervalThree = adapter.intervalThree;
|
|
intervalFour = adapter.intervalFour;
|
|
|
|
fonts.Destroy( true );
|
|
com.Release();
|
|
|
|
NST_VERIFY( !!Point::Client(presentation.hDeviceWindow) );
|
|
|
|
uint buffers = (timing.tripleBuffering ? 2 : 1);
|
|
presentation.BackBufferCount = buffers;
|
|
presentation.Flags = GetPresentationFlags();
|
|
presentation.SwapEffect = GetSwapEffect();
|
|
DWORD flags = D3DCREATE_PUREDEVICE|D3DCREATE_HARDWARE_VERTEXPROCESSING;
|
|
|
|
for (;;)
|
|
{
|
|
const HRESULT hResult = d3d.CreateDevice
|
|
(
|
|
adapter.ordinal,
|
|
adapter.deviceType == Adapter::DEVICE_HAL ? D3DDEVTYPE_HAL : D3DDEVTYPE_REF,
|
|
presentation.hDeviceWindow,
|
|
flags,
|
|
&presentation,
|
|
&com
|
|
);
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
break;
|
|
}
|
|
else if (hResult == D3DERR_DEVICELOST)
|
|
{
|
|
throw Application::Exception( L"Can't start! Direct3D is busy!" );
|
|
}
|
|
else if (buffers != presentation.BackBufferCount)
|
|
{
|
|
buffers = presentation.BackBufferCount;
|
|
Io::Log() << "Direct3D: Warning! IDirect3D9::CreateDevice() failed, retrying with one back-buffer only..\r\n";
|
|
}
|
|
else if (flags == (D3DCREATE_PUREDEVICE|D3DCREATE_HARDWARE_VERTEXPROCESSING))
|
|
{
|
|
flags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
|
|
Io::Log() << "Direct3D: Warning! IDirect3D9::CreateDevice() failed, retrying without a pure device..\r\n";
|
|
}
|
|
else if (flags == D3DCREATE_HARDWARE_VERTEXPROCESSING)
|
|
{
|
|
flags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
|
|
Io::Log() << "Direct3D: Warning! IDirect3D9::CreateDevice() failed, retrying with software vertex processing mode..\r\n";
|
|
}
|
|
else
|
|
{
|
|
if (HDC const hdc = ::GetDC( NULL ))
|
|
{
|
|
const int bits = ::GetDeviceCaps( hdc, BITSPIXEL );
|
|
::ReleaseDC( NULL, hdc );
|
|
|
|
if (bits && bits != 16 && bits != 32)
|
|
throw Application::Exception( IDS_ERR_BAD_BPP );
|
|
}
|
|
|
|
throw Application::Exception( IDS_ERR_D3D_DEVICE_FAILED );
|
|
}
|
|
}
|
|
|
|
Prepare();
|
|
fonts.Create( *this );
|
|
|
|
Io::Log() << "Direct3D: creating "
|
|
<< (adapter.deviceType == Adapter::DEVICE_HAL ? "HAL device #" : "REF device #")
|
|
<< adapter.ordinal
|
|
<< "\r\n";
|
|
|
|
LogDisplaySwitch();
|
|
}
|
|
|
|
bool Direct2D::Device::GetDisplayMode(D3DDISPLAYMODE& displayMode) const
|
|
{
|
|
IDirect3D9* base;
|
|
|
|
if (SUCCEEDED(com->GetDirect3D( &base )))
|
|
{
|
|
const HRESULT hResult = base->GetAdapterDisplayMode( ordinal, &displayMode );
|
|
base->Release();
|
|
|
|
if (SUCCEEDED(hResult))
|
|
return true;
|
|
}
|
|
|
|
displayMode.Width = presentation.BackBufferWidth;
|
|
displayMode.Height = presentation.BackBufferHeight;
|
|
|
|
return false;
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("t", on)
|
|
#endif
|
|
|
|
DWORD Direct2D::Device::GetPresentationFlags() const
|
|
{
|
|
return (dialogBoxMode && !presentation.Windowed) ? D3DPRESENTFLAG_LOCKABLE_BACKBUFFER : 0;
|
|
}
|
|
|
|
D3DSWAPEFFECT Direct2D::Device::GetSwapEffect() const
|
|
{
|
|
return (dialogBoxMode && !presentation.Windowed) || (presentation.BackBufferCount > 1) ? D3DSWAPEFFECT_DISCARD : D3DSWAPEFFECT_COPY;
|
|
}
|
|
|
|
uint Direct2D::Device::GetDesiredPresentationRate(const Mode& mode) const
|
|
{
|
|
if (presentation.Windowed)
|
|
{
|
|
return 0;
|
|
}
|
|
else if (timing.autoHz)
|
|
{
|
|
int match = INT_MAX;
|
|
Mode::Rates::const_iterator close(mode.rates.begin());
|
|
|
|
for (Mode::Rates::const_iterator it(mode.rates.end()), begin(mode.rates.begin());; )
|
|
{
|
|
--it;
|
|
|
|
for (uint i=5; --i; )
|
|
{
|
|
int diff = int(timing.frameRate * i) - int(*it);
|
|
|
|
if (diff == 0)
|
|
return *it;
|
|
|
|
if (diff < 0)
|
|
diff = int(*it) - int(timing.frameRate * i);
|
|
|
|
if (match > diff)
|
|
{
|
|
match = diff;
|
|
close = it;
|
|
}
|
|
}
|
|
|
|
if (it == begin)
|
|
break;
|
|
}
|
|
|
|
return *close;
|
|
}
|
|
else for (Mode::Rates::const_iterator it(mode.rates.begin()), end(mode.rates.end()); it != end; ++it)
|
|
{
|
|
if (*it == Mode::DEFAULT_RATE)
|
|
return Mode::DEFAULT_RATE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
DWORD Direct2D::Device::GetDesiredPresentationInterval(const uint rate) const
|
|
{
|
|
if (!timing.vsync || rate % timing.frameRate)
|
|
{
|
|
return D3DPRESENT_INTERVAL_IMMEDIATE;
|
|
}
|
|
else if (!presentation.Windowed)
|
|
{
|
|
if (timing.frameRate * 4 == rate && intervalFour)
|
|
{
|
|
return D3DPRESENT_INTERVAL_FOUR;
|
|
}
|
|
else if (timing.frameRate * 3 == rate && intervalThree)
|
|
{
|
|
return D3DPRESENT_INTERVAL_THREE;
|
|
}
|
|
else if (timing.frameRate * 2 == rate && intervalTwo)
|
|
{
|
|
return D3DPRESENT_INTERVAL_TWO;
|
|
}
|
|
}
|
|
|
|
return timing.frameRate == rate ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
|
|
}
|
|
|
|
uint Direct2D::Device::GetRefreshRate() const
|
|
{
|
|
if (presentation.Windowed)
|
|
{
|
|
D3DDISPLAYMODE mode;
|
|
return GetDisplayMode( mode ) ? mode.RefreshRate : 0;
|
|
}
|
|
else
|
|
{
|
|
return presentation.FullScreen_RefreshRateInHz;
|
|
}
|
|
}
|
|
|
|
DWORD Direct2D::Device::GetDesiredPresentationInterval() const
|
|
{
|
|
return GetDesiredPresentationInterval( GetRefreshRate() );
|
|
}
|
|
|
|
HRESULT Direct2D::Device::Reset()
|
|
{
|
|
fonts.OnLost();
|
|
|
|
const uint oldInterval = presentation.PresentationInterval;
|
|
presentation.PresentationInterval = GetDesiredPresentationInterval();
|
|
uint buffers = timing.tripleBuffering ? 2 : 1;
|
|
presentation.BackBufferCount = buffers;
|
|
presentation.Flags = GetPresentationFlags();
|
|
presentation.SwapEffect = GetSwapEffect();
|
|
|
|
for (;;)
|
|
{
|
|
const HRESULT hResult = com->Reset( &presentation );
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
break;
|
|
}
|
|
else if (hResult == D3DERR_DEVICELOST)
|
|
{
|
|
return D3DERR_DEVICELOST;
|
|
}
|
|
else if (buffers != presentation.BackBufferCount)
|
|
{
|
|
buffers = presentation.BackBufferCount;
|
|
Io::Log() << "Direct3D: Warning! IDirect3DDevice9::Reset() failed, retrying with one back-buffer only..\r\n";
|
|
}
|
|
else throw Application::Exception
|
|
(
|
|
IDS_ERR_FAILED,
|
|
hResult == D3DERR_INVALIDCALL ? L"IDirect3DDevice9::Reset() (code: D3DERR_INVALIDCALL)" :
|
|
hResult == D3DERR_OUTOFVIDEOMEMORY ? L"IDirect3DDevice9::Reset() (code: D3DERR_OUTOFVIDEOMEMORY)" :
|
|
hResult == D3DERR_DRIVERINTERNALERROR ? L"IDirect3DDevice9::Reset() (code: D3DERR_DRIVERINTERNALERROR)" :
|
|
hResult == E_OUTOFMEMORY ? L"IDirect3DDevice9::Reset() (code: E_OUTOFMEMORY)" :
|
|
L"IDirect3DDevice9::Reset()"
|
|
);
|
|
}
|
|
|
|
if (!presentation.Windowed && dialogBoxMode && FAILED(com->SetDialogBoxMode( true )))
|
|
throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::SetDialogBoxMode()" );
|
|
|
|
Prepare();
|
|
fonts.OnReset();
|
|
|
|
if (presentation.PresentationInterval != oldInterval)
|
|
{
|
|
Io::Log() <<
|
|
(
|
|
presentation.PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE ? "Direct3D: disabling VSYNC\r\n" :
|
|
presentation.PresentationInterval == D3DPRESENT_INTERVAL_TWO ? "Direct3D: enabling VSYNC on second refresh\r\n" :
|
|
presentation.PresentationInterval == D3DPRESENT_INTERVAL_THREE ? "Direct3D: enabling VSYNC on third refresh\r\n" :
|
|
"Direct3D: enabling VSYNC\r\n"
|
|
);
|
|
}
|
|
|
|
if (!presentation.Windowed && ::GetMenu( presentation.hDeviceWindow ))
|
|
::DrawMenuBar( presentation.hDeviceWindow );
|
|
|
|
return D3D_OK;
|
|
}
|
|
|
|
void Direct2D::Device::Prepare() const
|
|
{
|
|
com->SetRenderState( D3DRS_ZWRITEENABLE, false );
|
|
com->SetRenderState( D3DRS_COLORVERTEX, false );
|
|
com->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
|
|
com->SetRenderState( D3DRS_LIGHTING, false );
|
|
}
|
|
|
|
void Direct2D::Device::LogDisplaySwitch() const
|
|
{
|
|
Io::Log log;
|
|
log << "Direct3D: entering ";
|
|
|
|
D3DDISPLAYMODE mode;
|
|
|
|
if (GetDisplayMode( mode ))
|
|
{
|
|
log << mode.Width
|
|
<< 'x'
|
|
<< mode.Height
|
|
<< 'x'
|
|
<< Base::FormatToBpp(mode.Format)
|
|
<< ' '
|
|
<< mode.RefreshRate
|
|
<< "hz ";
|
|
}
|
|
|
|
log << (presentation.Windowed ? "window mode\r\n" : "full-screen mode\r\n");
|
|
}
|
|
|
|
uint Direct2D::Device::GetMaxMessageLength() const
|
|
{
|
|
return fonts.Width() ? (presentation.BackBufferWidth - fonts.Width() * 7) / fonts.Width() : 64;
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("", on)
|
|
#endif
|
|
|
|
HRESULT Direct2D::Device::Repair(const HRESULT lastError)
|
|
{
|
|
NST_ASSERT( FAILED(lastError) );
|
|
|
|
uint id = 0;
|
|
wcstring msg;
|
|
|
|
switch (lastError)
|
|
{
|
|
case D3DERR_DEVICELOST:
|
|
case D3DERR_DEVICENOTRESET:
|
|
|
|
switch (com->TestCooperativeLevel())
|
|
{
|
|
case D3DERR_DEVICELOST:
|
|
|
|
return D3DERR_DEVICELOST;
|
|
|
|
case D3DERR_DEVICENOTRESET:
|
|
|
|
return Reset();
|
|
|
|
case D3DERR_DRIVERINTERNALERROR:
|
|
|
|
msg = L"Internal video driver error! Try upgrading it!";
|
|
break;
|
|
|
|
default:
|
|
|
|
id = IDS_ERR_FAILED;
|
|
msg = L"IDirect3DDevice9::TestCooperativeLevel()";
|
|
break;
|
|
}
|
|
|
|
case D3DERR_DRIVERINTERNALERROR:
|
|
|
|
msg = L"Internal video driver error! Try upgrading it!";
|
|
break;
|
|
|
|
case E_OUTOFMEMORY:
|
|
|
|
msg = L"Out of memory!";
|
|
break;
|
|
|
|
case D3DERR_OUTOFVIDEOMEMORY:
|
|
|
|
msg = L"Out of video memory!";
|
|
break;
|
|
|
|
default:
|
|
|
|
msg = L"Unknown Direct3D error!";
|
|
break;
|
|
}
|
|
|
|
throw Application::Exception( id, msg );
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("t", on)
|
|
#endif
|
|
|
|
bool Direct2D::Device::CanToggleDialogBoxMode(bool enable) const
|
|
{
|
|
return !presentation.Windowed && dialogBoxMode != enable;
|
|
}
|
|
|
|
/**
|
|
* Verifies if a switch to a fullscreen mode can be made or if the mode is already set to fullscreen.
|
|
*
|
|
* @param mode The video mode to set to fullscreen.
|
|
* @return True if switched to the fullscreen mode can be made. False if the mode is already set to fullscreen.
|
|
*/
|
|
bool Direct2D::Device::CanSwitchFullscreen(const Mode& mode) const
|
|
{
|
|
return
|
|
(
|
|
presentation.Windowed ||
|
|
presentation.BackBufferWidth != mode.width ||
|
|
presentation.BackBufferHeight != mode.height ||
|
|
presentation.BackBufferFormat != (mode.bpp == 16 ? D3DFMT_R5G6B5 : D3DFMT_X8R8G8B8) ||
|
|
presentation.FullScreen_RefreshRateInHz != GetDesiredPresentationRate( mode )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Switches to fullscreen mode. Creates fonts to be displayed in fullscreen.
|
|
*
|
|
* @param mode The video mode to set to fullscreen.
|
|
* @throws Application::Exception If failed switch to fullscreen.
|
|
* @see Application::Exception
|
|
*/
|
|
void Direct2D::Device::SwitchFullscreen(const Mode& mode)
|
|
{
|
|
presentation.Windowed = false;
|
|
presentation.BackBufferWidth = mode.width;
|
|
presentation.BackBufferHeight = mode.height;
|
|
presentation.BackBufferFormat = (mode.bpp == 16 ? D3DFMT_R5G6B5 : D3DFMT_X8R8G8B8);
|
|
presentation.FullScreen_RefreshRateInHz = GetDesiredPresentationRate( mode );
|
|
|
|
if (FAILED(Reset()))
|
|
throw Application::Exception( L"Couldn't switch display mode!" );
|
|
|
|
fonts.Create( *this );
|
|
LogDisplaySwitch();
|
|
}
|
|
|
|
HRESULT Direct2D::Device::ToggleDialogBoxMode()
|
|
{
|
|
NST_ASSERT( !presentation.Windowed );
|
|
|
|
if (dialogBoxMode)
|
|
{
|
|
dialogBoxMode = false;
|
|
com->SetDialogBoxMode( false );
|
|
}
|
|
else
|
|
{
|
|
dialogBoxMode = true;
|
|
}
|
|
|
|
return Reset();
|
|
}
|
|
|
|
/**
|
|
* Switches to window mode. Destroys any fullscreen fonts and creates fonts to be displayed in status bar.
|
|
*
|
|
* @throws Application::Exception If failed switch to window mode.
|
|
* @see Application::Exception
|
|
*/
|
|
void Direct2D::Device::SwitchWindowed()
|
|
{
|
|
fonts.Destroy( false );
|
|
|
|
presentation.Windowed = true;
|
|
presentation.BackBufferWidth = 0;
|
|
presentation.BackBufferHeight = 0;
|
|
presentation.BackBufferFormat = D3DFMT_UNKNOWN;
|
|
presentation.FullScreen_RefreshRateInHz = 0;
|
|
|
|
if (dialogBoxMode)
|
|
{
|
|
dialogBoxMode = false;
|
|
com->SetDialogBoxMode( false );
|
|
}
|
|
|
|
if (FAILED(Reset()))
|
|
throw Application::Exception( L"Couldn't switch display mode!" );
|
|
|
|
fonts.Create( *this );
|
|
LogDisplaySwitch();
|
|
}
|
|
|
|
HRESULT Direct2D::Device::ResetWindowClient(const Point& client,HRESULT hResult)
|
|
{
|
|
NST_ASSERT( presentation.Windowed && client.x > 0 && client.y > 0 );
|
|
|
|
presentation.BackBufferWidth = client.x;
|
|
presentation.BackBufferHeight = client.y;
|
|
|
|
if (SUCCEEDED(hResult) || hResult == INVALID_RECT)
|
|
hResult = Reset();
|
|
|
|
return hResult;
|
|
}
|
|
|
|
bool Direct2D::Device::ResetFrameRate(uint frameRate,bool vsync,bool tripleBuffering,const Base& base)
|
|
{
|
|
timing.frameRate = frameRate;
|
|
timing.vsync = vsync;
|
|
|
|
bool update = false;
|
|
|
|
if (timing.tripleBuffering != tripleBuffering)
|
|
{
|
|
timing.tripleBuffering = tripleBuffering;
|
|
update = true;
|
|
}
|
|
|
|
if (!presentation.Windowed)
|
|
{
|
|
const Mode mode
|
|
(
|
|
presentation.BackBufferWidth,
|
|
presentation.BackBufferHeight,
|
|
presentation.BackBufferFormat == D3DFMT_X8R8G8B8 ? 32 : 16
|
|
);
|
|
|
|
frameRate = GetDesiredPresentationRate( *base.GetAdapter(ordinal).modes.find(mode) );
|
|
|
|
if (presentation.FullScreen_RefreshRateInHz != frameRate)
|
|
{
|
|
presentation.FullScreen_RefreshRateInHz = frameRate;
|
|
update = true;
|
|
}
|
|
}
|
|
|
|
return update || presentation.PresentationInterval != GetDesiredPresentationInterval();
|
|
}
|
|
|
|
Direct2D::Device::Fonts::Fonts()
|
|
: width(0) {}
|
|
|
|
void Direct2D::Device::Fonts::Font::Create(const Device& device)
|
|
{
|
|
wcstring fontName = L"System";
|
|
uint fontHeight = 12;
|
|
|
|
D3DDISPLAYMODE mode;
|
|
device.GetDisplayMode( mode );
|
|
|
|
if (mode.Width > 320 && mode.Height > 240)
|
|
{
|
|
fontHeight = mode.Height / (device.presentation.Windowed ? 32 : 16);
|
|
|
|
switch (PRIMARYLANGID(::GetUserDefaultLangID()))
|
|
{
|
|
case LANG_JAPANESE: fontName = L"MS Gothic"; break;
|
|
case LANG_CHINESE: fontName = L"MS Hei"; break;
|
|
case LANG_KOREAN: fontName = L"GulimChe"; break;
|
|
default: fontName = L"Arial"; break;
|
|
}
|
|
}
|
|
|
|
if (com)
|
|
{
|
|
Object::Pod<D3DXFONT_DESC> desc;
|
|
com->GetDesc( &desc );
|
|
|
|
if (desc.Height == int(fontHeight) && bool(desc.Width > 320 && desc.Height > 240) == bool(mode.Width > 320 && mode.Height > 240))
|
|
return;
|
|
|
|
com.Release();
|
|
}
|
|
|
|
::D3DXCreateFont
|
|
(
|
|
*device.com,
|
|
fontHeight,
|
|
0,
|
|
FW_NORMAL,
|
|
1,
|
|
false,
|
|
DEFAULT_CHARSET,
|
|
OUT_DEFAULT_PRECIS,
|
|
DEFAULT_QUALITY,
|
|
DEFAULT_PITCH|FF_DONTCARE,
|
|
fontName,
|
|
&com
|
|
);
|
|
}
|
|
|
|
uint Direct2D::Device::Fonts::Font::Width() const
|
|
{
|
|
TEXTMETRIC metric;
|
|
|
|
if (com && SUCCEEDED(com->GetTextMetrics( &metric )))
|
|
return metric.tmAveCharWidth;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void Direct2D::Device::Fonts::Font::Destroy()
|
|
{
|
|
length = 0;
|
|
com.Release();
|
|
}
|
|
|
|
void Direct2D::Device::Fonts::Font::OnReset() const
|
|
{
|
|
if (com)
|
|
com->OnResetDevice();
|
|
}
|
|
|
|
void Direct2D::Device::Fonts::Font::OnLost() const
|
|
{
|
|
if (com)
|
|
com->OnLostDevice();
|
|
}
|
|
|
|
void Direct2D::Device::Fonts::Font::Update(const GenericString& newstring)
|
|
{
|
|
string = newstring;
|
|
length = newstring.Length();
|
|
|
|
if (length && com)
|
|
com->PreloadText( string.Ptr(), string.Length() );
|
|
}
|
|
|
|
void Direct2D::Device::Fonts::Create(const Device& device)
|
|
{
|
|
nfo.Create( device );
|
|
|
|
if (!device.presentation.Windowed)
|
|
{
|
|
fps.Create( device );
|
|
msg.Create( device );
|
|
}
|
|
|
|
width = nfo.Width();
|
|
}
|
|
|
|
void Direct2D::Device::Fonts::Destroy(const bool newDevice)
|
|
{
|
|
width = 0;
|
|
|
|
fps.Destroy();
|
|
msg.Destroy();
|
|
|
|
if (newDevice)
|
|
nfo.Destroy();
|
|
else
|
|
nfo.OnReset();
|
|
}
|
|
|
|
void Direct2D::Device::Fonts::OnReset() const
|
|
{
|
|
fps.OnReset();
|
|
msg.OnReset();
|
|
nfo.OnReset();
|
|
}
|
|
|
|
void Direct2D::Device::Fonts::OnLost() const
|
|
{
|
|
fps.OnLost();
|
|
msg.OnLost();
|
|
nfo.OnLost();
|
|
}
|
|
|
|
Direct2D::Device::Timing::Timing()
|
|
:
|
|
autoHz (false),
|
|
vsync (false),
|
|
tripleBuffering (false),
|
|
frameRate (Mode::DEFAULT_RATE)
|
|
{
|
|
}
|
|
|
|
Direct2D::VertexBuffer::VertexBuffer()
|
|
: numVertices(4), screenCurvature(0), dirty(false) {}
|
|
|
|
Direct2D::VertexBuffer::~VertexBuffer()
|
|
{
|
|
Invalidate();
|
|
}
|
|
|
|
void Direct2D::VertexBuffer::Update(const Rect& picture,const Rect& c,const int s)
|
|
{
|
|
NST_ASSERT( picture.Width() > 0 && picture.Height() > 0 && c.Width() > 0 && c.Height() > 0 );
|
|
|
|
dirty = true;
|
|
|
|
rect = picture;
|
|
clip = c;
|
|
screenCurvature = s;
|
|
|
|
const uint n = (s ? (TSL_PATCHES+1) * (TSL_PATCHES+1) : 4);
|
|
|
|
if (numVertices != n)
|
|
{
|
|
numVertices = n;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void Direct2D::VertexBuffer::Invalidate()
|
|
{
|
|
if (com)
|
|
{
|
|
IDirect3DDevice9* device;
|
|
|
|
if (SUCCEEDED(com->GetDevice( &device )))
|
|
{
|
|
device->SetStreamSource( 0, NULL, 0, 0 );
|
|
device->Release();
|
|
}
|
|
|
|
com.Release();
|
|
}
|
|
}
|
|
|
|
HRESULT Direct2D::VertexBuffer::Validate(IDirect3DDevice9& device,const Textures& textures)
|
|
{
|
|
if (!com)
|
|
{
|
|
const HRESULT hResult = device.CreateVertexBuffer
|
|
(
|
|
numVertices * sizeof(Vertex),
|
|
D3DUSAGE_WRITEONLY,
|
|
FVF,
|
|
D3DPOOL_MANAGED,
|
|
&com,
|
|
NULL
|
|
);
|
|
|
|
if (FAILED(hResult))
|
|
{
|
|
if (hResult == D3DERR_DEVICELOST)
|
|
return D3DERR_DEVICELOST;
|
|
else
|
|
throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::CreateVertexBuffer()" );
|
|
}
|
|
|
|
dirty = true;
|
|
}
|
|
|
|
if (dirty)
|
|
{
|
|
dirty = false;
|
|
|
|
void* ptr;
|
|
const HRESULT hResult = com->Lock( 0, 0, &ptr, D3DLOCK_NOSYSLOCK );
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
Vertex* NST_RESTRICT v = static_cast<Vertex*>(ptr);
|
|
|
|
dirty = true;
|
|
|
|
if (screenCurvature)
|
|
{
|
|
const float z = 1.f - screenCurvature / 40.f;
|
|
const D3DXVECTOR2 p0( rect.left - 0.499f, rect.top - 0.499f );
|
|
const D3DXVECTOR2 p1( rect.right - 0.499f, rect.bottom - 0.499f );
|
|
const D3DXVECTOR2 t( rect.Width() * z, rect.Height() * z );
|
|
|
|
for (uint y=0; y <= TSL_PATCHES; ++y)
|
|
{
|
|
D3DXVECTOR2 vy;
|
|
|
|
float weight = y / float(TSL_PATCHES);
|
|
::D3DXVec2Hermite( &vy, &p0, &t, &p1, &t, weight );
|
|
|
|
vy.x = textures.GetScreenBottomV(clip.top + clip.Height() * weight);
|
|
float x1 = textures.GetEffectBottomV(clip.top + clip.Height() * weight);
|
|
|
|
for (uint x=0; x <= TSL_PATCHES; ++x, ++v)
|
|
{
|
|
D3DXVECTOR2 vx;
|
|
|
|
weight = x / float(TSL_PATCHES);
|
|
::D3DXVec2Hermite( &vx, &p0, &t, &p1, &t, weight );
|
|
|
|
v->x = vx.x;
|
|
v->y = vy.y;
|
|
v->z = 0.f;
|
|
v->rhw = 1.f;
|
|
v->u0 = textures.GetScreenLeftU(clip.left + clip.Width() * weight);
|
|
v->u1 = textures.GetEffectLeftU(clip.left + clip.Width() * weight);
|
|
v->v0 = vy.x;
|
|
v->v1 = x1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
v[0].x = rect.left - 0.499f;
|
|
v[0].y = rect.top - 0.499f;
|
|
v[0].z = 0.f;
|
|
v[0].rhw = 1.f;
|
|
v[0].u0 = textures.GetScreenLeftU( clip.left );
|
|
v[0].u1 = textures.GetEffectLeftU( clip.left );
|
|
v[0].v0 = textures.GetScreenTopV( clip.top );
|
|
v[0].v1 = textures.GetEffectTopV( clip.top );
|
|
v[1].x = rect.left - 0.499f;
|
|
v[1].y = rect.bottom - 0.499f;
|
|
v[1].z = 0.f;
|
|
v[1].rhw = 1.f;
|
|
v[1].u0 = textures.GetScreenLeftU( clip.left );
|
|
v[1].u1 = textures.GetEffectLeftU( clip.left );
|
|
v[1].v0 = textures.GetScreenBottomV( clip.bottom );
|
|
v[1].v1 = textures.GetEffectBottomV( clip.bottom );
|
|
v[2].x = rect.right - 0.499f;
|
|
v[2].y = rect.top - 0.499f;
|
|
v[2].z = 0.f;
|
|
v[2].rhw = 1.f;
|
|
v[2].u0 = textures.GetScreenRightU( clip.right );
|
|
v[2].u1 = textures.GetEffectRightU( clip.right );
|
|
v[2].v0 = textures.GetScreenTopV( clip.top );
|
|
v[2].v1 = textures.GetEffectTopV( clip.top );
|
|
v[3].x = rect.right - 0.499f;
|
|
v[3].y = rect.bottom - 0.499f;
|
|
v[3].z = 0.f;
|
|
v[3].rhw = 1.f;
|
|
v[3].u0 = textures.GetScreenRightU( clip.right );
|
|
v[3].u1 = textures.GetEffectRightU( clip.right );
|
|
v[3].v0 = textures.GetScreenBottomV( clip.bottom );
|
|
v[3].v1 = textures.GetEffectBottomV( clip.bottom );
|
|
}
|
|
|
|
com->Unlock();
|
|
}
|
|
else if (hResult == D3DERR_DEVICELOST)
|
|
{
|
|
return D3DERR_DEVICELOST;
|
|
}
|
|
else
|
|
{
|
|
throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DVertexBuffer9::Lock()" );
|
|
}
|
|
|
|
com->PreLoad();
|
|
}
|
|
|
|
if (FAILED(device.SetFVF( FVF )))
|
|
throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::SetFVF()" );
|
|
|
|
if (FAILED(device.SetStreamSource( 0, *com, 0, sizeof(Vertex) )))
|
|
throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::SetStreamSource()" );
|
|
|
|
return D3D_OK;
|
|
}
|
|
|
|
Direct2D::IndexBuffer::IndexBuffer()
|
|
: numStrips(0) {}
|
|
|
|
Direct2D::IndexBuffer::~IndexBuffer()
|
|
{
|
|
Invalidate();
|
|
}
|
|
|
|
void Direct2D::IndexBuffer::Update(bool s)
|
|
{
|
|
if (bool(numStrips) != s)
|
|
{
|
|
numStrips = (s ? (TSL_PATCHES * 2 + 1) * TSL_PATCHES - 1 : 0);
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void Direct2D::IndexBuffer::Invalidate()
|
|
{
|
|
if (com)
|
|
{
|
|
IDirect3DDevice9* device;
|
|
|
|
if (SUCCEEDED(com->GetDevice( &device )))
|
|
{
|
|
device->SetIndices( NULL );
|
|
device->Release();
|
|
}
|
|
|
|
com.Release();
|
|
}
|
|
}
|
|
|
|
HRESULT Direct2D::IndexBuffer::Validate(IDirect3DDevice9& device)
|
|
{
|
|
NST_ASSERT( !com || numStrips );
|
|
|
|
if (numStrips)
|
|
{
|
|
if (!com)
|
|
{
|
|
const HRESULT hResult = device.CreateIndexBuffer
|
|
(
|
|
(((TSL_PATCHES * 2 + 1) * TSL_PATCHES) + 1) * sizeof(WORD),
|
|
D3DUSAGE_WRITEONLY,
|
|
D3DFMT_INDEX16,
|
|
D3DPOOL_MANAGED,
|
|
&com,
|
|
NULL
|
|
);
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
void* ptr;
|
|
const HRESULT hResult = com->Lock( 0, 0, &ptr, D3DLOCK_NOSYSLOCK );
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
WORD* NST_RESTRICT data = static_cast<WORD*>(ptr);
|
|
|
|
for (uint p=0, i=0, n=TSL_PATCHES+1;;)
|
|
{
|
|
uint j = i + n;
|
|
|
|
do
|
|
{
|
|
*data++ = i;
|
|
*data++ = i++ + n;
|
|
}
|
|
while (i < j);
|
|
|
|
i += n-1;
|
|
|
|
do
|
|
{
|
|
*data++ = i-- + n;
|
|
*data++ = i;
|
|
}
|
|
while (i > j);
|
|
|
|
i += n;
|
|
p += 2;
|
|
|
|
if (p == n-1)
|
|
{
|
|
*data++ = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
com->Unlock();
|
|
}
|
|
else if (hResult == D3DERR_DEVICELOST)
|
|
{
|
|
return D3DERR_DEVICELOST;
|
|
}
|
|
else
|
|
{
|
|
throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DIndexBuffer9::Lock()" );
|
|
}
|
|
}
|
|
else if (hResult == D3DERR_DEVICELOST)
|
|
{
|
|
return D3DERR_DEVICELOST;
|
|
}
|
|
else
|
|
{
|
|
throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::CreateIndexBuffer()" );
|
|
}
|
|
}
|
|
|
|
com->PreLoad();
|
|
|
|
if (FAILED(device.SetIndices( *com )))
|
|
throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::SetIndices()" );
|
|
}
|
|
|
|
return D3D_OK;
|
|
}
|
|
|
|
Direct2D::Textures::Textures(D3DFORMAT backBufferFormat)
|
|
:
|
|
screenTexture (backBufferFormat),
|
|
effectTexture (backBufferFormat),
|
|
filter (Adapter::FILTER_NONE)
|
|
{}
|
|
|
|
void Direct2D::Textures::Update(const Adapter& adapter,const Point& screen,const uint scanlines,const Adapter::Filter f,const bool useVidMem)
|
|
{
|
|
NST_ASSERT( screen.x > 0 && screen.y > 0 );
|
|
|
|
filter = f;
|
|
|
|
effectTexture.Update( adapter, screen, scanlines );
|
|
screenTexture.Update( adapter, screen, useVidMem );
|
|
}
|
|
|
|
void Direct2D::Textures::Flush()
|
|
{
|
|
screenTexture.Flush();
|
|
}
|
|
|
|
void Direct2D::Textures::Invalidate()
|
|
{
|
|
effectTexture.Invalidate();
|
|
screenTexture.Invalidate();
|
|
}
|
|
|
|
HRESULT Direct2D::Textures::Validate(IDirect3DDevice9& device,const Adapter& adapter,const D3DFORMAT backBufferFormat)
|
|
{
|
|
if (screenTexture.Validate( device, backBufferFormat, adapter ) && effectTexture.Validate( device, backBufferFormat ))
|
|
{
|
|
const D3DTEXTUREFILTERTYPE type = (filter == Adapter::FILTER_NONE ? D3DTEXF_POINT : D3DTEXF_LINEAR);
|
|
|
|
for (uint i=0, n = (effectTexture ? 2 : 1); i < n; ++i)
|
|
{
|
|
device.SetSamplerState( i, D3DSAMP_MINFILTER, type );
|
|
device.SetSamplerState( i, D3DSAMP_MAGFILTER, type );
|
|
}
|
|
|
|
return D3D_OK;
|
|
}
|
|
|
|
return D3DERR_DEVICELOST;
|
|
}
|
|
|
|
bool Direct2D::Textures::SaveToFile(wcstring const file,const D3DXIMAGE_FILEFORMAT type) const
|
|
{
|
|
return screenTexture.SaveToFile( file, type );
|
|
}
|
|
|
|
Direct2D::Textures::Texture::Texture(uint s,D3DFORMAT f)
|
|
: size(256,256), stage(s)
|
|
{
|
|
desc.Width = size.x;
|
|
desc.Height = size.y;
|
|
desc.Format = f;
|
|
}
|
|
|
|
Direct2D::Textures::Texture::~Texture()
|
|
{
|
|
Invalidate();
|
|
}
|
|
|
|
uint Direct2D::Textures::Texture::GetSquared(const Point& p)
|
|
{
|
|
uint squared = NST_MAX(p.x,p.y);
|
|
|
|
squared--;
|
|
squared |= squared >> 1;
|
|
squared |= squared >> 2;
|
|
squared |= squared >> 4;
|
|
squared |= squared >> 8;
|
|
squared |= squared >> 16;
|
|
squared++;
|
|
|
|
return squared;
|
|
}
|
|
|
|
void Direct2D::Textures::Texture::Invalidate()
|
|
{
|
|
if (com)
|
|
{
|
|
IDirect3DDevice9* device;
|
|
|
|
if (SUCCEEDED(com->GetDevice( &device )))
|
|
{
|
|
device->SetTextureStageState( stage, D3DTSS_COLOROP, D3DTOP_DISABLE );
|
|
device->SetTexture( stage, NULL );
|
|
device->Release();
|
|
}
|
|
|
|
com.Release();
|
|
}
|
|
}
|
|
|
|
bool Direct2D::Textures::Texture::Validate(IDirect3DDevice9& device,const D3DFORMAT desiredFormat,const bool dynamicUsage)
|
|
{
|
|
if (!com)
|
|
{
|
|
const HRESULT hResult = ::D3DXCreateTexture
|
|
(
|
|
&device,
|
|
desc.Width,
|
|
desc.Height,
|
|
1,
|
|
dynamicUsage ? D3DUSAGE_DYNAMIC : 0,
|
|
desiredFormat,
|
|
dynamicUsage ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED,
|
|
&com
|
|
);
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
if (FAILED(com->GetLevelDesc( 0, &desc )))
|
|
throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::GetLevelDesc()" );
|
|
|
|
if (desc.Width >= size.x && desc.Height >= size.y)
|
|
{
|
|
if (!GetBitsPerPixel())
|
|
throw Application::Exception( L"Unsupported bits-per-pixel format!" );
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
throw Application::Exception( L"Maximum texture dimension too small!" );
|
|
}
|
|
}
|
|
else if (hResult == D3DERR_DEVICELOST)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::CreateTexture()" );
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Direct2D::Textures::ScreenTexture::ScreenTexture(D3DFORMAT f)
|
|
: Texture(0,f), useVidMem(false) {}
|
|
|
|
void Direct2D::Textures::ScreenTexture::Update(const Adapter& adapter,Point newSize,const bool wantVidMem)
|
|
{
|
|
size = newSize;
|
|
|
|
if (!adapter.anyTextureSize)
|
|
newSize = GetSquared(newSize);
|
|
|
|
if (desc.Width != newSize.x || desc.Height != newSize.y)
|
|
{
|
|
desc.Width = newSize.x;
|
|
desc.Height = newSize.y;
|
|
Invalidate();
|
|
}
|
|
|
|
if (useVidMem != wantVidMem)
|
|
{
|
|
useVidMem = wantVidMem;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void Direct2D::Textures::ScreenTexture::Flush()
|
|
{
|
|
if (desc.Pool == D3DPOOL_DEFAULT)
|
|
Invalidate();
|
|
}
|
|
|
|
bool Direct2D::Textures::ScreenTexture::Validate(IDirect3DDevice9& device,const D3DFORMAT desiredFormat,const Adapter& adapter)
|
|
{
|
|
if (!Texture::Validate( device, desiredFormat, useVidMem && adapter.videoMemScreen ) && !com)
|
|
return false;
|
|
|
|
device.SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
|
|
device.SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
|
|
device.SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
|
|
|
|
device.SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
|
|
device.SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );
|
|
|
|
if (FAILED(device.SetTexture( 0, *com )))
|
|
throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::SetTexture()" );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Direct2D::Textures::ScreenTexture::SaveToFile(wcstring const file,const D3DXIMAGE_FILEFORMAT type) const
|
|
{
|
|
NST_ASSERT( file && *file && size.x && size.y );
|
|
|
|
if (!com)
|
|
return false;
|
|
|
|
ComInterface<IDirect3DSurface9> surface;
|
|
|
|
{
|
|
ComInterface<IDirect3DDevice9> device;
|
|
|
|
if (FAILED(com->GetDevice( &device )))
|
|
return false;
|
|
|
|
if (FAILED(device->CreateOffscreenPlainSurface( size.x, size.y, D3DFMT_R8G8B8, D3DPOOL_SCRATCH, &surface, NULL )))
|
|
return false;
|
|
}
|
|
|
|
ulong masks[3];
|
|
GetBitMask(masks[0],masks[1],masks[2]);
|
|
|
|
uint shifts[3];
|
|
|
|
for (uint i=0; i < 3; ++i)
|
|
{
|
|
NST_ASSERT( masks[i] );
|
|
|
|
for (shifts[i]=0; !(masks[i] & 0x1); ++shifts[i])
|
|
masks[i] >>= 1;
|
|
}
|
|
|
|
D3DLOCKED_RECT dstLock;
|
|
|
|
if (FAILED(surface->LockRect( &dstLock, NULL, D3DLOCK_NOSYSLOCK )))
|
|
return false;
|
|
|
|
D3DLOCKED_RECT srcLock;
|
|
const RECT rect = {0,0,size.x,size.y};
|
|
|
|
if (FAILED(com->LockRect( 0, &srcLock, &rect, D3DLOCK_READONLY|D3DLOCK_NOSYSLOCK )))
|
|
{
|
|
surface->UnlockRect();
|
|
return false;
|
|
}
|
|
|
|
BYTE* NST_RESTRICT dst = static_cast<BYTE*>(dstLock.pBits);
|
|
const uint dstPitch = dstLock.Pitch - size.x * 3;
|
|
|
|
if (GetBitsPerPixel() == 16)
|
|
{
|
|
const WORD* NST_RESTRICT src = static_cast<WORD*>(srcLock.pBits);
|
|
const uint srcPitch = srcLock.Pitch - size.x * sizeof(WORD);
|
|
|
|
for (uint y=size.y; y; --y)
|
|
{
|
|
for (const BYTE* const end=dst+size.x*3; dst != end; dst += 3)
|
|
{
|
|
const uint pixel = *src++;
|
|
|
|
dst[2] = ((pixel >> shifts[0] & masks[0]) * 0xFF + masks[0] / 2) / masks[0];
|
|
dst[1] = ((pixel >> shifts[1] & masks[1]) * 0xFF + masks[1] / 2) / masks[1];
|
|
dst[0] = ((pixel >> shifts[2] & masks[2]) * 0xFF + masks[2] / 2) / masks[2];
|
|
}
|
|
|
|
src = reinterpret_cast<const WORD*>(reinterpret_cast<const BYTE*>(src) + srcPitch);
|
|
dst += dstPitch;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const DWORD* NST_RESTRICT src = static_cast<DWORD*>(srcLock.pBits);
|
|
const uint srcPitch = srcLock.Pitch - size.x * sizeof(DWORD);
|
|
|
|
for (uint y=size.y; y; --y)
|
|
{
|
|
for (const BYTE* const end=dst+size.x*3; dst != end; dst += 3)
|
|
{
|
|
const uint pixel = *src++;
|
|
|
|
dst[2] = ((pixel >> shifts[0] & masks[0]) * 0xFF + masks[0] / 2) / masks[0];
|
|
dst[1] = ((pixel >> shifts[1] & masks[1]) * 0xFF + masks[1] / 2) / masks[1];
|
|
dst[0] = ((pixel >> shifts[2] & masks[2]) * 0xFF + masks[2] / 2) / masks[2];
|
|
}
|
|
|
|
src = reinterpret_cast<const DWORD*>(reinterpret_cast<const BYTE*>(src) + srcPitch);
|
|
dst += dstPitch;
|
|
}
|
|
}
|
|
|
|
com->UnlockRect( 0 );
|
|
surface->UnlockRect();
|
|
|
|
return SUCCEEDED(::D3DXSaveSurfaceToFile( file, type, *surface, NULL, &rect ));
|
|
}
|
|
|
|
Direct2D::Textures::EffectTexture::EffectTexture(D3DFORMAT f)
|
|
: Texture(1,f), scanlines(0), dirty(false) {}
|
|
|
|
void Direct2D::Textures::EffectTexture::Update(const Adapter& adapter,Point newSize,const uint newScanlines)
|
|
{
|
|
NST_ASSERT( newScanlines <= MAX_SCANLINES );
|
|
|
|
newSize.x = 1;
|
|
size = newSize;
|
|
|
|
if (!adapter.anyTextureSize)
|
|
newSize = GetSquared(newSize);
|
|
|
|
if (desc.Width != newSize.x || desc.Height != newSize.y)
|
|
{
|
|
desc.Width = newSize.x;
|
|
desc.Height = newSize.y;
|
|
Invalidate();
|
|
}
|
|
|
|
if (scanlines != newScanlines)
|
|
{
|
|
dirty = true;
|
|
|
|
if ((scanlines > 0) != (newScanlines > 0))
|
|
Invalidate();
|
|
|
|
scanlines = newScanlines;
|
|
}
|
|
}
|
|
|
|
bool Direct2D::Textures::EffectTexture::Validate(IDirect3DDevice9& device,const D3DFORMAT desiredFormat)
|
|
{
|
|
NST_ASSERT( !com || scanlines );
|
|
|
|
if (scanlines)
|
|
{
|
|
if (Texture::Validate( device, desiredFormat, false ))
|
|
{
|
|
dirty = true;
|
|
}
|
|
else if (!com)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (dirty)
|
|
{
|
|
dirty = false;
|
|
|
|
ulong mask[3];
|
|
GetBitMask(mask[0],mask[1],mask[2]);
|
|
|
|
const uint intensity[2] =
|
|
{
|
|
mask[0] | mask[1] | mask[2],
|
|
((mask[0] * (100 - scanlines) / 100) & mask[0]) |
|
|
((mask[1] * (100 - scanlines) / 100) & mask[1]) |
|
|
((mask[2] * (100 - scanlines) / 100) & mask[2])
|
|
};
|
|
|
|
D3DLOCKED_RECT lockedRect;
|
|
const RECT rect = {0,0,desc.Width,desc.Height};
|
|
const HRESULT hResult = com->LockRect( 0, &lockedRect, &rect, D3DLOCK_NOSYSLOCK );
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
if (GetBitsPerPixel() == 32)
|
|
{
|
|
DWORD* NST_RESTRICT dst = static_cast<DWORD*>(lockedRect.pBits);
|
|
const uint pitch = lockedRect.Pitch - desc.Width * sizeof(DWORD);
|
|
|
|
for (uint y=desc.Height/2; y; --y)
|
|
{
|
|
for (uint i=0; i < 2; ++i)
|
|
{
|
|
for (uint x=desc.Width; x; --x)
|
|
*dst++ = intensity[i];
|
|
|
|
dst = reinterpret_cast<DWORD*>(reinterpret_cast<BYTE*>(dst) + pitch);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WORD* NST_RESTRICT dst = static_cast<WORD*>(lockedRect.pBits);
|
|
const uint pitch = lockedRect.Pitch - desc.Width * sizeof(WORD);
|
|
|
|
for (uint y=desc.Height/2; y; --y)
|
|
{
|
|
for (uint i=0; i < 2; ++i)
|
|
{
|
|
for (uint x=desc.Width; x; --x)
|
|
*dst++ = intensity[i];
|
|
|
|
dst = reinterpret_cast<WORD*>(reinterpret_cast<BYTE*>(dst) + pitch);
|
|
}
|
|
}
|
|
}
|
|
|
|
com->UnlockRect(0);
|
|
}
|
|
else if (hResult == D3DERR_DEVICELOST)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DTexture9::LockRect()" );
|
|
}
|
|
|
|
com->PreLoad();
|
|
}
|
|
|
|
device.SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_MODULATE );
|
|
device.SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
|
|
device.SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
|
|
|
|
device.SetSamplerState( 1, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP );
|
|
device.SetSamplerState( 1, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP );
|
|
|
|
if (FAILED(device.SetTexture( 1, *com )))
|
|
throw Application::Exception( IDS_ERR_FAILED, L"IDirect3DDevice9::SetTexture()" );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("", on)
|
|
#endif
|
|
}
|
|
}
|