Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

14352 lines
444 KiB

//========= 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 = D3DSHADE_NONE;
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( numVecs > 0 );
Assert( pVec );
if ( IsPC() || IsPS3() )
{
Assert( var + numVecs <= g_pHardwareConfig->NumVertexShaderConstants() );
if ( !bForce && memcmp( pVec, &m_DynamicState.m_pVectorVertexShaderConstant[var], numVecs * 4 * sizeof( float ) ) == 0 )
return;
Dx9Device()->SetVertexShaderConstantF( var, pVec, numVecs );
memcpy( &m_DynamicState.m_pVectorVertexShaderConstant[var], pVec, numVecs * 4 * sizeof(float) );
}
else
{
Assert( var + numVecs <= g_pHardwareConfig->NumVertexShaderConstants() );
}
if ( IsX360() && var + numVecs > m_MaxVectorVertexShaderConstant )
m_MaxVectorVertexShaderConstant = var + numVecs;
memcpy( &m_DesiredState.m_pVectorVertexShaderConstant[var], pVec, numVecs * 4 * sizeof(float) );
}
//-----------------------------------------------------------------------------
// 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() || IsPS3() )
{
if ( !bForce )
{
DWORD* pSrc = (DWORD*)pValues;
DWORD* pDst = (DWORD*)&m_DesiredState.m_pVectorPixelShaderConstant[nStartConst];
while( nNumConsts && ( pSrc[0] == pDst[0] ) && ( pSrc[1] == pDst[1] ) && ( pSrc[2] == pDst[2] ) && ( pSrc[3] == pDst[3] ) )
{
pSrc += 4;
pDst += 4;
nNumConsts--;
nStartConst++;
}
if ( !nNumConsts )
return;
pValues = reinterpret_cast< float const * >( pSrc );
}
Dx9Device()->SetPixelShaderConstantF( nStartConst, pValues, nNumConsts );
memcpy( &m_DynamicState.m_pVectorPixelShaderConstant[nStartConst], pValues, nNumConsts * 4 * sizeof(float) );
}
if ( IsX360() && nStartConst + nNumConsts > m_MaxVectorPixelShaderConstant )
{
m_MaxVectorPixelShaderConstant = nStartConst + nNumConsts;
Assert( m_MaxVectorPixelShaderConstant <= 32 );
if ( m_MaxVectorPixelShaderConstant > 32 )
{
// NOTE! There really are 224 pixel shader constants on the 360, but we do an optimization that only blasts the first 32 always.
Error( "Don't use more then the first 32 pixel shader constants on the 360!" );
}
}
memcpy( &m_DesiredState.m_pVectorPixelShaderConstant[nStartConst], pValues, nNumConsts * 4 * sizeof(float) );
}
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