|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
This program 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 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
|
|
|
|
//#define DYNAHEADER_CREATE_STORAGE
|
|
|
|
|
|
|
|
#include "HeadSpin.h"
|
|
|
|
#include "hsWindows.h"
|
|
|
|
|
|
|
|
#include <ctime>
|
|
|
|
|
|
|
|
#include "hsG3DDeviceSelector.h"
|
|
|
|
#include "hsStream.h"
|
|
|
|
|
|
|
|
#include "plPipeline.h"
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////
|
|
|
|
hsG3DDeviceMode::hsG3DDeviceMode()
|
|
|
|
: fWidth(0), fHeight(0),
|
|
|
|
fDepth(0),
|
|
|
|
fFlags(kNone)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
hsG3DDeviceMode::~hsG3DDeviceMode()
|
|
|
|
{
|
|
|
|
Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hsG3DDeviceMode::operator< (const hsG3DDeviceMode &mode) const
|
|
|
|
{
|
|
|
|
// Color depth overrides everything else
|
|
|
|
if (fDepth < mode.GetColorDepth())
|
|
|
|
return true;
|
|
|
|
// Only compare width and height if the color depth is the same
|
|
|
|
else if (fDepth == mode.GetColorDepth() )
|
|
|
|
{
|
|
|
|
if( fWidth < mode.GetWidth() )
|
|
|
|
return true;
|
|
|
|
else if( fWidth == mode.GetWidth() && fHeight < mode.GetHeight() )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void hsG3DDeviceMode::Clear()
|
|
|
|
{
|
|
|
|
fFlags = kNone;
|
|
|
|
fWidth = 0;
|
|
|
|
fHeight = 0;
|
|
|
|
fDepth = 0;
|
|
|
|
fZStencilDepths.Reset();
|
|
|
|
fFSAATypes.Reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
hsG3DDeviceRecord::hsG3DDeviceRecord()
|
|
|
|
: fFlags(kNone),
|
|
|
|
fG3DDeviceType(hsG3DDeviceSelector::kDevTypeUnknown),
|
|
|
|
fLayersAtOnce(0), fMemoryBytes(0),
|
|
|
|
fG3DHALorHEL(hsG3DDeviceSelector::kHHTypeUnknown),
|
|
|
|
fZBiasRating( 0 ), fLODBiasRating( 0 ),
|
|
|
|
fFogExpApproxStart( 0.0 ), fFogExp2ApproxStart( 0.0 ), fFogEndBias( 0.0 ), fMaxAnisotropicSamples( 1 )
|
|
|
|
{
|
|
|
|
SetFogKneeParams( kFogExp, 0, 0 );
|
|
|
|
SetFogKneeParams( kFogExp2, 0, 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
hsG3DDeviceRecord::~hsG3DDeviceRecord()
|
|
|
|
{
|
|
|
|
Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
hsG3DDeviceRecord::hsG3DDeviceRecord(const hsG3DDeviceRecord& src)
|
|
|
|
: fFlags(kNone),
|
|
|
|
fG3DDeviceType(hsG3DDeviceSelector::kDevTypeUnknown),
|
|
|
|
fG3DHALorHEL(hsG3DDeviceSelector::kHHTypeUnknown),
|
|
|
|
fZBiasRating( src.fZBiasRating ), fLODBiasRating( 0 ),
|
|
|
|
fFogExpApproxStart( src.fFogExpApproxStart ), fFogExp2ApproxStart( src.fFogExp2ApproxStart ),
|
|
|
|
fFogEndBias( src.fFogEndBias ), fMaxAnisotropicSamples( src.fMaxAnisotropicSamples )
|
|
|
|
{
|
|
|
|
*this = src;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsG3DDeviceRecord& hsG3DDeviceRecord::operator=(const hsG3DDeviceRecord& src)
|
|
|
|
{
|
|
|
|
fFlags = src.fFlags;
|
|
|
|
|
|
|
|
SetG3DDeviceType(src.GetG3DDeviceType());
|
|
|
|
SetG3DHALorHEL(src.GetG3DHALorHEL());
|
|
|
|
|
|
|
|
SetDriverDesc(src.GetDriverDesc());
|
|
|
|
SetDriverName(src.GetDriverName());
|
|
|
|
SetDriverVersion(src.GetDriverVersion());
|
|
|
|
SetDeviceDesc(src.GetDeviceDesc());
|
|
|
|
|
|
|
|
fCaps = src.fCaps;
|
|
|
|
fLayersAtOnce = src.fLayersAtOnce;
|
|
|
|
fMemoryBytes = src.fMemoryBytes;
|
|
|
|
fZBiasRating = src.fZBiasRating;
|
|
|
|
fLODBiasRating = src.fLODBiasRating;
|
|
|
|
fFogExpApproxStart = src.fFogExpApproxStart;
|
|
|
|
fFogExp2ApproxStart = src.fFogExp2ApproxStart;
|
|
|
|
fFogEndBias = src.fFogEndBias;
|
|
|
|
|
|
|
|
fModes.SetCount(src.fModes.GetCount());
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fModes.GetCount(); i++ )
|
|
|
|
fModes[i] = src.fModes[i];
|
|
|
|
|
|
|
|
fFogKnees[ 0 ] = src.fFogKnees[ 0 ];
|
|
|
|
fFogKnees[ 1 ] = src.fFogKnees[ 1 ];
|
|
|
|
fFogKneeVals[ 0 ] = src.fFogKneeVals[ 0 ];
|
|
|
|
fFogKneeVals[ 1 ] = src.fFogKneeVals[ 1 ];
|
|
|
|
|
|
|
|
fAASetting = src.fAASetting;
|
|
|
|
|
|
|
|
fMaxAnisotropicSamples = src.fMaxAnisotropicSamples;
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* hsG3DDeviceRecord::GetG3DDeviceTypeName() const
|
|
|
|
{
|
|
|
|
static const char* deviceNames[hsG3DDeviceSelector::kNumDevTypes] = {
|
|
|
|
"Unknown",
|
|
|
|
"Direct3D",
|
|
|
|
"OpenGL"
|
|
|
|
};
|
|
|
|
|
|
|
|
uint32_t devType = GetG3DDeviceType();
|
|
|
|
if( devType > hsG3DDeviceSelector::kNumDevTypes )
|
|
|
|
devType = hsG3DDeviceSelector::kDevTypeUnknown;
|
|
|
|
|
|
|
|
return deviceNames[devType];
|
|
|
|
}
|
|
|
|
|
|
|
|
void hsG3DDeviceRecord::RemoveDiscarded()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fModes.GetCount(); )
|
|
|
|
{
|
|
|
|
if( fModes[i].GetDiscarded() )
|
|
|
|
{
|
|
|
|
fModes[i].Clear();
|
|
|
|
fModes.Remove(i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if( !fModes.GetCount() )
|
|
|
|
SetDiscarded(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void hsG3DDeviceRecord::ClearModes()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fModes.GetCount(); i++ )
|
|
|
|
fModes[i].Clear();
|
|
|
|
fModes.Reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void hsG3DDeviceRecord::Clear()
|
|
|
|
{
|
|
|
|
fFlags = kNone;
|
|
|
|
|
|
|
|
fG3DDriverDesc = plString::Null;
|
|
|
|
fG3DDriverName = plString::Null;
|
|
|
|
fG3DDriverVersion = plString::Null;
|
|
|
|
fG3DDeviceDesc = plString::Null;
|
|
|
|
|
|
|
|
fCaps.Clear();
|
|
|
|
fLayersAtOnce = 0;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fModes.GetCount(); i++ )
|
|
|
|
fModes[i].Clear();
|
|
|
|
fModes.Reset();
|
|
|
|
|
|
|
|
fZBiasRating = 0;
|
|
|
|
fLODBiasRating = 0;
|
|
|
|
fFogExpApproxStart = 0;
|
|
|
|
fFogExp2ApproxStart = 0;
|
|
|
|
fFogEndBias = 0;
|
|
|
|
|
|
|
|
SetFogKneeParams( kFogExp, 0, 0 );
|
|
|
|
SetFogKneeParams( kFogExp2, 0, 0 );
|
|
|
|
|
|
|
|
fAASetting = 0;
|
|
|
|
fMaxAnisotropicSamples = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsG3DDeviceModeRecord::hsG3DDeviceModeRecord(const hsG3DDeviceRecord& devRec, const hsG3DDeviceMode& devMode)
|
|
|
|
: fDevice(devRec), fMode(devMode)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
hsG3DDeviceModeRecord::hsG3DDeviceModeRecord(const hsG3DDeviceModeRecord& src)
|
|
|
|
{
|
|
|
|
*this = src;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsG3DDeviceModeRecord& hsG3DDeviceModeRecord::operator=(const hsG3DDeviceModeRecord& src)
|
|
|
|
{
|
|
|
|
fDevice = src.fDevice;
|
|
|
|
fMode = src.fMode;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////
|
|
|
|
|
|
|
|
hsG3DDeviceSelector::~hsG3DDeviceSelector()
|
|
|
|
{
|
|
|
|
IClear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void hsG3DDeviceSelector::IRemoveDiscarded()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fRecords.GetCount(); )
|
|
|
|
{
|
|
|
|
fRecords[i].RemoveDiscarded();
|
|
|
|
|
|
|
|
if( fRecords[i].GetDiscarded() )
|
|
|
|
{
|
|
|
|
fRecords[i].Clear();
|
|
|
|
fRecords.Remove(i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void hsG3DDeviceSelector::IClear()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fRecords.GetCount(); i++ )
|
|
|
|
fRecords[i].Clear();
|
|
|
|
fRecords.Reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void hsG3DDeviceSelector::RemoveUnusableDevModes(bool bTough)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < fRecords.GetCount(); i++)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Remove modes
|
|
|
|
//
|
|
|
|
hsTArray<hsG3DDeviceMode>& modes = fRecords[i].GetModes();
|
|
|
|
for (int j = 0; j < modes.GetCount(); j++)
|
|
|
|
{
|
|
|
|
// Remove windowed modes
|
|
|
|
if ((modes[j].GetWidth() == 0) &&
|
|
|
|
(modes[j].GetHeight() == 0) &&
|
|
|
|
(modes[j].GetColorDepth() == 0))
|
|
|
|
{
|
|
|
|
modes[j].SetDiscarded(true);
|
|
|
|
}
|
|
|
|
// If tough, remove modes less than 640x480
|
|
|
|
else if (bTough && ((modes[j].GetWidth() < 640) || (modes[j].GetHeight() < 480)))
|
|
|
|
{
|
|
|
|
modes[j].SetDiscarded(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Remove devices
|
|
|
|
//
|
|
|
|
if (fRecords[i].GetG3DDeviceType() == hsG3DDeviceSelector::kDevTypeUnknown)
|
|
|
|
{
|
|
|
|
fRecords[i].SetDiscarded(true);
|
|
|
|
}
|
|
|
|
else if (fRecords[i].GetG3DDeviceType() == hsG3DDeviceSelector::kDevTypeDirect3D)
|
|
|
|
{
|
|
|
|
// Remove software Direct3D devices
|
|
|
|
if ((fRecords[i].GetG3DHALorHEL() != hsG3DDeviceSelector::kHHD3DHALDev) &&
|
|
|
|
(fRecords[i].GetG3DHALorHEL() != hsG3DDeviceSelector::kHHD3DTnLHalDev))
|
|
|
|
{
|
|
|
|
fRecords[i].SetDiscarded(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
IRemoveDiscarded();
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IAdjustDirectXMemory /////////////////////////////////////////////////////
|
|
|
|
// Adjusts the number coming off of the DirectX caps for "total video memory"
|
|
|
|
// to be more reflective of what is really on the board. According to
|
|
|
|
// Microsoft, the best way to do this is to add in the memory necessary for
|
|
|
|
// the entire desktop. Okay, whatever...
|
|
|
|
|
|
|
|
uint32_t hsG3DDeviceSelector::IAdjustDirectXMemory( uint32_t cardMem )
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
HDC deskDC;
|
|
|
|
int width, height, bpp, total;
|
|
|
|
|
|
|
|
deskDC = GetDC( nil );
|
|
|
|
width = GetDeviceCaps( deskDC, HORZRES );
|
|
|
|
height = GetDeviceCaps( deskDC, VERTRES );
|
|
|
|
bpp = GetDeviceCaps( deskDC, BITSPIXEL );
|
|
|
|
|
|
|
|
total = width * height;
|
|
|
|
if( bpp > 8 )
|
|
|
|
total *= ( bpp >> 3 );
|
|
|
|
|
|
|
|
return cardMem + total;
|
|
|
|
#else
|
|
|
|
return cardMem;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void hsG3DDeviceSelector::Enumerate(hsWinRef winRef)
|
|
|
|
{
|
|
|
|
IClear();
|
|
|
|
|
|
|
|
#ifdef HS_BUILD_FOR_WIN32
|
|
|
|
/// 9.6.2000 - Create the class to use as our temporary window class
|
|
|
|
WNDCLASS tempClass;
|
|
|
|
|
|
|
|
strcpy( fTempWinClass, "DSTestClass" );
|
|
|
|
memset( &tempClass, 0, sizeof( tempClass ) );
|
|
|
|
tempClass.lpfnWndProc = DefWindowProc;
|
|
|
|
tempClass.hInstance = GetModuleHandle( nil );
|
|
|
|
tempClass.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
|
|
|
|
tempClass.lpszClassName = fTempWinClass;
|
|
|
|
uint16_t ret = RegisterClass(&tempClass);
|
|
|
|
hsAssert(ret, "Cannot create temporary window class to test for device modes" );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ITryDirect3DTnL(winRef);
|
|
|
|
|
|
|
|
#ifdef HS_BUILD_FOR_WIN32
|
|
|
|
/// Get rid of the class
|
|
|
|
UnregisterClass( fTempWinClass, GetModuleHandle( nil ) );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hsG3DDeviceSelector::GetDefault (hsG3DDeviceModeRecord *dmr)
|
|
|
|
{
|
|
|
|
int32_t iTnL, iD3D, iOpenGL, device, mode, i;
|
|
|
|
device = iTnL = iD3D = iOpenGL = mode = -1;
|
|
|
|
|
|
|
|
if (device == -1)
|
|
|
|
{
|
|
|
|
// Get an index for any 3D devices
|
|
|
|
for (i = 0; i < fRecords.GetCount(); i++)
|
|
|
|
{
|
|
|
|
switch (fRecords[i].GetG3DDeviceType())
|
|
|
|
{
|
|
|
|
case kDevTypeDirect3D:
|
|
|
|
if (fRecords[i].GetG3DHALorHEL() == kHHD3DTnLHalDev)
|
|
|
|
{
|
|
|
|
if (iTnL == -1
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
|| plPipeline::fInitialPipeParams.ForceSecondMonitor
|
|
|
|
#endif // PLASMA_EXTERNAL_RELEASE
|
|
|
|
)
|
|
|
|
{
|
|
|
|
iTnL = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (fRecords[i].GetG3DHALorHEL() == kHHD3DHALDev)
|
|
|
|
{
|
|
|
|
if (iD3D == -1
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
|| plPipeline::fInitialPipeParams.ForceSecondMonitor
|
|
|
|
#endif // PLASMA_EXTERNAL_RELEASE
|
|
|
|
)
|
|
|
|
{
|
|
|
|
iD3D = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kDevTypeOpenGL:
|
|
|
|
if (iOpenGL == -1
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
|| plPipeline::fInitialPipeParams.ForceSecondMonitor
|
|
|
|
#endif // PLASMA_EXTERNAL_RELEASE
|
|
|
|
)
|
|
|
|
{
|
|
|
|
iOpenGL = i;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pick a default device (Priority D3D T&L, D3D HAL, OpenGL)
|
|
|
|
if (iTnL != -1)
|
|
|
|
device = iTnL;
|
|
|
|
else if (iD3D != -1)
|
|
|
|
device = iD3D;
|
|
|
|
else if (iOpenGL != -1)
|
|
|
|
device = iOpenGL;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Try and find the default mode
|
|
|
|
//
|
|
|
|
hsTArray<hsG3DDeviceMode>& modes = fRecords[device].GetModes();
|
|
|
|
|
|
|
|
// If there are no modes (for some insane reason), fail
|
|
|
|
if (modes.GetCount() == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (i = 0; i < modes.GetCount(); i++)
|
|
|
|
{
|
|
|
|
if ((modes[i].GetWidth() == kDefaultWidth) &&
|
|
|
|
(modes[i].GetHeight() == kDefaultHeight) &&
|
|
|
|
(modes[i].GetNumZStencilDepths() > 0))
|
|
|
|
{
|
|
|
|
// Don't be too picky about the depth, use what's available if the
|
|
|
|
// default isn't found.
|
|
|
|
if (mode == -1 || modes[mode].GetColorDepth() != kDefaultDepth)
|
|
|
|
mode = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Default mode not found, what kind of card is this?!
|
|
|
|
// Regardless, just use the first mode since this isn't a fatal error.
|
|
|
|
if (mode == -1)
|
|
|
|
mode = 0;
|
|
|
|
|
|
|
|
*dmr = hsG3DDeviceModeRecord(fRecords[device], modes[mode]);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//// Fudging Routines /////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
/// Here's our CFT--Chipset Fudgefactor Table
|
|
|
|
/// The table consists of entries for each of our supported chipsets in the table,
|
|
|
|
/// plus, flags to be forced set and flags to be forced cleared. Also included
|
|
|
|
/// is a Z-buffer suckiness rating, which represents how badly we need to bias
|
|
|
|
/// the z and w values to avoid z-buffer artifacts, stored as an float (i.e
|
|
|
|
/// a float). A rating of 0 means very good/default (read: Nvidia), while, say,
|
|
|
|
/// a 9.0 (i.e. shift the scale 9 times above normal) means s****y, like, say,
|
|
|
|
/// a Savage4. Also also included is a forced value for max # of layers (0 means
|
|
|
|
/// to use default). Also also also included is an LOD rating indicating how much
|
|
|
|
/// (and in which direction) to alter the base LOD bias value for this device. General
|
|
|
|
/// interpretation of this value is to add (-lodRating) to the LOD bias value.
|
|
|
|
/// This is because the LOD bias starts out negative and typically goes in 0.25
|
|
|
|
/// increments.
|
|
|
|
/// Also also ALSO included are three new values for fog tweaking. The first two--
|
|
|
|
/// fFogExp/Exp2ApproxStart, are the start of the linear approximation of exponential
|
|
|
|
/// fog. Tweak these to adjust the linear approximation on any cards that don't support
|
|
|
|
/// exponential and exponential-squared fog natively. The third value is the fFogEndBias--
|
|
|
|
/// this is a value (stored as a percentage of the max possible fog value) to add on to
|
|
|
|
/// to the linear fog-end parameter AFTER ALL CALCULATIONS. This is so we can, for
|
|
|
|
/// example, tweak the end of the fog on the ATI Rage cards to not fog out as quickly.
|
|
|
|
/// 9.14.2000 - fog end bias now has a new meaning. What it *really* represents is the
|
|
|
|
/// quantization of fog on a particular card, where the end bias = ( 2^bitdepth - 2 ) / ( 2^bitdepth - 1 )
|
|
|
|
/// So, for 8 bit fog, we end up with 254 / 255, etc. So far, everything is set to 8
|
|
|
|
/// bit fog, but we have it here just in case we need to change it in the future.
|
|
|
|
|
|
|
|
enum {
|
|
|
|
kDefaultChipset = 0x00,
|
|
|
|
kIntelI810Chipset,
|
|
|
|
kS3GenericChipset,
|
|
|
|
kATIRadeonChipset,
|
|
|
|
kATIR8X00Chipset,
|
|
|
|
kMatroxParhelia,
|
|
|
|
kNVidiaGeForceFXChipset
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
float fFogExpApproxStart;
|
|
|
|
float fFogExp2ApproxStart;
|
|
|
|
float fFogEndBias;
|
|
|
|
float fFogExpKnee; // Fog knees
|
|
|
|
float fFogExpKneeVal;
|
|
|
|
float fFogExp2Knee;
|
|
|
|
float fFogExp2KneeVal;
|
|
|
|
} FogTweakTable;
|
|
|
|
|
|
|
|
FogTweakTable dsDefaultFogVals = { 0, 0, 254.0 / 255.0, 0.5f, 0.15f, 0.5f, 0.15f };
|
|
|
|
FogTweakTable dsi810FogVals = { 0, 0, 254.0 / 255.0, 0.6f, 0.15f, 0.4f, 0.15f };
|
|
|
|
FogTweakTable dsRadeonFogVals = { 0, 0, 254.0 / 255.0, 0.7f, 0.15f, 0.5f, 0.2f };
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint8_t fType; // Our chipset ID
|
|
|
|
uint32_t *fFlagsToSet;
|
|
|
|
uint32_t *fFlagsToClear;
|
|
|
|
float fZSuckiness; // See above
|
|
|
|
uint32_t fForceMaxLayers; // The max # of layers we REALLY want (0 to not force)
|
|
|
|
float fLODRating;
|
|
|
|
FogTweakTable *fFogTweaks;
|
|
|
|
} CFTable;
|
|
|
|
|
|
|
|
uint32_t dsGeForceFXCapsSet[] = {
|
|
|
|
1, // First integer is always the length
|
|
|
|
hsG3DDeviceSelector::kCapsNoAA };
|
|
|
|
|
|
|
|
uint32_t dsS3GenerCapsClr[] = {
|
|
|
|
4, // First integer is always the length
|
|
|
|
hsG3DDeviceSelector::kCapsCompressTextures,
|
|
|
|
hsG3DDeviceSelector::kCapsFogExp,
|
|
|
|
hsG3DDeviceSelector::kCapsFogExp2,
|
|
|
|
hsG3DDeviceSelector::kCapsDoesSmallTextures };
|
|
|
|
|
|
|
|
uint32_t dsATIR8X00CapsSet[] = {
|
|
|
|
2, // First integer is always the length
|
|
|
|
hsG3DDeviceSelector::kCapsBadManaged,
|
|
|
|
hsG3DDeviceSelector::kCapsShareDepth
|
|
|
|
};
|
|
|
|
|
|
|
|
uint32_t dsATIR8X00CapsClr[] = {
|
|
|
|
1, // First integer is always the length
|
|
|
|
hsG3DDeviceSelector::kCapsDoesSmallTextures };
|
|
|
|
|
|
|
|
uint32_t dsDefaultCapsClr[] = {
|
|
|
|
1, // First integer is always the length
|
|
|
|
hsG3DDeviceSelector::kCapsDoesSmallTextures };
|
|
|
|
|
|
|
|
CFTable dsCFTable[] =
|
|
|
|
{
|
|
|
|
// Chipset ID // F2Set // F2Clear // ZSuck // MaxLayers // LODBias // Fog Value Tables
|
|
|
|
{ kDefaultChipset, nullptr, dsDefaultCapsClr, 0, 0, 0, &dsDefaultFogVals },
|
|
|
|
{ kNVidiaGeForceFXChipset, dsGeForceFXCapsSet, nullptr, 0, 0, 0, &dsDefaultFogVals },
|
|
|
|
{ kIntelI810Chipset, nullptr, dsDefaultCapsClr, 4.5f, 1, -0.5f, &dsi810FogVals },
|
|
|
|
{ kATIR8X00Chipset, dsATIR8X00CapsSet, dsATIR8X00CapsClr, 0, 0, 0, &dsRadeonFogVals },
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
//// IFudgeDirectXDevice //////////////////////////////////////////////////////
|
|
|
|
// Checks this DirectX device against all our known types and fudges our caps
|
|
|
|
// flags and bias values, etc, accordingly
|
|
|
|
|
|
|
|
#ifdef HS_SELECT_DIRECT3D
|
|
|
|
void hsG3DDeviceSelector::IFudgeDirectXDevice( hsG3DDeviceRecord &record,
|
|
|
|
D3DEnum_DriverInfo *driverInfo,
|
|
|
|
D3DEnum_DeviceInfo *deviceInfo )
|
|
|
|
{
|
|
|
|
uint32_t vendorID, deviceID;
|
|
|
|
char *szDriver, *szDesc;
|
|
|
|
|
|
|
|
|
|
|
|
/// Send it off to each D3D device, respectively
|
|
|
|
if( record.GetG3DDeviceType() == kDevTypeDirect3D )
|
|
|
|
{
|
|
|
|
if( !IGetD3DCardInfo( record, driverInfo, deviceInfo, &vendorID, &deviceID, &szDriver, &szDesc ) )
|
|
|
|
{
|
|
|
|
// {} to make VC6 happy in release build
|
|
|
|
hsAssert( false, "Trying to fudge D3D device but D3D support isn't in this EXE!" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hsAssert( false, "IFudgeDirectXDevice got a device type that support wasn't compiled for!" );
|
|
|
|
}
|
|
|
|
|
|
|
|
/// So capitalization won't matter in our tests
|
|
|
|
plString desc = plString::FromIso8859_1(szDesc).ToLower();
|
|
|
|
|
|
|
|
/// Detect ATI Radeon chipset
|
|
|
|
// We will probably need to differentiate between different Radeons at some point in
|
|
|
|
// the future, but not now.
|
|
|
|
ssize_t radeon = desc.Find("radeon");
|
|
|
|
if (stricmp(szDriver, "ati2dvag.dll") == 0 || radeon >= 0)
|
|
|
|
{
|
|
|
|
int series = 0;
|
|
|
|
if (radeon >= 0)
|
|
|
|
{
|
|
|
|
const char* str = desc.c_str() + radeon + strlen("radeon");
|
|
|
|
if( 1 == sscanf(str, "%d", &series) )
|
|
|
|
{
|
|
|
|
if( (series >= 8000) && (series < 9000) )
|
|
|
|
{
|
|
|
|
hsStatusMessage( "== Using fudge factors for ATI Radeon 8X00 chipset ==\n" );
|
|
|
|
ISetFudgeFactors( kATIR8X00Chipset, record );
|
|
|
|
}
|
|
|
|
else if (series >= 9000)
|
|
|
|
{
|
|
|
|
hsStatusMessage("== Using fudge factors for ATI Radeon 9X00 chipset ==\n");
|
|
|
|
ISetFudgeFactors(kATIRadeonChipset, record);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
series = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (series == 0)
|
|
|
|
{
|
|
|
|
hsStatusMessage("== Using fudge factors for ATI/AMD Radeon X/HD/R chipset ==\n");
|
|
|
|
ISetFudgeFactors(kDefaultChipset, record);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//// Other Cards //////////////////////////////////////////////////////////
|
|
|
|
/// Detect Intel i810 chipset
|
|
|
|
else if( deviceID == 0x00007125 &&
|
|
|
|
( stricmp( szDriver, "i81xdd.dll" ) == 0
|
|
|
|
|| ( desc.Find("intel") >= 0 && desc.Find("810") >= 0 ) ) )
|
|
|
|
{
|
|
|
|
hsStatusMessage( "== Using fudge factors for an Intel i810 chipset ==\n" );
|
|
|
|
ISetFudgeFactors( kIntelI810Chipset, record );
|
|
|
|
}
|
|
|
|
/// Detect for a GeForc FX card. We only need to nerf the really low end one.
|
|
|
|
else if( desc.Find("nvidia") >= 0 && desc.Find("geforce fx 5200") >= 0 )
|
|
|
|
{
|
|
|
|
hsStatusMessage( "== Using fudge factors for an NVidia GeForceFX-based chipset ==\n" );
|
|
|
|
ISetFudgeFactors( kNVidiaGeForceFXChipset, record );
|
|
|
|
}
|
|
|
|
/// Default fudge values
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hsStatusMessage( "== Using default fudge factors ==\n" );
|
|
|
|
ISetFudgeFactors( kDefaultChipset, record );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//// ISetFudgeFactors /////////////////////////////////////////////////////////
|
|
|
|
// Given a chipset ID, looks the values up in the CFT and sets the appropriate
|
|
|
|
// values.
|
|
|
|
|
|
|
|
void hsG3DDeviceSelector::ISetFudgeFactors( uint8_t chipsetID, hsG3DDeviceRecord &record )
|
|
|
|
{
|
|
|
|
int i, maxIDs, j;
|
|
|
|
|
|
|
|
|
|
|
|
maxIDs = sizeof( dsCFTable ) / sizeof( dsCFTable[ 0 ] );
|
|
|
|
|
|
|
|
/// Search for our chipset
|
|
|
|
for( i = 0; i < maxIDs; i++ )
|
|
|
|
{
|
|
|
|
if( dsCFTable[ i ].fType == chipsetID )
|
|
|
|
{
|
|
|
|
/// Found it!
|
|
|
|
|
|
|
|
// Flags to force set
|
|
|
|
if( dsCFTable[ i ].fFlagsToSet != nil )
|
|
|
|
{
|
|
|
|
for( j = 0; j < dsCFTable[ i ].fFlagsToSet[ 0 ]; j++ )
|
|
|
|
record.SetCap( dsCFTable[ i ].fFlagsToSet[ j + 1 ] );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Flags to force clear
|
|
|
|
if( dsCFTable[ i ].fFlagsToClear != nil )
|
|
|
|
{
|
|
|
|
for( j = 0; j < dsCFTable[ i ].fFlagsToClear[ 0 ]; j++ )
|
|
|
|
record.SetCap( dsCFTable[ i ].fFlagsToClear[ j + 1 ], false );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Suckiness
|
|
|
|
record.SetZBiasRating( dsCFTable[ i ].fZSuckiness );
|
|
|
|
|
|
|
|
// Max # of layers
|
|
|
|
if( dsCFTable[ i ].fForceMaxLayers > 0 )
|
|
|
|
record.SetLayersAtOnce( dsCFTable[ i ].fForceMaxLayers );
|
|
|
|
|
|
|
|
// LOD bias rating
|
|
|
|
record.SetLODBiasRating( dsCFTable[ i ].fLODRating );
|
|
|
|
|
|
|
|
// Fog tweaks
|
|
|
|
FogTweakTable *fogTweaks = dsCFTable[ i ].fFogTweaks;
|
|
|
|
|
|
|
|
record.SetFogApproxStarts( fogTweaks->fFogExpApproxStart, fogTweaks->fFogExp2ApproxStart );
|
|
|
|
record.SetFogEndBias( fogTweaks->fFogEndBias );
|
|
|
|
record.SetFogKneeParams( hsG3DDeviceRecord::kFogExp, fogTweaks->fFogExpKnee, fogTweaks->fFogExpKneeVal );
|
|
|
|
record.SetFogKneeParams( hsG3DDeviceRecord::kFogExp2, fogTweaks->fFogExp2Knee, fogTweaks->fFogExp2KneeVal );
|
|
|
|
|
|
|
|
if( record.GetCap(kCapsNoAA) )
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
for( j = 0; j < record.GetModes().GetCount(); j++ )
|
|
|
|
record.GetModes()[j].ClearFSAATypes();
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|