source-engine-2018-hl2_src/materialsystem/shaderapidx9/shaderapidx8.cpp
FluorescentCIAAfricanAmerican 3bf9df6b27 1
2020-04-22 12:56:21 -04:00

14355 lines
444 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//
// The dx8 implementation of the shader API
//===========================================================================//
/*
DX9 todo:
-make the transforms in the older shaders match the transforms in lightmappedgeneric
-fix polyoffset for hardware that doesn't support D3DRS_SLOPESCALEDEPTHBIAS and D3DRS_DEPTHBIAS
- code is there, I think matrix offset just needs tweaking
-fix forcehardwaresync - implement texture locking for hardware that doesn't support async query
-get the format for GetAdapterModeCount and EnumAdapterModes from somewhere (shaderapidx8.cpp, GetModeCount, GetModeInfo)
-record frame sync objects (allocframesyncobjects, free framesync objects, ForceHardwareSync)
-Need to fix ENVMAPMASKSCALE, BUMPOFFSET in lightmappedgeneric*.cpp and vertexlitgeneric*.cpp
fix this:
// FIXME: This also depends on the vertex format and whether or not we are static lit in dx9
#ifndef SHADERAPIDX9
if (m_DynamicState.m_VertexShader != shader) // garymcthack
#endif // !SHADERAPIDX9
unrelated to dx9:
mat_fullbright 1 doesn't work properly on alpha materials in testroom_standards
*/
#define DISABLE_PROTECTED_THINGS
#include "shaderapidx8.h"
#include "shaderapidx8_global.h"
#include "shadershadowdx8.h"
#include "locald3dtypes.h"
#include "utlvector.h"
#include "IHardwareConfigInternal.h"
#include "utlstack.h"
#include "shaderapi/ishaderutil.h"
#include "shaderapi/commandbuffer.h"
#include "shaderapidx8_global.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/itexture.h"
#include "imaterialinternal.h"
#include "imeshdx8.h"
#include "materialsystem/imorph.h"
#include "colorformatdx8.h"
#include "texturedx8.h"
#include "textureheap.h"
#include <malloc.h>
#include "interface.h"
#include "utlrbtree.h"
#include "utlsymbol.h"
#include "tier1/strtools.h"
#include "recording.h"
#ifndef _X360
#include <crtmemdebug.h>
#endif
#include "vertexshaderdx8.h"
#include "filesystem.h"
#include "mathlib/mathlib.h"
#include "materialsystem/materialsystem_config.h"
#include "worldsize.h"
#include "TransitionTable.h"
#include "tier0/vcrmode.h"
#include "tier0/vprof.h"
#include "tier1/tier1.h"
#include "tier1/utlbuffer.h"
#include "vertexdecl.h"
#include "tier0/icommandline.h"
#include "IShaderSystem.h"
#include "tier1/convar.h"
#include "tier1/KeyValues.h"
#include "Color.h"
#ifdef RECORDING
#include "materialsystem/IShader.h"
#endif
#include "../stdshaders/common_hlsl_cpp_consts.h" // hack hack hack!
#include "KeyValues.h"
#include "bitmap/imageformat.h"
#include "materialsystem/idebugtextureinfo.h"
#include "tier1/utllinkedlist.h"
#include "vtf/vtf.h"
#include "datacache/idatacache.h"
#include "renderparm.h"
#include "tier2/tier2.h"
#include "materialsystem/deformations.h"
#include "bitmap/tgawriter.h"
#include "tier0/icommandline.h"
#include "togl/rendermechanism.h" // provides GLMPRINTF/GLMPRINTSTR / GLMPRINTEXT macros which only activate if GLMDEBUG is nonzero and POSIX is defined.
#if defined( _X360 )
#include "xbox/xbox_console.h"
#include "xbox/xbox_win32stubs.h"
#include "xbox/xbox_launch.h"
#endif
#include "tier0/tslist.h"
#ifndef _X360
#include "wmi.h"
#endif
#include "filesystem/IQueuedLoader.h"
#include "shaderdevicedx8.h"
#include "togl/rendermechanism.h"
// Define this if you want to use a stubbed d3d.
//#define STUBD3D
#ifdef STUBD3D
#include "stubd3ddevice.h"
#endif
#include "winutils.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#if defined( OSX )
typedef unsigned int DWORD;
typedef DWORD* LPDWORD;
#endif
#ifdef _WIN32
#pragma warning (disable:4189)
#endif
ConVar mat_texture_limit( "mat_texture_limit", "-1", FCVAR_NEVER_AS_STRING,
"If this value is not -1, the material system will limit the amount of texture memory it uses in a frame."
" Useful for identifying performance cliffs. The value is in kilobytes." );
ConVar mat_frame_sync_enable( "mat_frame_sync_enable", "1", FCVAR_CHEAT );
ConVar mat_frame_sync_force_texture( "mat_frame_sync_force_texture", "0", FCVAR_CHEAT, "Force frame syncing to lock a managed texture." );
#if defined( _X360 )
ConVar mat_texturecachesize( "mat_texturecachesize", "176" );
ConVar mat_force_flush_texturecache( "mat_force_flush_texturecache", "0" );
#endif
extern ConVar mat_debugalttab;
#define ALLOW_SMP_ACCESS 0
#if ALLOW_SMP_ACCESS
static ConVar mat_use_smp( "mat_use_smp", "0" );
#endif
// Convars for driving PIX (not all hooked up yet...JasonM)
static ConVar r_pix_start( "r_pix_start", "0" );
static ConVar r_pix_recordframes( "r_pix_recordframes", "0" );
#define D3DDeviceWrapper IDirect3DDevice9
//-----------------------------------------------------------------------------
// Some important enumerations
//-----------------------------------------------------------------------------
enum
{
MAX_VERTEX_TEXTURE_COUNT = 4,
};
//-----------------------------------------------------------------------------
// These board states change with high frequency; are not shadowed
//-----------------------------------------------------------------------------
struct TextureStageState_t
{
D3DTEXTURETRANSFORMFLAGS m_TextureTransformFlags;
float m_BumpEnvMat00;
float m_BumpEnvMat01;
float m_BumpEnvMat10;
float m_BumpEnvMat11;
};
struct SamplerState_t
{
ShaderAPITextureHandle_t m_BoundTexture;
D3DTEXTUREADDRESS m_UTexWrap;
D3DTEXTUREADDRESS m_VTexWrap;
D3DTEXTUREADDRESS m_WTexWrap;
D3DTEXTUREFILTERTYPE m_MagFilter;
D3DTEXTUREFILTERTYPE m_MinFilter;
D3DTEXTUREFILTERTYPE m_MipFilter;
int m_FinestMipmapLevel;
float m_LodBias;
int m_nAnisotropicLevel;
bool m_TextureEnable;
bool m_SRGBReadEnable;
};
//-----------------------------------------------------------------------------
// State related to vertex textures
//-----------------------------------------------------------------------------
struct VertexTextureState_t
{
ShaderAPITextureHandle_t m_BoundTexture;
D3DTEXTUREADDRESS m_UTexWrap;
D3DTEXTUREADDRESS m_VTexWrap;
D3DTEXTUREFILTERTYPE m_MagFilter;
D3DTEXTUREFILTERTYPE m_MinFilter;
D3DTEXTUREFILTERTYPE m_MipFilter;
};
enum TransformType_t
{
TRANSFORM_IS_IDENTITY = 0,
TRANSFORM_IS_CAMERA_TO_WORLD,
TRANSFORM_IS_GENERAL,
};
enum TransformDirtyBits_t
{
STATE_CHANGED_VERTEX_SHADER = 0x1,
STATE_CHANGED_FIXED_FUNCTION = 0x2,
STATE_CHANGED = 0x3
};
enum
{
#if !defined( _X360 )
MAX_NUM_RENDERSTATES = ( D3DRS_BLENDOPALPHA+1 ),
#else
MAX_NUM_RENDERSTATES = D3DRS_MAX,
#endif
// MORPH_TARGET_FACTOR_COUNT = VERTEX_SHADER_MORPH_TARGET_FACTOR_COUNT * 4,
};
struct DynamicState_t
{
// Constant color
unsigned int m_ConstantColor;
// Normalize normals?
bool m_NormalizeNormals;
// Viewport state
D3DVIEWPORT9 m_Viewport;
// Transform state
D3DXMATRIX m_Transform[NUM_MATRIX_MODES];
unsigned char m_TransformType[NUM_MATRIX_MODES];
unsigned char m_TransformChanged[NUM_MATRIX_MODES];
// Ambient light color
D3DCOLOR m_Ambient;
D3DLIGHT m_Lights[MAX_NUM_LIGHTS];
LightDesc_t m_LightDescs[MAX_NUM_LIGHTS];
bool m_LightEnable[MAX_NUM_LIGHTS];
Vector4D m_AmbientLightCube[6];
unsigned char m_LightChanged[MAX_NUM_LIGHTS];
unsigned char m_LightEnableChanged[MAX_NUM_LIGHTS];
VertexShaderLightTypes_t m_LightType[MAX_NUM_LIGHTS];
Vector m_vLightingOrigin;
int m_NumLights;
// Shade mode
D3DSHADEMODE m_ShadeMode;
// Clear color
D3DCOLOR m_ClearColor;
// Fog
D3DCOLOR m_FogColor;
float m_PixelFogColor[4];
bool m_bFogGammaCorrectionDisabled;
bool m_FogEnable;
MaterialFogMode_t m_SceneFog;
D3DFOGMODE m_FogMode;
float m_FogStart;
float m_FogEnd;
float m_FogZ;
float m_FogMaxDensity;
float m_HeightClipZ;
MaterialHeightClipMode_t m_HeightClipMode;
// user clip planes
int m_UserClipPlaneEnabled;
int m_UserClipPlaneChanged;
D3DXPLANE m_UserClipPlaneWorld[MAXUSERCLIPPLANES];
D3DXPLANE m_UserClipPlaneProj[MAXUSERCLIPPLANES];
bool m_UserClipLastUpdatedUsingFixedFunction;
bool m_FastClipEnabled;
bool m_bFastClipPlaneChanged;
D3DXPLANE m_FastClipPlane;
// Used when overriding the user clip plane
bool m_bUserClipTransformOverride;
D3DXMATRIX m_UserClipTransform;
// Cull mode
D3DCULL m_DesiredCullMode;
D3DCULL m_CullMode;
bool m_bCullEnabled;
// Skinning
D3DVERTEXBLENDFLAGS m_VertexBlend;
int m_NumBones;
// Pixel and vertex shader constants...
Vector4D* m_pVectorVertexShaderConstant;
BOOL* m_pBooleanVertexShaderConstant;
IntVector4D* m_pIntegerVertexShaderConstant;
Vector4D* m_pVectorPixelShaderConstant;
BOOL* m_pBooleanPixelShaderConstant;
IntVector4D* m_pIntegerPixelShaderConstant;
// Texture stage state
TextureStageState_t m_TextureStage[MAX_TEXTURE_STAGES];
SamplerState_t m_SamplerState[MAX_SAMPLERS];
// Vertex texture stage state
VertexTextureState_t m_VertexTextureState[MAX_VERTEX_TEXTURE_COUNT];
DWORD m_RenderState[MAX_NUM_RENDERSTATES];
RECT m_ScissorRect;
IDirect3DVertexDeclaration9 *m_pVertexDecl;
bool m_bSRGBWritesEnabled;
bool m_bHWMorphingEnabled;
float m_DestAlphaDepthRange; //Dest alpha writes compress the depth to get better results. This holds the default setting that can be overriden with r_destalpharange
#if defined( _X360 )
int m_iVertexShaderGPRAllocation; //only need to track vertex shader
bool m_bBuffer2Frames;
#endif
DynamicState_t() {}
private:
DynamicState_t( DynamicState_t const& );
};
//-----------------------------------------------------------------------------
// Method to queue up dirty dynamic state change calls
//-----------------------------------------------------------------------------
typedef void (*StateCommitFunc_t)( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, DynamicState_t &currentState, bool bForce );
static void CommitSetViewports( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, DynamicState_t &currentState, bool bForce );
// NOTE: It's slightly memory inefficient, and definitely not typesafe,
// to put all commit funcs into the same table (vs, ff, per-draw, per-pass),
// but it makes the code a heck of a lot simpler and smaller.
enum CommitFunc_t
{
COMMIT_FUNC_CommitVertexTextures = 0,
COMMIT_FUNC_CommitFlexWeights,
COMMIT_FUNC_CommitSetScissorRect,
COMMIT_FUNC_CommitSetViewports,
#if defined( _X360 )
COMMIT_FUNC_CommitShaderGPRs,
#endif
COMMIT_FUNC_COUNT,
COMMIT_FUNC_BYTE_COUNT = ( COMMIT_FUNC_COUNT + 0x7 ) >> 3,
};
enum CommitFuncType_t
{
COMMIT_PER_DRAW = 0,
COMMIT_PER_PASS,
COMMIT_FUNC_TYPE_COUNT,
};
enum CommitShaderType_t
{
COMMIT_FIXED_FUNCTION = 0,
COMMIT_VERTEX_SHADER,
COMMIT_ALWAYS,
COMMIT_SHADER_TYPE_COUNT,
};
#define ADD_COMMIT_FUNC( _func, _shader, _func_name ) \
if ( !IsCommitFuncInUse( _func, _shader, COMMIT_FUNC_ ## _func_name ) ) \
{ \
AddCommitFunc( _func, _shader, _func_name ); \
MarkCommitFuncInUse( _func, _shader, COMMIT_FUNC_ ## _func_name ); \
}
#define ADD_RENDERSTATE_FUNC( _func, _shader, _func_name, _state, _val ) \
if ( m_bResettingRenderState || (m_DesiredState._state != _val) ) \
{ \
m_DesiredState._state = _val; \
ADD_COMMIT_FUNC( _func, _shader, _func_name ) \
}
#define ADD_VERTEX_TEXTURE_FUNC( _func, _shader, _func_name, _stage, _state, _val ) \
Assert( ( int )_stage < MAX_VERTEX_TEXTURE_COUNT ); \
if ( m_bResettingRenderState || (m_DesiredState.m_VertexTextureState[_stage]._state != _val) ) \
{ \
m_DesiredState.m_VertexTextureState[_stage]._state = _val; \
ADD_COMMIT_FUNC( _func, _shader, _func_name ) \
}
//-----------------------------------------------------------------------------
// Check render state support at compile time instead of runtime
//-----------------------------------------------------------------------------
#define SetSupportedRenderState( _state, _val ) \
{ \
if( _state != D3DRS_NOTSUPPORTED ) \
{ \
SetRenderState( _state, _val, false ); \
} \
}
#define SetSupportedRenderStateForce( _state, _val ) \
{ \
if( _state != D3DRS_NOTSUPPORTED ) \
{ \
SetRenderStateForce( _state, _val ); \
} \
}
//-----------------------------------------------------------------------------
// Allocated textures
//-----------------------------------------------------------------------------
struct Texture_t
{
Texture_t()
{
m_Flags = 0;
m_Count = 1;
m_CountIndex = 0;
m_nTimesBoundMax = 0;
m_nTimesBoundThisFrame = 0;
m_pTexture = NULL;
m_ppTexture = NULL;
m_ImageFormat = IMAGE_FORMAT_RGBA8888;
m_pTextureGroupCounterGlobal = NULL;
m_pTextureGroupCounterFrame = NULL;
m_FinestMipmapLevel = 0;
m_LodBias = 0.0f;
}
// FIXME: Compress this info
D3DTEXTUREADDRESS m_UTexWrap;
D3DTEXTUREADDRESS m_VTexWrap;
D3DTEXTUREADDRESS m_WTexWrap;
D3DTEXTUREFILTERTYPE m_MagFilter;
D3DTEXTUREFILTERTYPE m_MinFilter;
D3DTEXTUREFILTERTYPE m_MipFilter;
int m_FinestMipmapLevel;
float m_LodBias;
unsigned char m_NumLevels;
unsigned char m_SwitchNeeded; // Do we need to advance the current copy?
unsigned char m_NumCopies; // copies are used to optimize procedural textures
unsigned char m_CurrentCopy; // the current copy we're using...
int m_CreationFlags;
CUtlSymbol m_DebugName;
CUtlSymbol m_TextureGroupName;
int *m_pTextureGroupCounterGlobal; // Global counter for this texture's group.
int *m_pTextureGroupCounterFrame; // Per-frame global counter for this texture's group.
// stats stuff
int m_SizeBytes;
int m_SizeTexels;
int m_LastBoundFrame;
int m_nTimesBoundMax;
int m_nTimesBoundThisFrame;
enum Flags_t
{
IS_ALLOCATED = 0x0001,
IS_DEPTH_STENCIL = 0x0002,
IS_DEPTH_STENCIL_TEXTURE = 0x0004, // depth stencil texture, not surface
IS_RENDERABLE = ( IS_DEPTH_STENCIL | IS_ALLOCATED ),
IS_LOCKABLE = 0x0008,
IS_FINALIZED = 0x0010, // 360: completed async hi-res load
IS_FAILED = 0x0020, // 360: failed during load
CAN_CONVERT_FORMAT = 0x0040, // 360: allow format conversion
IS_LINEAR = 0x0080, // 360: unswizzled linear format
IS_RENDER_TARGET = 0x0100, // 360: marks a render target texture source
IS_RENDER_TARGET_SURFACE = 0x0200, // 360: marks a render target surface target
IS_VERTEX_TEXTURE = 0x0800,
};
short m_Width;
short m_Height;
short m_Depth;
unsigned short m_Flags;
typedef IDirect3DBaseTexture *IDirect3DBaseTexturePtr;
typedef IDirect3DBaseTexture **IDirect3DBaseTexturePtrPtr;
typedef IDirect3DSurface *IDirect3DSurfacePtr;
IDirect3DBaseTexturePtr GetTexture( void )
{
Assert( m_NumCopies == 1 );
Assert( !( m_Flags & IS_DEPTH_STENCIL ) );
return m_pTexture;
}
IDirect3DBaseTexturePtr GetTexture( int copy )
{
Assert( m_NumCopies > 1 );
Assert( !( m_Flags & IS_DEPTH_STENCIL ) );
return m_ppTexture[copy];
}
IDirect3DBaseTexturePtrPtr &GetTextureArray( void )
{
Assert( m_NumCopies > 1 );
Assert( !( m_Flags & IS_DEPTH_STENCIL ) );
return m_ppTexture;
}
IDirect3DSurfacePtr &GetDepthStencilSurface( void )
{
Assert( m_NumCopies == 1 );
Assert( (m_Flags & IS_DEPTH_STENCIL) );
return m_pDepthStencilSurface;
}
IDirect3DSurfacePtr &GetRenderTargetSurface( bool bSRGB )
{
Assert( m_NumCopies == 1 );
Assert( m_Flags & IS_RENDER_TARGET_SURFACE );
return m_pRenderTargetSurface[bSRGB];
}
void SetTexture( IDirect3DBaseTexturePtr pPtr )
{
m_pTexture = pPtr;
}
void SetTexture( int copy, IDirect3DBaseTexturePtr pPtr )
{
m_ppTexture[copy] = pPtr;
}
int GetMemUsage() const
{
return m_SizeBytes;
}
int GetWidth() const
{
return ( int )m_Width;
}
int GetHeight() const
{
return ( int )m_Height;
}
int GetDepth() const
{
return ( int )m_Depth;
}
int GetLodClamp() const
{
return m_FinestMipmapLevel;
}
void SetImageFormat( ImageFormat format )
{
m_ImageFormat = format;
}
ImageFormat GetImageFormat() const
{
return m_ImageFormat;
}
private:
union
{
IDirect3DBaseTexture *m_pTexture; // used when there's one copy
IDirect3DBaseTexture **m_ppTexture; // used when there are more than one copies
IDirect3DSurface *m_pDepthStencilSurface; // used when there's one depth stencil surface
IDirect3DSurface *m_pRenderTargetSurface[2];
};
ImageFormat m_ImageFormat;
public:
short m_Count;
short m_CountIndex;
short GetCount() const
{
return m_Count;
}
};
#define MAX_DEFORMATION_PARAMETERS 16
#define DEFORMATION_STACK_DEPTH 10
struct Deformation_t
{
int m_nDeformationType;
int m_nNumParameters;
float m_flDeformationParameters[MAX_DEFORMATION_PARAMETERS];
};
//-----------------------------------------------------------------------------
// The DX8 implementation of the shader API
//-----------------------------------------------------------------------------
class CShaderAPIDx8 : public CShaderDeviceDx8, public IShaderAPIDX8, public IDebugTextureInfo
{
typedef CShaderDeviceDx8 BaseClass;
public:
// constructor, destructor
CShaderAPIDx8( );
virtual ~CShaderAPIDx8();
// Methods of IShaderAPI
public:
virtual void SetViewports( int nCount, const ShaderViewport_t* pViewports );
virtual int GetViewports( ShaderViewport_t* pViewports, int nMax ) const;
virtual void ClearBuffers( bool bClearColor, bool bClearDepth, bool bClearStencil, int renderTargetWidth, int renderTargetHeight );
virtual void ClearColor3ub( unsigned char r, unsigned char g, unsigned char b );
virtual void ClearColor4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a );
virtual void BindVertexShader( VertexShaderHandle_t hVertexShader );
virtual void BindGeometryShader( GeometryShaderHandle_t hGeometryShader );
virtual void BindPixelShader( PixelShaderHandle_t hPixelShader );
virtual void SetRasterState( const ShaderRasterState_t& state );
virtual void SetFlexWeights( int nFirstWeight, int nCount, const MorphWeight_t* pWeights );
// Methods of IShaderDynamicAPI
public:
virtual void GetBackBufferDimensions( int &nWidth, int &nHeight ) const
{
// Chain to the device
BaseClass::GetBackBufferDimensions( nWidth, nHeight );
}
virtual void MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords );
public:
// Methods of CShaderAPIBase
virtual bool OnDeviceInit();
virtual void OnDeviceShutdown();
virtual void ReleaseShaderObjects();
virtual void RestoreShaderObjects();
virtual void BeginPIXEvent( unsigned long color, const char *szName );
virtual void EndPIXEvent();
virtual void AdvancePIXFrame();
public:
// Methods of IShaderAPIDX8
virtual void QueueResetRenderState();
//
// Abandon all hope ye who pass below this line which hasn't been ported.
//
// Sets the mode...
bool SetMode( void* VD3DHWND, int nAdapter, const ShaderDeviceInfo_t &info );
// Change the video mode after it's already been set.
void ChangeVideoMode( const ShaderDeviceInfo_t &info );
// Sets the default render state
void SetDefaultState();
// Methods to ask about particular state snapshots
virtual bool IsTranslucent( StateSnapshot_t id ) const;
virtual bool IsAlphaTested( StateSnapshot_t id ) const;
virtual bool UsesVertexAndPixelShaders( StateSnapshot_t id ) const;
virtual int CompareSnapshots( StateSnapshot_t snapshot0, StateSnapshot_t snapshot1 );
// Computes the vertex format for a particular set of snapshot ids
VertexFormat_t ComputeVertexFormat( int num, StateSnapshot_t* pIds ) const;
VertexFormat_t ComputeVertexUsage( int num, StateSnapshot_t* pIds ) const;
// What fields in the morph do we actually use?
virtual MorphFormat_t ComputeMorphFormat( int numSnapshots, StateSnapshot_t* pIds ) const;
// Uses a state snapshot
void UseSnapshot( StateSnapshot_t snapshot );
// Color state
void Color3f( float r, float g, float b );
void Color4f( float r, float g, float b, float a );
void Color3fv( float const* c );
void Color4fv( float const* c );
void Color3ub( unsigned char r, unsigned char g, unsigned char b );
void Color3ubv( unsigned char const* pColor );
void Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a );
void Color4ubv( unsigned char const* pColor );
// Set the number of bone weights
virtual void SetNumBoneWeights( int numBones );
virtual void EnableHWMorphing( bool bEnable );
// Sets the vertex and pixel shaders
virtual void SetVertexShaderIndex( int vshIndex = -1 );
virtual void SetPixelShaderIndex( int pshIndex = 0 );
// Matrix state
void MatrixMode( MaterialMatrixMode_t matrixMode );
void PushMatrix();
void PopMatrix();
void LoadMatrix( float *m );
void LoadBoneMatrix( int boneIndex, const float *m );
void MultMatrix( float *m );
void MultMatrixLocal( float *m );
void GetMatrix( MaterialMatrixMode_t matrixMode, float *dst );
void LoadIdentity( void );
void LoadCameraToWorld( void );
void Ortho( double left, double top, double right, double bottom, double zNear, double zFar );
void PerspectiveX( double fovx, double aspect, double zNear, double zFar );
void PerspectiveOffCenterX( double fovx, double aspect, double zNear, double zFar, double bottom, double top, double left, double right );
void PickMatrix( int x, int y, int width, int height );
void Rotate( float angle, float x, float y, float z );
void Translate( float x, float y, float z );
void Scale( float x, float y, float z );
void ScaleXY( float x, float y );
// Binds a particular material to render with
void Bind( IMaterial* pMaterial );
IMaterialInternal* GetBoundMaterial();
// Level of anisotropic filtering
virtual void SetAnisotropicLevel( int nAnisotropyLevel );
virtual void SyncToken( const char *pToken );
// Cull mode
void CullMode( MaterialCullMode_t cullMode );
// Force writes only when z matches. . . useful for stenciling things out
// by rendering the desired Z values ahead of time.
void ForceDepthFuncEquals( bool bEnable );
// Turns off Z buffering
void OverrideDepthEnable( bool bEnable, bool bDepthEnable );
void OverrideAlphaWriteEnable( bool bOverrideEnable, bool bAlphaWriteEnable );
void OverrideColorWriteEnable( bool bOverrideEnable, bool bColorWriteEnable );
void SetHeightClipZ( float z );
void SetHeightClipMode( enum MaterialHeightClipMode_t heightClipMode );
void SetClipPlane( int index, const float *pPlane );
void EnableClipPlane( int index, bool bEnable );
void SetFastClipPlane(const float *pPlane);
void EnableFastClip(bool bEnable);
// The shade mode
void ShadeMode( ShaderShadeMode_t mode );
// Vertex blend state
void SetVertexBlendState( int numBones );
// Gets the dynamic mesh
IMesh* GetDynamicMesh( IMaterial* pMaterial, int nHWSkinBoneCount, bool buffered,
IMesh* pVertexOverride, IMesh* pIndexOverride );
IMesh* GetDynamicMeshEx( IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount,
bool bBuffered, IMesh* pVertexOverride, IMesh* pIndexOverride );
IMesh *GetFlexMesh();
// Returns the number of vertices we can render using the dynamic mesh
virtual void GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices );
virtual int GetMaxVerticesToRender( IMaterial *pMaterial );
virtual int GetMaxIndicesToRender( );
// Draws the mesh
void DrawMesh( CMeshBase* mesh );
// modifies the vertex data when necessary
void ModifyVertexData( );
// Draws
void BeginPass( StateSnapshot_t snapshot );
void RenderPass( int nPass, int nPassCount );
// We use smaller dynamic VBs during level transitions, to free up memory
virtual int GetCurrentDynamicVBSize( void );
virtual void DestroyVertexBuffers( bool bExitingLevel = false );
void SetVertexDecl( VertexFormat_t vertexFormat, bool bHasColorMesh, bool bUsingFlex, bool bUsingMorph );
// Sets the constant register for vertex and pixel shaders
FORCEINLINE void SetVertexShaderConstantInternal( int var, float const* pVec, int numVecs = 1, bool bForce = false );
void SetVertexShaderConstant( int var, float const* pVec, int numVecs = 1, bool bForce = false );
void SetBooleanVertexShaderConstant( int var, BOOL const* pVec, int numBools = 1, bool bForce = false );
void SetIntegerVertexShaderConstant( int var, int const* pVec, int numIntVecs = 1, bool bForce = false );
void SetPixelShaderConstant( int var, float const* pVec, int numVecs = 1, bool bForce = false );
FORCEINLINE void SetPixelShaderConstantInternal( int var, float const* pValues, int nNumConsts, bool bForce );
void SetBooleanPixelShaderConstant( int var, BOOL const* pVec, int numBools = 1, bool bForce = false );
void SetIntegerPixelShaderConstant( int var, int const* pVec, int numIntVecs = 1, bool bForce = false );
void InvalidateDelayedShaderConstants( void );
// Returns the nearest supported format
ImageFormat GetNearestSupportedFormat( ImageFormat fmt, bool bFilteringRequired = true ) const;
ImageFormat GetNearestRenderTargetFormat( ImageFormat format ) const;
virtual bool DoRenderTargetsNeedSeparateDepthBuffer() const;
// stuff that shouldn't be used from within a shader
void ModifyTexture( ShaderAPITextureHandle_t textureHandle );
void BindTexture( Sampler_t sampler, ShaderAPITextureHandle_t textureHandle );
virtual void BindVertexTexture( VertexTextureSampler_t nStage, ShaderAPITextureHandle_t textureHandle );
void DeleteTexture( ShaderAPITextureHandle_t textureHandle );
void WriteTextureToFile( ShaderAPITextureHandle_t hTexture, const char *szFileName );
bool IsTexture( ShaderAPITextureHandle_t textureHandle );
bool IsTextureResident( ShaderAPITextureHandle_t textureHandle );
FORCEINLINE bool TextureIsAllocated( ShaderAPITextureHandle_t hTexture )
{
return m_Textures.IsValidIndex( hTexture ) && ( GetTexture( hTexture ).m_Flags & Texture_t::IS_ALLOCATED );
}
FORCEINLINE void AssertValidTextureHandle( ShaderAPITextureHandle_t textureHandle )
{
#ifdef _DEBUG
Assert( TextureIsAllocated( textureHandle ) );
#endif
}
// Lets the shader know about the full-screen texture so it can
virtual void SetFullScreenTextureHandle( ShaderAPITextureHandle_t h );
virtual void SetLinearToGammaConversionTextures( ShaderAPITextureHandle_t hSRGBWriteEnabledTexture, ShaderAPITextureHandle_t hIdentityTexture );
// Set the render target to a texID.
// Set to SHADER_RENDERTARGET_BACKBUFFER if you want to use the regular framebuffer.
void SetRenderTarget( ShaderAPITextureHandle_t colorTextureHandle = SHADER_RENDERTARGET_BACKBUFFER,
ShaderAPITextureHandle_t depthTextureHandle = SHADER_RENDERTARGET_DEPTHBUFFER );
// Set the render target to a texID.
// Set to SHADER_RENDERTARGET_BACKBUFFER if you want to use the regular framebuffer.
void SetRenderTargetEx( int nRenderTargetID, ShaderAPITextureHandle_t colorTextureHandle = SHADER_RENDERTARGET_BACKBUFFER,
ShaderAPITextureHandle_t depthTextureHandle = SHADER_RENDERTARGET_DEPTHBUFFER );
// These are bound to the texture, not the texture environment
void TexMinFilter( ShaderTexFilterMode_t texFilterMode );
void TexMagFilter( ShaderTexFilterMode_t texFilterMode );
void TexWrap( ShaderTexCoordComponent_t coord, ShaderTexWrapMode_t wrapMode );
void TexSetPriority( int priority );
void TexLodClamp( int finest );
void TexLodBias( float bias );
ShaderAPITextureHandle_t CreateTextureHandle( void );
void CreateTextureHandles( ShaderAPITextureHandle_t *handles, int count );
ShaderAPITextureHandle_t CreateTexture(
int width,
int height,
int depth,
ImageFormat dstImageFormat,
int numMipLevels,
int numCopies,
int creationFlags,
const char *pDebugName,
const char *pTextureGroupName );
// Create a multi-frame texture (equivalent to calling "CreateTexture" multiple times, but more efficient)
void CreateTextures(
ShaderAPITextureHandle_t *pHandles,
int count,
int width,
int height,
int depth,
ImageFormat dstImageFormat,
int numMipLevels,
int numCopies,
int flags,
const char *pDebugName,
const char *pTextureGroupName );
ShaderAPITextureHandle_t CreateDepthTexture(
ImageFormat renderTargetFormat,
int width,
int height,
const char *pDebugName,
bool bTexture );
void TexImage2D(
int level,
int cubeFaceID,
ImageFormat dstFormat,
int zOffset,
int width,
int height,
ImageFormat srcFormat,
bool bSrcIsTiled,
void *imageData );
void TexSubImage2D(
int level,
int cubeFaceID,
int xOffset,
int yOffset,
int zOffset,
int width,
int height,
ImageFormat srcFormat,
int srcStride,
bool bSrcIsTiled,
void *imageData );
void TexImageFromVTF( IVTFTexture *pVTF, int iVTFFrame );
bool TexLock( int level, int cubeFaceID, int xOffset, int yOffset, int width, int height, CPixelWriter& writer );
void TexUnlock( );
// stuff that isn't to be used from within a shader
// what's the best way to hide this? subclassing?
virtual void ClearBuffersObeyStencil( bool bClearColor, bool bClearDepth );
virtual void ClearBuffersObeyStencilEx( bool bClearColor, bool bClearAlpha, bool bClearDepth );
virtual void PerformFullScreenStencilOperation( void );
void ReadPixels( int x, int y, int width, int height, unsigned char *data, ImageFormat dstFormat );
virtual void ReadPixels( Rect_t *pSrcRect, Rect_t *pDstRect, unsigned char *data, ImageFormat dstFormat, int nDstStride );
// Gets the current buffered state... (debug only)
void GetBufferedState( BufferedState_t& state );
// Buffered primitives
void FlushBufferedPrimitives();
void FlushBufferedPrimitivesInternal( );
// Make sure we finish drawing everything that has been requested
void FlushHardware();
// Use this to begin and end the frame
void BeginFrame();
void EndFrame();
// Used to clear the transition table when we know it's become invalid.
void ClearSnapshots();
// Backward compat
virtual int GetActualTextureStageCount() const;
virtual int GetActualSamplerCount() const;
virtual int StencilBufferBits() const;
virtual bool IsAAEnabled() const; // Is antialiasing being used?
virtual bool OnAdapterSet( );
bool m_bAdapterSet;
void UpdateFastClipUserClipPlane( void );
bool ReadPixelsFromFrontBuffer() const;
// returns the current time in seconds....
double CurrentTime() const;
// Get the current camera position in world space.
void GetWorldSpaceCameraPosition( float* pPos ) const;
// Fog methods
void FogMode( MaterialFogMode_t fogMode );
void FogStart( float fStart );
void FogEnd( float fEnd );
void FogMaxDensity( float flMaxDensity );
void SetFogZ( float fogZ );
void GetFogDistances( float *fStart, float *fEnd, float *fFogZ );
void SceneFogMode( MaterialFogMode_t fogMode );
MaterialFogMode_t GetSceneFogMode( );
MaterialFogMode_t GetPixelFogMode( );
int GetPixelFogCombo( );//0 is either range fog, or no fog simulated with rigged range fog values. 1 is height fog
bool ShouldUsePixelFogForMode( MaterialFogMode_t fogMode );
void SceneFogColor3ub( unsigned char r, unsigned char g, unsigned char b );
void GetSceneFogColor( unsigned char *rgb );
void GetSceneFogColor( unsigned char *r, unsigned char *g, unsigned char *b );
// Selection mode methods
int SelectionMode( bool selectionMode );
void SelectionBuffer( unsigned int* pBuffer, int size );
void ClearSelectionNames( );
void LoadSelectionName( int name );
void PushSelectionName( int name );
void PopSelectionName();
bool IsInSelectionMode() const;
void RegisterSelectionHit( float minz, float maxz );
void WriteHitRecord();
// Binds a standard texture
virtual void BindStandardTexture( Sampler_t sampler, StandardTextureId_t id );
virtual void BindStandardVertexTexture( VertexTextureSampler_t sampler, StandardTextureId_t id );
virtual void GetStandardTextureDimensions( int *pWidth, int *pHeight, StandardTextureId_t id );
// Gets the lightmap dimensions
virtual void GetLightmapDimensions( int *w, int *h );
// Use this to get the mesh builder that allows us to modify vertex data
CMeshBuilder* GetVertexModifyBuilder();
virtual bool InFlashlightMode() const;
virtual bool InEditorMode() const;
// Gets the bound morph's vertex format; returns 0 if no morph is bound
virtual MorphFormat_t GetBoundMorphFormat();
// Helper to get at the texture state stage
TextureStageState_t& TextureStage( int stage ) { return m_DynamicState.m_TextureStage[stage]; }
const TextureStageState_t& TextureStage( int stage ) const { return m_DynamicState.m_TextureStage[stage]; }
SamplerState_t& SamplerState( int nSampler ) { return m_DynamicState.m_SamplerState[nSampler]; }
const SamplerState_t& SamplerState( int nSampler ) const { return m_DynamicState.m_SamplerState[nSampler]; }
void SetAmbientLight( float r, float g, float b );
void SetLight( int lightNum, const LightDesc_t& desc );
void SetLightingOrigin( Vector vLightingOrigin );
void DisableAllLocalLights();
void SetAmbientLightCube( Vector4D colors[6] );
float GetAmbientLightCubeLuminance( void );
int GetMaxLights( void ) const;
const LightDesc_t& GetLight( int lightNum ) const;
void SetVertexShaderStateAmbientLightCube();
void SetPixelShaderStateAmbientLightCube( int pshReg, bool bForceToBlack = false );
void CopyRenderTargetToTexture( ShaderAPITextureHandle_t textureHandle );
void CopyRenderTargetToTextureEx( ShaderAPITextureHandle_t textureHandle, int nRenderTargetID, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL );
void CopyTextureToRenderTargetEx( int nRenderTargetID, ShaderAPITextureHandle_t textureHandle, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL );
void CopyRenderTargetToScratchTexture( ShaderAPITextureHandle_t srcHandle, ShaderAPITextureHandle_t dstHandle, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL );
virtual void LockRect( void** pOutBits, int* pOutPitch, ShaderAPITextureHandle_t texHandle, int mipmap, int x, int y, int w, int h, bool bWrite, bool bRead );
virtual void UnlockRect( ShaderAPITextureHandle_t texHandle, int mipmap );
virtual void CopyTextureToTexture( ShaderAPITextureHandle_t srcTex, ShaderAPITextureHandle_t dstTex );
// Returns the cull mode (for fill rate computation)
D3DCULL GetCullMode() const;
void SetCullModeState( bool bEnable, D3DCULL nDesiredCullMode );
void ApplyCullEnable( bool bEnable );
// Alpha to coverage
void ApplyAlphaToCoverage( bool bEnable );
#if defined( _X360 )
void ApplySRGBReadState( int iTextureStage, bool bSRGBReadEnabled );
#endif
// Applies Z Bias
void ApplyZBias( const ShadowState_t& shaderState );
// Applies texture enable
void ApplyTextureEnable( const ShadowState_t& state, int stage );
void ApplyFogMode( ShaderFogMode_t fogMode, bool bSRGBWritesEnabled, bool bDisableFogGammaCorrection );
void UpdatePixelFogColorConstant( void );
void EnabledSRGBWrite( bool bEnabled );
// Gamma<->Linear conversions according to the video hardware we're running on
float GammaToLinear_HardwareSpecific( float fGamma ) const;
float LinearToGamma_HardwareSpecific( float fLinear ) const;
// Applies alpha blending
void ApplyAlphaBlend( bool bEnable, D3DBLEND srcBlend, D3DBLEND destBlend );
// Applies alpha texture op
void ApplyColorTextureStage( int stage, D3DTEXTUREOP op, int arg1, int arg2 );
void ApplyAlphaTextureStage( int stage, D3DTEXTUREOP op, int arg1, int arg2 );
// Sets texture stage stage + render stage state
void SetSamplerState( int stage, D3DSAMPLERSTATETYPE state, DWORD val );
void SetTextureStageState( int stage, D3DTEXTURESTAGESTATETYPE state, DWORD val);
void SetRenderStateForce( D3DRENDERSTATETYPE state, DWORD val );
void SetRenderState( D3DRENDERSTATETYPE state, DWORD val,
bool bFlushBufferedPrimitivesIfChanged = false);
// Scissor Rect
void SetScissorRect( const int nLeft, const int nTop, const int nRight, const int nBottom, const bool bEnableScissor );
// Can we download textures?
virtual bool CanDownloadTextures() const;
void ForceHardwareSync_WithManagedTexture();
void ForceHardwareSync( void );
void UpdateFrameSyncQuery( int queryIndex, bool bIssue );
void EvictManagedResources();
virtual void EvictManagedResourcesInternal();
// Gets at a particular transform
inline D3DXMATRIX& GetTransform( int i )
{
return *m_pMatrixStack[i]->GetTop();
}
int GetCurrentNumBones( void ) const;
bool IsHWMorphingEnabled( ) const;
int GetCurrentLightCombo( void ) const; // Used for DX8 only
void GetDX9LightState( LightState_t *state ) const; // Used for DX9 only
MaterialFogMode_t GetCurrentFogType( void ) const;
void RecordString( const char *pStr );
virtual bool IsRenderingMesh() const { return m_pRenderMesh != 0; }
void SetTextureTransformDimension( TextureStage_t textureStage, int dimension, bool projected );
void DisableTextureTransform( TextureStage_t textureMatrix );
void SetBumpEnvMatrix( TextureStage_t textureStage, float m00, float m01, float m10, float m11 );
int GetCurrentFrameCounter( void ) const
{
return m_CurrentFrame;
}
// Workaround hack for visualization of selection mode
virtual void SetupSelectionModeVisualizationState();
// Allocate and delete query objects.
virtual ShaderAPIOcclusionQuery_t CreateOcclusionQueryObject( void );
virtual void DestroyOcclusionQueryObject( ShaderAPIOcclusionQuery_t h );
// Bracket drawing with begin and end so that we can get counts next frame.
virtual void BeginOcclusionQueryDrawing( ShaderAPIOcclusionQuery_t h );
virtual void EndOcclusionQueryDrawing( ShaderAPIOcclusionQuery_t h );
// Get the number of pixels rendered between begin and end on an earlier frame.
// Calling this in the same frame is a huge perf hit!
virtual int OcclusionQuery_GetNumPixelsRendered( ShaderAPIOcclusionQuery_t h, bool bFlush );
void SetFlashlightState( const FlashlightState_t &state, const VMatrix &worldToTexture );
void SetFlashlightStateEx( const FlashlightState_t &state, const VMatrix &worldToTexture, ITexture *pFlashlightDepthTexture );
const FlashlightState_t &GetFlashlightState( VMatrix &worldToTexture ) const;
const FlashlightState_t &GetFlashlightStateEx( VMatrix &worldToTexture, ITexture **pFlashlightDepthTexture ) const;
// Gets at the shadow state for a particular state snapshot
virtual bool IsDepthWriteEnabled( StateSnapshot_t id ) const;
// IDebugTextureInfo implementation.
virtual bool IsDebugTextureListFresh( int numFramesAllowed = 1 );
virtual void EnableDebugTextureList( bool bEnable );
virtual bool SetDebugTextureRendering( bool bEnable );
virtual void EnableGetAllTextures( bool bEnable );
virtual KeyValues* GetDebugTextureList();
virtual int GetTextureMemoryUsed( TextureMemoryType eTextureMemory );
virtual void ClearVertexAndPixelShaderRefCounts();
virtual void PurgeUnusedVertexAndPixelShaders();
// Called when the dx support level has changed
virtual void DXSupportLevelChanged();
// User clip plane override
virtual void EnableUserClipTransformOverride( bool bEnable );
virtual void UserClipTransform( const VMatrix &worldToProjection );
bool UsingSoftwareVertexProcessing() const;
// Mark all user clip planes as being dirty
void MarkAllUserClipPlanesDirty();
// Converts a D3DXMatrix to a VMatrix and back
void D3DXMatrixToVMatrix( const D3DXMATRIX &in, VMatrix &out );
void VMatrixToD3DXMatrix( const VMatrix &in, D3DXMATRIX &out );
ITexture *GetRenderTargetEx( int nRenderTargetID );
virtual void SetToneMappingScaleLinear( const Vector &scale );
virtual const Vector &GetToneMappingScaleLinear( void ) const;
float GetLightMapScaleFactor( void ) const;
void SetFloatRenderingParameter(int parm_number, float value);
void SetIntRenderingParameter(int parm_number, int value);
void SetVectorRenderingParameter(int parm_number, Vector const &value);
float GetFloatRenderingParameter(int parm_number) const;
int GetIntRenderingParameter(int parm_number) const;
Vector GetVectorRenderingParameter(int parm_number) const;
// For dealing with device lost in cases where Present isn't called all the time (Hammer)
virtual void HandleDeviceLost();
virtual void EnableLinearColorSpaceFrameBuffer( bool bEnable );
virtual void SetPSNearAndFarZ( int pshReg );
// stencil methods
void SetStencilEnable(bool onoff);
void SetStencilFailOperation(StencilOperation_t op);
void SetStencilZFailOperation(StencilOperation_t op);
void SetStencilPassOperation(StencilOperation_t op);
void SetStencilCompareFunction(StencilComparisonFunction_t cmpfn);
void SetStencilReferenceValue(int ref);
void SetStencilTestMask(uint32 msk);
void SetStencilWriteMask(uint32 msk);
void ClearStencilBufferRectangle(int xmin, int ymin, int xmax, int ymax,int value);
virtual void GetDXLevelDefaults(uint &max_dxlevel,uint &recommended_dxlevel);
#if defined( _X360 )
HXUIFONT OpenTrueTypeFont( const char *pFontname, int tall, int style );
void CloseTrueTypeFont( HXUIFONT hFont );
bool GetTrueTypeFontMetrics( HXUIFONT hFont, XUIFontMetrics *pFontMetrics, XUICharMetrics charMetrics[256] );
// Render a sequence of characters and extract the data into a buffer
// For each character, provide the width+height of the font texture subrect,
// an offset to apply when rendering the glyph, and an offset into a buffer to receive the RGBA data
bool GetTrueTypeGlyphs( HXUIFONT hFont, int numChars, wchar_t *pWch, int *pOffsetX, int *pOffsetY, int *pWidth, int *pHeight, unsigned char *pRGBA, int *pRGBAOffset );
ShaderAPITextureHandle_t CreateRenderTargetSurface( int width, int height, ImageFormat format, const char *pDebugName, const char *pTextureGroupName );
void PersistDisplay();
bool PostQueuedTexture( const void *pData, int nSize, ShaderAPITextureHandle_t *pHandles, int nHandles, int nWidth, int nHeight, int nDepth, int nMips, int *pRefCount );
void *GetD3DDevice();
void PushVertexShaderGPRAllocation( int iVertexShaderCount = 64 );
void PopVertexShaderGPRAllocation( void );
void EnableVSync_360( bool bEnable );
#endif
virtual bool OwnGPUResources( bool bEnable );
// ------------ New Vertex/Index Buffer interface ----------------------------
void BindVertexBuffer( int streamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions = 1 );
void BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes );
void Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount );
// Draw the mesh with the currently bound vertex and index buffers.
void DrawWithVertexAndIndexBuffers( void );
// ------------ End ----------------------------
// deformations
virtual void PushDeformation( const DeformationBase_t *pDeformation );
virtual void PopDeformation( );
virtual int GetNumActiveDeformations( ) const ;
// for shaders to set vertex shader constants. returns a packed state which can be used to set the dynamic combo
virtual int GetPackedDeformationInformation( int nMaskOfUnderstoodDeformations,
float *pConstantValuesOut,
int nBufferSize,
int nMaximumDeformations,
int *pNumDefsOut ) const ;
inline Texture_t &GetTexture( ShaderAPITextureHandle_t hTexture )
{
return m_Textures[hTexture];
}
// Gets the texture
IDirect3DBaseTexture* GetD3DTexture( ShaderAPITextureHandle_t hTexture );
virtual bool ShouldWriteDepthToDestAlpha( void ) const;
virtual void AcquireThreadOwnership();
virtual void ReleaseThreadOwnership();
private:
enum
{
SMALL_BACK_BUFFER_SURFACE_WIDTH = 256,
SMALL_BACK_BUFFER_SURFACE_HEIGHT = 256,
};
bool m_bEnableDebugTextureList;
bool m_bDebugGetAllTextures;
bool m_bDebugTexturesRendering;
KeyValues *m_pDebugTextureList;
int m_nTextureMemoryUsedLastFrame, m_nTextureMemoryUsedTotal;
int m_nTextureMemoryUsedPicMip1, m_nTextureMemoryUsedPicMip2;
int m_nDebugDataExportFrame;
FlashlightState_t m_FlashlightState;
VMatrix m_FlashlightWorldToTexture;
ITexture *m_pFlashlightDepthTexture;
CShaderAPIDx8( CShaderAPIDx8 const& );
enum
{
INVALID_TRANSITION_OP = 0xFFFF
};
// State transition table for the device is as follows:
// Other app init causes transition from OK to OtherAppInit, during transition we must release resources
// !Other app init causes transition from OtherAppInit to OK, during transition we must restore resources
// Minimized or device lost or device not reset causes transition from OK to LOST_DEVICE, during transition we must release resources
// Minimized or device lost or device not reset causes transition from OtherAppInit to LOST_DEVICE
// !minimized AND !device lost causes transition from LOST_DEVICE to NEEDS_RESET
// minimized or device lost causes transition from NEEDS_RESET to LOST_DEVICE
// Successful TryDeviceReset and !Other app init causes transition from NEEDS_RESET to OK, during transition we must restore resources
// Successful TryDeviceReset and Other app init causes transition from NEEDS_RESET to OtherAppInit
void ExportTextureList();
void AddBufferToTextureList( const char *pName, D3DSURFACE_DESC &desc );
void SetupTextureGroup( ShaderAPITextureHandle_t hTexture, const char *pTextureGroupName );
// Creates the matrix stack
void CreateMatrixStacks();
// Initializes the render state
void InitRenderState( );
// Resets all dx renderstates to dx default so that our shadows are correct.
void ResetDXRenderState( );
// Resets the render state
void ResetRenderState( bool bFullReset = true );
// Setup standard vertex shader constants (that don't change)
void SetStandardVertexShaderConstants( float fOverbright );
// Initializes vertex and pixel shaders
void InitVertexAndPixelShaders();
// Discards the vertex and index buffers
void DiscardVertexBuffers();
// Computes the fill rate
void ComputeFillRate();
// Takes a snapshot
virtual StateSnapshot_t TakeSnapshot( );
// Converts the clear color to be appropriate for HDR
D3DCOLOR GetActualClearColor( D3DCOLOR clearColor );
// We lost the device
void OnDeviceLost();
// Gets the matrix stack from the matrix mode
int GetMatrixStack( MaterialMatrixMode_t mode ) const;
// Flushes the matrix state, returns false if we don't need to
// do any more work
bool MatrixIsChanging( TransformType_t transform = TRANSFORM_IS_GENERAL );
// Updates the matrix transform state
void UpdateMatrixTransform( TransformType_t transform = TRANSFORM_IS_GENERAL );
// Sets the vertex shader modelView state..
// NOTE: GetProjectionMatrix should only be called from the Commit functions!
const D3DXMATRIX &GetProjectionMatrix( void );
void SetVertexShaderViewProj();
void SetVertexShaderModelViewProjAndModelView();
void SetPixelShaderFogParams( int reg );
void SetPixelShaderFogParams( int reg, ShaderFogMode_t fogMode );
FORCEINLINE void UpdateVertexShaderFogParams( void )
{
if ( g_pHardwareConfig->Caps().m_SupportsPixelShaders )
{
float ooFogRange = 1.0f;
float fStart = m_VertexShaderFogParams[0];
float fEnd = m_VertexShaderFogParams[1];
// Check for divide by zero
if ( fEnd != fStart )
{
ooFogRange = 1.0f / ( fEnd - fStart );
}
float fogParams[4];
fogParams[0] = ooFogRange * fEnd;
fogParams[1] = 1.0f;
fogParams[2] = 1.0f - clamp( m_flFogMaxDensity, 0.0f, 1.0f ); // Max fog density
fogParams[3] = ooFogRange;
float vertexShaderCameraPos[4];
vertexShaderCameraPos[0] = m_WorldSpaceCameraPositon[0];
vertexShaderCameraPos[1] = m_WorldSpaceCameraPositon[1];
vertexShaderCameraPos[2] = m_WorldSpaceCameraPositon[2];
vertexShaderCameraPos[3] = m_DynamicState.m_FogZ; // waterheight
// cFogEndOverFogRange, cFogOne, unused, cOOFogRange
SetVertexShaderConstant( VERTEX_SHADER_FOG_PARAMS, fogParams, 1 );
// eyepos.x eyepos.y eyepos.z cWaterZ
SetVertexShaderConstant( VERTEX_SHADER_CAMERA_POS, vertexShaderCameraPos );
}
}
// Compute stats info for a texture
void ComputeStatsInfo( ShaderAPITextureHandle_t hTexture, bool isCubeMap, bool isVolumeTexture );
// For procedural textures
void AdvanceCurrentCopy( ShaderAPITextureHandle_t hTexture );
// Deletes a D3D texture
void DeleteD3DTexture( ShaderAPITextureHandle_t hTexture );
// Unbinds a texture
void UnbindTexture( ShaderAPITextureHandle_t hTexture );
// Releases all D3D textures
void ReleaseAllTextures();
// Deletes all textures
void DeleteAllTextures();
// Gets the currently modified texture handle
ShaderAPITextureHandle_t GetModifyTextureHandle() const;
// Gets the bind id
ShaderAPITextureHandle_t GetBoundTextureBindId( Sampler_t sampler ) const;
// If mat_texture_limit is enabled, then this tells us if binding the specified texture would
// take us over the limit.
bool WouldBeOverTextureLimit( ShaderAPITextureHandle_t hTexture );
// Sets the texture state
void SetTextureState( Sampler_t sampler, ShaderAPITextureHandle_t hTexture, bool force = false );
// Grab/release the internal render targets such as the back buffer and the save game thumbnail
void AcquireInternalRenderTargets();
void ReleaseInternalRenderTargets();
// create/release linear->gamma table texture lookups. Only used by hardware supporting pixel shader 2b and up
void AcquireLinearToGammaTableTextures();
void ReleaseLinearToGammaTableTextures();
// Gets the texture being modified
IDirect3DBaseTexture* GetModifyTexture();
void SetModifyTexture( IDirect3DBaseTexture *pTex );
// returns true if we're using texture coordinates at a given stage
bool IsUsingTextureCoordinates( int stage, int flags ) const;
// Returns true if the board thinks we're generating spheremap coordinates
bool IsSpheremapRenderStateActive( int stage ) const;
// Returns true if we're modulating constant color into the vertex color
bool IsModulatingVertexColor() const;
// Recomputes ambient light cube
void RecomputeAmbientLightCube( );
// Debugging spew
void SpewBoardState();
// Compute and save the world space camera position.
void CacheWorldSpaceCameraPosition();
// Compute and save the projection atrix with polyoffset built in if we need it.
void CachePolyOffsetProjectionMatrix();
// Vertex shader helper functions
int FindVertexShader( VertexFormat_t fmt, char const* pFileName ) const;
int FindPixelShader( char const* pFileName ) const;
// Returns copies of the front and back buffers
IDirect3DSurface* GetFrontBufferImage( ImageFormat& format );
IDirect3DSurface* GetBackBufferImage( Rect_t *pSrcRect, Rect_t *pDstRect, ImageFormat& format );
IDirect3DSurface* GetBackBufferImageHDR( Rect_t *pSrcRect, Rect_t *pDstRect, ImageFormat& format );
// Copy bits from a host-memory surface
void CopyBitsFromHostSurface( IDirect3DSurface* pSurfaceBits,
const Rect_t &dstRect, unsigned char *pData, ImageFormat srcFormat, ImageFormat dstFormat, int nDstStride );
FORCEINLINE void SetTransform( D3DTRANSFORMSTATETYPE State, CONST D3DXMATRIX *pMatrix )
{
#if !defined( _X360 )
Dx9Device()->SetTransform( State, pMatrix );
#endif
}
FORCEINLINE void SetLight( DWORD Index, CONST D3DLIGHT9 *pLight )
{
#if !defined( _X360 )
Dx9Device()->SetLight( Index, pLight );
#endif
}
FORCEINLINE void LightEnable( DWORD LightIndex, bool bEnable )
{
#if !defined( _X360 )
Dx9Device()->LightEnable( LightIndex, bEnable );
#endif
}
void ExecuteCommandBuffer( uint8 *pCmdBuffer );
void SetStandardTextureHandle( StandardTextureId_t nId, ShaderAPITextureHandle_t );
// Methods related to queuing functions to be called per-(pMesh->Draw call) or per-pass
void ClearAllCommitFuncs( CommitFuncType_t func, CommitShaderType_t shader );
void CallCommitFuncs( CommitFuncType_t func, CommitShaderType_t shader, bool bForce );
bool IsCommitFuncInUse( CommitFuncType_t func, CommitShaderType_t shader, int nFunc ) const;
void MarkCommitFuncInUse( CommitFuncType_t func, CommitShaderType_t shader, int nFunc );
void AddCommitFunc( CommitFuncType_t func, CommitShaderType_t shader, StateCommitFunc_t f );
void CallCommitFuncs( CommitFuncType_t func, bool bUsingFixedFunction, bool bForce = false );
// Commits transforms and lighting
void CommitStateChanges();
// Commits transforms that have to be dealt with on a per pass basis (ie. projection matrix for polyoffset)
void CommitPerPassStateChanges( StateSnapshot_t id );
// Need to handle fog mode on a per-pass basis
void CommitPerPassFogMode( bool bUsingVertexAndPixelShaders );
void CommitPerPassXboxFixups();
// Commits user clip planes
void CommitUserClipPlanes( bool bUsingFixedFunction );
// Gets the user clip transform (world->view)
D3DXMATRIX & GetUserClipTransform( );
// transform commit
bool VertexShaderTransformChanged( int i );
bool FixedFunctionTransformChanged( int i );
void UpdateVertexShaderMatrix( int iMatrix );
void SetVertexShaderStateSkinningMatrices();
void CommitVertexShaderTransforms();
void CommitPerPassVertexShaderTransforms();
void UpdateFixedFunctionMatrix( int iMatrix );
void SetFixedFunctionStateSkinningMatrices();
void CommitFixedFunctionTransforms();
void CommitPerPassFixedFunctionTransforms();
// Recomputes the fast-clip plane matrices based on the current fast-clip plane
void CommitFastClipPlane( );
// Computes a matrix which includes the poly offset given an initial projection matrix
void ComputePolyOffsetMatrix( const D3DXMATRIX& matProjection, D3DXMATRIX &matProjectionOffset );
void SetSkinningMatrices();
// lighting commit
bool VertexShaderLightingChanged( int i );
bool VertexShaderLightingEnableChanged( int i );
bool FixedFunctionLightingChanged( int i );
bool FixedFunctionLightingEnableChanged( int i );
VertexShaderLightTypes_t ComputeLightType( int i ) const;
void SortLights( int* index );
void CommitVertexShaderLighting();
void CommitPixelShaderLighting( int pshReg );
void CommitFixedFunctionLighting();
// Gets the surface associated with a texture (refcount of surface is increased)
IDirect3DSurface* GetTextureSurface( ShaderAPITextureHandle_t textureHandle );
IDirect3DSurface* GetDepthTextureSurface( ShaderAPITextureHandle_t textureHandle );
//
// Methods related to hardware config
//
void SetDefaultConfigValuesForDxLevel( int dxLevelFromCaps, ShaderDeviceInfo_t &info, unsigned int nFlagsUsed );
// Determines hardware capabilities
bool DetermineHardwareCaps( );
// Alpha To Coverage entrypoints and states - much of this involves vendor-dependent paths and states...
bool CheckVendorDependentAlphaToCoverage();
void EnableAlphaToCoverage();
void DisableAlphaToCoverage();
// Vendor-dependent shadow mapping detection
void CheckVendorDependentShadowMappingSupport( bool &bSupportsShadowDepthTextures, bool &bSupportsFetch4 );
// Override caps based on a requested dx level
void OverrideCaps( int nForcedDXLevel );
// Reports support for a given MSAA mode
bool SupportsMSAAMode( int nMSAAMode );
// Reports support for a given CSAA mode
bool SupportsCSAAMode( int nNumSamples, int nQualityLevel );
// Gamma correction of fog color, or not...
D3DCOLOR ComputeGammaCorrectedFogColor( unsigned char r, unsigned char g, unsigned char b, bool bSRGBWritesEnabled );
void SetDefaultMaterial();
bool RestorePersistedDisplay( bool bUseFrontBuffer );
void ClearStdTextureHandles( void );
// debug logging
void PrintfVA( char *fmt, va_list vargs );
void Printf( const char *fmt, ... );
float Knob( char *knobname, float *setvalue = NULL );
// "normal" back buffer and depth buffer. Need to keep this around so that we
// know what to set the render target to when we are done rendering to a texture.
IDirect3DSurface *m_pBackBufferSurface;
IDirect3DSurface *m_pBackBufferSurfaceSRGB;
IDirect3DSurface *m_pZBufferSurface;
// Optimization for screenshots
IDirect3DSurface *m_pSmallBackBufferFP16TempSurface;
ShaderAPITextureHandle_t m_hFullScreenTexture;
ShaderAPITextureHandle_t m_hLinearToGammaTableTexture;
ShaderAPITextureHandle_t m_hLinearToGammaTableIdentityTexture;
//
// State needed at the time of rendering (after snapshots have been applied)
//
// Interface for the D3DXMatrixStack
ID3DXMatrixStack* m_pMatrixStack[NUM_MATRIX_MODES];
matrix3x4_t m_boneMatrix[NUM_MODEL_TRANSFORMS];
int m_maxBoneLoaded;
// Current matrix mode
D3DTRANSFORMSTATETYPE m_MatrixMode;
int m_CurrStack;
// The current camera position in world space.
Vector4D m_WorldSpaceCameraPositon;
// The current projection matrix with polyoffset baked into it.
D3DXMATRIX m_CachedPolyOffsetProjectionMatrix;
D3DXMATRIX m_CachedFastClipProjectionMatrix;
D3DXMATRIX m_CachedFastClipPolyOffsetProjectionMatrix;
// The texture stage state that changes frequently
DynamicState_t m_DynamicState;
// The *desired* dynamic state. Most dynamic state is committed into actual hardware state
// at either per-pass or per-material time. This can also be used to force the hardware
// to match the desired state after returning from alt-tab.
DynamicState_t m_DesiredState;
// A list of state commit functions to run as per-draw call commit time
unsigned char m_pCommitFlags[COMMIT_FUNC_TYPE_COUNT][COMMIT_SHADER_TYPE_COUNT][ COMMIT_FUNC_BYTE_COUNT ];
CUtlVector< StateCommitFunc_t > m_CommitFuncs[COMMIT_FUNC_TYPE_COUNT][COMMIT_SHADER_TYPE_COUNT];
// Render data
CMeshBase *m_pRenderMesh;
int m_nDynamicVBSize;
IMaterialInternal *m_pMaterial;
// Shadow depth bias states
float m_fShadowSlopeScaleDepthBias;
float m_fShadowDepthBias;
bool m_bReadPixelsEnabled;
// Render-to-texture stuff...
bool m_UsingTextureRenderTarget;
int m_ViewportMaxWidth;
int m_ViewportMaxHeight;
ShaderAPITextureHandle_t m_hCachedRenderTarget;
bool m_bUsingSRGBRenderTarget;
// Ambient cube map ok?
int m_CachedAmbientLightCube;
// The current frame
int m_CurrentFrame;
// The texture we're currently modifying
ShaderAPITextureHandle_t m_ModifyTextureHandle;
char m_ModifyTextureLockedLevel;
unsigned char m_ModifyTextureLockedFace;
// Stores all textures
CUtlFixedLinkedList< Texture_t > m_Textures;
// Mesh builder used to modify vertex data
CMeshBuilder m_ModifyBuilder;
float m_VertexShaderFogParams[2];
float m_flFogMaxDensity;
// Shadow state transition table
CTransitionTable m_TransitionTable;
StateSnapshot_t m_nCurrentSnapshot;
// Depth test override...
bool m_bOverrideMaterialIgnoreZ;
bool m_bIgnoreZValue;
// Are we in the middle of resetting the render state?
bool m_bResettingRenderState;
// Can we buffer 2 frames ahead?
bool m_bBuffer2FramesAhead;
// Selection name stack
CUtlStack< int > m_SelectionNames;
bool m_InSelectionMode;
unsigned int* m_pSelectionBufferEnd;
unsigned int* m_pSelectionBuffer;
unsigned int* m_pCurrSelectionRecord;
float m_SelectionMinZ;
float m_SelectionMaxZ;
int m_NumHits;
// fog
unsigned char m_SceneFogColor[3];
MaterialFogMode_t m_SceneFogMode;
// Tone Mapping state ( w is gamma scale )
Vector4D m_ToneMappingScale;
Deformation_t m_DeformationStack[DEFORMATION_STACK_DEPTH];
Deformation_t *m_pDeformationStackPtr;
void WriteShaderConstantsToGPU();
// rendering parameter storage
int IntRenderingParameters[MAX_INT_RENDER_PARMS];
float FloatRenderingParameters[MAX_FLOAT_RENDER_PARMS];
Vector VectorRenderingParameters[MAX_VECTOR_RENDER_PARMS];
ShaderAPITextureHandle_t m_StdTextureHandles[TEXTURE_MAX_STD_TEXTURES];
// PIX instrumentation utlities...enable these with PIX_INSTRUMENTATION
void StartPIXInstrumentation();
void EndPIXInstrumentation();
void SetPIXMarker( unsigned long color, const char *szName );
void IncrementPIXError();
bool PIXError();
int m_nPIXErrorCount;
int m_nPixFrame;
bool m_bPixCapturing;
void ComputeVertexDescription( unsigned char* pBuffer, VertexFormat_t vertexFormat, MeshDesc_t& desc ) const
{
return MeshMgr()->ComputeVertexDescription( pBuffer, vertexFormat, desc );
}
// Reports support for shadow depth texturing
bool SupportsShadowDepthTextures( void );
bool SupportsFetch4( void );
void SetShadowDepthBiasFactors( float fShadowSlopeScaleDepthBias, float fShadowDepthBias );
// Vendor-dependent depth stencil texture format
ImageFormat GetShadowDepthTextureFormat( void );
bool SupportsBorderColor( void ) const;
bool SupportsFetch4( void ) const;
void EnableBuffer2FramesAhead( bool bEnable );
void SetDepthFeatheringPixelShaderConstant( int iConstant, float fDepthBlendScale );
void SetDisallowAccess( bool b )
{
g_bShaderAccessDisallowed = b;
}
void EnableShaderShaderMutex( bool b )
{
Assert( g_ShaderMutex.GetOwnerId() == 0 );
g_bUseShaderMutex = b;
}
void ShaderLock()
{
g_ShaderMutex.Lock();
}
void ShaderUnlock()
{
g_ShaderMutex.Unlock();
}
// Vendor-dependent slim texture format
ImageFormat GetNullTextureFormat( void );
//The idea behind a delayed constant is this.
// Some shaders set constants based on rendering states, and some rendering states aren't updated until after a shader's already called Draw().
// So, for some functions that are state based, we save the constant we set and if the state changes between when it's set in the shader setup code
// and when the shader is drawn, we update that constant.
struct DelayedConstants_t
{
int iPixelShaderFogParams;
void Invalidate( void )
{
iPixelShaderFogParams = -1;
}
DelayedConstants_t( void ) { this->Invalidate(); }
};
DelayedConstants_t m_DelayedShaderConstants;
bool SetRenderTargetInternalXbox( ShaderAPITextureHandle_t hTexture, bool bForce = false );
#if defined( _X360 )
CUtlStack<int> m_VertexShaderGPRAllocationStack;
#endif
int m_MaxVectorVertexShaderConstant;
int m_MaxBooleanVertexShaderConstant;
int m_MaxIntegerVertexShaderConstant;
int m_MaxVectorPixelShaderConstant;
int m_MaxBooleanPixelShaderConstant;
int m_MaxIntegerPixelShaderConstant;
bool m_bGPUOwned;
bool m_bResetRenderStateNeeded;
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
bool m_NullDevice;
#endif
};
//-----------------------------------------------------------------------------
// Class Factory
//-----------------------------------------------------------------------------
static CShaderAPIDx8 g_ShaderAPIDX8;
IShaderAPIDX8 *g_pShaderAPIDX8 = &g_ShaderAPIDX8;
CShaderDeviceDx8* g_pShaderDeviceDx8 = &g_ShaderAPIDX8;
// FIXME: Remove IShaderAPI + IShaderDevice; they change after SetMode
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderAPIDx8, IShaderAPI,
SHADERAPI_INTERFACE_VERSION, g_ShaderAPIDX8 )
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderAPIDx8, IShaderDevice,
SHADER_DEVICE_INTERFACE_VERSION, g_ShaderAPIDX8 )
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderAPIDx8, IDebugTextureInfo,
DEBUG_TEXTURE_INFO_VERSION, g_ShaderAPIDX8 )
//-----------------------------------------------------------------------------
// Accessors for major interfaces
//-----------------------------------------------------------------------------
// Pix wants a max of 32 characters
// We'll give it the right-most substrings separated by slashes
static char s_pPIXMaterialName[32];
void PIXifyName( char *pDst, int destSize, const char *pSrc )
{
char *pSrcWalk = (char *)pSrc;
while ( V_strlen( pSrcWalk ) > 31 ) // While we still have too many characters
{
char *pTok = strpbrk( pSrcWalk, "/\\" ); // Find next token
if ( pTok )
pSrcWalk = pTok + 1;
else
break;
}
V_strncpy( pDst, pSrcWalk, min( 32, destSize ) );
}
static int AdjustUpdateRange( float const* pVec, void const *pOut, int numVecs, int* pSkip )
{
int skip = 0;
uint32* pSrc = (uint32*)pVec;
uint32* pDst = (uint32*)pOut;
while( numVecs && !( ( pSrc[0] ^ pDst[0] ) | ( pSrc[1] ^ pDst[1] ) | ( pSrc[2] ^ pDst[2] ) | ( pSrc[3] ^ pDst[3] ) ) )
{
pSrc += 4;
pDst += 4;
numVecs--;
skip++;
}
*pSkip = skip;
if ( !numVecs )
return 0;
uint32* pSrcLast = pSrc + numVecs * 4 - 4;
uint32* pDstLast = pDst + numVecs * 4 - 4;
while( numVecs > 1 && !( ( pSrcLast[0] ^ pDstLast[0] ) | ( pSrcLast[1] ^ pDstLast[1] ) | ( pSrcLast[2] ^ pDstLast[2] ) | ( pSrcLast[3] ^ pDstLast[3] ) ) )
{
pSrcLast -= 4;
pDstLast -= 4;
numVecs--;
}
return numVecs;
}
//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CShaderAPIDx8::CShaderAPIDx8() :
m_Textures( 32 ),
m_CurrStack( -1 ),
m_ModifyTextureHandle( INVALID_SHADERAPI_TEXTURE_HANDLE ),
m_pRenderMesh( 0 ),
m_nDynamicVBSize( DYNAMIC_VERTEX_BUFFER_MEMORY ),
m_pMaterial( NULL ),
m_CurrentFrame( 0 ),
m_CachedAmbientLightCube( STATE_CHANGED ),
m_InSelectionMode( false ),
m_SelectionMinZ( FLT_MAX ),
m_SelectionMaxZ( FLT_MIN ),
m_pSelectionBuffer( 0 ),
m_pSelectionBufferEnd( 0 ),
m_bResetRenderStateNeeded( false ),
m_ModifyTextureLockedLevel( -1 ),
m_nPixFrame(0),
m_bPixCapturing(false),
m_nPIXErrorCount(0),
m_pBackBufferSurface( 0 ),
m_pBackBufferSurfaceSRGB( 0 ),
m_pZBufferSurface( 0 ),
m_bResettingRenderState( false ),
m_bReadPixelsEnabled( false ),
m_ToneMappingScale( 1.0f, 1.0f, 1.0f, 1.0f ),
m_hFullScreenTexture( INVALID_SHADERAPI_TEXTURE_HANDLE ),
m_hLinearToGammaTableTexture( INVALID_SHADERAPI_TEXTURE_HANDLE ),
m_hLinearToGammaTableIdentityTexture( INVALID_SHADERAPI_TEXTURE_HANDLE ),
m_fShadowSlopeScaleDepthBias( 16.0f ),
m_fShadowDepthBias( 0.00008f ),
m_hCachedRenderTarget( INVALID_SHADERAPI_TEXTURE_HANDLE ),
m_bUsingSRGBRenderTarget( false )
{
// FIXME: Remove! Backward compat
m_bAdapterSet = false;
m_bBuffer2FramesAhead = false;
m_bReadPixelsEnabled = true;
memset( m_pMatrixStack, 0, sizeof(ID3DXMatrixStack*) * NUM_MATRIX_MODES );
memset( &m_DynamicState, 0, sizeof(m_DynamicState) );
//m_DynamicState.m_HeightClipMode = MATERIAL_HEIGHTCLIPMODE_DISABLE;
m_nWindowHeight = m_nWindowWidth = 0;
m_maxBoneLoaded = 0;
m_bEnableDebugTextureList = 0;
m_bDebugTexturesRendering = 0;
m_pDebugTextureList = NULL;
m_nTextureMemoryUsedLastFrame = 0;
m_nTextureMemoryUsedTotal = 0;
m_nTextureMemoryUsedPicMip1 = 0;
m_nTextureMemoryUsedPicMip2 = 0;
m_nDebugDataExportFrame = 0;
m_SceneFogColor[0] = 0;
m_SceneFogColor[1] = 0;
m_SceneFogColor[2] = 0;
m_SceneFogMode = MATERIAL_FOG_NONE;
// We haven't yet detected whether we support CreateQuery or not yet.
memset(IntRenderingParameters,0,sizeof(IntRenderingParameters));
memset(FloatRenderingParameters,0,sizeof(FloatRenderingParameters));
memset(VectorRenderingParameters,0,sizeof(VectorRenderingParameters));
m_pDeformationStackPtr = m_DeformationStack + DEFORMATION_STACK_DEPTH;
m_bGPUOwned = false;
m_MaxVectorVertexShaderConstant = 0;
m_MaxBooleanVertexShaderConstant = 0;
m_MaxIntegerVertexShaderConstant = 0;
m_MaxVectorPixelShaderConstant = 0;
m_MaxBooleanPixelShaderConstant = 0;
m_MaxIntegerPixelShaderConstant = 0;
ClearStdTextureHandles();
//Debugger();
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
m_NullDevice = !!CommandLine()->FindParm( "-nulldevice" );
#endif
}
CShaderAPIDx8::~CShaderAPIDx8()
{
if ( m_DynamicState.m_pVectorVertexShaderConstant )
{
delete[] m_DynamicState.m_pVectorVertexShaderConstant;
m_DynamicState.m_pVectorVertexShaderConstant = NULL;
}
if ( m_DynamicState.m_pBooleanVertexShaderConstant )
{
delete[] m_DynamicState.m_pBooleanVertexShaderConstant;
m_DynamicState.m_pBooleanVertexShaderConstant = NULL;
}
if ( m_DynamicState.m_pIntegerVertexShaderConstant )
{
delete[] m_DynamicState.m_pIntegerVertexShaderConstant;
m_DynamicState.m_pIntegerVertexShaderConstant = NULL;
}
if ( m_DynamicState.m_pVectorPixelShaderConstant )
{
delete[] m_DynamicState.m_pVectorPixelShaderConstant;
m_DynamicState.m_pVectorPixelShaderConstant = NULL;
}
if ( m_DynamicState.m_pBooleanPixelShaderConstant )
{
delete[] m_DynamicState.m_pBooleanPixelShaderConstant;
m_DynamicState.m_pBooleanPixelShaderConstant = NULL;
}
if ( m_DynamicState.m_pIntegerPixelShaderConstant )
{
delete[] m_DynamicState.m_pIntegerPixelShaderConstant;
m_DynamicState.m_pIntegerPixelShaderConstant = NULL;
}
if ( m_pDebugTextureList )
{
m_pDebugTextureList->deleteThis();
m_pDebugTextureList = NULL;
}
}
void CShaderAPIDx8::ClearStdTextureHandles( void )
{
for(int i = 0 ; i < ARRAYSIZE( m_StdTextureHandles ) ; i++ )
m_StdTextureHandles[i] = INVALID_SHADERAPI_TEXTURE_HANDLE;
}
//-----------------------------------------------------------------------------
// FIXME: Remove! Backward compat.
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::OnAdapterSet()
{
if ( !DetermineHardwareCaps( ) )
return false;
// Modify the caps based on requested DXlevels
int nForcedDXLevel = CommandLine()->ParmValue( "-dxlevel", 0 );
if ( nForcedDXLevel > 0 )
{
nForcedDXLevel = MAX( nForcedDXLevel, ABSOLUTE_MINIMUM_DXLEVEL );
}
// FIXME: Check g_pHardwareConfig->ActualCaps() for a preferred DX level
OverrideCaps( nForcedDXLevel );
m_bAdapterSet = true;
return true;
}
void CShaderAPIDx8::GetDXLevelDefaults(uint &max_dxlevel,uint &recommended_dxlevel)
{
max_dxlevel=g_pHardwareConfig->ActualCaps().m_nMaxDXSupportLevel;
recommended_dxlevel=g_pHardwareConfig->ActualCaps().m_nDXSupportLevel;
}
//-----------------------------------------------------------------------------
// Can we download textures?
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::CanDownloadTextures() const
{
if ( IsDeactivated() )
return false;
return IsActive();
}
//-----------------------------------------------------------------------------
// Grab the render targets
//-----------------------------------------------------------------------------
void CShaderAPIDx8::AcquireInternalRenderTargets()
{
GLMPRINTF(( ">-A- CShaderAPIDx8::AcquireInternalRenderTargets... "));
if ( mat_debugalttab.GetBool() )
{
Warning( "mat_debugalttab: CShaderAPIDx8::AcquireInternalRenderTargets\n" );
}
if ( !m_pBackBufferSurface )
{
Dx9Device()->GetRenderTarget( 0, &m_pBackBufferSurface );
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
if( !m_NullDevice )
#endif
{
Assert( m_pBackBufferSurface );
}
}
#if defined( _X360 )
if ( !m_pBackBufferSurfaceSRGB )
{
// create a SRGB back buffer clone
int backWidth, backHeight;
ShaderAPI()->GetBackBufferDimensions( backWidth, backHeight );
D3DFORMAT backBufferFormat = ImageLoader::ImageFormatToD3DFormat( g_pShaderDevice->GetBackBufferFormat() );
m_pBackBufferSurfaceSRGB = g_TextureHeap.AllocRenderTargetSurface( backWidth, backHeight, (D3DFORMAT)MAKESRGBFMT( backBufferFormat ), true, 0 );
}
#endif
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
if ( !m_pZBufferSurface && !m_NullDevice )
#else
if ( !m_pZBufferSurface )
#endif
{
Dx9Device()->GetDepthStencilSurface( &m_pZBufferSurface );
Assert( m_pZBufferSurface );
}
GLMPRINTF(( "<-A- CShaderAPIDx8::AcquireInternalRenderTargets...complete "));
}
//-----------------------------------------------------------------------------
// Release the render targets
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ReleaseInternalRenderTargets( )
{
GLMPRINTF(( ">-A- CShaderAPIDx8::ReleaseInternalRenderTargets... "));
if( mat_debugalttab.GetBool() )
{
Warning( "mat_debugalttab: CShaderAPIDx8::ReleaseInternalRenderTargets\n" );
}
// Note: This function does not release renderable textures created elsewhere
// Those should be released separately via the texure manager
if ( m_pBackBufferSurface )
{
#if POSIX
// dxabstract's AddRef/Release have optional args to help track usage
int nRetVal = m_pBackBufferSurface->Release( 0, "-B CShaderAPIDx8::ReleaseInternalRenderTargets public release color buffer");
#else
int nRetVal = m_pBackBufferSurface->Release();
#endif
//Assert( nRetVal == 0 );
m_pBackBufferSurface = NULL;
}
if ( m_pZBufferSurface )
{
#if POSIX
// dxabstract's AddRef/Release have optional args to help track usage
int nRetVal = m_pZBufferSurface->Release( 0, "-B CShaderAPIDx8::ReleaseInternalRenderTargets public release zbuffer");
#else
int nRetVal = m_pZBufferSurface->Release();
#endif
//Assert( nRetVal == 0 ); //FIXME not sure why we're seeing a refcount of 3 here
m_pZBufferSurface = NULL;
}
GLMPRINTF(( "<-A- CShaderAPIDx8::ReleaseInternalRenderTargets... complete"));
}
//-----------------------------------------------------------------------------
// During init, places the persisted texture back into the back buffer.
// The initial 360 fixup present will then not flash. This routine has to be
// self contained, no other shader api systems are viable during init.
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::RestorePersistedDisplay( bool bUseFrontBuffer )
{
#if defined( _X360 )
if ( !( XboxLaunch()->GetLaunchFlags() & LF_INTERNALLAUNCH ) )
{
// there is no persisted screen
return false;
}
OwnGPUResources( false );
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;"
" float4 main( PS_IN In ) : COLOR"
" {"
" return tex2D( detail, In.TexCoord );"
" }";
D3DVERTEXELEMENT9 VertexElements[3] =
{
{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
{ 0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
D3DDECL_END()
};
IDirect3DTexture *pTexture;
if ( bUseFrontBuffer )
{
Dx9Device()->GetFrontBuffer( &pTexture );
}
else
{
// 360 holds a persistent image across restarts
Dx9Device()->GetPersistedTexture( &pTexture );
}
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;
}
IDirect3DVertexShader9 *pVertexShader;
Dx9Device()->CreateVertexShader( (DWORD*)pShaderCode->GetBufferPointer(), &pVertexShader );
pShaderCode->Release();
pErrorMsg = NULL;
pShaderCode = NULL;
hr = D3DXCompileShader( strPixelShaderProgram, (UINT)strlen( strPixelShaderProgram ), NULL, NULL, "main", "ps_2_0", 0, &pShaderCode, &pErrorMsg, NULL );
if ( FAILED(hr) )
{
return false;
}
IDirect3DPixelShader9 *pPixelShader;
Dx9Device()->CreatePixelShader( (DWORD*)pShaderCode->GetBufferPointer(), &pPixelShader );
pShaderCode->Release();
int w, h;
GetBackBufferDimensions( w, h );
// Create a vertex declaration from the element descriptions.
IDirect3DVertexDeclaration9 *pVertexDecl;
Dx9Device()->CreateVertexDeclaration( VertexElements, &pVertexDecl );
XMMATRIX matWVP = XMMatrixOrthographicOffCenterLH( 0, (FLOAT)w, (FLOAT)h, 0, 0, 1 );
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" );
g_pShaderDeviceDx8->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() );
// Structure to hold vertex data.
struct COLORVERTEX
{
FLOAT Position[3];
float TexCoord[2];
};
COLORVERTEX Vertices[4];
Vertices[0].Position[0] = 0;
Vertices[0].Position[1] = 0;
Vertices[0].Position[2] = 0;
Vertices[0].TexCoord[0] = 0;
Vertices[0].TexCoord[1] = 0;
Vertices[1].Position[0] = w-1;
Vertices[1].Position[1] = 0;
Vertices[1].Position[2] = 0;
Vertices[1].TexCoord[0] = 1;
Vertices[1].TexCoord[1] = 0;
Vertices[2].Position[0] = w-1;
Vertices[2].Position[1] = h-1;
Vertices[2].Position[2] = 0;
Vertices[2].TexCoord[0] = 1;
Vertices[2].TexCoord[1] = 1;
Vertices[3].Position[0] = 0;
Vertices[3].Position[1] = h-1;
Vertices[3].Position[2] = 0;
Vertices[3].TexCoord[0] = 0;
Vertices[3].TexCoord[1] = 1;
Dx9Device()->SetTexture( 0, pTexture );
Dx9Device()->SetVertexShader( pVertexShader );
Dx9Device()->SetPixelShader( pPixelShader );
Dx9Device()->SetVertexShaderConstantF( 0, (FLOAT*)&matWVP, 4 );
Dx9Device()->SetVertexDeclaration( pVertexDecl );
Dx9Device()->DrawPrimitiveUP( D3DPT_QUADLIST, 1, Vertices, sizeof( COLORVERTEX ) );
Dx9Device()->SetVertexShader( NULL );
Dx9Device()->SetPixelShader( NULL );
Dx9Device()->SetTexture( 0, NULL );
Dx9Device()->SetVertexDeclaration( NULL );
pVertexShader->Release();
pPixelShader->Release();
pVertexDecl->Release();
pTexture->Release();
OwnGPUResources( true );
return true;
#else
return false;
#endif
}
//-----------------------------------------------------------------------------
// Initialize, shutdown the Device....
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::OnDeviceInit()
{
AcquireInternalRenderTargets();
g_pHardwareConfig->CapsForEdit().m_TextureMemorySize = g_pShaderDeviceMgrDx8->GetVidMemBytes( m_nAdapter );
CreateMatrixStacks();
// Hide the cursor
RECORD_COMMAND( DX8_SHOW_CURSOR, 1 );
RECORD_INT( false );
#if !defined( _X360 )
Dx9Device()->ShowCursor( false );
#endif
// Initialize the shader manager
ShaderManager()->Init();
// Initialize the shader shadow
ShaderShadow()->Init();
// Initialize the mesh manager
MeshMgr()->Init();
bool bToolsMode = IsWindows() && ( CommandLine()->CheckParm( "-tools" ) != NULL );
// Use fat vertices when running in tools
MeshMgr()->UseFatVertices( bToolsMode );
// Initialize the transition table.
m_TransitionTable.Init();
// Initialize the render state
InitRenderState();
// Clear the z and color buffers
ClearBuffers( true, true, true, -1, -1 );
AllocFrameSyncObjects();
AllocNonInteractiveRefreshObjects();
RECORD_COMMAND( DX8_BEGIN_SCENE, 0 );
// Apply mandatory initialization HW fixups, GPU state will be left as expected
if ( IsX360() )
{
// place the possible persisted display into the back buffer, ready for present()
RestorePersistedDisplay( false );
// 360 MUST perform an initial swap to stabilize the state
// this ensures any states (e.g. gamma) are respected
// without this, the 360 resets to internal default state on the first swap
OwnGPUResources( false );
Dx9Device()->Present( 0, 0, 0, 0 );
// present corrupts the GPU state and back buffer (according to docs)
// re-clear the back buffer in order to re-establish the expected contents
ResetRenderState( false );
ClearBuffers( true, true, true, -1, -1 );
// place the front buffer image in the back buffer, later systems will detect and grab
// other systems will detect and grab
RestorePersistedDisplay( true );
}
Dx9Device()->BeginScene();
return true;
}
void CShaderAPIDx8::OnDeviceShutdown()
{
if ( !IsPC() || !IsActive() )
return;
// Deallocate all textures
DeleteAllTextures();
// Release render targets
ReleaseInternalRenderTargets();
// Free objects that are used for frame syncing.
FreeFrameSyncObjects();
FreeNonInteractiveRefreshObjects();
for (int i = 0; i < NUM_MATRIX_MODES; ++i)
{
if (m_pMatrixStack[i])
{
int ref = m_pMatrixStack[i]->Release();
Assert( ref == 0 );
}
}
// Shutdown the transition table.
m_TransitionTable.Shutdown();
MeshMgr()->Shutdown();
ShaderManager()->Shutdown();
ReleaseAllVertexDecl( );
}
//-----------------------------------------------------------------------------
// Sets the mode...
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::SetMode( void* VD3DHWND, int nAdapter, const ShaderDeviceInfo_t &info )
{
//
// FIXME: Note that this entire function is backward compat and will go soon
//
bool bRestoreNeeded = false;
if ( IsActive() )
{
ReleaseResources();
OnDeviceShutdown();
ShutdownDevice();
bRestoreNeeded = true;
}
LOCK_SHADERAPI();
Assert( D3D() );
Assert( nAdapter < g_pShaderDeviceMgr->GetAdapterCount() );
const HardwareCaps_t& actualCaps = g_pShaderDeviceMgr->GetHardwareCaps( nAdapter );
ShaderDeviceInfo_t actualInfo = info;
int nDXLevel = actualInfo.m_nDXLevel ? actualInfo.m_nDXLevel : actualCaps.m_nDXSupportLevel;
static bool bSetModeOnce = false;
if ( !bSetModeOnce )
{
nDXLevel = MAX( ABSOLUTE_MINIMUM_DXLEVEL, CommandLine()->ParmValue( "-dxlevel", nDXLevel ) );
bSetModeOnce = true;
}
if ( nDXLevel > actualCaps.m_nMaxDXSupportLevel )
{
nDXLevel = actualCaps.m_nMaxDXSupportLevel;
}
actualInfo.m_nDXLevel = g_pShaderDeviceMgr->GetClosestActualDXLevel( nDXLevel );
if ( !g_pShaderDeviceMgrDx8->ValidateMode( nAdapter, actualInfo ) )
return false;
g_pShaderAPI = this;
g_pShaderDevice = this;
g_pShaderShadow = ShaderShadow();
bool bOk = InitDevice( VD3DHWND, nAdapter, actualInfo );
if ( !bOk )
return false;
if ( !OnDeviceInit() )
return false;
if ( bRestoreNeeded && IsPC() )
{
ReacquireResources();
}
return true;
}
//-----------------------------------------------------------------------------
// Creates the matrix stack
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CreateMatrixStacks()
{
MEM_ALLOC_D3D_CREDIT();
for (int i = 0; i < NUM_MATRIX_MODES; ++i)
{
HRESULT hr = D3DXCreateMatrixStack( 0, &m_pMatrixStack[i] );
Assert( hr == D3D_OK );
}
}
//-----------------------------------------------------------------------------
// Vendor-dependent code to turn on alpha to coverage
//-----------------------------------------------------------------------------
void CShaderAPIDx8::EnableAlphaToCoverage( void )
{
if( !g_pHardwareConfig->ActualCaps().m_bSupportsAlphaToCoverage || !IsAAEnabled() )
return;
D3DRENDERSTATETYPE renderState = (D3DRENDERSTATETYPE)g_pHardwareConfig->Caps().m_AlphaToCoverageState;
SetRenderState( renderState, g_pHardwareConfig->Caps().m_AlphaToCoverageEnableValue ); // Vendor dependent state
}
//-----------------------------------------------------------------------------
// Vendor-dependent code to turn off alpha to coverage
//-----------------------------------------------------------------------------
void CShaderAPIDx8::DisableAlphaToCoverage()
{
if( !g_pHardwareConfig->ActualCaps().m_bSupportsAlphaToCoverage || !IsAAEnabled() )
return;
D3DRENDERSTATETYPE renderState = (D3DRENDERSTATETYPE)g_pHardwareConfig->Caps().m_AlphaToCoverageState;
SetRenderState( renderState, g_pHardwareConfig->Caps().m_AlphaToCoverageDisableValue ); // Vendor dependent state
}
//-----------------------------------------------------------------------------
// Determine capabilities
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::DetermineHardwareCaps( )
{
HardwareCaps_t& actualCaps = g_pHardwareConfig->ActualCapsForEdit();
if ( !g_pShaderDeviceMgrDx8->ComputeCapsFromD3D( &actualCaps, m_DisplayAdapter ) )
return false;
// See if the file tells us otherwise
g_pShaderDeviceMgrDx8->ReadDXSupportLevels( actualCaps );
// Read dxsupport.cfg which has config overrides for particular cards.
g_pShaderDeviceMgrDx8->ReadHardwareCaps( actualCaps, actualCaps.m_nMaxDXSupportLevel );
// What's in "-shader" overrides dxsupport.cfg
const char *pShaderParam = CommandLine()->ParmValue( "-shader" );
if ( pShaderParam )
{
Q_strncpy( actualCaps.m_pShaderDLL, pShaderParam, sizeof( actualCaps.m_pShaderDLL ) );
}
return true;
}
//-----------------------------------------------------------------------------
// Override caps based on a particular dx level
//-----------------------------------------------------------------------------
void CShaderAPIDx8::OverrideCaps( int nForcedDXLevel )
{
// Just use the actual caps if we can't use what was requested or if the default is requested
if ( nForcedDXLevel <= 0 )
{
nForcedDXLevel = g_pHardwareConfig->ActualCaps().m_nDXSupportLevel;
}
nForcedDXLevel = g_pShaderDeviceMgr->GetClosestActualDXLevel( nForcedDXLevel );
g_pHardwareConfig->SetupHardwareCaps( nForcedDXLevel, g_pHardwareConfig->ActualCaps() );
}
//-----------------------------------------------------------------------------
// Called when the dx support level has changed
//-----------------------------------------------------------------------------
void CShaderAPIDx8::DXSupportLevelChanged()
{
LOCK_SHADERAPI();
if ( IsPC() )
{
OverrideCaps( ShaderUtil()->GetConfig().dxSupportLevel );
}
else
{
Assert( 0 );
}
}
//-----------------------------------------------------------------------------
// FIXME: Remove! Backward compat only
//-----------------------------------------------------------------------------
int CShaderAPIDx8::GetActualTextureStageCount() const
{
return g_pHardwareConfig->GetActualTextureStageCount();
}
int CShaderAPIDx8::GetActualSamplerCount() const
{
return g_pHardwareConfig->GetActualSamplerCount();
}
int CShaderAPIDx8::StencilBufferBits() const
{
return m_bUsingStencil ? m_iStencilBufferBits : 0;
}
bool CShaderAPIDx8::IsAAEnabled() const
{
bool bAntialiasing = ( m_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE );
return bAntialiasing;
}
//-----------------------------------------------------------------------------
// Methods related to queuing functions to be called per-(pMesh->Draw call) or per-pass
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::IsCommitFuncInUse( CommitFuncType_t func, CommitShaderType_t shader, int nFunc ) const
{
Assert( nFunc < COMMIT_FUNC_COUNT );
return ( m_pCommitFlags[func][shader][ nFunc >> 3 ] & ( 1 << ( nFunc & 0x7 ) ) ) != 0;
}
void CShaderAPIDx8::MarkCommitFuncInUse( CommitFuncType_t func, CommitShaderType_t shader, int nFunc )
{
m_pCommitFlags[func][shader][ nFunc >> 3 ] |= 1 << ( nFunc & 0x7 );
}
void CShaderAPIDx8::AddCommitFunc( CommitFuncType_t func, CommitShaderType_t shader, StateCommitFunc_t f )
{
m_CommitFuncs[func][shader].AddToTail( f );
}
//-----------------------------------------------------------------------------
// Clears all commit functions
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ClearAllCommitFuncs( CommitFuncType_t func, CommitShaderType_t shader )
{
memset( m_pCommitFlags[func][shader], 0, COMMIT_FUNC_BYTE_COUNT );
m_CommitFuncs[func][shader].RemoveAll();
}
//-----------------------------------------------------------------------------
// Calls all commit functions in a particular list
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CallCommitFuncs( CommitFuncType_t func, CommitShaderType_t shader, bool bForce )
{
// 360 does not have have a FF pipe
Assert ( IsPC() || ( IsX360() && shader != COMMIT_FIXED_FUNCTION ) );
// Don't bother committing anything if we're deactivated
if ( IsDeactivated() )
return;
CUtlVector< StateCommitFunc_t > &funcList = m_CommitFuncs[func][shader];
int nCount = funcList.Count();
if ( nCount == 0 )
return;
for ( int i = 0; i < nCount; ++i )
{
funcList[i]( Dx9Device(), m_DesiredState, m_DynamicState, bForce );
}
ClearAllCommitFuncs( func, shader );
}
//-----------------------------------------------------------------------------
// Calls all per-mesh draw commit functions
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CallCommitFuncs( CommitFuncType_t func, bool bUsingFixedFunction, bool bForce )
{
// Fixed-Function only funcs
if ( IsPC() && ( bUsingFixedFunction || bForce ) )
{
CallCommitFuncs( func, COMMIT_FIXED_FUNCTION, bForce );
}
// Vertex-shader only funcs
if ( !bUsingFixedFunction || bForce )
{
CallCommitFuncs( func, COMMIT_VERTEX_SHADER, bForce );
}
// State set in both FF + VS
CallCommitFuncs( func, COMMIT_ALWAYS, bForce );
}
//-----------------------------------------------------------------------------
// Sets the sampler state
//-----------------------------------------------------------------------------
static FORCEINLINE void SetSamplerState( IDirect3DDevice9 *pDevice, int stage, D3DSAMPLERSTATETYPE state, DWORD val )
{
RECORD_SAMPLER_STATE( stage, state, val );
#if defined( _X360 )
if ( state == D3DSAMP_NOTSUPPORTED )
return;
#endif
pDevice->SetSamplerState( stage, state, val );
}
inline void CShaderAPIDx8::SetSamplerState( int stage, D3DSAMPLERSTATETYPE state, DWORD val )
{
#ifndef DX_TO_GL_ABSTRACTION
if ( IsDeactivated() )
return;
#endif
::SetSamplerState( Dx9Device(), stage, state, val );
}
//-----------------------------------------------------------------------------
// Sets the texture stage state
//-----------------------------------------------------------------------------
inline void CShaderAPIDx8::SetTextureStageState( int stage, D3DTEXTURESTAGESTATETYPE state, DWORD val )
{
#if !defined( _X360 )
if ( IsDeactivated() )
return;
Dx9Device()->SetTextureStageState( stage, state, val );
#endif
}
inline void CShaderAPIDx8::SetRenderState( D3DRENDERSTATETYPE state, DWORD val, bool bFlushIfChanged )
{
#if ( !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) )
{
if ( IsDeactivated() )
return;
}
#else
{
Assert( state != D3DRS_NOTSUPPORTED ); //Use SetSupportedRenderState() macro to avoid this at compile time
//if ( state == D3DRS_NOTSUPPORTED )
// return;
}
#endif
Assert( state >= 0 && ( int )state < MAX_NUM_RENDERSTATES );
if ( m_DynamicState.m_RenderState[state] != val )
{
if ( bFlushIfChanged )
{
FlushBufferedPrimitives();
}
#ifdef DX_TO_GL_ABSTRACTION
Dx9Device()->SetRenderStateInline( state, val );
#else
Dx9Device()->SetRenderState( state, val );
#endif
m_DynamicState.m_RenderState[state] = val;
}
}
#ifdef DX_TO_GL_ABSTRACTION
// Purposely always writing the new state (even if it's not changed), in case SetRenderStateConstInline() compiles away to nothing (it sometimes does)
#define SetRenderStateConstMacro(t, state, val ) \
do \
{ \
Assert( state >= 0 && ( int )state < MAX_NUM_RENDERSTATES ); \
if ( t->m_DynamicState.m_RenderState[state] != (DWORD)val ) \
{ \
Dx9Device()->SetRenderStateConstInline( state, val ); \
} \
t->m_DynamicState.m_RenderState[state] = val; \
} while(0)
#else
#define SetRenderStateConstMacro(t, state, val ) t->SetRenderState( state, val );
#endif
//-----------------------------------------------------------------------------
// Commits viewports
//-----------------------------------------------------------------------------
static void CommitSetScissorRect( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, DynamicState_t &currentState, bool bForce )
{
// Set the enable/disable renderstate
bool bEnableChanged = desiredState.m_RenderState[D3DRS_SCISSORTESTENABLE] != currentState.m_RenderState[D3DRS_SCISSORTESTENABLE];
if ( bEnableChanged )
{
Dx9Device()->SetRenderState( D3DRS_SCISSORTESTENABLE, desiredState.m_RenderState[D3DRS_SCISSORTESTENABLE] );
currentState.m_RenderState[D3DRS_SCISSORTESTENABLE] = desiredState.m_RenderState[D3DRS_SCISSORTESTENABLE];
}
// Only bother with the rect if we're enabling
if ( desiredState.m_RenderState[D3DRS_SCISSORTESTENABLE] )
{
int nWidth, nHeight;
ITexture *pTexture = ShaderAPI()->GetRenderTargetEx( 0 );
if ( pTexture == NULL )
{
ShaderAPI()->GetBackBufferDimensions( nWidth, nHeight );
}
else
{
nWidth = pTexture->GetActualWidth();
nHeight = pTexture->GetActualHeight();
}
Assert( (desiredState.m_ScissorRect.left <= nWidth) && (desiredState.m_ScissorRect.bottom <= nHeight) &&
( desiredState.m_ScissorRect.top >= 0 ) && (desiredState.m_ScissorRect.left >= 0) );
clamp( desiredState.m_ScissorRect.right, 0, nWidth );
clamp( desiredState.m_ScissorRect.left, 0, nWidth );
clamp( desiredState.m_ScissorRect.top, 0, nHeight );
clamp( desiredState.m_ScissorRect.bottom, 0, nHeight );
Dx9Device()->SetScissorRect( &desiredState.m_ScissorRect );
currentState.m_ScissorRect = desiredState.m_ScissorRect;
}
}
// Routine for setting scissor rect
// If pScissorRect is NULL, disable scissoring by setting the render state
// If pScissorRect is non-NULL, set the RECT state in Direct3D AND set the renderstate
inline void CShaderAPIDx8::SetScissorRect( const int nLeft, const int nTop, const int nRight, const int nBottom, const bool bEnableScissor )
{
Assert( (nLeft <= nRight) && (nTop <= nBottom) ); //360 craps itself if this isn't true
if ( !g_pHardwareConfig->Caps().m_bScissorSupported )
return;
// If we're turning it on, check the validity of the rect
if ( bEnableScissor )
{
int nWidth, nHeight;
ITexture *pTexture = GetRenderTargetEx( 0 );
if ( pTexture == NULL )
{
GetBackBufferDimensions( nWidth, nHeight );
}
else
{
nWidth = pTexture->GetActualWidth();
nHeight = pTexture->GetActualHeight();
}
Assert( (nRight <= nWidth) && (nBottom <= nHeight) && ( nTop >= 0 ) && (nLeft >= 0) );
clamp( nRight, 0, nWidth );
clamp( nLeft, 0, nWidth );
clamp( nTop, 0, nHeight );
clamp( nBottom, 0, nHeight );
}
DWORD dwEnableScissor = bEnableScissor ? TRUE : FALSE;
RECT newScissorRect;
newScissorRect.left = nLeft;
newScissorRect.top = nTop;
newScissorRect.right = nRight;
newScissorRect.bottom = nBottom;
if ( !m_bResettingRenderState )
{
bool bEnableChanged = m_DesiredState.m_RenderState[D3DRS_SCISSORTESTENABLE] != dwEnableScissor;
bool bRectChanged = memcmp( &newScissorRect, &m_DesiredState.m_ScissorRect, sizeof(RECT) ) != 0;
if ( !bEnableChanged && !bRectChanged )
return;
}
if ( !IsDeactivated() )
{
// Do we need to do this always?
FlushBufferedPrimitives();
}
m_DesiredState.m_RenderState[D3DRS_SCISSORTESTENABLE] = dwEnableScissor;
memcpy( &m_DesiredState.m_ScissorRect, &newScissorRect, sizeof(RECT) );
ADD_COMMIT_FUNC( COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitSetScissorRect );
}
inline void CShaderAPIDx8::SetRenderStateForce( D3DRENDERSTATETYPE state, DWORD val )
{
#if ( !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) )
{
if ( IsDeactivated() )
return;
}
#else
{
Assert( state != D3DRS_NOTSUPPORTED ); //Use SetSupportedRenderStateForce() macro to avoid this at compile time
//if ( state == D3DRS_NOTSUPPORTED )
// return;
}
#endif
Dx9Device()->SetRenderState( state, val );
m_DynamicState.m_RenderState[state] = val;
}
//-----------------------------------------------------------------------------
// Set the values for pixel shader constants that don't change.
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetStandardVertexShaderConstants( float fOverbright )
{
LOCK_SHADERAPI();
if ( g_pHardwareConfig->GetDXSupportLevel() < 80 )
return;
// Set a couple standard constants....
Vector4D standardVertexShaderConstant( 0.0f, 1.0f, 2.0f, 0.5f );
SetVertexShaderConstant( VERTEX_SHADER_MATH_CONSTANTS0, standardVertexShaderConstant.Base(), 1 );
// [ gamma, overbright, 1/3, 1/overbright ]
standardVertexShaderConstant.Init( 1.0f/2.2f, fOverbright, 1.0f / 3.0f, 1.0f / fOverbright );
SetVertexShaderConstant( VERTEX_SHADER_MATH_CONSTANTS1, standardVertexShaderConstant.Base(), 1 );
int nModelIndex = g_pHardwareConfig->Caps().m_nDXSupportLevel < 90 ? VERTEX_SHADER_MODEL - 10 : VERTEX_SHADER_MODEL;
/*
if ( g_pHardwareConfig->Caps().m_SupportsVertexShaders_3_0 )
{
Vector4D factors[4];
factors[0].Init( 1, 0, 0, 0 );
factors[1].Init( 0, 1, 0, 0 );
factors[2].Init( 0, 0, 1, 0 );
factors[3].Init( 0, 0, 0, 1 );
SetVertexShaderConstant( VERTEX_SHADER_DOT_PRODUCT_FACTORS, factors[0].Base(), 4 );
}
*/
if ( g_pHardwareConfig->Caps().m_SupportsVertexShaders_2_0 )
{
float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 );
}
else
{
// These point to the lighting and the transforms
standardVertexShaderConstant.Init(
VERTEX_SHADER_LIGHTS,
VERTEX_SHADER_LIGHTS + 5,
// Use COLOR instead of UBYTE4 since Geforce3 does not support it
// vConst.w should be 3, but due to about hack, mul by 255 and add epsilon
// 360 supports UBYTE4, so no fixup required
(IsPC() || !IsX360()) ? 765.01f : 3.0f,
nModelIndex ); // DX8 has different constant packing
SetVertexShaderConstant( VERTEX_SHADER_LIGHT_INDEX, standardVertexShaderConstant.Base(), 1 );
}
}
//-----------------------------------------------------------------------------
// Initialize vertex and pixel shaders
//-----------------------------------------------------------------------------
void CShaderAPIDx8::InitVertexAndPixelShaders()
{
// Allocate space for the pixel and vertex shader constants...
if ( g_pHardwareConfig->Caps().m_SupportsPixelShaders )
{
// pixel shaders
{
if (m_DynamicState.m_pVectorPixelShaderConstant)
{
delete[] m_DynamicState.m_pVectorPixelShaderConstant;
}
m_DynamicState.m_pVectorPixelShaderConstant = new Vector4D[g_pHardwareConfig->Caps().m_NumPixelShaderConstants];
if (m_DesiredState.m_pVectorPixelShaderConstant)
{
delete[] m_DesiredState.m_pVectorPixelShaderConstant;
}
m_DesiredState.m_pVectorPixelShaderConstant = new Vector4D[g_pHardwareConfig->Caps().m_NumPixelShaderConstants];
if (m_DynamicState.m_pBooleanPixelShaderConstant)
{
delete[] m_DynamicState.m_pBooleanPixelShaderConstant;
}
m_DynamicState.m_pBooleanPixelShaderConstant = new BOOL[g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants];
if (m_DesiredState.m_pBooleanPixelShaderConstant)
{
delete[] m_DesiredState.m_pBooleanPixelShaderConstant;
}
m_DesiredState.m_pBooleanPixelShaderConstant = new BOOL[g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants];
if (m_DynamicState.m_pIntegerPixelShaderConstant)
{
delete[] m_DynamicState.m_pIntegerPixelShaderConstant;
}
m_DynamicState.m_pIntegerPixelShaderConstant = new IntVector4D[g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants];
if (m_DesiredState.m_pIntegerPixelShaderConstant)
{
delete[] m_DesiredState.m_pIntegerPixelShaderConstant;
}
m_DesiredState.m_pIntegerPixelShaderConstant = new IntVector4D[g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants];
// force reset vector pixel constants
int i;
for ( i = 0; i < g_pHardwareConfig->Caps().m_NumPixelShaderConstants; ++i )
{
m_DesiredState.m_pVectorPixelShaderConstant[i].Init();
}
SetPixelShaderConstant( 0, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), g_pHardwareConfig->Caps().m_NumPixelShaderConstants, true );
// force reset boolean pixel constants
int nNumBooleanPixelShaderConstants = g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants;
if ( nNumBooleanPixelShaderConstants )
{
for ( i = 0; i < nNumBooleanPixelShaderConstants; ++i )
{
m_DesiredState.m_pBooleanPixelShaderConstant[i] = 0;
}
SetBooleanPixelShaderConstant( 0, m_DesiredState.m_pBooleanPixelShaderConstant, nNumBooleanPixelShaderConstants, true );
}
// force reset integer pixel constants
int nNumIntegerPixelShaderConstants = g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants;
if ( nNumIntegerPixelShaderConstants )
{
for ( i = 0; i < nNumIntegerPixelShaderConstants; ++i )
{
m_DesiredState.m_pIntegerPixelShaderConstant[i].Init();
}
SetIntegerPixelShaderConstant( 0, m_DesiredState.m_pIntegerPixelShaderConstant[0].Base(), nNumIntegerPixelShaderConstants, true );
}
}
// vertex shaders
{
if (m_DynamicState.m_pVectorVertexShaderConstant)
{
delete[] m_DynamicState.m_pVectorVertexShaderConstant;
}
m_DynamicState.m_pVectorVertexShaderConstant = new Vector4D[g_pHardwareConfig->Caps().m_NumVertexShaderConstants];
if (m_DesiredState.m_pVectorVertexShaderConstant)
{
delete[] m_DesiredState.m_pVectorVertexShaderConstant;
}
m_DesiredState.m_pVectorVertexShaderConstant = new Vector4D[g_pHardwareConfig->Caps().m_NumVertexShaderConstants];
if (m_DynamicState.m_pBooleanVertexShaderConstant)
{
delete[] m_DynamicState.m_pBooleanVertexShaderConstant;
}
m_DynamicState.m_pBooleanVertexShaderConstant = new BOOL[g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants];
if (m_DesiredState.m_pBooleanVertexShaderConstant)
{
delete[] m_DesiredState.m_pBooleanVertexShaderConstant;
}
m_DesiredState.m_pBooleanVertexShaderConstant = new BOOL[g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants];
if (m_DynamicState.m_pIntegerVertexShaderConstant)
{
delete[] m_DynamicState.m_pIntegerVertexShaderConstant;
}
m_DynamicState.m_pIntegerVertexShaderConstant = new IntVector4D[g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants];
if (m_DesiredState.m_pIntegerVertexShaderConstant)
{
delete[] m_DesiredState.m_pIntegerVertexShaderConstant;
}
m_DesiredState.m_pIntegerVertexShaderConstant = new IntVector4D[g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants];
// force reset vector vertex constants
int i;
for ( i = 0; i < g_pHardwareConfig->Caps().m_NumVertexShaderConstants; ++i )
{
m_DesiredState.m_pVectorVertexShaderConstant[i].Init();
}
SetVertexShaderConstant( 0, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), g_pHardwareConfig->Caps().m_NumVertexShaderConstants, true );
// force reset boolean vertex constants
for ( i = 0; i < g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants; ++i )
{
m_DesiredState.m_pBooleanVertexShaderConstant[i] = 0;
}
SetBooleanVertexShaderConstant( 0, m_DesiredState.m_pBooleanVertexShaderConstant, g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants, true );
// force reset integer vertex constants
for ( i = 0; i < g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants; ++i )
{
m_DesiredState.m_pIntegerVertexShaderConstant[i].Init();
}
SetIntegerVertexShaderConstant( 0, m_DesiredState.m_pIntegerVertexShaderConstant[0].Base(), g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants, true );
}
if ( IsX360() )
{
// to init/update all constants, must disable ownership
bool bPreviousState = OwnGPUResources( false );
WriteShaderConstantsToGPU();
OwnGPUResources( bPreviousState );
}
SetStandardVertexShaderConstants( OVERBRIGHT );
}
// Set up the vertex and pixel shader stuff
ShaderManager()->ResetShaderState();
}
//-----------------------------------------------------------------------------
// Initialize render state
//-----------------------------------------------------------------------------
void CShaderAPIDx8::InitRenderState()
{
// Set the default shadow state
g_pShaderShadowDx8->SetDefaultState();
// Grab a snapshot of this state; we'll be using it to set the board
// state to something well defined.
m_TransitionTable.TakeDefaultStateSnapshot();
if ( !IsDeactivated() )
{
ResetRenderState();
}
}
//-----------------------------------------------------------------------------
// Commits vertex textures
//-----------------------------------------------------------------------------
static void CommitVertexTextures( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, DynamicState_t &currentState, bool bForce )
{
int nCount = g_pMaterialSystemHardwareConfig->GetVertexTextureCount();
for ( int i = 0; i < nCount; ++i )
{
VertexTextureState_t &currentVTState = currentState.m_VertexTextureState[i];
ShaderAPITextureHandle_t textureHandle = desiredState.m_VertexTextureState[i].m_BoundTexture;
Texture_t *pTexture = ( textureHandle != INVALID_SHADERAPI_TEXTURE_HANDLE ) ? &g_ShaderAPIDX8.GetTexture( textureHandle ) : NULL;
if ( pTexture && ( pTexture->m_Flags & Texture_t::IS_VERTEX_TEXTURE ) == 0 )
{
Warning( "Attempting to bind a vertex texture (%s) which was not created as a vertex texture!\n", pTexture->m_DebugName.String() );
}
if ( bForce || ( currentVTState.m_BoundTexture != textureHandle ) )
{
currentVTState.m_BoundTexture = textureHandle;
// RECORD_COMMAND( DX8_SET_VERTEXTEXTURE, 3 );
// RECORD_INT( D3DVERTEXTEXTURESAMPLER0 + nStage );
// RECORD_INT( pTexture ? pTexture->GetUniqueID() : 0xFFFF );
// RECORD_INT( 0 );
IDirect3DBaseTexture *pD3DTexture = ( textureHandle >= 0 ) ? g_ShaderAPIDX8.GetD3DTexture( textureHandle ) : NULL;
pDevice->SetTexture( D3DVERTEXTEXTURESAMPLER0 + i, pD3DTexture );
}
if ( !pTexture )
continue;
D3DTEXTUREADDRESS nNewUTexWrap = pTexture->m_UTexWrap;
if ( bForce || ( currentVTState.m_UTexWrap != nNewUTexWrap ) )
{
currentVTState.m_UTexWrap = nNewUTexWrap;
SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSU, currentVTState.m_UTexWrap );
}
D3DTEXTUREADDRESS nNewVTexWrap = pTexture->m_VTexWrap;
if ( bForce || ( currentVTState.m_VTexWrap != nNewVTexWrap ) )
{
currentVTState.m_VTexWrap = nNewVTexWrap;
SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSV, currentVTState.m_VTexWrap );
}
/*
D3DTEXTUREADDRESS nNewWTexWrap = pTexture->GetWTexWrap();
if ( bForce || ( currentVTState.m_WTexWrap != nNewWTexWrap ) )
{
currentVTState.m_WTexWrap = nNewWTexWrap;
SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSW, currentVTState.m_WTexWrap );
}
*/
D3DTEXTUREFILTERTYPE nNewMinFilter = pTexture->m_MinFilter;
if ( bForce || ( currentVTState.m_MinFilter != nNewMinFilter ) )
{
currentVTState.m_MinFilter = nNewMinFilter;
SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MINFILTER, currentVTState.m_MinFilter );
}
D3DTEXTUREFILTERTYPE nNewMagFilter = pTexture->m_MagFilter;
if ( bForce || ( currentVTState.m_MagFilter != nNewMagFilter ) )
{
currentVTState.m_MagFilter = nNewMagFilter;
SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MAGFILTER, currentVTState.m_MagFilter );
}
D3DTEXTUREFILTERTYPE nNewMipFilter = pTexture->m_MipFilter;
if ( bForce || ( currentVTState.m_MipFilter != nNewMipFilter ) )
{
currentVTState.m_MipFilter = nNewMipFilter;
SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MIPFILTER, currentVTState.m_MipFilter );
}
}
}
//-----------------------------------------------------------------------------
// Returns true if the state snapshot is translucent
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::IsTranslucent( StateSnapshot_t id ) const
{
LOCK_SHADERAPI();
return m_TransitionTable.GetSnapshot(id).m_AlphaBlendEnable;
}
//-----------------------------------------------------------------------------
// Returns true if the state snapshot is alpha tested
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::IsAlphaTested( StateSnapshot_t id ) const
{
LOCK_SHADERAPI();
return m_TransitionTable.GetSnapshot(id).m_AlphaTestEnable;
}
//-----------------------------------------------------------------------------
// Gets at the shadow state for a particular state snapshot
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::IsDepthWriteEnabled( StateSnapshot_t id ) const
{
LOCK_SHADERAPI();
return m_TransitionTable.GetSnapshot(id).m_ZWriteEnable;
}
//-----------------------------------------------------------------------------
// Returns true if the state snapshot uses shaders
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::UsesVertexAndPixelShaders( StateSnapshot_t id ) const
{
LOCK_SHADERAPI();
return m_TransitionTable.GetSnapshotShader(id).m_VertexShader != INVALID_SHADER;
}
//-----------------------------------------------------------------------------
// Takes a snapshot
//-----------------------------------------------------------------------------
StateSnapshot_t CShaderAPIDx8::TakeSnapshot( )
{
LOCK_SHADERAPI();
return m_TransitionTable.TakeSnapshot();
}
void CShaderAPIDx8::ResetDXRenderState( void )
{
float zero = 0.0f;
float one = 1.0f;
DWORD dZero = *((DWORD*)(&zero));
DWORD dOne = *((DWORD*)(&one));
// Set default values for all dx render states.
// NOTE: this does not include states encapsulated by the transition table,
// which are reset in CTransitionTable::UseDefaultState
SetSupportedRenderStateForce( D3DRS_FILLMODE, D3DFILL_SOLID );
SetSupportedRenderStateForce( D3DRS_SHADEMODE, D3DSHADE_GOURAUD );
SetSupportedRenderStateForce( D3DRS_LASTPIXEL, TRUE );
SetSupportedRenderStateForce( D3DRS_CULLMODE, D3DCULL_CCW );
SetSupportedRenderStateForce( D3DRS_DITHERENABLE, FALSE );
SetSupportedRenderStateForce( D3DRS_FOGENABLE, FALSE );
SetSupportedRenderStateForce( D3DRS_SPECULARENABLE, FALSE );
SetSupportedRenderStateForce( D3DRS_FOGCOLOR, 0 );
SetSupportedRenderStateForce( D3DRS_FOGTABLEMODE, D3DFOG_NONE );
SetSupportedRenderStateForce( D3DRS_FOGSTART, dZero );
SetSupportedRenderStateForce( D3DRS_FOGEND, dOne );
SetSupportedRenderStateForce( D3DRS_FOGDENSITY, dZero );
SetSupportedRenderStateForce( D3DRS_RANGEFOGENABLE, FALSE );
SetSupportedRenderStateForce( D3DRS_STENCILENABLE, FALSE);
SetSupportedRenderStateForce( D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP );
SetSupportedRenderStateForce( D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP );
SetSupportedRenderStateForce( D3DRS_STENCILPASS, D3DSTENCILOP_KEEP );
SetSupportedRenderStateForce( D3DRS_STENCILFUNC, D3DCMP_ALWAYS );
SetSupportedRenderStateForce( D3DRS_STENCILREF, 0 );
SetSupportedRenderStateForce( D3DRS_STENCILMASK, 0xFFFFFFFF );
SetSupportedRenderStateForce( D3DRS_STENCILWRITEMASK, 0xFFFFFFFF );
SetSupportedRenderStateForce( D3DRS_TEXTUREFACTOR, 0xFFFFFFFF );
SetSupportedRenderStateForce( D3DRS_WRAP0, 0 );
SetSupportedRenderStateForce( D3DRS_WRAP1, 0 );
SetSupportedRenderStateForce( D3DRS_WRAP2, 0 );
SetSupportedRenderStateForce( D3DRS_WRAP3, 0 );
SetSupportedRenderStateForce( D3DRS_WRAP4, 0 );
SetSupportedRenderStateForce( D3DRS_WRAP5, 0 );
SetSupportedRenderStateForce( D3DRS_WRAP6, 0 );
SetSupportedRenderStateForce( D3DRS_WRAP7, 0 );
SetSupportedRenderStateForce( D3DRS_CLIPPING, TRUE );
SetSupportedRenderStateForce( D3DRS_LIGHTING, TRUE );
SetSupportedRenderStateForce( D3DRS_AMBIENT, 0 );
SetSupportedRenderStateForce( D3DRS_FOGVERTEXMODE, D3DFOG_NONE);
SetSupportedRenderStateForce( D3DRS_COLORVERTEX, TRUE );
SetSupportedRenderStateForce( D3DRS_LOCALVIEWER, TRUE );
SetSupportedRenderStateForce( D3DRS_NORMALIZENORMALS, FALSE );
SetSupportedRenderStateForce( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_COLOR2 );
SetSupportedRenderStateForce( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL );
SetSupportedRenderStateForce( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL );
SetSupportedRenderStateForce( D3DRS_VERTEXBLEND, D3DVBF_DISABLE );
SetSupportedRenderStateForce( D3DRS_CLIPPLANEENABLE, 0 );
// -disable_d3d9_hacks is for debugging. For example, the "CENT" driver hack thing causes the flashlight pass to appear much brighter on NVidia drivers.
if ( IsPC() && !IsOpenGL() && !CommandLine()->CheckParm( "-disable_d3d9_hacks" ) )
{
if ( g_pHardwareConfig->Caps().m_bNeedsATICentroidHack && ( g_pHardwareConfig->Caps().m_VendorID == VENDORID_ATI ) )
{
SetSupportedRenderStateForce( D3DRS_POINTSIZE, MAKEFOURCC( 'C', 'E', 'N', 'T' ) );
}
if( g_pHardwareConfig->Caps().m_bDisableShaderOptimizations )
{
SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_Y, MAKEFOURCC( 'C', 'O', 'P', 'M' ) );
}
}
SetSupportedRenderStateForce( D3DRS_POINTSIZE, dOne );
SetSupportedRenderStateForce( D3DRS_POINTSIZE_MIN, dOne );
SetSupportedRenderStateForce( D3DRS_POINTSPRITEENABLE, FALSE );
SetSupportedRenderStateForce( D3DRS_POINTSCALEENABLE, FALSE );
SetSupportedRenderStateForce( D3DRS_POINTSCALE_A, dOne );
SetSupportedRenderStateForce( D3DRS_POINTSCALE_B, dZero );
SetSupportedRenderStateForce( D3DRS_POINTSCALE_C, dZero );
SetSupportedRenderStateForce( D3DRS_MULTISAMPLEANTIALIAS, TRUE );
SetSupportedRenderStateForce( D3DRS_MULTISAMPLEMASK, 0xFFFFFFFF );
SetSupportedRenderStateForce( D3DRS_PATCHEDGESTYLE, D3DPATCHEDGE_DISCRETE );
SetSupportedRenderStateForce( D3DRS_DEBUGMONITORTOKEN, D3DDMT_ENABLE );
float sixtyFour = 64.0f;
SetSupportedRenderStateForce( D3DRS_POINTSIZE_MAX, *((DWORD*)(&sixtyFour)));
SetSupportedRenderStateForce( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE );
SetSupportedRenderStateForce( D3DRS_TWEENFACTOR, dZero );
SetSupportedRenderStateForce( D3DRS_POSITIONDEGREE, D3DDEGREE_CUBIC );
SetSupportedRenderStateForce( D3DRS_NORMALDEGREE, D3DDEGREE_LINEAR );
SetSupportedRenderStateForce( D3DRS_SCISSORTESTENABLE, FALSE);
SetSupportedRenderStateForce( D3DRS_SLOPESCALEDEPTHBIAS, dZero );
SetSupportedRenderStateForce( D3DRS_ANTIALIASEDLINEENABLE, FALSE );
SetSupportedRenderStateForce( D3DRS_MINTESSELLATIONLEVEL, dOne );
SetSupportedRenderStateForce( D3DRS_MAXTESSELLATIONLEVEL, dOne );
SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_X, dZero );
SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_Y, dZero );
SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_Z, dOne );
SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_W, dZero );
SetSupportedRenderStateForce( D3DRS_ENABLEADAPTIVETESSELLATION, FALSE );
SetSupportedRenderStateForce( D3DRS_TWOSIDEDSTENCILMODE, FALSE );
SetSupportedRenderStateForce( D3DRS_CCW_STENCILFAIL, D3DSTENCILOP_KEEP );
SetSupportedRenderStateForce( D3DRS_CCW_STENCILZFAIL, D3DSTENCILOP_KEEP );
SetSupportedRenderStateForce( D3DRS_CCW_STENCILPASS, D3DSTENCILOP_KEEP );
SetSupportedRenderStateForce( D3DRS_CCW_STENCILFUNC, D3DCMP_ALWAYS );
SetSupportedRenderStateForce( D3DRS_COLORWRITEENABLE1, 0x0000000f );
SetSupportedRenderStateForce( D3DRS_COLORWRITEENABLE2, 0x0000000f );
SetSupportedRenderStateForce( D3DRS_COLORWRITEENABLE3, 0x0000000f );
SetSupportedRenderStateForce( D3DRS_BLENDFACTOR, 0xffffffff );
SetSupportedRenderStateForce( D3DRS_SRGBWRITEENABLE, 0);
SetSupportedRenderStateForce( D3DRS_DEPTHBIAS, dZero );
SetSupportedRenderStateForce( D3DRS_WRAP8, 0 );
SetSupportedRenderStateForce( D3DRS_WRAP9, 0 );
SetSupportedRenderStateForce( D3DRS_WRAP10, 0 );
SetSupportedRenderStateForce( D3DRS_WRAP11, 0 );
SetSupportedRenderStateForce( D3DRS_WRAP12, 0 );
SetSupportedRenderStateForce( D3DRS_WRAP13, 0 );
SetSupportedRenderStateForce( D3DRS_WRAP14, 0 );
SetSupportedRenderStateForce( D3DRS_WRAP15, 0 );
SetSupportedRenderStateForce( D3DRS_BLENDOP, D3DBLENDOP_ADD );
SetSupportedRenderStateForce( D3DRS_BLENDOPALPHA, D3DBLENDOP_ADD );
#if defined( _X360 )
SetSupportedRenderStateForce( D3DRS_HIZENABLE, D3DHIZ_AUTOMATIC );
SetSupportedRenderStateForce( D3DRS_HIZWRITEENABLE, D3DHIZ_AUTOMATIC );
SetSupportedRenderStateForce( D3DRS_HISTENCILENABLE, FALSE );
SetSupportedRenderStateForce( D3DRS_HISTENCILWRITEENABLE, FALSE );
SetSupportedRenderStateForce( D3DRS_HISTENCILFUNC, D3DHSCMP_EQUAL );
SetSupportedRenderStateForce( D3DRS_HISTENCILREF, 0 );
#endif
}
//-----------------------------------------------------------------------------
// Own GPU Resources. Return previous state.
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::OwnGPUResources( bool bEnable )
{
#if defined( _X360 )
if ( m_bGPUOwned == bEnable )
{
return m_bGPUOwned;
}
if ( !bEnable )
{
Dx9Device()->GpuDisownAll();
}
else
{
// owned GPU constants can be set very fast, and must be in blocks of 4
// there are 256, but the game only uses 217 (snapped to 220), leaving just enough room for shader literals
COMPILE_TIME_ASSERT( VERTEX_SHADER_MODEL + 3*NUM_MODEL_TRANSFORMS == 217 );
Dx9Device()->GpuOwnVertexShaderConstantF( 0, AlignValue( VERTEX_SHADER_MODEL + 3*NUM_MODEL_TRANSFORMS, 4 ) );
// there are 256, but the game only utilizes 32, leaving lots of room for shader literals
Dx9Device()->GpuOwnPixelShaderConstantF( 0, 32 );
}
bool bPrevious = m_bGPUOwned;
m_bGPUOwned = bEnable;
return bPrevious;
#else
return false;
#endif
}
//-----------------------------------------------------------------------------
// Reset render state (to its initial value)
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ResetRenderState( bool bFullReset )
{
LOCK_SHADERAPI();
RECORD_DEBUG_STRING( "BEGIN ResetRenderState" );
if ( !CanDownloadTextures() )
{
QueueResetRenderState();
return;
}
m_bResettingRenderState = true;
OwnGPUResources( true );
ResetDXRenderState();
// We're not currently rendering anything
m_nCurrentSnapshot = -1;
D3DXMatrixIdentity( &m_CachedPolyOffsetProjectionMatrix );
D3DXMatrixIdentity( &m_CachedFastClipProjectionMatrix );
D3DXMatrixIdentity( &m_CachedFastClipPolyOffsetProjectionMatrix );
m_UsingTextureRenderTarget = false;
m_SceneFogColor[0] = 0;
m_SceneFogColor[1] = 0;
m_SceneFogColor[2] = 0;
m_SceneFogMode = MATERIAL_FOG_NONE;
// This is state that isn't part of the snapshot per-se, because we
// don't need it when it's actually time to render. This just helps us
// to construct the shadow state.
m_DynamicState.m_ClearColor = D3DCOLOR_XRGB(0,0,0);
if ( bFullReset )
{
InitVertexAndPixelShaders();
}
else
{
// just need to dirty the dynamic state, desired state gets copied into below
Q_memset( m_DynamicState.m_pVectorPixelShaderConstant, 0, g_pHardwareConfig->Caps().m_NumPixelShaderConstants * sizeof( Vector4D ) );
Q_memset( m_DynamicState.m_pBooleanPixelShaderConstant, 0, g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants * sizeof( BOOL ) );
Q_memset( m_DynamicState.m_pIntegerPixelShaderConstant, 0, g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants * sizeof( IntVector4D ) );
Q_memset( m_DynamicState.m_pVectorVertexShaderConstant, 0, g_pHardwareConfig->Caps().m_NumVertexShaderConstants * sizeof( Vector4D ) );
Q_memset( m_DynamicState.m_pBooleanVertexShaderConstant, 0, g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants * sizeof( BOOL ) );
Q_memset( m_DynamicState.m_pIntegerVertexShaderConstant, 0, g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants * sizeof( IntVector4D ) );
SetStandardVertexShaderConstants( OVERBRIGHT );
}
//Set the default compressed depth range written to dest alpha. Only need to compress it for 8bit alpha to get a useful gradient.
m_DynamicState.m_DestAlphaDepthRange = (g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT) ? 8192.0f : 192.0f;
m_CachedAmbientLightCube = STATE_CHANGED;
// Set the constant color
m_DynamicState.m_ConstantColor = 0xFFFFFFFF;
Color4ub( 255, 255, 255, 255 );
// Ambient light color
m_DynamicState.m_Ambient = 0;
SetSupportedRenderState( D3DRS_AMBIENT, m_DynamicState.m_Ambient );
// Fog
m_VertexShaderFogParams[0] = m_VertexShaderFogParams[1] = 0.0f;
m_WorldSpaceCameraPositon.Init( 0, 0, 0.01f, 0 ); // Don't let z be zero, as some pixel shaders will divide by this
m_DynamicState.m_FogColor = 0xFFFFFFFF;
m_DynamicState.m_PixelFogColor[0] = m_DynamicState.m_PixelFogColor[1] =
m_DynamicState.m_PixelFogColor[2] = m_DynamicState.m_PixelFogColor[3] = 0.0f;
m_DynamicState.m_bFogGammaCorrectionDisabled = false;
m_DynamicState.m_FogEnable = false;
m_DynamicState.m_SceneFog = MATERIAL_FOG_NONE;
m_DynamicState.m_FogMode = D3DFOG_NONE;
m_DynamicState.m_FogStart = -1;
m_DynamicState.m_FogEnd = -1;
m_DynamicState.m_FogMaxDensity = -1.0f;
m_DynamicState.m_FogZ = 0.0f;
SetSupportedRenderState( D3DRS_FOGCOLOR, m_DynamicState.m_FogColor );
SetSupportedRenderState( D3DRS_FOGENABLE, m_DynamicState.m_FogEnable );
SetSupportedRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE );
SetSupportedRenderState( D3DRS_FOGVERTEXMODE, D3DFOG_NONE );
SetSupportedRenderState( D3DRS_RANGEFOGENABLE, false );
FogStart( 0 );
FogEnd( 0 );
FogMaxDensity( 1.0f );
m_DynamicState.m_bSRGBWritesEnabled = false;
// Set the cull mode
m_DynamicState.m_bCullEnabled = true;
m_DynamicState.m_CullMode = D3DCULL_CCW;
m_DynamicState.m_DesiredCullMode = D3DCULL_CCW;
SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
// No shade mode yet
m_DynamicState.m_ShadeMode = (D3DSHADEMODE)-1;
ShadeMode( SHADER_SMOOTH );
m_DynamicState.m_bHWMorphingEnabled = false;
// Skinning...
m_DynamicState.m_NumBones = 0;
m_DynamicState.m_VertexBlend = (D3DVERTEXBLENDFLAGS)-1;
SetSupportedRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE );
SetSupportedRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE );
// No normal normalization
m_DynamicState.m_NormalizeNormals = false;
SetSupportedRenderState( D3DRS_NORMALIZENORMALS, m_DynamicState.m_NormalizeNormals );
bool bAntialiasing = ( m_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE );
if ( g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT )
{
bAntialiasing = false;
}
SetRenderState( D3DRS_MULTISAMPLEANTIALIAS, bAntialiasing );
// Anisotropic filtering is disabled by default
if ( bFullReset )
{
SetAnisotropicLevel( 1 );
}
int i;
for ( i = 0; i < g_pHardwareConfig->ActualCaps().m_NumTextureStages; ++i )
{
TextureStage(i).m_TextureTransformFlags = D3DTTFF_DISABLE;
TextureStage(i).m_BumpEnvMat00 = 1.0f;
TextureStage(i).m_BumpEnvMat01 = 0.0f;
TextureStage(i).m_BumpEnvMat10 = 0.0f;
TextureStage(i).m_BumpEnvMat11 = 1.0f;
SetTextureStageState( i, D3DTSS_TEXTURETRANSFORMFLAGS, TextureStage(i).m_TextureTransformFlags );
SetTextureStageState( i, D3DTSS_BUMPENVMAT00, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat00) ) );
SetTextureStageState( i, D3DTSS_BUMPENVMAT01, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat01) ) );
SetTextureStageState( i, D3DTSS_BUMPENVMAT10, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat10) ) );
SetTextureStageState( i, D3DTSS_BUMPENVMAT11, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat11) ) );
}
for ( i = 0; i < g_pHardwareConfig->ActualCaps().m_NumSamplers; ++i )
{
SamplerState(i).m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE;
SamplerState(i).m_UTexWrap = D3DTADDRESS_WRAP;
SamplerState(i).m_VTexWrap = D3DTADDRESS_WRAP;
SamplerState(i).m_WTexWrap = D3DTADDRESS_WRAP;
SamplerState(i).m_MagFilter = D3DTEXF_POINT;
SamplerState(i).m_MinFilter = D3DTEXF_POINT;
SamplerState(i).m_MipFilter = D3DTEXF_NONE;
SamplerState(i).m_FinestMipmapLevel = 0;
SamplerState(i).m_LodBias = 0.0f;
SamplerState(i).m_TextureEnable = false;
SamplerState(i).m_SRGBReadEnable = false;
// Just some initial state...
Dx9Device()->SetTexture( i, 0 );
SetSamplerState( i, D3DSAMP_ADDRESSU, SamplerState(i).m_UTexWrap );
SetSamplerState( i, D3DSAMP_ADDRESSV, SamplerState(i).m_VTexWrap );
SetSamplerState( i, D3DSAMP_ADDRESSW, SamplerState(i).m_WTexWrap );
SetSamplerState( i, D3DSAMP_MINFILTER, SamplerState(i).m_MinFilter );
SetSamplerState( i, D3DSAMP_MAGFILTER, SamplerState(i).m_MagFilter );
SetSamplerState( i, D3DSAMP_MIPFILTER, SamplerState(i).m_MipFilter );
SetSamplerState( i, D3DSAMP_MAXMIPLEVEL, SamplerState(i).m_FinestMipmapLevel );
SetSamplerState( i, D3DSAMP_MIPMAPLODBIAS, SamplerState(i).m_LodBias );
SetSamplerState( i, D3DSAMP_BORDERCOLOR, RGB( 0,0,0 ) );
}
// FIXME!!!!! : This barfs with the debug runtime on 6800.
for( i = 0; i < g_pHardwareConfig->ActualCaps().m_nVertexTextureCount; i++ )
{
m_DynamicState.m_VertexTextureState[i].m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE;
Dx9Device()->SetTexture( D3DVERTEXTEXTURESAMPLER0 + i, NULL );
m_DynamicState.m_VertexTextureState[i].m_UTexWrap = D3DTADDRESS_CLAMP;
m_DynamicState.m_VertexTextureState[i].m_VTexWrap = D3DTADDRESS_CLAMP;
// m_DynamicState.m_VertexTextureState[i].m_WTexWrap = D3DTADDRESS_CLAMP;
m_DynamicState.m_VertexTextureState[i].m_MinFilter = D3DTEXF_POINT;
m_DynamicState.m_VertexTextureState[i].m_MagFilter = D3DTEXF_POINT;
m_DynamicState.m_VertexTextureState[i].m_MipFilter = D3DTEXF_POINT;
SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSU, m_DynamicState.m_VertexTextureState[i].m_UTexWrap );
SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSV, m_DynamicState.m_VertexTextureState[i].m_VTexWrap );
// SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSW, m_DynamicState.m_VertexTextureState[i].m_WTexWrap );
SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MINFILTER, m_DynamicState.m_VertexTextureState[i].m_MinFilter );
SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MAGFILTER, m_DynamicState.m_VertexTextureState[i].m_MagFilter );
SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MIPFILTER, m_DynamicState.m_VertexTextureState[i].m_MipFilter );
}
m_DynamicState.m_NumLights = 0;
for ( i = 0; i < MAX_NUM_LIGHTS; ++i)
{
m_DynamicState.m_LightEnable[i] = false;
m_DynamicState.m_LightChanged[i] = STATE_CHANGED;
m_DynamicState.m_LightEnableChanged[i] = STATE_CHANGED;
}
for ( i = 0; i < NUM_MATRIX_MODES; ++i)
{
// By setting this to *not* be identity, we force an update...
m_DynamicState.m_TransformType[i] = TRANSFORM_IS_GENERAL;
m_DynamicState.m_TransformChanged[i] = STATE_CHANGED;
}
// set the board state to match the default state
m_TransitionTable.UseDefaultState();
// Set the default render state
SetDefaultState();
// Constant for all time
SetSupportedRenderState( D3DRS_CLIPPING, TRUE );
SetSupportedRenderState( D3DRS_LOCALVIEWER, TRUE );
SetSupportedRenderState( D3DRS_POINTSCALEENABLE, FALSE );
SetSupportedRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL );
SetSupportedRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_COLOR2 );
SetSupportedRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL );
SetSupportedRenderState( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL );
SetSupportedRenderState( D3DRS_COLORVERTEX, TRUE ); // This defaults to true anyways. . .
// Set a default identity material.
SetDefaultMaterial();
#if 0
float fBias = -1.0f;
SetTextureStageState( 0, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) );
SetTextureStageState( 1, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) );
SetTextureStageState( 2, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) );
SetTextureStageState( 3, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) );
#endif
if ( bFullReset )
{
// Set the modelview matrix to identity too
for ( i = 0; i < NUM_MODEL_TRANSFORMS; ++i )
{
SetIdentityMatrix( m_boneMatrix[i] );
}
MatrixMode( MATERIAL_VIEW );
LoadIdentity();
MatrixMode( MATERIAL_PROJECTION );
LoadIdentity();
}
#ifdef _X360
m_DynamicState.m_bBuffer2Frames = m_bBuffer2FramesAhead;
SetRenderState( D3DRS_BUFFER2FRAMES, m_DynamicState.m_bBuffer2Frames );
#endif
m_DynamicState.m_Viewport.X = m_DynamicState.m_Viewport.Y =
m_DynamicState.m_Viewport.Width = m_DynamicState.m_Viewport.Height = 0xFFFFFFFF;
m_DynamicState.m_Viewport.MinZ = m_DynamicState.m_Viewport.MaxZ = 0.0;
// Be sure scissoring is off
m_DynamicState.m_RenderState[D3DRS_SCISSORTESTENABLE] = FALSE;
SetRenderState( D3DRS_SCISSORTESTENABLE, FALSE );
m_DynamicState.m_ScissorRect.left = -1;
m_DynamicState.m_ScissorRect.top = -1;
m_DynamicState.m_ScissorRect.right = -1;
m_DynamicState.m_ScissorRect.bottom = -1;
//SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE );
EnableFastClip( false );
float fFakePlane[4];
unsigned int iFakePlaneVal = 0xFFFFFFFF;
fFakePlane[0] = fFakePlane[1] = fFakePlane[2] = fFakePlane[3] = *((float *)&iFakePlaneVal);
SetFastClipPlane( fFakePlane ); //doing this to better wire up plane change detection
float zero[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
// Make sure that our state is dirty.
m_DynamicState.m_UserClipPlaneEnabled = 0;
m_DynamicState.m_UserClipPlaneChanged = 0;
m_DynamicState.m_UserClipLastUpdatedUsingFixedFunction = false;
for( i = 0; i < g_pHardwareConfig->MaxUserClipPlanes(); i++ )
{
// Make sure that our state is dirty.
m_DynamicState.m_UserClipPlaneWorld[i][0] = -1.0f;
m_DynamicState.m_UserClipPlaneProj[i][0] = -9999.0f;
m_DynamicState.m_UserClipPlaneEnabled |= ( 1 << i );
SetClipPlane( i, zero );
EnableClipPlane( i, false );
Assert( m_DynamicState.m_UserClipPlaneEnabled == 0 );
}
Assert( m_DynamicState.m_UserClipPlaneChanged == ((1 << g_pHardwareConfig->MaxUserClipPlanes()) - 1) );
m_DynamicState.m_FastClipEnabled = false;
m_DynamicState.m_bFastClipPlaneChanged = true;
// User clip override
m_DynamicState.m_bUserClipTransformOverride = false;
D3DXMatrixIdentity( &m_DynamicState.m_UserClipTransform );
// Viewport defaults to the window size
RECT windowRect;
#if !defined( DX_TO_GL_ABSTRACTION )
GetClientRect( (HWND)m_hWnd, &windowRect );
#else
toglGetClientRect( (VD3DHWND)m_hWnd, &windowRect );
#endif
ShaderViewport_t viewport;
viewport.Init( windowRect.left, windowRect.top,
windowRect.right - windowRect.left, windowRect.bottom - windowRect.top );
SetViewports( 1, &viewport );
// No render mesh
m_pRenderMesh = 0;
// Reset cached vertex decl
m_DynamicState.m_pVertexDecl = NULL;
// Reset the render target to be the normal backbuffer
if ( IsX360() )
{
m_hCachedRenderTarget = INVALID_SHADERAPI_TEXTURE_HANDLE;
m_bUsingSRGBRenderTarget = false;
}
AcquireInternalRenderTargets();
SetRenderTarget();
// Maintain vertex + pixel shader constant buffers
Vector4D *pVectorPixelShaderConstants = m_DesiredState.m_pVectorPixelShaderConstant;
int *pBooleanPixelShaderConstants = m_DesiredState.m_pBooleanPixelShaderConstant;
IntVector4D *pIntegerPixelShaderConstants = m_DesiredState.m_pIntegerPixelShaderConstant;
Vector4D *pVectorVertexShaderConstants = m_DesiredState.m_pVectorVertexShaderConstant;
int *pBooleanVertexShaderConstants = m_DesiredState.m_pBooleanVertexShaderConstant;
IntVector4D *pIntegerVertexShaderConstants = m_DesiredState.m_pIntegerVertexShaderConstant;
m_DesiredState = m_DynamicState;
m_DesiredState.m_pVectorPixelShaderConstant = pVectorPixelShaderConstants;
m_DesiredState.m_pBooleanPixelShaderConstant = pBooleanPixelShaderConstants;
m_DesiredState.m_pIntegerPixelShaderConstant = pIntegerPixelShaderConstants;
m_DesiredState.m_pVectorVertexShaderConstant = pVectorVertexShaderConstants;
m_DesiredState.m_pBooleanVertexShaderConstant = pBooleanVertexShaderConstants;
m_DesiredState.m_pIntegerVertexShaderConstant = pIntegerVertexShaderConstants;
if ( g_pHardwareConfig->Caps().m_SupportsPixelShaders )
{
if ( !bFullReset )
{
//Full resets init the values to defaults. Normal resets just leave them dirty.
if( g_pHardwareConfig->Caps().m_NumVertexShaderConstants != 0 )
SetVertexShaderConstant( 0, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), IsX360() ? 217 : g_pHardwareConfig->Caps().m_NumVertexShaderConstants, true ); //217 on X360 to play nice with fast blatting code
if( g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants != 0 )
SetIntegerVertexShaderConstant( 0, (int *)m_DesiredState.m_pIntegerVertexShaderConstant, g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants, true );
if( g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants != 0 )
SetBooleanVertexShaderConstant( 0, m_DesiredState.m_pBooleanVertexShaderConstant, g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants, true );
if( g_pHardwareConfig->Caps().m_NumPixelShaderConstants != 0 )
SetPixelShaderConstant( 0, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), g_pHardwareConfig->Caps().m_NumPixelShaderConstants, true );
if( g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants != 0 )
SetIntegerPixelShaderConstant( 0, (int *)m_DesiredState.m_pIntegerPixelShaderConstant, g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants, true );
if( g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants != 0 )
SetBooleanPixelShaderConstant( 0, m_DesiredState.m_pBooleanPixelShaderConstant, g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants, true );
}
}
RECORD_DEBUG_STRING( "END ResetRenderState" );
m_bResettingRenderState = false;
}
//-----------------------------------------------------------------------------
// Sets the default render state
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetDefaultState()
{
LOCK_SHADERAPI();
// NOTE: This used to be in the material system, but I want to avoid all the per pass/batch
// virtual function calls.
int numTextureStages = g_pHardwareConfig->GetTextureStageCount();
// FIXME: This is a brutal hack. We only need to load these transforms for fixed-function
// hardware. Cap the max here to 4.
if ( IsPC() )
{
numTextureStages = min( numTextureStages, 4 );
int i;
for( i = 0; i < numTextureStages; i++ )
{
CShaderAPIDx8::DisableTextureTransform( (TextureStage_t)i );
CShaderAPIDx8::MatrixMode( (MaterialMatrixMode_t)(MATERIAL_TEXTURE0 + i) );
CShaderAPIDx8::LoadIdentity( );
}
}
CShaderAPIDx8::MatrixMode( MATERIAL_MODEL );
CShaderAPIDx8::Color4ub( 255, 255, 255, 255 );
CShaderAPIDx8::ShadeMode( SHADER_SMOOTH );
CShaderAPIDx8::SetVertexShaderIndex( );
CShaderAPIDx8::SetPixelShaderIndex( );
MeshMgr()->MarkUnusedVertexFields( 0, 0, NULL );
}
//-----------------------------------------------------------------------------
//
// Methods related to vertex format
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Sets the vertex
//-----------------------------------------------------------------------------
inline void CShaderAPIDx8::SetVertexDecl( VertexFormat_t vertexFormat, bool bHasColorMesh, bool bUsingFlex, bool bUsingMorph )
{
VPROF("CShaderAPIDx8::SetVertexDecl");
IDirect3DVertexDeclaration9 *pDecl = FindOrCreateVertexDecl( vertexFormat, bHasColorMesh, bUsingFlex, bUsingMorph );
Assert( pDecl );
if ( ( pDecl != m_DynamicState.m_pVertexDecl ) && pDecl )
{
Dx9Device()->SetVertexDeclaration( pDecl );
m_DynamicState.m_pVertexDecl = pDecl;
}
}
//-----------------------------------------------------------------------------
//
// Methods related to vertex buffers
//
//-----------------------------------------------------------------------------
IMesh *CShaderAPIDx8::GetFlexMesh()
{
LOCK_SHADERAPI();
return MeshMgr()->GetFlexMesh();
}
//-----------------------------------------------------------------------------
// Gets the dynamic mesh
//-----------------------------------------------------------------------------
IMesh* CShaderAPIDx8::GetDynamicMesh( IMaterial* pMaterial, int nHWSkinBoneCount, bool buffered,
IMesh* pVertexOverride, IMesh* pIndexOverride )
{
Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() );
LOCK_SHADERAPI();
return MeshMgr()->GetDynamicMesh( pMaterial, 0, nHWSkinBoneCount, buffered, pVertexOverride, pIndexOverride );
}
IMesh* CShaderAPIDx8::GetDynamicMeshEx( IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount,
bool bBuffered, IMesh* pVertexOverride, IMesh* pIndexOverride )
{
Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() );
LOCK_SHADERAPI();
return MeshMgr()->GetDynamicMesh( pMaterial, vertexFormat, nHWSkinBoneCount, bBuffered, pVertexOverride, pIndexOverride );
}
//-----------------------------------------------------------------------------
// Returns the number of vertices we can render using the dynamic mesh
//-----------------------------------------------------------------------------
void CShaderAPIDx8::GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices )
{
LOCK_SHADERAPI();
MeshMgr()->GetMaxToRender( pMesh, bMaxUntilFlush, pMaxVerts, pMaxIndices );
}
int CShaderAPIDx8::GetMaxVerticesToRender( IMaterial *pMaterial )
{
pMaterial = ((IMaterialInternal *)pMaterial)->GetRealTimeVersion(); //always work with the realtime version internally
LOCK_SHADERAPI();
return MeshMgr()->GetMaxVerticesToRender( pMaterial );
}
int CShaderAPIDx8::GetMaxIndicesToRender( )
{
LOCK_SHADERAPI();
return MeshMgr()->GetMaxIndicesToRender( );
}
void CShaderAPIDx8::MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords )
{
LOCK_SHADERAPI();
MeshMgr()->MarkUnusedVertexFields( nFlags, nTexCoordCount, pUnusedTexCoords );
}
//-----------------------------------------------------------------------------
// Draws the mesh
//-----------------------------------------------------------------------------
void CShaderAPIDx8::DrawMesh( CMeshBase *pMesh )
{
VPROF("CShaderAPIDx8::DrawMesh");
if ( ShaderUtil()->GetConfig().m_bSuppressRendering )
return;
#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD )
PIXifyName( s_pPIXMaterialName, sizeof( s_pPIXMaterialName ), m_pMaterial->GetName() );
BeginPIXEvent( PIX_VALVE_ORANGE, s_pPIXMaterialName );
#endif
m_pRenderMesh = pMesh;
VertexFormat_t vertexFormat = m_pRenderMesh->GetVertexFormat();
SetVertexDecl( vertexFormat, m_pRenderMesh->HasColorMesh(), m_pRenderMesh->HasFlexMesh(), m_pMaterial->IsUsingVertexID() );
CommitStateChanges();
Assert( m_pRenderMesh && m_pMaterial );
m_pMaterial->DrawMesh( CompressionType( vertexFormat ) );
m_pRenderMesh = NULL;
#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD )
EndPIXEvent();
#endif
}
void CShaderAPIDx8::DrawWithVertexAndIndexBuffers( void )
{
VPROF("CShaderAPIDx8::DrawWithVertexAndIndexBuffers");
if ( ShaderUtil()->GetConfig().m_bSuppressRendering )
return;
#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD )
PIXifyName( s_pPIXMaterialName, sizeof( s_pPIXMaterialName ), m_pMaterial->GetName());
BeginPIXEvent( PIX_VALVE_ORANGE, s_pPIXMaterialName );
#endif
// m_pRenderMesh = pMesh;
// FIXME: need to make this deal with multiple streams, etc.
VertexFormat_t vertexFormat = MeshMgr()->GetCurrentVertexFormat();
SetVertexDecl( vertexFormat, false /*m_pRenderMesh->HasColorMesh()*/,
false /*m_pRenderMesh->HasFlexMesh()*/, false /*m_pRenderMesh->IsUsingMorphData()*/ );
CommitStateChanges();
if ( m_pMaterial )
{
m_pMaterial->DrawMesh( CompressionType( vertexFormat ) );
}
else
{
MeshMgr()->RenderPassWithVertexAndIndexBuffers();
}
// m_pRenderMesh = NULL;
#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD )
EndPIXEvent();
#endif
}
//-----------------------------------------------------------------------------
// Discards the vertex buffers
//-----------------------------------------------------------------------------
void CShaderAPIDx8::DiscardVertexBuffers()
{
MeshMgr()->DiscardVertexBuffers();
}
void CShaderAPIDx8::ForceHardwareSync_WithManagedTexture()
{
if ( IsX360() || !m_pFrameSyncTexture )
return;
// Set the default state for everything so we don't get more than we ask for here!
SetDefaultState();
D3DLOCKED_RECT rect;
tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ );
HRESULT hr = m_pFrameSyncTexture->LockRect( 0, &rect, NULL, 0 );
if ( SUCCEEDED( hr ) )
{
// modify..
unsigned long *pData = (unsigned long*)rect.pBits;
(*pData)++;
m_pFrameSyncTexture->UnlockRect( 0 );
// Now draw something with this texture.
DWORD iStage = 0;
IDirect3DBaseTexture9 *pOldTexture;
hr = Dx9Device()->GetTexture( iStage, &pOldTexture );
if ( SUCCEEDED( hr ) )
{
Dx9Device()->SetTexture( iStage, m_pFrameSyncTexture );
// Remember the old FVF.
DWORD oldFVF;
hr = Dx9Device()->GetFVF( &oldFVF );
if ( SUCCEEDED( hr ) )
{
// Set the new FVF.
Dx9Device()->SetFVF( D3DFVF_XYZ );
// Now, draw the simplest primitive D3D has ever seen.
unsigned short indices[3] = { 0, 1, 2 };
Vector verts[3] = {vec3_origin, vec3_origin, vec3_origin};
tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "DrawIndexedPrimitiveUP" );
Dx9Device()->DrawIndexedPrimitiveUP(
D3DPT_TRIANGLELIST,
0, // Min vertex index
3, // Num vertices used
1, // # primitives
indices, // indices
D3DFMT_INDEX16, // index format
verts, // Vertices
sizeof( Vector )// Vertex stride
);
Dx9Device()->SetFVF( oldFVF );
}
Dx9Device()->SetTexture( iStage, pOldTexture );
}
}
// If this assert fails, then we failed somewhere above.
AssertOnce( SUCCEEDED( hr ) );
}
void CShaderAPIDx8::UpdateFrameSyncQuery( int queryIndex, bool bIssue )
{
Assert(queryIndex < NUM_FRAME_SYNC_QUERIES);
// wait if already issued
if ( m_bQueryIssued[queryIndex] )
{
tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ );
double flStartTime = Plat_FloatTime();
BOOL dummyData = 0;
HRESULT hr = S_OK;
// NOTE: This fix helps out motherboards that are a little freaky.
// On such boards, sometimes the driver has to reset itself (an event which takes several seconds)
// and when that happens, the frame sync query object gets lost
for (;;)
{
hr = m_pFrameSyncQueryObject[queryIndex]->GetData( &dummyData, sizeof( dummyData ), D3DGETDATA_FLUSH );
if ( hr != S_FALSE )
break;
double flCurrTime = Plat_FloatTime();
// don't wait more than 200ms (5fps) for these
if ( flCurrTime - flStartTime > 0.200f )
break;
// Avoid burning a full core while waiting for the query. Spinning can actually harm performance
// because there might be driver threads that are trying to do work that end up starved, and the
// power drawn by the CPU may take away from the power available to the integrated graphics chip.
// A sleep of one millisecond should never be long enough to affect performance, especially since
// this should only trigger when the CPU is already ahead of the GPU.
// On L4D2/TF2 in GL mode this spinning was causing slowdowns.
ThreadSleep( 1 );
}
m_bQueryIssued[queryIndex] = false;
Assert(hr == S_OK || hr == D3DERR_DEVICELOST);
if ( hr == D3DERR_DEVICELOST )
{
MarkDeviceLost( );
return;
}
}
if ( bIssue )
{
m_pFrameSyncQueryObject[queryIndex]->Issue( D3DISSUE_END );
m_bQueryIssued[queryIndex] = true;
}
}
void CShaderAPIDx8::ForceHardwareSync( void )
{
LOCK_SHADERAPI();
VPROF( "CShaderAPIDx8::ForceHardwareSync" );
#ifdef DX_TO_GL_ABSTRACTION
if ( true )
#else
if ( !mat_frame_sync_enable.GetInt() )
#endif
return;
// need to flush the dynamic buffer and make sure the entire image is there
FlushBufferedPrimitives();
RECORD_COMMAND( DX8_HARDWARE_SYNC, 0 );
#if !defined( _X360 )
// How do you query dx9 for how many frames behind the hardware is or, alternatively, how do you tell the hardware to never be more than N frames behind?
// 1) The old QueryPendingFrameCount design was removed. It was
// a simple transaction with the driver through the
// GetDriverState, trivial for the drivers to lie. We came up
// with a much better scheme for tracking pending frames where
// the driver can not lie without a possible performance loss:
// use the asynchronous query system with D3DQUERYTYPE_EVENT and
// data size 0. When GetData returns S_OK for the query, you
// know that frame has finished.
if ( mat_frame_sync_force_texture.GetBool() )
{
ForceHardwareSync_WithManagedTexture();
}
else if ( m_pFrameSyncQueryObject[0] )
{
// FIXME: Could install a callback into the materialsystem to do something while waiting for
// the frame to finish (update sound, etc.)
// Disable VCR mode here or else it'll screw up (and we don't really care if this part plays back in exactly the same amount of time).
VCRSetEnabled( false );
m_currentSyncQuery ++;
if ( m_currentSyncQuery >= ARRAYSIZE(m_pFrameSyncQueryObject) )
{
m_currentSyncQuery = 0;
}
double fStart = Plat_FloatTime();
int waitIndex = ((m_currentSyncQuery + NUM_FRAME_SYNC_QUERIES) - (NUM_FRAME_SYNC_FRAMES_LATENCY+1)) % NUM_FRAME_SYNC_QUERIES;
UpdateFrameSyncQuery( waitIndex, false );
UpdateFrameSyncQuery( m_currentSyncQuery, true );
VCRSetEnabled( true );
}
#else
DWORD hFence = Dx9Device()->InsertFence();
Dx9Device()->BlockOnFence( hFence );
#endif
}
//-----------------------------------------------------------------------------
// Needs render state
//-----------------------------------------------------------------------------
void CShaderAPIDx8::QueueResetRenderState()
{
m_bResetRenderStateNeeded = true;
}
//-----------------------------------------------------------------------------
// Use this to begin and end the frame
//-----------------------------------------------------------------------------
void CShaderAPIDx8::BeginFrame()
{
LOCK_SHADERAPI();
if ( m_bResetRenderStateNeeded )
{
ResetRenderState( false );
m_bResetRenderStateNeeded = false;
}
#if ALLOW_SMP_ACCESS
Dx9Device()->SetASyncMode( mat_use_smp.GetInt() != 0 );
#endif
++m_CurrentFrame;
m_nTextureMemoryUsedLastFrame = 0;
}
void CShaderAPIDx8::EndFrame()
{
LOCK_SHADERAPI();
#if !defined( _X360 )
MEMCHECK;
#endif
ExportTextureList();
}
void CShaderAPIDx8::AddBufferToTextureList( const char *pName, D3DSURFACE_DESC &desc )
{
// ImageFormat imageFormat;
// imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format );
// if( imageFormat < 0 )
// {
// Assert( 0 );
// return;
// }
KeyValues *pSubKey = m_pDebugTextureList->CreateNewKey();
pSubKey->SetString( "Name", pName );
pSubKey->SetString( "TexGroup", TEXTURE_GROUP_RENDER_TARGET );
pSubKey->SetInt( "Size",
// ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height );
4 * desc.Width * desc.Height );
pSubKey->SetString( "Format", "32 bit buffer (hack)" );//ImageLoader::GetName( imageFormat ) );
pSubKey->SetInt( "Width", desc.Width );
pSubKey->SetInt( "Height", desc.Height );
pSubKey->SetInt( "BindsMax", 1 );
pSubKey->SetInt( "BindsFrame", 1 );
}
void CShaderAPIDx8::ExportTextureList()
{
if ( !m_bEnableDebugTextureList )
return;
if ( !m_pBackBufferSurface || !m_pZBufferSurface )
// Device vanished...
return;
m_nDebugDataExportFrame = m_CurrentFrame;
if ( IsPC() || !IsX360() )
{
if ( m_pDebugTextureList )
m_pDebugTextureList->deleteThis();
m_pDebugTextureList = new KeyValues( "TextureList" );
m_nTextureMemoryUsedTotal = 0;
m_nTextureMemoryUsedPicMip1 = 0;
m_nTextureMemoryUsedPicMip2 = 0;
for ( ShaderAPITextureHandle_t hTexture = m_Textures.Head() ; hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) )
{
Texture_t &tex = m_Textures[hTexture];
if ( !( tex.m_Flags & Texture_t::IS_ALLOCATED ) )
continue;
// Compute total texture memory usage
m_nTextureMemoryUsedTotal += tex.GetMemUsage();
// Compute picmip memory usage
{
int numBytes = tex.GetMemUsage();
if ( tex.m_NumLevels > 1 )
{
if ( tex.GetWidth() > 4 || tex.GetHeight() > 4 || tex.GetDepth() > 4 )
{
int topmipsize = ImageLoader::GetMemRequired( tex.GetWidth(), tex.GetHeight(), tex.GetDepth(), tex.GetImageFormat(), false );
numBytes -= topmipsize;
m_nTextureMemoryUsedPicMip1 += numBytes;
if ( tex.GetWidth() > 8 || tex.GetHeight() > 8 || tex.GetDepth() > 8 )
{
int othermipsizeRatio = ( ( tex.GetWidth() > 8 ) ? 2 : 1 ) * ( ( tex.GetHeight() > 8 ) ? 2 : 1 ) * ( ( tex.GetDepth() > 8 ) ? 2 : 1 );
int othermipsize = topmipsize / othermipsizeRatio;
numBytes -= othermipsize;
}
m_nTextureMemoryUsedPicMip1 += numBytes;
}
else
{
m_nTextureMemoryUsedPicMip1 += numBytes;
m_nTextureMemoryUsedPicMip2 += numBytes;
}
}
else
{
m_nTextureMemoryUsedPicMip1 += numBytes;
m_nTextureMemoryUsedPicMip2 += numBytes;
}
}
if ( !m_bDebugGetAllTextures &&
tex.m_LastBoundFrame != m_CurrentFrame )
continue;
if ( tex.m_LastBoundFrame != m_CurrentFrame )
tex.m_nTimesBoundThisFrame = 0;
KeyValues *pSubKey = m_pDebugTextureList->CreateNewKey();
pSubKey->SetString( "Name", tex.m_DebugName.String() );
pSubKey->SetString( "TexGroup", tex.m_TextureGroupName.String() );
pSubKey->SetInt( "Size", tex.GetMemUsage() );
if ( tex.GetCount() > 1 )
pSubKey->SetInt( "Count", tex.GetCount() );
pSubKey->SetString( "Format", ImageLoader::GetName( tex.GetImageFormat() ) );
pSubKey->SetInt( "Width", tex.GetWidth() );
pSubKey->SetInt( "Height", tex.GetHeight() );
pSubKey->SetInt( "BindsMax", tex.m_nTimesBoundMax );
pSubKey->SetInt( "BindsFrame", tex.m_nTimesBoundThisFrame );
}
D3DSURFACE_DESC desc;
m_pBackBufferSurface->GetDesc( &desc );
AddBufferToTextureList( "BACKBUFFER", desc );
AddBufferToTextureList( "FRONTBUFFER", desc );
// ImageFormat imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format );
// if( imageFormat >= 0 )
{
VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_frame_" TEXTURE_GROUP_RENDER_TARGET,
COUNTER_GROUP_TEXTURE_PER_FRAME,
// ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height );
2 * 4 * desc.Width * desc.Height ); // hack (times 2 for front and back buffer)
}
m_pZBufferSurface->GetDesc( &desc );
AddBufferToTextureList( "DEPTHBUFFER", desc );
// imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format );
// if( imageFormat >= 0 )
{
VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_frame_" TEXTURE_GROUP_RENDER_TARGET,
COUNTER_GROUP_TEXTURE_PER_FRAME,
// ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height );
4 * desc.Width * desc.Height ); // hack
}
}
#if defined( _X360 )
// toggle to do one shot transmission
m_bEnableDebugTextureList = false;
int numTextures = m_Textures.Count() + 3;
xTextureList_t* pXTextureList = (xTextureList_t *)_alloca( numTextures * sizeof( xTextureList_t ) );
memset( pXTextureList, 0, numTextures * sizeof( xTextureList_t ) );
numTextures = 0;
for ( ShaderAPITextureHandle_t hTexture = m_Textures.Head() ; hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) )
{
Texture_t &tex = m_Textures[hTexture];
if ( !m_bDebugGetAllTextures && tex.m_LastBoundFrame != m_CurrentFrame )
{
continue;
}
if ( !( tex.m_Flags & Texture_t::IS_ALLOCATED ) )
{
continue;
}
int refCount;
if ( tex.m_Flags & Texture_t::IS_DEPTH_STENCIL )
{
// interface forces us to ignore these
refCount = -1;
}
else
{
refCount = GetD3DTextureRefCount( CShaderAPIDx8::GetD3DTexture( hTexture ) );
}
pXTextureList[numTextures].pName = tex.m_DebugName.String();
pXTextureList[numTextures].size = tex.m_SizeBytes * tex.m_NumCopies;
pXTextureList[numTextures].pGroupName = tex.m_TextureGroupName.String();
pXTextureList[numTextures].pFormatName = D3DFormatName( ImageLoader::ImageFormatToD3DFormat( tex.GetImageFormat() ) );
pXTextureList[numTextures].width = tex.GetWidth();
pXTextureList[numTextures].height = tex.GetHeight();
pXTextureList[numTextures].depth = tex.GetDepth();
pXTextureList[numTextures].numLevels = tex.m_NumLevels;
pXTextureList[numTextures].binds = tex.m_nTimesBoundThisFrame;
pXTextureList[numTextures].refCount = refCount;
pXTextureList[numTextures].edram = ( tex.m_Flags & Texture_t::IS_RENDER_TARGET_SURFACE ) != 0;
pXTextureList[numTextures].procedural = tex.m_NumCopies > 1;
pXTextureList[numTextures].final = ( tex.m_Flags & Texture_t::IS_FINALIZED ) != 0;
pXTextureList[numTextures].failed = ( tex.m_Flags & Texture_t::IS_FAILED ) != 0;
numTextures++;
}
// build special entries for implicit surfaces/textures
D3DSURFACE_DESC desc;
m_pBackBufferSurface->GetDesc( &desc );
int size = ImageLoader::GetMemRequired(
desc.Width,
desc.Height,
0,
ImageLoader::D3DFormatToImageFormat( desc.Format ),
false );
pXTextureList[numTextures].pName = "_rt_BackBuffer";
pXTextureList[numTextures].size = size;
pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET_SURFACE;
pXTextureList[numTextures].pFormatName = D3DFormatName( desc.Format );
pXTextureList[numTextures].width = desc.Width;
pXTextureList[numTextures].height = desc.Height;
pXTextureList[numTextures].depth = 1;
pXTextureList[numTextures].binds = 1;
pXTextureList[numTextures].refCount = 1;
pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB( desc.Format );
pXTextureList[numTextures].edram = true;
numTextures++;
m_pZBufferSurface->GetDesc( &desc );
pXTextureList[numTextures].pName = "_rt_DepthBuffer";
pXTextureList[numTextures].size = size;
pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET_SURFACE;
pXTextureList[numTextures].pFormatName = D3DFormatName( desc.Format );
pXTextureList[numTextures].width = desc.Width;
pXTextureList[numTextures].height = desc.Height;
pXTextureList[numTextures].depth = 1;
pXTextureList[numTextures].binds = 1;
pXTextureList[numTextures].refCount = 1;
pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB( desc.Format );
pXTextureList[numTextures].edram = true;
numTextures++;
// front buffer resides in DDR
pXTextureList[numTextures].pName = "_rt_FrontBuffer";
pXTextureList[numTextures].size = size;
pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET;
pXTextureList[numTextures].pFormatName = D3DFormatName( desc.Format );
pXTextureList[numTextures].width = desc.Width;
pXTextureList[numTextures].height = desc.Height;
pXTextureList[numTextures].depth = 1;
pXTextureList[numTextures].binds = 1;
pXTextureList[numTextures].refCount = 1;
pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB( desc.Format );
numTextures++;
int totalMemory = 0;
for ( int i = 0; i < numTextures; i++ )
{
if ( pXTextureList[i].edram )
{
// skip edram based items
continue;
}
totalMemory += pXTextureList[i].size;
}
Msg( "Total D3D Texture Memory: %.2f MB\n", (float)totalMemory/( 1024.0f * 1024.0f ) );
// transmit to console
XBX_rTextureList( numTextures, pXTextureList );
#endif
}
//-----------------------------------------------------------------------------
// Releases/reloads resources when other apps want some memory
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ReleaseShaderObjects()
{
ReleaseInternalRenderTargets();
EvictManagedResourcesInternal();
// FIXME: Move into shaderdevice when textures move over.
#ifdef _DEBUG
// Helps to find the unreleased textures.
if ( TextureCount() > 0 )
{
ShaderAPITextureHandle_t hTexture;
for ( hTexture = m_Textures.Head(); hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) )
{
if ( GetTexture( hTexture ).m_NumCopies == 1 )
{
if ( GetTexture( hTexture ).GetTexture() )
{
Warning( "Didn't correctly clean up texture 0x%8.8x (%s)\n", hTexture, GetTexture( hTexture ).m_DebugName.String() );
}
}
else
{
for ( int k = GetTexture( hTexture ).m_NumCopies; --k >= 0; )
{
if ( GetTexture( hTexture ).GetTexture( k ) != 0 )
{
Warning( "Didn't correctly clean up texture 0x%8.8x (%s)\n", hTexture, GetTexture( hTexture ).m_DebugName.String() );
break;
}
}
}
}
}
#endif
Assert( TextureCount() == 0 );
}
void CShaderAPIDx8::RestoreShaderObjects()
{
AcquireInternalRenderTargets();
SetRenderTarget();
}
//--------------------------------------------------------------------
// PIX instrumentation routines
// Windows only for now. Turn these on with PIX_INSTRUMENTATION above
//--------------------------------------------------------------------
#if 0 // hack versions for OSX to be able to PIX log even when not built debug...
void CShaderAPIDx8::BeginPIXEvent( unsigned long color, const char* szName )
{
LOCK_SHADERAPI();
GLMBeginPIXEvent( szName ); // direct call no macro
return;
}
void CShaderAPIDx8::EndPIXEvent( void )
{
LOCK_SHADERAPI();
GLMEndPIXEvent(); // direct call no macro
return;
}
#else
#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD )
ConVar pix_break_on_event( "pix_break_on_event", "" );
#endif
void CShaderAPIDx8::BeginPIXEvent( unsigned long color, const char* szName )
{
#if ( defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) )
//LOCK_SHADERAPI();
const char *p = pix_break_on_event.GetString();
if ( p && V_strlen( p ) )
{
if ( V_stristr( szName, p ) != NULL )
{
DebuggerBreak();
}
}
#if defined ( DX_TO_GL_ABSTRACTION )
GLMBeginPIXEvent( szName );
#if defined( _WIN32 )
// AMD PerfStudio integration: Call into D3D9.DLL's D3DPERF_BeginEvent() (this gets intercepted by PerfStudio even in GL mode).
if ( g_pShaderDeviceMgrDx8->m_pBeginEvent )
{
wchar_t wszName[128];
mbstowcs( wszName, szName, 128 );
g_pShaderDeviceMgrDx8->m_pBeginEvent( 0x2F2F2F2F, wszName );
}
#endif
#elif defined(_X360 )
#ifndef _DEBUG
char szPIXEventName[32];
PIXifyName( szPIXEventName, szName );
PIXBeginNamedEvent( color, szPIXEventName );
#endif
#else // PC
if ( PIXError() )
return;
wchar_t wszName[128];
mbstowcs( wszName, szName, 128 );
// Fire the PIX event, trapping for errors...
if ( D3DPERF_BeginEvent( color, wszName ) < 0 )
{
Warning( "PIX error Beginning %s event\n", szName );
IncrementPIXError();
}
#endif
#endif // #if defined( PIX_INSTRUMENTATION )
}
void CShaderAPIDx8::EndPIXEvent( void )
{
#if ( defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) )
//LOCK_SHADERAPI();
#if defined ( DX_TO_GL_ABSTRACTION )
GLMEndPIXEvent();
#if defined( _WIN32 )
// AMD PerfStudio integration: Call into D3D9.DLL's D3DPERF_EndEvent() (this gets intercepted by PerfStudio even in GL mode).
if ( g_pShaderDeviceMgrDx8->m_pEndEvent )
{
g_pShaderDeviceMgrDx8->m_pEndEvent();
}
#endif
#elif defined( _X360 )
#ifndef _DEBUG
PIXEndNamedEvent();
#endif
#else // PC
if ( PIXError() )
return;
#if !defined( NVPERFHUD )
// Fire the PIX event, trapping for errors...
if ( D3DPERF_EndEvent() < 0 )
{
Warning("PIX error ending event\n");
IncrementPIXError();
}
#endif
#endif
#endif // #if defined( PIX_INSTRUMENTATION )
}
#endif
void CShaderAPIDx8::AdvancePIXFrame()
{
#if defined( PIX_INSTRUMENTATION )
// Ping PIX when this bool goes from false to true
if ( r_pix_start.GetBool() && (!m_bPixCapturing) )
{
StartPIXInstrumentation();
m_bPixCapturing = true;
}
// If we want to record frames...
if ( r_pix_recordframes.GetInt() )
{
if ( m_nPixFrame == 0 ) // First frame to record
{
StartPIXInstrumentation();
m_nPixFrame++;
}
else if( m_nPixFrame == r_pix_recordframes.GetInt() ) // Last frame to record
{
EndPIXInstrumentation();
r_pix_recordframes.SetValue(0);
m_nPixFrame = 0;
}
else
{
m_nPixFrame++; // Recording frames...
}
}
#endif
}
// No begin-end for this...use this to put discrete markers in the PIX stream
void CShaderAPIDx8::SetPIXMarker( unsigned long color, const char* szName )
{
#if defined( PIX_INSTRUMENTATION )
LOCK_SHADERAPI();
#if defined( DX_TO_GL_ABSTRACTION )
if ( g_pShaderDeviceMgrDx8->m_pSetMarker )
{
wchar_t wszName[128];
mbstowcs(wszName, szName, 128 );
g_pShaderDeviceMgrDx8->m_pSetMarker( 0x2F2F2F2F, wszName );
}
#elif defined( _X360 )
#ifndef _DEBUG
char szPIXMarkerName[32];
PIXifyName( szPIXMarkerName, szName );
PIXSetMarker( color, szPIXMarkerName );
#endif
#else // PC
if ( PIXError() )
return;
wchar_t wszName[128];
mbstowcs(wszName, szName, 128 );
D3DPERF_SetMarker( color, wszName );
#endif
#endif // PIX_INSTRUMENTATION
}
void CShaderAPIDx8::StartPIXInstrumentation()
{
#if defined( PIX_INSTRUMENTATION )
SetPIXMarker( PIX_VALVE_ORANGE, "Valve_PIX_Capture_Start" );
#endif
}
void CShaderAPIDx8::EndPIXInstrumentation()
{
#if defined( PIX_INSTRUMENTATION )
SetPIXMarker( PIX_VALVE_ORANGE, "Valve_PIX_Capture_End" );
#endif
}
void CShaderAPIDx8::IncrementPIXError()
{
#if defined( PIX_INSTRUMENTATION ) && !defined( NVPERFHUD )
m_nPIXErrorCount++;
if ( m_nPIXErrorCount >= MAX_PIX_ERRORS )
{
Warning( "Source engine built with PIX instrumentation, but PIX doesn't seem to have been used to instantiate the game, which is necessary on PC.\n" );
}
#endif
}
// Have we already hit several PIX errors?
bool CShaderAPIDx8::PIXError()
{
#if defined( PIX_INSTRUMENTATION ) && !defined( NVPERFHUD )
return m_nPIXErrorCount >= MAX_PIX_ERRORS;
#else
return false;
#endif
}
//-----------------------------------------------------------------------------
// Check for device lost
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ChangeVideoMode( const ShaderDeviceInfo_t &info )
{
if ( IsX360() )
return;
LOCK_SHADERAPI();
m_PendingVideoModeChangeConfig = info;
m_bPendingVideoModeChange = true;
if ( info.m_DisplayMode.m_nWidth != 0 && info.m_DisplayMode.m_nHeight != 0 )
{
m_nWindowWidth = info.m_DisplayMode.m_nWidth;
m_nWindowHeight = info.m_DisplayMode.m_nHeight;
}
}
//-----------------------------------------------------------------------------
// Compute fill rate
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ComputeFillRate()
{
if ( IsX360() )
{
// not valid
return;
}
static unsigned char* pBuf = 0;
int width, height;
GetWindowSize( width, height );
// Snapshot; look at total # pixels drawn...
if ( !pBuf )
{
int memSize = ShaderUtil()->GetMemRequired(
width,
height,
1,
IMAGE_FORMAT_RGB888,
false ) + 4;
pBuf = (unsigned char*)malloc( memSize );
}
ReadPixels(
0,
0,
width,
height,
pBuf,
IMAGE_FORMAT_RGB888 );
int mask = 0xFF;
int count = 0;
unsigned char* pRead = pBuf;
for (int i = 0; i < height; ++i)
{
for (int j = 0; j < width; ++j)
{
int val = *(int*)pRead;
count += (val & mask);
pRead += 3;
}
}
}
//-----------------------------------------------------------------------------
// Use this to get the mesh builder that allows us to modify vertex data
//-----------------------------------------------------------------------------
CMeshBuilder* CShaderAPIDx8::GetVertexModifyBuilder()
{
return &m_ModifyBuilder;
}
bool CShaderAPIDx8::InFlashlightMode() const
{
return ShaderUtil()->InFlashlightMode();
}
bool CShaderAPIDx8::InEditorMode() const
{
return ShaderUtil()->InEditorMode();
}
//-----------------------------------------------------------------------------
// Gets the bound morph's vertex format; returns 0 if no morph is bound
//-----------------------------------------------------------------------------
MorphFormat_t CShaderAPIDx8::GetBoundMorphFormat()
{
return ShaderUtil()->GetBoundMorphFormat();
}
//-----------------------------------------------------------------------------
// returns the current time in seconds...
//-----------------------------------------------------------------------------
double CShaderAPIDx8::CurrentTime() const
{
// FIXME: Return game time instead of real time!
// Or eliminate this altogether and put it into a material var
// (this is used by vertex modifiers in shader code at the moment)
return Plat_FloatTime();
}
//-----------------------------------------------------------------------------
// Methods called by the transition table that use dynamic state...
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ApplyZBias( const ShadowState_t& shaderState )
{
MaterialSystem_Config_t &config = ShaderUtil()->GetConfig();
float a = (config.m_SlopeScaleDepthBias_Decal != 0.0f) ? 1.0f / config.m_SlopeScaleDepthBias_Decal : 0.0f;
float b = (config.m_SlopeScaleDepthBias_Normal != 0.0f) ? 1.0f / config.m_SlopeScaleDepthBias_Normal : 0.0f;
float c = (config.m_DepthBias_Decal != 0.0f) ? 1.0f / config.m_DepthBias_Decal : 0.0f;
float d = (config.m_DepthBias_Normal != 0.0f) ? 1.0f / config.m_DepthBias_Normal : 0.0f;
// FIXME: No longer necessary; may be necessary if you want to use cat 4.3 drivers?
// GR - hack for R200
bool bPS14Only = g_pHardwareConfig->Caps().m_SupportsPixelShaders_1_4 && !g_pHardwareConfig->Caps().m_SupportsPixelShaders_2_0;
if( ( g_pHardwareConfig->Caps().m_VendorID == 0x1002 ) && bPS14Only )
{
// Slam to m_SlopeScaleDepthBias_Decal = 0, m_DepthBias_Decal = -4096
// which empirically is what appears to look good on r200
// NOTE: Slamming to 0 instead of -1.0 / 4096 because on Cat 4.9, WinXP, 8500,
// this causes the z values to be more than 50 units away from the original z values
a = 0.0f;
c = -1.0/4096.0;
}
// bias = (s * D3DRS_SLOPESCALEDEPTHBIAS) + D3DRS_DEPTHBIAS, where s is the maximum depth slope of the triangle being rendered
if ( g_pHardwareConfig->Caps().m_ZBiasAndSlopeScaledDepthBiasSupported )
{
float fSlopeScaleDepthBias, fDepthBias;
if ( shaderState.m_ZBias == SHADER_POLYOFFSET_DECAL )
{
fSlopeScaleDepthBias = a;
fDepthBias = c;
}
else if ( shaderState.m_ZBias == SHADER_POLYOFFSET_SHADOW_BIAS )
{
fSlopeScaleDepthBias = m_fShadowSlopeScaleDepthBias;
fDepthBias = m_fShadowDepthBias;
}
else // assume SHADER_POLYOFFSET_DISABLE
{
fSlopeScaleDepthBias = b;
fDepthBias = d;
}
if( ReverseDepthOnX360() )
{
fSlopeScaleDepthBias = -fSlopeScaleDepthBias;
fDepthBias = -fDepthBias;
}
SetRenderStateConstMacro( this, D3DRS_SLOPESCALEDEPTHBIAS, *((DWORD*) (&fSlopeScaleDepthBias)) );
SetRenderStateConstMacro( this, D3DRS_DEPTHBIAS, *((DWORD*) (&fDepthBias)) );
}
else
{
MarkAllUserClipPlanesDirty();
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |=
STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION;
}
}
void CShaderAPIDx8::ApplyTextureEnable( const ShadowState_t& state, int nSampler )
{
if ( state.m_SamplerState[nSampler].m_TextureEnable == SamplerState(nSampler).m_TextureEnable )
return;
if ( state.m_SamplerState[nSampler].m_TextureEnable )
{
SamplerState( nSampler ).m_TextureEnable = true;
// Should not be necessary/possible (SetTextureState() calls D3D9/DXAbstract, so the calling thread must already own the device.
//LOCK_SHADERAPI();
// Don't do this here!! It ends up giving us extra texture sets.
// We'll Assert in debug mode if you enable a texture stage
// but don't bind a texture.
// see CShaderAPIDx8::RenderPass() for this check.
// NOTE: We aren't doing this optimization quite yet. There are situations
// where you want a texture stage enabled for its texture coordinates, but
// you don't actually bind a texture (texmvspec for example.)
SetTextureState( (Sampler_t)nSampler, SamplerState(nSampler).m_BoundTexture, true );
}
else
{
SamplerState( nSampler ).m_TextureEnable = false;
SetTextureState( (Sampler_t)nSampler, INVALID_SHADERAPI_TEXTURE_HANDLE );
}
}
//-----------------------------------------------------------------------------
// Used to clear the transition table when we know it's become invalid.
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ClearSnapshots()
{
LOCK_SHADERAPI();
FlushBufferedPrimitives();
m_TransitionTable.Reset();
InitRenderState();
}
static void KillTranslation( D3DXMATRIX& mat )
{
mat[3] = 0.0f;
mat[7] = 0.0f;
mat[11] = 0.0f;
mat[12] = 0.0f;
mat[13] = 0.0f;
mat[14] = 0.0f;
mat[15] = 1.0f;
}
static void PrintMatrix( const char *name, const D3DXMATRIX& mat )
{
int row, col;
char buf[128];
Plat_DebugString( name );
Plat_DebugString( "\n" );
for( row = 0; row < 4; row++ )
{
Plat_DebugString( " " );
for( col = 0; col < 4; col++ )
{
sprintf( buf, "%f ", ( float )mat( row, col ) );
Plat_DebugString( buf );
}
Plat_DebugString( "\n" );
}
Plat_DebugString( "\n" );
}
//-----------------------------------------------------------------------------
// Gets the vertex format for a particular snapshot id
//-----------------------------------------------------------------------------
VertexFormat_t CShaderAPIDx8::ComputeVertexUsage( int num, StateSnapshot_t* pIds ) const
{
LOCK_SHADERAPI();
if (num == 0)
return 0;
// We don't have to all sorts of crazy stuff if there's only one snapshot
if ( num == 1 )
{
const ShadowShaderState_t& state = m_TransitionTable.GetSnapshotShader( pIds[0] );
return state.m_VertexUsage;
}
Assert( pIds );
// Aggregating vertex formats is a little tricky;
// For example, what do we do when two passes want user data?
// Can we assume they are the same? For now, I'm going to
// just print a warning in debug.
VertexCompressionType_t compression = VERTEX_COMPRESSION_INVALID;
int userDataSize = 0;
int numBones = 0;
int texCoordSize[VERTEX_MAX_TEXTURE_COORDINATES] = { 0, 0, 0, 0, 0, 0, 0, 0 };
int flags = 0;
for (int i = num; --i >= 0; )
{
const ShadowShaderState_t& state = m_TransitionTable.GetSnapshotShader( pIds[i] );
VertexFormat_t fmt = state.m_VertexUsage;
flags |= VertexFlags(fmt);
VertexCompressionType_t newCompression = CompressionType( fmt );
if ( ( compression != newCompression ) && ( compression != VERTEX_COMPRESSION_INVALID ) )
{
Warning("Encountered a material with two passes that specify different vertex compression types!\n");
compression = VERTEX_COMPRESSION_NONE; // Be safe, disable compression
}
int newNumBones = NumBoneWeights(fmt);
if ((numBones != newNumBones) && (newNumBones != 0))
{
if (numBones != 0)
{
Warning("Encountered a material with two passes that use different numbers of bones!\n");
}
numBones = newNumBones;
}
int newUserSize = UserDataSize(fmt);
if ((userDataSize != newUserSize) && (newUserSize != 0))
{
if (userDataSize != 0)
{
Warning("Encountered a material with two passes that use different user data sizes!\n");
}
userDataSize = newUserSize;
}
for ( int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j )
{
int newSize = TexCoordSize( (TextureStage_t)j, fmt );
if ( ( texCoordSize[j] != newSize ) && ( newSize != 0 ) )
{
if ( texCoordSize[j] != 0 )
{
Warning("Encountered a material with two passes that use different texture coord sizes!\n");
}
if ( texCoordSize[j] < newSize )
{
texCoordSize[j] = newSize;
}
}
}
}
return MeshMgr()->ComputeVertexFormat( flags, VERTEX_MAX_TEXTURE_COORDINATES,
texCoordSize, numBones, userDataSize );
}
VertexFormat_t CShaderAPIDx8::ComputeVertexFormat( int num, StateSnapshot_t* pIds ) const
{
LOCK_SHADERAPI();
VertexFormat_t fmt = ComputeVertexUsage( num, pIds );
return fmt;
}
//-----------------------------------------------------------------------------
// What fields in the morph do we actually use?
//-----------------------------------------------------------------------------
MorphFormat_t CShaderAPIDx8::ComputeMorphFormat( int numSnapshots, StateSnapshot_t* pIds ) const
{
LOCK_SHADERAPI();
MorphFormat_t format = 0;
for ( int i = 0; i < numSnapshots; ++i )
{
MorphFormat_t fmt = m_TransitionTable.GetSnapshotShader( pIds[i] ).m_MorphUsage;
format |= VertexFlags(fmt);
}
return format;
}
//-----------------------------------------------------------------------------
// Commits a range of vertex shader constants
//-----------------------------------------------------------------------------
static void CommitVertexShaderConstantRange( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState,
DynamicState_t &currentState, bool bForce, int nFirstConstant, int nCount )
{
if ( IsX360() )
{
// invalid code path for 360, not coded for 360 GPU contant awareness
Assert( 0 );
return;
}
int nFirstCommit = nFirstConstant;
int nCommitCount = 0;
for ( int i = 0; i < nCount; ++i )
{
int nVar = nFirstConstant + i;
bool bDifferentValue = bForce || ( desiredState.m_pVectorVertexShaderConstant[nVar] != currentState.m_pVectorVertexShaderConstant[nVar] );
if ( !bDifferentValue )
{
if ( nCommitCount != 0 )
{
// flush the prior range
pDevice->SetVertexShaderConstantF( nFirstCommit, desiredState.m_pVectorVertexShaderConstant[nFirstCommit].Base(), nCommitCount );
memcpy( &currentState.m_pVectorVertexShaderConstant[nFirstCommit],
&desiredState.m_pVectorVertexShaderConstant[nFirstCommit], nCommitCount * 4 * sizeof(float) );
}
// start of new range
nFirstCommit = nVar + 1;
nCommitCount = 0;
}
else
{
++nCommitCount;
}
}
if ( nCommitCount != 0 )
{
// flush range
pDevice->SetVertexShaderConstantF( nFirstCommit, desiredState.m_pVectorVertexShaderConstant[nFirstCommit].Base(), nCommitCount );
memcpy( &currentState.m_pVectorVertexShaderConstant[nFirstCommit],
&desiredState.m_pVectorVertexShaderConstant[nFirstCommit], nCommitCount * 4 * sizeof(float) );
}
}
//-----------------------------------------------------------------------------
// Gets the current buffered state... (debug only)
//-----------------------------------------------------------------------------
void CShaderAPIDx8::GetBufferedState( BufferedState_t& state )
{
memcpy( &state.m_Transform[0], &GetTransform(MATERIAL_MODEL), sizeof(D3DXMATRIX) );
memcpy( &state.m_Transform[1], &GetTransform(MATERIAL_VIEW), sizeof(D3DXMATRIX) );
memcpy( &state.m_Transform[2], &GetTransform(MATERIAL_PROJECTION), sizeof(D3DXMATRIX) );
memcpy( &state.m_Viewport, &m_DynamicState.m_Viewport, sizeof(state.m_Viewport) );
state.m_PixelShader = ShaderManager()->GetCurrentPixelShader();
state.m_VertexShader = ShaderManager()->GetCurrentVertexShader();
for (int i = 0; i < g_pHardwareConfig->GetSamplerCount(); ++i)
{
state.m_BoundTexture[i] = m_DynamicState.m_SamplerState[i].m_BoundTexture;
}
}
//-----------------------------------------------------------------------------
// constant color methods
//-----------------------------------------------------------------------------
void CShaderAPIDx8::Color3f( float r, float g, float b )
{
unsigned int color = D3DCOLOR_ARGB( 255, (int)(r * 255),
(int)(g * 255), (int)(b * 255) );
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color );
}
}
void CShaderAPIDx8::Color4f( float r, float g, float b, float a )
{
unsigned int color = D3DCOLOR_ARGB( (int)(a * 255), (int)(r * 255),
(int)(g * 255), (int)(b * 255) );
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color );
}
}
void CShaderAPIDx8::Color3fv( float const *c )
{
Assert( c );
unsigned int color = D3DCOLOR_ARGB( 255, (int)(c[0] * 255),
(int)(c[1] * 255), (int)(c[2] * 255) );
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color );
}
}
void CShaderAPIDx8::Color4fv( float const *c )
{
Assert( c );
unsigned int color = D3DCOLOR_ARGB( (int)(c[3] * 255), (int)(c[0] * 255),
(int)(c[1] * 255), (int)(c[2] * 255) );
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color );
}
}
void CShaderAPIDx8::Color3ub( unsigned char r, unsigned char g, unsigned char b )
{
unsigned int color = D3DCOLOR_ARGB( 255, r, g, b );
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color );
}
}
void CShaderAPIDx8::Color3ubv( unsigned char const* pColor )
{
Assert( pColor );
unsigned int color = D3DCOLOR_ARGB( 255, pColor[0], pColor[1], pColor[2] );
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color );
}
}
void CShaderAPIDx8::Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a )
{
unsigned int color = D3DCOLOR_ARGB( a, r, g, b );
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color );
}
}
void CShaderAPIDx8::Color4ubv( unsigned char const* pColor )
{
Assert( pColor );
unsigned int color = D3DCOLOR_ARGB( pColor[3], pColor[0], pColor[1], pColor[2] );
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color );
}
}
//-----------------------------------------------------------------------------
// The shade mode
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ShadeMode( ShaderShadeMode_t mode )
{
LOCK_SHADERAPI();
D3DSHADEMODE shadeMode = (mode == SHADER_FLAT) ? D3DSHADE_FLAT : D3DSHADE_GOURAUD;
if (m_DynamicState.m_ShadeMode != shadeMode)
{
m_DynamicState.m_ShadeMode = shadeMode;
SetRenderStateConstMacro( this, D3DRS_SHADEMODE, shadeMode );
}
}
//-----------------------------------------------------------------------------
// Buffering 2 frames ahead
//-----------------------------------------------------------------------------
void CShaderAPIDx8::EnableBuffer2FramesAhead( bool bEnable )
{
#ifdef _X360
m_bBuffer2FramesAhead = bEnable;
if ( bEnable != m_DynamicState.m_bBuffer2Frames )
{
SetRenderState( D3DRS_BUFFER2FRAMES, bEnable );
m_DynamicState.m_bBuffer2Frames = bEnable;
}
#endif
}
void CShaderAPIDx8::SetDepthFeatheringPixelShaderConstant( int iConstant, float fDepthBlendScale )
{
float fConstantValues[4];
if( IsX360() )
{
const D3DMATRIX &projMatrix = GetProjectionMatrix();
fConstantValues[0] = 50.0f / fDepthBlendScale;
fConstantValues[1] = 1.0f / projMatrix.m[2][2];
fConstantValues[2] = 1.0f / projMatrix.m[3][2];
fConstantValues[3] = projMatrix.m[2][2];
/*
D3DXMATRIX invProjMatrix;
D3DXMatrixInverse( &invProjMatrix, NULL, (D3DXMATRIX *)&projMatrix );
fConstantValues[1] = invProjMatrix.m[3][2];
fConstantValues[2] = invProjMatrix.m[3][3];
fConstantValues[3] = invProjMatrix.m[2][2];
*/
}
else
{
fConstantValues[0] = m_DynamicState.m_DestAlphaDepthRange / fDepthBlendScale;
fConstantValues[1] = fConstantValues[2] = fConstantValues[3] = 0.0f; //empty
}
SetPixelShaderConstant( iConstant, fConstantValues );
}
//-----------------------------------------------------------------------------
// Cull mode..
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetCullModeState( bool bEnable, D3DCULL nDesiredCullMode )
{
D3DCULL nCullMode = bEnable ? nDesiredCullMode : D3DCULL_NONE;
if ( nCullMode != m_DynamicState.m_CullMode )
{
SetRenderStateConstMacro( this, D3DRS_CULLMODE, nCullMode );
m_DynamicState.m_CullMode = nCullMode;
}
}
void CShaderAPIDx8::ApplyCullEnable( bool bEnable )
{
m_DynamicState.m_bCullEnabled = bEnable;
SetCullModeState( m_DynamicState.m_bCullEnabled, m_DynamicState.m_DesiredCullMode );
}
void CShaderAPIDx8::CullMode( MaterialCullMode_t nCullMode )
{
LOCK_SHADERAPI();
D3DCULL nNewCullMode;
switch( nCullMode )
{
case MATERIAL_CULLMODE_CCW:
// Culls backfacing polys (normal)
nNewCullMode = D3DCULL_CCW;
break;
case MATERIAL_CULLMODE_CW:
// Culls frontfacing polys
nNewCullMode = D3DCULL_CW;
break;
default:
Warning( "CullMode: invalid cullMode\n" );
return;
}
if (m_DynamicState.m_DesiredCullMode != nNewCullMode)
{
FlushBufferedPrimitives();
m_DynamicState.m_DesiredCullMode = nNewCullMode;
SetCullModeState( m_DynamicState.m_bCullEnabled, m_DynamicState.m_DesiredCullMode );
}
}
static ConVar mat_alphacoverage( "mat_alphacoverage", "1" );
void CShaderAPIDx8::ApplyAlphaToCoverage( bool bEnable )
{
if ( mat_alphacoverage.GetBool() )
{
if ( bEnable )
EnableAlphaToCoverage();
else
DisableAlphaToCoverage();
}
else
{
DisableAlphaToCoverage();
}
}
//-----------------------------------------------------------------------------
// Returns the current cull mode of the current material (for selection mode only)
//-----------------------------------------------------------------------------
D3DCULL CShaderAPIDx8::GetCullMode() const
{
Assert( m_pMaterial );
if ( m_pMaterial->GetMaterialVarFlag( MATERIAL_VAR_NOCULL ) )
return D3DCULL_NONE;
return m_DynamicState.m_DesiredCullMode;
}
void CShaderAPIDx8::SetRasterState( const ShaderRasterState_t& state )
{
// FIXME: Implement!
}
void CShaderAPIDx8::ForceDepthFuncEquals( bool bEnable )
{
LOCK_SHADERAPI();
if ( !g_pShaderDeviceDx8->IsDeactivated() )
{
m_TransitionTable.ForceDepthFuncEquals( bEnable );
}
}
void CShaderAPIDx8::OverrideDepthEnable( bool bEnable, bool bDepthEnable )
{
LOCK_SHADERAPI();
if ( !g_pShaderDeviceDx8->IsDeactivated() )
{
m_TransitionTable.OverrideDepthEnable( bEnable, bDepthEnable );
}
}
void CShaderAPIDx8::OverrideAlphaWriteEnable( bool bOverrideEnable, bool bAlphaWriteEnable )
{
LOCK_SHADERAPI();
if ( !g_pShaderDeviceDx8->IsDeactivated() )
{
m_TransitionTable.OverrideAlphaWriteEnable( bOverrideEnable, bAlphaWriteEnable );
}
}
void CShaderAPIDx8::OverrideColorWriteEnable( bool bOverrideEnable, bool bColorWriteEnable )
{
LOCK_SHADERAPI();
if ( !g_pShaderDeviceDx8->IsDeactivated() )
{
m_TransitionTable.OverrideColorWriteEnable( bOverrideEnable, bColorWriteEnable );
}
}
void CShaderAPIDx8::UpdateFastClipUserClipPlane( void )
{
float plane[4];
switch( m_DynamicState.m_HeightClipMode )
{
case MATERIAL_HEIGHTCLIPMODE_DISABLE:
EnableFastClip( false );
break;
case MATERIAL_HEIGHTCLIPMODE_RENDER_ABOVE_HEIGHT:
plane[0] = 0.0f;
plane[1] = 0.0f;
plane[2] = 1.0f;
plane[3] = m_DynamicState.m_HeightClipZ;
EnableFastClip( true );
SetFastClipPlane(plane);
break;
case MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT:
plane[0] = 0.0f;
plane[1] = 0.0f;
plane[2] = -1.0f;
plane[3] = -m_DynamicState.m_HeightClipZ;
EnableFastClip( true );
SetFastClipPlane(plane);
break;
}
}
void CShaderAPIDx8::SetHeightClipZ( float z )
{
LOCK_SHADERAPI();
if( z != m_DynamicState.m_HeightClipZ )
{
FlushBufferedPrimitives();
m_DynamicState.m_HeightClipZ = z;
UpdateVertexShaderFogParams();
UpdateFastClipUserClipPlane();
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |=
STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION;
}
}
void CShaderAPIDx8::SetHeightClipMode( MaterialHeightClipMode_t heightClipMode )
{
LOCK_SHADERAPI();
if( heightClipMode != m_DynamicState.m_HeightClipMode )
{
FlushBufferedPrimitives();
m_DynamicState.m_HeightClipMode = heightClipMode;
UpdateVertexShaderFogParams();
UpdateFastClipUserClipPlane();
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |=
STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION;
}
}
void CShaderAPIDx8::SetClipPlane( int index, const float *pPlane )
{
LOCK_SHADERAPI();
Assert( index < g_pHardwareConfig->MaxUserClipPlanes() && index >= 0 );
// NOTE: The plane here is specified in *world space*
// NOTE: This is done because they assume Ax+By+Cz+Dw = 0 (where w = 1 in real space)
// while we use Ax+By+Cz=D
D3DXPLANE plane;
plane.a = pPlane[0];
plane.b = pPlane[1];
plane.c = pPlane[2];
plane.d = -pPlane[3];
if ( plane != m_DynamicState.m_UserClipPlaneWorld[index] )
{
FlushBufferedPrimitives();
m_DynamicState.m_UserClipPlaneChanged |= ( 1 << index );
m_DynamicState.m_UserClipPlaneWorld[index] = plane;
}
}
//-----------------------------------------------------------------------------
// Converts a D3DXMatrix to a VMatrix and back
//-----------------------------------------------------------------------------
void CShaderAPIDx8::D3DXMatrixToVMatrix( const D3DXMATRIX &in, VMatrix &out )
{
MatrixTranspose( *(const VMatrix*)&in, out );
}
void CShaderAPIDx8::VMatrixToD3DXMatrix( const VMatrix &in, D3DXMATRIX &out )
{
MatrixTranspose( in, *(VMatrix*)&out );
}
//-----------------------------------------------------------------------------
// Mark all user clip planes as being dirty
//-----------------------------------------------------------------------------
void CShaderAPIDx8::MarkAllUserClipPlanesDirty()
{
m_DynamicState.m_UserClipPlaneChanged |= ( 1 << g_pHardwareConfig->MaxUserClipPlanes() ) - 1;
m_DynamicState.m_bFastClipPlaneChanged = true;
}
//-----------------------------------------------------------------------------
// User clip plane override
//-----------------------------------------------------------------------------
void CShaderAPIDx8::EnableUserClipTransformOverride( bool bEnable )
{
LOCK_SHADERAPI();
if ( m_DynamicState.m_bUserClipTransformOverride != bEnable )
{
FlushBufferedPrimitives();
m_DynamicState.m_bUserClipTransformOverride = bEnable;
MarkAllUserClipPlanesDirty();
}
}
//-----------------------------------------------------------------------------
// Specify user clip transform
//-----------------------------------------------------------------------------
void CShaderAPIDx8::UserClipTransform( const VMatrix &worldToProjection )
{
LOCK_SHADERAPI();
D3DXMATRIX dxWorldToProjection;
VMatrixToD3DXMatrix( worldToProjection, dxWorldToProjection );
if ( m_DynamicState.m_UserClipTransform != dxWorldToProjection )
{
m_DynamicState.m_UserClipTransform = dxWorldToProjection;
if ( m_DynamicState.m_bUserClipTransformOverride )
{
FlushBufferedPrimitives();
MarkAllUserClipPlanesDirty();
}
}
}
//-----------------------------------------------------------------------------
// Enables a user clip plane
//-----------------------------------------------------------------------------
void CShaderAPIDx8::EnableClipPlane( int index, bool bEnable )
{
LOCK_SHADERAPI();
Assert( index < g_pHardwareConfig->MaxUserClipPlanes() && index >= 0 );
if( ( m_DynamicState.m_UserClipPlaneEnabled & ( 1 << index ) ? true : false ) != bEnable )
{
FlushBufferedPrimitives();
if( bEnable )
{
m_DynamicState.m_UserClipPlaneEnabled |= ( 1 << index );
}
else
{
m_DynamicState.m_UserClipPlaneEnabled &= ~( 1 << index );
}
SetRenderStateConstMacro( this, D3DRS_CLIPPLANEENABLE, m_DynamicState.m_UserClipPlaneEnabled );
}
}
//-----------------------------------------------------------------------------
// Recomputes the fast-clip plane matrices based on the current fast-clip plane
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitFastClipPlane( )
{
// Don't bother recomputing if unchanged or disabled
if ( !m_DynamicState.m_bFastClipPlaneChanged || !m_DynamicState.m_FastClipEnabled )
return;
m_DynamicState.m_bFastClipPlaneChanged = false;
D3DXMatrixIdentity( &m_CachedFastClipProjectionMatrix );
// Compute worldToProjection - need inv. transpose for transforming plane.
D3DXMATRIX viewToProjInvTrans, viewToProjInv, viewToProj = GetTransform(MATERIAL_PROJECTION);
viewToProj._43 *= 0.5f; // pull in zNear because the shear in effect
// moves it out: clipping artifacts when looking down at water
// could occur if this multiply is not done
D3DXMATRIX worldToViewInvTrans, worldToViewInv, worldToView = GetUserClipTransform();
D3DXMatrixInverse( &worldToViewInv, NULL, &worldToView );
D3DXMatrixTranspose( &worldToViewInvTrans, &worldToViewInv );
D3DXMatrixInverse( &viewToProjInv, NULL, &viewToProj );
D3DXMatrixTranspose( &viewToProjInvTrans, &viewToProjInv );
D3DXPLANE plane;
D3DXPlaneNormalize( &plane, &m_DynamicState.m_FastClipPlane );
D3DXVECTOR4 clipPlane( plane.a, plane.b, plane.c, plane.d );
// transform clip plane into view space
D3DXVec4Transform( &clipPlane, &clipPlane, &worldToViewInvTrans );
// transform clip plane into projection space
D3DXVec4Transform( &clipPlane, &clipPlane, &viewToProjInvTrans );
#define ALLOW_FOR_FASTCLIPDUMPS 0
#if (ALLOW_FOR_FASTCLIPDUMPS == 1)
static ConVar shader_dumpfastclipprojectioncoords( "shader_dumpfastclipprojectioncoords", "0", 0, "dump fast clip projected matrix" );
if( shader_dumpfastclipprojectioncoords.GetBool() )
DevMsg( "Fast clip plane projected coordinates: %f %f %f %f", clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w );
#endif
if( (clipPlane.z * clipPlane.w) <= -0.4f ) // a plane with (z*w) > -0.4 at this point is behind the camera and will cause graphical glitches. Toss it. (0.4 found through experimentation)
{
#if (ALLOW_FOR_FASTCLIPDUMPS == 1)
if( shader_dumpfastclipprojectioncoords.GetBool() )
DevMsg( " %f %f %f %f\n", clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w );
#endif
D3DXVec4Normalize( &clipPlane, &clipPlane );
//if ((fabs(clipPlane.z) > 0.01) && (fabs(clipPlane.w) > 0.01f))
{
// put projection space clip plane in Z column
m_CachedFastClipProjectionMatrix._13 = clipPlane.x;
m_CachedFastClipProjectionMatrix._23 = clipPlane.y;
m_CachedFastClipProjectionMatrix._33 = clipPlane.z;
m_CachedFastClipProjectionMatrix._43 = clipPlane.w;
}
}
#if (ALLOW_FOR_FASTCLIPDUMPS == 1)
else
{
if( shader_dumpfastclipprojectioncoords.GetBool() )
DevMsg( "\n" ); //finish off the line above
}
#endif
m_CachedFastClipProjectionMatrix = viewToProj * m_CachedFastClipProjectionMatrix;
// Update the cached polyoffset matrix (with clip) too:
ComputePolyOffsetMatrix( m_CachedFastClipProjectionMatrix, m_CachedFastClipPolyOffsetProjectionMatrix );
}
//-----------------------------------------------------------------------------
// Sets the fast-clip plane; but doesn't update the matrices
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetFastClipPlane( const float *pPlane )
{
LOCK_SHADERAPI();
D3DXPLANE plane;
plane.a = pPlane[0];
plane.b = pPlane[1];
plane.c = pPlane[2];
plane.d = -pPlane[3];
if ( plane != m_DynamicState.m_FastClipPlane )
{
FlushBufferedPrimitives();
UpdateVertexShaderFogParams();
m_DynamicState.m_FastClipPlane = plane;
// Mark a dirty bit so when it comes time to commit view + projection transforms,
// we also update the fast clip matrices
m_DynamicState.m_bFastClipPlaneChanged = true;
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |=
STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION;
}
}
//-----------------------------------------------------------------------------
// Enables/disables fast-clip mode
//-----------------------------------------------------------------------------
void CShaderAPIDx8::EnableFastClip( bool bEnable )
{
LOCK_SHADERAPI();
if( m_DynamicState.m_FastClipEnabled != bEnable )
{
FlushBufferedPrimitives();
UpdateVertexShaderFogParams();
m_DynamicState.m_FastClipEnabled = bEnable;
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |=
STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION;
}
}
/*
// -----------------------------------------------------------------------------
// SetInvariantClipVolume - This routine takes six planes as input and sets the
// appropriate Direct3D user clip plane state
// What we mean by "invariant clipping" here is that certain devices implement
// user clip planes at the raster level, which means that multi-pass rendering
// where one pass is unclipped (such as base geometry) and another pass *IS*
// clipped (such as flashlight geometry), there is no z-fighting since the
// clipping is implemented at the raster level in an "invariant" way
// -----------------------------------------------------------------------------
void CShaderAPIDx8::SetInvariantClipVolume( Frustum_t *pFrustumPlanes )
{
// Only do this on modern nVidia hardware, which does invariant clipping
if ( m_VendorID == VENDORID_NVIDIA )
{
if ( pFrustumPlanes )
{
// if ()
// {
//
// }
for (int i=0; i<6; i++)
{
const cplane_t *pPlane = pFrustumPlanes->GetPlane(i);
SetClipPlane( i, (float *) &pPlane->normal );
EnableClipPlane( i, true );
// FRUSTUM_RIGHT = 0,
// FRUSTUM_LEFT = 1,
// FRUSTUM_TOP = 2,
// FRUSTUM_BOTTOM = 3,
// FRUSTUM_NEARZ = 4,
// FRUSTUM_FARZ = 5,
}
}
else // NULL disables the invariant clip volume...
{
for (int i=0; i<6; i++)
{
EnableClipPlane( i, false );
}
}
}
}
*/
//-----------------------------------------------------------------------------
// Vertex blending
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetVertexBlendState( int numBones )
{
if (numBones < 0)
{
numBones = m_DynamicState.m_NumBones;
}
// For fixed-function, the number of weights is actually one less than
// the number of bones
if (numBones > 0)
--numBones;
bool normalizeNormals = true;
D3DVERTEXBLENDFLAGS vertexBlend;
switch(numBones)
{
case 0:
vertexBlend = D3DVBF_DISABLE;
normalizeNormals = false;
break;
case 1:
vertexBlend = D3DVBF_1WEIGHTS;
break;
case 2:
vertexBlend = D3DVBF_2WEIGHTS;
break;
case 3:
vertexBlend = D3DVBF_3WEIGHTS;
break;
default:
vertexBlend = D3DVBF_DISABLE;
Assert(0);
break;
}
if (m_DynamicState.m_VertexBlend != vertexBlend)
{
m_DynamicState.m_VertexBlend = vertexBlend;
SetSupportedRenderState( D3DRS_VERTEXBLEND, vertexBlend );
}
// Activate normalize normals when skinning is on
if (m_DynamicState.m_NormalizeNormals != normalizeNormals)
{
m_DynamicState.m_NormalizeNormals = normalizeNormals;
SetSupportedRenderState( D3DRS_NORMALIZENORMALS, normalizeNormals );
}
}
//-----------------------------------------------------------------------------
// Vertex blending
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetNumBoneWeights( int numBones )
{
LOCK_SHADERAPI();
if (m_DynamicState.m_NumBones != numBones)
{
FlushBufferedPrimitives();
m_DynamicState.m_NumBones = numBones;
if ( m_TransitionTable.CurrentShadowState() )
{
SetVertexBlendState( m_TransitionTable.CurrentShadowState()->m_VertexBlendEnable ? -1 : 0 );
}
}
}
void CShaderAPIDx8::EnableHWMorphing( bool bEnable )
{
LOCK_SHADERAPI();
if ( m_DynamicState.m_bHWMorphingEnabled != bEnable )
{
FlushBufferedPrimitives();
m_DynamicState.m_bHWMorphingEnabled = bEnable;
}
}
void CShaderAPIDx8::EnabledSRGBWrite( bool bEnabled )
{
m_DynamicState.m_bSRGBWritesEnabled = bEnabled;
if ( g_pHardwareConfig->SupportsPixelShaders_2_b() )
{
UpdatePixelFogColorConstant();
if ( bEnabled && g_pHardwareConfig->NeedsShaderSRGBConversion() )
BindTexture( SHADER_SAMPLER15, m_hLinearToGammaTableTexture );
else
BindTexture( SHADER_SAMPLER15, m_hLinearToGammaTableIdentityTexture );
}
}
#if defined( _X360 )
void CShaderAPIDx8::ApplySRGBReadState( int iTextureStage, bool bSRGBReadEnabled )
{
Sampler_t sampler = (Sampler_t)iTextureStage;
SamplerState_t &samplerState = SamplerState( sampler );
samplerState.m_SRGBReadEnable = bSRGBReadEnabled;
if ( ( samplerState.m_BoundTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) || !samplerState.m_TextureEnable )
{
return;
}
IDirect3DBaseTexture *pBindTexture = CShaderAPIDx8::GetD3DTexture( samplerState.m_BoundTexture );
if ( !pBindTexture )
{
return;
}
DWORD linearFormatBackup = pBindTexture->Format.dword[0]; //if we convert to srgb format, we need the original format for reverting. We only need the first DWORD of GPUTEXTURE_FETCH_CONSTANT.
if ( bSRGBReadEnabled )
{
pBindTexture->Format.SignX = pBindTexture->Format.SignY = pBindTexture->Format.SignZ = 3; //convert to srgb format for the bind. This effectively emulates the old srgb read sampler state
}
Dx9Device()->SetTexture( sampler, pBindTexture );
// copy back the format in case we changed it
pBindTexture->Format.dword[0] = linearFormatBackup;
}
#endif
//-----------------------------------------------------------------------------
// Fog methods...
//-----------------------------------------------------------------------------
void CShaderAPIDx8::UpdatePixelFogColorConstant( void )
{
Assert( HardwareConfig()->SupportsPixelShaders_2_b() );
float fogColor[4];
switch( GetPixelFogMode() )
{
case MATERIAL_FOG_NONE:
{
for( int i = 0; i != 3; ++i )
fogColor[i] = 0.0f;
}
break;
case MATERIAL_FOG_LINEAR:
{
//setup the fog for mixing linear fog in the pixel shader so that it emulates ff range fog
for( int i = 0; i != 3; ++i )
fogColor[i] = m_DynamicState.m_PixelFogColor[i];
if( m_DynamicState.m_bSRGBWritesEnabled )
{
//since the fog color will assuredly get converted from linear to gamma, we should probably convert it from gamma to linear
for( int i = 0; i != 3; ++i )
fogColor[i] = GammaToLinear_HardwareSpecific( fogColor[i] );
}
if( (!m_DynamicState.m_bFogGammaCorrectionDisabled) && (g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER) )
{
for( int i = 0; i != 3; ++i )
fogColor[i] *= m_ToneMappingScale.x; // Linear
}
}
break;
case MATERIAL_FOG_LINEAR_BELOW_FOG_Z:
{
//water fog has been around a while and has never tonemap scaled, and has always been in linear space
if( g_pHardwareConfig->NeedsShaderSRGBConversion() )
{
//srgb in ps2b uses the 2.2 curve
for( int i = 0; i != 3; ++i )
fogColor[i] = pow( m_DynamicState.m_PixelFogColor[i], 2.2f );
}
else
{
for( int i = 0; i != 3; ++i )
fogColor[i] = GammaToLinear_HardwareSpecific( m_DynamicState.m_PixelFogColor[i] ); //this is how water fog color has always been setup in the past
}
if( (!m_DynamicState.m_bFogGammaCorrectionDisabled) && (g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER) )
{
for( int i = 0; i != 3; ++i )
fogColor[i] *= m_ToneMappingScale.x; // Linear
}
}
break;
NO_DEFAULT;
};
fogColor[3] = 1.0f / m_DynamicState.m_DestAlphaDepthRange;
SetPixelShaderConstant( LINEAR_FOG_COLOR, fogColor );
}
void CShaderAPIDx8::ApplyFogMode( ShaderFogMode_t fogMode, bool bSRGBWritesEnabled, bool bDisableFogGammaCorrection )
{
HDRType_t hdrType = g_pHardwareConfig->GetHDRType();
if ( fogMode == SHADER_FOGMODE_DISABLED )
{
if( hdrType != HDR_TYPE_FLOAT )
{
FogMode( MATERIAL_FOG_NONE );
}
if( m_DelayedShaderConstants.iPixelShaderFogParams != -1 )
SetPixelShaderFogParams( m_DelayedShaderConstants.iPixelShaderFogParams, fogMode );
return;
}
bool bShouldGammaCorrect = true; // By default, we'll gamma correct.
unsigned char r = 0, g = 0, b = 0; // Black fog
if( hdrType != HDR_TYPE_FLOAT )
{
FogMode( m_SceneFogMode );
}
if( m_DelayedShaderConstants.iPixelShaderFogParams != -1 )
SetPixelShaderFogParams( m_DelayedShaderConstants.iPixelShaderFogParams, fogMode );
switch( fogMode )
{
case SHADER_FOGMODE_BLACK: // Additive decals
bShouldGammaCorrect = false;
break;
case SHADER_FOGMODE_OO_OVERBRIGHT:
case SHADER_FOGMODE_GREY: // Mod2x decals
r = g = b = 128;
break;
case SHADER_FOGMODE_WHITE: // Multiplicative decals
r = g = b = 255;
bShouldGammaCorrect = false;
break;
case SHADER_FOGMODE_FOGCOLOR:
GetSceneFogColor( &r, &g, &b ); // Scene fog color
break;
NO_DEFAULT
}
bShouldGammaCorrect &= !bDisableFogGammaCorrection;
m_DynamicState.m_bFogGammaCorrectionDisabled = !bShouldGammaCorrect;
D3DCOLOR color;
if ( bShouldGammaCorrect )
{
color = ComputeGammaCorrectedFogColor( r, g, b, bSRGBWritesEnabled );
}
else
{
color = D3DCOLOR_ARGB( 255, r, g, b );
}
const float fColorScale = 1.0f / 255.0f;
m_DynamicState.m_PixelFogColor[0] = (float)(r) * fColorScale;
m_DynamicState.m_PixelFogColor[1] = (float)(g) * fColorScale;
m_DynamicState.m_PixelFogColor[2] = (float)(b) * fColorScale;
if( g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_b() )
{
UpdatePixelFogColorConstant();
}
if ( color != m_DynamicState.m_FogColor )
{
if( hdrType != HDR_TYPE_FLOAT )
{
m_DynamicState.m_FogColor = color;
SetRenderStateConstMacro( this, D3DRS_FOGCOLOR, m_DynamicState.m_FogColor );
}
}
}
void CShaderAPIDx8::SceneFogMode( MaterialFogMode_t fogMode )
{
LOCK_SHADERAPI();
if( m_SceneFogMode != fogMode )
{
FlushBufferedPrimitives();
m_SceneFogMode = fogMode;
if ( m_TransitionTable.CurrentShadowState() )
{
// Get the shadow state in sync since it depends on SceneFogMode.
ApplyFogMode( m_TransitionTable.CurrentShadowState()->m_FogMode, m_TransitionTable.CurrentShadowState()->m_SRGBWriteEnable, m_TransitionTable.CurrentShadowState()->m_bDisableFogGammaCorrection );
}
}
}
MaterialFogMode_t CShaderAPIDx8::GetSceneFogMode()
{
return m_SceneFogMode;
}
MaterialFogMode_t CShaderAPIDx8::GetPixelFogMode()
{
if( ShouldUsePixelFogForMode( m_SceneFogMode ) )
return m_SceneFogMode;
else
return MATERIAL_FOG_NONE;
}
int CShaderAPIDx8::GetPixelFogCombo( void )
{
if( (m_SceneFogMode != MATERIAL_FOG_NONE) && ShouldUsePixelFogForMode( m_SceneFogMode ) )
return m_SceneFogMode - 1; //PIXELFOGTYPE dynamic combos are shifted down one. MATERIAL_FOG_NONE is simulated by range fog with rigged parameters. Gets rid of a dynamic combo across the ps2x set
else
return MATERIAL_FOG_NONE;
}
static ConVar r_pixelfog( "r_pixelfog", "1" );
bool CShaderAPIDx8::ShouldUsePixelFogForMode( MaterialFogMode_t fogMode )
{
if( fogMode == MATERIAL_FOG_NONE )
return false;
if( IsX360() || IsPosix() ) // Always use pixel fog on X360 and Posix
return true;
if( g_pHardwareConfig->Caps().m_nDXSupportLevel < 90 ) //pixel fog not available until at least ps2.0
return false;
switch( m_SceneFogMode )
{
case MATERIAL_FOG_LINEAR:
return (g_pHardwareConfig->SupportsPixelShaders_2_b() && //lightmappedgeneric_ps20.fxc can't handle the instruction count
r_pixelfog.GetBool()); //use pixel fog if preferred
case MATERIAL_FOG_LINEAR_BELOW_FOG_Z:
return true;
};
return false;
}
//-----------------------------------------------------------------------------
// Fog methods...
//-----------------------------------------------------------------------------
void CShaderAPIDx8::FogMode( MaterialFogMode_t fogMode )
{
bool bFogEnable;
if ( IsX360() )
{
// FF fog not applicable on 360
return;
}
m_DynamicState.m_SceneFog = fogMode;
switch( fogMode )
{
default:
Assert( 0 );
// fall through
case MATERIAL_FOG_NONE:
bFogEnable = false;
break;
case MATERIAL_FOG_LINEAR:
// use vertex fog to achieve linear range fog
bFogEnable = true;
break;
case MATERIAL_FOG_LINEAR_BELOW_FOG_Z:
// use pixel fog on 9.0 and greater for height fog
bFogEnable = g_pHardwareConfig->Caps().m_nDXSupportLevel < 90;
break;
}
if( ShouldUsePixelFogForMode( fogMode ) )
{
bFogEnable = false; //disable FF fog when doing fog in the pixel shader
}
#if 0
// HACK - do this to disable fog always
bFogEnable = false;
m_DynamicState.m_SceneFog = MATERIAL_FOG_NONE;
#endif
// These two are always set to this, so don't bother setting them.
// We are always using vertex fog.
// SetRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE );
// SetRenderState( D3DRS_RANGEFOGENABLE, false );
// Set fog enable if it's different than before.
if ( bFogEnable != m_DynamicState.m_FogEnable )
{
SetSupportedRenderState( D3DRS_FOGENABLE, bFogEnable );
m_DynamicState.m_FogEnable = bFogEnable;
}
}
void CShaderAPIDx8::FogStart( float fStart )
{
LOCK_SHADERAPI();
if (fStart != m_DynamicState.m_FogStart)
{
// need to flush the dynamic buffer
FlushBufferedPrimitives();
SetRenderStateConstMacro( this, D3DRS_FOGSTART, *((DWORD*)(&fStart)));
m_VertexShaderFogParams[0] = fStart;
UpdateVertexShaderFogParams();
m_DynamicState.m_FogStart = fStart;
}
}
void CShaderAPIDx8::FogEnd( float fEnd )
{
LOCK_SHADERAPI();
if (fEnd != m_DynamicState.m_FogEnd)
{
// need to flush the dynamic buffer
FlushBufferedPrimitives();
SetRenderStateConstMacro( this, D3DRS_FOGEND, *((DWORD*)(&fEnd)));
m_VertexShaderFogParams[1] = fEnd;
UpdateVertexShaderFogParams();
m_DynamicState.m_FogEnd = fEnd;
}
}
void CShaderAPIDx8::SetFogZ( float fogZ )
{
LOCK_SHADERAPI();
if (fogZ != m_DynamicState.m_FogZ)
{
// need to flush the dynamic buffer
FlushBufferedPrimitives();
m_DynamicState.m_FogZ = fogZ;
UpdateVertexShaderFogParams();
}
}
void CShaderAPIDx8::FogMaxDensity( float flMaxDensity )
{
LOCK_SHADERAPI();
if (flMaxDensity != m_DynamicState.m_FogMaxDensity)
{
// need to flush the dynamic buffer
FlushBufferedPrimitives();
// SetRenderState(D3DRS_FOGDENSITY, *((DWORD*)(&flMaxDensity))); // ??? do I need to to this ???
m_flFogMaxDensity = flMaxDensity;
UpdateVertexShaderFogParams();
m_DynamicState.m_FogMaxDensity = flMaxDensity;
}
}
void CShaderAPIDx8::GetFogDistances( float *fStart, float *fEnd, float *fFogZ )
{
LOCK_SHADERAPI();
if( fStart )
*fStart = m_DynamicState.m_FogStart;
if( fEnd )
*fEnd = m_DynamicState.m_FogEnd;
if( fFogZ )
*fFogZ = m_DynamicState.m_FogZ;
}
void CShaderAPIDx8::SceneFogColor3ub( unsigned char r, unsigned char g, unsigned char b )
{
LOCK_SHADERAPI();
if( m_SceneFogColor[0] != r || m_SceneFogColor[1] != g || m_SceneFogColor[2] != b )
{
FlushBufferedPrimitives();
m_SceneFogColor[0] = r;
m_SceneFogColor[1] = g;
m_SceneFogColor[2] = b;
if ( m_TransitionTable.CurrentShadowState() )
{
ApplyFogMode( m_TransitionTable.CurrentShadowState()->m_FogMode, m_TransitionTable.CurrentShadowState()->m_SRGBWriteEnable, m_TransitionTable.CurrentShadowState()->m_bDisableFogGammaCorrection );
}
}
}
void CShaderAPIDx8::GetSceneFogColor( unsigned char *rgb )
{
LOCK_SHADERAPI();
rgb[0] = m_SceneFogColor[0];
rgb[1] = m_SceneFogColor[1];
rgb[2] = m_SceneFogColor[2];
}
void CShaderAPIDx8::GetSceneFogColor( unsigned char *r, unsigned char *g, unsigned char *b )
{
*r = m_SceneFogColor[0];
*g = m_SceneFogColor[1];
*b = m_SceneFogColor[2];
}
//-----------------------------------------------------------------------------
// Gamma correction of fog color, or not...
//-----------------------------------------------------------------------------
D3DCOLOR CShaderAPIDx8::ComputeGammaCorrectedFogColor( unsigned char r, unsigned char g, unsigned char b, bool bSRGBWritesEnabled )
{
#ifdef _DEBUG
if( g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT && !bSRGBWritesEnabled )
{
// Assert( 0 );
}
#endif
bool bLinearSpace = g_pHardwareConfig->Caps().m_bFogColorAlwaysLinearSpace ||
( bSRGBWritesEnabled && ( g_pHardwareConfig->Caps().m_bFogColorSpecifiedInLinearSpace || g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT ) );
bool bScaleFogByToneMappingScale = g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER;
float fr = ( r / 255.0f );
float fg = ( g / 255.0f );
float fb = ( b / 255.0f );
if ( bLinearSpace )
{
fr = GammaToLinear( fr );
fg = GammaToLinear( fg );
fb = GammaToLinear( fb );
if ( bScaleFogByToneMappingScale )
{
fr *= m_ToneMappingScale.x; //
fg *= m_ToneMappingScale.x; // Linear
fb *= m_ToneMappingScale.x; //
}
}
else if ( bScaleFogByToneMappingScale )
{
fr *= m_ToneMappingScale.w; //
fg *= m_ToneMappingScale.w; // Gamma
fb *= m_ToneMappingScale.w; //
}
fr = min( fr, 1.0f );
fg = min( fg, 1.0f );
fb = min( fb, 1.0f );
r = (int)( fr * 255 );
g = (int)( fg * 255 );
b = (int)( fb * 255 );
return D3DCOLOR_ARGB( 255, r, g, b );
}
//-----------------------------------------------------------------------------
// Some methods chaining vertex + pixel shaders through to the shader manager
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetVertexShaderIndex( int vshIndex )
{
ShaderManager()->SetVertexShaderIndex( vshIndex );
}
void CShaderAPIDx8::SetPixelShaderIndex( int pshIndex )
{
ShaderManager()->SetPixelShaderIndex( pshIndex );
}
void CShaderAPIDx8::SyncToken( const char *pToken )
{
LOCK_SHADERAPI();
RECORD_COMMAND( DX8_SYNC_TOKEN, 1 );
RECORD_STRING( pToken );
}
//-----------------------------------------------------------------------------
// Deals with vertex buffers
//-----------------------------------------------------------------------------
void CShaderAPIDx8::DestroyVertexBuffers( bool bExitingLevel )
{
LOCK_SHADERAPI();
MeshMgr()->DestroyVertexBuffers( );
// After a map is shut down, we switch to using smaller dynamic VBs
// (VGUI shouldn't need much), so that we have more memory free during map loading
m_nDynamicVBSize = bExitingLevel ? DYNAMIC_VERTEX_BUFFER_MEMORY_SMALL : DYNAMIC_VERTEX_BUFFER_MEMORY;
}
int CShaderAPIDx8::GetCurrentDynamicVBSize( void )
{
return m_nDynamicVBSize;
}
FORCEINLINE void CShaderAPIDx8::SetVertexShaderConstantInternal( int var, float const* pVec, int numVecs, bool bForce )
{
Assert( pVec );
// DX8 asm shaders use a constant mapping which has transforms and vertex shader
// specific constants shifted down by 10 constants (two 5-constant light structures)
if ( IsPC() )
{
if ( (g_pHardwareConfig->Caps().m_nDXSupportLevel < 90) && (var >= VERTEX_SHADER_MODULATION_COLOR) )
{
var -= 10;
}
Assert( var + numVecs <= g_pHardwareConfig->NumVertexShaderConstants() );
if ( !bForce )
{
int skip = 0;
numVecs = AdjustUpdateRange( pVec, &m_DesiredState.m_pVectorVertexShaderConstant[var], numVecs, &skip );
if ( !numVecs )
return;
var += skip;
pVec += skip * 4;
}
Dx9Device()->SetVertexShaderConstantF( var, pVec, numVecs );
memcpy( &m_DynamicState.m_pVectorVertexShaderConstant[var], pVec, numVecs * 4 * sizeof(float) );
}
else
{
Assert( var + numVecs <= g_pHardwareConfig->NumVertexShaderConstants() );
}
memcpy( &m_DesiredState.m_pVectorVertexShaderConstant[var], pVec, numVecs * 4 * sizeof(float) );
if ( IsX360() )
{
m_MaxVectorVertexShaderConstant = max( m_MaxVectorVertexShaderConstant, var + numVecs );
}
}
//-----------------------------------------------------------------------------
// Sets the constant register for vertex and pixel shaders
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetVertexShaderConstant( int var, float const* pVec, int numVecs, bool bForce )
{
SetVertexShaderConstantInternal( var, pVec, numVecs, bForce );
}
//-----------------------------------------------------------------------------
// Sets the boolean registers for vertex shader control flow
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetBooleanVertexShaderConstant( int var, int const* pVec, int numBools, bool bForce )
{
Assert( pVec );
Assert( var + numBools <= g_pHardwareConfig->NumBooleanVertexShaderConstants() );
if ( IsPC() && g_pHardwareConfig->GetDXSupportLevel() < 90 )
{
return;
}
if ( IsPC() && !bForce && memcmp( pVec, &m_DesiredState.m_pBooleanVertexShaderConstant[var], numBools * sizeof( BOOL ) ) == 0 )
{
return;
}
if ( IsPC() )
{
Dx9Device()->SetVertexShaderConstantB( var, pVec, numBools );
memcpy( &m_DynamicState.m_pBooleanVertexShaderConstant[var], pVec, numBools * sizeof(BOOL) );
}
memcpy( &m_DesiredState.m_pBooleanVertexShaderConstant[var], pVec, numBools * sizeof(BOOL) );
if ( IsX360() && var + numBools > m_MaxBooleanVertexShaderConstant )
{
m_MaxBooleanVertexShaderConstant = var + numBools;
Assert( m_MaxBooleanVertexShaderConstant <= 16 );
}
}
//-----------------------------------------------------------------------------
// Sets the integer registers for vertex shader control flow
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetIntegerVertexShaderConstant( int var, int const* pVec, int numIntVecs, bool bForce )
{
Assert( pVec );
Assert( var + numIntVecs <= g_pHardwareConfig->NumIntegerVertexShaderConstants() );
if ( IsPC() && g_pHardwareConfig->GetDXSupportLevel() < 90 )
{
return;
}
if ( IsPC() && !bForce && memcmp( pVec, &m_DesiredState.m_pIntegerVertexShaderConstant[var], numIntVecs * sizeof( IntVector4D ) ) == 0 )
{
return;
}
if ( IsPC() )
{
Dx9Device()->SetVertexShaderConstantI( var, pVec, numIntVecs );
memcpy( &m_DynamicState.m_pIntegerVertexShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D) );
}
memcpy( &m_DesiredState.m_pIntegerVertexShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D) );
if ( IsX360() && var + numIntVecs > m_MaxIntegerVertexShaderConstant )
{
m_MaxIntegerVertexShaderConstant = var + numIntVecs;
Assert( m_MaxIntegerVertexShaderConstant <= 16 );
}
}
FORCEINLINE void CShaderAPIDx8::SetPixelShaderConstantInternal( int nStartConst, float const* pValues, int nNumConsts, bool bForce )
{
Assert( nStartConst + nNumConsts <= g_pHardwareConfig->NumPixelShaderConstants() );
if ( IsPC() )
{
if ( ! bForce )
{
int skip = 0;
nNumConsts = AdjustUpdateRange( pValues, &m_DesiredState.m_pVectorPixelShaderConstant[nStartConst], nNumConsts, &skip );
if ( !nNumConsts )
return;
nStartConst += skip;
pValues += skip * 4;
}
Dx9Device()->SetPixelShaderConstantF( nStartConst, pValues, nNumConsts );
memcpy( &m_DynamicState.m_pVectorPixelShaderConstant[nStartConst], pValues, nNumConsts * 4 * sizeof(float) );
}
memcpy( &m_DesiredState.m_pVectorPixelShaderConstant[nStartConst], pValues, nNumConsts * 4 * sizeof(float) );
if ( IsX360() )
{
m_MaxVectorPixelShaderConstant = max( m_MaxVectorPixelShaderConstant, nStartConst + nNumConsts );
Assert( m_MaxVectorPixelShaderConstant <= 32 );
}
}
void CShaderAPIDx8::SetPixelShaderConstant( int var, float const* pVec, int numVecs, bool bForce )
{
SetPixelShaderConstantInternal( var, pVec, numVecs, bForce );
}
template<class T> FORCEINLINE T GetData( uint8 const *pData )
{
return * ( reinterpret_cast< T const *>( pData ) );
}
void CShaderAPIDx8::SetStandardTextureHandle( StandardTextureId_t nId, ShaderAPITextureHandle_t nHandle )
{
Assert( nId < ARRAYSIZE( m_StdTextureHandles ) );
m_StdTextureHandles[nId] = nHandle;
}
void CShaderAPIDx8::ExecuteCommandBuffer( uint8 *pCmdBuf )
{
uint8 *pReturnStack[20];
uint8 **pSP = &pReturnStack[ARRAYSIZE(pReturnStack)];
uint8 *pLastCmd;
for(;;)
{
uint8 *pCmd=pCmdBuf;
int nCmd = GetData<int>( pCmdBuf );
switch( nCmd )
{
case CBCMD_END:
{
if ( pSP == &pReturnStack[ARRAYSIZE(pReturnStack)] )
return;
else
{
// pop pc
pCmdBuf = *( pSP ++ );
break;
}
}
case CBCMD_JUMP:
pCmdBuf = GetData<uint8 *>( pCmdBuf + sizeof( int ) );
break;
case CBCMD_JSR:
{
Assert( pSP > &(pReturnStack[0] ) );
// *(--pSP ) = pCmdBuf + sizeof( int ) + sizeof( uint8 *);
// pCmdBuf = GetData<uint8 *>( pCmdBuf + sizeof( int ) );
ExecuteCommandBuffer( GetData<uint8 *>( pCmdBuf + sizeof( int ) ) );
pCmdBuf = pCmdBuf + sizeof( int ) + sizeof( uint8 *);
break;
}
case CBCMD_SET_PIXEL_SHADER_FLOAT_CONST:
{
int nStartConst = GetData<int>( pCmdBuf + sizeof( int ) );
int nNumConsts = GetData<int>( pCmdBuf + 2 * sizeof( int ) );
float const *pValues = reinterpret_cast< float const *> ( pCmdBuf + 3 * sizeof( int ) );
pCmdBuf += nNumConsts * 4 * sizeof( float ) + 3 * sizeof( int );
SetPixelShaderConstantInternal( nStartConst, pValues, nNumConsts, false );
break;
}
case CBCMD_SETPIXELSHADERFOGPARAMS:
{
int nReg = GetData<int>( pCmdBuf + sizeof( int ) );
pCmdBuf += 2 * sizeof( int );
SetPixelShaderFogParams( nReg ); // !! speed fixme
break;
}
case CBCMD_STORE_EYE_POS_IN_PSCONST:
{
int nReg = GetData<int>( pCmdBuf + sizeof( int ) );
pCmdBuf += 2 * sizeof( int );
SetPixelShaderConstantInternal( nReg, m_WorldSpaceCameraPositon.Base(), 1, false );
break;
}
case CBCMD_COMMITPIXELSHADERLIGHTING:
{
int nReg = GetData<int>( pCmdBuf + sizeof( int ) );
pCmdBuf += 2 * sizeof( int );
CommitPixelShaderLighting( nReg );
break;
}
case CBCMD_SETPIXELSHADERSTATEAMBIENTLIGHTCUBE:
{
int nReg = GetData<int>( pCmdBuf + sizeof( int ) );
pCmdBuf += 2 * sizeof( int );
float *pCubeBase = m_DynamicState.m_AmbientLightCube[0].Base();
SetPixelShaderConstantInternal( nReg, pCubeBase, 6, false );
break;
}
case CBCMD_SETAMBIENTCUBEDYNAMICSTATEVERTEXSHADER:
{
pCmdBuf +=sizeof( int );
SetVertexShaderStateAmbientLightCube();
break;
}
case CBCMD_SET_DEPTH_FEATHERING_CONST:
{
int nConst = GetData<int>( pCmdBuf + sizeof( int ) );
float fDepthBlendScale = GetData<float>( pCmdBuf + 2 * sizeof( int ) );
pCmdBuf += 2 * sizeof( int ) + sizeof( float );
SetDepthFeatheringPixelShaderConstant( nConst, fDepthBlendScale );
break;
}
case CBCMD_SET_VERTEX_SHADER_FLOAT_CONST:
{
int nStartConst = GetData<int>( pCmdBuf + sizeof( int ) );
int nNumConsts = GetData<int>( pCmdBuf + 2 * sizeof( int ) );
float const *pValues = reinterpret_cast< float const *> ( pCmdBuf + 3 * sizeof( int ) );
pCmdBuf += nNumConsts * 4 * sizeof( float ) + 3 * sizeof( int );
SetVertexShaderConstantInternal( nStartConst, pValues, nNumConsts, false );
break;
}
case CBCMD_BIND_STANDARD_TEXTURE:
{
int nSampler = GetData<int>( pCmdBuf + sizeof( int ) );
int nTextureID = GetData<int>( pCmdBuf + 2 * sizeof( int ) );
pCmdBuf += 3 * sizeof( int );
if ( m_StdTextureHandles[nTextureID] != INVALID_SHADERAPI_TEXTURE_HANDLE )
{
BindTexture( (Sampler_t) nSampler, m_StdTextureHandles[nTextureID] );
}
else
{
ShaderUtil()->BindStandardTexture( (Sampler_t) nSampler, (StandardTextureId_t ) nTextureID );
}
break;
}
case CBCMD_BIND_SHADERAPI_TEXTURE_HANDLE:
{
int nSampler = GetData<int>( pCmdBuf + sizeof( int ) );
ShaderAPITextureHandle_t hTexture = GetData<ShaderAPITextureHandle_t>( pCmdBuf + 2 * sizeof( int ) );
Assert( hTexture != INVALID_SHADERAPI_TEXTURE_HANDLE );
pCmdBuf += 2 * sizeof( int ) + sizeof( ShaderAPITextureHandle_t );
BindTexture( (Sampler_t) nSampler, hTexture );
break;
}
case CBCMD_SET_PSHINDEX:
{
int nIdx = GetData<int>( pCmdBuf + sizeof( int ) );
ShaderManager()->SetPixelShaderIndex( nIdx );
pCmdBuf += 2 * sizeof( int );
break;
}
case CBCMD_SET_VSHINDEX:
{
int nIdx = GetData<int>( pCmdBuf + sizeof( int ) );
ShaderManager()->SetVertexShaderIndex( nIdx );
pCmdBuf += 2 * sizeof( int );
break;
}
#ifndef NDEBUG
default:
{
Assert(0);
}
#endif
}
pLastCmd = pCmd;
}
}
//-----------------------------------------------------------------------------
// Sets the boolean registers for pixel shader control flow
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetBooleanPixelShaderConstant( int var, int const* pVec, int numBools, bool bForce )
{
Assert( pVec );
Assert( var + numBools <= g_pHardwareConfig->NumBooleanPixelShaderConstants() );
if ( IsPC() && !bForce && memcmp( pVec, &m_DesiredState.m_pBooleanPixelShaderConstant[var], numBools * sizeof( BOOL ) ) == 0 )
{
return;
}
if ( IsPC() )
{
Dx9Device()->SetPixelShaderConstantB( var, pVec, numBools );
memcpy( &m_DynamicState.m_pBooleanPixelShaderConstant[var], pVec, numBools * sizeof(BOOL) );
}
memcpy( &m_DesiredState.m_pBooleanPixelShaderConstant[var], pVec, numBools * sizeof(BOOL) );
if ( IsX360() && var + numBools > m_MaxBooleanPixelShaderConstant )
{
m_MaxBooleanPixelShaderConstant = var + numBools;
Assert( m_MaxBooleanPixelShaderConstant <= 16 );
}
}
//-----------------------------------------------------------------------------
// Sets the integer registers for pixel shader control flow
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetIntegerPixelShaderConstant( int var, int const* pVec, int numIntVecs, bool bForce )
{
Assert( pVec );
Assert( var + numIntVecs <= g_pHardwareConfig->NumIntegerPixelShaderConstants() );
if ( IsPC() && !bForce && memcmp( pVec, &m_DesiredState.m_pIntegerPixelShaderConstant[var], numIntVecs * sizeof( IntVector4D ) ) == 0 )
{
return;
}
if ( IsPC() )
{
Dx9Device()->SetPixelShaderConstantI( var, pVec, numIntVecs );
memcpy( &m_DynamicState.m_pIntegerPixelShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D) );
}
memcpy( &m_DesiredState.m_pIntegerPixelShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D) );
if ( IsX360() && var + numIntVecs > m_MaxIntegerPixelShaderConstant )
{
m_MaxIntegerPixelShaderConstant = var + numIntVecs;
Assert( m_MaxBooleanPixelShaderConstant <= 16 );
}
}
void CShaderAPIDx8::InvalidateDelayedShaderConstants( void )
{
m_DelayedShaderConstants.Invalidate();
}
//-----------------------------------------------------------------------------
//
// Methods dealing with texture stage state
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Gets the texture associated with a texture state...
//-----------------------------------------------------------------------------
inline IDirect3DBaseTexture* CShaderAPIDx8::GetD3DTexture( ShaderAPITextureHandle_t hTexture )
{
if ( hTexture == INVALID_SHADERAPI_TEXTURE_HANDLE )
{
return NULL;
}
AssertValidTextureHandle( hTexture );
Texture_t& tex = GetTexture( hTexture );
if ( tex.m_NumCopies == 1 )
{
return tex.GetTexture();
}
else
{
return tex.GetTexture( tex.m_CurrentCopy );
}
}
//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
inline ShaderAPITextureHandle_t CShaderAPIDx8::GetModifyTextureHandle() const
{
return m_ModifyTextureHandle;
}
inline IDirect3DBaseTexture* CShaderAPIDx8::GetModifyTexture()
{
return CShaderAPIDx8::GetD3DTexture( m_ModifyTextureHandle );
}
void CShaderAPIDx8::SetModifyTexture( IDirect3DBaseTexture* pTex )
{
if ( m_ModifyTextureHandle == INVALID_SHADERAPI_TEXTURE_HANDLE )
return;
Texture_t& tex = GetTexture( m_ModifyTextureHandle );
if ( tex.m_NumCopies == 1 )
{
tex.SetTexture( pTex );
}
else
{
tex.SetTexture( tex.m_CurrentCopy, pTex );
}
}
inline ShaderAPITextureHandle_t CShaderAPIDx8::GetBoundTextureBindId( Sampler_t sampler ) const
{
return SamplerState( sampler ).m_BoundTexture;
}
inline bool CShaderAPIDx8::WouldBeOverTextureLimit( ShaderAPITextureHandle_t hTexture )
{
if ( IsPC() )
{
if ( mat_texture_limit.GetInt() < 0 )
return false;
Texture_t &tex = GetTexture( hTexture );
if ( tex.m_LastBoundFrame == m_CurrentFrame )
return false;
return m_nTextureMemoryUsedLastFrame + tex.GetMemUsage() > (mat_texture_limit.GetInt() * 1024);
}
return false;
}
#define SETSAMPLESTATEANDMIRROR( sampler, samplerState, state_type, mirror_field, value ) \
if ( samplerState.mirror_field != value ) \
{ \
samplerState.mirror_field = value; \
::SetSamplerState( g_pD3DDevice, sampler, state_type, value ); \
}
#define SETSAMPLESTATEANDMIRROR_FLOAT( sampler, samplerState, state_type, mirror_field, value ) \
if ( samplerState.mirror_field != value ) \
{ \
samplerState.mirror_field = value; \
::SetSamplerState( g_pD3DDevice, sampler, state_type, *(DWORD*)&value ); \
}
//-----------------------------------------------------------------------------
// Sets state on the board related to the texture state
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetTextureState( Sampler_t sampler, ShaderAPITextureHandle_t hTexture, bool force )
{
// Get the dynamic texture info
SamplerState_t &samplerState = SamplerState( sampler );
// Set the texture state, but only if it changes
if ( ( samplerState.m_BoundTexture == hTexture ) && !force )
return;
// Disabling texturing
if ( hTexture == INVALID_SHADERAPI_TEXTURE_HANDLE || WouldBeOverTextureLimit( hTexture ) )
{
Dx9Device()->SetTexture( sampler, 0 );
return;
}
samplerState.m_BoundTexture = hTexture;
// Don't set this if we're disabled
if ( !samplerState.m_TextureEnable )
return;
RECORD_COMMAND( DX8_SET_TEXTURE, 3 );
RECORD_INT( stage );
RECORD_INT( hTexture );
RECORD_INT( GetTexture( hTexture).m_CurrentCopy );
IDirect3DBaseTexture *pTexture = CShaderAPIDx8::GetD3DTexture( hTexture );
#if defined( _X360 )
DWORD linearFormatBackup = pTexture->Format.dword[0];
if ( samplerState.m_SRGBReadEnable )
{
// convert to srgb format for the bind. This effectively emulates the old srgb read sampler state
pTexture->Format.SignX =
pTexture->Format.SignY =
pTexture->Format.SignZ = 3;
}
#endif
Dx9Device()->SetTexture( sampler, pTexture );
#if defined( _X360 )
// put the format back in linear space
pTexture->Format.dword[0] = linearFormatBackup;
#endif
Texture_t &tex = GetTexture( hTexture );
if ( tex.m_LastBoundFrame != m_CurrentFrame )
{
tex.m_LastBoundFrame = m_CurrentFrame;
tex.m_nTimesBoundThisFrame = 0;
if ( tex.m_pTextureGroupCounterFrame )
{
// Update the per-frame texture group counter.
*tex.m_pTextureGroupCounterFrame += tex.GetMemUsage();
}
// Track memory usage.
m_nTextureMemoryUsedLastFrame += tex.GetMemUsage();
}
if ( !m_bDebugTexturesRendering )
++tex.m_nTimesBoundThisFrame;
tex.m_nTimesBoundMax = MAX( tex.m_nTimesBoundMax, tex.m_nTimesBoundThisFrame );
static MaterialSystem_Config_t &materialSystemConfig = ShaderUtil()->GetConfig();
D3DTEXTUREFILTERTYPE minFilter = tex.m_MinFilter;
D3DTEXTUREFILTERTYPE magFilter = tex.m_MagFilter;
D3DTEXTUREFILTERTYPE mipFilter = tex.m_MipFilter;
int finestMipmapLevel = tex.m_FinestMipmapLevel;
float lodBias = tex.m_LodBias;
if ( materialSystemConfig.bMipMapTextures == 0 )
{
mipFilter = D3DTEXF_NONE;
}
if ( materialSystemConfig.bFilterTextures == 0 && tex.m_NumLevels > 1 )
{
minFilter = D3DTEXF_NONE;
magFilter = D3DTEXF_NONE;
mipFilter = D3DTEXF_POINT;
}
D3DTEXTUREADDRESS uTexWrap = tex.m_UTexWrap;
D3DTEXTUREADDRESS vTexWrap = tex.m_VTexWrap;
D3DTEXTUREADDRESS wTexWrap = tex.m_WTexWrap;
// For now do this the old way on OSX since the dxabstract layer doesn't support SetSamplerStates
// ###OSX### punting on OSX for now
#if DX_TO_GL_ABSTRACTION && !OSX
if ( ( samplerState.m_MinFilter != minFilter ) || ( samplerState.m_MagFilter != magFilter ) || ( samplerState.m_MipFilter != mipFilter ) ||
( samplerState.m_UTexWrap != uTexWrap ) || ( samplerState.m_VTexWrap != vTexWrap ) || ( samplerState.m_WTexWrap != wTexWrap ) ||
( samplerState.m_FinestMipmapLevel != finestMipmapLevel ) || ( samplerState.m_LodBias != lodBias ) )
{
samplerState.m_UTexWrap = uTexWrap;
samplerState.m_VTexWrap = vTexWrap;
samplerState.m_WTexWrap = wTexWrap;
samplerState.m_MinFilter = minFilter;
samplerState.m_MagFilter = magFilter;
samplerState.m_MipFilter = mipFilter;
samplerState.m_FinestMipmapLevel = finestMipmapLevel;
samplerState.m_LodBias = lodBias;
Dx9Device()->SetSamplerStates( sampler, uTexWrap, vTexWrap, wTexWrap, minFilter, magFilter, mipFilter, finestMipmapLevel, lodBias );
}
#else
SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_ADDRESSU, m_UTexWrap, uTexWrap );
SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_ADDRESSV, m_VTexWrap, vTexWrap );
SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_ADDRESSW, m_WTexWrap, wTexWrap );
SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_MINFILTER, m_MinFilter, minFilter );
SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_MAGFILTER, m_MagFilter, magFilter );
SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_MIPFILTER, m_MipFilter, mipFilter );
SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_MAXMIPLEVEL, m_FinestMipmapLevel, finestMipmapLevel );
SETSAMPLESTATEANDMIRROR_FLOAT( sampler, samplerState, D3DSAMP_MIPMAPLODBIAS, m_LodBias, lodBias );
#endif
}
void CShaderAPIDx8::BindTexture( Sampler_t sampler, ShaderAPITextureHandle_t textureHandle )
{
LOCK_SHADERAPI();
SetTextureState( sampler, textureHandle );
}
void CShaderAPIDx8::BindVertexTexture( VertexTextureSampler_t nStage, ShaderAPITextureHandle_t textureHandle )
{
Assert( g_pMaterialSystemHardwareConfig->GetVertexTextureCount() != 0 );
LOCK_SHADERAPI();
ADD_VERTEX_TEXTURE_FUNC( COMMIT_PER_PASS, COMMIT_VERTEX_SHADER,
CommitVertexTextures, nStage, m_BoundTexture, textureHandle );
}
//-----------------------------------------------------------------------------
// Texture allocation/deallocation
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Computes stats info for a texture
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ComputeStatsInfo( ShaderAPITextureHandle_t hTexture, bool isCubeMap, bool isVolumeTexture )
{
Texture_t &textureData = GetTexture( hTexture );
textureData.m_SizeBytes = 0;
textureData.m_SizeTexels = 0;
textureData.m_LastBoundFrame = -1;
if ( IsX360() )
{
textureData.m_nTimesBoundThisFrame = 0;
}
IDirect3DBaseTexture* pD3DTex = CShaderAPIDx8::GetD3DTexture( hTexture );
if ( IsPC() || !IsX360() )
{
if ( isCubeMap )
{
IDirect3DCubeTexture* pTex = static_cast<IDirect3DCubeTexture*>(pD3DTex);
if ( !pTex )
{
Assert( 0 );
return;
}
int numLevels = pTex->GetLevelCount();
for (int i = 0; i < numLevels; ++i)
{
D3DSURFACE_DESC desc;
HRESULT hr = pTex->GetLevelDesc( i, &desc );
Assert( !FAILED(hr) );
textureData.m_SizeBytes += 6 * ImageLoader::GetMemRequired( desc.Width, desc.Height, 1, textureData.GetImageFormat(), false );
textureData.m_SizeTexels += 6 * desc.Width * desc.Height;
}
}
else if ( isVolumeTexture )
{
IDirect3DVolumeTexture9* pTex = static_cast<IDirect3DVolumeTexture9*>(pD3DTex);
if ( !pTex )
{
Assert( 0 );
return;
}
int numLevels = pTex->GetLevelCount();
for (int i = 0; i < numLevels; ++i)
{
D3DVOLUME_DESC desc;
HRESULT hr = pTex->GetLevelDesc( i, &desc );
Assert( !FAILED( hr ) );
textureData.m_SizeBytes += ImageLoader::GetMemRequired( desc.Width, desc.Height, desc.Depth, textureData.GetImageFormat(), false );
textureData.m_SizeTexels += desc.Width * desc.Height;
}
}
else
{
IDirect3DTexture* pTex = static_cast<IDirect3DTexture*>(pD3DTex);
if ( !pTex )
{
Assert( 0 );
return;
}
int numLevels = pTex->GetLevelCount();
for (int i = 0; i < numLevels; ++i)
{
D3DSURFACE_DESC desc;
HRESULT hr = pTex->GetLevelDesc( i, &desc );
Assert( !FAILED( hr ) );
textureData.m_SizeBytes += ImageLoader::GetMemRequired( desc.Width, desc.Height, 1, textureData.GetImageFormat(), false );
textureData.m_SizeTexels += desc.Width * desc.Height;
}
}
}
#if defined( _X360 )
// 360 uses gpu storage size (which accounts for page alignment bloat), not format size
textureData.m_SizeBytes = g_TextureHeap.GetSize( pD3DTex );
#endif
}
static D3DFORMAT ComputeFormat( IDirect3DBaseTexture* pTexture, bool isCubeMap )
{
Assert( pTexture );
D3DSURFACE_DESC desc;
if (isCubeMap)
{
IDirect3DCubeTexture* pTex = static_cast<IDirect3DCubeTexture*>(pTexture);
HRESULT hr = pTex->GetLevelDesc( 0, &desc );
Assert( !FAILED(hr) );
}
else
{
IDirect3DTexture* pTex = static_cast<IDirect3DTexture*>(pTexture);
HRESULT hr = pTex->GetLevelDesc( 0, &desc );
Assert( !FAILED(hr) );
}
return desc.Format;
}
ShaderAPITextureHandle_t CShaderAPIDx8::CreateDepthTexture(
ImageFormat renderTargetFormat,
int width,
int height,
const char *pDebugName,
bool bTexture )
{
LOCK_SHADERAPI();
ShaderAPITextureHandle_t i = CreateTextureHandle();
Texture_t *pTexture = &GetTexture( i );
pTexture->m_Flags = Texture_t::IS_ALLOCATED;
if( bTexture )
{
pTexture->m_Flags |= Texture_t::IS_DEPTH_STENCIL_TEXTURE;
}
else
{
pTexture->m_Flags |= Texture_t::IS_DEPTH_STENCIL;
}
pTexture->m_DebugName = pDebugName;
pTexture->m_Width = width;
pTexture->m_Height = height;
pTexture->m_Depth = 1; // fake
pTexture->m_Count = 1; // created single texture
pTexture->m_CountIndex = 0; // created single texture
pTexture->m_CreationFlags = 0; // fake
pTexture->m_NumCopies = 1;
pTexture->m_CurrentCopy = 0;
ImageFormat renderFormat = FindNearestSupportedFormat( renderTargetFormat, false, true, false );
#if defined( _X360 )
D3DFORMAT nDepthFormat = ReverseDepthOnX360() ? D3DFMT_D24FS8 : D3DFMT_D24S8;
#else
D3DFORMAT nDepthFormat = m_bUsingStencil ? D3DFMT_D24S8 : D3DFMT_D24X8;
#endif
D3DFORMAT format = FindNearestSupportedDepthFormat( m_nAdapter, m_AdapterFormat, renderFormat, nDepthFormat );
D3DMULTISAMPLE_TYPE multisampleType = D3DMULTISAMPLE_NONE;
pTexture->m_NumLevels = 1;
pTexture->m_SizeTexels = width * height;
pTexture->m_SizeBytes = ImageLoader::GetMemRequired( width, height, 1, renderFormat, false );
RECORD_COMMAND( DX8_CREATE_DEPTH_TEXTURE, 5 );
RECORD_INT( i );
RECORD_INT( width );
RECORD_INT( height );
RECORD_INT( format );
RECORD_INT( multisampleType );
HRESULT hr;
if ( !bTexture )
{
#if defined( _X360 )
int backWidth, backHeight;
ShaderAPI()->GetBackBufferDimensions( backWidth, backHeight );
D3DFORMAT backBufferFormat = ImageLoader::ImageFormatToD3DFormat( g_pShaderDevice->GetBackBufferFormat() );
// immediately follows back buffer in EDRAM
D3DSURFACE_PARAMETERS surfParameters;
surfParameters.Base = 2*XGSurfaceSize( backWidth, backHeight, backBufferFormat, D3DMULTISAMPLE_NONE );
surfParameters.ColorExpBias = 0;
surfParameters.HierarchicalZBase = 0;
hr = Dx9Device()->CreateDepthStencilSurface(
width, height, format, multisampleType, 0, TRUE, &pTexture->GetDepthStencilSurface(), &surfParameters );
#else
hr = Dx9Device()->CreateDepthStencilSurface(
width, height, format, multisampleType, 0, TRUE, &pTexture->GetDepthStencilSurface(), NULL );
#endif
}
else
{
IDirect3DTexture9 *pTex;
hr = Dx9Device()->CreateTexture( width, height, 1, D3DUSAGE_DEPTHSTENCIL, format, D3DPOOL_DEFAULT, &pTex, NULL );
pTexture->SetTexture( pTex );
}
if ( FAILED( hr ) )
{
switch( hr )
{
case D3DERR_INVALIDCALL:
Warning( "ShaderAPIDX8::CreateDepthStencilSurface: D3DERR_INVALIDCALL\n" );
break;
case D3DERR_OUTOFVIDEOMEMORY:
Warning( "ShaderAPIDX8::CreateDepthStencilSurface: D3DERR_OUTOFVIDEOMEMORY\n" );
break;
default:
break;
}
Assert( 0 );
}
#ifdef _XBOX
D3DSURFACE_DESC desc;
hr = pTexture->GetDepthStencilSurface()->GetDesc( &desc );
Assert( !FAILED( hr ) );
pTexture->m_nTimesBoundThisFrame = 0;
pTexture->m_StaticSizeBytes = desc.Size;
pTexture->m_DynamicSizeBytes = 0;
pTexture->m_DebugName = pDebugName;
pTexture->m_TextureGroupName = TEXTURE_GROUP_RENDER_TARGET;
pTexture->SetImageFormat( IMAGE_FORMAT_UNKNOWN );
#endif
return i;
}
// FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Could keep a free-list for this instead of linearly searching. We
// don't create textures all the time, so this is probably fine for now.
// FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ShaderAPITextureHandle_t CShaderAPIDx8::CreateTextureHandle( void )
{
ShaderAPITextureHandle_t handle;
CreateTextureHandles( &handle, 1 );
return handle;
}
void CShaderAPIDx8::CreateTextureHandles( ShaderAPITextureHandle_t *handles, int count )
{
TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );
if ( count <= 0 )
return;
MEM_ALLOC_CREDIT();
int idxCreating = 0;
ShaderAPITextureHandle_t hTexture;
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Search", __FUNCTION__ );
for ( hTexture = m_Textures.Head(); hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) )
{
if ( !( m_Textures[hTexture].m_Flags & Texture_t::IS_ALLOCATED ) )
{
handles[ idxCreating ++ ] = hTexture;
if ( idxCreating >= count )
return;
}
}
}
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Add", __FUNCTION__ );
while ( idxCreating < count )
handles[ idxCreating ++ ] = m_Textures.AddToTail();
}
//-----------------------------------------------------------------------------
// Creates a lovely texture
//-----------------------------------------------------------------------------
ShaderAPITextureHandle_t CShaderAPIDx8::CreateTexture(
int width,
int height,
int depth,
ImageFormat dstImageFormat,
int numMipLevels,
int numCopies,
int creationFlags,
const char *pDebugName,
const char *pTextureGroupName )
{
ShaderAPITextureHandle_t handle = 0;
CreateTextures( &handle, 1, width, height, depth, dstImageFormat, numMipLevels, numCopies, creationFlags, pDebugName, pTextureGroupName );
return handle;
}
void CShaderAPIDx8::CreateTextures(
ShaderAPITextureHandle_t *pHandles,
int count,
int width,
int height,
int depth,
ImageFormat dstImageFormat,
int numMipLevels,
int numCopies,
int creationFlags,
const char *pDebugName,
const char *pTextureGroupName )
{
TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );
LOCK_SHADERAPI();
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - PostLock", __FUNCTION__ );
Assert( this == g_pShaderAPI );
if ( depth == 0 )
{
depth = 1;
}
bool isCubeMap = (creationFlags & TEXTURE_CREATE_CUBEMAP) != 0;
bool isRenderTarget = (creationFlags & TEXTURE_CREATE_RENDERTARGET) != 0;
bool managed = (creationFlags & TEXTURE_CREATE_MANAGED) != 0;
bool isDepthBuffer = (creationFlags & TEXTURE_CREATE_DEPTHBUFFER) != 0;
bool isDynamic = (creationFlags & TEXTURE_CREATE_DYNAMIC) != 0;
bool isSRGB = (creationFlags & TEXTURE_CREATE_SRGB) != 0; // for Posix/GL only... not used here ?
#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9)
extern bool g_ShaderDeviceUsingD3D9Ex;
if ( g_ShaderDeviceUsingD3D9Ex && managed )
{
// Managed textures aren't available under D3D9Ex, but we never lose
// texture data, so it's ok to use the default pool. Really. We can't
// lock default-pool textures like we normally would to upload, but we
// have special logic to blit full updates via D3DX helper functions
// in D3D9Ex mode (see texturedx8.cpp)
managed = false;
creationFlags &= ~TEXTURE_CREATE_MANAGED;
}
#endif
// Can't be both managed + dynamic. Dynamic is an optimization, but
// if it's not managed, then we gotta do special client-specific stuff
// So, managed wins out!
if ( managed )
{
creationFlags &= ~TEXTURE_CREATE_DYNAMIC;
isDynamic = false;
}
// Create a set of texture handles
CreateTextureHandles( pHandles, count );
Texture_t **arrTxp = ( Texture_t ** ) stackalloc( count * sizeof( Texture_t * ) );
unsigned short usSetFlags = 0;
usSetFlags |= ( IsPosix() || ( creationFlags & (TEXTURE_CREATE_DYNAMIC | TEXTURE_CREATE_MANAGED) ) ) ? Texture_t::IS_LOCKABLE : 0;
usSetFlags |= ( creationFlags & TEXTURE_CREATE_VERTEXTEXTURE) ? Texture_t::IS_VERTEX_TEXTURE : 0;
#if defined( _X360 )
usSetFlags |= ( creationFlags & TEXTURE_CREATE_RENDERTARGET ) ? Texture_t::IS_RENDER_TARGET : 0;
usSetFlags |= ( creationFlags & TEXTURE_CREATE_CANCONVERTFORMAT ) ? Texture_t::CAN_CONVERT_FORMAT : 0;
#endif
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - CreateFrames", __FUNCTION__ );
for ( int idxFrame = 0; idxFrame < count; ++ idxFrame )
{
arrTxp[ idxFrame ] = &GetTexture( pHandles[ idxFrame ] );
Texture_t *pTexture = arrTxp[ idxFrame ];
pTexture->m_Flags = Texture_t::IS_ALLOCATED;
pTexture->m_DebugName = pDebugName;
pTexture->m_Width = width;
pTexture->m_Height = height;
pTexture->m_Depth = depth;
pTexture->m_Count = count;
pTexture->m_CountIndex = idxFrame;
pTexture->m_CreationFlags = creationFlags;
pTexture->m_Flags |= usSetFlags;
RECORD_COMMAND( DX8_CREATE_TEXTURE, 12 );
RECORD_INT( textureHandle );
RECORD_INT( width );
RECORD_INT( height );
RECORD_INT( depth ); // depth for volume textures
RECORD_INT( ImageLoader::ImageFormatToD3DFormat( FindNearestSupportedFormat(dstImageFormat)) );
RECORD_INT( numMipLevels );
RECORD_INT( isCubeMap );
RECORD_INT( numCopies <= 1 ? 1 : numCopies );
RECORD_INT( isRenderTarget ? 1 : 0 );
RECORD_INT( managed );
RECORD_INT( isDepthBuffer ? 1 : 0 );
RECORD_INT( isDynamic ? 1 : 0 );
IDirect3DBaseTexture* pD3DTex;
// Set the initial texture state
if ( numCopies <= 1 )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - CreateD3DTexture", __FUNCTION__ );
pTexture->m_NumCopies = 1;
pD3DTex = CreateD3DTexture( width, height, depth, dstImageFormat, numMipLevels, creationFlags, (char*)pDebugName );
pTexture->SetTexture( pD3DTex );
}
else
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - CreateD3DTexture", __FUNCTION__ );
pTexture->m_NumCopies = numCopies;
{
// X360TEMP
// MEM_ALLOC_CREDIT();
pTexture->GetTextureArray() = new IDirect3DBaseTexture* [numCopies];
}
for (int k = 0; k < numCopies; ++k)
{
pD3DTex = CreateD3DTexture( width, height, depth, dstImageFormat, numMipLevels, creationFlags, (char*)pDebugName );
pTexture->SetTexture( k, pD3DTex );
}
}
pTexture->m_CurrentCopy = 0;
pD3DTex = CShaderAPIDx8::GetD3DTexture( pHandles[ idxFrame ] );
#if defined( _X360 )
if ( pD3DTex )
{
D3DSURFACE_DESC desc;
HRESULT hr;
if ( creationFlags & TEXTURE_CREATE_CUBEMAP )
{
hr = ((IDirect3DCubeTexture *)pD3DTex)->GetLevelDesc( 0, &desc );
}
else
{
hr = ((IDirect3DTexture *)pD3DTex)->GetLevelDesc( 0, &desc );
}
Assert( !FAILED( hr ) );
// for proper info get the actual format because the input format may have been redirected
dstImageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format );
Assert( dstImageFormat != IMAGE_FORMAT_UNKNOWN );
// track linear or tiled
if ( !XGIsTiledFormat( desc.Format ) )
{
pTexture->m_Flags |= Texture_t::IS_LINEAR;
}
}
#endif
pTexture->SetImageFormat( dstImageFormat );
pTexture->m_UTexWrap = D3DTADDRESS_CLAMP;
pTexture->m_VTexWrap = D3DTADDRESS_CLAMP;
pTexture->m_WTexWrap = D3DTADDRESS_CLAMP;
if ( isRenderTarget )
{
#if !defined( _X360 )
if ( ( dstImageFormat == IMAGE_FORMAT_NV_INTZ ) || ( dstImageFormat == IMAGE_FORMAT_NV_RAWZ ) ||
( dstImageFormat == IMAGE_FORMAT_ATI_DST16 ) || ( dstImageFormat == IMAGE_FORMAT_ATI_DST24 ) )
{
pTexture->m_MinFilter = pTexture->m_MagFilter = D3DTEXF_POINT;
}
else
#endif
{
pTexture->m_MinFilter = pTexture->m_MagFilter = D3DTEXF_LINEAR;
}
pTexture->m_NumLevels = 1;
pTexture->m_MipFilter = D3DTEXF_NONE;
}
else
{
pTexture->m_NumLevels = pD3DTex ? pD3DTex->GetLevelCount() : 1;
pTexture->m_MipFilter = (pTexture->m_NumLevels != 1) ? D3DTEXF_LINEAR : D3DTEXF_NONE;
pTexture->m_MinFilter = pTexture->m_MagFilter = D3DTEXF_LINEAR;
}
pTexture->m_SwitchNeeded = false;
ComputeStatsInfo( pHandles[idxFrame], isCubeMap, (depth > 1) );
SetupTextureGroup( pHandles[idxFrame], pTextureGroupName );
}
}
void CShaderAPIDx8::SetupTextureGroup( ShaderAPITextureHandle_t hTexture, const char *pTextureGroupName )
{
Texture_t *pTexture = &GetTexture( hTexture );
Assert( !pTexture->m_pTextureGroupCounterGlobal );
// Setup the texture group stuff.
if ( pTextureGroupName && pTextureGroupName[0] != 0 )
{
pTexture->m_TextureGroupName = pTextureGroupName;
}
else
{
pTexture->m_TextureGroupName = TEXTURE_GROUP_UNACCOUNTED;
}
// 360 cannot vprof due to multicore loading until vprof is reentrant and these counters are real.
#if defined( VPROF_ENABLED ) && !defined( _X360 )
char counterName[256];
Q_snprintf( counterName, sizeof( counterName ), "TexGroup_global_%s", pTexture->m_TextureGroupName.String() );
pTexture->m_pTextureGroupCounterGlobal = g_VProfCurrentProfile.FindOrCreateCounter( counterName, COUNTER_GROUP_TEXTURE_GLOBAL );
Q_snprintf( counterName, sizeof( counterName ), "TexGroup_frame_%s", pTexture->m_TextureGroupName.String() );
pTexture->m_pTextureGroupCounterFrame = g_VProfCurrentProfile.FindOrCreateCounter( counterName, COUNTER_GROUP_TEXTURE_PER_FRAME );
#else
pTexture->m_pTextureGroupCounterGlobal = NULL;
pTexture->m_pTextureGroupCounterFrame = NULL;
#endif
if ( pTexture->m_pTextureGroupCounterGlobal )
{
*pTexture->m_pTextureGroupCounterGlobal += pTexture->GetMemUsage();
}
}
//-----------------------------------------------------------------------------
// Deletes a texture...
//-----------------------------------------------------------------------------
void CShaderAPIDx8::DeleteD3DTexture( ShaderAPITextureHandle_t hTexture )
{
int numDeallocated = 0;
Texture_t &texture = GetTexture( hTexture );
if ( texture.m_Flags & Texture_t::IS_DEPTH_STENCIL )
{
// garymcthack - need to make sure that playback knows how to deal with these.
RECORD_COMMAND( DX8_DESTROY_DEPTH_TEXTURE, 1 );
RECORD_INT( hTexture );
if ( texture.GetDepthStencilSurface() )
{
int nRetVal = texture.GetDepthStencilSurface()->Release();
Assert( nRetVal == 0 );
texture.GetDepthStencilSurface() = 0;
numDeallocated = 1;
}
else
{
// FIXME: we hit this on shutdown of HLMV on some machines
Assert( 0 );
}
}
else if ( texture.m_NumCopies == 1 )
{
if ( texture.GetTexture() )
{
RECORD_COMMAND( DX8_DESTROY_TEXTURE, 1 );
RECORD_INT( hTexture );
DestroyD3DTexture( texture.GetTexture() );
texture.SetTexture( 0 );
numDeallocated = 1;
}
}
else
{
if ( texture.GetTextureArray() )
{
RECORD_COMMAND( DX8_DESTROY_TEXTURE, 1 );
RECORD_INT( hTexture );
// Multiple copy texture
for (int j = 0; j < texture.m_NumCopies; ++j)
{
if (texture.GetTexture( j ))
{
DestroyD3DTexture( texture.GetTexture( j ) );
texture.SetTexture( j, 0 );
++numDeallocated;
}
}
delete [] texture.GetTextureArray();
texture.GetTextureArray() = 0;
}
}
texture.m_NumCopies = 0;
// Remove this texture from its global texture group counter.
if ( texture.m_pTextureGroupCounterGlobal )
{
*texture.m_pTextureGroupCounterGlobal -= texture.GetMemUsage();
Assert( *texture.m_pTextureGroupCounterGlobal >= 0 );
texture.m_pTextureGroupCounterGlobal = NULL;
}
// remove this texture from std textures
for( int i=0 ; i < ARRAYSIZE( m_StdTextureHandles ) ; i++ )
{
if ( m_StdTextureHandles[i] == hTexture )
m_StdTextureHandles[i] = INVALID_SHADERAPI_TEXTURE_HANDLE;
}
}
//-----------------------------------------------------------------------------
// Unbinds a texture from all texture stages
//-----------------------------------------------------------------------------
void CShaderAPIDx8::UnbindTexture( ShaderAPITextureHandle_t hTexture )
{
// Make sure no texture units are currently bound to it...
for ( int unit = 0; unit < g_pHardwareConfig->GetSamplerCount(); ++unit )
{
if ( hTexture == SamplerState( unit ).m_BoundTexture )
{
// Gotta set this here because INVALID_SHADERAPI_TEXTURE_HANDLE means don't actually
// set bound texture (it's used for disabling texturemapping)
SamplerState( unit ).m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE;
SetTextureState( (Sampler_t)unit, INVALID_SHADERAPI_TEXTURE_HANDLE );
}
}
int nVertexTextureCount = g_pHardwareConfig->GetVertexTextureCount();
for ( int nSampler = 0; nSampler < nVertexTextureCount; ++nSampler )
{
if ( hTexture == m_DynamicState.m_VertexTextureState[ nSampler ].m_BoundTexture )
{
// Gotta set this here because INVALID_SHADERAPI_TEXTURE_HANDLE means don't actually
// set bound texture (it's used for disabling texturemapping)
BindVertexTexture( (VertexTextureSampler_t)nSampler, INVALID_SHADERAPI_TEXTURE_HANDLE );
}
}
}
//-----------------------------------------------------------------------------
// Deletes a texture...
//-----------------------------------------------------------------------------
void CShaderAPIDx8::DeleteTexture( ShaderAPITextureHandle_t textureHandle )
{
LOCK_SHADERAPI();
AssertValidTextureHandle( textureHandle );
if ( !TextureIsAllocated( textureHandle ) )
{
// already deallocated
return;
}
// Unbind it!
UnbindTexture( textureHandle );
// Delete it baby
DeleteD3DTexture( textureHandle );
// Now remove the texture from the list
// Mark as deallocated so that it can be reused.
GetTexture( textureHandle ).m_Flags = 0;
}
void CShaderAPIDx8::WriteTextureToFile( ShaderAPITextureHandle_t hTexture, const char *szFileName )
{
Texture_t *pTexInt = &GetTexture( hTexture );
//Assert( pTexInt->IsCubeMap() == false );
//Assert( pTexInt->IsVolumeTexture() == false );
IDirect3DTexture *pD3DTexture = (IDirect3DTexture *)pTexInt->GetTexture();
// Get the level of the texture we want to read from
IDirect3DSurface* pTextureLevel;
HRESULT hr = pD3DTexture ->GetSurfaceLevel( 0, &pTextureLevel );
if ( FAILED( hr ) )
return;
D3DSURFACE_DESC surfaceDesc;
pD3DTexture->GetLevelDesc( 0, &surfaceDesc );
D3DLOCKED_RECT lockedRect;
//if( pTexInt->m_Flags & Texture_t::IS_RENDER_TARGET )
#if !defined( _X360 ) //TODO: x360 version
{
//render targets can't be locked, luckily we can copy the surface to system memory and lock that.
IDirect3DSurface *pSystemSurface;
Assert( !IsX360() );
hr = Dx9Device()->CreateOffscreenPlainSurface( surfaceDesc.Width, surfaceDesc.Height, surfaceDesc.Format, D3DPOOL_SYSTEMMEM, &pSystemSurface, NULL );
Assert( SUCCEEDED( hr ) );
pSystemSurface->GetDesc( &surfaceDesc );
hr = Dx9Device()->GetRenderTargetData( pTextureLevel, pSystemSurface );
Assert( SUCCEEDED( hr ) );
//pretend this is the texture level we originally grabbed with GetSurfaceLevel() and continue on
pTextureLevel->Release();
pTextureLevel = pSystemSurface;
}
#endif
// lock the region
if ( FAILED( pTextureLevel->LockRect( &lockedRect, NULL, D3DLOCK_READONLY ) ) )
{
Assert( 0 );
pTextureLevel->Release();
return;
}
TGAWriter::WriteTGAFile( szFileName, surfaceDesc.Width, surfaceDesc.Height, pTexInt->GetImageFormat(), (const uint8 *)lockedRect.pBits, lockedRect.Pitch );
if ( FAILED( pTextureLevel->UnlockRect() ) )
{
Assert( 0 );
pTextureLevel->Release();
return;
}
pTextureLevel->Release();
}
//-----------------------------------------------------------------------------
// Releases all textures
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ReleaseAllTextures()
{
ClearStdTextureHandles();
ShaderAPITextureHandle_t hTexture;
for ( hTexture = m_Textures.Head(); hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) )
{
if ( TextureIsAllocated( hTexture ) )
{
// Delete it baby
DeleteD3DTexture( hTexture );
}
}
// Make sure all texture units are pointing to nothing
for (int unit = 0; unit < g_pHardwareConfig->GetSamplerCount(); ++unit )
{
SamplerState( unit ).m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE;
SetTextureState( (Sampler_t)unit, INVALID_SHADERAPI_TEXTURE_HANDLE );
}
}
void CShaderAPIDx8::DeleteAllTextures()
{
ReleaseAllTextures();
m_Textures.Purge();
}
bool CShaderAPIDx8::IsTexture( ShaderAPITextureHandle_t textureHandle )
{
LOCK_SHADERAPI();
if ( !TextureIsAllocated( textureHandle ) )
{
return false;
}
#if !defined( _X360 )
if ( GetTexture( textureHandle ).m_Flags & Texture_t::IS_DEPTH_STENCIL )
{
return GetTexture( textureHandle ).GetDepthStencilSurface() != 0;
}
else if ( ( GetTexture( textureHandle ).m_NumCopies == 1 && GetTexture( textureHandle ).GetTexture() != 0 ) ||
( GetTexture( textureHandle ).m_NumCopies > 1 && GetTexture( textureHandle ).GetTexture( 0 ) != 0 ) )
{
return true;
}
else
{
return false;
}
#else
// query is about texture handle validity, not presence
// texture handle is allocated, texture may or may not be present
return true;
#endif
}
//-----------------------------------------------------------------------------
// Gets the surface associated with a texture (refcount of surface is increased)
//-----------------------------------------------------------------------------
IDirect3DSurface* CShaderAPIDx8::GetTextureSurface( ShaderAPITextureHandle_t textureHandle )
{
MEM_ALLOC_D3D_CREDIT();
IDirect3DSurface* pSurface;
// We'll be modifying this sucka
AssertValidTextureHandle( textureHandle );
Texture_t &tex = GetTexture( textureHandle );
if ( !( tex.m_Flags & Texture_t::IS_ALLOCATED ) )
{
return NULL;
}
if ( IsX360() && ( tex.m_Flags & Texture_t::IS_RENDER_TARGET_SURFACE ) )
{
pSurface = tex.GetRenderTargetSurface( false );
#if POSIX
// dxabstract's AddRef/Release have optional args to help track usage
pSurface->AddRef( 0, "CShaderAPIDx8::GetTextureSurface public addref");
#else
pSurface->AddRef();
#endif
return pSurface;
}
IDirect3DBaseTexture* pD3DTex = CShaderAPIDx8::GetD3DTexture( textureHandle );
IDirect3DTexture* pTex = static_cast<IDirect3DTexture*>( pD3DTex );
Assert( pTex );
if ( !pTex )
{
return NULL;
}
HRESULT hr = pTex->GetSurfaceLevel( 0, &pSurface );
Assert( hr == D3D_OK );
return pSurface;
}
//-----------------------------------------------------------------------------
// Gets the surface associated with a texture (refcount of surface is increased)
//-----------------------------------------------------------------------------
IDirect3DSurface* CShaderAPIDx8::GetDepthTextureSurface( ShaderAPITextureHandle_t textureHandle )
{
AssertValidTextureHandle( textureHandle );
if ( !TextureIsAllocated( textureHandle ) )
{
return NULL;
}
return GetTexture( textureHandle ).GetDepthStencilSurface();
}
//-----------------------------------------------------------------------------
// Changes the render target
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetRenderTargetEx( int nRenderTargetID, ShaderAPITextureHandle_t colorTextureHandle, ShaderAPITextureHandle_t depthTextureHandle )
{
LOCK_SHADERAPI();
if ( IsDeactivated( ) )
{
return;
}
// GR - need to flush batched geometry
FlushBufferedPrimitives();
#if defined( PIX_INSTRUMENTATION )
{
const char *pRT = "Backbuffer";
const char *pDS = "DefaultDepthStencil";
if ( colorTextureHandle == SHADER_RENDERTARGET_NONE )
{
pRT = "None";
}
else if ( colorTextureHandle != SHADER_RENDERTARGET_BACKBUFFER )
{
Texture_t &tex = GetTexture( colorTextureHandle );
pRT = tex.m_DebugName.String();
}
if ( depthTextureHandle == SHADER_RENDERTARGET_NONE )
{
pDS = "None";
}
else if ( depthTextureHandle != SHADER_RENDERTARGET_DEPTHBUFFER )
{
Texture_t &tex = GetTexture( depthTextureHandle );
pDS = tex.m_DebugName.String();
}
char buf[256];
sprintf( buf, "SRT:%s %s", pRT ? pRT : "?", pDS ? pDS : "?" );
BeginPIXEvent( 0xFFFFFFFF, buf );
EndPIXEvent();
}
#endif
#if !defined( _X360 )
RECORD_COMMAND( DX8_TEST_COOPERATIVE_LEVEL, 0 );
HRESULT hr = Dx9Device()->TestCooperativeLevel();
if ( hr != D3D_OK )
{
MarkDeviceLost();
return;
}
#endif
IDirect3DSurface* pColorSurface = NULL;
IDirect3DSurface* pZSurface = NULL;
RECORD_COMMAND( DX8_SET_RENDER_TARGET, 3 );
RECORD_INT( nRenderTargetID );
RECORD_INT( colorTextureHandle );
RECORD_INT( depthTextureHandle );
// The 0th render target defines which depth buffer we are using, so
// don't bother if we are another render target
if ( nRenderTargetID > 0 )
{
depthTextureHandle = SHADER_RENDERTARGET_NONE;
}
// NOTE!!!! If this code changes, also change Dx8SetRenderTarget in playback.cpp
bool usingTextureTarget = false;
if ( colorTextureHandle == SHADER_RENDERTARGET_BACKBUFFER )
{
pColorSurface = m_pBackBufferSurface;
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
if( pColorSurface )
#endif
{
// This is just to make the code a little simpler...
// (simplifies the release logic)
#if POSIX
// dxabstract's AddRef/Release have optional args to help track usage
pColorSurface->AddRef( 0, "+C CShaderAPIDx8::SetRenderTargetEx public addref 1");
#else
pColorSurface->AddRef();
#endif
}
}
else
{
// get the texture (Refcount increases)
UnbindTexture( colorTextureHandle );
pColorSurface = GetTextureSurface( colorTextureHandle );
if ( !pColorSurface )
{
return;
}
usingTextureTarget = true;
}
if ( depthTextureHandle == SHADER_RENDERTARGET_DEPTHBUFFER )
{
// using the default depth buffer
pZSurface = m_pZBufferSurface;
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
if( pZSurface )
#endif
{
// simplify the prologue logic
#if POSIX
// dxabstract's AddRef/Release have optional args to help track usage
pZSurface->AddRef( 0, "+D CShaderAPIDx8::SetRenderTargetEx public addref 1");
#else
pZSurface->AddRef();
#endif
}
}
else if ( depthTextureHandle == SHADER_RENDERTARGET_NONE )
{
// GR - disable depth buffer
pZSurface = NULL;
}
else
{
UnbindTexture( depthTextureHandle );
Texture_t &tex = GetTexture( depthTextureHandle );
//Cannot use a depth/stencil surface derived from a texture.
//Asserting helps get the whole call stack instead of letting the 360 report an error with a partial stack
Assert( !( IsX360() && (tex.m_Flags & Texture_t::IS_DEPTH_STENCIL_TEXTURE) ) );
if ( tex.m_Flags & Texture_t::IS_DEPTH_STENCIL )
{
pZSurface = GetDepthTextureSurface( depthTextureHandle );
if ( pZSurface )
{
#if POSIX
// dxabstract's AddRef/Release have optional args to help track usage
pZSurface->AddRef( 0, "+D CShaderAPIDx8::SetRenderTargetEx public addref 2");
#else
pZSurface->AddRef();
#endif
}
}
else
{
HRESULT hr = ((IDirect3DTexture9*)tex.GetTexture())->GetSurfaceLevel( 0, &pZSurface );
}
if ( !pZSurface )
{
// Refcount of color surface was increased above
#if POSIX
// dxabstract's AddRef/Release have optional args to help track usage
pColorSurface->Release( 0, "-C CShaderAPIDx8::SetRenderTargetEx public release 1" );
#else
pColorSurface->Release();
#endif
return;
}
usingTextureTarget = true;
}
#ifdef _DEBUG
if ( pZSurface )
{
D3DSURFACE_DESC zSurfaceDesc, colorSurfaceDesc;
pZSurface->GetDesc( &zSurfaceDesc );
pColorSurface->GetDesc( &colorSurfaceDesc );
if ( !HushAsserts() )
{
Assert( colorSurfaceDesc.Width <= zSurfaceDesc.Width );
Assert( colorSurfaceDesc.Height <= zSurfaceDesc.Height );
}
}
#endif
// we only set this flag for the 0th render target so that NULL
// render targets 1-3 don't mess with the viewport on the main RT
if( nRenderTargetID == 0 )
m_UsingTextureRenderTarget = usingTextureTarget;
// NOTE: The documentation says that SetRenderTarget increases the refcount
// but it doesn't appear to in practice. If this somehow changes (perhaps
// in a device-specific manner, we're in trouble).
if ( IsPC() || !IsX360() )
{
if ( pColorSurface == m_pBackBufferSurface && nRenderTargetID > 0 )
{
// SetRenderTargetEx is overloaded so that if you pass NULL in for anything that
// isn't the zeroth render target, you effectively disable that MRT index.
// (Passing in NULL for the zeroth render target means that you want to use the backbuffer
// as the render target.)
// hack hack hack!!!!! If the render target id > 0 and the user passed in NULL, disable the render target
Dx9Device()->SetRenderTarget( nRenderTargetID, NULL );
}
else
{
Dx9Device()->SetRenderTarget( nRenderTargetID, pColorSurface );
}
}
else
{
Assert( nRenderTargetID == 0 );
SetRenderTargetInternalXbox( colorTextureHandle );
}
// The 0th render target defines which depth buffer we are using, so
// don't bother if we are another render target
if ( nRenderTargetID == 0 )
{
Dx9Device()->SetDepthStencilSurface( pZSurface );
}
// The 0th render target defines the viewport, therefore it also defines
// the viewport limits.
if ( m_UsingTextureRenderTarget && nRenderTargetID == 0 )
{
D3DSURFACE_DESC desc;
HRESULT hr;
if ( !pZSurface )
{
hr = pColorSurface->GetDesc( &desc );
}
else
{
hr = pZSurface->GetDesc( &desc );
}
Assert( !FAILED(hr) );
m_ViewportMaxWidth = desc.Width;
m_ViewportMaxHeight = desc.Height;
}
static bool assert_on_refzero = false;
int ref;
if ( pZSurface )
{
#if POSIX
ref = pZSurface->Release( 0, "-D CShaderAPIDx8::SetRenderTargetEx public release (z surface)");
#else
ref = pZSurface->Release();
#endif
if(assert_on_refzero)
{
Assert( ref != 0 );
}
}
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
if( pColorSurface )
#endif
{
#if POSIX
ref = pColorSurface->Release( 0, "-C CShaderAPIDx8::SetRenderTargetEx public release (color surface)");
#else
ref = pColorSurface->Release();
#endif
if(assert_on_refzero)
{
Assert( ref != 0 );
}
}
// Changing the render target sets a default viewport. Force rewrite to preserve the current desired state.
m_DynamicState.m_Viewport.X = 0;
m_DynamicState.m_Viewport.Y = 0;
m_DynamicState.m_Viewport.Width = (DWORD)-1;
m_DynamicState.m_Viewport.Height = (DWORD)-1;
ADD_COMMIT_FUNC( COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitSetViewports );
}
//-----------------------------------------------------------------------------
// Changes the render target
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetRenderTarget( ShaderAPITextureHandle_t colorTextureHandle, ShaderAPITextureHandle_t depthTextureHandle )
{
LOCK_SHADERAPI();
SetRenderTargetEx( 0, colorTextureHandle, depthTextureHandle );
}
//-----------------------------------------------------------------------------
// Returns the nearest supported format
//-----------------------------------------------------------------------------
ImageFormat CShaderAPIDx8::GetNearestSupportedFormat( ImageFormat fmt, bool bFilteringRequired /* = true */ ) const
{
return FindNearestSupportedFormat( fmt, false, false, bFilteringRequired );
}
ImageFormat CShaderAPIDx8::GetNearestRenderTargetFormat( ImageFormat fmt ) const
{
return FindNearestSupportedFormat( fmt, false, true, false );
}
bool CShaderAPIDx8::DoRenderTargetsNeedSeparateDepthBuffer() const
{
LOCK_SHADERAPI();
return m_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE;
}
//-----------------------------------------------------------------------------
// Indicates we're modifying a texture
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ModifyTexture( ShaderAPITextureHandle_t textureHandle )
{
tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ );
LOCK_SHADERAPI();
// Can't do this if we're locked!
Assert( m_ModifyTextureLockedLevel < 0 );
AssertValidTextureHandle( textureHandle );
m_ModifyTextureHandle = textureHandle;
// If we're got a multi-copy texture, we need to up the current copy count
Texture_t& tex = GetTexture( textureHandle );
if (tex.m_NumCopies > 1)
{
// Each time we modify a texture, we'll want to switch texture
// as soon as a TexImage2D call is made...
tex.m_SwitchNeeded = true;
}
}
//-----------------------------------------------------------------------------
// Advances the current copy of a texture...
//-----------------------------------------------------------------------------
void CShaderAPIDx8::AdvanceCurrentCopy( ShaderAPITextureHandle_t hTexture )
{
// May need to switch textures....
Texture_t& tex = GetTexture( hTexture );
if (tex.m_NumCopies > 1)
{
if (++tex.m_CurrentCopy >= tex.m_NumCopies)
tex.m_CurrentCopy = 0;
// When the current copy changes, we need to make sure this texture
// isn't bound to any stages any more; thereby guaranteeing the new
// copy will be re-bound.
UnbindTexture( hTexture );
}
}
//-----------------------------------------------------------------------------
// Locks, unlocks the current texture
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::TexLock( int level, int cubeFaceID, int xOffset, int yOffset,
int width, int height, CPixelWriter& writer )
{
tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ );
LOCK_SHADERAPI();
Assert( m_ModifyTextureLockedLevel < 0 );
ShaderAPITextureHandle_t hTexture = GetModifyTextureHandle();
if ( !m_Textures.IsValidIndex( hTexture ) )
return false;
// Blow off mip levels if we don't support mipmapping
if ( !g_pHardwareConfig->SupportsMipmapping() && ( level > 0 ) )
return false;
// This test here just makes sure we don't try to download mipmap levels
// if we weren't able to create them in the first place
Texture_t& tex = GetTexture( hTexture );
if ( level >= tex.m_NumLevels )
{
return false;
}
// May need to switch textures....
if ( tex.m_SwitchNeeded )
{
AdvanceCurrentCopy( hTexture );
tex.m_SwitchNeeded = false;
}
IDirect3DBaseTexture *pTexture = GetModifyTexture();
#if defined( _X360 )
// 360 can't lock a bound texture
if ( pTexture->IsSet( Dx9Device() ) )
{
UnbindTexture( hTexture );
}
#endif
bool bOK = LockTexture( hTexture, tex.m_CurrentCopy, pTexture,
level, (D3DCUBEMAP_FACES)cubeFaceID, xOffset, yOffset, width, height, false, writer );
if ( bOK )
{
m_ModifyTextureLockedLevel = level;
m_ModifyTextureLockedFace = cubeFaceID;
}
return bOK;
}
void CShaderAPIDx8::TexUnlock( )
{
tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ );
LOCK_SHADERAPI();
if ( m_ModifyTextureLockedLevel >= 0 )
{
Texture_t& tex = GetTexture( GetModifyTextureHandle() );
UnlockTexture( GetModifyTextureHandle(), tex.m_CurrentCopy, GetModifyTexture(),
m_ModifyTextureLockedLevel, (D3DCUBEMAP_FACES)m_ModifyTextureLockedFace );
m_ModifyTextureLockedLevel = -1;
}
}
//-----------------------------------------------------------------------------
// Texture image upload
//-----------------------------------------------------------------------------
void CShaderAPIDx8::TexImage2D(
int level,
int cubeFaceID,
ImageFormat dstFormat,
int z,
int width,
int height,
ImageFormat srcFormat,
bool bSrcIsTiled,
void *pSrcData )
{
LOCK_SHADERAPI();
Assert( pSrcData );
AssertValidTextureHandle( GetModifyTextureHandle() );
if ( !m_Textures.IsValidIndex( GetModifyTextureHandle() ) )
{
return;
}
Assert( (width <= g_pHardwareConfig->Caps().m_MaxTextureWidth) && (height <= g_pHardwareConfig->Caps().m_MaxTextureHeight) );
// Blow off mip levels if we don't support mipmapping
if ( !g_pHardwareConfig->SupportsMipmapping() && (level > 0))
{
return;
}
// This test here just makes sure we don't try to download mipmap levels
// if we weren't able to create them in the first place
Texture_t& tex = GetTexture( GetModifyTextureHandle() );
if ( level >= tex.m_NumLevels )
{
return;
}
// May need to switch textures....
if (tex.m_SwitchNeeded)
{
AdvanceCurrentCopy( GetModifyTextureHandle() );
tex.m_SwitchNeeded = false;
}
TextureLoadInfo_t info;
info.m_TextureHandle = GetModifyTextureHandle();
info.m_pTexture = GetModifyTexture();
info.m_nLevel = level;
info.m_nCopy = tex.m_CurrentCopy;
info.m_CubeFaceID = (D3DCUBEMAP_FACES)cubeFaceID;
info.m_nWidth = width;
info.m_nHeight = height;
info.m_nZOffset = z;
info.m_SrcFormat = srcFormat;
info.m_pSrcData = (unsigned char *)pSrcData;
#if defined( _X360 )
info.m_bSrcIsTiled = bSrcIsTiled;
info.m_bCanConvertFormat = ( tex.m_Flags & Texture_t::CAN_CONVERT_FORMAT ) != 0;
#else
info.m_bTextureIsLockable = ( tex.m_Flags & Texture_t::IS_LOCKABLE ) != 0;
#endif
LoadTexture( info );
SetModifyTexture( info.m_pTexture );
}
//-----------------------------------------------------------------------------
// Upload to a sub-piece of a texture
//-----------------------------------------------------------------------------
void CShaderAPIDx8::TexSubImage2D(
int level,
int cubeFaceID,
int xOffset,
int yOffset,
int zOffset,
int width,
int height,
ImageFormat srcFormat,
int srcStride,
bool bSrcIsTiled,
void *pSrcData )
{
LOCK_SHADERAPI();
Assert( pSrcData );
AssertValidTextureHandle( GetModifyTextureHandle() );
if ( !m_Textures.IsValidIndex( GetModifyTextureHandle() ) )
{
return;
}
// Blow off mip levels if we don't support mipmapping
if ( !g_pHardwareConfig->SupportsMipmapping() && ( level > 0 ) )
{
return;
}
// NOTE: This can only be done with procedural textures if this method is
// being used to download the entire texture, cause last frame's partial update
// may be in a completely different texture! Sadly, I don't have all of the
// information I need, but I can at least check a couple things....
#ifdef _DEBUG
if ( GetTexture( GetModifyTextureHandle() ).m_NumCopies > 1 )
{
Assert( (xOffset == 0) && (yOffset == 0) );
}
#endif
// This test here just makes sure we don't try to download mipmap levels
// if we weren't able to create them in the first place
Texture_t& tex = GetTexture( GetModifyTextureHandle() );
if ( level >= tex.m_NumLevels )
{
return;
}
// May need to switch textures....
if ( tex.m_SwitchNeeded )
{
AdvanceCurrentCopy( GetModifyTextureHandle() );
tex.m_SwitchNeeded = false;
}
TextureLoadInfo_t info;
info.m_TextureHandle = GetModifyTextureHandle();
info.m_pTexture = GetModifyTexture();
info.m_nLevel = level;
info.m_nCopy = tex.m_CurrentCopy;
info.m_CubeFaceID = (D3DCUBEMAP_FACES)cubeFaceID;
info.m_nWidth = width;
info.m_nHeight = height;
info.m_nZOffset = zOffset;
info.m_SrcFormat = srcFormat;
info.m_pSrcData = (unsigned char *)pSrcData;
#if defined( _X360 )
info.m_bSrcIsTiled = bSrcIsTiled;
info.m_bCanConvertFormat = ( tex.m_Flags & Texture_t::CAN_CONVERT_FORMAT ) != 0;
#else
info.m_bTextureIsLockable = ( tex.m_Flags & Texture_t::IS_LOCKABLE ) != 0;
#endif
LoadSubTexture( info, xOffset, yOffset, srcStride );
}
//-----------------------------------------------------------------------------
// Volume texture upload
//-----------------------------------------------------------------------------
void CShaderAPIDx8::TexImageFromVTF( IVTFTexture *pVTF, int iVTFFrame )
{
LOCK_SHADERAPI();
Assert( pVTF );
AssertValidTextureHandle( GetModifyTextureHandle() );
if ( !m_Textures.IsValidIndex( GetModifyTextureHandle() ) )
{
return;
}
Texture_t& tex = GetTexture( GetModifyTextureHandle() );
// May need to switch textures....
if (tex.m_SwitchNeeded)
{
AdvanceCurrentCopy( GetModifyTextureHandle() );
tex.m_SwitchNeeded = false;
}
TextureLoadInfo_t info;
info.m_TextureHandle = GetModifyTextureHandle();
info.m_pTexture = GetModifyTexture();
info.m_nLevel = 0;
info.m_nCopy = tex.m_CurrentCopy;
info.m_CubeFaceID = (D3DCUBEMAP_FACES)0;
info.m_nWidth = 0;
info.m_nHeight = 0;
info.m_nZOffset = 0;
info.m_SrcFormat = pVTF->Format();
info.m_pSrcData = NULL;
#if defined( _X360 )
info.m_bSrcIsTiled = pVTF->IsPreTiled();
info.m_bCanConvertFormat = ( tex.m_Flags & Texture_t::CAN_CONVERT_FORMAT ) != 0;
#else
info.m_bTextureIsLockable = ( tex.m_Flags & Texture_t::IS_LOCKABLE ) != 0;
#endif
if ( pVTF->Depth() > 1 )
{
LoadVolumeTextureFromVTF( info, pVTF, iVTFFrame );
}
else if ( pVTF->IsCubeMap() )
{
if ( HardwareConfig()->SupportsCubeMaps() )
{
LoadCubeTextureFromVTF( info, pVTF, iVTFFrame );
}
else
{
info.m_CubeFaceID = (D3DCUBEMAP_FACES)6;
LoadTextureFromVTF( info, pVTF, iVTFFrame );
}
}
else
{
LoadTextureFromVTF( info, pVTF, iVTFFrame );
}
SetModifyTexture( info.m_pTexture );
}
//-----------------------------------------------------------------------------
// Is the texture resident?
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::IsTextureResident( ShaderAPITextureHandle_t textureHandle )
{
return true;
}
//-----------------------------------------------------------------------------
// Level of anisotropic filtering
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetAnisotropicLevel( int nAnisotropyLevel )
{
LOCK_SHADERAPI();
// NOTE: This must be called before the rest of the code in this function so
// anisotropic can be set per-texture to force it on! This will also avoid
// a possible infinite loop that existed before.
g_pShaderUtil->NoteAnisotropicLevel( nAnisotropyLevel );
// Never set this to 1. In the case we want it set to 1, we will use this to override
// aniso per-texture, so set it to something reasonable
if ( nAnisotropyLevel > g_pHardwareConfig->Caps().m_nMaxAnisotropy || nAnisotropyLevel <= 1 )
{
// Set it to 1/4 the max but between 2-8
nAnisotropyLevel = max( 2, min( 8, ( g_pHardwareConfig->Caps().m_nMaxAnisotropy / 4 ) ) );
}
// Set the D3D max insotropy state for all samplers
for ( int i = 0; i < g_pHardwareConfig->Caps().m_NumSamplers; ++i)
{
SamplerState(i).m_nAnisotropicLevel = nAnisotropyLevel;
SetSamplerState( i, D3DSAMP_MAXANISOTROPY, SamplerState(i).m_nAnisotropicLevel );
}
}
//-----------------------------------------------------------------------------
// Sets the priority
//-----------------------------------------------------------------------------
void CShaderAPIDx8::TexSetPriority( int priority )
{
#if !defined( _X360 )
LOCK_SHADERAPI();
// A hint to the cacher...
ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle();
if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE )
return;
Texture_t& tex = GetTexture( hModifyTexture );
if ( tex.m_NumCopies > 1 )
{
for (int i = 0; i < tex.m_NumCopies; ++i)
tex.GetTexture( i )->SetPriority( priority );
}
else
{
tex.GetTexture()->SetPriority( priority );
}
#endif
}
void CShaderAPIDx8::TexLodClamp( int finest )
{
LOCK_SHADERAPI();
ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle();
if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE )
return;
Texture_t& tex = GetTexture( hModifyTexture );
tex.m_FinestMipmapLevel = finest;
}
void CShaderAPIDx8::TexLodBias( float bias )
{
LOCK_SHADERAPI();
ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle();
if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE )
return;
Texture_t& tex = GetTexture( hModifyTexture );
tex.m_LodBias = bias;
}
//-----------------------------------------------------------------------------
// Texturemapping state
//-----------------------------------------------------------------------------
void CShaderAPIDx8::TexWrap( ShaderTexCoordComponent_t coord, ShaderTexWrapMode_t wrapMode )
{
LOCK_SHADERAPI();
ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle();
if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE )
return;
D3DTEXTUREADDRESS address;
switch( wrapMode )
{
case SHADER_TEXWRAPMODE_CLAMP:
address = D3DTADDRESS_CLAMP;
break;
case SHADER_TEXWRAPMODE_REPEAT:
address = D3DTADDRESS_WRAP;
break;
case SHADER_TEXWRAPMODE_BORDER:
address = D3DTADDRESS_BORDER;
break;
default:
address = D3DTADDRESS_CLAMP;
Warning( "CShaderAPIDx8::TexWrap: unknown wrapMode\n" );
break;
}
switch( coord )
{
case SHADER_TEXCOORD_S:
GetTexture( hModifyTexture ).m_UTexWrap = address;
break;
case SHADER_TEXCOORD_T:
GetTexture( hModifyTexture ).m_VTexWrap = address;
break;
case SHADER_TEXCOORD_U:
GetTexture( hModifyTexture ).m_WTexWrap = address;
break;
default:
Warning( "CShaderAPIDx8::TexWrap: unknown coord\n" );
break;
}
}
void CShaderAPIDx8::TexMinFilter( ShaderTexFilterMode_t texFilterMode )
{
LOCK_SHADERAPI();
ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle();
if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE )
return;
switch( texFilterMode )
{
case SHADER_TEXFILTERMODE_NEAREST:
GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_POINT;
GetTexture( hModifyTexture ).m_MipFilter = D3DTEXF_NONE;
break;
case SHADER_TEXFILTERMODE_LINEAR:
GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_LINEAR;
GetTexture( hModifyTexture ).m_MipFilter = D3DTEXF_NONE;
break;
case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_NEAREST:
GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_POINT;
GetTexture( hModifyTexture ).m_MipFilter =
GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_POINT : D3DTEXF_NONE;
break;
case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST:
GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_LINEAR;
GetTexture( hModifyTexture ).m_MipFilter =
GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_POINT : D3DTEXF_NONE;
break;
case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_LINEAR:
GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_POINT;
GetTexture( hModifyTexture ).m_MipFilter =
GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_LINEAR : D3DTEXF_NONE;
break;
case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR:
GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_LINEAR;
GetTexture( hModifyTexture ).m_MipFilter =
GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_LINEAR : D3DTEXF_NONE;
break;
case SHADER_TEXFILTERMODE_ANISOTROPIC:
GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_ANISOTROPIC;
GetTexture( hModifyTexture ).m_MipFilter =
GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_LINEAR : D3DTEXF_NONE;
break;
default:
Warning( "CShaderAPIDx8::TexMinFilter: Unknown texFilterMode\n" );
break;
}
}
void CShaderAPIDx8::TexMagFilter( ShaderTexFilterMode_t texFilterMode )
{
LOCK_SHADERAPI();
ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle();
if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE )
return;
switch( texFilterMode )
{
case SHADER_TEXFILTERMODE_NEAREST:
GetTexture( hModifyTexture ).m_MagFilter = D3DTEXF_POINT;
break;
case SHADER_TEXFILTERMODE_LINEAR:
GetTexture( hModifyTexture ).m_MagFilter = D3DTEXF_LINEAR;
break;
case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_NEAREST:
Warning( "CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_NEAREST_MIPMAP_NEAREST is invalid\n" );
break;
case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST:
Warning( "CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST is invalid\n" );
break;
case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_LINEAR:
Warning( "CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_NEAREST_MIPMAP_LINEAR is invalid\n" );
break;
case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR:
Warning( "CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR is invalid\n" );
break;
case SHADER_TEXFILTERMODE_ANISOTROPIC:
GetTexture( hModifyTexture ).m_MagFilter = g_pHardwareConfig->Caps().m_bSupportsMagAnisotropicFiltering ? D3DTEXF_ANISOTROPIC : D3DTEXF_LINEAR;
break;
default:
Warning( "CShaderAPIDx8::TexMAGFilter: Unknown texFilterMode\n" );
break;
}
}
//-----------------------------------------------------------------------------
// Gets the matrix stack from the matrix mode
//-----------------------------------------------------------------------------
int CShaderAPIDx8::GetMatrixStack( MaterialMatrixMode_t mode ) const
{
Assert( mode >= 0 && mode < NUM_MATRIX_MODES );
return mode;
}
//-----------------------------------------------------------------------------
// Returns true if we're modulating constant color into the vertex color
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::IsModulatingVertexColor() const
{
return m_TransitionTable.CurrentShadowShaderState()->m_ModulateConstantColor;
}
//-----------------------------------------------------------------------------
// Material property (used to deal with overbright for lights)
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetDefaultMaterial()
{
#if !defined( _X360 )
D3DMATERIAL mat;
mat.Diffuse.r = mat.Diffuse.g = mat.Diffuse.b = mat.Diffuse.a = 1.0f;
mat.Ambient.r = mat.Ambient.g = mat.Ambient.b = mat.Ambient.a = 0.0f;
mat.Specular.r = mat.Specular.g = mat.Specular.b = mat.Specular.a = 0.0f;
mat.Emissive.r = mat.Emissive.g = mat.Emissive.b = mat.Emissive.a = 0.0f;
mat.Power = 1.0f;
Dx9Device()->SetMaterial( &mat );
#endif
}
//-----------------------------------------------------------------------------
// lighting related methods
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetAmbientLight( float r, float g, float b )
{
LOCK_SHADERAPI();
unsigned int ambient = D3DCOLOR_ARGB( 255, (int)(r * 255),
(int)(g * 255), (int)(b * 255) );
if (ambient != m_DynamicState.m_Ambient)
{
m_DynamicState.m_Ambient = ambient;
SetSupportedRenderState( D3DRS_AMBIENT, ambient );
}
}
void CShaderAPIDx8::SetLightingOrigin( Vector vLightingOrigin )
{
if ( vLightingOrigin != m_DynamicState.m_vLightingOrigin )
{
FlushBufferedPrimitives();
m_DynamicState.m_vLightingOrigin = vLightingOrigin;
}
}
//#define NO_LOCAL_LIGHTS
void CShaderAPIDx8::SetLight( int lightNum, const LightDesc_t& desc_ )
{
LOCK_SHADERAPI();
#ifdef NO_LOCAL_LIGHTS
LightDesc_t desc = desc_;
desc.m_Type = MATERIAL_LIGHT_DISABLE;
#else
LightDesc_t &desc = const_cast<LightDesc_t &>(desc_); // to permit '&'
#endif
Assert( lightNum < g_pHardwareConfig->Caps().m_MaxNumLights && lightNum >= 0 );
if( lightNum >= g_pHardwareConfig->Caps().m_MaxNumLights || lightNum < 0 )
return;
m_DynamicState.m_LightDescs[lightNum] = desc;
FlushBufferedPrimitives();
if (desc.m_Type == MATERIAL_LIGHT_DISABLE)
{
if (m_DynamicState.m_LightEnable[lightNum])
{
m_DynamicState.m_LightEnableChanged[lightNum] = STATE_CHANGED;
m_DynamicState.m_LightEnable[lightNum] = false;
}
return;
}
if (!m_DynamicState.m_LightEnable[lightNum])
{
m_DynamicState.m_LightEnableChanged[lightNum] = STATE_CHANGED;
m_DynamicState.m_LightEnable[lightNum] = true;
}
D3DLIGHT light;
switch( desc.m_Type )
{
case MATERIAL_LIGHT_POINT:
light.Type = D3DLIGHT_POINT;
light.Range = desc.m_Range;
break;
case MATERIAL_LIGHT_DIRECTIONAL:
light.Type = D3DLIGHT_DIRECTIONAL;
light.Range = 1e12; // This is supposed to be ignored
break;
case MATERIAL_LIGHT_SPOT:
light.Type = D3DLIGHT_SPOT;
light.Range = desc.m_Range;
break;
default:
m_DynamicState.m_LightEnable[lightNum] = false;
return;
}
// This is a D3D limitation
Assert( (light.Range >= 0) && (light.Range <= sqrt(FLT_MAX)) );
memcpy( &light.Diffuse, &desc.m_Color[0], 3*sizeof(float) );
memcpy( &light.Specular, &desc.m_Color[0], 3*sizeof(float) );
light.Diffuse.a = 1.0f;
light.Specular.a = 1.0f;
light.Ambient.a = light.Ambient.b = light.Ambient.g = light.Ambient.r = 0;
memcpy( &light.Position, &desc.m_Position[0], 3 * sizeof(float) );
memcpy( &light.Direction, &desc.m_Direction[0], 3 * sizeof(float) );
light.Falloff = desc.m_Falloff;
light.Attenuation0 = desc.m_Attenuation0;
light.Attenuation1 = desc.m_Attenuation1;
light.Attenuation2 = desc.m_Attenuation2;
// normalize light color...
light.Theta = desc.m_Theta;
light.Phi = desc.m_Phi;
if (light.Phi > M_PI)
light.Phi = M_PI;
// This piece of crap line of code is because if theta gets too close to phi,
// we get no light at all.
if (light.Theta - light.Phi > -1e-3)
light.Theta = light.Phi - 1e-3;
m_DynamicState.m_LightChanged[lightNum] = STATE_CHANGED;
memcpy( &m_DynamicState.m_Lights[lightNum], &light, sizeof(light) );
}
void CShaderAPIDx8::DisableAllLocalLights()
{
LOCK_SHADERAPI();
bool bFlushed = false;
for ( int lightNum = 0; lightNum < MAX_NUM_LIGHTS; lightNum++ )
{
if (m_DynamicState.m_LightEnable[lightNum])
{
if ( !bFlushed )
{
FlushBufferedPrimitives();
bFlushed = true;
}
m_DynamicState.m_LightDescs[lightNum].m_Type = MATERIAL_LIGHT_DISABLE;
m_DynamicState.m_LightEnableChanged[lightNum] = STATE_CHANGED;
m_DynamicState.m_LightEnable[lightNum] = false;
}
}
}
int CShaderAPIDx8::GetMaxLights( void ) const
{
return g_pHardwareConfig->Caps().m_MaxNumLights;
}
const LightDesc_t& CShaderAPIDx8::GetLight( int lightNum ) const
{
Assert( lightNum < g_pHardwareConfig->Caps().m_MaxNumLights && lightNum >= 0 );
return m_DynamicState.m_LightDescs[lightNum];
}
//-----------------------------------------------------------------------------
// Ambient cube
//-----------------------------------------------------------------------------
//#define NO_AMBIENT_CUBE 1
void CShaderAPIDx8::SetAmbientLightCube( Vector4D cube[6] )
{
LOCK_SHADERAPI();
/*
int i;
for( i = 0; i < 6; i++ )
{
ColorClamp( cube[i].AsVector3D() );
// if( i == 0 )
// {
// Warning( "%d: %f %f %f\n", i, cube[i][0], cube[i][1], cube[i][2] );
// }
}
*/
if (memcmp(&m_DynamicState.m_AmbientLightCube[0][0], cube, 6 * sizeof(Vector4D)))
{
memcpy( &m_DynamicState.m_AmbientLightCube[0][0], cube, 6 * sizeof(Vector4D) );
#ifdef NO_AMBIENT_CUBE
memset( &m_DynamicState.m_AmbientLightCube[0][0], 0, 6 * sizeof(Vector4D) );
#endif
//#define DEBUG_AMBIENT_CUBE
#ifdef DEBUG_AMBIENT_CUBE
m_DynamicState.m_AmbientLightCube[0][0] = 1.0f;
m_DynamicState.m_AmbientLightCube[0][1] = 0.0f;
m_DynamicState.m_AmbientLightCube[0][2] = 0.0f;
m_DynamicState.m_AmbientLightCube[1][0] = 0.0f;
m_DynamicState.m_AmbientLightCube[1][1] = 1.0f;
m_DynamicState.m_AmbientLightCube[1][2] = 0.0f;
m_DynamicState.m_AmbientLightCube[2][0] = 0.0f;
m_DynamicState.m_AmbientLightCube[2][1] = 0.0f;
m_DynamicState.m_AmbientLightCube[2][2] = 1.0f;
m_DynamicState.m_AmbientLightCube[3][0] = 1.0f;
m_DynamicState.m_AmbientLightCube[3][1] = 0.0f;
m_DynamicState.m_AmbientLightCube[3][2] = 1.0f;
m_DynamicState.m_AmbientLightCube[4][0] = 1.0f;
m_DynamicState.m_AmbientLightCube[4][1] = 1.0f;
m_DynamicState.m_AmbientLightCube[4][2] = 0.0f;
m_DynamicState.m_AmbientLightCube[5][0] = 0.0f;
m_DynamicState.m_AmbientLightCube[5][1] = 1.0f;
m_DynamicState.m_AmbientLightCube[5][2] = 1.0f;
#endif
m_CachedAmbientLightCube = STATE_CHANGED;
}
}
void CShaderAPIDx8::SetVertexShaderStateAmbientLightCube()
{
if (m_CachedAmbientLightCube & STATE_CHANGED_VERTEX_SHADER)
{
SetVertexShaderConstant( VERTEX_SHADER_AMBIENT_LIGHT, m_DynamicState.m_AmbientLightCube[0].Base(), 6 );
m_CachedAmbientLightCube &= ~STATE_CHANGED_VERTEX_SHADER;
}
}
void CShaderAPIDx8::SetPixelShaderStateAmbientLightCube( int pshReg, bool bForceToBlack )
{
float *pCubeBase;
Vector4D tempCube[6];
if( bForceToBlack )
{
for ( int i=0; i<6 ; i++ )
tempCube[i].Init();
pCubeBase = tempCube[0].Base();
}
else
{
pCubeBase = m_DynamicState.m_AmbientLightCube[0].Base();
}
SetPixelShaderConstant( pshReg, pCubeBase, 6 );
}
float CShaderAPIDx8::GetAmbientLightCubeLuminance( void )
{
Vector4D vLuminance( 0.3f, 0.59f, 0.11f, 0.0f );
float fLuminance = 0.0f;
for (int i=0; i<6; i++)
{
fLuminance += vLuminance.Dot( m_DynamicState.m_AmbientLightCube[i] );
}
return fLuminance / 6.0f;
}
static inline RECT* RectToRECT( Rect_t *pSrcRect, RECT &dstRect )
{
if ( !pSrcRect )
return NULL;
dstRect.left = pSrcRect->x;
dstRect.top = pSrcRect->y;
dstRect.right = pSrcRect->x + pSrcRect->width;
dstRect.bottom = pSrcRect->y + pSrcRect->height;
return &dstRect;
}
void CShaderAPIDx8::CopyRenderTargetToTextureEx( ShaderAPITextureHandle_t textureHandle, int nRenderTargetID, Rect_t *pSrcRect, Rect_t *pDstRect )
{
LOCK_SHADERAPI();
VPROF_BUDGET( "CShaderAPIDx8::CopyRenderTargetToTexture", "Refraction overhead" );
if ( !TextureIsAllocated( textureHandle ) )
return;
#if defined( PIX_INSTRUMENTATION )
{
const char *pRT = ( nRenderTargetID < 0 ) ? "DS" : "RT";
if ( textureHandle == SHADER_RENDERTARGET_NONE )
{
pRT = "None";
}
else if ( textureHandle != SHADER_RENDERTARGET_BACKBUFFER )
{
Texture_t &tex = GetTexture( textureHandle );
pRT = tex.m_DebugName.String();
}
char buf[256];
sprintf( buf, "CopyRTToTexture:%s", pRT ? pRT : "?" );
BeginPIXEvent( 0xFFFFFFFF, buf );
EndPIXEvent();
}
#endif
// Don't flush here!! If you have to flush here, then there is a driver bug.
// FlushHardware( );
AssertValidTextureHandle( textureHandle );
Texture_t *pTexture = &GetTexture( textureHandle );
Assert( pTexture );
IDirect3DTexture *pD3DTexture = (IDirect3DTexture *)pTexture->GetTexture();
Assert( pD3DTexture );
#if !defined( _X360 )
IDirect3DSurface* pRenderTargetSurface;
HRESULT hr = Dx9Device()->GetRenderTarget( nRenderTargetID, &pRenderTargetSurface );
if ( FAILED( hr ) )
{
Assert( 0 );
return;
}
IDirect3DSurface *pDstSurf;
hr = pD3DTexture->GetSurfaceLevel( 0, &pDstSurf );
Assert( !FAILED( hr ) );
if ( FAILED( hr ) )
{
pRenderTargetSurface->Release();
return;
}
bool tryblit = true;
if ( tryblit )
{
RECORD_COMMAND( DX8_COPY_FRAMEBUFFER_TO_TEXTURE, 1 );
RECORD_INT( textureHandle );
RECT srcRect, dstRect;
hr = Dx9Device()->StretchRect( pRenderTargetSurface, RectToRECT( pSrcRect, srcRect ),
pDstSurf, RectToRECT( pDstRect, dstRect ), D3DTEXF_LINEAR );
Assert( !FAILED( hr ) );
}
pDstSurf->Release();
pRenderTargetSurface->Release();
#else
DWORD flags = 0;
switch( nRenderTargetID )
{
case -1:
flags = D3DRESOLVE_DEPTHSTENCIL | D3DRESOLVE_FRAGMENT0;
break;
case 0:
flags = D3DRESOLVE_RENDERTARGET0;
break;
case 1:
case 2:
case 3:
// not supporting MRT
Assert( 0 );
return;
NO_DEFAULT
};
// not prepared to handle mip mapping yet
Assert( pD3DTexture->GetLevelCount() == 1 );
D3DPOINT dstPoint = { 0 };
if ( pDstRect )
{
dstPoint.x = pDstRect->x;
dstPoint.y = pDstRect->y;
}
int destWidth, destHeight;
if( pDstRect )
{
destWidth = pDstRect->width;
destHeight = pDstRect->height;
Assert( (destWidth <= pTexture->GetWidth()) && (destHeight <= pTexture->GetHeight()) );
}
else
{
destWidth = pTexture->GetWidth();
destHeight = pTexture->GetHeight();
}
RECT srcRect;
RECT *pResolveRect = NULL;
int srcWidth, srcHeight;
if ( pSrcRect )
{
RectToRECT( pSrcRect, srcRect );
pResolveRect = &srcRect;
// resolve has no stretching ability, and we can only compensate when doing a resolve to a whole texture larger than the source
Assert( !pDstRect || ( pSrcRect->width <= pDstRect->width && pSrcRect->height <= pDstRect->height ) );
srcWidth = pSrcRect->width;
srcHeight = pSrcRect->height;
}
else
{
srcRect.left = srcRect.top = 0;
srcRect.right = m_DynamicState.m_Viewport.Width;
srcRect.bottom = m_DynamicState.m_Viewport.Height;
if( (srcRect.right < 0) || (srcRect.bottom < 0) )
{
if( m_UsingTextureRenderTarget )
{
srcRect.right = m_ViewportMaxWidth;
srcRect.bottom = m_ViewportMaxHeight;
}
else
{
int w,h;
GetBackBufferDimensions( w, h );
srcRect.right = w;
srcRect.bottom = h;
}
}
srcWidth = srcRect.right;
srcHeight = srcRect.bottom;
}
if( (srcWidth != destWidth) || (srcHeight != destHeight) )
{
//Not a 1:1 resolve, we should only have gotten this far if we can downsize the target texture to compensate
Assert( (destWidth > srcWidth) && (destHeight > srcHeight) && (dstPoint.x == 0) && (dstPoint.y == 0) );
//What we're doing is telling D3D that this texture is smaller than it is so the resolve is 1:1.
//We leave the texture in this state until it resolves from something bigger.
//All outside code still thinks this texture is it's original size. And it still owns enough memory to go back to it's original size.
pD3DTexture->Format.Size.TwoD.Width = srcWidth - 1;
pD3DTexture->Format.Size.TwoD.Height = srcHeight - 1; //no idea why they store it as size-1, but they do
pResolveRect = NULL;
}
else
{
//restore D3D texture to full size in case it was previously downsized
pD3DTexture->Format.Size.TwoD.Width = pTexture->GetWidth() - 1;
pD3DTexture->Format.Size.TwoD.Height = pTexture->GetHeight() - 1; //no idea why they store it as size-1, but they do
}
// if we convert to srgb format, we need the original format for reverting. We only need the first DWORD of GPUTEXTURE_FETCH_CONSTANT.
DWORD linearFormatBackup = pD3DTexture->Format.dword[0];
if ( !( flags & D3DRESOLVE_DEPTHSTENCIL ) && ( m_DynamicState.m_bSRGBWritesEnabled ) )
{
// we need a matched resolve regarding sRGB to get values transfered as-is
// when the surface is sRGB, use the corresponding sRGB texture
pD3DTexture->Format.SignX = pD3DTexture->Format.SignY = pD3DTexture->Format.SignZ = 3;
}
HRESULT hr = Dx9Device()->Resolve( flags, (D3DRECT*)pResolveRect, pD3DTexture, &dstPoint, 0, 0, NULL, 0, 0, NULL );
Assert( !FAILED( hr ) );
pD3DTexture->Format.dword[0] = linearFormatBackup;
#endif
}
void CShaderAPIDx8::CopyRenderTargetToScratchTexture( ShaderAPITextureHandle_t srcRt, ShaderAPITextureHandle_t dstTex, Rect_t *pSrcRect, Rect_t *pDstRect )
{
LOCK_SHADERAPI();
if ( !TextureIsAllocated( srcRt ) || !TextureIsAllocated( dstTex ) )
{
Assert( !"Fix that render target or dest texture aren't allocated." );
return;
}
HRESULT hr = D3D_OK;
IDirect3DSurface9* srcSurf = NULL;
AssertValidTextureHandle( srcRt );
Texture_t *pSrcRt = &GetTexture( srcRt );
Assert( pSrcRt );
IDirect3DTexture *pD3DSrcRt = ( IDirect3DTexture * ) pSrcRt->GetTexture();
Assert( pD3DSrcRt );
hr = pD3DSrcRt->GetSurfaceLevel( 0, &srcSurf );
Assert( SUCCEEDED( hr ) && srcSurf );
IDirect3DSurface9* dstSurf = NULL;
AssertValidTextureHandle( dstTex );
Texture_t *pDstTex = &GetTexture( dstTex );
Assert( pDstTex );
IDirect3DTexture *pD3DDstTex = ( IDirect3DTexture * ) pDstTex->GetTexture();
Assert( pD3DDstTex );
hr = pD3DDstTex->GetSurfaceLevel( 0, &dstSurf );
Assert( SUCCEEDED( hr ) && dstSurf );
// This does it.
hr = Dx9Device()->GetRenderTargetData( srcSurf, dstSurf );
Assert( SUCCEEDED( hr ) );
srcSurf->Release();
dstSurf->Release();
}
//-------------------------------------------------------------------------
// Allows locking and unlocking of very specific surface types. pOutBits and pOutPitch will not be touched if
// the lock fails.
//-------------------------------------------------------------------------
void CShaderAPIDx8::LockRect( void** pOutBits, int* pOutPitch, ShaderAPITextureHandle_t texHandle, int mipmap, int x, int y, int w, int h, bool bWrite, bool bRead )
{
LOCK_SHADERAPI();
Assert( pOutBits );
Assert( pOutPitch );
if ( !TextureIsAllocated( texHandle ) )
{
Assert( !"Fix that texture isn't allocated." );
return;
}
HRESULT hr = D3D_OK;
IDirect3DSurface9* surf = NULL;
AssertValidTextureHandle( texHandle );
Texture_t *pTex = &GetTexture( texHandle );
Assert( pTex );
IDirect3DTexture *pD3DTex = ( IDirect3DTexture * ) pTex->GetTexture();
Assert( pD3DTex );
hr = pD3DTex->GetSurfaceLevel( mipmap, &surf );
Assert( SUCCEEDED( hr ) && surf );
D3DLOCKED_RECT lockRect = { 0 };
RECT srcRect = { x, y, w, h };
DWORD flags = 0;
if ( !bRead && !bWrite )
{
Assert( !"Asking to neither read nor write? Probably a caller bug." );
goto cleanup;
}
if ( bRead && !bWrite )
flags = D3DLOCK_READONLY;
hr = surf->LockRect( &lockRect, &srcRect, flags );
if ( FAILED( hr ) )
{
Assert( !"Lock failed, look into why." );
goto cleanup;
}
(*pOutBits) = lockRect.pBits;
(*pOutPitch) = lockRect.Pitch;
cleanup:
surf->Release();
}
void CShaderAPIDx8::UnlockRect( ShaderAPITextureHandle_t texHandle, int mipmap )
{
LOCK_SHADERAPI();
if ( !TextureIsAllocated( texHandle ) )
{
Assert( !"Fix that texture isn't allocated." );
return;
}
HRESULT hr = D3D_OK;
IDirect3DSurface9* surf = NULL;
AssertValidTextureHandle( texHandle );
Texture_t *pTex = &GetTexture( texHandle );
Assert( pTex );
IDirect3DTexture *pD3DTex = ( IDirect3DTexture * ) pTex->GetTexture();
Assert( pD3DTex );
hr = pD3DTex->GetSurfaceLevel( mipmap, &surf );
Assert( SUCCEEDED( hr ) && surf );
hr = surf->UnlockRect();
Assert( SUCCEEDED( hr ) );
surf->Release();
}
static float GetAspectRatio( const Texture_t* pTex )
{
Assert( pTex );
if ( pTex->m_Height != 0 )
return float( pTex->m_Width ) / float( pTex->m_Height );
Assert( !"Height of texture is 0, that seems like a bug." );
return 0.0f;
}
struct TextureExtents_t
{
int width;
int height;
int depth;
int mipmaps;
int fine;
int coarse;
TextureExtents_t() : width( 0 ), height( 0 ), depth( 0 ), mipmaps( 0 ), fine( 0 ), coarse( 0 ) { }
};
// Returns positive integer on success, 0 or <0 on failure.
static int FindCommonMipmapRange( int *pOutSrcFine, int *pOutDstFine, Texture_t *pSrcTex, Texture_t *pDstTex )
{
Assert( pOutSrcFine && pOutDstFine );
Assert( pSrcTex && pDstTex );
if ( GetAspectRatio( pSrcTex ) != GetAspectRatio( pDstTex ) )
return 0;
TextureExtents_t src,
dst;
// LOD Clamp indicates that there's no actual data in the finer mipmap levels yet, so respect it when determining
// the source and destination levels that could have data.
const int srcLodClamp = pSrcTex->GetLodClamp();
src.width = Max( 1, pSrcTex->GetWidth() >> srcLodClamp );
src.height = Max( 1, pSrcTex->GetHeight() >> srcLodClamp );
src.depth = Max( 1, pSrcTex->GetDepth() >> srcLodClamp );
src.mipmaps = pSrcTex->m_NumLevels - srcLodClamp;
Assert( src.mipmaps >= 1 );
const int dstLodClamp = pDstTex->GetLodClamp();
dst.width = Max( 1, pDstTex->GetWidth() >> dstLodClamp );
dst.height = Max( 1, pDstTex->GetHeight() >> dstLodClamp );
dst.depth = Max( 1, pDstTex->GetDepth() >> dstLodClamp );
dst.mipmaps = pDstTex->m_NumLevels - dstLodClamp;
Assert( dst.mipmaps >= 1 );
TextureExtents_t *pLarger = NULL,
*pSmaller = NULL;
if ( src.width >= dst.width && src.height >= dst.height && src.depth >= dst.depth )
{
pLarger = &src;
pSmaller = &dst;
}
else
{
pLarger = &dst;
pSmaller = &src;
}
// Since we are same aspect ratio, only need to test one dimension
while ( ( pLarger->width >> pLarger->fine ) > pSmaller->width )
{
++pLarger->fine;
--pLarger->mipmaps;
}
( *pOutSrcFine ) = src.fine;
( *pOutDstFine ) = dst.fine;
return Min( src.mipmaps, dst.mipmaps );
}
void CShaderAPIDx8::CopyTextureToTexture( ShaderAPITextureHandle_t srcTex, ShaderAPITextureHandle_t dstTex )
{
LOCK_SHADERAPI();
AssertValidTextureHandle( srcTex );
AssertValidTextureHandle( dstTex );
Assert( TextureIsAllocated( srcTex ) && TextureIsAllocated( dstTex ) );
Texture_t *pSrcTex = &GetTexture( srcTex );
Texture_t *pDstTex = &GetTexture( dstTex );
Assert( pSrcTex && pDstTex );
// Must have same image format
Assert( pSrcTex->GetImageFormat() == pDstTex->GetImageFormat() );
int srcFine = 0,
dstFine = 0;
int mipmapCount = FindCommonMipmapRange( &srcFine, &dstFine, pSrcTex, pDstTex );
if ( mipmapCount <= 0 )
{
// This is legit for things that are streamed in that are very small (near the 32x32 cutoff we do at the
// tip of the mipmap pyramid). But leaving it here because it's useful if you're tracking a specific bug.
// Warning( "Attempted to copy textures that had non-overlapping mipmap pyramids. This has failed and no copy has taken place.\n" );
return;
}
IDirect3DTexture* pSrcD3DTex = ( IDirect3DTexture * ) pSrcTex->GetTexture();
IDirect3DTexture* pDstD3DTex = ( IDirect3DTexture * ) pDstTex->GetTexture();
HRESULT hr = S_OK;
for ( int i = 0; i < mipmapCount; ++i )
{
int srcMipmap = srcFine + i;
int dstMipmap = dstFine + i;
IDirect3DSurface9* pSrcSurf = NULL;
IDirect3DSurface9* pDstSurf = NULL;
hr = pSrcD3DTex->GetSurfaceLevel( srcMipmap, &pSrcSurf );
Assert( SUCCEEDED( hr ) && pSrcSurf );
hr = pDstD3DTex->GetSurfaceLevel( dstMipmap, &pDstSurf );
Assert( SUCCEEDED( hr ) && pDstSurf );
hr = g_pD3DDevice->StretchRect( pSrcSurf, NULL, pDstSurf, NULL, D3DTEXF_NONE );
Assert( SUCCEEDED( hr ) );
pSrcSurf->Release();
pDstSurf->Release();
}
}
void CShaderAPIDx8::CopyRenderTargetToTexture( ShaderAPITextureHandle_t textureHandle )
{
LOCK_SHADERAPI();
CopyRenderTargetToTextureEx( textureHandle, 0 );
}
void CShaderAPIDx8::CopyTextureToRenderTargetEx( int nRenderTargetID, ShaderAPITextureHandle_t textureHandle, Rect_t *pSrcRect, Rect_t *pDstRect )
{
LOCK_SHADERAPI();
VPROF( "CShaderAPIDx8::CopyRenderTargetToTexture" );
if ( !TextureIsAllocated( textureHandle ) )
return;
// Don't flush here!! If you have to flush here, then there is a driver bug.
// FlushHardware( );
AssertValidTextureHandle( textureHandle );
Texture_t *pTexture = &GetTexture( textureHandle );
Assert( pTexture );
IDirect3DTexture *pD3DTexture = (IDirect3DTexture *)pTexture->GetTexture();
Assert( pD3DTexture );
#if !defined( _X360 )
IDirect3DSurface* pRenderTargetSurface;
HRESULT hr = Dx9Device()->GetRenderTarget( nRenderTargetID, &pRenderTargetSurface );
if ( FAILED( hr ) )
{
Assert( 0 );
return;
}
IDirect3DSurface *pDstSurf;
hr = pD3DTexture->GetSurfaceLevel( 0, &pDstSurf );
Assert( !FAILED( hr ) );
if ( FAILED( hr ) )
{
pRenderTargetSurface->Release();
return;
}
bool tryblit = true;
if ( tryblit )
{
RECORD_COMMAND( DX8_COPY_FRAMEBUFFER_TO_TEXTURE, 1 );
RECORD_INT( textureHandle );
RECT srcRect, dstRect;
hr = Dx9Device()->StretchRect( pDstSurf, RectToRECT( pSrcRect, srcRect ),
pRenderTargetSurface, RectToRECT( pDstRect, dstRect ), D3DTEXF_LINEAR );
Assert( !FAILED( hr ) );
}
pDstSurf->Release();
pRenderTargetSurface->Release();
#else
Assert( 0 );
#endif
}
//-----------------------------------------------------------------------------
// modifies the vertex data when necessary
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ModifyVertexData( )
{
// this should be a dead code path
Assert( 0 );
#if 0
// We have to modulate the vertex color by the constant color sometimes
if (IsModulatingVertexColor())
{
m_ModifyBuilder.Reset();
float factor[4];
unsigned char* pColor = (unsigned char*)&m_DynamicState.m_ConstantColor;
factor[0] = pColor[0] / 255.0f;
factor[1] = pColor[1] / 255.0f;
factor[2] = pColor[2] / 255.0f;
factor[3] = pColor[3] / 255.0f;
for ( int i = 0; i < m_ModifyBuilder.VertexCount(); ++i )
{
unsigned int color = m_ModifyBuilder.Color();
unsigned char* pVertexColor = (unsigned char*)&color;
pVertexColor[0] = (unsigned char)((float)pVertexColor[0] * factor[0]);
pVertexColor[1] = (unsigned char)((float)pVertexColor[1] * factor[1]);
pVertexColor[2] = (unsigned char)((float)pVertexColor[2] * factor[2]);
pVertexColor[3] = (unsigned char)((float)pVertexColor[3] * factor[3]);
m_ModifyBuilder.Color4ubv( pVertexColor );
m_ModifyBuilder.AdvanceVertex();
}
}
#endif
}
static const char *TextureArgToString( int arg )
{
static char buf[128];
switch( arg & D3DTA_SELECTMASK )
{
case D3DTA_DIFFUSE:
strcpy( buf, "D3DTA_DIFFUSE" );
break;
case D3DTA_CURRENT:
strcpy( buf, "D3DTA_CURRENT" );
break;
case D3DTA_TEXTURE:
strcpy( buf, "D3DTA_TEXTURE" );
break;
case D3DTA_TFACTOR:
strcpy( buf, "D3DTA_TFACTOR" );
break;
case D3DTA_SPECULAR:
strcpy( buf, "D3DTA_SPECULAR" );
break;
case D3DTA_TEMP:
strcpy( buf, "D3DTA_TEMP" );
break;
default:
strcpy( buf, "<ERROR>" );
break;
}
if( arg & D3DTA_COMPLEMENT )
{
strcat( buf, "|D3DTA_COMPLEMENT" );
}
if( arg & D3DTA_ALPHAREPLICATE )
{
strcat( buf, "|D3DTA_ALPHAREPLICATE" );
}
return buf;
}
static const char *TextureOpToString( D3DTEXTUREOP op )
{
switch( op )
{
case D3DTOP_DISABLE:
return "D3DTOP_DISABLE";
case D3DTOP_SELECTARG1:
return "D3DTOP_SELECTARG1";
case D3DTOP_SELECTARG2:
return "D3DTOP_SELECTARG2";
case D3DTOP_MODULATE:
return "D3DTOP_MODULATE";
case D3DTOP_MODULATE2X:
return "D3DTOP_MODULATE2X";
case D3DTOP_MODULATE4X:
return "D3DTOP_MODULATE4X";
case D3DTOP_ADD:
return "D3DTOP_ADD";
case D3DTOP_ADDSIGNED:
return "D3DTOP_ADDSIGNED";
case D3DTOP_ADDSIGNED2X:
return "D3DTOP_ADDSIGNED2X";
case D3DTOP_SUBTRACT:
return "D3DTOP_SUBTRACT";
case D3DTOP_ADDSMOOTH:
return "D3DTOP_ADDSMOOTH";
case D3DTOP_BLENDDIFFUSEALPHA:
return "D3DTOP_BLENDDIFFUSEALPHA";
case D3DTOP_BLENDTEXTUREALPHA:
return "D3DTOP_BLENDTEXTUREALPHA";
case D3DTOP_BLENDFACTORALPHA:
return "D3DTOP_BLENDFACTORALPHA";
case D3DTOP_BLENDTEXTUREALPHAPM:
return "D3DTOP_BLENDTEXTUREALPHAPM";
case D3DTOP_BLENDCURRENTALPHA:
return "D3DTOP_BLENDCURRENTALPHA";
case D3DTOP_PREMODULATE:
return "D3DTOP_PREMODULATE";
case D3DTOP_MODULATEALPHA_ADDCOLOR:
return "D3DTOP_MODULATEALPHA_ADDCOLOR";
case D3DTOP_MODULATECOLOR_ADDALPHA:
return "D3DTOP_MODULATECOLOR_ADDALPHA";
case D3DTOP_MODULATEINVALPHA_ADDCOLOR:
return "D3DTOP_MODULATEINVALPHA_ADDCOLOR";
case D3DTOP_MODULATEINVCOLOR_ADDALPHA:
return "D3DTOP_MODULATEINVCOLOR_ADDALPHA";
case D3DTOP_BUMPENVMAP:
return "D3DTOP_BUMPENVMAP";
case D3DTOP_BUMPENVMAPLUMINANCE:
return "D3DTOP_BUMPENVMAPLUMINANCE";
case D3DTOP_DOTPRODUCT3:
return "D3DTOP_DOTPRODUCT3";
case D3DTOP_MULTIPLYADD:
return "D3DTOP_MULTIPLYADD";
case D3DTOP_LERP:
return "D3DTOP_LERP";
default:
return "<ERROR>";
}
}
static const char *BlendModeToString( int blendMode )
{
switch( blendMode )
{
case D3DBLEND_ZERO:
return "D3DBLEND_ZERO";
case D3DBLEND_ONE:
return "D3DBLEND_ONE";
case D3DBLEND_SRCCOLOR:
return "D3DBLEND_SRCCOLOR";
case D3DBLEND_INVSRCCOLOR:
return "D3DBLEND_INVSRCCOLOR";
case D3DBLEND_SRCALPHA:
return "D3DBLEND_SRCALPHA";
case D3DBLEND_INVSRCALPHA:
return "D3DBLEND_INVSRCALPHA";
case D3DBLEND_DESTALPHA:
return "D3DBLEND_DESTALPHA";
case D3DBLEND_INVDESTALPHA:
return "D3DBLEND_INVDESTALPHA";
case D3DBLEND_DESTCOLOR:
return "D3DBLEND_DESTCOLOR";
case D3DBLEND_INVDESTCOLOR:
return "D3DBLEND_INVDESTCOLOR";
case D3DBLEND_SRCALPHASAT:
return "D3DBLEND_SRCALPHASAT";
#if !defined( _X360 )
case D3DBLEND_BOTHSRCALPHA:
return "D3DBLEND_BOTHSRCALPHA";
case D3DBLEND_BOTHINVSRCALPHA:
return "D3DBLEND_BOTHINVSRCALPHA";
#endif
default:
return "<ERROR>";
}
}
//-----------------------------------------------------------------------------
// Spew Board State
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SpewBoardState()
{
// FIXME: This has regressed
return;
#ifdef DEBUG_BOARD_STATE
/*
{
static ID3DXFont* pFont = 0;
if (!pFont)
{
HFONT hFont = CreateFont( 0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_MODERN, 0 );
Assert( hFont != 0 );
HRESULT hr = D3DXCreateFont( Dx9Device(), hFont, &pFont );
}
static char buf[1024];
static RECT r = { 0, 0, 640, 480 };
if (m_DynamicState.m_VertexBlend == 0)
return;
#if 1
D3DXMATRIX* m = &GetTransform(MATERIAL_MODEL);
D3DXMATRIX* m2 = &GetTransform(MATERIAL_MODEL + 1);
sprintf(buf,"FVF %x\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n",
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n",
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n",
ShaderManager()->GetCurrentVertexShader(),
m->m[0][0], m->m[0][1], m->m[0][2], m->m[0][3],
m->m[1][0], m->m[1][1], m->m[1][2], m->m[1][3],
m->m[2][0], m->m[2][1], m->m[2][2], m->m[2][3],
m->m[3][0], m->m[3][1], m->m[3][2], m->m[3][3],
m2->m[0][0], m2->m[0][1], m2->m[0][2], m2->m[0][3],
m2->m[1][0], m2->m[1][1], m2->m[1][2], m2->m[1][3],
m2->m[2][0], m2->m[2][1], m2->m[2][2], m2->m[2][3],
m2->m[3][0], m2->m[3][1], m2->m[3][2], m2->m[3][3]
);
#else
Vector4D *pVec2 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODELVIEWPROJ];
Vector4D *pVec3 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_VIEWPROJ];
Vector4D *pVec4 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODEL];
sprintf(buf,"\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n",
pVec1[0][0], pVec1[0][1], pVec1[0][2], pVec1[0][3],
pVec1[1][0], pVec1[1][1], pVec1[1][2], pVec1[1][3],
pVec1[2][0], pVec1[2][1], pVec1[2][2], pVec1[2][3],
pVec1[3][0], pVec1[3][1], pVec1[3][2], pVec1[3][3],
pVec2[0][0], pVec2[0][1], pVec2[0][2], pVec2[0][3],
pVec2[1][0], pVec2[1][1], pVec2[1][2], pVec2[1][3],
pVec2[2][0], pVec2[2][1], pVec2[2][2], pVec2[2][3],
pVec2[3][0], pVec2[3][1], pVec2[3][2], pVec2[3][3],
pVec3[0][0], pVec3[0][1], pVec3[0][2], pVec3[0][3],
pVec3[1][0], pVec3[1][1], pVec3[1][2], pVec3[1][3],
pVec3[2][0], pVec3[2][1], pVec3[2][2], pVec3[2][3],
pVec3[3][0], pVec3[3][1], pVec3[3][2], pVec3[3][3],
pVec4[0][0], pVec4[0][1], pVec4[0][2], pVec4[0][3],
pVec4[1][0], pVec4[1][1], pVec4[1][2], pVec4[1][3],
pVec4[2][0], pVec4[2][1], pVec4[2][2], pVec4[2][3],
0, 0, 0, 1
);
#endif
pFont->Begin();
pFont->DrawText( buf, -1, &r, DT_LEFT | DT_TOP,
D3DCOLOR_RGBA( 255, 255, 255, 255 ) );
pFont->End();
return;
}
#if 0
Vector4D *pVec2 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODELVIEWPROJ];
Vector4D *pVec3 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_VIEWPROJ];
Vector4D *pVec4 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODEL];
static char buf2[1024];
sprintf(buf2,"\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n",
pVec1[0][0], pVec1[0][1], pVec1[0][2], pVec1[0][3],
pVec1[1][0], pVec1[1][1], pVec1[1][2], pVec1[1][3],
pVec1[2][0], pVec1[2][1], pVec1[2][2], pVec1[2][3],
pVec1[3][0], pVec1[3][1], pVec1[3][2], pVec1[3][3],
pVec2[0][0], pVec2[0][1], pVec2[0][2], pVec2[0][3],
pVec2[1][0], pVec2[1][1], pVec2[1][2], pVec2[1][3],
pVec2[2][0], pVec2[2][1], pVec2[2][2], pVec2[2][3],
pVec2[3][0], pVec2[3][1], pVec2[3][2], pVec2[3][3],
pVec3[0][0], pVec3[0][1], pVec3[0][2], pVec3[0][3],
pVec3[1][0], pVec3[1][1], pVec3[1][2], pVec3[1][3],
pVec3[2][0], pVec3[2][1], pVec3[2][2], pVec3[2][3],
pVec3[3][0], pVec3[3][1], pVec3[3][2], pVec3[3][3],
pVec4[0][0], pVec4[0][1], pVec4[0][2], pVec4[0][3],
pVec4[1][0], pVec4[1][1], pVec4[1][2], pVec4[1][3],
pVec4[2][0], pVec4[2][1], pVec4[2][2], pVec4[2][3],
0, 0, 0, 1.0f
);
Plat_DebugString(buf2);
return;
#endif
*/
char buf[256];
sprintf(buf, "\nSnapshot id %d : \n", m_TransitionTable.CurrentSnapshot() );
Plat_DebugString(buf);
ShadowState_t &boardState = m_TransitionTable.BoardState();
ShadowShaderState_t &boardShaderState = m_TransitionTable.BoardShaderState();
sprintf(buf,"Depth States: ZFunc %d, ZWrite %d, ZEnable %d, ZBias %d\n",
boardState.m_ZFunc, boardState.m_ZWriteEnable,
boardState.m_ZEnable, boardState.m_ZBias );
Plat_DebugString(buf);
sprintf(buf,"Cull Enable %d Cull Mode %d Color Write %d Fill %d Const Color Mod %d sRGBWriteEnable %d\n",
boardState.m_CullEnable, m_DynamicState.m_CullMode, boardState.m_ColorWriteEnable,
boardState.m_FillMode, boardShaderState.m_ModulateConstantColor, boardState.m_SRGBWriteEnable );
Plat_DebugString(buf);
sprintf(buf,"Blend States: Blend Enable %d Test Enable %d Func %d SrcBlend %d (%s) DstBlend %d (%s)\n",
boardState.m_AlphaBlendEnable, boardState.m_AlphaTestEnable,
boardState.m_AlphaFunc, boardState.m_SrcBlend, BlendModeToString( boardState.m_SrcBlend ),
boardState.m_DestBlend, BlendModeToString( boardState.m_DestBlend ) );
Plat_DebugString(buf);
int len = sprintf(buf,"Alpha Ref %d, Lighting: %d, Ambient Color %x, LightsEnabled ",
boardState.m_AlphaRef, boardState.m_Lighting, m_DynamicState.m_Ambient);
int i;
for ( i = 0; i < g_pHardwareConfig->Caps().m_MaxNumLights; ++i)
{
len += sprintf(buf+len,"%d ", m_DynamicState.m_LightEnable[i] );
}
sprintf(buf+len,"\n");
Plat_DebugString(buf);
sprintf(buf,"Fixed Function: %d, VertexBlend %d\n",
boardState.m_UsingFixedFunction, m_DynamicState.m_VertexBlend );
Plat_DebugString(buf);
sprintf(buf,"Pass Vertex Usage: %llx Pixel Shader %p Vertex Shader %p\n",
boardShaderState.m_VertexUsage, ShaderManager()->GetCurrentPixelShader(),
ShaderManager()->GetCurrentVertexShader() );
Plat_DebugString(buf);
// REGRESSED!!!!
/*
D3DXMATRIX* m = &GetTransform(MATERIAL_MODEL);
sprintf(buf,"WorldMat [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n",
m->m[0][0], m->m[0][1], m->m[0][2], m->m[0][3],
m->m[1][0], m->m[1][1], m->m[1][2], m->m[1][3],
m->m[2][0], m->m[2][1], m->m[2][2], m->m[2][3],
m->m[3][0], m->m[3][1], m->m[3][2], m->m[3][3] );
Plat_DebugString(buf);
m = &GetTransform(MATERIAL_MODEL + 1);
sprintf(buf,"WorldMat2 [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n",
m->m[0][0], m->m[0][1], m->m[0][2], m->m[0][3],
m->m[1][0], m->m[1][1], m->m[1][2], m->m[1][3],
m->m[2][0], m->m[2][1], m->m[2][2], m->m[2][3],
m->m[3][0], m->m[3][1], m->m[3][2], m->m[3][3] );
Plat_DebugString(buf);
m = &GetTransform(MATERIAL_VIEW);
sprintf(buf,"ViewMat [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n",
m->m[0][0], m->m[0][1], m->m[0][2],
m->m[1][0], m->m[1][1], m->m[1][2],
m->m[2][0], m->m[2][1], m->m[2][2],
m->m[3][0], m->m[3][1], m->m[3][2] );
Plat_DebugString(buf);
m = &GetTransform(MATERIAL_PROJECTION);
sprintf(buf,"ProjMat [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n",
m->m[0][0], m->m[0][1], m->m[0][2],
m->m[1][0], m->m[1][1], m->m[1][2],
m->m[2][0], m->m[2][1], m->m[2][2],
m->m[3][0], m->m[3][1], m->m[3][2] );
Plat_DebugString(buf);
for (i = 0; i < GetTextureStageCount(); ++i)
{
m = &GetTransform(MATERIAL_TEXTURE0 + i);
sprintf(buf,"TexMat%d [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n",
i, m->m[0][0], m->m[0][1], m->m[0][2],
m->m[1][0], m->m[1][1], m->m[1][2],
m->m[2][0], m->m[2][1], m->m[2][2],
m->m[3][0], m->m[3][1], m->m[3][2] );
Plat_DebugString(buf);
}
*/
sprintf(buf,"Viewport (%d %d) [%d %d] %4.3f %4.3f\n",
m_DynamicState.m_Viewport.X, m_DynamicState.m_Viewport.Y,
m_DynamicState.m_Viewport.Width, m_DynamicState.m_Viewport.Height,
m_DynamicState.m_Viewport.MinZ, m_DynamicState.m_Viewport.MaxZ);
Plat_DebugString(buf);
for (i = 0; i < MAX_TEXTURE_STAGES; ++i)
{
sprintf(buf,"Stage %d :\n", i);
Plat_DebugString(buf);
sprintf(buf," Color Op: %d (%s) Color Arg1: %d (%s)",
boardState.m_TextureStage[i].m_ColorOp,
TextureOpToString( boardState.m_TextureStage[i].m_ColorOp ),
boardState.m_TextureStage[i].m_ColorArg1,
TextureArgToString( boardState.m_TextureStage[i].m_ColorArg1 ) );
Plat_DebugString(buf);
sprintf( buf, " Color Arg2: %d (%s)\n",
boardState.m_TextureStage[i].m_ColorArg2,
TextureArgToString( boardState.m_TextureStage[i].m_ColorArg2 ) );
Plat_DebugString(buf);
sprintf(buf," Alpha Op: %d (%s) Alpha Arg1: %d (%s)",
boardState.m_TextureStage[i].m_AlphaOp,
TextureOpToString( boardState.m_TextureStage[i].m_AlphaOp ),
boardState.m_TextureStage[i].m_AlphaArg1,
TextureArgToString( boardState.m_TextureStage[i].m_AlphaArg1 ) );
Plat_DebugString(buf);
sprintf(buf," Alpha Arg2: %d (%s)\n",
boardState.m_TextureStage[i].m_AlphaArg2,
TextureArgToString( boardState.m_TextureStage[i].m_AlphaArg2 ) );
Plat_DebugString(buf);
}
for ( int i = 0; i < MAX_SAMPLERS; ++i )
{
sprintf(buf," Texture Enabled: %d Bound Texture: %d UWrap: %d VWrap: %d\n",
SamplerState(i).m_TextureEnable, GetBoundTextureBindId( (Sampler_t)i ),
SamplerState(i).m_UTexWrap, SamplerState(i).m_VTexWrap );
Plat_DebugString(buf);
sprintf(buf," Mag Filter: %d Min Filter: %d Mip Filter: %d\n",
SamplerState(i).m_MagFilter, SamplerState(i).m_MinFilter,
SamplerState(i).m_MipFilter );
sprintf(buf," MaxMipLevel: %d\n", SamplerState(i).m_FinestMipmapLevel );
Plat_DebugString(buf);
}
#else
Plat_DebugString("::SpewBoardState() Not Implemented Yet");
#endif
}
//-----------------------------------------------------------------------------
// Begin a render pass
//-----------------------------------------------------------------------------
void CShaderAPIDx8::BeginPass( StateSnapshot_t snapshot )
{
LOCK_SHADERAPI();
VPROF("CShaderAPIDx8::BeginPass");
if (IsDeactivated())
return;
m_nCurrentSnapshot = snapshot;
// Assert( m_pRenderMesh );
// FIXME: This only does anything with temp meshes, so don't bother yet for the new code.
if( m_pRenderMesh )
{
m_pRenderMesh->BeginPass( );
}
}
//-----------------------------------------------------------------------------
// Render da polygon!
//-----------------------------------------------------------------------------
void CShaderAPIDx8::RenderPass( int nPass, int nPassCount )
{
if ( IsDeactivated() )
return;
Assert( m_nCurrentSnapshot != -1 );
// Assert( m_pRenderMesh ); MESHFIXME
m_TransitionTable.UseSnapshot( m_nCurrentSnapshot );
CommitPerPassStateChanges( m_nCurrentSnapshot );
// Make sure that we bound a texture for every stage that is enabled
// NOTE: not enabled/finished yet... see comment in CShaderAPIDx8::ApplyTextureEnable
// int nSampler;
// for ( nSampler = 0; nSampler < g_pHardwareConfig->GetSamplerCount(); nSampler++ )
// {
// if ( SamplerState( nSampler ).m_TextureEnable )
// {
// }
// }
#ifdef DEBUG_BOARD_STATE
// Spew out render state...
if ( m_pMaterial->PerformDebugTrace() )
{
SpewBoardState();
}
#endif
#ifdef TEST_CACHE_LOCKS
g_pDataCache->Flush();
#endif
// Assert( m_pRenderMesh ); MESHFIXME
if ( m_pRenderMesh )
{
m_pRenderMesh->RenderPass();
}
else
{
MeshMgr()->RenderPassWithVertexAndIndexBuffers();
}
m_nCurrentSnapshot = -1;
}
//-----------------------------------------------------------------------------
// Matrix mode
//-----------------------------------------------------------------------------
void CShaderAPIDx8::MatrixMode( MaterialMatrixMode_t matrixMode )
{
// NOTE!!!!!!
// The only time that m_MatrixMode is used is for texture matrices. Do not use
// it for anything else unless you change this code!
if ( matrixMode >= MATERIAL_TEXTURE0 && matrixMode <= MATERIAL_TEXTURE7 )
{
m_MatrixMode = ( D3DTRANSFORMSTATETYPE )( matrixMode - MATERIAL_TEXTURE0 + D3DTS_TEXTURE0 );
}
else
{
m_MatrixMode = (D3DTRANSFORMSTATETYPE)-1;
}
m_CurrStack = GetMatrixStack( matrixMode );
}
//-----------------------------------------------------------------------------
// the current camera position in world space.
//-----------------------------------------------------------------------------
void CShaderAPIDx8::GetWorldSpaceCameraPosition( float* pPos ) const
{
memcpy( pPos, m_WorldSpaceCameraPositon.Base(), sizeof( float[3] ) );
}
void CShaderAPIDx8::CacheWorldSpaceCameraPosition()
{
D3DXMATRIX& view = GetTransform(MATERIAL_VIEW);
m_WorldSpaceCameraPositon[0] =
-( view( 3, 0 ) * view( 0, 0 ) +
view( 3, 1 ) * view( 0, 1 ) +
view( 3, 2 ) * view( 0, 2 ) );
m_WorldSpaceCameraPositon[1] =
-( view( 3, 0 ) * view( 1, 0 ) +
view( 3, 1 ) * view( 1, 1 ) +
view( 3, 2 ) * view( 1, 2 ) );
m_WorldSpaceCameraPositon[2] =
-( view( 3, 0 ) * view( 2, 0 ) +
view( 3, 1 ) * view( 2, 1 ) +
view( 3, 2 ) * view( 2, 2 ) );
m_WorldSpaceCameraPositon[3] = 1.0f;
// Protect against zero, as some pixel shaders will divide by this in CalcWaterFogAlpha() in common_ps_fxc.h
if ( fabs( m_WorldSpaceCameraPositon[2] ) <= 0.00001f )
{
m_WorldSpaceCameraPositon[2] = 0.01f;
}
}
//-----------------------------------------------------------------------------
// Computes a matrix which includes the poly offset given an initial projection matrix
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ComputePolyOffsetMatrix( const D3DXMATRIX& matProjection, D3DXMATRIX &matProjectionOffset )
{
// We never need to do this on hardware that can handle zbias
if ( g_pHardwareConfig->Caps().m_ZBiasAndSlopeScaledDepthBiasSupported )
return;
float offsetVal =
-1.0f * (m_DesiredState.m_Viewport.MaxZ - m_DesiredState.m_Viewport.MinZ) /
16384.0f;
D3DXMATRIX offset;
D3DXMatrixTranslation( &offset, 0.0f, 0.0f, offsetVal );
D3DXMatrixMultiply( &matProjectionOffset, &matProjection, &offset );
}
//-----------------------------------------------------------------------------
// Caches off the poly-offset projection matrix
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CachePolyOffsetProjectionMatrix()
{
ComputePolyOffsetMatrix( GetTransform(MATERIAL_PROJECTION), m_CachedPolyOffsetProjectionMatrix );
}
//-----------------------------------------------------------------------------
// Performs a flush on the matrix state if necessary
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::MatrixIsChanging( TransformType_t type )
{
if ( IsDeactivated() )
{
return false;
}
// early out if the transform is already one of our standard types
if ((type != TRANSFORM_IS_GENERAL) && (type == m_DynamicState.m_TransformType[m_CurrStack]))
return false;
// Only flush state if we're changing something other than a texture transform
int textureMatrix = m_CurrStack - MATERIAL_TEXTURE0;
if (( textureMatrix < 0 ) || (textureMatrix >= NUM_TEXTURE_TRANSFORMS))
FlushBufferedPrimitivesInternal();
return true;
}
void CShaderAPIDx8::SetTextureTransformDimension( TextureStage_t textureMatrix, int dimension, bool projected )
{
D3DTEXTURETRANSFORMFLAGS textureTransformFlags = ( D3DTEXTURETRANSFORMFLAGS )dimension;
if( projected )
{
Assert( sizeof( int ) == sizeof( D3DTEXTURETRANSFORMFLAGS ) );
( *( int * )&textureTransformFlags ) |= D3DTTFF_PROJECTED;
}
if (TextureStage(textureMatrix).m_TextureTransformFlags != textureTransformFlags )
{
SetTextureStageState( textureMatrix, D3DTSS_TEXTURETRANSFORMFLAGS, textureTransformFlags );
TextureStage(textureMatrix).m_TextureTransformFlags = textureTransformFlags;
}
}
void CShaderAPIDx8::DisableTextureTransform( TextureStage_t textureMatrix )
{
if (TextureStage(textureMatrix).m_TextureTransformFlags != D3DTTFF_DISABLE )
{
SetTextureStageState( textureMatrix, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
TextureStage(textureMatrix).m_TextureTransformFlags = D3DTTFF_DISABLE;
}
}
void CShaderAPIDx8::SetBumpEnvMatrix( TextureStage_t textureStage, float m00, float m01, float m10, float m11 )
{
TextureStageState_t &textureStageState = TextureStage( textureStage );
if( textureStageState.m_BumpEnvMat00 != m00 ||
textureStageState.m_BumpEnvMat01 != m01 ||
textureStageState.m_BumpEnvMat10 != m10 ||
textureStageState.m_BumpEnvMat11 != m11 )
{
SetTextureStageState( textureStage, D3DTSS_BUMPENVMAT00, *( ( LPDWORD ) (&m00) ) );
SetTextureStageState( textureStage, D3DTSS_BUMPENVMAT01, *( ( LPDWORD ) (&m01) ) );
SetTextureStageState( textureStage, D3DTSS_BUMPENVMAT10, *( ( LPDWORD ) (&m10) ) );
SetTextureStageState( textureStage, D3DTSS_BUMPENVMAT11, *( ( LPDWORD ) (&m11) ) );
textureStageState.m_BumpEnvMat00 = m00;
textureStageState.m_BumpEnvMat01 = m01;
textureStageState.m_BumpEnvMat10 = m10;
textureStageState.m_BumpEnvMat11 = m11;
}
}
//-----------------------------------------------------------------------------
// Sets the actual matrix state
//-----------------------------------------------------------------------------
void CShaderAPIDx8::UpdateMatrixTransform( TransformType_t type )
{
int textureMatrix = m_CurrStack - MATERIAL_TEXTURE0;
if (( textureMatrix >= 0 ) && (textureMatrix < NUM_TEXTURE_TRANSFORMS))
{
// NOTE: Flush shouldn't happen here because we
// expect that texture transforms will be set within the shader
// FIXME: We only want to use D3DTTFF_COUNT3 for cubemaps
// D3DTFF_COUNT2 is used for non-cubemaps. Of course, if there's
// no performance penalty for COUNT3, we should just use that.
D3DTEXTURETRANSFORMFLAGS transformFlags;
transformFlags = (type == TRANSFORM_IS_IDENTITY) ? D3DTTFF_DISABLE : D3DTTFF_COUNT3;
if (TextureStage(textureMatrix).m_TextureTransformFlags != transformFlags )
{
SetTextureStageState( textureMatrix, D3DTSS_TEXTURETRANSFORMFLAGS, transformFlags );
TextureStage(textureMatrix).m_TextureTransformFlags = transformFlags;
}
}
m_DynamicState.m_TransformType[m_CurrStack] = type;
m_DynamicState.m_TransformChanged[m_CurrStack] = STATE_CHANGED;
#ifdef _DEBUG
// Store off the board state
D3DXMATRIX *pSrc = &GetTransform(m_CurrStack);
D3DXMATRIX *pDst = &m_DynamicState.m_Transform[m_CurrStack];
// Assert( *pSrc != *pDst );
memcpy( pDst, pSrc, sizeof(D3DXMATRIX) );
#endif
if ( m_CurrStack == MATERIAL_VIEW )
{
CacheWorldSpaceCameraPosition();
}
if ( !IsX360() && m_CurrStack == MATERIAL_PROJECTION )
{
CachePolyOffsetProjectionMatrix();
}
// Any time the view or projection matrix changes, the user clip planes need recomputing....
// Assuming we're not overriding the user clip transform
if ( ( m_CurrStack == MATERIAL_PROJECTION ) ||
( ( m_CurrStack == MATERIAL_VIEW ) && ( !m_DynamicState.m_bUserClipTransformOverride ) ) )
{
MarkAllUserClipPlanesDirty();
}
// Set the state if it's a texture transform
if ( (m_CurrStack >= MATERIAL_TEXTURE0) && (m_CurrStack <= MATERIAL_TEXTURE7) )
{
SetTransform( m_MatrixMode, &GetTransform(m_CurrStack) );
}
}
//--------------------------------------------------------------------------------
// deformations
//--------------------------------------------------------------------------------
void CShaderAPIDx8::PushDeformation( DeformationBase_t const *pDef )
{
Assert( m_pDeformationStackPtr > m_DeformationStack );
--m_pDeformationStackPtr;
m_pDeformationStackPtr->m_nDeformationType = pDef->m_eType;
switch( pDef->m_eType )
{
case DEFORMATION_CLAMP_TO_BOX_IN_WORLDSPACE:
{
BoxDeformation_t const *pBox = reinterpret_cast< const BoxDeformation_t *>( pDef );
m_pDeformationStackPtr->m_nNumParameters = 16;
memcpy( m_pDeformationStackPtr->m_flDeformationParameters, &( pBox->m_SourceMins.x ), 16 * sizeof( float ) );
break;
}
break;
default:
Assert( 0 );
}
}
void CShaderAPIDx8::PopDeformation( )
{
Assert( m_pDeformationStackPtr != m_DeformationStack + DEFORMATION_STACK_DEPTH );
++m_pDeformationStackPtr;
}
int CShaderAPIDx8::GetNumActiveDeformations( void ) const
{
return ( m_DeformationStack + DEFORMATION_STACK_DEPTH ) - m_pDeformationStackPtr;
}
// for shaders to set vertex shader constants. returns a packed state which can be used to set the dynamic combo
int CShaderAPIDx8::GetPackedDeformationInformation( int nMaskOfUnderstoodDeformations,
float *pConstantValuesOut,
int nBufferSize,
int nMaximumDeformations,
int *pDefCombosOut ) const
{
int nCombosFound = 0;
memset( pDefCombosOut, 0, sizeof( pDefCombosOut[0] ) * nMaximumDeformations );
size_t nRemainingBufferSize = nBufferSize;
for( const Deformation_t *i = m_DeformationStack + DEFORMATION_STACK_DEPTH -1; i >= m_pDeformationStackPtr; i-- )
{
int nFloatsOut = 4 * ( ( i->m_nNumParameters + 3 )>> 2 );
if (
( ( 1 << i->m_nDeformationType ) & nMaskOfUnderstoodDeformations ) &&
( nRemainingBufferSize >= ( nFloatsOut * sizeof( float ) ) ) )
{
memcpy( pConstantValuesOut, i->m_flDeformationParameters, nFloatsOut * sizeof( float ) );
pConstantValuesOut += nFloatsOut;
nRemainingBufferSize -= nFloatsOut * sizeof( float );
( *pDefCombosOut++ ) = i->m_nDeformationType;
nCombosFound++;
}
}
return nCombosFound;
}
//-----------------------------------------------------------------------------
// Matrix stack operations
//-----------------------------------------------------------------------------
void CShaderAPIDx8::PushMatrix()
{
// NOTE: No matrix transform update needed here.
m_pMatrixStack[m_CurrStack]->Push();
}
void CShaderAPIDx8::PopMatrix()
{
if (MatrixIsChanging())
{
m_pMatrixStack[m_CurrStack]->Pop();
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::LoadIdentity( )
{
if (MatrixIsChanging(TRANSFORM_IS_IDENTITY))
{
m_pMatrixStack[m_CurrStack]->LoadIdentity( );
UpdateMatrixTransform( TRANSFORM_IS_IDENTITY );
}
}
void CShaderAPIDx8::LoadCameraToWorld( )
{
if (MatrixIsChanging(TRANSFORM_IS_CAMERA_TO_WORLD))
{
// could just use the transpose instead, if we know there's no scale
float det;
D3DXMATRIX inv;
D3DXMatrixInverse( &inv, &det, &GetTransform(MATERIAL_VIEW) );
// Kill translation
inv.m[3][0] = inv.m[3][1] = inv.m[3][2] = 0.0f;
m_pMatrixStack[m_CurrStack]->LoadMatrix( &inv );
UpdateMatrixTransform( TRANSFORM_IS_CAMERA_TO_WORLD );
}
}
void CShaderAPIDx8::LoadMatrix( float *m )
{
// Check for identity...
if ( (fabs(m[0] - 1.0f) < 1e-3) && (fabs(m[5] - 1.0f) < 1e-3) && (fabs(m[10] - 1.0f) < 1e-3) && (fabs(m[15] - 1.0f) < 1e-3) &&
(fabs(m[1]) < 1e-3) && (fabs(m[2]) < 1e-3) && (fabs(m[3]) < 1e-3) &&
(fabs(m[4]) < 1e-3) && (fabs(m[6]) < 1e-3) && (fabs(m[7]) < 1e-3) &&
(fabs(m[8]) < 1e-3) && (fabs(m[9]) < 1e-3) && (fabs(m[11]) < 1e-3) &&
(fabs(m[12]) < 1e-3) && (fabs(m[13]) < 1e-3) && (fabs(m[14]) < 1e-3) )
{
LoadIdentity();
return;
}
if (MatrixIsChanging())
{
m_pMatrixStack[m_CurrStack]->LoadMatrix( (D3DXMATRIX*)m );
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::LoadBoneMatrix( int boneIndex, const float *m )
{
if ( IsDeactivated() )
return;
memcpy( m_boneMatrix[boneIndex].Base(), m, sizeof(float)*12 );
if ( boneIndex > m_maxBoneLoaded )
{
m_maxBoneLoaded = boneIndex;
}
if ( boneIndex == 0 )
{
MatrixMode( MATERIAL_MODEL );
VMatrix transposeMatrix;
transposeMatrix.Init( *(matrix3x4_t *)m );
MatrixTranspose( transposeMatrix, transposeMatrix );
LoadMatrix( (float*)transposeMatrix.m );
}
}
//-----------------------------------------------------------------------------
// Commits morph target factors
//-----------------------------------------------------------------------------
static void CommitFlexWeights( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState,
DynamicState_t &currentState, bool bForce )
{
if ( IsX360() )
{
// not supporting for 360
return;
}
CommitVertexShaderConstantRange( pDevice, desiredState, currentState, bForce,
VERTEX_SHADER_FLEX_WEIGHTS, VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT );
}
void CShaderAPIDx8::SetFlexWeights( int nFirstWeight, int nCount, const MorphWeight_t* pWeights )
{
if ( IsX360() )
{
// not supported for 360
return;
}
LOCK_SHADERAPI();
if ( g_pHardwareConfig->Caps().m_NumVertexShaderConstants < VERTEX_SHADER_FLEX_WEIGHTS + VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT )
return;
if ( nFirstWeight + nCount > VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT )
{
Warning( "Attempted to set too many flex weights! Max is %d\n", VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT );
nCount = VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT - nFirstWeight;
}
if ( nCount <= 0 )
return;
float *pDest = m_DesiredState.m_pVectorVertexShaderConstant[ VERTEX_SHADER_FLEX_WEIGHTS + nFirstWeight ].Base();
memcpy( pDest, pWeights, nCount * sizeof(MorphWeight_t) );
ADD_COMMIT_FUNC( COMMIT_PER_DRAW, COMMIT_VERTEX_SHADER, CommitFlexWeights );
}
void CShaderAPIDx8::MultMatrix( float *m )
{
if (MatrixIsChanging())
{
m_pMatrixStack[m_CurrStack]->MultMatrix( (D3DXMATRIX*)m );
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::MultMatrixLocal( float *m )
{
if (MatrixIsChanging())
{
m_pMatrixStack[m_CurrStack]->MultMatrixLocal( (D3DXMATRIX*)m );
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::Rotate( float angle, float x, float y, float z )
{
if (MatrixIsChanging())
{
D3DXVECTOR3 axis( x, y, z );
m_pMatrixStack[m_CurrStack]->RotateAxisLocal( &axis, M_PI * angle / 180.0f );
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::Translate( float x, float y, float z )
{
if (MatrixIsChanging())
{
m_pMatrixStack[m_CurrStack]->TranslateLocal( x, y, z );
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::Scale( float x, float y, float z )
{
if (MatrixIsChanging())
{
m_pMatrixStack[m_CurrStack]->ScaleLocal( x, y, z );
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::ScaleXY( float x, float y )
{
if (MatrixIsChanging())
{
m_pMatrixStack[m_CurrStack]->ScaleLocal( x, y, 1.0f );
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::Ortho( double left, double top, double right, double bottom, double zNear, double zFar )
{
if (MatrixIsChanging())
{
D3DXMATRIX matrix;
// FIXME: This is being used incorrectly! Should read:
// D3DXMatrixOrthoOffCenterRH( &matrix, left, right, bottom, top, zNear, zFar );
// Which is certainly why we need these extra -1 scales in y. Bleah
// NOTE: The camera can be imagined as the following diagram:
// /z
// /
// /____ x Z is going into the screen
// |
// |
// |y
//
// (0,0,z) represents the upper-left corner of the screen.
// Our projection transform needs to transform from this space to a LH coordinate
// system that looks thusly:
//
// y| /z
// | /
// |/____ x Z is going into the screen
//
// Where x,y lies between -1 and 1, and z lies from 0 to 1
// This is because the viewport transformation from projection space to pixels
// introduces a -1 scale in the y coordinates
// D3DXMatrixOrthoOffCenterLH( &matrix, left, right, bottom, top, zNear, zFar );
D3DXMatrixOrthoOffCenterRH( &matrix, left, right, top, bottom, zNear, zFar );
m_pMatrixStack[m_CurrStack]->MultMatrixLocal(&matrix);
Assert( m_CurrStack == MATERIAL_PROJECTION );
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::PerspectiveX( double fovx, double aspect, double zNear, double zFar )
{
if (MatrixIsChanging())
{
float width = 2 * zNear * tan( fovx * M_PI / 360.0 );
float height = width / aspect;
Assert( m_CurrStack == MATERIAL_PROJECTION );
D3DXMATRIX rh;
D3DXMatrixPerspectiveRH( &rh, width, height, zNear, zFar );
m_pMatrixStack[m_CurrStack]->MultMatrixLocal(&rh);
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::PerspectiveOffCenterX( double fovx, double aspect, double zNear, double zFar, double bottom, double top, double left, double right )
{
if (MatrixIsChanging())
{
float width = 2 * zNear * tan( fovx * M_PI / 360.0 );
float height = width / aspect;
// bottom, top, left, right are 0..1 so convert to -1..1
float flFrontPlaneLeft = -(width/2.0f) * (1.0f - left) + left * (width/2.0f);
float flFrontPlaneRight = -(width/2.0f) * (1.0f - right) + right * (width/2.0f);
float flFrontPlaneBottom = -(height/2.0f) * (1.0f - bottom) + bottom * (height/2.0f);
float flFrontPlaneTop = -(height/2.0f) * (1.0f - top) + top * (height/2.0f);
Assert( m_CurrStack == MATERIAL_PROJECTION );
D3DXMATRIX rh;
D3DXMatrixPerspectiveOffCenterRH( &rh, flFrontPlaneLeft, flFrontPlaneRight, flFrontPlaneBottom, flFrontPlaneTop, zNear, zFar );
m_pMatrixStack[m_CurrStack]->MultMatrixLocal(&rh);
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::PickMatrix( int x, int y, int width, int height )
{
if (MatrixIsChanging())
{
Assert( m_CurrStack == MATERIAL_PROJECTION );
// This is going to create a matrix to append to the standard projection.
// Projection space goes from -1 to 1 in x and y. This matrix we append
// will transform the pick region to -1 to 1 in projection space
ShaderViewport_t viewport;
GetViewports( &viewport, 1 );
int vx = viewport.m_nTopLeftX;
int vy = viewport.m_nTopLeftX;
int vwidth = viewport.m_nWidth;
int vheight = viewport.m_nHeight;
// Compute the location of the pick region in projection space...
float px = 2.0 * (float)(x - vx) / (float)vwidth - 1;
float py = 2.0 * (float)(y - vy)/ (float)vheight - 1;
float pw = 2.0 * (float)width / (float)vwidth;
float ph = 2.0 * (float)height / (float)vheight;
// we need to translate (px, py) to the origin
// and scale so (pw,ph) -> (2, 2)
D3DXMATRIX matrix;
D3DXMatrixIdentity( &matrix );
matrix.m[0][0] = 2.0 / pw;
matrix.m[1][1] = 2.0 / ph;
matrix.m[3][0] = -2.0 * px / pw;
matrix.m[3][1] = -2.0 * py / ph;
m_pMatrixStack[m_CurrStack]->MultMatrixLocal(&matrix);
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::GetMatrix( MaterialMatrixMode_t matrixMode, float *dst )
{
memcpy( dst, (void*)(FLOAT*)GetTransform(matrixMode), sizeof(D3DXMATRIX) );
}
//-----------------------------------------------------------------------------
// Did a transform change?
//-----------------------------------------------------------------------------
inline bool CShaderAPIDx8::VertexShaderTransformChanged( int i )
{
return (m_DynamicState.m_TransformChanged[i] & STATE_CHANGED_VERTEX_SHADER) != 0;
}
inline bool CShaderAPIDx8::FixedFunctionTransformChanged( int i )
{
return (m_DynamicState.m_TransformChanged[i] & STATE_CHANGED_FIXED_FUNCTION) != 0;
}
const D3DXMATRIX &CShaderAPIDx8::GetProjectionMatrix( void )
{
bool bUsingZBiasProjectionMatrix =
!g_pHardwareConfig->Caps().m_ZBiasAndSlopeScaledDepthBiasSupported &&
( m_TransitionTable.CurrentSnapshot() != -1 ) &&
m_TransitionTable.CurrentShadowState() &&
m_TransitionTable.CurrentShadowState()->m_ZBias;
if ( !m_DynamicState.m_FastClipEnabled )
{
if ( bUsingZBiasProjectionMatrix )
return m_CachedPolyOffsetProjectionMatrix;
return GetTransform( MATERIAL_PROJECTION );
}
if ( bUsingZBiasProjectionMatrix )
return m_CachedFastClipPolyOffsetProjectionMatrix;
return m_CachedFastClipProjectionMatrix;
}
//-----------------------------------------------------------------------------
// Workaround hack for visualization of selection mode
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetupSelectionModeVisualizationState()
{
Dx9Device()->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
D3DXMATRIX ident;
D3DXMatrixIdentity( &ident );
SetTransform( D3DTS_WORLD, &ident );
SetTransform( D3DTS_VIEW, &ident );
SetTransform( D3DTS_PROJECTION, &ident );
if ( g_pHardwareConfig->Caps().m_SupportsPixelShaders )
{
SetVertexShaderConstant( VERTEX_SHADER_VIEWPROJ, ident, 4 );
SetVertexShaderConstant( VERTEX_SHADER_MODELVIEWPROJ, ident, 4 );
float *pRowTwo = (float *)ident + 8;
SetVertexShaderConstant( VERTEX_SHADER_MODELVIEWPROJ_THIRD_ROW, pRowTwo, 1 ); // Row two of an identity matrix
SetVertexShaderConstant( VERTEX_SHADER_MODEL, ident, 3 * NUM_MODEL_TRANSFORMS );
}
}
//-----------------------------------------------------------------------------
// Set view transforms
//-----------------------------------------------------------------------------
static void printmat4x4( char *label, float *m00 )
{
// print label..
// fetch 4 from row, print as a row
// fetch 4 from column, print as a row
#ifdef DX_TO_GL_ABSTRACTION
float row[4];
float col[4];
GLMPRINTF(("-M- -- %s --", label ));
for( int n=0; n<4; n++ )
{
// extract row and column floats
for( int i=0; i<4;i++)
{
row[i] = m00[(n*4)+i];
col[i] = m00[(i*4)+n];
}
GLMPRINTF(( "-M- [ %7.4f %7.4f %7.4f %7.4f ] T=> [ %7.4f %7.4f %7.4f %7.4f ]",
row[0],row[1],row[2],row[3],
col[0],col[1],col[2],col[3]
));
}
GLMPRINTF(("-M-"));
#endif
}
void CShaderAPIDx8::SetVertexShaderViewProj()
{
//GLM_FUNC;
//GLMPRINTF(( ">-M- SetVertexShaderViewProj" ));
if (g_pHardwareConfig->Caps().m_SupportsPixelShaders)
{
D3DXMATRIX transpose;
if(0)
{
transpose = GetTransform(MATERIAL_VIEW) * GetProjectionMatrix();
D3DXMatrixTranspose( &transpose, &transpose );
}
else
{
// show work
D3DXMATRIX matView,matProj;
matView = GetTransform(MATERIAL_VIEW);
matProj = GetProjectionMatrix();
transpose = matView * matProj;
//printmat4x4( "matView", (float*)&matView );
//printmat4x4( "matProj", (float*)&matProj );
//printmat4x4( "result (view * proj) pre-transpose", (float*)&transpose );
D3DXMatrixTranspose( &transpose, &transpose );
#if 0 // turned off while we try to do fixup-Y in shader translate
if (IsPosix()) // flip all shader projection matrices for Y on GL since you can't have an upside-down viewport specification
{
// flip Y
transpose._21 *= -1.0f;
transpose._22 *= -1.0f;
transpose._23 *= -1.0f;
transpose._24 *= -1.0f;
}
#endif
//printmat4x4( "result (view * proj) post-transpose", (float*)&transpose );
}
SetVertexShaderConstant( VERTEX_SHADER_VIEWPROJ, transpose, 4 );
// If we're doing FastClip, the above viewproj matrix won't work well for
// vertex shaders which compute projPos.z, hence we'll compute a more useful
// viewproj and put the third row of it in another constant
transpose = GetTransform( MATERIAL_VIEW ) * GetTransform( MATERIAL_PROJECTION ); // Get the non-FastClip projection matrix
D3DXMatrixTranspose( &transpose, &transpose );
float *pRowTwo = (float *)transpose + 8;
SetVertexShaderConstant( VERTEX_SHADER_VIEWPROJ_THIRD_ROW, pRowTwo, 1 );
}
//GLMPRINTF(( "<-M- SetVertexShaderViewProj" ));
}
void CShaderAPIDx8::SetVertexShaderModelViewProjAndModelView( void )
{
//GLM_FUNC;
//GLMPRINTF(( ">-M- SetVertexShaderModelViewProjAndModelView" ));
if (g_pHardwareConfig->Caps().m_SupportsPixelShaders)
{
D3DXMATRIX modelView, transpose;
if (0)
{
D3DXMatrixMultiply( &modelView, &GetTransform(MATERIAL_MODEL), &GetTransform(MATERIAL_VIEW) );
D3DXMatrixMultiply( &transpose, &modelView, &GetProjectionMatrix() );
}
else
{
// show work
D3DXMATRIX matView,matProj,matModel;
matModel = GetTransform(MATERIAL_MODEL);
matView = GetTransform(MATERIAL_VIEW);
matProj = GetProjectionMatrix();
D3DXMatrixMultiply( &modelView, &matModel, &matView );
D3DXMatrixMultiply( &transpose, &modelView, &matProj );
//printmat4x4( "matModel", (float*)&matModel );
//printmat4x4( "matView", (float*)&matView );
//printmat4x4( "matProj", (float*)&matProj );
//printmat4x4( "result (model * view * proj) pre-transpose", (float*)&transpose );
}
D3DXMatrixTranspose( &transpose, &transpose );
#if 0 // turned off while we try to do fixup-Y in shader translate
if (IsPosix()) // flip all shader projection matrices for Y on GL since you can't have an upside-down viewport specification
{
// flip Y
transpose._21 *= -1.0f;
transpose._22 *= -1.0f;
transpose._23 *= -1.0f;
transpose._24 *= -1.0f;
}
#endif
SetVertexShaderConstant( VERTEX_SHADER_MODELVIEWPROJ, transpose, 4 );
// If we're doing FastClip, the above modelviewproj matrix won't work well for
// vertex shaders which compute projPos.z, hence we'll compute a more useful
// modelviewproj and put the third row of it in another constant
D3DXMatrixMultiply( &transpose, &modelView, &GetTransform( MATERIAL_PROJECTION ) ); // Get the non-FastClip projection matrix
D3DXMatrixTranspose( &transpose, &transpose );
float *pRowTwo = (float *)transpose + 8;
SetVertexShaderConstant( VERTEX_SHADER_MODELVIEWPROJ_THIRD_ROW, pRowTwo, 1 );
}
//GLMPRINTF(( "<-M- SetVertexShaderModelViewProjAndModelView" ));
}
void CShaderAPIDx8::UpdateVertexShaderMatrix( int iMatrix )
{
//GLM_FUNC;
if ( iMatrix == 0 )
{
int matrix = MATERIAL_MODEL;
if (VertexShaderTransformChanged(matrix))
{
int vertexShaderConstant = VERTEX_SHADER_MODEL + iMatrix * 3;
// Put the transform into the vertex shader constants...
D3DXMATRIX transpose;
D3DXMatrixTranspose( &transpose, &GetTransform(matrix) );
SetVertexShaderConstant( vertexShaderConstant, transpose, 3 );
// clear the change flag
m_DynamicState.m_TransformChanged[matrix] &= ~STATE_CHANGED_VERTEX_SHADER;
}
}
else
{
SetVertexShaderConstant( VERTEX_SHADER_MODEL + iMatrix, m_boneMatrix[iMatrix].Base(), 3 );
}
}
void CShaderAPIDx8::SetVertexShaderStateSkinningMatrices()
{
//GLM_FUNC;
// casting from 4x3 matrices to a 4x4 D3DXMATRIX, need 4 floats of overflow
float results[12+4];
// get the first one from the MATERIAL_MODEL matrix stack
D3DXMatrixTranspose( (D3DXMATRIX *)&results[0], &GetTransform( MATERIAL_MODEL ) );
memcpy( m_boneMatrix[0].Base(), results, 12 * sizeof(float) );
m_maxBoneLoaded++;
int matricesLoaded = max( 1, m_maxBoneLoaded );
m_maxBoneLoaded = 0;
m_DynamicState.m_TransformChanged[MATERIAL_MODEL] &= ~STATE_CHANGED_VERTEX_SHADER;
SetVertexShaderConstant( VERTEX_SHADER_MODEL, m_boneMatrix[0].Base(), matricesLoaded * 3, true );
// ###OSX### punting on OSX for now
#if defined( DX_TO_GL_ABSTRACTION ) && !defined( OSX )
Dx9Device()->SetMaxUsedVertexShaderConstantsHint( VERTEX_SHADER_MODEL + ( matricesLoaded * 3 ) );
#endif
}
//-----------------------------------------------------------------------------
// Commits vertex shader transforms that can change on a per pass basis
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitPerPassVertexShaderTransforms()
{
//GLMPRINTF(( ">-M- CommitPerPassVertexShaderTransforms" ));
Assert( g_pHardwareConfig->Caps().m_SupportsPixelShaders );
bool projChanged = VertexShaderTransformChanged( MATERIAL_PROJECTION );
//projChanged = true; //only for debug
if ( projChanged )
{
//GLMPRINTF(( "-M- projChanged=true in CommitPerPassVertexShaderTransforms" ));
SetVertexShaderViewProj();
SetVertexShaderModelViewProjAndModelView();
// Clear change flags
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] &= ~STATE_CHANGED_VERTEX_SHADER;
}
else
{
//GLMPRINTF(( "-M- projChanged=false in CommitPerPassVertexShaderTransforms" ));
}
//GLMPRINTF(( "<-M- CommitPerPassVertexShaderTransforms" ));
}
//-----------------------------------------------------------------------------
// Commits vertex shader transforms
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitVertexShaderTransforms()
{
//GLMPRINTF(( ">-M- CommitVertexShaderTransforms" ));
Assert( g_pHardwareConfig->Caps().m_SupportsPixelShaders );
bool viewChanged = VertexShaderTransformChanged(MATERIAL_VIEW);
bool projChanged = VertexShaderTransformChanged(MATERIAL_PROJECTION);
bool modelChanged = VertexShaderTransformChanged(MATERIAL_MODEL) && (m_DynamicState.m_NumBones < 1);
//GLMPRINTF(( "-M- viewChanged=%s projChanged=%s modelChanged = %s in CommitVertexShaderTransforms", viewChanged?"Y":"N",projChanged?"Y":"N",modelChanged?"Y":"N" ));
if (viewChanged)
{
//GLMPRINTF(( "-M- viewChanged --> UpdateVertexShaderFogParams" ));
UpdateVertexShaderFogParams();
}
if( viewChanged || projChanged )
{
// NOTE: We have to deal with fast-clip *before*
//GLMPRINTF(( "-M- viewChanged||projChanged --> SetVertexShaderViewProj" ));
SetVertexShaderViewProj();
}
if( viewChanged || modelChanged || projChanged )
{
//GLMPRINTF(( "-M- viewChanged||projChanged||modelChanged --> SetVertexShaderModelViewProjAndModelView" ));
SetVertexShaderModelViewProjAndModelView();
}
if( modelChanged && m_DynamicState.m_NumBones < 1 )
{
UpdateVertexShaderMatrix( 0 );
}
// Clear change flags
m_DynamicState.m_TransformChanged[MATERIAL_MODEL] &= ~STATE_CHANGED_VERTEX_SHADER;
m_DynamicState.m_TransformChanged[MATERIAL_VIEW] &= ~STATE_CHANGED_VERTEX_SHADER;
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] &= ~STATE_CHANGED_VERTEX_SHADER;
//GLMPRINTF(( "<-M- CommitVertexShaderTransforms" ));
}
void CShaderAPIDx8::UpdateFixedFunctionMatrix( int iMatrix )
{
if ( IsX360() )
return;
int matrix = MATERIAL_MODEL + iMatrix;
if ( FixedFunctionTransformChanged( matrix ) )
{
SetTransform( D3DTS_WORLDMATRIX(iMatrix), &GetTransform(matrix) );
// clear the change flag
m_DynamicState.m_TransformChanged[matrix] &= ~STATE_CHANGED_FIXED_FUNCTION;
}
}
void CShaderAPIDx8::SetFixedFunctionStateSkinningMatrices()
{
if ( IsX360() )
return;
for( int i=1; i < g_pHardwareConfig->MaxBlendMatrices(); i++ )
{
UpdateFixedFunctionMatrix( i );
}
}
//-----------------------------------------------------------------------------
// Commits transforms for the fixed function pipeline that can happen on a per pass basis
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitPerPassFixedFunctionTransforms()
{
if ( IsX360() )
return;
// Update projection
if ( FixedFunctionTransformChanged( MATERIAL_PROJECTION ) )
{
D3DTRANSFORMSTATETYPE matrix = D3DTS_PROJECTION;
SetTransform( matrix, &GetProjectionMatrix() );
// clear the change flag
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] &= ~STATE_CHANGED_FIXED_FUNCTION;
}
}
//-----------------------------------------------------------------------------
// Commits transforms for the fixed function pipeline
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitFixedFunctionTransforms()
{
if ( IsX360() )
return;
// Update view + projection
int i;
for ( i = MATERIAL_VIEW; i <= MATERIAL_PROJECTION; ++i)
{
if (FixedFunctionTransformChanged( i ))
{
D3DTRANSFORMSTATETYPE matrix = (i == MATERIAL_VIEW) ? D3DTS_VIEW : D3DTS_PROJECTION;
if ( i == MATERIAL_PROJECTION )
{
SetTransform( matrix, &GetProjectionMatrix() );
}
else
{
SetTransform( matrix, &GetTransform(i) );
}
// clear the change flag
m_DynamicState.m_TransformChanged[i] &= ~STATE_CHANGED_FIXED_FUNCTION;
}
}
UpdateFixedFunctionMatrix( 0 );
}
void CShaderAPIDx8::SetSkinningMatrices()
{
LOCK_SHADERAPI();
Assert( m_pMaterial );
if ( m_DynamicState.m_NumBones == 0 )
{
// ###OSX### punting on OSX for now
#if defined( DX_TO_GL_ABSTRACTION ) && !defined( OSX)
Dx9Device()->SetMaxUsedVertexShaderConstantsHint( VERTEX_SHADER_BONE_TRANSFORM( 0 ) + 3 );
#endif
return;
}
uint nMaxVertexConstantIndex = 0;
if ( IsX360() || UsesVertexShader(m_pMaterial->GetVertexFormat()) )
{
SetVertexShaderStateSkinningMatrices();
}
else if ( IsPC() )
{
#if defined( DX_TO_GL_ABSTRACTION ) && !defined( OSX)
Assert( 0 );
#else
SetFixedFunctionStateSkinningMatrices();
#endif
}
else
{
Assert( 0 );
}
}
//-----------------------------------------------------------------------------
// Commits vertex shader lighting
//-----------------------------------------------------------------------------
inline bool CShaderAPIDx8::VertexShaderLightingChanged( int i )
{
return (m_DynamicState.m_LightChanged[i] & STATE_CHANGED_VERTEX_SHADER) != 0;
}
inline bool CShaderAPIDx8::VertexShaderLightingEnableChanged( int i )
{
return (m_DynamicState.m_LightEnableChanged[i] & STATE_CHANGED_VERTEX_SHADER) != 0;
}
inline bool CShaderAPIDx8::FixedFunctionLightingChanged( int i )
{
return (m_DynamicState.m_LightChanged[i] & STATE_CHANGED_FIXED_FUNCTION) != 0;
}
inline bool CShaderAPIDx8::FixedFunctionLightingEnableChanged( int i )
{
return (m_DynamicState.m_LightEnableChanged[i] & STATE_CHANGED_FIXED_FUNCTION) != 0;
}
//-----------------------------------------------------------------------------
// Computes the light type
//-----------------------------------------------------------------------------
VertexShaderLightTypes_t CShaderAPIDx8::ComputeLightType( int i ) const
{
if (!m_DynamicState.m_LightEnable[i])
return LIGHT_NONE;
switch( m_DynamicState.m_Lights[i].Type )
{
case D3DLIGHT_POINT:
return LIGHT_POINT;
case D3DLIGHT_DIRECTIONAL:
return LIGHT_DIRECTIONAL;
case D3DLIGHT_SPOT:
return LIGHT_SPOT;
}
Assert(0);
return LIGHT_NONE;
}
//-----------------------------------------------------------------------------
// Sort the lights by type
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SortLights( int* index )
{
m_DynamicState.m_NumLights = 0;
for (int i = 0; i < MAX_NUM_LIGHTS; ++i)
{
VertexShaderLightTypes_t type = ComputeLightType(i); // returns LIGHT_NONE if the light is disabled
int j = m_DynamicState.m_NumLights;
if (type != LIGHT_NONE)
{
while ( --j >= 0 )
{
if (m_DynamicState.m_LightType[j] <= type)
break;
// shift...
m_DynamicState.m_LightType[j+1] = m_DynamicState.m_LightType[j];
index[j+1] = index[j];
}
++j;
m_DynamicState.m_LightType[j] = type;
index[j] = i;
++m_DynamicState.m_NumLights;
}
}
}
//-----------------------------------------------------------------------------
// Vertex Shader lighting
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitVertexShaderLighting()
{
// If nothing changed, then don't bother. Otherwise, reload...
int i;
for ( i = 0; i < MAX_NUM_LIGHTS; ++i )
{
if (VertexShaderLightingChanged(i) || VertexShaderLightingEnableChanged(i))
break;
}
// Yeah baby
if ( i == MAX_NUM_LIGHTS )
return;
// First, gotta sort the lights by their type
int lightIndex[MAX_NUM_LIGHTS];
memset( lightIndex, 0, sizeof( lightIndex ) );
SortLights( lightIndex );
// Clear the lighting enable flags
for ( i = 0; i < MAX_NUM_LIGHTS; ++i )
{
m_DynamicState.m_LightEnableChanged[i] &= ~STATE_CHANGED_VERTEX_SHADER;
m_DynamicState.m_LightChanged[i] &= ~STATE_CHANGED_VERTEX_SHADER;
}
bool bAtLeastDX90 = g_pHardwareConfig->GetDXSupportLevel() >= 90;
// Set the lighting state
for ( i = 0; i < m_DynamicState.m_NumLights; ++i )
{
D3DLIGHT& light = m_DynamicState.m_Lights[lightIndex[i]];
Vector4D lightState[5];
// The first one is the light color (and light type code on DX9)
float w = (light.Type == D3DLIGHT_DIRECTIONAL) && bAtLeastDX90 ? 1.0f : 0.0f;
lightState[0].Init( light.Diffuse.r, light.Diffuse.g, light.Diffuse.b, w);
// The next constant holds the light direction (and light type code on DX9)
w = (light.Type == D3DLIGHT_SPOT) && bAtLeastDX90 ? 1.0f : 0.0f;
lightState[1].Init( light.Direction.x, light.Direction.y, light.Direction.z, w );
// The next constant holds the light position
lightState[2].Init( light.Position.x, light.Position.y, light.Position.z, 1.0f );
// The next constant holds exponent, stopdot, stopdot2, 1 / (stopdot - stopdot2)
if (light.Type == D3DLIGHT_SPOT)
{
float stopdot = cos( light.Theta * 0.5f );
float stopdot2 = cos( light.Phi * 0.5f );
float oodot = (stopdot > stopdot2) ? 1.0f / (stopdot - stopdot2) : 0.0f;
lightState[3].Init( light.Falloff, stopdot, stopdot2, oodot );
}
else
{
lightState[3].Init( 0, 1, 1, 1 );
}
// The last constant holds attenuation0, attenuation1, attenuation2
lightState[4].Init( light.Attenuation0, light.Attenuation1, light.Attenuation2, 0.0f );
// Set the state
SetVertexShaderConstant( VERTEX_SHADER_LIGHTS + i * 5, lightState[0].Base(), 5 );
}
if ( g_pHardwareConfig->NumIntegerVertexShaderConstants() > 0 && g_pHardwareConfig->NumBooleanVertexShaderConstants() > 0 )
{
// Vertex Shader loop counter for number of lights (Only the .x component is used by our shaders)
// .x is the iteration count, .y is the initial value and .z is the increment step
int nLoopControl[4] = {m_DynamicState.m_NumLights, 0, 1, 0};
SetIntegerVertexShaderConstant( 0, nLoopControl, 1 );
// Enable lights using vertex shader static flow control
int nLightEnable[VERTEX_SHADER_LIGHT_ENABLE_BOOL_CONST_COUNT] = {0, 0, 0, 0};
for ( i = 0; i < m_DynamicState.m_NumLights; ++i )
{
nLightEnable[i] = 1;
}
SetBooleanVertexShaderConstant( VERTEX_SHADER_LIGHT_ENABLE_BOOL_CONST, nLightEnable, VERTEX_SHADER_LIGHT_ENABLE_BOOL_CONST_COUNT );
}
}
//-----------------------------------------------------------------------------
// Set the pixel shader constants for lights
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitPixelShaderLighting( int pshReg )
{
#ifndef NDEBUG
char const *materialName = m_pMaterial->GetName();
#endif
// First, gotta sort the lights by their type
int lightIndex[MAX_NUM_LIGHTS];
SortLights( lightIndex );
// Offset to create a point light from directional
const float fFarAway = 10000.0f;
// Total pixel shader lighting state for four lights
Vector4D lightState[6];
for ( int i = 0; i < 6; i++ )
lightState[i].Init();
int nNumLights = m_DynamicState.m_NumLights;
if ( nNumLights > 0 )
{
D3DLIGHT *light = &m_DynamicState.m_Lights[lightIndex[0]];
lightState[0].Init( light->Diffuse.r, light->Diffuse.g, light->Diffuse.b, 0.0f );
if ( light->Type == D3DLIGHT_DIRECTIONAL )
{
Vector vDir(light->Direction.x, light->Direction.y, light->Direction.z );
Vector vPos = m_DynamicState.m_vLightingOrigin - vDir * fFarAway;
lightState[1].Init( vPos.x, vPos.y, vPos.z, 0.0f );
}
else
{
lightState[1].Init( light->Position.x, light->Position.y, light->Position.z, 0.0f );
}
if ( nNumLights > 1 ) // At least two lights
{
light = &m_DynamicState.m_Lights[lightIndex[1]];
lightState[2].Init( light->Diffuse.r, light->Diffuse.g, light->Diffuse.b, 0.0f );
if ( light->Type == D3DLIGHT_DIRECTIONAL )
{
Vector vDir(light->Direction.x, light->Direction.y, light->Direction.z );
Vector vPos = m_DynamicState.m_vLightingOrigin - vDir * fFarAway;
lightState[3].Init( vPos.x, vPos.y, vPos.z, 0.0f );
}
else
{
lightState[3].Init( light->Position.x, light->Position.y, light->Position.z, 0.0f );
}
if ( nNumLights > 2 ) // At least three lights
{
light = &m_DynamicState.m_Lights[lightIndex[2]];
lightState[4].Init( light->Diffuse.r, light->Diffuse.g, light->Diffuse.b, 0.0f );
if ( light->Type == D3DLIGHT_DIRECTIONAL )
{
Vector vDir(light->Direction.x, light->Direction.y, light->Direction.z );
Vector vPos = m_DynamicState.m_vLightingOrigin - vDir * fFarAway;
lightState[5].Init( vPos.x, vPos.y, vPos.z, 0.0f );
}
else
{
lightState[5].Init( light->Position.x, light->Position.y, light->Position.z, 0.0f );
}
if ( nNumLights > 3 ) // At least four lights (our current max)
{
light = &m_DynamicState.m_Lights[lightIndex[3]]; // Spread 4th light's constants across w components
lightState[0].w = light->Diffuse.r;
lightState[1].w = light->Diffuse.g;
lightState[2].w = light->Diffuse.b;
if ( light->Type == D3DLIGHT_DIRECTIONAL )
{
Vector vDir(light->Direction.x, light->Direction.y, light->Direction.z );
Vector vPos = m_DynamicState.m_vLightingOrigin - vDir * fFarAway;
lightState[3].w = vPos.x;
lightState[4].w = vPos.y;
lightState[5].w = vPos.z;
}
else
{
lightState[3].w = light->Position.x;
lightState[4].w = light->Position.y;
lightState[5].w = light->Position.z;
}
}
}
}
}
SetPixelShaderConstant( pshReg, lightState[0].Base(), 6 );
}
//-----------------------------------------------------------------------------
// Fixed function lighting
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitFixedFunctionLighting()
{
if ( IsX360() )
{
return;
}
// Commit each light
for (int i = 0; i < g_pHardwareConfig->MaxNumLights(); ++i)
{
// Change light enable
if ( FixedFunctionLightingEnableChanged( i ) )
{
LightEnable( i, m_DynamicState.m_LightEnable[i] );
// Clear change flag
m_DynamicState.m_LightEnableChanged[i] &= ~STATE_CHANGED_FIXED_FUNCTION;
}
// Change lighting state...
if ( m_DynamicState.m_LightEnable[i] )
{
if ( FixedFunctionLightingChanged( i ) )
{
// Store off the "correct" falloff...
D3DLIGHT& light = m_DynamicState.m_Lights[i];
float falloff = light.Falloff;
SetLight( i, &light );
// Clear change flag
m_DynamicState.m_LightChanged[i] &= ~STATE_CHANGED_FIXED_FUNCTION;
// restore the correct falloff
light.Falloff = falloff;
}
}
}
}
//-----------------------------------------------------------------------------
// Commits user clip planes
//-----------------------------------------------------------------------------
D3DXMATRIX& CShaderAPIDx8::GetUserClipTransform( )
{
if ( !m_DynamicState.m_bUserClipTransformOverride )
return GetTransform(MATERIAL_VIEW);
return m_DynamicState.m_UserClipTransform;
}
//-----------------------------------------------------------------------------
// Commits user clip planes
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitUserClipPlanes( bool bUsingFixedFunction )
{
// We need to transform the clip planes, specified in world space,
// to be in projection space.. To transform the plane, we must transform
// the intercept and then transform the normal.
if( bUsingFixedFunction != m_DynamicState.m_UserClipLastUpdatedUsingFixedFunction )
{
//fixed function clip planes are in world space, vertex shader clip planes are in clip space, so we need to update every clip plane whenever there's a flip
m_DynamicState.m_UserClipPlaneChanged = (1 << g_pHardwareConfig->MaxUserClipPlanes()) - 1;
m_DynamicState.m_UserClipLastUpdatedUsingFixedFunction = bUsingFixedFunction;
}
D3DXMATRIX worldToProjectionInvTrans;
#ifndef _DEBUG
if( m_DynamicState.m_UserClipPlaneChanged & m_DynamicState.m_UserClipPlaneEnabled & ((1 << g_pHardwareConfig->MaxUserClipPlanes()) - 1) )
#endif
{
//we're going to need the transformation matrix at least once this call
if( bUsingFixedFunction )
{
if( m_DynamicState.m_bUserClipTransformOverride )
{
//D3DXMatrixIdentity( &worldToProjectionInvTrans ); //TODO: Test user clip transforms with this
//Since GetUserClipTransform() returns the view matrix if a user supplied transform doesn't exist, the general solution to this should be to transform the user transform by the inverse view matrix
//Since we don't know if the user clip is invertable, we'll premultiply by inverse view and cross our fingers that it's right more often than wrong
D3DXMATRIX viewInverse = GetTransform( MATERIAL_VIEW );
D3DXMatrixInverse(&viewInverse, NULL, &viewInverse);
worldToProjectionInvTrans = viewInverse * GetUserClipTransform(); //taking a cue from the multiplication below, multiplication goes left into right
D3DXMatrixInverse(&worldToProjectionInvTrans, NULL, &worldToProjectionInvTrans);
D3DXMatrixTranspose(&worldToProjectionInvTrans, &worldToProjectionInvTrans);
}
else
{
D3DXMatrixIdentity( &worldToProjectionInvTrans );
}
}
else
{
worldToProjectionInvTrans = GetUserClipTransform( ) * GetTransform( MATERIAL_PROJECTION );
D3DXMatrixInverse(&worldToProjectionInvTrans, NULL, &worldToProjectionInvTrans);
D3DXMatrixTranspose(&worldToProjectionInvTrans, &worldToProjectionInvTrans);
}
}
for (int i = 0; i < g_pHardwareConfig->MaxUserClipPlanes(); ++i)
{
// Don't bother with the plane if it's not enabled
if ( (m_DynamicState.m_UserClipPlaneEnabled & (1 << i)) == 0 )
continue;
// Don't bother if it didn't change...
if ( (m_DynamicState.m_UserClipPlaneChanged & (1 << i)) == 0 )
{
#ifdef _DEBUG
//verify that the plane has not actually changed
D3DXPLANE clipPlaneProj;
D3DXPlaneTransform( &clipPlaneProj, &m_DynamicState.m_UserClipPlaneWorld[i], &worldToProjectionInvTrans );
Assert ( clipPlaneProj == m_DynamicState.m_UserClipPlaneProj[i] );
#endif
continue;
}
m_DynamicState.m_UserClipPlaneChanged &= ~(1 << i);
D3DXPLANE clipPlaneProj;
D3DXPlaneTransform( &clipPlaneProj, &m_DynamicState.m_UserClipPlaneWorld[i], &worldToProjectionInvTrans );
if ( clipPlaneProj != m_DynamicState.m_UserClipPlaneProj[i] )
{
Dx9Device()->SetClipPlane( i, (float*)clipPlaneProj );
m_DynamicState.m_UserClipPlaneProj[i] = clipPlaneProj;
}
}
}
//-----------------------------------------------------------------------------
// Need to handle fog mode on a per-pass basis
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitPerPassFogMode( bool bUsingVertexAndPixelShaders )
{
if ( IsX360() )
{
// FF fog not applicable on 360
return;
}
D3DFOGMODE dxFogMode = D3DFOG_NONE;
if ( m_DynamicState.m_FogEnable )
{
dxFogMode = bUsingVertexAndPixelShaders ? D3DFOG_NONE : D3DFOG_LINEAR;
}
// Set fog mode if it's different than before.
if( m_DynamicState.m_FogMode != dxFogMode )
{
SetRenderStateConstMacro( this, D3DRS_FOGVERTEXMODE, dxFogMode );
m_DynamicState.m_FogMode = dxFogMode;
}
}
//-----------------------------------------------------------------------------
// Handle Xbox GPU/DX API fixups necessary before actual draw.
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitPerPassXboxFixups()
{
#if defined( _X360 )
// send updated shader constants to gpu
WriteShaderConstantsToGPU();
// sRGB write state may have changed after RT set, have to re-set correct RT
SetRenderTargetInternalXbox( m_hCachedRenderTarget );
#endif
}
//-----------------------------------------------------------------------------
// These states can change between each pass
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitPerPassStateChanges( StateSnapshot_t id )
{
if ( IsX360() || UsesVertexAndPixelShaders(id) )
{
CommitPerPassVertexShaderTransforms();
CommitPerPassFogMode( true );
CommitPerPassXboxFixups();
CallCommitFuncs( COMMIT_PER_PASS, false );
}
else if ( IsPC() )
{
CommitPerPassFixedFunctionTransforms();
CommitPerPassFogMode( false );
CallCommitFuncs( COMMIT_PER_PASS, true );
}
else
{
Assert( 0 );
}
}
//-----------------------------------------------------------------------------
// Commits transforms and lighting
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitStateChanges()
{
VPROF("CShaderAPIDx8::CommitStateChanges");
CommitFastClipPlane();
bool bUsingFixedFunction = !IsX360() && m_pMaterial && !UsesVertexShader( m_pMaterial->GetVertexFormat() );
// xboxissue - cannot support ff pipeline
Assert ( IsPC() || ( IsX360() && !bUsingFixedFunction ) );
if ( IsX360() || !bUsingFixedFunction )
{
CommitVertexShaderTransforms();
if ( m_pMaterial && m_pMaterial->IsVertexLit() )
{
CommitVertexShaderLighting();
}
}
else if ( IsPC() )
{
CommitFixedFunctionTransforms();
if ( m_pMaterial && ( m_pMaterial->IsVertexLit() || m_pMaterial->NeedsFixedFunctionFlashlight() ) )
{
CommitFixedFunctionLighting();
}
}
else
{
Assert( 0 );
}
if ( m_DynamicState.m_UserClipPlaneEnabled )
{
CommitUserClipPlanes( bUsingFixedFunction );
}
CallCommitFuncs( COMMIT_PER_DRAW, bUsingFixedFunction );
}
//-----------------------------------------------------------------------------
// Commits viewports
//-----------------------------------------------------------------------------
static void CommitSetViewports( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, DynamicState_t &currentState, bool bForce )
{
bool bChanged = bForce || memcmp( &desiredState.m_Viewport, &currentState.m_Viewport, sizeof(D3DVIEWPORT9) );
// The width + height can be zero at startup sometimes.
if ( bChanged && ( desiredState.m_Viewport.Width != 0 ) && ( desiredState.m_Viewport.Height != 0 ) )
{
if( ReverseDepthOnX360() ) //reverse depth on 360 for better perf through hierarchical z
{
D3DVIEWPORT9 reverseDepthViewport;
reverseDepthViewport = desiredState.m_Viewport;
reverseDepthViewport.MinZ = 1.0f - desiredState.m_Viewport.MinZ;
reverseDepthViewport.MaxZ = 1.0f - desiredState.m_Viewport.MaxZ;
Dx9Device()->SetViewport( &reverseDepthViewport );
}
else
{
Dx9Device()->SetViewport( &desiredState.m_Viewport );
}
memcpy( &currentState.m_Viewport, &desiredState.m_Viewport, sizeof( D3DVIEWPORT9 ) );
}
}
void CShaderAPIDx8::SetViewports( int nCount, const ShaderViewport_t* pViewports )
{
Assert( nCount == 1 && pViewports[0].m_nVersion == SHADER_VIEWPORT_VERSION );
if ( nCount != 1 )
return;
LOCK_SHADERAPI();
D3DVIEWPORT9 viewport;
viewport.X = pViewports[0].m_nTopLeftX;
viewport.Y = pViewports[0].m_nTopLeftY;
viewport.Width = pViewports[0].m_nWidth;
viewport.Height = pViewports[0].m_nHeight;
viewport.MinZ = pViewports[0].m_flMinZ;
viewport.MaxZ = pViewports[0].m_flMaxZ;
// Clamp the viewport to the current render target...
if ( !m_UsingTextureRenderTarget )
{
// Clamp to both the back buffer and the window, if it is resizing
int nMaxWidth = 0, nMaxHeight = 0;
GetBackBufferDimensions( nMaxWidth, nMaxHeight );
if ( IsPC() && m_IsResizing )
{
RECT viewRect;
#if !defined( DX_TO_GL_ABSTRACTION )
GetClientRect( ( HWND )m_ViewHWnd, &viewRect );
#else
toglGetClientRect( (VD3DHWND)m_ViewHWnd, &viewRect );
#endif
m_nWindowWidth = viewRect.right - viewRect.left;
m_nWindowHeight = viewRect.bottom - viewRect.top;
nMaxWidth = min( m_nWindowWidth, nMaxWidth );
nMaxHeight = min( m_nWindowHeight, nMaxHeight );
}
// Dimensions can freak out on app exit, so at least make sure the viewport is positive
if ( (viewport.Width > (unsigned int)nMaxWidth ) && (nMaxWidth > 0) )
{
viewport.Width = nMaxWidth;
}
// Dimensions can freak out on app exit, so at least make sure the viewport is positive
if ( ( viewport.Height > (unsigned int)nMaxHeight ) && (nMaxHeight > 0) )
{
viewport.Height = nMaxHeight;
}
}
else
{
if ( viewport.Width > (unsigned int)m_ViewportMaxWidth )
{
viewport.Width = m_ViewportMaxWidth;
}
if ( viewport.Height > (unsigned int)m_ViewportMaxHeight )
{
viewport.Height = m_ViewportMaxHeight;
}
}
// FIXME: Once we extract buffered primitives out, we can directly fill in desired state
// and avoid the memcmp and copy
if ( memcmp( &m_DesiredState.m_Viewport, &viewport, sizeof(D3DVIEWPORT9) ) )
{
if ( !IsDeactivated() )
{
// State changed... need to flush the dynamic buffer
FlushBufferedPrimitives();
}
memcpy( &m_DesiredState.m_Viewport, &viewport, sizeof(D3DVIEWPORT9) );
}
ADD_COMMIT_FUNC( COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitSetViewports );
}
//-----------------------------------------------------------------------------
// Gets the current viewport size
//-----------------------------------------------------------------------------
int CShaderAPIDx8::GetViewports( ShaderViewport_t* pViewports, int nMax ) const
{
if ( !pViewports || nMax == 0 )
return 1;
LOCK_SHADERAPI();
pViewports[0].m_nTopLeftX = m_DesiredState.m_Viewport.X;
pViewports[0].m_nTopLeftY = m_DesiredState.m_Viewport.Y;
pViewports[0].m_nWidth = m_DesiredState.m_Viewport.Width;
pViewports[0].m_nHeight = m_DesiredState.m_Viewport.Height;
pViewports[0].m_flMinZ = m_DesiredState.m_Viewport.MinZ;
pViewports[0].m_flMaxZ = m_DesiredState.m_Viewport.MaxZ;
return 1;
}
//-----------------------------------------------------------------------------
// Flushes buffered primitives
//-----------------------------------------------------------------------------
void CShaderAPIDx8::FlushBufferedPrimitives( )
{
if ( ShaderUtil() )
{
if ( !ShaderUtil()->OnFlushBufferedPrimitives() )
{
return;
}
}
FlushBufferedPrimitivesInternal();
}
void CShaderAPIDx8::FlushBufferedPrimitivesInternal( )
{
LOCK_SHADERAPI();
// This shouldn't happen in the inner rendering loop!
Assert( m_pRenderMesh == 0 );
// NOTE: We've gotta store off the matrix mode because
// it'll get reset by the default state application caused by the flush
int tempStack = m_CurrStack;
D3DTRANSFORMSTATETYPE tempMatrixMode = m_MatrixMode;
MeshMgr()->Flush();
m_CurrStack = tempStack;
m_MatrixMode = tempMatrixMode;
}
//-----------------------------------------------------------------------------
// Flush the hardware
//-----------------------------------------------------------------------------
void CShaderAPIDx8::FlushHardware( )
{
LOCK_SHADERAPI();
FlushBufferedPrimitives();
Dx9Device()->EndScene();
DiscardVertexBuffers();
Dx9Device()->BeginScene();
ForceHardwareSync();
}
//-----------------------------------------------------------------------------
// Deal with device lost (alt-tab)
//-----------------------------------------------------------------------------
void CShaderAPIDx8::HandleDeviceLost()
{
if ( IsX360() )
{
return;
}
LOCK_SHADERAPI();
if ( !IsActive() )
return;
// need to flush the dynamic buffer
FlushBufferedPrimitives();
if ( !IsDeactivated() )
{
Dx9Device()->EndScene();
}
CheckDeviceLost( m_bOtherAppInitializing );
if ( !IsDeactivated() )
{
Dx9Device()->BeginScene();
}
}
//-----------------------------------------------------------------------------
// Buffer clear color
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ClearColor3ub( unsigned char r, unsigned char g, unsigned char b )
{
LOCK_SHADERAPI();
float a = 255;//(r * 0.30f + g * 0.59f + b * 0.11f) / MAX_HDR_OVERBRIGHT;
// GR - need to force alpha to black for HDR
m_DynamicState.m_ClearColor = D3DCOLOR_ARGB((unsigned char)a,r,g,b);
}
void CShaderAPIDx8::ClearColor4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a )
{
LOCK_SHADERAPI();
m_DynamicState.m_ClearColor = D3DCOLOR_ARGB(a,r,g,b);
}
// Converts the clear color to be appropriate for HDR
D3DCOLOR CShaderAPIDx8::GetActualClearColor( D3DCOLOR clearColor )
{
bool bConvert = !IsX360() && m_TransitionTable.CurrentState().m_bLinearColorSpaceFrameBufferEnable;
#if defined( _X360 )
// The PC disables SRGBWrite when clearing so that the clear color won't get gamma converted
// The 360 cannot disable that state, and thus compensates for the sRGB conversion
// the desired result is the clear color written to the RT as-is
if ( clearColor & D3DCOLOR_ARGB( 0, 255, 255, 255 ) )
{
IDirect3DSurface *pRTSurface = NULL;
Dx9Device()->GetRenderTarget( 0, &pRTSurface );
if ( pRTSurface )
{
D3DSURFACE_DESC desc;
HRESULT hr = pRTSurface->GetDesc( &desc );
if ( !FAILED( hr ) && IS_D3DFORMAT_SRGB( desc.Format ) )
{
bConvert = true;
}
pRTSurface->Release();
}
}
#endif
if ( bConvert )
{
// HDRFIXME: need to make sure this works this way.
// HDRFIXME: Is there a helper function that'll do this easier?
// convert clearColor from gamma to linear since our frame buffer is linear.
Vector vecGammaColor;
vecGammaColor.x = ( 1.0f / 255.0f ) * ( ( clearColor >> 16 ) & 0xff );
vecGammaColor.y = ( 1.0f / 255.0f ) * ( ( clearColor >> 8 ) & 0xff );
vecGammaColor.z = ( 1.0f / 255.0f ) * ( clearColor & 0xff );
Vector vecLinearColor;
vecLinearColor.x = GammaToLinear( vecGammaColor.x );
vecLinearColor.y = GammaToLinear( vecGammaColor.y );
vecLinearColor.z = GammaToLinear( vecGammaColor.z );
clearColor &= D3DCOLOR_RGBA( 0, 0, 0, 255 );
clearColor |= D3DCOLOR_COLORVALUE( vecLinearColor.x, vecLinearColor.y, vecLinearColor.z, 0.0f );
}
return clearColor;
}
//-----------------------------------------------------------------------------
// Clear buffers while obeying stencil
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ClearBuffersObeyStencil( bool bClearColor, bool bClearDepth )
{
//copy the clear color bool into the clear alpha bool
ClearBuffersObeyStencilEx( bClearColor, bClearColor, bClearDepth );
}
void CShaderAPIDx8::ClearBuffersObeyStencilEx( bool bClearColor, bool bClearAlpha, bool bClearDepth )
{
LOCK_SHADERAPI();
if ( !bClearColor && !bClearAlpha && !bClearDepth )
return;
FlushBufferedPrimitives();
// Before clearing can happen, user clip planes must be disabled
SetRenderState( D3DRS_CLIPPLANEENABLE, 0 );
D3DCOLOR clearColor = GetActualClearColor( m_DynamicState.m_ClearColor );
unsigned char r, g, b, a;
b = clearColor& 0xFF;
g = ( clearColor >> 8 ) & 0xFF;
r = ( clearColor >> 16 ) & 0xFF;
a = ( clearColor >> 24 ) & 0xFF;
ShaderUtil()->DrawClearBufferQuad( r, g, b, a, bClearColor, bClearAlpha, bClearDepth );
// Reset user clip plane state
FlushBufferedPrimitives();
SetRenderState( D3DRS_CLIPPLANEENABLE, m_DynamicState.m_UserClipPlaneEnabled );
}
//-------------------------------------------------------------------------
//Perform stencil operations to every pixel on the screen
//-------------------------------------------------------------------------
void CShaderAPIDx8::PerformFullScreenStencilOperation( void )
{
LOCK_SHADERAPI();
FlushBufferedPrimitives();
// We'll be drawing a large quad in altered worldspace, user clip planes must be disabled
SetRenderStateConstMacro( this, D3DRS_CLIPPLANEENABLE, 0 );
ShaderUtil()->DrawClearBufferQuad( 0, 0, 0, 0, false, false, false );
// Reset user clip plane state
FlushBufferedPrimitives();
SetRenderStateConstMacro( this, D3DRS_CLIPPLANEENABLE, m_DynamicState.m_UserClipPlaneEnabled );
}
//-----------------------------------------------------------------------------
// Buffer clear
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ClearBuffers( bool bClearColor, bool bClearDepth, bool bClearStencil, int renderTargetWidth, int renderTargetHeight )
{
LOCK_SHADERAPI();
if ( ShaderUtil()->GetConfig().m_bSuppressRendering )
return;
if ( IsDeactivated() )
return;
// State changed... need to flush the dynamic buffer
FlushBufferedPrimitives();
CallCommitFuncs( COMMIT_PER_DRAW, true );
float depth = (ShaderUtil()->GetConfig().bReverseDepth ^ ReverseDepthOnX360()) ? 0.0f : 1.0f;
DWORD mask = 0;
if ( bClearColor )
{
mask |= D3DCLEAR_TARGET;
}
if ( bClearDepth )
{
mask |= D3DCLEAR_ZBUFFER;
}
if ( bClearStencil && m_bUsingStencil )
{
mask |= D3DCLEAR_STENCIL;
}
// Only clear the current view... right!??!
D3DRECT clear;
clear.x1 = m_DesiredState.m_Viewport.X;
clear.y1 = m_DesiredState.m_Viewport.Y;
clear.x2 = clear.x1 + m_DesiredState.m_Viewport.Width;
clear.y2 = clear.y1 + m_DesiredState.m_Viewport.Height;
// SRGBWrite is disabled when clearing so that the clear color won't get gamma converted
bool bSRGBWriteEnable = false;
if ( !IsX360() && bClearColor && m_TransitionTable.CurrentShadowState() )
{
bSRGBWriteEnable = m_TransitionTable.CurrentShadowState()->m_SRGBWriteEnable;
}
#if !defined( _X360 )
if ( bSRGBWriteEnable )
{
// This path used to be !IsPosix(), but this makes no sense and causes the clear color to differ in D3D9 vs. GL.
Dx9Device()->SetRenderState( D3DRS_SRGBWRITEENABLE, 0 );
}
#endif
D3DCOLOR clearColor = GetActualClearColor( m_DynamicState.m_ClearColor );
if ( mask != 0 )
{
bool bRenderTargetMatchesViewport =
( renderTargetWidth == -1 && renderTargetHeight == -1 ) ||
( m_DesiredState.m_Viewport.Width == -1 && m_DesiredState.m_Viewport.Height == -1 ) ||
( renderTargetWidth == ( int )m_DesiredState.m_Viewport.Width &&
renderTargetHeight == ( int )m_DesiredState.m_Viewport.Height );
if ( bRenderTargetMatchesViewport )
{
RECORD_COMMAND( DX8_CLEAR, 6 );
RECORD_INT( 0 );
RECORD_STRUCT( &clear, sizeof(clear) );
RECORD_INT( mask );
RECORD_INT( clearColor );
RECORD_FLOAT( depth );
RECORD_INT( 0 );
Dx9Device()->Clear( 0, NULL, mask, clearColor, depth, 0L );
}
else
{
RECORD_COMMAND( DX8_CLEAR, 6 );
RECORD_INT( 0 );
RECORD_STRUCT( &clear, sizeof(clear) );
RECORD_INT( mask );
RECORD_INT( clearColor );
RECORD_FLOAT( depth );
RECORD_INT( 0 );
Dx9Device()->Clear( 1, &clear, mask, clearColor, depth, 0L );
}
}
// Restore state
if ( bSRGBWriteEnable )
{
// sRGBWriteEnable shouldn't be true if we have no shadow state. . . Assert just in case.
Assert( m_TransitionTable.CurrentShadowState() );
m_TransitionTable.ApplySRGBWriteEnable( *m_TransitionTable.CurrentShadowState() );
}
}
//-----------------------------------------------------------------------------
// Bind
//-----------------------------------------------------------------------------
void CShaderAPIDx8::BindVertexShader( VertexShaderHandle_t hVertexShader )
{
ShaderManager()->BindVertexShader( hVertexShader );
}
void CShaderAPIDx8::BindGeometryShader( GeometryShaderHandle_t hGeometryShader )
{
Assert( hGeometryShader == GEOMETRY_SHADER_HANDLE_INVALID );
}
void CShaderAPIDx8::BindPixelShader( PixelShaderHandle_t hPixelShader )
{
ShaderManager()->BindPixelShader( hPixelShader );
}
//-----------------------------------------------------------------------------
// Returns a copy of the front buffer
//-----------------------------------------------------------------------------
IDirect3DSurface* CShaderAPIDx8::GetFrontBufferImage( ImageFormat& format )
{
#if !defined( _X360 )
// need to flush the dynamic buffer and make sure the entire image is there
FlushBufferedPrimitives();
int w, h;
GetBackBufferDimensions( w, h );
HRESULT hr;
IDirect3DSurface *pFullScreenSurfaceBits = 0;
hr = Dx9Device()->CreateOffscreenPlainSurface( w, h,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pFullScreenSurfaceBits, NULL );
if (FAILED(hr))
return 0;
hr = Dx9Device()->GetFrontBufferData( 0, pFullScreenSurfaceBits );
if (FAILED(hr))
return 0;
int windowWidth, windowHeight;
GetWindowSize( windowWidth, windowHeight );
IDirect3DSurface *pSurfaceBits = 0;
hr = Dx9Device()->CreateOffscreenPlainSurface( windowWidth, windowHeight,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurfaceBits, NULL );
Assert( hr == D3D_OK );
POINT pnt;
pnt.x = pnt.y = 0;
#ifdef _WIN32
BOOL result = ClientToScreen( ( HWND )m_hWnd, &pnt );
#else
BOOL result = ClientToScreen( (VD3DHWND)m_hWnd, &pnt );
#endif
Assert( result );
RECT srcRect;
srcRect.left = pnt.x;
srcRect.top = pnt.y;
srcRect.right = pnt.x + windowWidth;
srcRect.bottom = pnt.y + windowHeight;
POINT dstPnt;
dstPnt.x = dstPnt.y = 0;
D3DLOCKED_RECT lockedSrcRect;
hr = pFullScreenSurfaceBits->LockRect( &lockedSrcRect, &srcRect, D3DLOCK_READONLY );
Assert( hr == D3D_OK );
D3DLOCKED_RECT lockedDstRect;
hr = pSurfaceBits->LockRect( &lockedDstRect, NULL, 0 );
Assert( hr == D3D_OK );
int i;
for( i = 0; i < windowHeight; i++ )
{
memcpy( ( unsigned char * )lockedDstRect.pBits + ( i * lockedDstRect.Pitch ),
( unsigned char * )lockedSrcRect.pBits + ( i * lockedSrcRect.Pitch ),
windowWidth * 4 ); // hack . . what if this is a different format?
}
hr = pSurfaceBits->UnlockRect();
Assert( hr == D3D_OK );
hr = pFullScreenSurfaceBits->UnlockRect();
Assert( hr == D3D_OK );
pFullScreenSurfaceBits->Release();
format = ImageLoader::D3DFormatToImageFormat( D3DFMT_A8R8G8B8 );
return pSurfaceBits;
#else
Assert( 0 );
return NULL;
#endif
}
//-----------------------------------------------------------------------------
// Lets the shader know about the full-screen texture so it can
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetFullScreenTextureHandle( ShaderAPITextureHandle_t h )
{
LOCK_SHADERAPI();
m_hFullScreenTexture = h;
}
//-----------------------------------------------------------------------------
// Lets the shader know about the full-screen texture so it can
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetLinearToGammaConversionTextures( ShaderAPITextureHandle_t hSRGBWriteEnabledTexture, ShaderAPITextureHandle_t hIdentityTexture )
{
LOCK_SHADERAPI();
m_hLinearToGammaTableTexture = hSRGBWriteEnabledTexture;
m_hLinearToGammaTableIdentityTexture = hIdentityTexture;
}
//-----------------------------------------------------------------------------
// Returns a copy of the back buffer
//-----------------------------------------------------------------------------
IDirect3DSurface* CShaderAPIDx8::GetBackBufferImageHDR( Rect_t *pSrcRect, Rect_t *pDstRect, ImageFormat& format )
{
#if !defined( _X360 )
HRESULT hr;
IDirect3DSurface *pSurfaceBits = 0;
IDirect3DSurface *pTmpSurface = NULL;
// Get the back buffer
IDirect3DSurface* pBackBuffer;
hr = Dx9Device()->GetRenderTarget( 0, &pBackBuffer );
if (FAILED(hr))
return 0;
// Find about its size and format
D3DSURFACE_DESC desc;
D3DTEXTUREFILTERTYPE filter;
hr = pBackBuffer->GetDesc( &desc );
if (FAILED(hr))
goto CleanUp;
filter = ((pDstRect->width != pSrcRect->width) || (pDstRect->height != pSrcRect->height)) ? D3DTEXF_LINEAR : D3DTEXF_NONE;
if ( ( pDstRect->x + pDstRect->width <= SMALL_BACK_BUFFER_SURFACE_WIDTH ) &&
( pDstRect->y + pDstRect->height <= SMALL_BACK_BUFFER_SURFACE_HEIGHT ) )
{
if (!m_pSmallBackBufferFP16TempSurface)
{
hr = Dx9Device()->CreateRenderTarget(
SMALL_BACK_BUFFER_SURFACE_WIDTH, SMALL_BACK_BUFFER_SURFACE_HEIGHT,
desc.Format, D3DMULTISAMPLE_NONE, 0, TRUE, &m_pSmallBackBufferFP16TempSurface,
NULL );
}
pTmpSurface = m_pSmallBackBufferFP16TempSurface;
#if POSIX
pTmpSurface->AddRef( 0, "CShaderAPIDx8::GetBackBufferImageHDR public addref");
#else
pTmpSurface->AddRef();
#endif
desc.Width = SMALL_BACK_BUFFER_SURFACE_WIDTH;
desc.Height = SMALL_BACK_BUFFER_SURFACE_HEIGHT;
RECT srcRect, destRect;
RectToRECT( pSrcRect, srcRect );
RectToRECT( pDstRect, destRect );
hr = Dx9Device()->StretchRect( pBackBuffer, &srcRect, pTmpSurface, &destRect, filter );
if ( FAILED(hr) )
goto CleanUp;
}
else
{
// Normally we would only have to create a separate render target here and StretchBlt to it first
// if AA was enabled, but certain machines/drivers get reboots if we do GetRenderTargetData
// straight off the backbuffer.
hr = Dx9Device()->CreateRenderTarget( desc.Width, desc.Height, desc.Format,
D3DMULTISAMPLE_NONE, 0, TRUE, &pTmpSurface, NULL );
if ( FAILED(hr) )
goto CleanUp;
hr = Dx9Device()->StretchRect( pBackBuffer, NULL, pTmpSurface, NULL, filter );
if ( FAILED(hr) )
goto CleanUp;
}
// Create a buffer the same size and format
hr = Dx9Device()->CreateOffscreenPlainSurface( desc.Width, desc.Height,
desc.Format, D3DPOOL_SYSTEMMEM, &pSurfaceBits, NULL );
if (FAILED(hr))
goto CleanUp;
// Blit from the back buffer to our scratch buffer
hr = Dx9Device()->GetRenderTargetData( pTmpSurface ? pTmpSurface : pBackBuffer, pSurfaceBits );
if (FAILED(hr))
goto CleanUp2;
format = ImageLoader::D3DFormatToImageFormat(desc.Format);
if ( pTmpSurface )
{
pTmpSurface->Release();
}
pBackBuffer->Release();
return pSurfaceBits;
CleanUp2:
pSurfaceBits->Release();
CleanUp:
if ( pTmpSurface )
{
pTmpSurface->Release();
}
pBackBuffer->Release();
#else
Assert( 0 );
#endif
return 0;
}
//-----------------------------------------------------------------------------
// Returns a copy of the back buffer
//-----------------------------------------------------------------------------
IDirect3DSurface* CShaderAPIDx8::GetBackBufferImage( Rect_t *pSrcRect, Rect_t *pDstRect, ImageFormat& format )
{
#if !defined( _X360 )
if ( !m_pBackBufferSurface || ( m_hFullScreenTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) )
return NULL;
HRESULT hr;
D3DSURFACE_DESC desc;
FlushBufferedPrimitives();
// Get the current render target
IDirect3DSurface* pRenderTarget;
hr = Dx9Device()->GetRenderTarget( 0, &pRenderTarget );
if (FAILED(hr))
return 0;
// Find about its size and format
hr = pRenderTarget->GetDesc( &desc );
if ( desc.Format == D3DFMT_A16B16G16R16F || desc.Format == D3DFMT_A32B32G32R32F )
return GetBackBufferImageHDR( pSrcRect, pDstRect, format );
IDirect3DSurface *pSurfaceBits = NULL;
IDirect3DSurface *pTmpSurface = NULL;
int nRenderTargetRefCount;
nRenderTargetRefCount = 0;
if ( (desc.MultiSampleType == D3DMULTISAMPLE_NONE) && (pRenderTarget != m_pBackBufferSurface) &&
(pSrcRect->width == pDstRect->width) && (pSrcRect->height == pDstRect->height) )
{
// Don't bother to blit through the full-screen texture if we don't
// have to stretch, we're not coming from the backbuffer, and we don't have to do AA resolve
pTmpSurface = pRenderTarget;
#if POSIX
pTmpSurface->AddRef( 0, "CShaderAPIDx8::GetBackBufferImage public addref");
#else
pTmpSurface->AddRef();
#endif
}
else
{
Texture_t *pTex = &GetTexture( m_hFullScreenTexture );
IDirect3DTexture* pFullScreenTexture = (IDirect3DTexture*)pTex->GetTexture();
D3DTEXTUREFILTERTYPE filter = ((pDstRect->width != pSrcRect->width) || (pDstRect->height != pSrcRect->height)) ? D3DTEXF_LINEAR : D3DTEXF_NONE;
hr = pFullScreenTexture->GetSurfaceLevel( 0, &pTmpSurface );
if ( FAILED(hr) )
goto CleanUp;
if ( pTmpSurface == pRenderTarget )
{
Warning( "Can't blit from full-sized offscreen buffer!\n" );
goto CleanUp;
}
RECT srcRect, destRect;
srcRect.left = pSrcRect->x; srcRect.right = pSrcRect->x + pSrcRect->width;
srcRect.top = pSrcRect->y; srcRect.bottom = pSrcRect->y + pSrcRect->height;
srcRect.left = clamp( srcRect.left, 0, (int)desc.Width );
srcRect.right = clamp( srcRect.right, 0, (int)desc.Width );
srcRect.top = clamp( srcRect.top, 0, (int)desc.Height );
srcRect.bottom = clamp( srcRect.bottom, 0, (int)desc.Height );
destRect.left = pDstRect->x ; destRect.right = pDstRect->x + pDstRect->width;
destRect.top = pDstRect->y; destRect.bottom = pDstRect->y + pDstRect->height;
destRect.left = clamp( destRect.left, 0, (int)desc.Width );
destRect.right = clamp( destRect.right, 0, (int)desc.Width );
destRect.top = clamp( destRect.top, 0, (int)desc.Height );
destRect.bottom = clamp( destRect.bottom, 0, (int)desc.Height );
hr = Dx9Device()->StretchRect( pRenderTarget, &srcRect, pTmpSurface, &destRect, filter );
if ( FAILED(hr) )
{
AssertOnce( "Error resizing pixels!\n" );
goto CleanUp;
}
}
D3DSURFACE_DESC tmpDesc;
hr = pTmpSurface->GetDesc( &tmpDesc );
Assert( !FAILED(hr) );
// Create a buffer the same size and format
hr = Dx9Device()->CreateOffscreenPlainSurface( tmpDesc.Width, tmpDesc.Height,
desc.Format, D3DPOOL_SYSTEMMEM, &pSurfaceBits, NULL );
if ( FAILED(hr) )
{
AssertOnce( "Error creating offscreen surface!\n" );
goto CleanUp;
}
// Blit from the back buffer to our scratch buffer
hr = Dx9Device()->GetRenderTargetData( pTmpSurface, pSurfaceBits );
if ( FAILED(hr) )
{
AssertOnce( "Error copying bits into the offscreen surface!\n" );
goto CleanUp;
}
format = ImageLoader::D3DFormatToImageFormat( desc.Format );
pTmpSurface->Release();
#ifdef _DEBUG
nRenderTargetRefCount =
#endif
pRenderTarget->Release();
AssertOnce( nRenderTargetRefCount == 1 );
return pSurfaceBits;
CleanUp:
if ( pSurfaceBits )
{
pSurfaceBits->Release();
}
if ( pTmpSurface )
{
pTmpSurface->Release();
}
#else
Assert( 0 );
#endif
return 0;
}
//-----------------------------------------------------------------------------
// Copy bits from a host-memory surface
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CopyBitsFromHostSurface( IDirect3DSurface* pSurfaceBits,
const Rect_t &dstRect, unsigned char *pData, ImageFormat srcFormat, ImageFormat dstFormat, int nDstStride )
{
// Copy out the bits...
RECT rect;
rect.left = dstRect.x;
rect.right = dstRect.x + dstRect.width;
rect.top = dstRect.y;
rect.bottom = dstRect.y + dstRect.height;
D3DLOCKED_RECT lockedRect;
HRESULT hr;
int flags = D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK;
tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ );
hr = pSurfaceBits->LockRect( &lockedRect, &rect, flags );
if ( !FAILED( hr ) )
{
unsigned char *pImage = (unsigned char *)lockedRect.pBits;
ShaderUtil()->ConvertImageFormat( (unsigned char *)pImage, srcFormat,
pData, dstFormat, dstRect.width, dstRect.height, lockedRect.Pitch, nDstStride );
hr = pSurfaceBits->UnlockRect( );
}
}
//-----------------------------------------------------------------------------
// Reads from the current read buffer + stretches
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ReadPixels( Rect_t *pSrcRect, Rect_t *pDstRect, unsigned char *pData, ImageFormat dstFormat, int nDstStride )
{
LOCK_SHADERAPI();
Assert( pDstRect );
if ( IsPC() || !IsX360() )
{
Rect_t srcRect;
if ( !pSrcRect )
{
srcRect.x = srcRect.y = 0;
srcRect.width = m_nWindowWidth;
srcRect.height = m_nWindowHeight;
pSrcRect = &srcRect;
}
ImageFormat format;
IDirect3DSurface* pSurfaceBits = GetBackBufferImage( pSrcRect, pDstRect, format );
if ( pSurfaceBits )
{
CopyBitsFromHostSurface( pSurfaceBits, *pDstRect, pData, format, dstFormat, nDstStride );
// Release the temporary surface
pSurfaceBits->Release();
}
}
else
{
#if defined( _X360 )
// 360 requires material system to handle due to RT complexities
ShaderUtil()->ReadBackBuffer( pSrcRect, pDstRect, pData, dstFormat, nDstStride );
#endif
}
}
//-----------------------------------------------------------------------------
// Reads from the current read buffer
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ReadPixels( int x, int y, int width, int height, unsigned char *pData, ImageFormat dstFormat )
{
Rect_t rect;
rect.x = x;
rect.y = y;
rect.width = width;
rect.height = height;
if ( IsPC() || !IsX360() )
{
ImageFormat format;
IDirect3DSurface* pSurfaceBits = GetBackBufferImage( &rect, &rect, format );
if (pSurfaceBits)
{
CopyBitsFromHostSurface( pSurfaceBits, rect, pData, format, dstFormat, 0 );
// Release the temporary surface
pSurfaceBits->Release();
}
}
else
{
#if defined( _X360 )
// 360 requires material system to handle due to RT complexities
ShaderUtil()->ReadBackBuffer( &rect, &rect, pData, dstFormat, 0 );
#endif
}
}
//-----------------------------------------------------------------------------
// Binds a particular material to render with
//-----------------------------------------------------------------------------
void CShaderAPIDx8::Bind( IMaterial* pMaterial )
{
LOCK_SHADERAPI();
IMaterialInternal* pMatInt = static_cast<IMaterialInternal*>( pMaterial );
bool bMaterialChanged;
if ( m_pMaterial && pMatInt && m_pMaterial->InMaterialPage() && pMatInt->InMaterialPage() )
{
bMaterialChanged = ( m_pMaterial->GetMaterialPage() != pMatInt->GetMaterialPage() );
}
else
{
bMaterialChanged = ( m_pMaterial != pMatInt ) || ( m_pMaterial && m_pMaterial->InMaterialPage() ) || ( pMatInt && pMatInt->InMaterialPage() );
}
if ( bMaterialChanged )
{
FlushBufferedPrimitives();
#ifdef RECORDING
RECORD_DEBUG_STRING( ( char * )pMaterial->GetName() );
IShader *pShader = pMatInt->GetShader();
if( pShader && pShader->GetName() )
{
RECORD_DEBUG_STRING( pShader->GetName() );
}
else
{
RECORD_DEBUG_STRING( "<NULL SHADER>" );
}
#endif
m_pMaterial = pMatInt;
#if ( defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) )
PIXifyName( s_pPIXMaterialName, sizeof( s_pPIXMaterialName ), m_pMaterial->GetName() );
#endif
}
}
// Get the currently bound material
IMaterialInternal* CShaderAPIDx8::GetBoundMaterial()
{
return m_pMaterial;
}
//-----------------------------------------------------------------------------
// Binds a standard texture
//-----------------------------------------------------------------------------
void CShaderAPIDx8::BindStandardTexture( Sampler_t sampler, StandardTextureId_t id )
{
if ( m_StdTextureHandles[id] != INVALID_SHADERAPI_TEXTURE_HANDLE )
{
BindTexture( sampler, m_StdTextureHandles[id] );
}
else
{
ShaderUtil()->BindStandardTexture( sampler, id );
}
}
void CShaderAPIDx8::BindStandardVertexTexture( VertexTextureSampler_t sampler, StandardTextureId_t id )
{
ShaderUtil()->BindStandardVertexTexture( sampler, id );
}
void CShaderAPIDx8::GetStandardTextureDimensions( int *pWidth, int *pHeight, StandardTextureId_t id )
{
ShaderUtil()->GetStandardTextureDimensions( pWidth, pHeight, id );
}
//-----------------------------------------------------------------------------
// Gets the lightmap dimensions
//-----------------------------------------------------------------------------
void CShaderAPIDx8::GetLightmapDimensions( int *w, int *h )
{
ShaderUtil()->GetLightmapDimensions( w, h );
}
//-----------------------------------------------------------------------------
// Selection mode methods
//-----------------------------------------------------------------------------
int CShaderAPIDx8::SelectionMode( bool selectionMode )
{
LOCK_SHADERAPI();
int numHits = m_NumHits;
if (m_InSelectionMode)
{
WriteHitRecord();
}
m_InSelectionMode = selectionMode;
m_pCurrSelectionRecord = m_pSelectionBuffer;
m_NumHits = 0;
return numHits;
}
bool CShaderAPIDx8::IsInSelectionMode() const
{
return m_InSelectionMode;
}
void CShaderAPIDx8::SelectionBuffer( unsigned int* pBuffer, int size )
{
LOCK_SHADERAPI();
Assert( !m_InSelectionMode );
Assert( pBuffer && size );
m_pSelectionBufferEnd = pBuffer + size;
m_pSelectionBuffer = pBuffer;
m_pCurrSelectionRecord = pBuffer;
}
void CShaderAPIDx8::ClearSelectionNames( )
{
LOCK_SHADERAPI();
if (m_InSelectionMode)
{
WriteHitRecord();
}
m_SelectionNames.Clear();
}
void CShaderAPIDx8::LoadSelectionName( int name )
{
LOCK_SHADERAPI();
if (m_InSelectionMode)
{
WriteHitRecord();
Assert( m_SelectionNames.Count() > 0 );
m_SelectionNames.Top() = name;
}
}
void CShaderAPIDx8::PushSelectionName( int name )
{
LOCK_SHADERAPI();
if (m_InSelectionMode)
{
WriteHitRecord();
m_SelectionNames.Push(name);
}
}
void CShaderAPIDx8::PopSelectionName()
{
LOCK_SHADERAPI();
if (m_InSelectionMode)
{
WriteHitRecord();
m_SelectionNames.Pop();
}
}
void CShaderAPIDx8::WriteHitRecord( )
{
FlushBufferedPrimitives();
if (m_SelectionNames.Count() && (m_SelectionMinZ != FLT_MAX))
{
Assert( m_pCurrSelectionRecord + m_SelectionNames.Count() + 3 < m_pSelectionBufferEnd );
*m_pCurrSelectionRecord++ = m_SelectionNames.Count();
// NOTE: because of rounding, "(uint32)(float)UINT32_MAX" yields zero(!), hence the use of doubles.
// [ ALSO: As of Nov 2011, VS2010 exhibits a debug build code-gen bug if we cast the result to int32 instead of uint32 ]
*m_pCurrSelectionRecord++ = (uint32)( 0.5 + m_SelectionMinZ*(double)((uint32)~0) );
*m_pCurrSelectionRecord++ = (uint32)( 0.5 + m_SelectionMaxZ*(double)((uint32)~0) );
for (int i = 0; i < m_SelectionNames.Count(); ++i)
{
*m_pCurrSelectionRecord++ = m_SelectionNames[i];
}
++m_NumHits;
}
m_SelectionMinZ = FLT_MAX;
m_SelectionMaxZ = FLT_MIN;
}
// We hit somefin in selection mode
void CShaderAPIDx8::RegisterSelectionHit( float minz, float maxz )
{
if (minz < 0)
minz = 0;
if (maxz > 1)
maxz = 1;
if (m_SelectionMinZ > minz)
m_SelectionMinZ = minz;
if (m_SelectionMaxZ < maxz)
m_SelectionMaxZ = maxz;
}
int CShaderAPIDx8::GetCurrentNumBones( void ) const
{
return m_DynamicState.m_NumBones;
}
bool CShaderAPIDx8::IsHWMorphingEnabled( ) const
{
return m_DynamicState.m_bHWMorphingEnabled;
}
//-----------------------------------------------------------------------------
// Inserts the lighting block into the code
//-----------------------------------------------------------------------------
// If you change the number of lighting combinations, change this enum
enum
{
DX8_LIGHTING_COMBINATION_COUNT = 22,
DX9_LIGHTING_COMBINATION_COUNT = 35
};
#define MAX_LIGHTS 4
// NOTE: These should match g_lightType* in vsh_prep.pl!
static int g_DX8LightCombinations[][4] =
{
// static ambient local1 local2
// This is a special case for no lighting at all.
{ LIGHT_NONE, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE },
// This is a special case so that we don't have to do the ambient cube
// when we only have static lighting
{ LIGHT_STATIC, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_NONE },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_NONE },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_SPOT },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_POINT, },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_DIRECTIONAL, },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_POINT, },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_DIRECTIONAL, },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_NONE },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_NONE },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_SPOT },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_POINT, },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_DIRECTIONAL, },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_POINT, },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_DIRECTIONAL, },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, }
};
// NOTE: These should match g_lightType* in vsh_prep.pl!
// They also correspond to the parallel g_LocalLightTypeXArray[] arrays in common_vs_fxc.h
static int g_DX9LightCombinations[][MAX_LIGHTS] =
{
// local0 local1 local2 local3
{ LIGHT_NONE, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE }, // Zero lights [ Combo 0]
{ LIGHT_SPOT, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE }, // One light [ Combo 1]
{ LIGHT_POINT, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_NONE, LIGHT_NONE }, // Two lights [ Combo 4]
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_POINT, LIGHT_POINT, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_NONE }, // Three lights [ Combo 10]
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_POINT, LIGHT_POINT, LIGHT_POINT, LIGHT_NONE },
{ LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT }, // Four lights [ Combo 20]
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_DIRECTIONAL },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT, LIGHT_DIRECTIONAL },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT, LIGHT_POINT },
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL },
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
{ LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
{ LIGHT_POINT, LIGHT_POINT, LIGHT_POINT, LIGHT_POINT },
{ LIGHT_POINT, LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL },
{ LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
{ LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
{ LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL }
};
// This is just for getting light combos for DX8
// For DX9, use GetDX9LightState()
// It is up to the shader cpp files to use the right method
int CShaderAPIDx8::GetCurrentLightCombo( void ) const
{
Assert( g_pHardwareConfig->Caps().m_nDXSupportLevel <= 81 );
Assert( m_DynamicState.m_NumLights <= 2 );
COMPILE_TIME_ASSERT( DX8_LIGHTING_COMBINATION_COUNT ==
sizeof( g_DX8LightCombinations ) / sizeof( g_DX8LightCombinations[0] ) );
// hack . . do this a cheaper way.
bool bUseAmbientCube;
if( m_DynamicState.m_AmbientLightCube[0][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[0][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[0][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[1][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[1][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[1][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[2][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[2][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[2][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[3][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[3][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[3][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[4][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[4][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[4][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[5][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[5][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[5][2] == 0.0f )
{
bUseAmbientCube = false;
}
else
{
bUseAmbientCube = true;
}
Assert( m_pRenderMesh );
const VertexShaderLightTypes_t *pLightType = m_DynamicState.m_LightType;
if( m_DynamicState.m_NumLights == 0 && !bUseAmbientCube )
{
if( m_pRenderMesh->HasColorMesh() )
return 1; // special case for static lighting only
else
return 0; // special case for no lighting at all.
}
int i;
// hack - skip the first two for now since we don't know if the ambient cube is needed or not.
for( i = 2; i < DX9_LIGHTING_COMBINATION_COUNT; ++i )
{
int j;
for( j = 0; j < m_DynamicState.m_NumLights; ++j )
{
if( pLightType[j] != g_DX8LightCombinations[i][j+2] )
break;
}
if( j == m_DynamicState.m_NumLights )
{
while( j < 2 )
{
if (g_DX8LightCombinations[i][j+2] != LIGHT_NONE)
break;
++j;
}
if( j == 2 )
{
if( m_pRenderMesh->HasColorMesh() )
{
return i + 10;
}
else
{
return i;
}
}
}
}
// should never get here!
Assert( 0 );
return 0;
}
void CShaderAPIDx8::GetDX9LightState( LightState_t *state ) const
{
// hack . . do this a cheaper way.
if( m_DynamicState.m_AmbientLightCube[0][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[0][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[0][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[1][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[1][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[1][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[2][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[2][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[2][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[3][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[3][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[3][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[4][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[4][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[4][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[5][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[5][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[5][2] == 0.0f )
{
state->m_bAmbientLight = false;
}
else
{
state->m_bAmbientLight = true;
}
Assert( m_pRenderMesh );
Assert( m_DynamicState.m_NumLights <= 4 );
if ( g_pHardwareConfig->SupportsPixelShaders_2_b() )
{
Assert( m_DynamicState.m_NumLights <= MAX_LIGHTS ); // 2b hardware gets four lights
}
else
{
Assert( m_DynamicState.m_NumLights <= (MAX_LIGHTS-2) ); // 2.0 hardware gets two less
}
#ifdef OSX
state->m_nNumLights = MIN(MAX_NUM_LIGHTS,m_DynamicState.m_NumLights);
#else
state->m_nNumLights = m_DynamicState.m_NumLights;
#endif
state->m_nNumLights = m_DynamicState.m_NumLights;
state->m_bStaticLightVertex = m_pRenderMesh->HasColorMesh();
state->m_bStaticLightTexel = false; // For now
}
MaterialFogMode_t CShaderAPIDx8::GetCurrentFogType( void ) const
{
return m_DynamicState.m_SceneFog;
}
void CShaderAPIDx8::RecordString( const char *pStr )
{
RECORD_STRING( pStr );
}
void CShaderAPIDx8::EvictManagedResourcesInternal()
{
if ( IsX360() )
return;
if ( !ThreadOwnsDevice() || !ThreadInMainThread() )
{
ShaderUtil()->OnThreadEvent( SHADER_THREAD_EVICT_RESOURCES );
return;
}
if ( mat_debugalttab.GetBool() )
{
Warning( "mat_debugalttab: CShaderAPIDx8::EvictManagedResourcesInternal\n" );
}
#if !defined( _X360 )
if ( Dx9Device() )
{
Dx9Device()->EvictManagedResources();
}
#endif
}
void CShaderAPIDx8::EvictManagedResources( void )
{
if ( IsX360() )
{
return;
}
LOCK_SHADERAPI();
Assert(ThreadOwnsDevice());
// Tell other material system applications to release resources
SendIPCMessage( EVICT_MESSAGE );
EvictManagedResourcesInternal();
}
bool CShaderAPIDx8::IsDebugTextureListFresh( int numFramesAllowed /* = 1 */ )
{
return ( m_nDebugDataExportFrame <= m_CurrentFrame ) && ( m_nDebugDataExportFrame >= m_CurrentFrame - numFramesAllowed );
}
bool CShaderAPIDx8::SetDebugTextureRendering( bool bEnable )
{
bool bVal = m_bDebugTexturesRendering;
m_bDebugTexturesRendering = bEnable;
return bVal;
}
void CShaderAPIDx8::EnableDebugTextureList( bool bEnable )
{
m_bEnableDebugTextureList = bEnable;
}
void CShaderAPIDx8::EnableGetAllTextures( bool bEnable )
{
m_bDebugGetAllTextures = bEnable;
}
KeyValues* CShaderAPIDx8::GetDebugTextureList()
{
return m_pDebugTextureList;
}
int CShaderAPIDx8::GetTextureMemoryUsed( TextureMemoryType eTextureMemory )
{
switch ( eTextureMemory )
{
case MEMORY_BOUND_LAST_FRAME:
return m_nTextureMemoryUsedLastFrame;
case MEMORY_TOTAL_LOADED:
return m_nTextureMemoryUsedTotal;
case MEMORY_ESTIMATE_PICMIP_1:
return m_nTextureMemoryUsedPicMip1;
case MEMORY_ESTIMATE_PICMIP_2:
return m_nTextureMemoryUsedPicMip2;
default:
return 0;
}
}
// Allocate and delete query objects.
ShaderAPIOcclusionQuery_t CShaderAPIDx8::CreateOcclusionQueryObject( void )
{
// don't allow this on <80 because it falls back to wireframe in that case
if( m_DeviceSupportsCreateQuery == 0 || g_pHardwareConfig->Caps().m_nDXSupportLevel < 80 )
return INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE;
// While we're deactivated, m_OcclusionQueryObjects just holds NULL pointers.
// Create a dummy one here and let ReacquireResources create the actual D3D object.
if ( IsDeactivated() )
return INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE;
IDirect3DQuery9 *pQuery = NULL;
HRESULT hr = Dx9Device()->CreateQuery( D3DQUERYTYPE_OCCLUSION, &pQuery );
return ( hr == D3D_OK ) ? (ShaderAPIOcclusionQuery_t)pQuery : INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE;
}
void CShaderAPIDx8::DestroyOcclusionQueryObject( ShaderAPIOcclusionQuery_t handle )
{
IDirect3DQuery9 *pQuery = (IDirect3DQuery9 *)handle;
int nRetVal = pQuery->Release();
Assert( nRetVal == 0 );
}
// Bracket drawing with begin and end so that we can get counts next frame.
void CShaderAPIDx8::BeginOcclusionQueryDrawing( ShaderAPIOcclusionQuery_t handle )
{
IDirect3DQuery9 *pQuery = (IDirect3DQuery9 *)handle;
HRESULT hResult = pQuery->Issue( D3DISSUE_BEGIN );
Assert( hResult == D3D_OK );
}
void CShaderAPIDx8::EndOcclusionQueryDrawing( ShaderAPIOcclusionQuery_t handle )
{
IDirect3DQuery9 *pQuery = (IDirect3DQuery9 *)handle;
HRESULT hResult = pQuery->Issue( D3DISSUE_END );
Assert( hResult == D3D_OK );
}
// Get the number of pixels rendered between begin and end on an earlier frame.
// Calling this in the same frame is a huge perf hit!
int CShaderAPIDx8::OcclusionQuery_GetNumPixelsRendered( ShaderAPIOcclusionQuery_t handle, bool bFlush )
{
LOCK_SHADERAPI();
IDirect3DQuery9 *pQuery = (IDirect3DQuery9 *)handle;
tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ );
DWORD nPixels;
HRESULT hResult = pQuery->GetData( &nPixels, sizeof( nPixels ), bFlush ? D3DGETDATA_FLUSH : 0 );
// This means that the query will not finish and resulted in an error, game should use
// the previous query's results and not reissue the query
if ( ( hResult == D3DERR_DEVICELOST ) || ( hResult == D3DERR_NOTAVAILABLE ) )
return OCCLUSION_QUERY_RESULT_ERROR;
// This means the query isn't finished yet, game will have to use the previous query's
// results and not reissue the query; wait for query to finish later.
if ( hResult == S_FALSE )
return OCCLUSION_QUERY_RESULT_PENDING;
// NOTE: This appears to work around a driver bug for ATI on Vista
if ( nPixels & 0x80000000 )
{
nPixels = 0;
}
return nPixels;
}
void CShaderAPIDx8::SetPixelShaderFogParams( int reg, ShaderFogMode_t fogMode )
{
m_DelayedShaderConstants.iPixelShaderFogParams = reg; //save it off in case the ShaderFogMode_t disables fog. We only find out later.
float fogParams[4];
if( (GetPixelFogMode() != MATERIAL_FOG_NONE) && (fogMode != SHADER_FOGMODE_DISABLED) )
{
float ooFogRange = 1.0f;
float fStart = m_VertexShaderFogParams[0];
float fEnd = m_VertexShaderFogParams[1];
// Check for divide by zero
if ( fEnd != fStart )
{
ooFogRange = 1.0f / ( fEnd - fStart );
}
fogParams[0] = fStart * ooFogRange; // fogStart / ( fogEnd - fogStart )
fogParams[1] = m_DynamicState.m_FogZ; // water height
fogParams[2] = clamp( m_flFogMaxDensity, 0.0f, 1.0f ); // Max fog density
fogParams[3] = ooFogRange; // 1 / ( fogEnd - fogStart );
if (GetPixelFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z)
{
// terms are unused for height fog, forcing 1.0 allows unified PS math
fogParams[0] = 0.0f;
fogParams[2] = 1.0f;
}
}
else
{
//emulating MATERIAL_FOG_NONE by setting the parameters so that CalcRangeFog() always returns 0. Gets rid of a dynamic combo across the ps2x set.
fogParams[0] = 0.0f;
fogParams[1] = m_DynamicState.m_FogZ; // water height
fogParams[2] = 1.0f; // Max fog density
fogParams[3] = 0.0f;
}
// cFogEndOverFogRange, cFogOne, unused, cOOFogRange
SetPixelShaderConstant( reg, fogParams, 1 );
}
void CShaderAPIDx8::SetPixelShaderFogParams( int reg )
{
SetPixelShaderFogParams( reg, m_TransitionTable.CurrentShadowState()->m_FogMode );
}
void CShaderAPIDx8::SetFlashlightState( const FlashlightState_t &state, const VMatrix &worldToTexture )
{
LOCK_SHADERAPI();
SetFlashlightStateEx( state, worldToTexture, NULL );
}
void CShaderAPIDx8::SetFlashlightStateEx( const FlashlightState_t &state, const VMatrix &worldToTexture, ITexture *pFlashlightDepthTexture )
{
LOCK_SHADERAPI();
// fixme: do a test here.
FlushBufferedPrimitives();
m_FlashlightState = state;
m_FlashlightWorldToTexture = worldToTexture;
m_pFlashlightDepthTexture = pFlashlightDepthTexture;
if ( !g_pHardwareConfig->SupportsPixelShaders_2_b() )
{
m_FlashlightState.m_bEnableShadows = false;
m_pFlashlightDepthTexture = NULL;
}
}
const FlashlightState_t &CShaderAPIDx8::GetFlashlightState( VMatrix &worldToTexture ) const
{
worldToTexture = m_FlashlightWorldToTexture;
return m_FlashlightState;
}
const FlashlightState_t &CShaderAPIDx8::GetFlashlightStateEx( VMatrix &worldToTexture, ITexture **ppFlashlightDepthTexture ) const
{
worldToTexture = m_FlashlightWorldToTexture;
*ppFlashlightDepthTexture = m_pFlashlightDepthTexture;
return m_FlashlightState;
}
bool CShaderAPIDx8::SupportsMSAAMode( int nMSAAMode )
{
if ( IsX360() )
{
return false;
}
return ( D3D_OK == D3D()->CheckDeviceMultiSampleType( m_DisplayAdapter, m_DeviceType,
m_PresentParameters.BackBufferFormat,
m_PresentParameters.Windowed,
ComputeMultisampleType( nMSAAMode ), NULL ) );
}
bool CShaderAPIDx8::SupportsCSAAMode( int nNumSamples, int nQualityLevel )
{
#ifdef DX_TO_GL_ABSTRACTION
// GL_NV_framebuffer_multisample_coverage
return false;
#endif
// Only nVidia does this kind of AA
if ( g_pHardwareConfig->Caps().m_VendorID != VENDORID_NVIDIA )
return false;
DWORD dwQualityLevels = 0;
HRESULT hr = D3D()->CheckDeviceMultiSampleType( m_DisplayAdapter, m_DeviceType,
m_PresentParameters.BackBufferFormat,
m_PresentParameters.Windowed,
ComputeMultisampleType( nNumSamples ), &dwQualityLevels );
return ( ( D3D_OK == hr ) && ( (int) dwQualityLevels >= nQualityLevel ) );
}
bool CShaderAPIDx8::SupportsShadowDepthTextures( void )
{
return g_pHardwareConfig->Caps().m_bSupportsShadowDepthTextures;
}
bool CShaderAPIDx8::SupportsBorderColor( void ) const
{
return g_pHardwareConfig->Caps().m_bSupportsBorderColor;
}
bool CShaderAPIDx8::SupportsFetch4( void )
{
return IsPC() && g_pHardwareConfig->Caps().m_bSupportsFetch4;
}
ImageFormat CShaderAPIDx8::GetShadowDepthTextureFormat( void )
{
return g_pHardwareConfig->Caps().m_ShadowDepthTextureFormat;
}
ImageFormat CShaderAPIDx8::GetNullTextureFormat( void )
{
return g_pHardwareConfig->Caps().m_NullTextureFormat;
}
void CShaderAPIDx8::SetShadowDepthBiasFactors( float fShadowSlopeScaleDepthBias, float fShadowDepthBias )
{
m_fShadowSlopeScaleDepthBias = fShadowSlopeScaleDepthBias;
m_fShadowDepthBias = fShadowDepthBias;
}
void CShaderAPIDx8::ClearVertexAndPixelShaderRefCounts()
{
LOCK_SHADERAPI();
ShaderManager()->ClearVertexAndPixelShaderRefCounts();
}
void CShaderAPIDx8::PurgeUnusedVertexAndPixelShaders()
{
LOCK_SHADERAPI();
ShaderManager()->PurgeUnusedVertexAndPixelShaders();
}
bool CShaderAPIDx8::UsingSoftwareVertexProcessing() const
{
return g_pHardwareConfig->Caps().m_bSoftwareVertexProcessing;
}
ITexture *CShaderAPIDx8::GetRenderTargetEx( int nRenderTargetID )
{
return ShaderUtil()->GetRenderTargetEx( nRenderTargetID );
}
float CShaderAPIDx8::GetLightMapScaleFactor( void ) const
{
switch( HardwareConfig()->GetHDRType() )
{
case HDR_TYPE_FLOAT:
return 1.0;
break;
case HDR_TYPE_INTEGER:
return 16.0;
case HDR_TYPE_NONE:
default:
return GammaToLinearFullRange( 2.0 ); // light map scale
}
}
void CShaderAPIDx8::SetToneMappingScaleLinear( const Vector &scale )
{
if ( g_pHardwareConfig->SupportsPixelShaders_2_0() )
{
// Flush buffered primitives before changing the tone map scalar!
FlushBufferedPrimitives();
Vector scale_to_use = scale;
m_ToneMappingScale.AsVector3D() = scale_to_use;
bool mode_uses_srgb=false;
switch( HardwareConfig()->GetHDRType() )
{
case HDR_TYPE_NONE:
m_ToneMappingScale.x = 1.0; // output scale
m_ToneMappingScale.z = 1.0; // reflection map scale
break;
case HDR_TYPE_FLOAT:
m_ToneMappingScale.x = scale_to_use.x; // output scale
m_ToneMappingScale.z = 1.0; // reflection map scale
break;
case HDR_TYPE_INTEGER:
mode_uses_srgb = true;
m_ToneMappingScale.x = scale_to_use.x; // output scale
m_ToneMappingScale.z = 16.0; // reflection map scale
break;
}
m_ToneMappingScale.y = GetLightMapScaleFactor(); // light map scale
// w component gets gamma scale
m_ToneMappingScale.w = LinearToGammaFullRange( m_ToneMappingScale.x );
SetPixelShaderConstant( TONE_MAPPING_SCALE_PSH_CONSTANT, m_ToneMappingScale.Base() );
// We have to change the fog color since we tone map directly in the shaders in integer HDR mode.
if ( HardwareConfig()->GetHDRType() == HDR_TYPE_INTEGER && m_TransitionTable.CurrentShadowState() )
{
// Get the shadow state in sync since it depends on SetToneMappingScaleLinear.
ApplyFogMode( m_TransitionTable.CurrentShadowState()->m_FogMode, mode_uses_srgb, m_TransitionTable.CurrentShadowState()->m_bDisableFogGammaCorrection );
}
}
}
const Vector & CShaderAPIDx8::GetToneMappingScaleLinear( void ) const
{
return m_ToneMappingScale.AsVector3D();
}
void CShaderAPIDx8::EnableLinearColorSpaceFrameBuffer( bool bEnable )
{
LOCK_SHADERAPI();
m_TransitionTable.EnableLinearColorSpaceFrameBuffer( bEnable );
}
void CShaderAPIDx8::SetPSNearAndFarZ( int pshReg )
{
VMatrix m;
GetMatrix( MATERIAL_PROJECTION, m.m[0] );
// m[2][2] = F/(N-F) (flip sign if RH)
// m[3][2] = NF/(N-F)
float vNearFar[4];
float N = m[3][2] / m[2][2];
float F = (m[3][2]*N) / (N + m[3][2]);
vNearFar[0] = N;
vNearFar[1] = F;
SetPixelShaderConstant( pshReg, vNearFar, 1 );
}
void CShaderAPIDx8::SetFloatRenderingParameter( int parm_number, float value )
{
LOCK_SHADERAPI();
if ( parm_number < ARRAYSIZE( FloatRenderingParameters ))
FloatRenderingParameters[parm_number] = value;
}
void CShaderAPIDx8::SetIntRenderingParameter( int parm_number, int value )
{
LOCK_SHADERAPI();
if ( parm_number < ARRAYSIZE( IntRenderingParameters ))
IntRenderingParameters[parm_number] = value;
}
void CShaderAPIDx8::SetVectorRenderingParameter( int parm_number, Vector const & value )
{
LOCK_SHADERAPI();
if ( parm_number < ARRAYSIZE( VectorRenderingParameters ))
VectorRenderingParameters[parm_number] = value;
}
float CShaderAPIDx8::GetFloatRenderingParameter( int parm_number ) const
{
LOCK_SHADERAPI();
if ( parm_number < ARRAYSIZE( FloatRenderingParameters ))
return FloatRenderingParameters[parm_number];
else
return 0.0;
}
int CShaderAPIDx8::GetIntRenderingParameter( int parm_number ) const
{
LOCK_SHADERAPI();
if ( parm_number < ARRAYSIZE( IntRenderingParameters ))
return IntRenderingParameters[parm_number];
else
return 0;
}
Vector CShaderAPIDx8::GetVectorRenderingParameter( int parm_number ) const
{
LOCK_SHADERAPI();
if ( parm_number < ARRAYSIZE( VectorRenderingParameters ))
return VectorRenderingParameters[parm_number];
else
return Vector( 0, 0, 0 );
}
// stencil entry points
void CShaderAPIDx8::SetStencilEnable( bool onoff )
{
LOCK_SHADERAPI();
SetRenderState( D3DRS_STENCILENABLE, onoff?TRUE:FALSE, true );
}
void CShaderAPIDx8::SetStencilFailOperation( StencilOperation_t op )
{
LOCK_SHADERAPI();
SetRenderState( D3DRS_STENCILFAIL, op, true );
}
void CShaderAPIDx8::SetStencilZFailOperation( StencilOperation_t op )
{
LOCK_SHADERAPI();
SetRenderState( D3DRS_STENCILZFAIL, op, true );
}
void CShaderAPIDx8::SetStencilPassOperation( StencilOperation_t op )
{
LOCK_SHADERAPI();
SetRenderState( D3DRS_STENCILPASS, op, true );
}
void CShaderAPIDx8::SetStencilCompareFunction( StencilComparisonFunction_t cmpfn )
{
LOCK_SHADERAPI();
SetRenderState( D3DRS_STENCILFUNC, cmpfn, true );
}
void CShaderAPIDx8::SetStencilReferenceValue( int ref )
{
LOCK_SHADERAPI();
SetRenderState( D3DRS_STENCILREF, ref, true );
}
void CShaderAPIDx8::SetStencilTestMask( uint32 msk )
{
LOCK_SHADERAPI();
SetRenderState( D3DRS_STENCILMASK, msk, true );
}
void CShaderAPIDx8::SetStencilWriteMask( uint32 msk )
{
LOCK_SHADERAPI();
SetRenderState( D3DRS_STENCILWRITEMASK, msk, true );
}
void CShaderAPIDx8::ClearStencilBufferRectangle(
int xmin, int ymin, int xmax, int ymax,int value)
{
LOCK_SHADERAPI();
D3DRECT clear;
clear.x1 = xmin;
clear.y1 = ymin;
clear.x2 = xmax;
clear.y2 = ymax;
Dx9Device()->Clear(
1, &clear, D3DCLEAR_STENCIL, 0, 0, value );
}
int CShaderAPIDx8::CompareSnapshots( StateSnapshot_t snapshot0, StateSnapshot_t snapshot1 )
{
LOCK_SHADERAPI();
const ShadowState_t &shadow0 = m_TransitionTable.GetSnapshot(snapshot0);
const ShadowState_t &shadow1 = m_TransitionTable.GetSnapshot(snapshot1);
const ShadowShaderState_t &shader0 = m_TransitionTable.GetSnapshotShader(snapshot0);
const ShadowShaderState_t &shader1 = m_TransitionTable.GetSnapshotShader(snapshot1);
int dVertex = shader0.m_VertexShader - shader1.m_VertexShader;
if ( dVertex )
return dVertex;
int dVCombo = shader0.m_nStaticVshIndex - shader1.m_nStaticVshIndex;
if ( dVCombo)
return dVCombo;
int dPixel = shader0.m_PixelShader - shader1.m_PixelShader;
if ( dPixel )
return dPixel;
int dPCombo = shader0.m_nStaticPshIndex - shader1.m_nStaticPshIndex;
if ( dPCombo)
return dPCombo;
return snapshot0 - snapshot1;
}
//-----------------------------------------------------------------------------
// X360 TTF support requires XUI state manipulation of d3d.
// Font support lives inside the shaderapi in order to maintain privacy of d3d.
//-----------------------------------------------------------------------------
#if defined( _X360 )
HXUIFONT CShaderAPIDx8::OpenTrueTypeFont( const char *pFontname, int tall, int style )
{
LOCK_SHADERAPI();
struct fontTable_t
{
const char *pFontName;
const char *pPath;
};
// explicit mapping now required, dvd searching to expensive
static fontTable_t fontToFilename[] =
{
{"tf2", "tf/resource/tf2.ttf"},
{"tf2 build", "tf/resource/tf2build.ttf"},
{"tf2 professor", "tf/resource/tf2professor.ttf"},
{"tf2 secondary", "tf/resource/tf2secondary.ttf"},
{"team fortress", "tf/resource/tf.ttf"},
{"tfd", "tf/resource/tfd.ttf"},
{"tflogo", "tf/resource/tflogo.ttf"},
{"hl2ep2", "ep2/resource/hl2ep2.ttf"},
{"hl2ep1", "episodic/resource/hl2ep1.ttf"},
{"halflife2", "hl2/resource/halflife2.ttf"},
{"hl2cross", "hl2/resource/HL2Crosshairs.ttf"},
{"courier new", "platform/vgui/fonts/cour.ttf"},
{"times new roman", "platform/vgui/fonts/times.ttf"},
{"trebuchet ms", "platform/vgui/fonts/trebuc.ttf"},
{"verdana", "platform/vgui/fonts/verdana.ttf"},
{"tahoma", "platform/vgui/fonts/tahoma.ttf"},
};
// remap typeface to diskname
const char *pDiskname = NULL;
for ( int i=0; i<ARRAYSIZE( fontToFilename ); i++ )
{
if ( !V_stricmp( pFontname, fontToFilename[i].pFontName ) )
{
pDiskname = fontToFilename[i].pPath;
break;
}
}
if ( !pDiskname )
{
// not found
DevMsg( "True Type Font: '%s' unknown.\n", pFontname );
return NULL;
}
// font will be registered using typeface name
wchar_t wchFontname[MAX_PATH];
Q_UTF8ToUnicode( pFontname, wchFontname, sizeof( wchFontname ) );
// find font in registered typefaces
TypefaceDescriptor *pDescriptors = NULL;
DWORD numTypeFaces = 0;
HRESULT hr = XuiEnumerateTypefaces( &pDescriptors, &numTypeFaces );
if ( FAILED( hr ) )
{
return NULL;
}
bool bRegistered = false;
for ( DWORD i=0; i<numTypeFaces; i++ )
{
if ( !V_wcscmp( pDescriptors->szTypeface, wchFontname ) )
{
bRegistered = true;
break;
}
}
XuiDestroyTypefaceList( pDescriptors, numTypeFaces );
if ( !bRegistered )
{
// unregistered type face, register type face and retry
// only file based resource locators work
char filename[MAX_PATH];
V_snprintf( filename, sizeof( filename ), "file://d:/%s", pDiskname );
Q_FixSlashes( filename, '/' );
wchar_t wchFilename[MAX_PATH];
Q_UTF8ToUnicode( filename, wchFilename, sizeof( wchFilename ) );
TypefaceDescriptor desc;
desc.fBaselineAdjust = 0;
desc.szFallbackTypeface = NULL;
desc.szLocator = wchFilename;
desc.szReserved1 = 0;
desc.szTypeface = wchFontname;
hr = XuiRegisterTypeface( &desc, FALSE );
if ( FAILED( hr ) )
{
return NULL;
}
}
// empirically derived factor to achieve desired cell height
float pointSize = tall * 0.59f;
HXUIFONT hFont = NULL;
hr = XuiCreateFont( wchFontname, pointSize, style, 0, &hFont );
if ( FAILED( hr ) )
{
return NULL;
}
return hFont;
}
#endif
//-----------------------------------------------------------------------------
// Release TTF
//-----------------------------------------------------------------------------
#if defined( _X360 )
void CShaderAPIDx8::CloseTrueTypeFont( HXUIFONT hFont )
{
if ( !hFont )
return;
LOCK_SHADERAPI();
XuiReleaseFont( hFont );
}
#endif
//-----------------------------------------------------------------------------
// Get the TTF Metrics
//-----------------------------------------------------------------------------
#if defined( _X360 )
bool CShaderAPIDx8::GetTrueTypeFontMetrics( HXUIFONT hFont, XUIFontMetrics *pFontMetrics, XUICharMetrics charMetrics[256] )
{
if ( !hFont )
return false;
LOCK_SHADERAPI();
V_memset( charMetrics, 0, 256 * sizeof( XUICharMetrics ) );
HRESULT hr = XuiGetFontMetrics( hFont, pFontMetrics );
if ( !FAILED( hr ) )
{
// X360 issue: max character width may be too small.
// Run through each character and fixup
for ( int i = 1; i < 256; i++ )
{
wchar_t wch = i;
hr = XuiGetCharMetrics( hFont, wch, &charMetrics[i] );
if ( !FAILED( hr ) )
{
float maxWidth = charMetrics[i].fMaxX;
if ( charMetrics[i].fMinX < 0 )
{
maxWidth = charMetrics[i].fMaxX - charMetrics[i].fMinX;
}
if ( maxWidth > pFontMetrics->fMaxWidth )
{
pFontMetrics->fMaxWidth = maxWidth;
}
if ( charMetrics[i].fAdvance > pFontMetrics->fMaxWidth )
{
pFontMetrics->fMaxWidth = charMetrics[i].fAdvance;
}
}
}
// fonts are getting cut off, MaxHeight seems to be misreported smaller
// take MaxHeight to be the larger of its reported value or (ascent + descent)
float maxHeight = 0;
if ( pFontMetrics->fMaxDescent <= 0 )
{
// descent is negative for below baseline
maxHeight = pFontMetrics->fMaxAscent - pFontMetrics->fMaxDescent;
}
if ( maxHeight > pFontMetrics->fMaxHeight )
{
pFontMetrics->fMaxHeight = maxHeight;
}
}
return ( !FAILED( hr ) );
}
#endif
//-----------------------------------------------------------------------------
// Gets the glyph bits in rgba order. This function PURPOSELY hijacks D3D
// because XUI is involved. It is called at a very specific place in the VGUI
// render frame where its deleterious affects are going to be harmless.
//-----------------------------------------------------------------------------
#if defined( _X360 )
bool CShaderAPIDx8::GetTrueTypeGlyphs( HXUIFONT hFont, int numChars, wchar_t *pWch, int *pOffsetX, int *pOffsetY, int *pWidth, int *pHeight, unsigned char *pRGBA, int *pRGBAOffset )
{
if ( !hFont )
return false;
// Ensure this doesn't talk to D3D at the same time as the loading bar
AUTO_LOCK_FM( m_nonInteractiveModeMutex );
LOCK_SHADERAPI();
bool bSuccess = false;
IDirect3DSurface *pRTSurface = NULL;
IDirect3DSurface *pSavedSurface = NULL;
IDirect3DSurface *pSavedDepthSurface = NULL;
IDirect3DTexture *pTexture = NULL;
D3DVIEWPORT9 savedViewport;
D3DXMATRIX matView;
D3DXMATRIX matXForm;
D3DLOCKED_RECT lockedRect;
// have to reset to default state to rasterize glyph correctly
// state will get re-established during next mesh draw
ResetRenderState( false );
Dx9Device()->SetRenderState( D3DRS_ZENABLE, FALSE );
Dx9Device()->GetRenderTarget( 0, &pSavedSurface );
Dx9Device()->GetDepthStencilSurface( &pSavedDepthSurface );
Dx9Device()->GetViewport( &savedViewport );
// Figure out the size of surface/texture we need to allocate
int rtWidth = 0;
int rtHeight = 0;
for ( int i = 0; i < numChars; i++ )
{
rtWidth += pWidth[i];
rtHeight = max( rtHeight, pHeight[i] );
}
// per resolve() restrictions
rtWidth = AlignValue( rtWidth, 32 );
rtHeight = AlignValue( rtHeight, 32 );
// create a render target to capture the glyph render
pRTSurface = g_TextureHeap.AllocRenderTargetSurface( rtWidth, rtHeight, D3DFMT_A8R8G8B8 );
if ( !pRTSurface )
goto cleanUp;
Dx9Device()->SetRenderTarget( 0, pRTSurface );
// Disable depth here otherwise you get a colour/depth multisample mismatch error (in 480p)
Dx9Device()->SetDepthStencilSurface( NULL );
Dx9Device()->Clear( 0, NULL, D3DCLEAR_TARGET, 0x00000000, ( ReverseDepthOnX360() ? 0.0 : 1.0f ), 0 );
// create texture to get glyph render from EDRAM
HRESULT hr = Dx9Device()->CreateTexture( rtWidth, rtHeight, 1, 0, D3DFMT_A8R8G8B8, 0, &pTexture, NULL );
if ( FAILED( hr ) )
goto cleanUp;
bool bPreviousOwnState = OwnGPUResources( false );
XuiRenderBegin( m_hDC, 0x00000000 );
D3DXMatrixIdentity( &matView );
XuiRenderSetViewTransform( m_hDC, &matView );
XuiRenderSetTransform( m_hDC, &matView );
// rasterize the glyph
XuiSelectFont( m_hDC, hFont );
XuiSetColorFactor( m_hDC, 0xFFFFFFFF );
// Draw the characters, stepping across the texture
int xCursor = 0;
for ( int i = 0; i < numChars; i++)
{
// FIXME: the drawRect params don't make much sense (should use "(xCursor+pWidth[i]), pHeight[i]", but then some characters disappear!)
XUIRect drawRect = XUIRect( xCursor + pOffsetX[i], pOffsetY[i], rtWidth, rtHeight );
wchar_t text[2] = { pWch[i], 0 };
XuiDrawText( m_hDC, text, XUI_FONT_STYLE_NORMAL|XUI_FONT_STYLE_SINGLE_LINE|XUI_FONT_STYLE_NO_WORDWRAP, 0, &drawRect );
xCursor += pWidth[i];
}
XuiRenderEnd( m_hDC );
OwnGPUResources( bPreviousOwnState );
// transfer from edram to system
hr = Dx9Device()->Resolve( 0, NULL, pTexture, NULL, 0, 0, NULL, 0, 0, NULL );
if ( FAILED( hr ) )
goto cleanUp;
hr = pTexture->LockRect( 0, &lockedRect, NULL, 0 );
if ( FAILED( hr ) )
goto cleanUp;
// transfer to linear format, one character at a time
xCursor = 0;
for ( int i = 0;i < numChars; i++ )
{
int destPitch = pWidth[i]*4;
unsigned char *pLinear = pRGBA + pRGBAOffset[i];
RECT copyRect = { xCursor, 0, xCursor + pWidth[i], pHeight[i] };
xCursor += pWidth[i];
XGUntileSurface( pLinear, destPitch, NULL, lockedRect.pBits, rtWidth, rtHeight, &copyRect, 4 );
// convert argb to rgba
float r, g, b, a;
for ( int y = 0; y < pHeight[i]; y++ )
{
unsigned char *pSrc = (unsigned char*)pLinear + y*destPitch;
for ( int x = 0; x < pWidth[i]; x++ )
{
// undo pre-multiplied alpha since glyph bits will be sourced as a rgba texture
if ( !pSrc[0] )
a = 1;
else
a = (float)pSrc[0] * 1.0f/255.0f;
r = ((float)pSrc[1] * 1.0f/255.0f)/a * 255.0f;
if ( r > 255 )
r = 255;
g = ((float)pSrc[2] * 1.0f/255.0f)/a * 255.0f;
if ( g > 255 )
g = 255;
b = ((float)pSrc[3] * 1.0f/255.0f)/a * 255.0f;
if ( b > 255 )
b = 255;
pSrc[3] = pSrc[0];
pSrc[2] = b;
pSrc[1] = g;
pSrc[0] = r;
pSrc += 4;
}
}
}
pTexture->UnlockRect( 0 );
bSuccess = true;
cleanUp:
if ( pRTSurface )
{
Dx9Device()->SetRenderTarget( 0, pSavedSurface );
Dx9Device()->SetDepthStencilSurface( pSavedDepthSurface );
Dx9Device()->SetViewport( &savedViewport );
pRTSurface->Release();
}
if ( pTexture )
pTexture->Release();
if ( pSavedSurface )
pSavedSurface->Release();
// XUI changed renderstates behind our back, so we need to reset to defaults again to get back in synch:
ResetRenderState( false );
return bSuccess;
}
#endif
//-----------------------------------------------------------------------------
// Create a 360 Render Target Surface
//-----------------------------------------------------------------------------
#if defined( _X360 )
ShaderAPITextureHandle_t CShaderAPIDx8::CreateRenderTargetSurface( int width, int height, ImageFormat format, const char *pDebugName, const char *pTextureGroupName )
{
LOCK_SHADERAPI();
ShaderAPITextureHandle_t textureHandle = CreateTextureHandle();
Texture_t *pTexture = &GetTexture( textureHandle );
pTexture->m_Flags = (Texture_t::IS_ALLOCATED | Texture_t::IS_RENDER_TARGET_SURFACE);
pTexture->m_DebugName = pDebugName;
pTexture->m_Width = width;
pTexture->m_Height = height;
pTexture->m_Depth = 1;
pTexture->m_NumCopies = 1;
pTexture->m_CurrentCopy = 0;
ImageFormat dstImageFormat = FindNearestSupportedFormat( format, false, true, false );
D3DFORMAT actualFormat = ImageLoader::ImageFormatToD3DFormat( dstImageFormat );
pTexture->GetRenderTargetSurface( false ) = g_TextureHeap.AllocRenderTargetSurface( width, height, actualFormat );
pTexture->GetRenderTargetSurface( true ) = g_TextureHeap.AllocRenderTargetSurface( width, height, (D3DFORMAT)MAKESRGBFMT( actualFormat ) );
pTexture->SetImageFormat( dstImageFormat );
pTexture->m_UTexWrap = D3DTADDRESS_CLAMP;
pTexture->m_VTexWrap = D3DTADDRESS_CLAMP;
pTexture->m_WTexWrap = D3DTADDRESS_CLAMP;
pTexture->m_MagFilter = D3DTEXF_LINEAR;
pTexture->m_NumLevels = 1;
pTexture->m_MipFilter = D3DTEXF_NONE;
pTexture->m_MinFilter = D3DTEXF_LINEAR;
pTexture->m_SwitchNeeded = false;
ComputeStatsInfo( textureHandle, false, false );
SetupTextureGroup( textureHandle, pTextureGroupName );
return textureHandle;
}
#endif
//-----------------------------------------------------------------------------
// Shader constants are batched and written to gpu once prior to draw.
//-----------------------------------------------------------------------------
void CShaderAPIDx8::WriteShaderConstantsToGPU()
{
#if defined( _X360 )
// vector vertex constants can just blast their set range
if ( m_MaxVectorVertexShaderConstant )
{
if ( m_bGPUOwned )
{
// faster path, write directly into GPU command buffer, bypassing shadow state
// can only set what is actually owned
Assert( m_MaxVectorVertexShaderConstant <= VERTEX_SHADER_MODEL + 3*NUM_MODEL_TRANSFORMS );
int numVectors = AlignValue( m_MaxVectorVertexShaderConstant, 4 );
BYTE* pCommandBufferData;
Dx9Device()->GpuBeginVertexShaderConstantF4( 0, (D3DVECTOR4**)&pCommandBufferData, numVectors );
memcpy( pCommandBufferData, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), numVectors * (sizeof( float ) * 4) );
Dx9Device()->GpuEndVertexShaderConstantF4();
}
else
{
Dx9Device()->SetVertexShaderConstantF( 0, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), m_MaxVectorVertexShaderConstant );
}
memcpy( m_DynamicState.m_pVectorVertexShaderConstant[0].Base(), m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), m_MaxVectorVertexShaderConstant * 4 * sizeof(float) );
m_MaxVectorVertexShaderConstant = 0;
}
if ( m_MaxVectorPixelShaderConstant )
{
if ( m_bGPUOwned )
{
// faster path, write directly into GPU command buffer, bypassing shadow state
// can only set what is actually owned
Assert( m_MaxVectorPixelShaderConstant <= 32 );
int numVectors = AlignValue( m_MaxVectorPixelShaderConstant, 4 );
BYTE* pCommandBufferData;
Dx9Device()->GpuBeginPixelShaderConstantF4( 0, (D3DVECTOR4**)&pCommandBufferData, numVectors );
memcpy( pCommandBufferData, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), numVectors * (sizeof( float ) * 4) );
Dx9Device()->GpuEndPixelShaderConstantF4();
}
else
{
Dx9Device()->SetPixelShaderConstantF( 0, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), m_MaxVectorPixelShaderConstant );
}
memcpy( m_DynamicState.m_pVectorPixelShaderConstant[0].Base(), m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), m_MaxVectorPixelShaderConstant * 4 * sizeof(float) );
m_MaxVectorPixelShaderConstant = 0;
}
// boolean and integer constants can just blast their set range
// these are currently extremely small in number, if this changes they may benefit from a fast path pattern
if ( m_MaxBooleanVertexShaderConstant )
{
Dx9Device()->SetVertexShaderConstantB( 0, m_DesiredState.m_pBooleanVertexShaderConstant, m_MaxBooleanVertexShaderConstant );
memcpy( m_DynamicState.m_pBooleanVertexShaderConstant, m_DesiredState.m_pBooleanVertexShaderConstant, m_MaxBooleanVertexShaderConstant * sizeof(BOOL) );
m_MaxBooleanVertexShaderConstant = 0;
}
if ( m_MaxIntegerVertexShaderConstant )
{
Dx9Device()->SetVertexShaderConstantI( 0, (int *)m_DesiredState.m_pIntegerVertexShaderConstant, m_MaxIntegerVertexShaderConstant );
memcpy( m_DynamicState.m_pIntegerVertexShaderConstant[0].Base(), m_DesiredState.m_pIntegerVertexShaderConstant[0].Base(), m_MaxIntegerVertexShaderConstant * sizeof(IntVector4D) );
m_MaxIntegerVertexShaderConstant = 0;
}
if ( m_MaxBooleanPixelShaderConstant )
{
Dx9Device()->SetPixelShaderConstantB( 0, m_DesiredState.m_pBooleanPixelShaderConstant, m_MaxBooleanPixelShaderConstant );
memcpy( m_DynamicState.m_pBooleanPixelShaderConstant, m_DesiredState.m_pBooleanPixelShaderConstant, m_MaxBooleanPixelShaderConstant * sizeof(BOOL) );
m_MaxBooleanPixelShaderConstant = 0;
}
// integer pixel constants are not used, so not supporting
#if 0
if ( m_MaxIntegerPixelShaderConstant )
{
Dx9Device()->SetPixelShaderConstantI( 0, (int *)m_DesiredState.m_pIntegerPixelShaderConstant, m_MaxIntegerPixelShaderConstant );
memcpy( m_DynamicState.m_pIntegerPixelShaderConstant[0].Base(), m_DesiredState.m_pIntegerPixelShaderConstant[0].Base(), m_MaxIntegerPixelShaderConstant * sizeof(IntVector4D) );
m_MaxIntegerPixelShaderConstant = 0;
}
#endif
#endif
}
//-----------------------------------------------------------------------------
// The application is about to perform a hard reboot, but wants to hide the screen flash
// by persisting the front buffer across a reboot boundary. The persisted frame buffer
// can be detected and restored.
//-----------------------------------------------------------------------------
#if defined( _X360 )
void CShaderAPIDx8::PersistDisplay()
{
if ( m_PresentParameters.FrontBufferFormat != D3DFMT_LE_X8R8G8B8 )
{
// The format must be what PersistDisplay() expects, otherwise D3DRIP.
// If this hits due to sRGB bit set that confuses PersistDisplay(),
// the fix may be to slam the presentation parameters to the expected format,
// do a ResetDevice(), and then PersistDisplay().
Assert( 0 );
return;
}
IDirect3DTexture *pTexture;
HRESULT hr = Dx9Device()->GetFrontBuffer( &pTexture );
if ( !FAILED( hr ) )
{
OwnGPUResources( false );
Dx9Device()->PersistDisplay( pTexture, NULL );
pTexture->Release();
}
}
#endif
#if defined( _X360 )
bool CShaderAPIDx8::PostQueuedTexture( const void *pData, int nDataSize, ShaderAPITextureHandle_t *pHandles, int numHandles, int nWidth, int nHeight, int nDepth, int numMips, int *pRefCount )
{
CUtlBuffer vtfBuffer;
IVTFTexture *pVTFTexture = NULL;
bool bOK = false;
if ( !pData || !nDataSize )
{
// invalid
goto cleanUp;
}
// get a unique vtf and mount texture
// vtf can expect non-volatile buffer data to be stable through vtf lifetime
// this prevents redundant copious amounts of image memory transfers
pVTFTexture = CreateVTFTexture();
vtfBuffer.SetExternalBuffer( (void *)pData, nDataSize, nDataSize );
if ( !pVTFTexture->UnserializeFromBuffer( vtfBuffer, false, false, false, 0 ) )
{
goto cleanUp;
}
// provided vtf buffer is all mips, determine top mip due to possible picmip
int iTopMip = 0;
int mipWidth, mipHeight, mipDepth;
do
{
pVTFTexture->ComputeMipLevelDimensions( iTopMip, &mipWidth, &mipHeight, &mipDepth );
if ( nWidth == mipWidth && nHeight == mipHeight && nDepth == mipDepth )
{
break;
}
iTopMip++;
}
while ( mipWidth != 1 || mipHeight != 1 || mipDepth != 1 );
// create and blit
for ( int iFrame = 0; iFrame < numHandles; iFrame++ )
{
ShaderAPITextureHandle_t hTexture = pHandles[iFrame];
Texture_t *pTexture = &GetTexture( hTexture );
int nFaceCount = ( pTexture->m_CreationFlags & TEXTURE_CREATE_CUBEMAP ) ? CUBEMAP_FACE_COUNT-1 : 1;
IDirect3DBaseTexture *pD3DTexture;
if ( pTexture->m_CreationFlags & TEXTURE_CREATE_NOD3DMEMORY )
{
pD3DTexture = pTexture->GetTexture();
if ( !g_TextureHeap.AllocD3DMemory( pD3DTexture ) )
{
goto cleanUp;
}
}
else
{
pD3DTexture = pTexture->GetTexture();
}
// blit the hi-res texture bits into d3d memory
for ( int iFace = 0; iFace < nFaceCount; ++iFace )
{
for ( int iMip = 0; iMip < numMips; ++iMip )
{
pVTFTexture->ComputeMipLevelDimensions( iTopMip + iMip, &mipWidth, &mipHeight, &mipDepth );
unsigned char *pSourceBits = pVTFTexture->ImageData( iFrame, iFace, iTopMip + iMip, 0, 0, 0 );
TextureLoadInfo_t info;
info.m_TextureHandle = hTexture;
info.m_pTexture = pD3DTexture;
info.m_nLevel = iMip;
info.m_nCopy = 0;
info.m_CubeFaceID = (D3DCUBEMAP_FACES)iFace;
info.m_nWidth = mipWidth;
info.m_nHeight = mipHeight;
info.m_nZOffset = 0;
info.m_SrcFormat = pVTFTexture->Format();
info.m_pSrcData = pSourceBits;
info.m_bSrcIsTiled = pVTFTexture->IsPreTiled();
info.m_bCanConvertFormat = ( pTexture->m_Flags & Texture_t::CAN_CONVERT_FORMAT ) != 0;
LoadTexture( info );
}
}
pTexture->m_Flags |= Texture_t::IS_FINALIZED;
(*pRefCount)--;
}
// success
bOK = true;
cleanUp:
if ( pVTFTexture )
{
DestroyVTFTexture( pVTFTexture );
}
if ( !bOK )
{
// undo artificial lock
(*pRefCount) -= numHandles;
}
return bOK;
}
#endif
#if defined( _X360 )
void *CShaderAPIDx8::GetD3DDevice()
{
return Dx9Device();
}
#endif
#if defined( _X360 )
static void r_enable_gpr_allocations_callback( IConVar *var, const char *pOldValue, float flOldValue )
{
if ( ((ConVar *)var)->GetBool() == false )
{
//reset back the default 64/64 allocation before we stop updating
if( Dx9Device() != NULL )
{
Dx9Device()->SetShaderGPRAllocation( 0, 0, 0 );
}
}
}
ConVar r_enable_gpr_allocations( "r_enable_gpr_allocations", "1", 0, "Enable usage of IDirect3DDevice9::SetShaderGPRAllocation()", r_enable_gpr_allocations_callback );
static void CommitShaderGPRs( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, DynamicState_t &currentState, bool bForce )
{
if( desiredState.m_iVertexShaderGPRAllocation != currentState.m_iVertexShaderGPRAllocation )
{
pDevice->SetShaderGPRAllocation( 0, desiredState.m_iVertexShaderGPRAllocation, 128 - desiredState.m_iVertexShaderGPRAllocation );
currentState.m_iVertexShaderGPRAllocation = desiredState.m_iVertexShaderGPRAllocation;
}
}
void CShaderAPIDx8::PushVertexShaderGPRAllocation( int iVertexShaderCount )
{
Assert( (iVertexShaderCount >= 16) && (iVertexShaderCount <= 112) );
m_VertexShaderGPRAllocationStack.Push( iVertexShaderCount );
if ( r_enable_gpr_allocations.GetBool() )
{
if ( m_DynamicState.m_iVertexShaderGPRAllocation != iVertexShaderCount )
{
m_DesiredState.m_iVertexShaderGPRAllocation = iVertexShaderCount;
ADD_COMMIT_FUNC( COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitShaderGPRs );
}
}
}
void CShaderAPIDx8::PopVertexShaderGPRAllocation( void )
{
m_VertexShaderGPRAllocationStack.Pop();
if ( r_enable_gpr_allocations.GetBool() )
{
int iVertexShaderCount;
if ( m_VertexShaderGPRAllocationStack.Count() )
iVertexShaderCount = m_VertexShaderGPRAllocationStack.Top();
else
iVertexShaderCount = 64;
if ( m_DynamicState.m_iVertexShaderGPRAllocation != iVertexShaderCount )
{
m_DesiredState.m_iVertexShaderGPRAllocation = iVertexShaderCount;
ADD_COMMIT_FUNC( COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitShaderGPRs );
}
}
}
void CShaderAPIDx8::EnableVSync_360( bool bEnable )
{
if( bEnable )
{
Dx9Device()->SetRenderState( D3DRS_PRESENTIMMEDIATETHRESHOLD, 0 ); //only swap on vertical blanks
}
else
{
Dx9Device()->SetRenderState( D3DRS_PRESENTIMMEDIATETHRESHOLD, 100 ); //allow a swap at any point in the DAC scan
}
}
#endif
// ------------ New Vertex/Index Buffer interface ----------------------------
void CShaderAPIDx8::BindVertexBuffer( int streamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions )
{
LOCK_SHADERAPI();
MeshMgr()->BindVertexBuffer( streamID, pVertexBuffer, nOffsetInBytes, nFirstVertex, nVertexCount, fmt, nRepetitions );
}
void CShaderAPIDx8::BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes )
{
LOCK_SHADERAPI();
MeshMgr()->BindIndexBuffer( pIndexBuffer, nOffsetInBytes );
}
void CShaderAPIDx8::Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount )
{
LOCK_SHADERAPI();
MeshMgr()->Draw( primitiveType, nFirstIndex, nIndexCount );
}
// ------------ End ----------------------------
float CShaderAPIDx8::GammaToLinear_HardwareSpecific( float fGamma ) const
{
if( IsPC() )
{
return SrgbGammaToLinear( fGamma );
}
else if( IsX360() )
{
return SrgbGammaToLinear( fGamma );
}
else
{
// Unknown console
return pow( fGamma, 2.2f ); // Use a gamma 2.2 curve
}
}
float CShaderAPIDx8::LinearToGamma_HardwareSpecific( float fLinear ) const
{
if ( IsPC() )
{
return SrgbLinearToGamma( fLinear );
}
else if ( IsX360() )
{
return SrgbLinearToGamma( fLinear );
}
else
{
// Unknown console
return pow( fLinear, ( 1.0f / 2.2f ) ); // Use a gamma 2.2 curve
}
}
bool CShaderAPIDx8::ShouldWriteDepthToDestAlpha( void ) const
{
return IsPC() && g_pHardwareConfig->SupportsPixelShaders_2_b() &&
(m_SceneFogMode != MATERIAL_FOG_LINEAR_BELOW_FOG_Z) &&
(GetIntRenderingParameter(INT_RENDERPARM_WRITE_DEPTH_TO_DESTALPHA) != 0);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CShaderAPIDx8::AcquireThreadOwnership()
{
SetCurrentThreadAsOwner();
#if (defined( _X360 ) || defined( DX_TO_GL_ABSTRACTION ))
Dx9Device()->AcquireThreadOwnership();
#endif
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ReleaseThreadOwnership()
{
RemoveThreadOwner();
#if (defined( _X360 ) || defined( DX_TO_GL_ABSTRACTION ))
Dx9Device()->ReleaseThreadOwnership();
#endif
}
//-----------------------------------------------------------------------------
// Actual low level setting of the color RT. All Xbox RT funnels here
// to track the actual RT state. Returns true if the RT gets set, otherwise false.
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::SetRenderTargetInternalXbox( ShaderAPITextureHandle_t hRenderTargetTexture, bool bForce )
{
// valid for 360 only
if ( IsPC() )
{
Assert( 0 );
return false;
}
if ( hRenderTargetTexture == INVALID_SHADERAPI_TEXTURE_HANDLE )
{
// could be a reset, force to back buffer
hRenderTargetTexture = SHADER_RENDERTARGET_BACKBUFFER;
}
if ( m_hCachedRenderTarget == INVALID_SHADERAPI_TEXTURE_HANDLE )
{
// let the set go through to establish the initial state
bForce = true;
}
if ( !bForce && ( hRenderTargetTexture == m_hCachedRenderTarget && m_DynamicState.m_bSRGBWritesEnabled == m_bUsingSRGBRenderTarget ) )
{
// current RT matches expected state, leave state intact
return false;
}
// track the updated state
m_bUsingSRGBRenderTarget = m_DynamicState.m_bSRGBWritesEnabled;
m_hCachedRenderTarget = hRenderTargetTexture;
IDirect3DSurface *pSurface;
if ( m_hCachedRenderTarget == SHADER_RENDERTARGET_BACKBUFFER )
{
if ( !m_bUsingSRGBRenderTarget )
{
pSurface = m_pBackBufferSurface;
}
else
{
pSurface = m_pBackBufferSurfaceSRGB;
}
}
else
{
AssertValidTextureHandle( m_hCachedRenderTarget );
Texture_t *pTexture = &GetTexture( m_hCachedRenderTarget );
pSurface = pTexture->GetRenderTargetSurface( m_bUsingSRGBRenderTarget );
}
// the 360 does a wierd reset of some states on a SetRenderTarget()
// the viewport is a clobbered state, it may not be changed by later callers, so it MUST be put back as expected
// the other clobbered states are waiting to be discovered ... sigh
#if defined( _X360 )
D3DVIEWPORT9 viewport;
Dx9Device()->GetViewport( &viewport );
Dx9Device()->SetRenderTarget( 0, pSurface );
Dx9Device()->SetViewport( &viewport );
#endif
return true;
}
//-----------------------------------------------------------------------------
// debug logging
//-----------------------------------------------------------------------------
void CShaderAPIDx8::PrintfVA( char *fmt, va_list vargs )
{
#ifdef DX_TO_GL_ABSTRACTION
#if GLMDEBUG
GLMPrintfVA( fmt, vargs );
#endif
#else
AssertOnce( !"Impl me" );
#endif
}
void CShaderAPIDx8::Printf( const char *fmt, ... )
{
#ifdef DX_TO_GL_ABSTRACTION
#if GLMDEBUG
va_list vargs;
va_start(vargs, fmt);
GLMPrintfVA( fmt, vargs );
va_end( vargs );
#endif
#else
AssertOnce( !"Impl me" );
#endif
}
float CShaderAPIDx8::Knob( char *knobname, float *setvalue )
{
#ifdef DX_TO_GL_ABSTRACTION
#if GLMDEBUG
return GLMKnob( knobname, setvalue );
#else
return 0.0f;
#endif
#else
return 0.0f;
#endif
}
#if defined( _X360 )
extern ConVar r_blocking_spew_threshold;
void D3DBlockingSpewCallback( DWORD Flags, D3DBLOCKTYPE BlockType, float ClockTime, DWORD ThreadTime )
{
if( ClockTime >= r_blocking_spew_threshold.GetFloat() )
{
const char *pBlockType = "";
switch( BlockType )
{
case D3DBLOCKTYPE_NONE:
pBlockType = "D3DBLOCKTYPE_NONE";
break;
case D3DBLOCKTYPE_PRIMARY_OVERRUN:
pBlockType = "D3DBLOCKTYPE_PRIMARY_OVERRUN";
break;
case D3DBLOCKTYPE_SECONDARY_OVERRUN:
pBlockType = "D3DBLOCKTYPE_SECONDARY_OVERRUN";
break;
case D3DBLOCKTYPE_SWAP_THROTTLE:
pBlockType = "D3DBLOCKTYPE_SWAP_THROTTLE";
break;
case D3DBLOCKTYPE_BLOCK_UNTIL_IDLE:
pBlockType = "D3DBLOCKTYPE_BLOCK_UNTIL_IDLE";
break;
case D3DBLOCKTYPE_BLOCK_UNTIL_NOT_BUSY:
pBlockType = "D3DBLOCKTYPE_BLOCK_UNTIL_NOT_BUSY";
break;
case D3DBLOCKTYPE_BLOCK_ON_FENCE:
pBlockType = "D3DBLOCKTYPE_BLOCK_ON_FENCE";
break;
case D3DBLOCKTYPE_VERTEX_SHADER_RELEASE:
pBlockType = "D3DBLOCKTYPE_VERTEX_SHADER_RELEASE";
break;
case D3DBLOCKTYPE_PIXEL_SHADER_RELEASE:
pBlockType = "D3DBLOCKTYPE_PIXEL_SHADER_RELEASE";
break;
case D3DBLOCKTYPE_VERTEX_BUFFER_RELEASE:
pBlockType = "D3DBLOCKTYPE_VERTEX_BUFFER_RELEASE";
break;
case D3DBLOCKTYPE_VERTEX_BUFFER_LOCK:
pBlockType = "D3DBLOCKTYPE_VERTEX_BUFFER_LOCK";
break;
case D3DBLOCKTYPE_INDEX_BUFFER_RELEASE:
pBlockType = "D3DBLOCKTYPE_INDEX_BUFFER_RELEASE";
break;
case D3DBLOCKTYPE_INDEX_BUFFER_LOCK:
pBlockType = "D3DBLOCKTYPE_INDEX_BUFFER_LOCK";
break;
case D3DBLOCKTYPE_TEXTURE_RELEASE:
pBlockType = "D3DBLOCKTYPE_TEXTURE_RELEASE";
break;
case D3DBLOCKTYPE_TEXTURE_LOCK:
pBlockType = "D3DBLOCKTYPE_TEXTURE_LOCK";
break;
case D3DBLOCKTYPE_COMMAND_BUFFER_RELEASE:
pBlockType = "D3DBLOCKTYPE_COMMAND_BUFFER_RELEASE";
break;
case D3DBLOCKTYPE_COMMAND_BUFFER_LOCK:
pBlockType = "D3DBLOCKTYPE_COMMAND_BUFFER_LOCK";
break;
case D3DBLOCKTYPE_CONSTANT_BUFFER_RELEASE:
pBlockType = "D3DBLOCKTYPE_CONSTANT_BUFFER_RELEASE";
break;
case D3DBLOCKTYPE_CONSTANT_BUFFER_LOCK:
pBlockType = "D3DBLOCKTYPE_CONSTANT_BUFFER_LOCK";
break;
NO_DEFAULT;
};
Warning( "D3D Block: %s for %.2f ms\n", pBlockType, ClockTime );
}
}
static void r_blocking_spew_threshold_callback( IConVar *var, const char *pOldValue, float flOldValue )
{
if( Dx9Device() != NULL )
{
if ( ((ConVar *)var)->GetFloat() >= 0.0f )
{
Dx9Device()->SetBlockCallback( 0, D3DBlockingSpewCallback );
}
else
{
Dx9Device()->SetBlockCallback( 0, NULL );
}
}
}
ConVar r_blocking_spew_threshold( "r_blocking_spew_threshold", "-1", 0, "Enable spew of Direct3D Blocks. Specify the minimum blocking time in milliseconds before spewing a warning.", r_blocking_spew_threshold_callback );
#endif