diff options
Diffstat (limited to 'materialsystem/shaderapidx9/shaderdevicedx8.cpp')
| -rw-r--r-- | materialsystem/shaderapidx9/shaderdevicedx8.cpp | 3707 |
1 files changed, 3707 insertions, 0 deletions
diff --git a/materialsystem/shaderapidx9/shaderdevicedx8.cpp b/materialsystem/shaderapidx9/shaderdevicedx8.cpp new file mode 100644 index 0000000..5b094e1 --- /dev/null +++ b/materialsystem/shaderapidx9/shaderdevicedx8.cpp @@ -0,0 +1,3707 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#define DISABLE_PROTECTED_THINGS +#include "locald3dtypes.h" + +#include "shaderdevicedx8.h" +#include "shaderapi/ishaderutil.h" +#include "shaderapidx8_global.h" +#include "filesystem.h" +#include "tier0/icommandline.h" +#include "tier2/tier2.h" +#include "shadershadowdx8.h" +#include "colorformatdx8.h" +#include "materialsystem/IShader.h" +#include "shaderapidx8.h" +#include "shaderapidx8_global.h" +#include "imeshdx8.h" +#include "materialsystem/materialsystem_config.h" +#include "vertexshaderdx8.h" +#include "recording.h" +#include "winutils.h" +#include "tier0/vprof_telemetry.h" + +#if defined ( DX_TO_GL_ABSTRACTION ) +// Placed here so inlines placed in dxabstract.h can access gGL +COpenGLEntryPoints *gGL = NULL; +#endif + +#define D3D_BATCH_PERF_ANALYSIS 0 + +#if D3D_BATCH_PERF_ANALYSIS +#if defined( DX_TO_GL_ABSTRACTION ) +#error Cannot enable D3D_BATCH_PERF_ANALYSIS when using DX_TO_GL_ABSTRACTION, use GL_BATCH_PERF_ANALYSIS instead. +#endif +// Define this if you want all d3d9 interfaces hooked and run through the dx9hook.h shim interfaces. For profiling, etc. +#define DO_DX9_HOOK +#endif + +#ifdef DO_DX9_HOOK + +#if D3D_BATCH_PERF_ANALYSIS +ConVar d3d_batch_vis( "d3d_batch_vis", "0" ); +ConVar d3d_batch_vis_abs_scale( "d3d_batch_vis_abs_scale", ".050" ); +ConVar d3d_present_vis_abs_scale( "d3d_batch_vis_abs_scale", ".050" ); +ConVar d3d_batch_vis_y_scale( "d3d_batch_vis_y_scale", "0.0" ); +uint64 g_nTotalD3DCalls, g_nTotalD3DCycles; +static double s_rdtsc_to_ms; +#endif + +#include "dx9hook.h" +#endif + +#ifndef _X360 +#include "wmi.h" +#endif + +#if defined( _X360 ) +#include "xbox/xbox_console.h" +#include "xbox/xbox_win32stubs.h" +#endif + + +//#define DX8_COMPATABILITY_MODE + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +static CShaderDeviceMgrDx8 g_ShaderDeviceMgrDx8; +CShaderDeviceMgrDx8* g_pShaderDeviceMgrDx8 = &g_ShaderDeviceMgrDx8; + +#ifndef SHADERAPIDX10 + +// In the shaderapidx10.dll, we use its version of IShaderDeviceMgr. +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderDeviceMgrDx8, IShaderDeviceMgr, + SHADER_DEVICE_MGR_INTERFACE_VERSION, g_ShaderDeviceMgrDx8 ) + +#endif + +#if defined( _X360 ) +IDirect3D9 *m_pD3D; +#endif + +IDirect3DDevice *g_pD3DDevice = NULL; + +#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) +// HACK: need to pass knowledge of D3D9Ex usage into callers of D3D Create* methods +// so they do not try to specify D3DPOOL_MANAGED, which is unsupported in D3D9Ex +bool g_ShaderDeviceUsingD3D9Ex = false; +static ConVar mat_supports_d3d9ex( "mat_supports_d3d9ex", "0", FCVAR_HIDDEN ); +#endif + +// hook into mat_forcedynamic from the engine. +static ConVar mat_forcedynamic( "mat_forcedynamic", "0", FCVAR_CHEAT ); + +// this is hooked into the engines convar +ConVar mat_debugalttab( "mat_debugalttab", "0", FCVAR_CHEAT ); + + +//----------------------------------------------------------------------------- +// +// Device manager +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CShaderDeviceMgrDx8::CShaderDeviceMgrDx8() +{ + m_pD3D = NULL; + m_bObeyDxCommandlineOverride = true; + m_bAdapterInfoIntialized = false; + +#if defined( PIX_INSTRUMENTATION ) && defined ( DX_TO_GL_ABSTRACTION ) && defined( _WIN32 ) + m_hD3D9 = NULL; + m_pBeginEvent = NULL; + m_pEndEvent = NULL; + m_pSetMarker = NULL; + m_pSetOptions = NULL; +#endif +} + +CShaderDeviceMgrDx8::~CShaderDeviceMgrDx8() +{ +} + +#ifdef OSX +#include <Carbon/Carbon.h> +#endif +//----------------------------------------------------------------------------- +// Connect, disconnect +//----------------------------------------------------------------------------- +bool CShaderDeviceMgrDx8::Connect( CreateInterfaceFn factory ) +{ + LOCK_SHADERAPI(); + + if ( !BaseClass::Connect( factory ) ) + return false; + +#if defined ( DX_TO_GL_ABSTRACTION ) + gGL = ToGLConnectLibraries( factory ); +#endif + +#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) && !defined(RECORDING) && !defined( DX_TO_GL_ABSTRACTION ) + m_pD3D = NULL; + + // Attempt to create a D3D9Ex device (Windows Vista and later) if possible + bool bD3D9ExForceDisable = ( CommandLine()->FindParm( "-nod3d9ex" ) != 0 ) || + ( CommandLine()->ParmValue( "-dxlevel", 95 ) < 90 ); + + bool bD3D9ExAvailable = false; + if ( HMODULE hMod = ::LoadLibraryA( "d3d9.dll" ) ) + { + typedef HRESULT ( WINAPI *CreateD3D9ExFunc_t )( UINT, IUnknown** ); + if ( CreateD3D9ExFunc_t pfnCreateD3D9Ex = (CreateD3D9ExFunc_t) ::GetProcAddress( hMod, "Direct3DCreate9Ex" ) ) + { + IUnknown *pD3D9Ex = NULL; + if ( (*pfnCreateD3D9Ex)( D3D_SDK_VERSION, &pD3D9Ex ) == S_OK && pD3D9Ex ) + { + bD3D9ExAvailable = true; + if ( bD3D9ExForceDisable ) + { + pD3D9Ex->Release(); + } + else + { + g_ShaderDeviceUsingD3D9Ex = true; + // The following is more "correct" but incompatible with the Steam overlay: + //pD3D9Ex->QueryInterface( IID_IDirect3D9, (void**) &m_pD3D ); + //pD3D9Ex->Release(); + m_pD3D = static_cast< IDirect3D9* >( pD3D9Ex ); + } + } + } + ::FreeLibrary( hMod ); + } + + if ( !m_pD3D ) + { + g_ShaderDeviceUsingD3D9Ex = false; + m_pD3D = Direct3DCreate9(D3D_SDK_VERSION); + } + + mat_supports_d3d9ex.SetValue( bD3D9ExAvailable ? 1 : 0 ); +#else + #if defined( DO_DX9_HOOK ) + m_pD3D = Direct3DCreate9Hook(D3D_SDK_VERSION); + #else + m_pD3D = Direct3DCreate9(D3D_SDK_VERSION); + #endif +#endif + + if ( !m_pD3D ) + { + Warning( "Failed to create D3D9!\n" ); + return false; + } + +#if defined( PIX_INSTRUMENTATION ) && defined ( DX_TO_GL_ABSTRACTION ) && defined( _WIN32 ) + // This is a little odd, but AMD PerfStudio hooks D3D9.DLL and intercepts all of the D3DPERF API's (even for OpenGL apps). + // So dynamically load d3d9.dll and get the address of these exported functions. + if ( !m_hD3D9 ) + { + m_hD3D9 = LoadLibraryA("d3d9.dll"); + } + if ( m_hD3D9 ) + { + Plat_DebugString( "PIX_INSTRUMENTATION: Loaded d3d9.dll\n" ); + printf( "PIX_INSTRUMENTATION: Loaded d3d9.dll\n" ); + + m_pBeginEvent = (D3DPERF_BeginEvent_FuncPtr)GetProcAddress( m_hD3D9, "D3DPERF_BeginEvent" ); + m_pEndEvent = (D3DPERF_EndEvent_FuncPtr)GetProcAddress( m_hD3D9, "D3DPERF_EndEvent" ); + m_pSetMarker = (D3DPERF_SetMarker_FuncPtr)GetProcAddress( m_hD3D9, "D3DPERF_SetOptions" ); + m_pSetOptions = (D3DPERF_SetOptions_FuncPtr)GetProcAddress( m_hD3D9, "D3DPERF_SetMarker" ); + } +#endif + + // FIXME: Want this to be here, but we can't because Steam + // hasn't had it's application ID set up yet. + +// InitAdapterInfo(); + return true; +} + +void CShaderDeviceMgrDx8::Disconnect() +{ + LOCK_SHADERAPI(); + +#if defined( PIX_INSTRUMENTATION ) && defined ( DX_TO_GL_ABSTRACTION ) && defined( _WIN32 ) + if ( m_hD3D9 ) + { + m_pBeginEvent = NULL; + m_pEndEvent = NULL; + m_pSetMarker = NULL; + m_pSetOptions = NULL; + + FreeLibrary( m_hD3D9 ); + m_hD3D9 = NULL; + } +#endif + + if ( m_pD3D ) + { + m_pD3D->Release(); + m_pD3D = 0; + } + +#if defined ( DX_TO_GL_ABSTRACTION ) + ToGLDisconnectLibraries(); +#endif + + BaseClass::Disconnect(); +} + + + +//----------------------------------------------------------------------------- +// Initialization +//----------------------------------------------------------------------------- +InitReturnVal_t CShaderDeviceMgrDx8::Init( ) +{ + // FIXME: Remove call to InitAdapterInfo once Steam startup issues are resolved. + // Do it in Connect instead. + InitAdapterInfo(); + + return INIT_OK; +} + + +//----------------------------------------------------------------------------- +// Shutdown +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::Shutdown( ) +{ + LOCK_SHADERAPI(); + +// FIXME: Make PIX work + +// BeginPIXEvent( PIX_VALVE_ORANGE, "Shutdown" ); + + if ( g_pShaderAPI ) + { + g_pShaderAPI->OnDeviceShutdown(); + } + + if ( g_pShaderDevice ) + { + g_pShaderDevice->ShutdownDevice(); + g_pMaterialSystemHardwareConfig = NULL; + } + +// EndPIXEvent(); + + +} + + + +//----------------------------------------------------------------------------- +// Inline methods +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Initialize adapter information +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::InitAdapterInfo() +{ + if ( m_bAdapterInfoIntialized ) + return; + + m_bAdapterInfoIntialized = true; + m_Adapters.RemoveAll(); + + Assert(m_pD3D); + int nCount = m_pD3D->GetAdapterCount( ); + for( int i = 0; i < nCount; ++i ) + { + int j = m_Adapters.AddToTail(); + AdapterInfo_t &info = m_Adapters[j]; + +#ifdef _DEBUG + memset( &info.m_ActualCaps, 0xDD, sizeof(info.m_ActualCaps) ); +#endif + + info.m_ActualCaps.m_bDeviceOk = ComputeCapsFromD3D( &info.m_ActualCaps, i ); + if ( !info.m_ActualCaps.m_bDeviceOk ) + continue; + + ReadDXSupportLevels( info.m_ActualCaps ); + + // Read dxsupport.cfg which has config overrides for particular cards. + ReadHardwareCaps( info.m_ActualCaps, info.m_ActualCaps.m_nMaxDXSupportLevel ); + + // What's in "-shader" overrides dxsupport.cfg + const char *pShaderParam = CommandLine()->ParmValue( "-shader" ); + if ( pShaderParam ) + { + Q_strncpy( info.m_ActualCaps.m_pShaderDLL, pShaderParam, sizeof( info.m_ActualCaps.m_pShaderDLL ) ); + } + } +} + +//-------------------------------------------------------------------------------- +// Code to detect support for texture border color (widely supported but the caps +// bit is messed up in drivers due to a stupid WHQL test that requires this to work +// with float textures which we don't generally care about wrt this address mode) +//-------------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::CheckBorderColorSupport( HardwareCaps_t *pCaps, int nAdapter ) +{ +#ifdef DX_TO_GL_ABSTRACTION + if( true ) +#else + if( IsX360() ) +#endif + { + pCaps->m_bSupportsBorderColor = true; + } + else // Most PC parts do this, but let's not deal with that yet (JasonM) + { + pCaps->m_bSupportsBorderColor = false; + } +} + +//-------------------------------------------------------------------------------- +// Vendor-dependent code to detect support for various flavors of shadow mapping +//-------------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::CheckVendorDependentShadowMappingSupport( HardwareCaps_t *pCaps, int nAdapter ) +{ + // Set a default null texture format...may be overridden below by IHV-specific surface type + pCaps->m_NullTextureFormat = IMAGE_FORMAT_ARGB8888; + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, D3DFMT_R5G6B5 ) == S_OK ) + { + pCaps->m_NullTextureFormat = IMAGE_FORMAT_RGB565; + } + +#if defined( _X360 ) + pCaps->m_ShadowDepthTextureFormat = ReverseDepthOnX360() ? IMAGE_FORMAT_X360_DST24F : IMAGE_FORMAT_X360_DST24; + pCaps->m_bSupportsShadowDepthTextures = true; + pCaps->m_bSupportsFetch4 = false; + return; +#elif defined ( DX_TO_GL_ABSTRACTION ) + // We may want to only do this on the higher-end Mac SKUs, since it's not free... + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_NV_DST16; // This format shunts us down the right shader combo path + + pCaps->m_bSupportsShadowDepthTextures = true; + + pCaps->m_bSupportsFetch4 = false; + return; +#endif + + if ( IsPC() || !IsX360() ) + { + bool bToolsMode = IsWindows() && ( CommandLine()->CheckParm( "-tools" ) != NULL ); + bool bFound16Bit = false; + + if ( ( pCaps->m_VendorID == VENDORID_NVIDIA ) && ( pCaps->m_SupportsShaderModel_3_0 ) ) // ps_3_0 parts from nVidia + { + // First, test for null texture support + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, NVFMT_NULL ) == S_OK ) + { + pCaps->m_NullTextureFormat = IMAGE_FORMAT_NV_NULL; + } + + // + // NVIDIA has two no-PCF formats (these are not filtering modes, but surface formats + // NVFMT_RAWZ is supported by NV4x (not supported here yet...requires a dp3 to reconstruct in shader code, which doesn't seem to work) + // NVFMT_INTZ is supported on newer chips as of G8x (just read like ATI non-fetch4 mode) + // +/* + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, NVFMT_INTZ ) == S_OK ) + { + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_NV_INTZ; + pCaps->m_bSupportsFetch4 = false; + pCaps->m_bSupportsShadowDepthTextures = true; + return; + } +*/ + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, D3DFMT_D16 ) == S_OK ) + { + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_NV_DST16; + pCaps->m_bSupportsFetch4 = false; + pCaps->m_bSupportsShadowDepthTextures = true; + bFound16Bit = true; + + if ( !bToolsMode ) // Tools will continue on and try for 24 bit... + return; + } + + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, D3DFMT_D24S8 ) == S_OK ) + { + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_NV_DST24; + pCaps->m_bSupportsFetch4 = false; + pCaps->m_bSupportsShadowDepthTextures = true; + return; + } + + if ( bFound16Bit ) // Found 16 bit but not 24 + return; + } + else if ( ( pCaps->m_VendorID == VENDORID_ATI ) && pCaps->m_SupportsPixelShaders_2_b ) // ps_2_b parts from ATI + { + // Initially, check for Fetch4 (tied to ATIFMT_D24S8 support) + pCaps->m_bSupportsFetch4 = false; + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, ATIFMT_D24S8 ) == S_OK ) + { + pCaps->m_bSupportsFetch4 = true; + } + + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, ATIFMT_D16 ) == S_OK ) // Prefer 16-bit + { + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_ATI_DST16; + pCaps->m_bSupportsShadowDepthTextures = true; + bFound16Bit = true; + + if ( !bToolsMode ) // Tools will continue on and try for 24 bit... + return; + } + + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, ATIFMT_D24S8 ) == S_OK ) + { + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_ATI_DST24; + pCaps->m_bSupportsShadowDepthTextures = true; + return; + } + + if ( bFound16Bit ) // Found 16 bit but not 24 + return; + } + } + + // Other vendor or old hardware + pCaps->m_ShadowDepthTextureFormat = IMAGE_FORMAT_UNKNOWN; + pCaps->m_bSupportsShadowDepthTextures = false; + pCaps->m_bSupportsFetch4 = false; +} + + +//----------------------------------------------------------------------------- +// Vendor-dependent code to detect Alpha To Coverage Backdoors +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::CheckVendorDependentAlphaToCoverage( HardwareCaps_t *pCaps, int nAdapter ) +{ + pCaps->m_bSupportsAlphaToCoverage = false; + + // Bail out on OpenGL +#ifdef DX_TO_GL_ABSTRACTION + pCaps->m_bSupportsAlphaToCoverage = true; + pCaps->m_AlphaToCoverageEnableValue = TRUE; + pCaps->m_AlphaToCoverageDisableValue = FALSE; + pCaps->m_AlphaToCoverageState = D3DRS_ADAPTIVETESS_Y; // Just match the NVIDIA state hackery + return; +#endif + + if ( pCaps->m_nDXSupportLevel < 90 ) + return; + +#ifdef _X360 + { + pCaps->m_bSupportsAlphaToCoverage = true; + pCaps->m_AlphaToCoverageEnableValue = TRUE; + pCaps->m_AlphaToCoverageDisableValue = FALSE; + pCaps->m_AlphaToCoverageState = D3DRS_ALPHATOMASKENABLE; + return; + } +#endif // _X360 + + if ( pCaps->m_VendorID == VENDORID_NVIDIA ) + { + // nVidia has two modes...assume SSAA is superior to MSAA and hence more desirable (though it's probably not) + // + // Currently, they only seem to expose any of this on 7800 and up though older parts certainly + // support at least the MSAA mode since they support it on OpenGL via the arb_multisample extension + bool bNVIDIA_MSAA = false; + bool bNVIDIA_SSAA = false; + + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, // Check MSAA version + D3DFMT_X8R8G8B8, 0, D3DRTYPE_SURFACE, + (D3DFORMAT)MAKEFOURCC('A', 'T', 'O', 'C')) == S_OK ) + { + bNVIDIA_MSAA = true; + } + + if ( m_pD3D->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, // Check SSAA version + D3DFMT_X8R8G8B8, 0, D3DRTYPE_SURFACE, + (D3DFORMAT)MAKEFOURCC('S', 'S', 'A', 'A')) == S_OK ) + { + bNVIDIA_SSAA = true; + } + + // nVidia pitches SSAA but we prefer ATOC + if ( bNVIDIA_MSAA )// || bNVIDIA_SSAA ) + { + // if ( bNVIDIA_SSAA ) + // m_AlphaToCoverageEnableValue = MAKEFOURCC('S', 'S', 'A', 'A'); + // else + pCaps->m_AlphaToCoverageEnableValue = MAKEFOURCC('A', 'T', 'O', 'C'); + + pCaps->m_AlphaToCoverageState = D3DRS_ADAPTIVETESS_Y; + pCaps->m_AlphaToCoverageDisableValue = (DWORD)D3DFMT_UNKNOWN; + pCaps->m_bSupportsAlphaToCoverage = true; + return; + } + } + else if ( pCaps->m_VendorID == VENDORID_ATI ) + { + // Supported on all ATI parts...just go ahead and set the state when appropriate + pCaps->m_AlphaToCoverageState = D3DRS_POINTSIZE; + pCaps->m_AlphaToCoverageEnableValue = MAKEFOURCC('A','2','M','1'); + pCaps->m_AlphaToCoverageDisableValue = MAKEFOURCC('A','2','M','0'); + pCaps->m_bSupportsAlphaToCoverage = true; + return; + } +} + +ConVar mat_hdr_level( "mat_hdr_level", "2", FCVAR_ARCHIVE ); +ConVar mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap", "16", FCVAR_CHEAT ); +#ifdef DX_TO_GL_ABSTRACTION +ConVar mat_depthbias_shadowmap( "mat_depthbias_shadowmap", "20", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY ); +#else +ConVar mat_depthbias_shadowmap( "mat_depthbias_shadowmap", "0.0005", FCVAR_CHEAT ); +#endif + +// For testing Fast Clip +ConVar mat_fastclip( "mat_fastclip", "0", FCVAR_CHEAT ); + +//----------------------------------------------------------------------------- +// Determine capabilities +//----------------------------------------------------------------------------- +bool CShaderDeviceMgrDx8::ComputeCapsFromD3D( HardwareCaps_t *pCaps, int nAdapter ) +{ + D3DCAPS caps; + D3DADAPTER_IDENTIFIER9 ident; + HRESULT hr; + + // NOTE: When getting the caps, we want to be limited by the hardware + // even if we're running with software T&L... + hr = m_pD3D->GetDeviceCaps( nAdapter, DX8_DEVTYPE, &caps ); + if ( FAILED( hr ) ) + return false; + + hr = m_pD3D->GetAdapterIdentifier( nAdapter, D3DENUM_WHQL_LEVEL, &ident ); + if ( FAILED( hr ) ) + return false; + + if ( IsOpenGL() ) + { + if ( !ident.DeviceId && !ident.VendorId ) + { + ident.DeviceId = 1; // fake default device/vendor ID for OpenGL + ident.VendorId = 1; + } + } + + // Intended for debugging only + if ( CommandLine()->CheckParm( "-force_device_id" ) ) + { + const char *pDevID = CommandLine()->ParmValue( "-force_device_id", "" ); + if ( pDevID ) + { + int nDevID = V_atoi( pDevID ); // use V_atoi for hex support + if ( nDevID > 0 ) + { + ident.DeviceId = nDevID; + } + } + } + + // Intended for debugging only + if ( CommandLine()->CheckParm( "-force_vendor_id" ) ) + { + const char *pVendorID = CommandLine()->ParmValue( "-force_vendor_id", "" ); + if ( pVendorID ) + { + int nVendorID = V_atoi( pVendorID ); // use V_atoi for hex support + if ( pVendorID > 0 ) + { + ident.VendorId = nVendorID; + } + } + } + + Q_strncpy( pCaps->m_pDriverName, ident.Description, MATERIAL_ADAPTER_NAME_LENGTH ); + pCaps->m_VendorID = ident.VendorId; + pCaps->m_DeviceID = ident.DeviceId; + pCaps->m_SubSysID = ident.SubSysId; + pCaps->m_Revision = ident.Revision; + + pCaps->m_nDriverVersionHigh = ident.DriverVersion.HighPart; + pCaps->m_nDriverVersionLow = ident.DriverVersion.LowPart; + + pCaps->m_pShaderDLL[0] = 0; + pCaps->m_nMaxViewports = 1; + + pCaps->m_PreferDynamicTextures = ( caps.Caps2 & D3DCAPS2_DYNAMICTEXTURES ) ? 1 : 0; + + pCaps->m_HasProjectedBumpEnv = ( caps.TextureCaps & D3DPTEXTURECAPS_NOPROJECTEDBUMPENV ) == 0; + + pCaps->m_HasSetDeviceGammaRamp = (caps.Caps2 & D3DCAPS2_CANCALIBRATEGAMMA) != 0; + pCaps->m_SupportsVertexShaders = ((caps.VertexShaderVersion >> 8) & 0xFF) >= 1; + pCaps->m_SupportsPixelShaders = ((caps.PixelShaderVersion >> 8) & 0xFF) >= 1; + + pCaps->m_bScissorSupported = ( caps.RasterCaps & D3DPRASTERCAPS_SCISSORTEST ) != 0; + +#if defined( DX8_COMPATABILITY_MODE ) + pCaps->m_SupportsPixelShaders_1_4 = false; + pCaps->m_SupportsPixelShaders_2_0 = false; + pCaps->m_SupportsPixelShaders_2_b = false; + pCaps->m_SupportsVertexShaders_2_0 = false; + pCaps->m_SupportsShaderModel_3_0 = false; + pCaps->m_SupportsMipmappedCubemaps = false; +#else + pCaps->m_SupportsPixelShaders_1_4 = ( caps.PixelShaderVersion & 0xffff ) >= 0x0104; + pCaps->m_SupportsPixelShaders_2_0 = ( caps.PixelShaderVersion & 0xffff ) >= 0x0200; + pCaps->m_SupportsPixelShaders_2_b = ( ( caps.PixelShaderVersion & 0xffff ) >= 0x0200) && (caps.PS20Caps.NumInstructionSlots >= 512); // More caps to this, but this will do + pCaps->m_SupportsVertexShaders_2_0 = ( caps.VertexShaderVersion & 0xffff ) >= 0x0200; + pCaps->m_SupportsShaderModel_3_0 = ( caps.PixelShaderVersion & 0xffff ) >= 0x0300; + pCaps->m_SupportsMipmappedCubemaps = ( caps.TextureCaps & D3DPTEXTURECAPS_MIPCUBEMAP ) ? true : false; +#endif + + // Slam this off for OpenGL + if ( IsOpenGL() ) + { + pCaps->m_SupportsShaderModel_3_0 = false; + } + + // Slam 3.0 shaders off for Intel + if ( pCaps->m_VendorID == VENDORID_INTEL ) + { + pCaps->m_SupportsShaderModel_3_0 = false; + } + + pCaps->m_MaxVertexShader30InstructionSlots = 0; + pCaps->m_MaxPixelShader30InstructionSlots = 0; + + if ( pCaps->m_SupportsShaderModel_3_0 ) + { + pCaps->m_MaxVertexShader30InstructionSlots = caps.MaxVertexShader30InstructionSlots; + pCaps->m_MaxPixelShader30InstructionSlots = caps.MaxPixelShader30InstructionSlots; + } + + if( CommandLine()->CheckParm( "-nops2b" ) ) + { + pCaps->m_SupportsPixelShaders_2_b = false; + } + + pCaps->m_bSoftwareVertexProcessing = false; + if ( IsWindows() && CommandLine()->CheckParm( "-mat_softwaretl" ) ) + { + pCaps->m_bSoftwareVertexProcessing = true; + } + + if ( IsWindows() && !( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) ) + { + // no hardware t&l. . use software + pCaps->m_bSoftwareVertexProcessing = true; + } + + // Set mat_forcedynamic if software vertex processing since the software vp pipe has + // problems with sparse vertex buffers (it transforms the whole thing.) + if ( pCaps->m_bSoftwareVertexProcessing ) + { + mat_forcedynamic.SetValue( 1 ); + } + + if ( pCaps->m_bSoftwareVertexProcessing ) + { + pCaps->m_SupportsVertexShaders = true; + pCaps->m_SupportsVertexShaders_2_0 = true; + } + +#ifdef OSX + // Static control flow is disabled by default on OSX (the Mac version of togl has known bugs preventing this path from working properly that we've fixed in togl linux/win) + pCaps->m_bSupportsStaticControlFlow = CommandLine()->CheckParm( "-glslcontrolflow" ) != NULL; +#else + pCaps->m_bSupportsStaticControlFlow = !CommandLine()->CheckParm( "-noglslcontrolflow" ); +#endif + + // NOTE: Texture stages is a fixed-function concept + // NOTE: Normally, the number of texture units == the number of texture + // stages except for NVidia hardware, which reports more stages than units. + // The reason for this is because they expose the inner hardware pixel + // pipeline through the extra stages. The only thing we use stages for + // in the hardware is for configuring the color + alpha args + ops. + pCaps->m_NumSamplers = caps.MaxSimultaneousTextures; + pCaps->m_NumTextureStages = caps.MaxTextureBlendStages; + if ( pCaps->m_SupportsPixelShaders_2_0 ) + { + pCaps->m_NumSamplers = 16; + } + else + { + Assert( pCaps->m_NumSamplers <= pCaps->m_NumTextureStages ); + } + + // Clamp + pCaps->m_NumSamplers = min( pCaps->m_NumSamplers, (int)MAX_SAMPLERS ); + pCaps->m_NumTextureStages = min( pCaps->m_NumTextureStages, (int)MAX_TEXTURE_STAGES ); + + if ( D3DSupportsCompressedTextures() ) + { + pCaps->m_SupportsCompressedTextures = COMPRESSED_TEXTURES_ON; + } + else + { + pCaps->m_SupportsCompressedTextures = COMPRESSED_TEXTURES_OFF; + } + + pCaps->m_bSupportsAnisotropicFiltering = (caps.TextureFilterCaps & D3DPTFILTERCAPS_MINFANISOTROPIC) != 0; + pCaps->m_bSupportsMagAnisotropicFiltering = (caps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFANISOTROPIC) != 0; + + // OpenGL does not support this--at least not on OSX which is the primary GL target, so just don't use that path on GL at all. +#if !defined( DX_TO_GL_ABSTRACTION ) + pCaps->m_bCanStretchRectFromTextures = ( ( caps.DevCaps2 & D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES ) != 0 ) && ( pCaps->m_VendorID != VENDORID_INTEL ); +#else + pCaps->m_bCanStretchRectFromTextures = false; +#endif + + pCaps->m_nMaxAnisotropy = pCaps->m_bSupportsAnisotropicFiltering ? caps.MaxAnisotropy : 1; + + pCaps->m_SupportsCubeMaps = ( caps.TextureCaps & D3DPTEXTURECAPS_CUBEMAP ) ? true : false; + pCaps->m_SupportsNonPow2Textures = + ( !( caps.TextureCaps & D3DPTEXTURECAPS_POW2 ) || + ( caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL ) ); + + Assert( caps.TextureCaps & D3DPTEXTURECAPS_PROJECTED ); + + if ( pCaps->m_bSoftwareVertexProcessing ) + { + // This should be pushed down based on pixel shaders. + pCaps->m_NumVertexShaderConstants = 256; + pCaps->m_NumBooleanVertexShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool vs registers + pCaps->m_NumBooleanPixelShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool ps registers + pCaps->m_NumIntegerVertexShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool vs registers + pCaps->m_NumIntegerPixelShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool ps registers + } + else + { + pCaps->m_NumVertexShaderConstants = caps.MaxVertexShaderConst; + if ( CommandLine()->FindParm( "-limitvsconst" ) ) + { + pCaps->m_NumVertexShaderConstants = min( 256, pCaps->m_NumVertexShaderConstants ); + } + pCaps->m_NumBooleanVertexShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool vs registers + pCaps->m_NumBooleanPixelShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool ps registers + + // This is a little misleading...this is really 16 int4 registers + pCaps->m_NumIntegerVertexShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool vs registers + pCaps->m_NumIntegerPixelShaderConstants = pCaps->m_SupportsPixelShaders_2_0 ? 16 : 0; // 2.0 parts have 16 bool ps registers + } + + if ( pCaps->m_SupportsPixelShaders ) + { + if ( pCaps->m_SupportsPixelShaders_2_0 ) + { + pCaps->m_NumPixelShaderConstants = 32; + } + else + { + pCaps->m_NumPixelShaderConstants = 8; + } + } + else + { + pCaps->m_NumPixelShaderConstants = 0; + } + + pCaps->m_SupportsHardwareLighting = (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0; + + pCaps->m_MaxNumLights = caps.MaxActiveLights; + if ( pCaps->m_MaxNumLights > MAX_NUM_LIGHTS ) + { + pCaps->m_MaxNumLights = MAX_NUM_LIGHTS; + } + + if ( IsOpenGL() ) + { + // Set according to control flow bit on OpenGL + pCaps->m_MaxNumLights = MIN( pCaps->m_MaxNumLights, ( pCaps->m_bSupportsStaticControlFlow && pCaps->m_SupportsPixelShaders_2_b ) ? MAX_NUM_LIGHTS : ( MAX_NUM_LIGHTS - 2 ) ); + } + + if ( pCaps->m_bSoftwareVertexProcessing ) + { + pCaps->m_SupportsHardwareLighting = true; + pCaps->m_MaxNumLights = 2; + } + pCaps->m_MaxTextureWidth = caps.MaxTextureWidth; + pCaps->m_MaxTextureHeight = caps.MaxTextureHeight; + pCaps->m_MaxTextureDepth = caps.MaxVolumeExtent ? caps.MaxVolumeExtent : 1; + pCaps->m_MaxTextureAspectRatio = caps.MaxTextureAspectRatio; + if ( pCaps->m_MaxTextureAspectRatio == 0 ) + { + pCaps->m_MaxTextureAspectRatio = max( pCaps->m_MaxTextureWidth, pCaps->m_MaxTextureHeight); + } + pCaps->m_MaxPrimitiveCount = caps.MaxPrimitiveCount; + pCaps->m_MaxBlendMatrices = caps.MaxVertexBlendMatrices; + pCaps->m_MaxBlendMatrixIndices = caps.MaxVertexBlendMatrixIndex; + + bool addSupported = (caps.TextureOpCaps & D3DTEXOPCAPS_ADD) != 0; + bool modSupported = (caps.TextureOpCaps & D3DTEXOPCAPS_MODULATE2X) != 0; + + pCaps->m_bNeedsATICentroidHack = false; + pCaps->m_bDisableShaderOptimizations = false; + + pCaps->m_SupportsMipmapping = true; + pCaps->m_SupportsOverbright = true; + + // Thank you to all you driver writers who actually correctly return caps + if ( !modSupported || !addSupported ) + { + Assert( 0 ); + pCaps->m_SupportsOverbright = false; + } + + // Check if ZBias and SlopeScaleDepthBias are supported. .if not, tweak the projection matrix instead + // for polyoffset. + pCaps->m_ZBiasAndSlopeScaledDepthBiasSupported = + ( ( caps.RasterCaps & D3DPRASTERCAPS_DEPTHBIAS) != 0 ) && + ( ( caps.RasterCaps & D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS ) != 0 ); + if ( IsX360() ) + { + // driver lies, force it + pCaps->m_ZBiasAndSlopeScaledDepthBiasSupported = true; + } + + // Spheremapping supported? + pCaps->m_bSupportsSpheremapping = (caps.VertexProcessingCaps & D3DVTXPCAPS_TEXGEN_SPHEREMAP) != 0; + + // How many user clip planes? + pCaps->m_MaxUserClipPlanes = caps.MaxUserClipPlanes; + if ( CommandLine()->CheckParm( "-nouserclip" ) /* || (IsOpenGL() && (!CommandLine()->FindParm("-glslmode"))) || r_emulategl.GetBool() */ ) + { + // rbarris 03Feb10: this now ignores POSIX / -glslmode / r_emulategl because we're defaulting GLSL mode "on". + // so this will mean that the engine will always ask for user clip planes. + // this will misbehave under ARB mode, since ARB shaders won't respect that state. + // it's difficult to make this fluid without teaching the engine about a cap that could change during run. + + pCaps->m_MaxUserClipPlanes = 0; + } + + if ( pCaps->m_MaxUserClipPlanes > MAXUSERCLIPPLANES ) + { + pCaps->m_MaxUserClipPlanes = MAXUSERCLIPPLANES; + } + + pCaps->m_FakeSRGBWrite = false; + pCaps->m_CanDoSRGBReadFromRTs = true; + pCaps->m_bSupportsGLMixedSizeTargets = false; +#ifdef DX_TO_GL_ABSTRACTION + // using #if because we're referencing fields in the RHS which don't exist in Windows headers for the caps9 struct + pCaps->m_FakeSRGBWrite = caps.FakeSRGBWrite != 0; + pCaps->m_CanDoSRGBReadFromRTs = caps.CanDoSRGBReadFromRTs != 0; + pCaps->m_bSupportsGLMixedSizeTargets = caps.MixedSizeTargets != 0; +#endif + + // Query for SRGB support as needed for our DX 9 stuff + if ( IsPC() || !IsX360() ) + { + pCaps->m_SupportsSRGB = ( D3D()->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_SRGBREAD, D3DRTYPE_TEXTURE, D3DFMT_DXT1 ) == S_OK); + + if ( pCaps->m_SupportsSRGB ) + { + pCaps->m_SupportsSRGB = ( D3D()->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE, D3DRTYPE_TEXTURE, D3DFMT_A8R8G8B8 ) == S_OK); + } + } + else + { + // 360 does support it, but is queried in the wrong manner, so force it + pCaps->m_SupportsSRGB = true; + } + + if ( CommandLine()->CheckParm( "-nosrgb" ) ) + { + pCaps->m_SupportsSRGB = false; + } + + pCaps->m_bSupportsVertexTextures = ( D3D()->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, D3DFMT_X8R8G8B8, + D3DUSAGE_QUERY_VERTEXTEXTURE, D3DRTYPE_TEXTURE, D3DFMT_R32F ) == S_OK ); + + if ( IsOpenGL() ) + { + pCaps->m_bSupportsVertexTextures = false; + } + + // FIXME: vs30 has a fixed setting here at 4. + // Future hardware will need some other way of computing this. + pCaps->m_nVertexTextureCount = pCaps->m_bSupportsVertexTextures ? 4 : 0; + + // FIXME: How do I actually compute this? + pCaps->m_nMaxVertexTextureDimension = pCaps->m_bSupportsVertexTextures ? 4096 : 0; + + // Does the device support filterable int16 textures? + bool bSupportsInteger16Textures = + ( D3D()->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, + D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_FILTER, + D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16 ) == S_OK ); + + // Does the device support filterable fp16 textures? + bool bSupportsFloat16Textures = + ( D3D()->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, + D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_FILTER, + D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16F ) == S_OK ); + + // Does the device support blendable fp16 render targets? + bool bSupportsFloat16RenderTargets = + ( D3D()->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, + D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING | D3DUSAGE_RENDERTARGET, + D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16F ) == S_OK ); + + // Essentially a proxy for a DX10 device running DX9 code path + pCaps->m_bSupportsFloat32RenderTargets = ( D3D()->CheckDeviceFormat( nAdapter, DX8_DEVTYPE, + D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING | D3DUSAGE_RENDERTARGET, + D3DRTYPE_TEXTURE, D3DFMT_A32B32G32R32F ) == S_OK ); + + pCaps->m_bFogColorSpecifiedInLinearSpace = false; + pCaps->m_bFogColorAlwaysLinearSpace = false; + + // Assume not DX10. Check below. + pCaps->m_bDX10Card = false; + pCaps->m_bDX10Blending = false; + + if ( IsOpenGL() && ( pCaps->m_VendorID == 1 ) ) + { + // Linux/Win OpenGL - always assume the device supports DX10 style blending + pCaps->m_bFogColorAlwaysLinearSpace = true; + pCaps->m_bDX10Card = true; + pCaps->m_bDX10Blending = true; + } + + // NVidia wants fog color to be specified in linear space + if ( IsPC() && pCaps->m_SupportsSRGB ) + { + if ( pCaps->m_VendorID == VENDORID_NVIDIA ) + { + pCaps->m_bFogColorSpecifiedInLinearSpace = true; + + if ( IsOpenGL() ) + { + // If we're not the Quadro 4500 or GeForce 7x000, we're an NVIDIA DX10 part on MacOS + if ( !( (pCaps->m_DeviceID == 0x009d) || ( (pCaps->m_DeviceID >= 0x0391) && (pCaps->m_DeviceID <= 0x0395) ) ) ) + { + pCaps->m_bFogColorAlwaysLinearSpace = true; + pCaps->m_bDX10Card = true; + pCaps->m_bDX10Blending = true; + } + } + else + { + // On G80 and later, always specify in linear space + if ( pCaps->m_bSupportsFloat32RenderTargets ) + { + pCaps->m_bFogColorAlwaysLinearSpace = true; + pCaps->m_bDX10Card = true; + pCaps->m_bDX10Blending = true; + } + } + } + else if ( pCaps->m_VendorID == VENDORID_ATI ) + { + if ( IsOpenGL() ) + { + // If we're not a Radeon X1x00 (device IDs in this range), we're a DX10 chip + if ( !( (pCaps->m_DeviceID >= 0x7109) && (pCaps->m_DeviceID <= 0x7291) ) ) + { + pCaps->m_bFogColorSpecifiedInLinearSpace = true; + pCaps->m_bFogColorAlwaysLinearSpace = true; + pCaps->m_bDX10Card = true; + pCaps->m_bDX10Blending = true; + } + } + else + { + // Check for DX10 part + pCaps->m_bDX10Card = pCaps->m_SupportsShaderModel_3_0 && + ( pCaps->m_MaxVertexShader30InstructionSlots > 1024 ) && + ( pCaps->m_MaxPixelShader30InstructionSlots > 512 ) ; + + // On ATI, DX10 card means DX10 blending + pCaps->m_bDX10Blending = pCaps->m_bDX10Card; + + if( pCaps->m_bDX10Blending ) + { + pCaps->m_bFogColorSpecifiedInLinearSpace = true; + pCaps->m_bFogColorAlwaysLinearSpace = true; + } + } + } + else if ( pCaps->m_VendorID == VENDORID_INTEL ) + { + // Intel does not have performant vertex textures + pCaps->m_bDX10Card = false; + + // Intel supports DX10 SRGB on Broadwater and better + // The two checks are for devices from GMA generation (0x29A2-0x2A43) and HD graphics (0x0042-0x2500) + pCaps->m_bDX10Blending = ( ( pCaps->m_DeviceID >= 0x29A2 ) && ( pCaps->m_DeviceID <= 0x2A43 ) ) || + ( ( pCaps->m_DeviceID >= 0x0042 ) && ( pCaps->m_DeviceID <= 0x2500 ) ); + + if( pCaps->m_bDX10Blending ) + { + pCaps->m_bFogColorSpecifiedInLinearSpace = true; + pCaps->m_bFogColorAlwaysLinearSpace = true; + } + } + } + + // Do we have everything necessary to run with integer HDR? Note that + // even if we don't support integer 16-bit/component textures, we + // can still run in this mode if fp16 textures are supported. + bool bSupportsIntegerHDR = pCaps->m_SupportsPixelShaders_2_0 && + pCaps->m_SupportsVertexShaders_2_0 && + // (caps.Caps3 & D3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD) && + // (caps.PrimitiveMiscCaps & D3DPMISCCAPS_SEPARATEALPHABLEND) && + ( bSupportsInteger16Textures || bSupportsFloat16Textures ) && + pCaps->m_SupportsSRGB; + + // Do we have everything necessary to run with float HDR? + bool bSupportsFloatHDR = pCaps->m_SupportsShaderModel_3_0 && + // (caps.Caps3 & D3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD) && + // (caps.PrimitiveMiscCaps & D3DPMISCCAPS_SEPARATEALPHABLEND) && + bSupportsFloat16Textures && + bSupportsFloat16RenderTargets && + pCaps->m_SupportsSRGB && + !IsX360(); + + pCaps->m_MaxHDRType = HDR_TYPE_NONE; + if ( bSupportsFloatHDR ) + pCaps->m_MaxHDRType = HDR_TYPE_FLOAT; + else + if ( bSupportsIntegerHDR ) + pCaps->m_MaxHDRType = HDR_TYPE_INTEGER; + + if ( bSupportsFloatHDR && ( mat_hdr_level.GetInt() == 3 ) ) + { + pCaps->m_HDRType = HDR_TYPE_FLOAT; + } + else if ( bSupportsIntegerHDR ) + { + pCaps->m_HDRType = HDR_TYPE_INTEGER; + } + else + { + pCaps->m_HDRType = HDR_TYPE_NONE; + } + + pCaps->m_bColorOnSecondStream = caps.MaxStreams > 1; + + pCaps->m_bSupportsStreamOffset = ( ( caps.DevCaps2 & D3DDEVCAPS2_STREAMOFFSET ) && // Tie these caps together since we want to filter out + pCaps->m_SupportsPixelShaders_2_0 ); // any DX8 parts which export D3DDEVCAPS2_STREAMOFFSET + + pCaps->m_flMinGammaControlPoint = 0.0f; + pCaps->m_flMaxGammaControlPoint = 65535.0f; + pCaps->m_nGammaControlPointCount = 256; + + // Compute the effective DX support level based on all the other caps + ComputeDXSupportLevel( *pCaps ); + int nCmdlineMaxDXLevel = CommandLine()->ParmValue( "-maxdxlevel", 0 ); + if ( IsOpenGL() && ( nCmdlineMaxDXLevel > 0 ) ) + { + // Prevent customers from slamming us below DX level 90 in OpenGL mode. + nCmdlineMaxDXLevel = MAX( nCmdlineMaxDXLevel, 90 ); + } + if( nCmdlineMaxDXLevel > 0 ) + { + pCaps->m_nMaxDXSupportLevel = min( pCaps->m_nMaxDXSupportLevel, nCmdlineMaxDXLevel ); + } + pCaps->m_nDXSupportLevel = pCaps->m_nMaxDXSupportLevel; + + int nModelIndex = pCaps->m_nDXSupportLevel < 90 ? VERTEX_SHADER_MODEL - 10 : VERTEX_SHADER_MODEL; + pCaps->m_MaxVertexShaderBlendMatrices = (pCaps->m_NumVertexShaderConstants - nModelIndex) / 3; + + if ( pCaps->m_MaxVertexShaderBlendMatrices > NUM_MODEL_TRANSFORMS ) + { + pCaps->m_MaxVertexShaderBlendMatrices = NUM_MODEL_TRANSFORMS; + } + + CheckBorderColorSupport( pCaps, nAdapter ); + + // This may get more complex if we start using multiple flavors of compressed vertex - for now it's "on or off" + pCaps->m_SupportsCompressedVertices = ( pCaps->m_nDXSupportLevel >= 90 ) && ( pCaps->m_CanDoSRGBReadFromRTs ) ? VERTEX_COMPRESSION_ON : VERTEX_COMPRESSION_NONE; + if ( CommandLine()->CheckParm( "-no_compressed_verts" ) ) // m_CanDoSRGBReadFromRTs limits us to Snow Leopard or later on OSX + { + pCaps->m_SupportsCompressedVertices = VERTEX_COMPRESSION_NONE; + } + + // Various vendor-dependent checks... + CheckVendorDependentAlphaToCoverage( pCaps, nAdapter ); + CheckVendorDependentShadowMappingSupport( pCaps, nAdapter ); + + // If we're not on a 3.0 part, these values are more appropriate (X800 & X850 parts from ATI do shadow mapping but not 3.0 ) + if ( !IsOpenGL() ) + { + if ( !pCaps->m_SupportsShaderModel_3_0 ) + { + mat_slopescaledepthbias_shadowmap.SetValue( 5.9f ); + mat_depthbias_shadowmap.SetValue( 0.003f ); + } + } + + if( pCaps->m_MaxUserClipPlanes == 0 ) + { + pCaps->m_UseFastClipping = true; + } + + pCaps->m_MaxSimultaneousRenderTargets = caps.NumSimultaneousRTs; + + return true; +} + +//----------------------------------------------------------------------------- +// Compute the effective DX support level based on all the other caps +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::ComputeDXSupportLevel( HardwareCaps_t &caps ) +{ + // NOTE: Support level is actually DX level * 10 + subversion + // So, 70 = DX7, 80 = DX8, 81 = DX8 w/ 1.4 pixel shaders + // 90 = DX9 w/ 2.0 pixel shaders + // 95 = DX9 w/ 3.0 pixel shaders and vertex textures + // 98 = DX9 XBox360 + // NOTE: 82 = NVidia nv3x cards, which can't run dx9 fast + + // FIXME: Improve this!! There should be a whole list of features + // we require in order to be considered a DX7 board, DX8 board, etc. + + if ( IsX360() ) + { + caps.m_nMaxDXSupportLevel = 98; + return; + } + + bool bIsOpenGL = IsOpenGL(); + + if ( caps.m_SupportsShaderModel_3_0 && !bIsOpenGL ) // Note that we don't tie vertex textures to 30 shaders anymore + { + caps.m_nMaxDXSupportLevel = 95; + return; + } + + // NOTE: sRGB is currently required for DX90 because it isn't doing + // gamma correctly if that feature doesn't exist + if ( caps.m_SupportsVertexShaders_2_0 && caps.m_SupportsPixelShaders_2_0 && caps.m_SupportsSRGB ) + { + caps.m_nMaxDXSupportLevel = 90; + return; + } + + if ( caps.m_SupportsPixelShaders && caps.m_SupportsVertexShaders )// && caps.m_bColorOnSecondStream) + { + if (caps.m_SupportsPixelShaders_1_4) + { + caps.m_nMaxDXSupportLevel = 81; + return; + } + caps.m_nMaxDXSupportLevel = 80; + return; + } + + if( caps.m_SupportsCubeMaps && ( caps.m_MaxBlendMatrices >= 2 ) ) + { + caps.m_nMaxDXSupportLevel = 70; + return; + } + + if ( ( caps.m_NumSamplers >= 2) && caps.m_SupportsMipmapping ) + { + caps.m_nMaxDXSupportLevel = 60; + return; + } + + Assert( 0 ); + // we don't support this! + caps.m_nMaxDXSupportLevel = 50; +} + + + +//----------------------------------------------------------------------------- +// Gets the number of adapters... +//----------------------------------------------------------------------------- +int CShaderDeviceMgrDx8::GetAdapterCount() const +{ + // FIXME: Remove call to InitAdapterInfo once Steam startup issues are resolved. + const_cast<CShaderDeviceMgrDx8*>( this )->InitAdapterInfo(); + + return m_Adapters.Count(); +} + + +//----------------------------------------------------------------------------- +// Returns info about each adapter +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::GetAdapterInfo( int nAdapter, MaterialAdapterInfo_t& info ) const +{ + // FIXME: Remove call to InitAdapterInfo once Steam startup issues are resolved. + const_cast<CShaderDeviceMgrDx8*>( this )->InitAdapterInfo(); + + Assert( ( nAdapter >= 0 ) && ( nAdapter < m_Adapters.Count() ) ); + const HardwareCaps_t &caps = m_Adapters[ nAdapter ].m_ActualCaps; + memcpy( &info, &caps, sizeof(MaterialAdapterInfo_t) ); +} + + +//----------------------------------------------------------------------------- +// Sets the adapter +//----------------------------------------------------------------------------- +bool CShaderDeviceMgrDx8::SetAdapter( int nAdapter, int nAdapterFlags ) +{ + LOCK_SHADERAPI(); + + // FIXME: + // g_pShaderDeviceDx8->m_bReadPixelsEnabled = (nAdapterFlags & MATERIAL_INIT_READ_PIXELS_ENABLED) != 0; + + // Set up hardware information for this adapter... + g_pShaderDeviceDx8->m_DeviceType = (nAdapterFlags & MATERIAL_INIT_REFERENCE_RASTERIZER) ? + D3DDEVTYPE_REF : D3DDEVTYPE_HAL; + + g_pShaderDeviceDx8->m_DisplayAdapter = nAdapter; + if ( g_pShaderDeviceDx8->m_DisplayAdapter >= (UINT)GetAdapterCount() ) + { + g_pShaderDeviceDx8->m_DisplayAdapter = 0; + } + +#ifdef NVPERFHUD + // hack for nvperfhud + g_pShaderDeviceDx8->m_DisplayAdapter = m_pD3D->GetAdapterCount() - 1; + g_pShaderDeviceDx8->m_DeviceType = D3DDEVTYPE_REF; +#endif + + // backward compat + if ( !g_pShaderDeviceDx8->OnAdapterSet() ) + return false; + +// if ( !g_pShaderDeviceDx8->Init() ) +// { +// Warning( "Unable to initialize dx8 device!\n" ); +// return false; +// } + + g_pShaderDevice = g_pShaderDeviceDx8; + + return true; +} + + +//----------------------------------------------------------------------------- +// Returns the number of modes +//----------------------------------------------------------------------------- +int CShaderDeviceMgrDx8::GetModeCount( int nAdapter ) const +{ + LOCK_SHADERAPI(); + Assert( m_pD3D && (nAdapter < GetAdapterCount() ) ); + +#if !defined( _X360 ) + // fixme - what format should I use here? + return m_pD3D->GetAdapterModeCount( nAdapter, D3DFMT_X8R8G8B8 ); +#else + return 1; // Only one mode, which is the current mode set in the 360 dashboard. Going to fill it in with exactly what the 360 is set to. +#endif +} + + +//----------------------------------------------------------------------------- +// Returns mode information.. +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::GetModeInfo( ShaderDisplayMode_t* pInfo, int nAdapter, int nMode ) const +{ + Assert( pInfo->m_nVersion == SHADER_DISPLAY_MODE_VERSION ); + + LOCK_SHADERAPI(); + Assert( m_pD3D && (nAdapter < GetAdapterCount() ) ); + Assert( nMode < GetModeCount( nAdapter ) ); + +#if !defined( _X360 ) + HRESULT hr; + D3DDISPLAYMODE d3dInfo; + + // fixme - what format should I use here? + hr = D3D()->EnumAdapterModes( nAdapter, D3DFMT_X8R8G8B8, nMode, &d3dInfo ); + Assert( !FAILED(hr) ); + + pInfo->m_nWidth = d3dInfo.Width; + pInfo->m_nHeight = d3dInfo.Height; + pInfo->m_Format = ImageLoader::D3DFormatToImageFormat( d3dInfo.Format ); + pInfo->m_nRefreshRateNumerator = d3dInfo.RefreshRate; + pInfo->m_nRefreshRateDenominator = 1; +#else + pInfo->m_Format = ImageLoader::D3DFormatToImageFormat( D3DFMT_X8R8G8B8 ); + pInfo->m_nRefreshRateNumerator = 60; + pInfo->m_nRefreshRateDenominator = 1; + + pInfo->m_nWidth = GetSystemMetrics( SM_CXSCREEN ); + pInfo->m_nHeight = GetSystemMetrics( SM_CYSCREEN ); +#endif +} + + +//----------------------------------------------------------------------------- +// Returns the current mode information for an adapter +//----------------------------------------------------------------------------- +void CShaderDeviceMgrDx8::GetCurrentModeInfo( ShaderDisplayMode_t* pInfo, int nAdapter ) const +{ + Assert( pInfo->m_nVersion == SHADER_DISPLAY_MODE_VERSION ); + + LOCK_SHADERAPI(); + Assert( D3D() ); + + HRESULT hr; + D3DDISPLAYMODE mode = { 0 }; +#if !defined( _X360 ) + hr = D3D()->GetAdapterDisplayMode( nAdapter, &mode ); + Assert( !FAILED(hr) ); +#else + if ( !g_pD3DDevice ) + { + // the console has no prior display or mode until its created + mode.Width = GetSystemMetrics( SM_CXSCREEN ); + mode.Height = GetSystemMetrics( SM_CYSCREEN ); + mode.RefreshRate = 60; + mode.Format = D3DFMT_X8R8G8B8; + } + else + { + hr = g_pD3DDevice->GetDisplayMode( 0, &mode ); + Assert( !FAILED(hr) ); + } +#endif + + pInfo->m_nWidth = mode.Width; + pInfo->m_nHeight = mode.Height; + pInfo->m_Format = ImageLoader::D3DFormatToImageFormat( mode.Format ); + pInfo->m_nRefreshRateNumerator = mode.RefreshRate; + pInfo->m_nRefreshRateDenominator = 1; +} + + +//----------------------------------------------------------------------------- +// Sets the video mode +//----------------------------------------------------------------------------- +CreateInterfaceFn CShaderDeviceMgrDx8::SetMode( void *hWnd, int nAdapter, const ShaderDeviceInfo_t& mode ) +{ + LOCK_SHADERAPI(); + + Assert( nAdapter < GetAdapterCount() ); + int nDXLevel = mode.m_nDXLevel != 0 ? mode.m_nDXLevel : m_Adapters[nAdapter].m_ActualCaps.m_nDXSupportLevel; + if ( m_bObeyDxCommandlineOverride ) + { + nDXLevel = CommandLine()->ParmValue( "-dxlevel", nDXLevel ); + m_bObeyDxCommandlineOverride = false; + } + if ( nDXLevel > m_Adapters[nAdapter].m_ActualCaps.m_nMaxDXSupportLevel ) + { + nDXLevel = m_Adapters[nAdapter].m_ActualCaps.m_nMaxDXSupportLevel; + } + nDXLevel = GetClosestActualDXLevel( nDXLevel ); + + if ( nDXLevel >= 100 ) + return NULL; + + bool bReacquireResourcesNeeded = false; + if ( g_pShaderDevice ) + { + bReacquireResourcesNeeded = IsPC(); + g_pShaderDevice->ReleaseResources(); + } + + if ( g_pShaderAPI ) + { + g_pShaderAPI->OnDeviceShutdown(); + g_pShaderAPI = NULL; + } + + if ( g_pShaderDevice ) + { + g_pShaderDevice->ShutdownDevice(); + g_pShaderDevice = NULL; + } + + g_pShaderShadow = NULL; + + ShaderDeviceInfo_t adjustedMode = mode; + adjustedMode.m_nDXLevel = nDXLevel; + if ( !g_pShaderDeviceDx8->InitDevice( hWnd, nAdapter, adjustedMode ) ) + return NULL; + + if ( !g_pShaderAPIDX8->OnDeviceInit() ) + return NULL; + + g_pShaderDevice = g_pShaderDeviceDx8; + g_pShaderAPI = g_pShaderAPIDX8; + g_pShaderShadow = g_pShaderShadowDx8; + + if ( bReacquireResourcesNeeded ) + { + g_pShaderDevice->ReacquireResources(); + } + + return ShaderInterfaceFactory; +} + + +//----------------------------------------------------------------------------- +// Validates the mode... +//----------------------------------------------------------------------------- +bool CShaderDeviceMgrDx8::ValidateMode( int nAdapter, const ShaderDeviceInfo_t &info ) const +{ + if ( nAdapter >= (int)D3D()->GetAdapterCount() ) + return false; + + ShaderDisplayMode_t displayMode; + + if ( info.m_bWindowed ) + { + // windowed mode always appears on the primary display, so we should use that adapter's + // settings + GetCurrentModeInfo( &displayMode, 0 ); + + // make sure the window fits within the current video mode + if ( ( info.m_DisplayMode.m_nWidth > displayMode.m_nWidth ) || + ( info.m_DisplayMode.m_nHeight > displayMode.m_nHeight ) ) + return false; + } + else + { + GetCurrentModeInfo( &displayMode, nAdapter ); + } + + // Make sure the image format requested is valid + ImageFormat backBufferFormat = FindNearestSupportedBackBufferFormat( nAdapter, + DX8_DEVTYPE, displayMode.m_Format, info.m_DisplayMode.m_Format, info.m_bWindowed ); + return ( backBufferFormat != IMAGE_FORMAT_UNKNOWN ); +} + + +//----------------------------------------------------------------------------- +// Returns the amount of video memory in bytes for a particular adapter +//----------------------------------------------------------------------------- +int CShaderDeviceMgrDx8::GetVidMemBytes( int nAdapter ) const +{ +#if defined( _X360 ) + return 256*1024*1024; +#elif defined (DX_TO_GL_ABSTRACTION) + D3DADAPTER_IDENTIFIER9 devIndentifier; + D3D()->GetAdapterIdentifier( nAdapter, D3DENUM_WHQL_LEVEL, &devIndentifier ); + return devIndentifier.VideoMemory; +#else + // FIXME: This currently ignores the adapter + uint64 nBytes = ::GetVidMemBytes(); + if ( nBytes > INT_MAX ) + return INT_MAX; + return nBytes; +#endif +} + + + +//----------------------------------------------------------------------------- +// +// Shader device +// +//----------------------------------------------------------------------------- + +#if 0 +// FIXME: Enable after I've separated it out from shaderapidx8 a little better +static CShaderDeviceDx8 s_ShaderDeviceDX8; +CShaderDeviceDx8* g_pShaderDeviceDx8 = &s_ShaderDeviceDX8; +#endif + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CShaderDeviceDx8::CShaderDeviceDx8() +{ + g_pD3DDevice = NULL; + for ( int i = 0; i < ARRAYSIZE(m_pFrameSyncQueryObject); i++ ) + { + m_pFrameSyncQueryObject[i] = NULL; + m_bQueryIssued[i] = false; + } + m_pFrameSyncTexture = NULL; + m_bQueuedDeviceLost = false; + m_DeviceState = DEVICE_STATE_OK; + m_bOtherAppInitializing = false; + m_IsResizing = false; + m_bPendingVideoModeChange = false; + m_DeviceSupportsCreateQuery = -1; + m_bUsingStencil = false; + m_bResourcesReleased = false; + m_iStencilBufferBits = 0; + m_NonInteractiveRefresh.m_Mode = MATERIAL_NON_INTERACTIVE_MODE_NONE; + m_NonInteractiveRefresh.m_pVertexShader = NULL; + m_NonInteractiveRefresh.m_pPixelShader = NULL; + m_NonInteractiveRefresh.m_pPixelShaderStartup = NULL; + m_NonInteractiveRefresh.m_pPixelShaderStartupPass2 = NULL; + m_NonInteractiveRefresh.m_pVertexDecl = NULL; + m_NonInteractiveRefresh.m_nPacifierFrame = 0; + m_numReleaseResourcesRefCount = 0; +} + +CShaderDeviceDx8::~CShaderDeviceDx8() +{ +} + + +//----------------------------------------------------------------------------- +// Computes device creation paramters +//----------------------------------------------------------------------------- +static DWORD ComputeDeviceCreationFlags( D3DCAPS& caps, bool bSoftwareVertexProcessing ) +{ + // Find out what type of device to make + bool bPureDeviceSupported = (caps.DevCaps & D3DDEVCAPS_PUREDEVICE) != 0; + + DWORD nDeviceCreationFlags; + if ( !bSoftwareVertexProcessing ) + { + nDeviceCreationFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING; + if ( bPureDeviceSupported ) + { + nDeviceCreationFlags |= D3DCREATE_PUREDEVICE; + } + } + else + { + nDeviceCreationFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING; + } + nDeviceCreationFlags |= D3DCREATE_FPU_PRESERVE; + +#ifdef _X360 + nDeviceCreationFlags |= D3DCREATE_BUFFER_2_FRAMES; +#endif + + return nDeviceCreationFlags; +} + + +//----------------------------------------------------------------------------- +// Computes the supersample flags +//----------------------------------------------------------------------------- +D3DMULTISAMPLE_TYPE CShaderDeviceDx8::ComputeMultisampleType( int nSampleCount ) +{ + switch (nSampleCount) + { +#if !defined( _X360 ) + case 2: return D3DMULTISAMPLE_2_SAMPLES; + case 3: return D3DMULTISAMPLE_3_SAMPLES; + case 4: return D3DMULTISAMPLE_4_SAMPLES; + case 5: return D3DMULTISAMPLE_5_SAMPLES; + case 6: return D3DMULTISAMPLE_6_SAMPLES; + case 7: return D3DMULTISAMPLE_7_SAMPLES; + case 8: return D3DMULTISAMPLE_8_SAMPLES; + case 9: return D3DMULTISAMPLE_9_SAMPLES; + case 10: return D3DMULTISAMPLE_10_SAMPLES; + case 11: return D3DMULTISAMPLE_11_SAMPLES; + case 12: return D3DMULTISAMPLE_12_SAMPLES; + case 13: return D3DMULTISAMPLE_13_SAMPLES; + case 14: return D3DMULTISAMPLE_14_SAMPLES; + case 15: return D3DMULTISAMPLE_15_SAMPLES; + case 16: return D3DMULTISAMPLE_16_SAMPLES; +#else + case 2: return D3DMULTISAMPLE_2_SAMPLES; + case 4: return D3DMULTISAMPLE_4_SAMPLES; +#endif + default: + case 0: + case 1: + return D3DMULTISAMPLE_NONE; + } +} + + +//----------------------------------------------------------------------------- +// Sets the present parameters +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::SetPresentParameters( void* hWnd, int nAdapter, const ShaderDeviceInfo_t &info ) +{ + ShaderDisplayMode_t mode; + g_pShaderDeviceMgr->GetCurrentModeInfo( &mode, nAdapter ); + + HRESULT hr; + ZeroMemory( &m_PresentParameters, sizeof(m_PresentParameters) ); + + m_PresentParameters.Windowed = info.m_bWindowed; + m_PresentParameters.SwapEffect = info.m_bUsingMultipleWindows ? D3DSWAPEFFECT_COPY : D3DSWAPEFFECT_DISCARD; + + // for 360, we want to create it ourselves for hierarchical z support + m_PresentParameters.EnableAutoDepthStencil = IsX360() ? FALSE : TRUE; + + // What back-buffer format should we use? + ImageFormat backBufferFormat = FindNearestSupportedBackBufferFormat( nAdapter, + DX8_DEVTYPE, m_AdapterFormat, info.m_DisplayMode.m_Format, info.m_bWindowed ); + + // What depth format should we use? + m_bUsingStencil = info.m_bUseStencil; + if ( info.m_nDXLevel >= 80 ) + { + // always stencil for dx9/hdr + m_bUsingStencil = true; + } +#if defined( _X360 ) + D3DFORMAT nDepthFormat = ReverseDepthOnX360() ? D3DFMT_D24FS8 : D3DFMT_D24S8; +#else + D3DFORMAT nDepthFormat = m_bUsingStencil ? D3DFMT_D24S8 : D3DFMT_D24X8; +#endif + m_PresentParameters.AutoDepthStencilFormat = FindNearestSupportedDepthFormat( + nAdapter, m_AdapterFormat, backBufferFormat, nDepthFormat ); + m_PresentParameters.hDeviceWindow = (VD3DHWND)hWnd; + + // store how many stencil buffer bits we have available with the depth/stencil buffer + switch( m_PresentParameters.AutoDepthStencilFormat ) + { + case D3DFMT_D24S8: + m_iStencilBufferBits = 8; + break; +#if defined( _X360 ) + case D3DFMT_D24FS8: + m_iStencilBufferBits = 8; + break; +#else + case D3DFMT_D24X4S4: + m_iStencilBufferBits = 4; + break; + case D3DFMT_D15S1: + m_iStencilBufferBits = 1; + break; +#endif + default: + m_iStencilBufferBits = 0; + m_bUsingStencil = false; //couldn't acquire a stencil buffer + }; + + if ( IsX360() || !info.m_bWindowed ) + { + bool useDefault = ( info.m_DisplayMode.m_nWidth == 0 ) || ( info.m_DisplayMode.m_nHeight == 0 ); + m_PresentParameters.BackBufferCount = 1; + m_PresentParameters.BackBufferWidth = useDefault ? mode.m_nWidth : info.m_DisplayMode.m_nWidth; + m_PresentParameters.BackBufferHeight = useDefault ? mode.m_nHeight : info.m_DisplayMode.m_nHeight; + m_PresentParameters.BackBufferFormat = ImageLoader::ImageFormatToD3DFormat( backBufferFormat ); +#if defined( _X360 ) + m_PresentParameters.FrontBufferFormat = D3DFMT_LE_X8R8G8B8; +#endif + if ( !info.m_bWaitForVSync || CommandLine()->FindParm( "-forcenovsync" ) ) + { + m_PresentParameters.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + } + else + { + m_PresentParameters.PresentationInterval = D3DPRESENT_INTERVAL_ONE; + } + + m_PresentParameters.FullScreen_RefreshRateInHz = info.m_DisplayMode.m_nRefreshRateDenominator ? + info.m_DisplayMode.m_nRefreshRateNumerator / info.m_DisplayMode.m_nRefreshRateDenominator : D3DPRESENT_RATE_DEFAULT; + +#if defined( _X360 ) + XVIDEO_MODE videoMode; + XGetVideoMode( &videoMode ); + + // want 30 for 60Hz, and 25 for 50Hz (PAL) + int nNewFpsMax = ( ( int )( videoMode.RefreshRate + 0.5f ) ) >> 1; + // slam to either 30 or 25 so that we don't end up with any other cases. + if( nNewFpsMax < 26 ) + { + nNewFpsMax = 25; + } + else + { + nNewFpsMax = 30; + } + DevMsg( "*******Monitor refresh is %f, setting fps_max to %d*********\n", videoMode.RefreshRate, nNewFpsMax ); + ConVarRef fps_max( "fps_max" ); + fps_max.SetValue( nNewFpsMax ); + + // setup hardware scaling - should be native 720p upsampling to 1080i + if ( info.m_bScaleToOutputResolution ) + { + m_PresentParameters.VideoScalerParameters.ScalerSourceRect.x2 = m_PresentParameters.BackBufferWidth; + m_PresentParameters.VideoScalerParameters.ScalerSourceRect.y2 = m_PresentParameters.BackBufferHeight; + m_PresentParameters.VideoScalerParameters.ScaledOutputWidth = videoMode.dwDisplayWidth; + m_PresentParameters.VideoScalerParameters.ScaledOutputHeight = videoMode.dwDisplayHeight; + DevMsg( "VIDEO SCALING: scaling from %dx%d to %dx%d\n", ( int )m_PresentParameters.BackBufferWidth, ( int )m_PresentParameters.BackBufferHeight, + ( int )videoMode.dwDisplayWidth, ( int )videoMode.dwDisplayHeight ); + } + else + { + DevMsg( "VIDEO SCALING: No scaling: %dx%d\n", ( int )m_PresentParameters.BackBufferWidth, ( int )m_PresentParameters.BackBufferHeight ); + } +#endif + } + else + { + // NJS: We are seeing a lot of time spent in present in some cases when this isn't set. + m_PresentParameters.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + if ( info.m_bResizing ) + { + if ( info.m_bLimitWindowedSize && + ( info.m_nWindowedSizeLimitWidth < mode.m_nWidth || info.m_nWindowedSizeLimitHeight < mode.m_nHeight ) ) + { + // When using material system in windowed resizing apps, it's + // sometimes not a good idea to allocate stuff as big as the screen + // video cards can soo run out of resources + m_PresentParameters.BackBufferWidth = info.m_nWindowedSizeLimitWidth; + m_PresentParameters.BackBufferHeight = info.m_nWindowedSizeLimitHeight; + } + else + { + // When in resizing windowed mode, + // we want to allocate enough memory to deal with any resizing... + m_PresentParameters.BackBufferWidth = mode.m_nWidth; + m_PresentParameters.BackBufferHeight = mode.m_nHeight; + } + } + else + { + m_PresentParameters.BackBufferWidth = info.m_DisplayMode.m_nWidth; + m_PresentParameters.BackBufferHeight = info.m_DisplayMode.m_nHeight; + } + m_PresentParameters.BackBufferFormat = ImageLoader::ImageFormatToD3DFormat( backBufferFormat ); + m_PresentParameters.BackBufferCount = 1; + } + + if ( info.m_nAASamples > 0 && ( m_PresentParameters.SwapEffect == D3DSWAPEFFECT_DISCARD ) ) + { + D3DMULTISAMPLE_TYPE multiSampleType = ComputeMultisampleType( info.m_nAASamples ); + DWORD nQualityLevel; + + // FIXME: Should we add the quality level to the ShaderAdapterMode_t struct? + // 16x on nVidia refers to CSAA or "Coverage Sampled Antialiasing" + const HardwareCaps_t &adapterCaps = g_ShaderDeviceMgrDx8.GetHardwareCaps( nAdapter ); + if ( ( info.m_nAASamples == 16 ) && ( adapterCaps.m_VendorID == VENDORID_NVIDIA ) ) + { + multiSampleType = ComputeMultisampleType(4); + hr = D3D()->CheckDeviceMultiSampleType( nAdapter, DX8_DEVTYPE, + m_PresentParameters.BackBufferFormat, m_PresentParameters.Windowed, + multiSampleType, &nQualityLevel ); // 4x at highest quality level + + if ( !FAILED( hr ) && ( nQualityLevel == 16 ) ) + { + nQualityLevel = nQualityLevel - 1; // Highest quality level triggers 16x CSAA + } + else + { + nQualityLevel = 0; // No CSAA + } + } + else // Regular MSAA on any old vendor + { + hr = D3D()->CheckDeviceMultiSampleType( nAdapter, DX8_DEVTYPE, + m_PresentParameters.BackBufferFormat, m_PresentParameters.Windowed, + multiSampleType, &nQualityLevel ); + + nQualityLevel = 0; + } + + if ( !FAILED( hr ) ) + { + m_PresentParameters.MultiSampleType = multiSampleType; + m_PresentParameters.MultiSampleQuality = nQualityLevel; + } + } + else + { + m_PresentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; + m_PresentParameters.MultiSampleQuality = 0; + } +} + + +//----------------------------------------------------------------------------- +// Initializes, shuts down the D3D device +//----------------------------------------------------------------------------- +bool CShaderDeviceDx8::InitDevice( void* hwnd, int nAdapter, const ShaderDeviceInfo_t &info ) +{ + //Debugger(); + + // good place to run some self tests. + //#if OSX + //{ + // extern void GLMgrSelfTests( void ); + // GLMgrSelfTests(); + //} + //#endif + + // windowed + if ( !CreateD3DDevice( (VD3DHWND)hwnd, nAdapter, info ) ) + return false; + + // Hook up our own windows proc to get at messages to tell us when + // other instances of the material system are trying to set the mode + InstallWindowHook( (VD3DHWND)m_hWnd ); + return true; +} + +void CShaderDeviceDx8::ShutdownDevice() +{ + if ( IsPC() && IsActive() ) + { + Dx9Device()->Release(); + +#ifdef STUBD3D + delete ( CStubD3DDevice * )Dx9Device(); +#endif + + g_pD3DDevice = NULL; + + RemoveWindowHook( (VD3DHWND)m_hWnd ); + m_hWnd = 0; + } +} + + +//----------------------------------------------------------------------------- +// Are we using graphics? +//----------------------------------------------------------------------------- +bool CShaderDeviceDx8::IsUsingGraphics() const +{ + //*****LOCK_SHADERAPI(); + return IsActive(); +} + + +//----------------------------------------------------------------------------- +// Returns the current adapter in use +//----------------------------------------------------------------------------- +int CShaderDeviceDx8::GetCurrentAdapter() const +{ + LOCK_SHADERAPI(); + return m_DisplayAdapter; +} + + +//----------------------------------------------------------------------------- +// Returns the current adapter in use +//----------------------------------------------------------------------------- +char *CShaderDeviceDx8::GetDisplayDeviceName() +{ + if( m_sDisplayDeviceName.IsEmpty() ) + { + D3DADAPTER_IDENTIFIER9 ident; + // On Win10, this function is getting called with m_nAdapter still initialized to -1. + // It's failing, and m_sDisplayDeviceName has garbage, and tf2 fails to launch. + // To repro this, run "hl2.exe -dev -fullscreen -game tf" on Win10. + HRESULT hr = D3D()->GetAdapterIdentifier( Max( m_nAdapter, 0 ), 0, &ident ); + if ( FAILED(hr) ) + { + Assert( false ); + ident.DeviceName[0] = 0; + } + m_sDisplayDeviceName = ident.DeviceName; + } + return m_sDisplayDeviceName.GetForModify(); +} + + +//----------------------------------------------------------------------------- +// Use this to spew information about the 3D layer +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::SpewDriverInfo() const +{ + LOCK_SHADERAPI(); + HRESULT hr; + D3DCAPS caps; + D3DADAPTER_IDENTIFIER9 ident; + + RECORD_COMMAND( DX8_GET_DEVICE_CAPS, 0 ); + + RECORD_COMMAND( DX8_GET_ADAPTER_IDENTIFIER, 2 ); + RECORD_INT( m_nAdapter ); + RECORD_INT( 0 ); + + Dx9Device()->GetDeviceCaps( &caps ); + hr = D3D()->GetAdapterIdentifier( m_nAdapter, D3DENUM_WHQL_LEVEL, &ident ); + + Warning("Shader API Driver Info:\n\nDriver : %s Version : %lld\n", + ident.Driver, ident.DriverVersion.QuadPart ); + Warning("Driver Description : %s\n", ident.Description ); + Warning("Chipset version %d %d %d %d\n\n", + ident.VendorId, ident.DeviceId, ident.SubSysId, ident.Revision ); + + ShaderDisplayMode_t mode; + g_pShaderDeviceMgr->GetCurrentModeInfo( &mode, m_nAdapter ); + Warning("Display mode : %d x %d (%s)\n", + mode.m_nWidth, mode.m_nHeight, ImageLoader::GetName( mode.m_Format ) ); + Warning("Vertex Shader Version : %d.%d Pixel Shader Version : %d.%d\n", + (caps.VertexShaderVersion >> 8) & 0xFF, caps.VertexShaderVersion & 0xFF, + (caps.PixelShaderVersion >> 8) & 0xFF, caps.PixelShaderVersion & 0xFF); + Warning("\nDevice Caps :\n"); + Warning("CANBLTSYSTONONLOCAL %s CANRENDERAFTERFLIP %s HWRASTERIZATION %s\n", + (caps.DevCaps & D3DDEVCAPS_CANBLTSYSTONONLOCAL) ? " Y " : " N ", + (caps.DevCaps & D3DDEVCAPS_CANRENDERAFTERFLIP) ? " Y " : " N ", + (caps.DevCaps & D3DDEVCAPS_HWRASTERIZATION) ? " Y " : "*N*" ); + Warning("HWTRANSFORMANDLIGHT %s NPATCHES %s PUREDEVICE %s\n", + (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) ? " Y " : " N ", + (caps.DevCaps & D3DDEVCAPS_NPATCHES) ? " Y " : " N ", + (caps.DevCaps & D3DDEVCAPS_PUREDEVICE) ? " Y " : " N " ); + Warning("SEPARATETEXTUREMEMORIES %s TEXTURENONLOCALVIDMEM %s TEXTURESYSTEMMEMORY %s\n", + (caps.DevCaps & D3DDEVCAPS_SEPARATETEXTUREMEMORIES) ? "*Y*" : " N ", + (caps.DevCaps & D3DDEVCAPS_TEXTURENONLOCALVIDMEM) ? " Y " : " N ", + (caps.DevCaps & D3DDEVCAPS_TEXTURESYSTEMMEMORY) ? " Y " : " N " ); + Warning("TEXTUREVIDEOMEMORY %s TLVERTEXSYSTEMMEMORY %s TLVERTEXVIDEOMEMORY %s\n", + (caps.DevCaps & D3DDEVCAPS_TEXTUREVIDEOMEMORY) ? " Y " : "*N*", + (caps.DevCaps & D3DDEVCAPS_TLVERTEXSYSTEMMEMORY) ? " Y " : "*N*", + (caps.DevCaps & D3DDEVCAPS_TLVERTEXVIDEOMEMORY) ? " Y " : " N " ); + + Warning("\nPrimitive Caps :\n"); + Warning("BLENDOP %s CLIPPLANESCALEDPOINTS %s CLIPTLVERTS %s\n", + (caps.PrimitiveMiscCaps & D3DPMISCCAPS_BLENDOP) ? " Y " : " N ", + (caps.PrimitiveMiscCaps & D3DPMISCCAPS_CLIPPLANESCALEDPOINTS) ? " Y " : " N ", + (caps.PrimitiveMiscCaps & D3DPMISCCAPS_CLIPTLVERTS) ? " Y " : " N " ); + Warning("COLORWRITEENABLE %s MASKZ %s TSSARGTEMP %s\n", + (caps.PrimitiveMiscCaps & D3DPMISCCAPS_COLORWRITEENABLE) ? " Y " : " N ", + (caps.PrimitiveMiscCaps & D3DPMISCCAPS_MASKZ) ? " Y " : "*N*", + (caps.PrimitiveMiscCaps & D3DPMISCCAPS_TSSARGTEMP) ? " Y " : " N " ); + + Warning("\nRaster Caps :\n"); + Warning("FOGRANGE %s FOGTABLE %s FOGVERTEX %s ZFOG %s WFOG %s\n", + (caps.RasterCaps & D3DPRASTERCAPS_FOGRANGE) ? " Y " : " N ", + (caps.RasterCaps & D3DPRASTERCAPS_FOGTABLE) ? " Y " : " N ", + (caps.RasterCaps & D3DPRASTERCAPS_FOGVERTEX) ? " Y " : " N ", + (caps.RasterCaps & D3DPRASTERCAPS_ZFOG) ? " Y " : " N ", + (caps.RasterCaps & D3DPRASTERCAPS_WFOG) ? " Y " : " N " ); + Warning("MIPMAPLODBIAS %s WBUFFER %s ZBIAS %s ZTEST %s\n", + (caps.RasterCaps & D3DPRASTERCAPS_MIPMAPLODBIAS) ? " Y " : " N ", + (caps.RasterCaps & D3DPRASTERCAPS_WBUFFER) ? " Y " : " N ", + (caps.RasterCaps & D3DPRASTERCAPS_DEPTHBIAS) ? " Y " : " N ", + (caps.RasterCaps & D3DPRASTERCAPS_ZTEST) ? " Y " : "*N*" ); + + Warning("Size of Texture Memory : %d kb\n", g_pHardwareConfig->Caps().m_TextureMemorySize / 1024 ); + Warning("Max Texture Dimensions : %d x %d\n", + caps.MaxTextureWidth, caps.MaxTextureHeight ); + if (caps.MaxTextureAspectRatio != 0) + Warning("Max Texture Aspect Ratio : *%d*\n", caps.MaxTextureAspectRatio ); + Warning("Max Textures : %d Max Stages : %d\n", + caps.MaxSimultaneousTextures, caps.MaxTextureBlendStages ); + + Warning("\nTexture Caps :\n"); + Warning("ALPHA %s CUBEMAP %s MIPCUBEMAP %s SQUAREONLY %s\n", + (caps.TextureCaps & D3DPTEXTURECAPS_ALPHA) ? " Y " : " N ", + (caps.TextureCaps & D3DPTEXTURECAPS_CUBEMAP) ? " Y " : " N ", + (caps.TextureCaps & D3DPTEXTURECAPS_MIPCUBEMAP) ? " Y " : " N ", + (caps.TextureCaps & D3DPTEXTURECAPS_SQUAREONLY) ? "*Y*" : " N " ); + + Warning( "vendor id: 0x%x\n", g_pHardwareConfig->ActualCaps().m_VendorID ); + Warning( "device id: 0x%x\n", g_pHardwareConfig->ActualCaps().m_DeviceID ); + + Warning( "SHADERAPI CAPS:\n" ); + Warning( "m_NumSamplers: %d\n", g_pHardwareConfig->Caps().m_NumSamplers ); + Warning( "m_NumTextureStages: %d\n", g_pHardwareConfig->Caps().m_NumTextureStages ); + Warning( "m_HasSetDeviceGammaRamp: %s\n", g_pHardwareConfig->Caps().m_HasSetDeviceGammaRamp ? "yes" : "no" ); + Warning( "m_SupportsVertexShaders (1.1): %s\n", g_pHardwareConfig->Caps().m_SupportsVertexShaders ? "yes" : "no" ); + Warning( "m_SupportsVertexShaders_2_0: %s\n", g_pHardwareConfig->Caps().m_SupportsVertexShaders_2_0 ? "yes" : "no" ); + Warning( "m_SupportsPixelShaders (1.1): %s\n", g_pHardwareConfig->Caps().m_SupportsPixelShaders ? "yes" : "no" ); + Warning( "m_SupportsPixelShaders_1_4: %s\n", g_pHardwareConfig->Caps().m_SupportsPixelShaders_1_4 ? "yes" : "no" ); + Warning( "m_SupportsPixelShaders_2_0: %s\n", g_pHardwareConfig->Caps().m_SupportsPixelShaders_2_0 ? "yes" : "no" ); + Warning( "m_SupportsPixelShaders_2_b: %s\n", g_pHardwareConfig->Caps().m_SupportsPixelShaders_2_b ? "yes" : "no" ); + Warning( "m_SupportsShaderModel_3_0: %s\n", g_pHardwareConfig->Caps().m_SupportsShaderModel_3_0 ? "yes" : "no" ); + + switch( g_pHardwareConfig->Caps().m_SupportsCompressedTextures ) + { + case COMPRESSED_TEXTURES_ON: + Warning( "m_SupportsCompressedTextures: COMPRESSED_TEXTURES_ON\n" ); + break; + case COMPRESSED_TEXTURES_OFF: + Warning( "m_SupportsCompressedTextures: COMPRESSED_TEXTURES_ON\n" ); + break; + case COMPRESSED_TEXTURES_NOT_INITIALIZED: + Warning( "m_SupportsCompressedTextures: COMPRESSED_TEXTURES_NOT_INITIALIZED\n" ); + break; + default: + Assert( 0 ); + break; + } + Warning( "m_SupportsCompressedVertices: %d\n", g_pHardwareConfig->Caps().m_SupportsCompressedVertices ); + Warning( "m_bSupportsAnisotropicFiltering: %s\n", g_pHardwareConfig->Caps().m_bSupportsAnisotropicFiltering ? "yes" : "no" ); + Warning( "m_nMaxAnisotropy: %d\n", g_pHardwareConfig->Caps().m_nMaxAnisotropy ); + Warning( "m_MaxTextureWidth: %d\n", g_pHardwareConfig->Caps().m_MaxTextureWidth ); + Warning( "m_MaxTextureHeight: %d\n", g_pHardwareConfig->Caps().m_MaxTextureHeight ); + Warning( "m_MaxTextureAspectRatio: %d\n", g_pHardwareConfig->Caps().m_MaxTextureAspectRatio ); + Warning( "m_MaxPrimitiveCount: %d\n", g_pHardwareConfig->Caps().m_MaxPrimitiveCount ); + Warning( "m_ZBiasAndSlopeScaledDepthBiasSupported: %s\n", g_pHardwareConfig->Caps().m_ZBiasAndSlopeScaledDepthBiasSupported ? "yes" : "no" ); + Warning( "m_SupportsMipmapping: %s\n", g_pHardwareConfig->Caps().m_SupportsMipmapping ? "yes" : "no" ); + Warning( "m_SupportsOverbright: %s\n", g_pHardwareConfig->Caps().m_SupportsOverbright ? "yes" : "no" ); + Warning( "m_SupportsCubeMaps: %s\n", g_pHardwareConfig->Caps().m_SupportsCubeMaps ? "yes" : "no" ); + Warning( "m_NumPixelShaderConstants: %d\n", g_pHardwareConfig->Caps().m_NumPixelShaderConstants ); + Warning( "m_NumVertexShaderConstants: %d\n", g_pHardwareConfig->Caps().m_NumVertexShaderConstants ); + Warning( "m_NumBooleanVertexShaderConstants: %d\n", g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants ); + Warning( "m_NumIntegerVertexShaderConstants: %d\n", g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants ); + Warning( "m_TextureMemorySize: %d\n", g_pHardwareConfig->Caps().m_TextureMemorySize ); + Warning( "m_MaxNumLights: %d\n", g_pHardwareConfig->Caps().m_MaxNumLights ); + Warning( "m_SupportsHardwareLighting: %s\n", g_pHardwareConfig->Caps().m_SupportsHardwareLighting ? "yes" : "no" ); + Warning( "m_MaxBlendMatrices: %d\n", g_pHardwareConfig->Caps().m_MaxBlendMatrices ); + Warning( "m_MaxBlendMatrixIndices: %d\n", g_pHardwareConfig->Caps().m_MaxBlendMatrixIndices ); + Warning( "m_MaxVertexShaderBlendMatrices: %d\n", g_pHardwareConfig->Caps().m_MaxVertexShaderBlendMatrices ); + Warning( "m_SupportsMipmappedCubemaps: %s\n", g_pHardwareConfig->Caps().m_SupportsMipmappedCubemaps ? "yes" : "no" ); + Warning( "m_SupportsNonPow2Textures: %s\n", g_pHardwareConfig->Caps().m_SupportsNonPow2Textures ? "yes" : "no" ); + Warning( "m_nDXSupportLevel: %d\n", g_pHardwareConfig->Caps().m_nDXSupportLevel ); + Warning( "m_PreferDynamicTextures: %s\n", g_pHardwareConfig->Caps().m_PreferDynamicTextures ? "yes" : "no" ); + Warning( "m_HasProjectedBumpEnv: %s\n", g_pHardwareConfig->Caps().m_HasProjectedBumpEnv ? "yes" : "no" ); + Warning( "m_MaxUserClipPlanes: %d\n", g_pHardwareConfig->Caps().m_MaxUserClipPlanes ); + Warning( "m_SupportsSRGB: %s\n", g_pHardwareConfig->Caps().m_SupportsSRGB ? "yes" : "no" ); + switch( g_pHardwareConfig->Caps().m_HDRType ) + { + case HDR_TYPE_NONE: + Warning( "m_HDRType: HDR_TYPE_NONE\n" ); + break; + case HDR_TYPE_INTEGER: + Warning( "m_HDRType: HDR_TYPE_INTEGER\n" ); + break; + case HDR_TYPE_FLOAT: + Warning( "m_HDRType: HDR_TYPE_FLOAT\n" ); + break; + default: + Assert( 0 ); + break; + } + Warning( "m_bSupportsSpheremapping: %s\n", g_pHardwareConfig->Caps().m_bSupportsSpheremapping ? "yes" : "no" ); + Warning( "m_UseFastClipping: %s\n", g_pHardwareConfig->Caps().m_UseFastClipping ? "yes" : "no" ); + Warning( "m_pShaderDLL: %s\n", g_pHardwareConfig->Caps().m_pShaderDLL ); + Warning( "m_bNeedsATICentroidHack: %s\n", g_pHardwareConfig->Caps().m_bNeedsATICentroidHack ? "yes" : "no" ); + Warning( "m_bDisableShaderOptimizations: %s\n", g_pHardwareConfig->Caps().m_bDisableShaderOptimizations ? "yes" : "no" ); + Warning( "m_bColorOnSecondStream: %s\n", g_pHardwareConfig->Caps().m_bColorOnSecondStream ? "yes" : "no" ); + Warning( "m_MaxSimultaneousRenderTargets: %d\n", g_pHardwareConfig->Caps().m_MaxSimultaneousRenderTargets ); +} + + +//----------------------------------------------------------------------------- +// Back buffer information +//----------------------------------------------------------------------------- +ImageFormat CShaderDeviceDx8::GetBackBufferFormat() const +{ + return ImageLoader::D3DFormatToImageFormat( m_PresentParameters.BackBufferFormat ); +} + +void CShaderDeviceDx8::GetBackBufferDimensions( int& width, int& height ) const +{ + width = m_PresentParameters.BackBufferWidth; + height = m_PresentParameters.BackBufferHeight; +} + + +//----------------------------------------------------------------------------- +// Detects support for CreateQuery +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::DetectQuerySupport( IDirect3DDevice9 *pD3DDevice ) +{ + // Do I need to detect whether this device supports CreateQuery before creating it? + if ( m_DeviceSupportsCreateQuery != -1 ) + return; + + IDirect3DQuery9 *pQueryObject = NULL; + + // Detect whether query is supported by creating and releasing: + HRESULT hr = pD3DDevice->CreateQuery( D3DQUERYTYPE_EVENT, &pQueryObject ); + if ( !FAILED(hr) && pQueryObject ) + { + pQueryObject->Release(); + m_DeviceSupportsCreateQuery = 1; + } + else + { + m_DeviceSupportsCreateQuery = 0; + } +} + + +const char *GetD3DErrorText( HRESULT hr ) +{ + const char *pszMoreInfo = NULL; + +#if defined( _WIN32 ) && !defined(DX_TO_GL_ABSTRACTION) + switch ( hr ) + { + case D3DERR_WRONGTEXTUREFORMAT: + pszMoreInfo = "D3DERR_WRONGTEXTUREFORMAT: The pixel format of the texture surface is not valid."; + break; + case D3DERR_UNSUPPORTEDCOLOROPERATION: + pszMoreInfo = "D3DERR_UNSUPPORTEDCOLOROPERATION: The device does not support a specified texture-blending operation for color values."; + break; + case D3DERR_UNSUPPORTEDCOLORARG: + pszMoreInfo = "D3DERR_UNSUPPORTEDCOLORARG: The device does not support a specified texture-blending argument for color values."; + break; + case D3DERR_UNSUPPORTEDALPHAOPERATION: + pszMoreInfo = "D3DERR_UNSUPPORTEDALPHAOPERATION: The device does not support a specified texture-blending operation for the alpha channel."; + break; + case D3DERR_UNSUPPORTEDALPHAARG: + pszMoreInfo = "D3DERR_UNSUPPORTEDALPHAARG: The device does not support a specified texture-blending argument for the alpha channel."; + break; + case D3DERR_TOOMANYOPERATIONS: + pszMoreInfo = "D3DERR_TOOMANYOPERATIONS: The application is requesting more texture-filtering operations than the device supports."; + break; + case D3DERR_CONFLICTINGTEXTUREFILTER: + pszMoreInfo = "D3DERR_CONFLICTINGTEXTUREFILTER: The current texture filters cannot be used together."; + break; + case D3DERR_UNSUPPORTEDFACTORVALUE: + pszMoreInfo = "D3DERR_UNSUPPORTEDFACTORVALUE: The device does not support the specified texture factor value."; + break; + case D3DERR_CONFLICTINGRENDERSTATE: + pszMoreInfo = "D3DERR_CONFLICTINGRENDERSTATE: The currently set render states cannot be used together."; + break; + case D3DERR_UNSUPPORTEDTEXTUREFILTER: + pszMoreInfo = "D3DERR_UNSUPPORTEDTEXTUREFILTER: The device does not support the specified texture filter."; + break; + case D3DERR_CONFLICTINGTEXTUREPALETTE: + pszMoreInfo = "D3DERR_CONFLICTINGTEXTUREPALETTE: The current textures cannot be used simultaneously."; + break; + case D3DERR_DRIVERINTERNALERROR: + pszMoreInfo = "D3DERR_DRIVERINTERNALERROR: Internal driver error."; + break; + case D3DERR_NOTFOUND: + pszMoreInfo = "D3DERR_NOTFOUND: The requested item was not found."; + break; + case D3DERR_DEVICELOST: + pszMoreInfo = "D3DERR_DEVICELOST: The device has been lost but cannot be reset at this time. Therefore, rendering is not possible."; + break; + case D3DERR_DEVICENOTRESET: + pszMoreInfo = "D3DERR_DEVICENOTRESET: The device has been lost."; + break; + case D3DERR_NOTAVAILABLE: + pszMoreInfo = "D3DERR_NOTAVAILABLE: This device does not support the queried technique."; + break; + case D3DERR_OUTOFVIDEOMEMORY: + pszMoreInfo = "D3DERR_OUTOFVIDEOMEMORY: Direct3D does not have enough display memory to perform the operation. The device is using more resources in a single scene than can fit simultaneously into video memory."; + break; + case D3DERR_INVALIDDEVICE: + pszMoreInfo = "D3DERR_INVALIDDEVICE: The requested device type is not valid."; + break; + case D3DERR_INVALIDCALL: + pszMoreInfo = "D3DERR_INVALIDCALL: The method call is invalid."; + break; + case D3DERR_DRIVERINVALIDCALL: + pszMoreInfo = "D3DERR_DRIVERINVALIDCALL"; + break; + case D3DERR_WASSTILLDRAWING: + pszMoreInfo = "D3DERR_WASSTILLDRAWING: The previous blit operation that is transferring information to or from this surface is incomplete."; + break; + } +#endif // _WIN32 + + return pszMoreInfo; +} + + +//----------------------------------------------------------------------------- +// Actually creates the D3D Device once the present parameters are set up +//----------------------------------------------------------------------------- +IDirect3DDevice9* CShaderDeviceDx8::InvokeCreateDevice( void* hWnd, int nAdapter, DWORD deviceCreationFlags ) +{ + IDirect3DDevice9 *pD3DDevice = NULL; + D3DDEVTYPE devType = DX8_DEVTYPE; + +#if NVPERFHUD + nAdapter = D3D()->GetAdapterCount()-1; + devType = D3DDEVTYPE_REF; + deviceCreationFlags = D3DCREATE_FPU_PRESERVE | D3DCREATE_HARDWARE_VERTEXPROCESSING; +#endif + +#if 1 // with the changes for opengl to enable threading, we no longer need the d3d device to have threading guards +#ifndef _X360 + // Create the device with multi-threaded safeguards if we're using mat_queue_mode 2. + // The logic to enable multithreaded rendering happens well after the device has been created, + // so we replicate some of that logic here. + ConVarRef mat_queue_mode( "mat_queue_mode" ); + if ( mat_queue_mode.GetInt() == 2 || + ( mat_queue_mode.GetInt() == -2 && GetCPUInformation()->m_nPhysicalProcessors >= 2 ) || + ( mat_queue_mode.GetInt() == -1 && GetCPUInformation()->m_nPhysicalProcessors >= 2 ) ) + { + deviceCreationFlags |= D3DCREATE_MULTITHREADED; + } +#endif +#endif + +#ifdef ENABLE_NULLREF_DEVICE_SUPPORT + devType = CommandLine()->FindParm( "-nulldevice" ) ? D3DDEVTYPE_NULLREF: devType; +#endif + + HRESULT hr = D3D()->CreateDevice( nAdapter, devType, + (VD3DHWND)hWnd, deviceCreationFlags, &m_PresentParameters, &pD3DDevice ); + + if ( !FAILED( hr ) && pD3DDevice ) + return pD3DDevice; + + if ( !IsPC() ) + return NULL; + + // try again, other applications may be taking their time + Sleep( 1000 ); + hr = D3D()->CreateDevice( nAdapter, devType, + (VD3DHWND)hWnd, deviceCreationFlags, &m_PresentParameters, &pD3DDevice ); + if ( !FAILED( hr ) && pD3DDevice ) + return pD3DDevice; + + // in this case, we actually are allocating too much memory.... + // This will cause us to use less buffers... + if ( m_PresentParameters.Windowed ) + { + m_PresentParameters.SwapEffect = D3DSWAPEFFECT_COPY; + m_PresentParameters.BackBufferCount = 0; + hr = D3D()->CreateDevice( nAdapter, devType, + (VD3DHWND)hWnd, deviceCreationFlags, &m_PresentParameters, &pD3DDevice ); + } + if ( !FAILED( hr ) && pD3DDevice ) + return pD3DDevice; + + const char *pszMoreInfo = NULL; + switch ( hr ) + { +#ifdef _WIN32 + case D3DERR_INVALIDCALL: + // Override the error text for this error since it has a known meaning for CreateDevice failures. + pszMoreInfo = "D3DERR_INVALIDCALL: The device or the device driver may not support Direct3D or may not support the resolution or color depth specified."; + break; +#endif // _WIN32 + default: + pszMoreInfo = GetD3DErrorText( hr ); + break; + } + + // Otherwise we failed, show a message and shutdown + if ( pszMoreInfo ) + { + DWarning( "init", 0, "Failed to create %s device!\nError 0x%lX: %s\n\nPlease see the following for more info.\n" + "http://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=772\n", IsOpenGL() ? "OpenGL" : "D3D", hr, pszMoreInfo ); + } + else + { + DWarning( "init", 0, "Failed to create %s device!\nError 0x%lX.\n\nPlease see the following for more info.\n" + "http://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=772\n", IsOpenGL() ? "OpenGL" : "D3D", hr ); + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Creates the D3D Device +//----------------------------------------------------------------------------- +bool CShaderDeviceDx8::CreateD3DDevice( void* pHWnd, int nAdapter, const ShaderDeviceInfo_t &info ) +{ + Assert( info.m_nVersion == SHADER_DEVICE_INFO_VERSION ); + + MEM_ALLOC_CREDIT_( __FILE__ ": D3D Device" ); + + VD3DHWND hWnd = (VD3DHWND)pHWnd; + +#if ( !defined( PIX_INSTRUMENTATION ) && !defined( _X360 ) && !defined( NVPERFHUD ) ) + D3DPERF_SetOptions(1); // Explicitly disallow PIX instrumented profiling in external builds +#endif + + // Get some caps.... + D3DCAPS caps; + HRESULT hr = D3D()->GetDeviceCaps( nAdapter, DX8_DEVTYPE, &caps ); + if ( FAILED( hr ) ) + return false; + + // Determine the adapter format + ShaderDisplayMode_t mode; + g_pShaderDeviceMgrDx8->GetCurrentModeInfo( &mode, nAdapter ); + m_AdapterFormat = mode.m_Format; + + // FIXME: Need to do this prior to SetPresentParameters. Fix. + // Make it part of HardwareCaps_t + InitializeColorInformation( nAdapter, DX8_DEVTYPE, m_AdapterFormat ); + + const HardwareCaps_t &adapterCaps = g_ShaderDeviceMgrDx8.GetHardwareCaps( nAdapter ); + DWORD deviceCreationFlags = ComputeDeviceCreationFlags( caps, adapterCaps.m_bSoftwareVertexProcessing ); + SetPresentParameters( hWnd, nAdapter, info ); + + // Tell all other instances of the material system to let go of memory + SendIPCMessage( RELEASE_MESSAGE ); + + // Creates the device + IDirect3DDevice9 *pD3DDevice = InvokeCreateDevice( pHWnd, nAdapter, deviceCreationFlags ); + + if ( !pD3DDevice ) + return false; + + // Check to see if query is supported + DetectQuerySupport( pD3DDevice ); + +#ifdef STUBD3D + Dx9Device() = new CStubD3DDevice( pD3DDevice, g_pFullFileSystem ); +#else + g_pD3DDevice = pD3DDevice; +#endif + +#if defined( _X360 ) + // Create the depth buffer, created manually to enable hierarchical z + { + D3DSURFACE_PARAMETERS DepthStencilParams; + + // Depth is immediately after the back buffer in EDRAM + // allocate the hierarchical z tiles at the end of the area so all other allocations can trivially allocate at 0 + DepthStencilParams.Base = XGSurfaceSize( + m_PresentParameters.BackBufferWidth, + m_PresentParameters.BackBufferHeight, + m_PresentParameters.BackBufferFormat, + m_PresentParameters.MultiSampleType ); + DepthStencilParams.ColorExpBias = 0; + DepthStencilParams.HierarchicalZBase = GPU_HIERARCHICAL_Z_TILES - XGHierarchicalZSize( m_PresentParameters.BackBufferWidth, m_PresentParameters.BackBufferHeight, m_PresentParameters.MultiSampleType ); + + IDirect3DSurface *pDepthStencilSurface = NULL; + hr = Dx9Device()->CreateDepthStencilSurface( + m_PresentParameters.BackBufferWidth, + m_PresentParameters.BackBufferHeight, + m_PresentParameters.AutoDepthStencilFormat, + m_PresentParameters.MultiSampleType, + m_PresentParameters.MultiSampleQuality, + TRUE, + &pDepthStencilSurface, + &DepthStencilParams ); + Assert( SUCCEEDED( hr ) ); + if ( FAILED( hr ) ) + return false; + + hr = Dx9Device()->SetDepthStencilSurface( pDepthStencilSurface ); + Assert( SUCCEEDED( hr ) ); + if ( FAILED( hr ) ) + return false; + } + + // Initialize XUI, needed for TTF font rasterization + // xui requires and shares our d3d device + { + hr = XuiRenderInitShared( pD3DDevice, &m_PresentParameters, XuiD3DXTextureLoader ); + if ( FAILED( hr ) ) + return false; + + XUIInitParams xuiInit; + XUI_INIT_PARAMS( xuiInit ); + xuiInit.dwFlags = XUI_INIT_PARAMS_FLAGS_NONE; + xuiInit.pHooks = NULL; + hr = XuiInit( &xuiInit ); + if ( FAILED( hr ) ) + return false; + + hr = XuiRenderCreateDC( &m_hDC ); + if ( FAILED( hr ) ) + return false; + } +#endif + + // CheckDeviceLost(); + + // Tell all other instances of the material system it's ok to grab memory + SendIPCMessage( REACQUIRE_MESSAGE ); + + m_hWnd = pHWnd; + m_nAdapter = m_DisplayAdapter = nAdapter; + m_DeviceState = DEVICE_STATE_OK; + m_bIsMinimized = false; + m_bQueuedDeviceLost = false; + + m_IsResizing = info.m_bWindowed && info.m_bResizing; + + // This is our current view. + m_ViewHWnd = hWnd; + GetWindowSize( m_nWindowWidth, m_nWindowHeight ); + + g_pHardwareConfig->SetupHardwareCaps( info, g_ShaderDeviceMgrDx8.GetHardwareCaps( nAdapter ) ); + + // FIXME: Bake this into hardware config + // What texture formats do we support? + if ( D3DSupportsCompressedTextures() ) + { + g_pHardwareConfig->ActualCapsForEdit().m_SupportsCompressedTextures = COMPRESSED_TEXTURES_ON; + g_pHardwareConfig->CapsForEdit().m_SupportsCompressedTextures = COMPRESSED_TEXTURES_ON; + } + else + { + g_pHardwareConfig->ActualCapsForEdit().m_SupportsCompressedTextures = COMPRESSED_TEXTURES_OFF; + g_pHardwareConfig->CapsForEdit().m_SupportsCompressedTextures = COMPRESSED_TEXTURES_OFF; + } + + return ( !FAILED( hr ) ); +} + + +//----------------------------------------------------------------------------- +// Frame sync +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::AllocFrameSyncTextureObject() +{ + if ( IsX360() ) + return; + + FreeFrameSyncTextureObject(); + + // Create a tiny managed texture. + HRESULT hr = Dx9Device()->CreateTexture( + 1, 1, // width, height + 0, // levels + D3DUSAGE_DYNAMIC, // usage + D3DFMT_A8R8G8B8, // format + D3DPOOL_DEFAULT, + &m_pFrameSyncTexture, + NULL ); + if ( FAILED( hr ) ) + { + m_pFrameSyncTexture = NULL; + } +} + +void CShaderDeviceDx8::FreeFrameSyncTextureObject() +{ + if ( IsX360() ) + return; + + if ( m_pFrameSyncTexture ) + { + m_pFrameSyncTexture->Release(); + m_pFrameSyncTexture = NULL; + } +} + +void CShaderDeviceDx8::AllocFrameSyncObjects( void ) +{ + if ( IsX360() ) + return; + + if ( mat_debugalttab.GetBool() ) + { + Warning( "mat_debugalttab: CShaderAPIDX8::AllocFrameSyncObjects\n" ); + } + + // Allocate the texture for frame syncing in case we force that to be on. + AllocFrameSyncTextureObject(); + + if ( m_DeviceSupportsCreateQuery == 0 ) + { + for ( int i = 0; i < ARRAYSIZE(m_pFrameSyncQueryObject); i++ ) + { + m_pFrameSyncQueryObject[i] = NULL; + m_bQueryIssued[i] = false; + } + return; + } + + // FIXME FIXME FIXME!!!!! Need to record this. + for ( int i = 0; i < ARRAYSIZE(m_pFrameSyncQueryObject); i++ ) + { + HRESULT hr = Dx9Device()->CreateQuery( D3DQUERYTYPE_EVENT, &m_pFrameSyncQueryObject[i] ); + if( hr == D3DERR_NOTAVAILABLE ) + { + Warning( "D3DQUERYTYPE_EVENT not available on this driver\n" ); + Assert( m_pFrameSyncQueryObject[i] == NULL ); + } + else + { + Assert( hr == D3D_OK ); + Assert( m_pFrameSyncQueryObject[i] ); + m_pFrameSyncQueryObject[i]->Issue( D3DISSUE_END ); + m_bQueryIssued[i] = true; + } + } +} + +void CShaderDeviceDx8::FreeFrameSyncObjects( void ) +{ + if ( IsX360() ) + return; + + if ( mat_debugalttab.GetBool() ) + { + Warning( "mat_debugalttab: CShaderAPIDX8::FreeFrameSyncObjects\n" ); + } + + FreeFrameSyncTextureObject(); + + // FIXME FIXME FIXME!!!!! Need to record this. + for ( int i = 0; i < ARRAYSIZE(m_pFrameSyncQueryObject); i++ ) + { + if ( m_pFrameSyncQueryObject[i] ) + { + if ( m_bQueryIssued[i] ) + { + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "D3DQueryGetData %t", tmSendCallStack( TELEMETRY_LEVEL0, 0 ) ); + + double flStartTime = Plat_FloatTime(); + BOOL dummyData = 0; + HRESULT hr = S_OK; + + // Make every attempt (within 2 seconds) to get the result from the query. Doing so may prevent + // crashes in the driver if we try to release outstanding queries. + do + { + hr = m_pFrameSyncQueryObject[i]->GetData( &dummyData, sizeof( dummyData ), D3DGETDATA_FLUSH ); + double flCurrTime = Plat_FloatTime(); + + // don't wait more than 2 seconds for these + if ( flCurrTime - flStartTime > 2.00 ) + break; + } while ( hr == S_FALSE ); + } +#ifdef DBGFLAG_ASSERT + int nRetVal = +#endif + m_pFrameSyncQueryObject[i]->Release(); + Assert( nRetVal == 0 ); + m_pFrameSyncQueryObject[i] = NULL; + m_bQueryIssued[i] = false; + } + } +} + + +//----------------------------------------------------------------------------- +// Occurs when another application is initializing +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::OtherAppInitializing( bool initializing ) +{ + if ( !ThreadOwnsDevice() || !ThreadInMainThread() ) + { + if ( initializing ) + { + ShaderUtil()->OnThreadEvent( SHADER_THREAD_OTHER_APP_START ); + } + else + { + ShaderUtil()->OnThreadEvent( SHADER_THREAD_OTHER_APP_END ); + } + return; + } + Assert( m_bOtherAppInitializing != initializing ); + + if ( !IsDeactivated() ) + { + Dx9Device()->EndScene(); + } + + // NOTE: OtherApp is set in this way because we need to know we're + // active as we release and restore everything + CheckDeviceLost( initializing ); + + if ( !IsDeactivated() ) + { + Dx9Device()->BeginScene(); + } +} + + +void CShaderDeviceDx8::HandleThreadEvent( uint32 threadEvent ) +{ + Assert(ThreadOwnsDevice()); + switch ( threadEvent ) + { + case SHADER_THREAD_OTHER_APP_START: + OtherAppInitializing(true); + break; + case SHADER_THREAD_RELEASE_RESOURCES: + ReleaseResources(); + break; + case SHADER_THREAD_EVICT_RESOURCES: + EvictManagedResourcesInternal(); + break; + case SHADER_THREAD_RESET_RENDER_STATE: + ResetRenderState(); + break; + case SHADER_THREAD_ACQUIRE_RESOURCES: + ReacquireResources(); + break; + case SHADER_THREAD_OTHER_APP_END: + OtherAppInitializing(false); + break; + + } +} + +//----------------------------------------------------------------------------- +// We lost the device, but we have a chance to recover +//----------------------------------------------------------------------------- +bool CShaderDeviceDx8::TryDeviceReset() +{ + if ( IsX360() ) + return true; + + // Don't try to reset the device until we're sure our resources have been released + if ( !m_bResourcesReleased ) + { + return false; + } + + // FIXME: Make this rebuild the Dx9Device from scratch! + // Helps with compatibility + HRESULT hr = Dx9Device()->Reset( &m_PresentParameters ); + bool bResetSuccess = !FAILED(hr); + +#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) + if ( bResetSuccess && g_ShaderDeviceUsingD3D9Ex ) + { + bResetSuccess = SUCCEEDED( Dx9Device()->TestCooperativeLevel() ); + if ( bResetSuccess ) + { + Warning("video driver has crashed and been reset, re-uploading resources now"); + } + } +#endif + + if ( bResetSuccess ) + m_bResourcesReleased = false; + return bResetSuccess; +} + + +//----------------------------------------------------------------------------- +// Release, reacquire resources +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::ReleaseResources() +{ + if ( !ThreadOwnsDevice() || !ThreadInMainThread() ) + { + // Set our resources as not being released yet. + // We reset this in two places since release resources can be called without a call to TryDeviceReset. + m_bResourcesReleased = false; + ShaderUtil()->OnThreadEvent( SHADER_THREAD_RELEASE_RESOURCES ); + return; + } + + // Only the initial "ReleaseResources" actually has effect + if ( m_numReleaseResourcesRefCount ++ != 0 ) + { + Warning( "ReleaseResources has no effect, now at level %d.\n", m_numReleaseResourcesRefCount ); + DevWarning( "ReleaseResources called twice is a bug: use IsDeactivated to check for a valid device.\n" ); + Assert( 0 ); + return; + } + + LOCK_SHADERAPI(); + CPixEvent( PIX_VALVE_ORANGE, "ReleaseResources" ); + + FreeFrameSyncObjects(); + FreeNonInteractiveRefreshObjects(); + ShaderUtil()->ReleaseShaderObjects(); + MeshMgr()->ReleaseBuffers(); + g_pShaderAPI->ReleaseShaderObjects(); + +#ifdef _DEBUG + if ( MeshMgr()->BufferCount() != 0 ) + { + for( int i = 0; i < MeshMgr()->BufferCount(); i++ ) + { + } + } +#endif + + // All meshes cleaned up? + Assert( MeshMgr()->BufferCount() == 0 ); + + // Signal that our resources have been released so that we can try to reset the device + m_bResourcesReleased = true; +} + + +void CShaderDeviceDx8::ReacquireResources() +{ + ReacquireResourcesInternal(); +} + +void CShaderDeviceDx8::ReacquireResourcesInternal( bool bResetState, bool bForceReacquire, char const *pszForceReason ) +{ + if ( !ThreadOwnsDevice() || !ThreadInMainThread() ) + { + if ( bResetState ) + { + ShaderUtil()->OnThreadEvent( SHADER_THREAD_RESET_RENDER_STATE ); + } + ShaderUtil()->OnThreadEvent( SHADER_THREAD_ACQUIRE_RESOURCES ); + return; + } + if ( bForceReacquire ) + { + // If we are forcing reacquire then warn if release calls are remaining unpaired + if ( m_numReleaseResourcesRefCount > 1 ) + { + Warning( "Forcefully resetting device (%s), resources release level was %d.\n", pszForceReason ? pszForceReason : "unspecified", m_numReleaseResourcesRefCount ); + Assert( 0 ); + } + m_numReleaseResourcesRefCount = 0; + } + else + { + // Only the final "ReacquireResources" actually has effect + if ( -- m_numReleaseResourcesRefCount != 0 ) + { + Warning( "ReacquireResources has no effect, now at level %d.\n", m_numReleaseResourcesRefCount ); + DevWarning( "ReacquireResources being discarded is a bug: use IsDeactivated to check for a valid device.\n" ); + Assert( 0 ); + + if ( m_numReleaseResourcesRefCount < 0 ) + { + m_numReleaseResourcesRefCount = 0; + } + + return; + } + } + + if ( bResetState ) + { + ResetRenderState(); + } + + LOCK_SHADERAPI(); + CPixEvent event( PIX_VALVE_ORANGE, "ReacquireResources" ); + + g_pShaderAPI->RestoreShaderObjects(); + AllocFrameSyncObjects(); + AllocNonInteractiveRefreshObjects(); + MeshMgr()->RestoreBuffers(); + ShaderUtil()->RestoreShaderObjects( CShaderDeviceMgrBase::ShaderInterfaceFactory ); +} + + +//----------------------------------------------------------------------------- +// Changes the window size +//----------------------------------------------------------------------------- +bool CShaderDeviceDx8::ResizeWindow( const ShaderDeviceInfo_t &info ) +{ + if ( IsX360() ) + return false; + + m_bPendingVideoModeChange = false; + + // We don't need to do crap if the window was set up to set up + // to be resizing... + if ( info.m_bResizing ) + return false; + + g_pShaderDeviceMgr->InvokeModeChangeCallbacks(); + + ReleaseResources(); + + SetPresentParameters( (VD3DHWND)m_hWnd, m_DisplayAdapter, info ); + HRESULT hr = Dx9Device()->Reset( &m_PresentParameters ); + if ( FAILED( hr ) ) + { + Warning( "ResizeWindow: Reset failed, hr = 0x%08lX.\n", hr ); + return false; + } + else + { + ReacquireResourcesInternal( true, true, "ResizeWindow" ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Queue up the fact that the device was lost +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::MarkDeviceLost( ) +{ + if ( IsX360() ) + return; + + m_bQueuedDeviceLost = true; +} + + +//----------------------------------------------------------------------------- +// Checks if the device was lost +//----------------------------------------------------------------------------- +#if defined( _DEBUG ) && !defined( _X360 ) +ConVar mat_forcelostdevice( "mat_forcelostdevice", "0" ); +#endif + +void CShaderDeviceDx8::CheckDeviceLost( bool bOtherAppInitializing ) +{ +#if !defined( _X360 ) + // FIXME: We could also queue up if WM_SIZE changes and look at that + // but that seems to only make sense if we have resizable windows where + // we do *not* allocate buffers as large as the entire current video mode + // which we're not doing +#ifdef _WIN32 + m_bIsMinimized = ( static_cast<BOOL>(IsIconic( ( HWND )m_hWnd )) == (BOOL)TRUE ); +#else + m_bIsMinimized = ( IsIconic( (VD3DHWND)m_hWnd ) == TRUE ); +#endif + m_bOtherAppInitializing = bOtherAppInitializing; + +#ifdef _DEBUG + if ( mat_forcelostdevice.GetBool() ) + { + mat_forcelostdevice.SetValue( 0 ); + MarkDeviceLost(); + } +#endif + + HRESULT hr = D3D_OK; +#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) + if ( g_ShaderDeviceUsingD3D9Ex && m_DeviceState == DEVICE_STATE_OK ) + { + // Steady state - PresentEx return value will mark us lost if necessary. + // We do not care if we are minimized in this state. + m_bIsMinimized = false; + } + else +#endif + { + RECORD_COMMAND( DX8_TEST_COOPERATIVE_LEVEL, 0 ); + hr = Dx9Device()->TestCooperativeLevel(); + } + + // If some other call returned device lost previously in the frame, spoof the return value from TCL + if ( m_bQueuedDeviceLost ) + { + hr = (hr != D3D_OK) ? hr : D3DERR_DEVICENOTRESET; + m_bQueuedDeviceLost = false; + } + + if ( m_DeviceState == DEVICE_STATE_OK ) + { + // We can transition out of ok if bOtherAppInitializing is set + // or if we become minimized, or if TCL returns anything other than D3D_OK. + if ( ( hr != D3D_OK ) || m_bIsMinimized ) + { + // purge unreferenced materials + g_pShaderUtil->UncacheUnusedMaterials( true ); + + // We were ok, now we're not. Release resources + ReleaseResources(); + m_DeviceState = DEVICE_STATE_LOST_DEVICE; + } + else if ( bOtherAppInitializing ) + { + // purge unreferenced materials + g_pShaderUtil->UncacheUnusedMaterials( true ); + + // We were ok, now we're not. Release resources + ReleaseResources(); + m_DeviceState = DEVICE_STATE_OTHER_APP_INIT; + } + } + + // Immediately checking devicelost after ok helps in the case where we got D3DERR_DEVICENOTRESET + // in which case we want to immdiately try to switch out of DEVICE_STATE_LOST and into DEVICE_STATE_NEEDS_RESET + if ( m_DeviceState == DEVICE_STATE_LOST_DEVICE ) + { + // We can only try to reset if we're not minimized and not lost + if ( !m_bIsMinimized && (hr != D3DERR_DEVICELOST) ) + { + m_DeviceState = DEVICE_STATE_NEEDS_RESET; + } + } + + // Immediately checking needs reset also helps for the case where we got D3DERR_DEVICENOTRESET + if ( m_DeviceState == DEVICE_STATE_NEEDS_RESET ) + { + if ( ( hr == D3DERR_DEVICELOST ) || m_bIsMinimized ) + { + m_DeviceState = DEVICE_STATE_LOST_DEVICE; + } + else + { + bool bResetSucceeded = TryDeviceReset(); + if ( bResetSucceeded ) + { + if ( !bOtherAppInitializing ) + { + m_DeviceState = DEVICE_STATE_OK; + + // We were bad, now we're ok. Restore resources and reset render state. + ReacquireResourcesInternal( true, true, "NeedsReset" ); + } + else + { + m_DeviceState = DEVICE_STATE_OTHER_APP_INIT; + } + } + } + } + + if ( m_DeviceState == DEVICE_STATE_OTHER_APP_INIT ) + { + if ( ( hr != D3D_OK ) || m_bIsMinimized ) + { + m_DeviceState = DEVICE_STATE_LOST_DEVICE; + } + else if ( !bOtherAppInitializing ) + { + m_DeviceState = DEVICE_STATE_OK; + + // We were bad, now we're ok. Restore resources and reset render state. + ReacquireResourcesInternal( true, true, "OtherAppInit" ); + } + } + + // Do mode change if we have a video mode change. + if ( m_bPendingVideoModeChange && !IsDeactivated() ) + { +#ifdef _DEBUG + Warning( "mode change!\n" ); +#endif + // now purge unreferenced materials + g_pShaderUtil->UncacheUnusedMaterials( true ); + + ResizeWindow( m_PendingVideoModeChangeConfig ); + } +#endif +} + + +//----------------------------------------------------------------------------- +// Special method to refresh the screen on the XBox360 +//----------------------------------------------------------------------------- +bool CShaderDeviceDx8::AllocNonInteractiveRefreshObjects() +{ +#if defined( _X360 ) + + const char *strVertexShaderProgram = + " float4x4 matWVP : register(c0);" + " struct VS_IN" + " {" + " float4 ObjPos : POSITION;" + " float2 TexCoord : TEXCOORD;" + " };" + " struct VS_OUT" + " {" + " float4 ProjPos : POSITION;" + " float2 TexCoord : TEXCOORD;" + " };" + " VS_OUT main( VS_IN In )" + " {" + " VS_OUT Out; " + " Out.ProjPos = mul( matWVP, In.ObjPos );" + " Out.TexCoord = In.TexCoord;" + " return Out;" + " }"; + + const char *strPixelShaderProgram = + " struct PS_IN" + " {" + " float2 TexCoord : TEXCOORD;" + " };" + " sampler detail : register( s0 );" + " float4 main( PS_IN In ) : COLOR" + " {" + " return tex2D( detail, In.TexCoord );" + " }"; + + const char *strPixelShaderProgram2 = + " struct PS_IN" + " {" + " float2 TexCoord : TEXCOORD;" + " };" + " sampler detail : register( s0 );" + " float4 main( PS_IN In ) : COLOR" + " {" + " return tex2D( detail, In.TexCoord );" + " }"; + + const char *strPixelShaderProgram3 = + " struct PS_IN" + " {" + " float2 TexCoord : TEXCOORD;" + " };" + " float SrgbGammaToLinear( float flSrgbGammaValue )" + " {" + " float x = saturate( flSrgbGammaValue );" + " return ( x <= 0.04045f ) ? ( x / 12.92f ) : ( pow( ( x + 0.055f ) / 1.055f, 2.4f ) );" + " }" + "float X360LinearToGamma( float flLinearValue )" + "{" + " float fl360GammaValue;" + "" + " flLinearValue = saturate( flLinearValue );" + " if ( flLinearValue < ( 128.0f / 1023.0f ) )" + " {" + " if ( flLinearValue < ( 64.0f / 1023.0f ) )" + " {" + " fl360GammaValue = flLinearValue * ( 1023.0f * ( 1.0f / 255.0f ) );" + " }" + " else" + " {" + " fl360GammaValue = flLinearValue * ( ( 1023.0f / 2.0f ) * ( 1.0f / 255.0f ) ) + ( 32.0f / 255.0f );" + " }" + " }" + " else" + " {" + " if ( flLinearValue < ( 512.0f / 1023.0f ) )" + " {" + " fl360GammaValue = flLinearValue * ( ( 1023.0f / 4.0f ) * ( 1.0f / 255.0f ) ) + ( 64.0f / 255.0f );" + " }" + " else" + " {" + " fl360GammaValue = flLinearValue * ( ( 1023.0f /8.0f ) * ( 1.0f / 255.0f ) ) + ( 128.0f /255.0f );" + " if ( fl360GammaValue > 1.0f )" + " {" + " fl360GammaValue = 1.0f;" + " }" + " }" + " }" + "" + " fl360GammaValue = saturate( fl360GammaValue );" + " return fl360GammaValue;" + "}" + " sampler detail : register( s0 );" + " float4 main( PS_IN In ) : COLOR" + " {" + " float4 vTextureColor = tex2D( detail, In.TexCoord );" + " vTextureColor.r = X360LinearToGamma( SrgbGammaToLinear( vTextureColor.r ) );" + " vTextureColor.g = X360LinearToGamma( SrgbGammaToLinear( vTextureColor.g ) );" + " vTextureColor.b = X360LinearToGamma( SrgbGammaToLinear( vTextureColor.b ) );" + " return vTextureColor;" + " }"; + + D3DVERTEXELEMENT9 VertexElements[4] = + { + { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, + { 0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, + D3DDECL_END() + }; + + ID3DXBuffer *pErrorMsg = NULL; + ID3DXBuffer *pShaderCode = NULL; + + HRESULT hr = D3DXCompileShader( strVertexShaderProgram, (UINT)strlen( strVertexShaderProgram ), NULL, NULL, "main", "vs_2_0", 0, &pShaderCode, &pErrorMsg, NULL ); + if ( FAILED( hr ) ) + return false; + + Dx9Device()->CreateVertexShader( (DWORD*)pShaderCode->GetBufferPointer(), &m_NonInteractiveRefresh.m_pVertexShader ); + pShaderCode->Release(); + pShaderCode = NULL; + if ( pErrorMsg ) + { + pErrorMsg->Release(); + pErrorMsg = NULL; + } + + hr = D3DXCompileShader( strPixelShaderProgram, (UINT)strlen( strPixelShaderProgram ), NULL, NULL, "main", "ps_2_0", 0, &pShaderCode, &pErrorMsg, NULL ); + if ( FAILED(hr) ) + return false; + + Dx9Device()->CreatePixelShader( (DWORD*)pShaderCode->GetBufferPointer(), &m_NonInteractiveRefresh.m_pPixelShader ); + pShaderCode->Release(); + if ( pErrorMsg ) + { + pErrorMsg->Release(); + pErrorMsg = NULL; + } + + hr = D3DXCompileShader( strPixelShaderProgram3, (UINT)strlen( strPixelShaderProgram3 ), NULL, NULL, "main", "ps_2_0", 0, &pShaderCode, &pErrorMsg, NULL ); + if ( FAILED(hr) ) + return false; + + Dx9Device()->CreatePixelShader( (DWORD*)pShaderCode->GetBufferPointer(), &m_NonInteractiveRefresh.m_pPixelShaderStartup ); + pShaderCode->Release(); + if ( pErrorMsg ) + { + pErrorMsg->Release(); + pErrorMsg = NULL; + } + + hr = D3DXCompileShader( strPixelShaderProgram2, (UINT)strlen( strPixelShaderProgram2 ), NULL, NULL, "main", "ps_2_0", 0, &pShaderCode, &pErrorMsg, NULL ); + if ( FAILED(hr) ) + return false; + + Dx9Device()->CreatePixelShader( (DWORD*)pShaderCode->GetBufferPointer(), &m_NonInteractiveRefresh.m_pPixelShaderStartupPass2 ); + pShaderCode->Release(); + if ( pErrorMsg ) + { + pErrorMsg->Release(); + pErrorMsg = NULL; + } + + // Create a vertex declaration from the element descriptions. + Dx9Device()->CreateVertexDeclaration( VertexElements, &m_NonInteractiveRefresh.m_pVertexDecl ); + +#endif + + return true; +} + +void CShaderDeviceDx8::FreeNonInteractiveRefreshObjects() +{ + if ( m_NonInteractiveRefresh.m_pVertexShader ) + { + m_NonInteractiveRefresh.m_pVertexShader->Release(); + m_NonInteractiveRefresh.m_pVertexShader = NULL; + } + + if ( m_NonInteractiveRefresh.m_pPixelShader ) + { + m_NonInteractiveRefresh.m_pPixelShader->Release(); + m_NonInteractiveRefresh.m_pPixelShader = NULL; + } + + if ( m_NonInteractiveRefresh.m_pPixelShaderStartup ) + { + m_NonInteractiveRefresh.m_pPixelShaderStartup->Release(); + m_NonInteractiveRefresh.m_pPixelShaderStartup = NULL; + } + + if ( m_NonInteractiveRefresh.m_pPixelShaderStartupPass2 ) + { + m_NonInteractiveRefresh.m_pPixelShaderStartupPass2->Release(); + m_NonInteractiveRefresh.m_pPixelShaderStartupPass2 = NULL; + } + + if ( m_NonInteractiveRefresh.m_pVertexDecl ) + { + m_NonInteractiveRefresh.m_pVertexDecl->Release(); + m_NonInteractiveRefresh.m_pVertexDecl = NULL; + } +} + +bool CShaderDeviceDx8::InNonInteractiveMode() const +{ + return m_NonInteractiveRefresh.m_Mode != MATERIAL_NON_INTERACTIVE_MODE_NONE; +} + +void CShaderDeviceDx8::EnableNonInteractiveMode( MaterialNonInteractiveMode_t mode, ShaderNonInteractiveInfo_t *pInfo ) +{ + if ( !IsX360() ) + return; + if ( pInfo && ( pInfo->m_hTempFullscreenTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) ) + { + mode = MATERIAL_NON_INTERACTIVE_MODE_NONE; + } + m_NonInteractiveRefresh.m_Mode = mode; + if ( pInfo ) + { + m_NonInteractiveRefresh.m_Info = *pInfo; + } + m_NonInteractiveRefresh.m_nPacifierFrame = 0; + + if ( mode != MATERIAL_NON_INTERACTIVE_MODE_NONE ) + { + ConVarRef mat_monitorgamma( "mat_monitorgamma" ); + ConVarRef mat_monitorgamma_tv_range_min( "mat_monitorgamma_tv_range_min" ); + ConVarRef mat_monitorgamma_tv_range_max( "mat_monitorgamma_tv_range_max" ); + ConVarRef mat_monitorgamma_tv_exp( "mat_monitorgamma_tv_exp" ); + ConVarRef mat_monitorgamma_tv_enabled( "mat_monitorgamma_tv_enabled" ); + SetHardwareGammaRamp( mat_monitorgamma.GetFloat(), mat_monitorgamma_tv_range_min.GetFloat(), mat_monitorgamma_tv_range_max.GetFloat(), + mat_monitorgamma_tv_exp.GetFloat(), mat_monitorgamma_tv_enabled.GetBool() ); + } + +#ifdef _X360 + if ( mode != MATERIAL_NON_INTERACTIVE_MODE_NONE ) + { + // HACK: VSync off (prevents us wasting time blocking on VSync due to our irregular present intervals) + Dx9Device()->SetRenderState( D3DRS_PRESENTINTERVAL, D3DPRESENT_INTERVAL_IMMEDIATE ); + } + else + { + // HACK: VSync on (defaulting to on on 360 is fine, but really should save+restore this state) + Dx9Device()->SetRenderState( D3DRS_PRESENTINTERVAL, D3DPRESENT_INTERVAL_ONE ); + } +#endif + +// Msg( "Time elapsed: %.3f Peak %.3f Ave %.5f Count %d Count Above %d\n", Plat_FloatTime() - m_NonInteractiveRefresh.m_flStartTime, +// m_NonInteractiveRefresh.m_flPeakDt, m_NonInteractiveRefresh.m_flTotalDt / m_NonInteractiveRefresh.m_nSamples, m_NonInteractiveRefresh.m_nSamples, m_NonInteractiveRefresh.m_nCountAbove66 ); + + m_NonInteractiveRefresh.m_flStartTime = m_NonInteractiveRefresh.m_flLastPresentTime = + m_NonInteractiveRefresh.m_flLastPacifierTime = Plat_FloatTime(); + m_NonInteractiveRefresh.m_flPeakDt = 0.0f; + m_NonInteractiveRefresh.m_flTotalDt = 0.0f; + m_NonInteractiveRefresh.m_nSamples = 0; + m_NonInteractiveRefresh.m_nCountAbove66 = 0; +} + +void CShaderDeviceDx8::UpdatePresentStats() +{ + float t = Plat_FloatTime(); + float flActualDt = t - m_NonInteractiveRefresh.m_flLastPresentTime; + if ( flActualDt > m_NonInteractiveRefresh.m_flPeakDt ) + { + m_NonInteractiveRefresh.m_flPeakDt = flActualDt; + } + if ( flActualDt > 0.066 ) + { + ++m_NonInteractiveRefresh.m_nCountAbove66; + } + + m_NonInteractiveRefresh.m_flTotalDt += flActualDt; + ++m_NonInteractiveRefresh.m_nSamples; + + t = Plat_FloatTime(); + m_NonInteractiveRefresh.m_flLastPresentTime = t; +} + +void CShaderDeviceDx8::RefreshFrontBufferNonInteractive() +{ + if ( !IsX360() || !InNonInteractiveMode() ) + return; + + // Other code should not be talking to D3D at the same time as this + AUTO_LOCK( m_nonInteractiveModeMutex ); + +#ifdef _X360 + g_pShaderAPI->OwnGPUResources( false ); + IDirect3DBaseTexture *pTexture = g_pShaderAPI->GetD3DTexture( m_NonInteractiveRefresh.m_Info.m_hTempFullscreenTexture ); + + int w, h; + g_pShaderAPI->GetBackBufferDimensions( w, h ); + XMMATRIX matWVP = XMMatrixOrthographicOffCenterLH( 0, (FLOAT)w, (FLOAT)h, 0, 0, 1 ); + + // Structure to hold vertex data. + struct TEXVERTEX + { + FLOAT Position[3]; + FLOAT TexCoord[2]; + }; + TEXVERTEX Vertices[4]; + + Vertices[0].Position[0] = -0.5f; + Vertices[0].Position[1] = -0.5f; + Vertices[0].Position[2] = 0; + Vertices[0].TexCoord[0] = 0; + Vertices[0].TexCoord[1] = 0; + + Vertices[1].Position[0] = w-0.5f; + Vertices[1].Position[1] = -0.5f; + Vertices[1].Position[2] = 0; + Vertices[1].TexCoord[0] = 1; + Vertices[1].TexCoord[1] = 0; + + Vertices[2].Position[0] = w-0.5f; + Vertices[2].Position[1] = h-0.5f; + Vertices[2].Position[2] = 0; + Vertices[2].TexCoord[0] = 1; + Vertices[2].TexCoord[1] = 1; + + Vertices[3].Position[0] = -0.5f; + Vertices[3].Position[1] = h-0.5f; + Vertices[3].Position[2] = 0; + Vertices[3].TexCoord[0] = 0; + Vertices[3].TexCoord[1] = 1; + + D3DVIEWPORT9 viewport; + viewport.X = viewport.Y = 0; + viewport.Width = w; viewport.Height = h; + viewport.MinZ = ReverseDepthOnX360() ? 1.0f : 0.0f; + viewport.MaxZ = 1.0f - viewport.MinZ; + + bool bInStartupMode = ( m_NonInteractiveRefresh.m_Mode == MATERIAL_NON_INTERACTIVE_MODE_STARTUP ); + + float flDepth = (ShaderUtil()->GetConfig().bReverseDepth ^ ReverseDepthOnX360()) ? 0.0f : 1.0f; + Dx9Device()->Clear( 0, NULL, D3DCLEAR_ZBUFFER, 0, flDepth, 0L ); + + Dx9Device()->SetViewport( &viewport ); + Dx9Device()->SetTexture( 0, pTexture ); + Dx9Device()->SetVertexShader( m_NonInteractiveRefresh.m_pVertexShader ); + Dx9Device()->SetPixelShader( bInStartupMode ? m_NonInteractiveRefresh.m_pPixelShaderStartup : m_NonInteractiveRefresh.m_pPixelShader ); + Dx9Device()->SetVertexShaderConstantF( 0, (FLOAT*)&matWVP, 4 ); + Dx9Device()->SetVertexDeclaration( m_NonInteractiveRefresh.m_pVertexDecl ); + Dx9Device()->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP ); + Dx9Device()->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP ); + Dx9Device()->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); + Dx9Device()->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); + + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ ); + + Dx9Device()->DrawPrimitiveUP( D3DPT_QUADLIST, 1, Vertices, sizeof( TEXVERTEX ) ); + + if ( bInStartupMode ) + { + float flXPos = m_NonInteractiveRefresh.m_Info.m_flNormalizedX; + float flYPos = m_NonInteractiveRefresh.m_Info.m_flNormalizedY; + float flHeight = m_NonInteractiveRefresh.m_Info.m_flNormalizedSize; + int nSize = h * flHeight; + int x = w * flXPos - nSize * 0.5f; + int y = h * flYPos - nSize * 0.5f; + w = h = nSize; + + Vertices[0].Position[0] = x - 0.5f; + Vertices[0].Position[1] = y - 0.5f; + Vertices[1].Position[0] = x+w-0.5f; + Vertices[1].Position[1] = y - 0.5f; + Vertices[2].Position[0] = x+w-0.5f; + Vertices[2].Position[1] = y+h-0.5f; + Vertices[3].Position[0] = x - 0.5f; + Vertices[3].Position[1] = y+h-0.5f; + + float t = Plat_FloatTime(); + float flDt = t - m_NonInteractiveRefresh.m_flLastPacifierTime; + if ( flDt > 0.030f ) + { + if ( ++m_NonInteractiveRefresh.m_nPacifierFrame >= m_NonInteractiveRefresh.m_Info.m_nPacifierCount ) + { + m_NonInteractiveRefresh.m_nPacifierFrame = 0; + } + m_NonInteractiveRefresh.m_flLastPacifierTime = t; + } + + pTexture = g_pShaderAPI->GetD3DTexture( m_NonInteractiveRefresh.m_Info.m_pPacifierTextures[ m_NonInteractiveRefresh.m_nPacifierFrame ] ); + Dx9Device()->SetRenderState( D3DRS_ALPHABLENDENABLE, 1 ); + Dx9Device()->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); + Dx9Device()->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); + Dx9Device()->SetTexture( 0, pTexture ); + Dx9Device()->SetPixelShader( m_NonInteractiveRefresh.m_pPixelShaderStartupPass2 ); + Dx9Device()->DrawPrimitiveUP( D3DPT_QUADLIST, 1, Vertices, sizeof( TEXVERTEX ) ); + } + + Dx9Device()->SetVertexShader( NULL ); + Dx9Device()->SetPixelShader( NULL ); + Dx9Device()->SetTexture( 0, NULL ); + Dx9Device()->SetVertexDeclaration( NULL ); + + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "D3DPresent" ); + + Dx9Device()->Present( 0, 0, 0, 0 ); + g_pShaderAPI->QueueResetRenderState(); + g_pShaderAPI->OwnGPUResources( true ); + + UpdatePresentStats(); +#endif +} + + +//----------------------------------------------------------------------------- +// Page flip +//----------------------------------------------------------------------------- +void CShaderDeviceDx8::Present() +{ + LOCK_SHADERAPI(); + + // need to flush the dynamic buffer + g_pShaderAPI->FlushBufferedPrimitives(); + + if ( !IsDeactivated() ) + { + Dx9Device()->EndScene(); + } + + HRESULT hr = S_OK; + + // if we're in queued mode, don't present if the device is already lost + bool bValidPresent = true; + bool bInMainThread = ThreadInMainThread(); + if ( !bInMainThread ) + { + // don't present if the device is in an invalid state and in queued mode + if ( m_DeviceState != DEVICE_STATE_OK ) + { + bValidPresent = false; + } + // check for lost device early in threaded mode + CheckDeviceLost( m_bOtherAppInitializing ); + if ( m_DeviceState != DEVICE_STATE_OK ) + { + bValidPresent = false; + } + } + // Copy the back buffer into the non-interactive temp buffer + if ( m_NonInteractiveRefresh.m_Mode == MATERIAL_NON_INTERACTIVE_MODE_LEVEL_LOAD ) + { + g_pShaderAPI->CopyRenderTargetToTextureEx( m_NonInteractiveRefresh.m_Info.m_hTempFullscreenTexture, 0, NULL, NULL ); + } + + // If we're not iconified, try to present (without this check, we can flicker when Alt-Tabbed away) +#ifdef _WIN32 + if ( IsX360() || (IsIconic( ( HWND )m_hWnd ) == 0 && bValidPresent) ) +#else + if ( IsX360() || (IsIconic( (VD3DHWND)m_hWnd ) == 0 && bValidPresent) ) +#endif + { + if ( IsPC() && ( m_IsResizing || ( m_ViewHWnd != (VD3DHWND)m_hWnd ) ) ) + { + RECT destRect; + #ifndef DX_TO_GL_ABSTRACTION + GetClientRect( ( HWND )m_ViewHWnd, &destRect ); + #else + toglGetClientRect( (VD3DHWND)m_ViewHWnd, &destRect ); + #endif + + ShaderViewport_t viewport; + g_pShaderAPI->GetViewports( &viewport, 1 ); + + RECT srcRect; + srcRect.left = viewport.m_nTopLeftX; + srcRect.right = viewport.m_nTopLeftX + viewport.m_nWidth; + srcRect.top = viewport.m_nTopLeftY; + srcRect.bottom = viewport.m_nTopLeftY + viewport.m_nHeight; + + hr = Dx9Device()->Present( &srcRect, &destRect, (VD3DHWND)m_ViewHWnd, 0 ); + } + else + { + g_pShaderAPI->OwnGPUResources( false ); + hr = Dx9Device()->Present( 0, 0, 0, 0 ); + } + } + + UpdatePresentStats(); + + if ( IsWindows() ) + { + if ( hr == D3DERR_DRIVERINTERNALERROR ) + { + /* Usually this bug means that the driver has run out of internal video + memory, due to leaking it slowly over several application restarts. + As of summer 2007, IE in particular seemed to leak a lot of driver + memory for every image context it created in the browser window. A + reboot clears out the leaked memory and will generally allow the game + to be run again; occasionally (but not frequently) it's necessary to + reduce video settings in the game as well to run. But, this is too + fine a distinction to explain in a dialog, so place the guilt on the + user and ask them to reduce video settings regardless. + */ + + Error( "Internal driver error at Present.\n" + "You're likely out of OS Paged Pool Memory! For more info, see\n" + "http://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=150\n" ); + } + if ( hr == D3DERR_DEVICELOST ) + { + MarkDeviceLost(); + } + } + + MeshMgr()->DiscardVertexBuffers(); + + if ( bInMainThread ) + { + CheckDeviceLost( m_bOtherAppInitializing ); + } + + if ( IsX360() ) + { + // according to docs - "Mandatory Reset of GPU Registers" + // 360 must force the cached state to be dirty after any present() + g_pShaderAPI->ResetRenderState( false ); + } + +#ifdef RECORD_KEYFRAMES + static int frame = 0; + ++frame; + if (frame == KEYFRAME_INTERVAL) + { + RECORD_COMMAND( DX8_KEYFRAME, 0 ); + + g_pShaderAPI->ResetRenderState(); + frame = 0; + } +#endif + + g_pShaderAPI->AdvancePIXFrame(); + + if ( !IsDeactivated() ) + { +#ifndef DX_TO_GL_ABSTRACTION + if ( ( ShaderUtil()->GetConfig().bMeasureFillRate || ShaderUtil()->GetConfig().bVisualizeFillRate ) ) + { + g_pShaderAPI->ClearBuffers( true, true, true, -1, -1 ); + } +#endif + + Dx9Device()->BeginScene(); + } +} + + +// We need to scale our colors to the range [16, 235] to keep our colors within TV standards. Some colors might +// still be out of gamut if any of the R, G, or B channels are more than 191 units apart from each other in +// the 0-255 scale, but it looks like the 360 deals with this for us by lowering the bright saturated color components. +// NOTE: I'm leaving the max at 255 to retain whiter than whites. On most TV's, we seems a little dark in the bright colors +// compared to TV and movies when played in the same conditions. This keeps out brights on par with what customers are +// used to seeing. +// TV's generally have a 2.5 gamma, so we need to convert our 2.2 frame buffer into a 2.5 frame buffer for display on a TV + +void CShaderDeviceDx8::SetHardwareGammaRamp( float fGamma, float fGammaTVRangeMin, float fGammaTVRangeMax, float fGammaTVExponent, bool bTVEnabled ) +{ + DevMsg( 2, "SetHardwareGammaRamp( %f )\n", fGamma ); + + Assert( Dx9Device() ); + if( !Dx9Device() ) + return; + + D3DGAMMARAMP gammaRamp; + for ( int i = 0; i < 256; i++ ) + { + float flInputValue = float( i ) / 255.0f; + + // Since the 360's sRGB read/write is a piecewise linear approximation, we need to correct for the difference in gamma space here + float flSrgbGammaValue; + if ( IsX360() ) // Should we also do this for the PS3? + { + // First undo the 360 broken sRGB curve by bringing the value back into linear space + float flLinearValue = X360GammaToLinear( flInputValue ); + flLinearValue = clamp( flLinearValue, 0.0f, 1.0f ); + + // Now apply a true sRGB curve to mimic PC hardware + flSrgbGammaValue = SrgbLinearToGamma( flLinearValue ); // ( flLinearValue <= 0.0031308f ) ? ( flLinearValue * 12.92f ) : ( 1.055f * powf( flLinearValue, ( 1.0f / 2.4f ) ) ) - 0.055f; + flSrgbGammaValue = clamp( flSrgbGammaValue, 0.0f, 1.0f ); + } + else + { + flSrgbGammaValue = flInputValue; + } + + // Apply the user controlled exponent curve + float flCorrection = pow( flSrgbGammaValue, ( fGamma / 2.2f ) ); + flCorrection = clamp( flCorrection, 0.0f, 1.0f ); + + // TV adjustment - Apply an exp and a scale and bias + if ( bTVEnabled ) + { + // Adjust for TV gamma of 2.5 by applying an exponent of 2.2 / 2.5 = 0.88 + flCorrection = pow( flCorrection, 2.2f / fGammaTVExponent ); + flCorrection = clamp( flCorrection, 0.0f, 1.0f ); + + // Scale and bias to fit into the 16-235 range for TV's + flCorrection = ( flCorrection * ( fGammaTVRangeMax - fGammaTVRangeMin ) / 255.0f ) + ( fGammaTVRangeMin / 255.0f ); + flCorrection = clamp( flCorrection, 0.0f, 1.0f ); + } + + // Generate final int value + unsigned int val = ( int )( flCorrection * 65535.0f ); + gammaRamp.red[i] = val; + gammaRamp.green[i] = val; + gammaRamp.blue[i] = val; + } + + Dx9Device()->SetGammaRamp( 0, D3DSGR_NO_CALIBRATION, &gammaRamp ); +} + + +//----------------------------------------------------------------------------- +// Shader compilation +//----------------------------------------------------------------------------- +IShaderBuffer* CShaderDeviceDx8::CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ) +{ + return ShaderManager()->CompileShader( pProgram, nBufLen, pShaderVersion ); +} + +VertexShaderHandle_t CShaderDeviceDx8::CreateVertexShader( IShaderBuffer *pBuffer ) +{ + return ShaderManager()->CreateVertexShader( pBuffer ); +} + +void CShaderDeviceDx8::DestroyVertexShader( VertexShaderHandle_t hShader ) +{ + ShaderManager()->DestroyVertexShader( hShader ); +} + +GeometryShaderHandle_t CShaderDeviceDx8::CreateGeometryShader( IShaderBuffer* pShaderBuffer ) +{ + Assert( 0 ); + return GEOMETRY_SHADER_HANDLE_INVALID; +} + +void CShaderDeviceDx8::DestroyGeometryShader( GeometryShaderHandle_t hShader ) +{ + Assert( hShader == GEOMETRY_SHADER_HANDLE_INVALID ); +} + +PixelShaderHandle_t CShaderDeviceDx8::CreatePixelShader( IShaderBuffer *pBuffer ) +{ + return ShaderManager()->CreatePixelShader( pBuffer ); +} + +void CShaderDeviceDx8::DestroyPixelShader( PixelShaderHandle_t hShader ) +{ + ShaderManager()->DestroyPixelShader( hShader ); +} + +#ifdef DX_TO_GL_ABSTRACTION +void CShaderDeviceDx8::DoStartupShaderPreloading( void ) +{ + ShaderManager()->DoStartupShaderPreloading(); +} +#endif + + +//----------------------------------------------------------------------------- +// Creates/destroys Mesh +// NOTE: Will be deprecated soon! +//----------------------------------------------------------------------------- +IMesh* CShaderDeviceDx8::CreateStaticMesh( VertexFormat_t vertexFormat, const char *pTextureBudgetGroup, IMaterial * pMaterial ) +{ + LOCK_SHADERAPI(); + return MeshMgr()->CreateStaticMesh( vertexFormat, pTextureBudgetGroup, pMaterial ); +} + +void CShaderDeviceDx8::DestroyStaticMesh( IMesh* pMesh ) +{ + LOCK_SHADERAPI(); + MeshMgr()->DestroyStaticMesh( pMesh ); +} + + +//----------------------------------------------------------------------------- +// Creates/destroys vertex buffers + index buffers +//----------------------------------------------------------------------------- +IVertexBuffer *CShaderDeviceDx8::CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroup ) +{ + LOCK_SHADERAPI(); + return MeshMgr()->CreateVertexBuffer( type, fmt, nVertexCount, pBudgetGroup ); +} + +void CShaderDeviceDx8::DestroyVertexBuffer( IVertexBuffer *pVertexBuffer ) +{ + LOCK_SHADERAPI(); + MeshMgr()->DestroyVertexBuffer( pVertexBuffer ); +} + +IIndexBuffer *CShaderDeviceDx8::CreateIndexBuffer( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroup ) +{ + LOCK_SHADERAPI(); + return MeshMgr()->CreateIndexBuffer( bufferType, fmt, nIndexCount, pBudgetGroup ); +} + +void CShaderDeviceDx8::DestroyIndexBuffer( IIndexBuffer *pIndexBuffer ) +{ + LOCK_SHADERAPI(); + MeshMgr()->DestroyIndexBuffer( pIndexBuffer ); +} + +IVertexBuffer *CShaderDeviceDx8::GetDynamicVertexBuffer( int streamID, VertexFormat_t vertexFormat, bool bBuffered ) +{ + LOCK_SHADERAPI(); + return MeshMgr()->GetDynamicVertexBuffer( streamID, vertexFormat, bBuffered ); +} + +IIndexBuffer *CShaderDeviceDx8::GetDynamicIndexBuffer( MaterialIndexFormat_t fmt, bool bBuffered ) +{ + LOCK_SHADERAPI(); + return MeshMgr()->GetDynamicIndexBuffer( fmt, bBuffered ); +} + +#ifdef _X360 +void CShaderDeviceDx8::SpewVideoInfo360( const CCommand &args ) +{ + XVIDEO_MODE videoMode; + XGetVideoMode( &videoMode ); + + Warning( "back buffer size: %dx%d\n", m_PresentParameters.BackBufferWidth, m_PresentParameters.BackBufferHeight ); + Warning( "display resolution: %dx%d %s\n", videoMode.dwDisplayWidth, videoMode.dwDisplayHeight, videoMode.fIsInterlaced ? "interlaced" : "progressive" ); + Warning( "refresh rate: %f\n", videoMode.RefreshRate ); + Warning( "aspect: %s\n", videoMode.fIsWideScreen ? "16x9 (widescreen)" : "4x3 (normal)" ); + Warning( "%s\n", videoMode.fIsHiDef ? "hidef" : "lodef" ); + switch( videoMode.VideoStandard ) + { + case XC_VIDEO_STANDARD_NTSC_M: + Warning( "video standard: NTSC_M\n" ); + break; + case XC_VIDEO_STANDARD_NTSC_J: + Warning( "video standard: NTSC_J\n" ); + break; + case XC_VIDEO_STANDARD_PAL_I: + Warning( "video standard: PAL_I\n" ); + break; + default: + Warning( "error: UNKNOWN VIDEO STANDARD!\n" ); + Assert( 0 ); + break; + } + ConVarRef fps_max( "fps_max" ); + Warning( "fps_max: %f\n", fps_max.GetFloat() ); + switch( m_PresentParameters.MultiSampleType ) + { + case D3DMULTISAMPLE_NONE: + Warning( "multisample type: D3DMULTISAMPLE_NONE\n" ); + break; + case D3DMULTISAMPLE_2_SAMPLES: + Warning( "multisample type: D3DMULTISAMPLE_2_SAMPLES\n" ); + break; + case D3DMULTISAMPLE_4_SAMPLES: + Warning( "multisample type: D3DMULTISAMPLE_4_SAMPLES\n" ); + break; + } +} +#endif |