diff options
Diffstat (limited to 'materialsystem/shaderapidx9/shaderapidx8.cpp')
| -rw-r--r-- | materialsystem/shaderapidx9/shaderapidx8.cpp | 14354 |
1 files changed, 14354 insertions, 0 deletions
diff --git a/materialsystem/shaderapidx9/shaderapidx8.cpp b/materialsystem/shaderapidx9/shaderapidx8.cpp new file mode 100644 index 0000000..6aa025c --- /dev/null +++ b/materialsystem/shaderapidx9/shaderapidx8.cpp @@ -0,0 +1,14354 @@ +//========= 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 ¤tState, bool bForce ); +static void CommitSetViewports( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, DynamicState_t ¤tState, 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 ¤tState, 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 ¤tState, bool bForce ) +{ + int nCount = g_pMaterialSystemHardwareConfig->GetVertexTextureCount(); + for ( int i = 0; i < nCount; ++i ) + { + VertexTextureState_t ¤tVTState = currentState.m_VertexTextureState[i]; + ShaderAPITextureHandle_t textureHandle = desiredState.m_VertexTextureState[i].m_BoundTexture; + + Texture_t *pTexture = ( textureHandle != INVALID_SHADERAPI_TEXTURE_HANDLE ) ? &g_ShaderAPIDX8.GetTexture( textureHandle ) : NULL; + if ( pTexture && ( pTexture->m_Flags & Texture_t::IS_VERTEX_TEXTURE ) == 0 ) + { + Warning( "Attempting to bind a vertex texture (%s) which was not created as a vertex texture!\n", pTexture->m_DebugName.String() ); + } + + if ( bForce || ( currentVTState.m_BoundTexture != textureHandle ) ) + { + currentVTState.m_BoundTexture = textureHandle; + +// RECORD_COMMAND( DX8_SET_VERTEXTEXTURE, 3 ); +// RECORD_INT( D3DVERTEXTEXTURESAMPLER0 + nStage ); +// RECORD_INT( pTexture ? pTexture->GetUniqueID() : 0xFFFF ); +// RECORD_INT( 0 ); + + IDirect3DBaseTexture *pD3DTexture = ( textureHandle >= 0 ) ? g_ShaderAPIDX8.GetD3DTexture( textureHandle ) : NULL; + + pDevice->SetTexture( D3DVERTEXTEXTURESAMPLER0 + i, pD3DTexture ); + } + + if ( !pTexture ) + continue; + + D3DTEXTUREADDRESS nNewUTexWrap = pTexture->m_UTexWrap; + if ( bForce || ( currentVTState.m_UTexWrap != nNewUTexWrap ) ) + { + currentVTState.m_UTexWrap = nNewUTexWrap; + SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSU, currentVTState.m_UTexWrap ); + } + + D3DTEXTUREADDRESS nNewVTexWrap = pTexture->m_VTexWrap; + if ( bForce || ( currentVTState.m_VTexWrap != nNewVTexWrap ) ) + { + currentVTState.m_VTexWrap = nNewVTexWrap; + SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSV, currentVTState.m_VTexWrap ); + } + + /* + D3DTEXTUREADDRESS nNewWTexWrap = pTexture->GetWTexWrap(); + if ( bForce || ( currentVTState.m_WTexWrap != nNewWTexWrap ) ) + { + currentVTState.m_WTexWrap = nNewWTexWrap; + SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSW, currentVTState.m_WTexWrap ); + } + */ + + D3DTEXTUREFILTERTYPE nNewMinFilter = pTexture->m_MinFilter; + if ( bForce || ( currentVTState.m_MinFilter != nNewMinFilter ) ) + { + currentVTState.m_MinFilter = nNewMinFilter; + SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MINFILTER, currentVTState.m_MinFilter ); + } + + D3DTEXTUREFILTERTYPE nNewMagFilter = pTexture->m_MagFilter; + if ( bForce || ( currentVTState.m_MagFilter != nNewMagFilter ) ) + { + currentVTState.m_MagFilter = nNewMagFilter; + SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MAGFILTER, currentVTState.m_MagFilter ); + } + + D3DTEXTUREFILTERTYPE nNewMipFilter = pTexture->m_MipFilter; + if ( bForce || ( currentVTState.m_MipFilter != nNewMipFilter ) ) + { + currentVTState.m_MipFilter = nNewMipFilter; + SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MIPFILTER, currentVTState.m_MipFilter ); + } + } +} + + +//----------------------------------------------------------------------------- +// Returns true if the state snapshot is translucent +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::IsTranslucent( StateSnapshot_t id ) const +{ + LOCK_SHADERAPI(); + return m_TransitionTable.GetSnapshot(id).m_AlphaBlendEnable; +} + + +//----------------------------------------------------------------------------- +// Returns true if the state snapshot is alpha tested +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::IsAlphaTested( StateSnapshot_t id ) const +{ + LOCK_SHADERAPI(); + return m_TransitionTable.GetSnapshot(id).m_AlphaTestEnable; +} + + +//----------------------------------------------------------------------------- +// Gets at the shadow state for a particular state snapshot +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::IsDepthWriteEnabled( StateSnapshot_t id ) const +{ + LOCK_SHADERAPI(); + return m_TransitionTable.GetSnapshot(id).m_ZWriteEnable; +} + + + +//----------------------------------------------------------------------------- +// Returns true if the state snapshot uses shaders +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::UsesVertexAndPixelShaders( StateSnapshot_t id ) const +{ + LOCK_SHADERAPI(); + return m_TransitionTable.GetSnapshotShader(id).m_VertexShader != INVALID_SHADER; +} + + +//----------------------------------------------------------------------------- +// Takes a snapshot +//----------------------------------------------------------------------------- +StateSnapshot_t CShaderAPIDx8::TakeSnapshot( ) +{ + LOCK_SHADERAPI(); + + return m_TransitionTable.TakeSnapshot(); +} + +void CShaderAPIDx8::ResetDXRenderState( void ) +{ + float zero = 0.0f; + float one = 1.0f; + DWORD dZero = *((DWORD*)(&zero)); + DWORD dOne = *((DWORD*)(&one)); + + // Set default values for all dx render states. + // NOTE: this does not include states encapsulated by the transition table, + // which are reset in CTransitionTable::UseDefaultState + SetSupportedRenderStateForce( D3DRS_FILLMODE, D3DFILL_SOLID ); + SetSupportedRenderStateForce( D3DRS_SHADEMODE, D3DSHADE_GOURAUD ); + SetSupportedRenderStateForce( D3DRS_LASTPIXEL, TRUE ); + SetSupportedRenderStateForce( D3DRS_CULLMODE, D3DCULL_CCW ); + SetSupportedRenderStateForce( D3DRS_DITHERENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_FOGENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_SPECULARENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_FOGCOLOR, 0 ); + SetSupportedRenderStateForce( D3DRS_FOGTABLEMODE, D3DFOG_NONE ); + SetSupportedRenderStateForce( D3DRS_FOGSTART, dZero ); + SetSupportedRenderStateForce( D3DRS_FOGEND, dOne ); + SetSupportedRenderStateForce( D3DRS_FOGDENSITY, dZero ); + SetSupportedRenderStateForce( D3DRS_RANGEFOGENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_STENCILENABLE, FALSE); + SetSupportedRenderStateForce( D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP ); + SetSupportedRenderStateForce( D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP ); + SetSupportedRenderStateForce( D3DRS_STENCILPASS, D3DSTENCILOP_KEEP ); + SetSupportedRenderStateForce( D3DRS_STENCILFUNC, D3DCMP_ALWAYS ); + SetSupportedRenderStateForce( D3DRS_STENCILREF, 0 ); + SetSupportedRenderStateForce( D3DRS_STENCILMASK, 0xFFFFFFFF ); + SetSupportedRenderStateForce( D3DRS_STENCILWRITEMASK, 0xFFFFFFFF ); + SetSupportedRenderStateForce( D3DRS_TEXTUREFACTOR, 0xFFFFFFFF ); + SetSupportedRenderStateForce( D3DRS_WRAP0, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP1, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP2, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP3, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP4, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP5, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP6, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP7, 0 ); + SetSupportedRenderStateForce( D3DRS_CLIPPING, TRUE ); + SetSupportedRenderStateForce( D3DRS_LIGHTING, TRUE ); + SetSupportedRenderStateForce( D3DRS_AMBIENT, 0 ); + SetSupportedRenderStateForce( D3DRS_FOGVERTEXMODE, D3DFOG_NONE); + SetSupportedRenderStateForce( D3DRS_COLORVERTEX, TRUE ); + SetSupportedRenderStateForce( D3DRS_LOCALVIEWER, TRUE ); + SetSupportedRenderStateForce( D3DRS_NORMALIZENORMALS, FALSE ); + SetSupportedRenderStateForce( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_COLOR2 ); + SetSupportedRenderStateForce( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL ); + SetSupportedRenderStateForce( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL ); + SetSupportedRenderStateForce( D3DRS_VERTEXBLEND, D3DVBF_DISABLE ); + SetSupportedRenderStateForce( D3DRS_CLIPPLANEENABLE, 0 ); + + // -disable_d3d9_hacks is for debugging. For example, the "CENT" driver hack thing causes the flashlight pass to appear much brighter on NVidia drivers. + if ( IsPC() && !IsOpenGL() && !CommandLine()->CheckParm( "-disable_d3d9_hacks" ) ) + { + if ( g_pHardwareConfig->Caps().m_bNeedsATICentroidHack && ( g_pHardwareConfig->Caps().m_VendorID == VENDORID_ATI ) ) + { + SetSupportedRenderStateForce( D3DRS_POINTSIZE, MAKEFOURCC( 'C', 'E', 'N', 'T' ) ); + } + + if( g_pHardwareConfig->Caps().m_bDisableShaderOptimizations ) + { + SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_Y, MAKEFOURCC( 'C', 'O', 'P', 'M' ) ); + } + } + SetSupportedRenderStateForce( D3DRS_POINTSIZE, dOne ); + SetSupportedRenderStateForce( D3DRS_POINTSIZE_MIN, dOne ); + SetSupportedRenderStateForce( D3DRS_POINTSPRITEENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_POINTSCALEENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_POINTSCALE_A, dOne ); + SetSupportedRenderStateForce( D3DRS_POINTSCALE_B, dZero ); + SetSupportedRenderStateForce( D3DRS_POINTSCALE_C, dZero ); + SetSupportedRenderStateForce( D3DRS_MULTISAMPLEANTIALIAS, TRUE ); + SetSupportedRenderStateForce( D3DRS_MULTISAMPLEMASK, 0xFFFFFFFF ); + SetSupportedRenderStateForce( D3DRS_PATCHEDGESTYLE, D3DPATCHEDGE_DISCRETE ); + SetSupportedRenderStateForce( D3DRS_DEBUGMONITORTOKEN, D3DDMT_ENABLE ); + float sixtyFour = 64.0f; + SetSupportedRenderStateForce( D3DRS_POINTSIZE_MAX, *((DWORD*)(&sixtyFour))); + SetSupportedRenderStateForce( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_TWEENFACTOR, dZero ); + SetSupportedRenderStateForce( D3DRS_POSITIONDEGREE, D3DDEGREE_CUBIC ); + SetSupportedRenderStateForce( D3DRS_NORMALDEGREE, D3DDEGREE_LINEAR ); + SetSupportedRenderStateForce( D3DRS_SCISSORTESTENABLE, FALSE); + SetSupportedRenderStateForce( D3DRS_SLOPESCALEDEPTHBIAS, dZero ); + SetSupportedRenderStateForce( D3DRS_ANTIALIASEDLINEENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_MINTESSELLATIONLEVEL, dOne ); + SetSupportedRenderStateForce( D3DRS_MAXTESSELLATIONLEVEL, dOne ); + SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_X, dZero ); + SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_Y, dZero ); + SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_Z, dOne ); + SetSupportedRenderStateForce( D3DRS_ADAPTIVETESS_W, dZero ); + SetSupportedRenderStateForce( D3DRS_ENABLEADAPTIVETESSELLATION, FALSE ); + SetSupportedRenderStateForce( D3DRS_TWOSIDEDSTENCILMODE, FALSE ); + SetSupportedRenderStateForce( D3DRS_CCW_STENCILFAIL, D3DSTENCILOP_KEEP ); + SetSupportedRenderStateForce( D3DRS_CCW_STENCILZFAIL, D3DSTENCILOP_KEEP ); + SetSupportedRenderStateForce( D3DRS_CCW_STENCILPASS, D3DSTENCILOP_KEEP ); + SetSupportedRenderStateForce( D3DRS_CCW_STENCILFUNC, D3DCMP_ALWAYS ); + SetSupportedRenderStateForce( D3DRS_COLORWRITEENABLE1, 0x0000000f ); + SetSupportedRenderStateForce( D3DRS_COLORWRITEENABLE2, 0x0000000f ); + SetSupportedRenderStateForce( D3DRS_COLORWRITEENABLE3, 0x0000000f ); + SetSupportedRenderStateForce( D3DRS_BLENDFACTOR, 0xffffffff ); + SetSupportedRenderStateForce( D3DRS_SRGBWRITEENABLE, 0); + SetSupportedRenderStateForce( D3DRS_DEPTHBIAS, dZero ); + SetSupportedRenderStateForce( D3DRS_WRAP8, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP9, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP10, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP11, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP12, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP13, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP14, 0 ); + SetSupportedRenderStateForce( D3DRS_WRAP15, 0 ); + SetSupportedRenderStateForce( D3DRS_BLENDOP, D3DBLENDOP_ADD ); + SetSupportedRenderStateForce( D3DRS_BLENDOPALPHA, D3DBLENDOP_ADD ); + +#if defined( _X360 ) + SetSupportedRenderStateForce( D3DRS_HIZENABLE, D3DHIZ_AUTOMATIC ); + SetSupportedRenderStateForce( D3DRS_HIZWRITEENABLE, D3DHIZ_AUTOMATIC ); + + SetSupportedRenderStateForce( D3DRS_HISTENCILENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_HISTENCILWRITEENABLE, FALSE ); + SetSupportedRenderStateForce( D3DRS_HISTENCILFUNC, D3DHSCMP_EQUAL ); + SetSupportedRenderStateForce( D3DRS_HISTENCILREF, 0 ); +#endif +} + +//----------------------------------------------------------------------------- +// Own GPU Resources. Return previous state. +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::OwnGPUResources( bool bEnable ) +{ +#if defined( _X360 ) + if ( m_bGPUOwned == bEnable ) + { + return m_bGPUOwned; + } + + if ( !bEnable ) + { + Dx9Device()->GpuDisownAll(); + } + else + { + // owned GPU constants can be set very fast, and must be in blocks of 4 + // there are 256, but the game only uses 217 (snapped to 220), leaving just enough room for shader literals + COMPILE_TIME_ASSERT( VERTEX_SHADER_MODEL + 3*NUM_MODEL_TRANSFORMS == 217 ); + Dx9Device()->GpuOwnVertexShaderConstantF( 0, AlignValue( VERTEX_SHADER_MODEL + 3*NUM_MODEL_TRANSFORMS, 4 ) ); + // there are 256, but the game only utilizes 32, leaving lots of room for shader literals + Dx9Device()->GpuOwnPixelShaderConstantF( 0, 32 ); + } + + bool bPrevious = m_bGPUOwned; + m_bGPUOwned = bEnable; + + return bPrevious; +#else + return false; +#endif +} + +//----------------------------------------------------------------------------- +// Reset render state (to its initial value) +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ResetRenderState( bool bFullReset ) +{ + LOCK_SHADERAPI(); + RECORD_DEBUG_STRING( "BEGIN ResetRenderState" ); + + if ( !CanDownloadTextures() ) + { + QueueResetRenderState(); + return; + } + + m_bResettingRenderState = true; + + OwnGPUResources( true ); + + ResetDXRenderState(); + + // We're not currently rendering anything + m_nCurrentSnapshot = -1; + + D3DXMatrixIdentity( &m_CachedPolyOffsetProjectionMatrix ); + D3DXMatrixIdentity( &m_CachedFastClipProjectionMatrix ); + D3DXMatrixIdentity( &m_CachedFastClipPolyOffsetProjectionMatrix ); + m_UsingTextureRenderTarget = false; + + m_SceneFogColor[0] = 0; + m_SceneFogColor[1] = 0; + m_SceneFogColor[2] = 0; + m_SceneFogMode = MATERIAL_FOG_NONE; + + // This is state that isn't part of the snapshot per-se, because we + // don't need it when it's actually time to render. This just helps us + // to construct the shadow state. + m_DynamicState.m_ClearColor = D3DCOLOR_XRGB(0,0,0); + + if ( bFullReset ) + { + InitVertexAndPixelShaders(); + } + else + { + // just need to dirty the dynamic state, desired state gets copied into below + Q_memset( m_DynamicState.m_pVectorPixelShaderConstant, 0, g_pHardwareConfig->Caps().m_NumPixelShaderConstants * sizeof( Vector4D ) ); + Q_memset( m_DynamicState.m_pBooleanPixelShaderConstant, 0, g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants * sizeof( BOOL ) ); + Q_memset( m_DynamicState.m_pIntegerPixelShaderConstant, 0, g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants * sizeof( IntVector4D ) ); + + Q_memset( m_DynamicState.m_pVectorVertexShaderConstant, 0, g_pHardwareConfig->Caps().m_NumVertexShaderConstants * sizeof( Vector4D ) ); + Q_memset( m_DynamicState.m_pBooleanVertexShaderConstant, 0, g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants * sizeof( BOOL ) ); + Q_memset( m_DynamicState.m_pIntegerVertexShaderConstant, 0, g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants * sizeof( IntVector4D ) ); + + SetStandardVertexShaderConstants( OVERBRIGHT ); + } + + //Set the default compressed depth range written to dest alpha. Only need to compress it for 8bit alpha to get a useful gradient. + m_DynamicState.m_DestAlphaDepthRange = (g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT) ? 8192.0f : 192.0f; + + m_CachedAmbientLightCube = STATE_CHANGED; + + // Set the constant color + m_DynamicState.m_ConstantColor = 0xFFFFFFFF; + Color4ub( 255, 255, 255, 255 ); + + // Ambient light color + m_DynamicState.m_Ambient = 0; + SetSupportedRenderState( D3DRS_AMBIENT, m_DynamicState.m_Ambient ); + + // Fog + m_VertexShaderFogParams[0] = m_VertexShaderFogParams[1] = 0.0f; + m_WorldSpaceCameraPositon.Init( 0, 0, 0.01f, 0 ); // Don't let z be zero, as some pixel shaders will divide by this + m_DynamicState.m_FogColor = 0xFFFFFFFF; + m_DynamicState.m_PixelFogColor[0] = m_DynamicState.m_PixelFogColor[1] = + m_DynamicState.m_PixelFogColor[2] = m_DynamicState.m_PixelFogColor[3] = 0.0f; + m_DynamicState.m_bFogGammaCorrectionDisabled = false; + m_DynamicState.m_FogEnable = false; + m_DynamicState.m_SceneFog = MATERIAL_FOG_NONE; + m_DynamicState.m_FogMode = D3DFOG_NONE; + m_DynamicState.m_FogStart = -1; + m_DynamicState.m_FogEnd = -1; + m_DynamicState.m_FogMaxDensity = -1.0f; + m_DynamicState.m_FogZ = 0.0f; + + SetSupportedRenderState( D3DRS_FOGCOLOR, m_DynamicState.m_FogColor ); + SetSupportedRenderState( D3DRS_FOGENABLE, m_DynamicState.m_FogEnable ); + SetSupportedRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE ); + SetSupportedRenderState( D3DRS_FOGVERTEXMODE, D3DFOG_NONE ); + SetSupportedRenderState( D3DRS_RANGEFOGENABLE, false ); + + FogStart( 0 ); + FogEnd( 0 ); + FogMaxDensity( 1.0f ); + + m_DynamicState.m_bSRGBWritesEnabled = false; + + // Set the cull mode + m_DynamicState.m_bCullEnabled = true; + m_DynamicState.m_CullMode = D3DCULL_CCW; + m_DynamicState.m_DesiredCullMode = D3DCULL_CCW; + SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW ); + + // No shade mode yet + m_DynamicState.m_ShadeMode = (D3DSHADEMODE)-1; + ShadeMode( SHADER_SMOOTH ); + + m_DynamicState.m_bHWMorphingEnabled = false; + + // Skinning... + m_DynamicState.m_NumBones = 0; + m_DynamicState.m_VertexBlend = (D3DVERTEXBLENDFLAGS)-1; + SetSupportedRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE ); + SetSupportedRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE ); + + // No normal normalization + m_DynamicState.m_NormalizeNormals = false; + SetSupportedRenderState( D3DRS_NORMALIZENORMALS, m_DynamicState.m_NormalizeNormals ); + + bool bAntialiasing = ( m_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE ); + if ( g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT ) + { + bAntialiasing = false; + } + SetRenderState( D3DRS_MULTISAMPLEANTIALIAS, bAntialiasing ); + + // Anisotropic filtering is disabled by default + if ( bFullReset ) + { + SetAnisotropicLevel( 1 ); + } + + int i; + for ( i = 0; i < g_pHardwareConfig->ActualCaps().m_NumTextureStages; ++i ) + { + TextureStage(i).m_TextureTransformFlags = D3DTTFF_DISABLE; + TextureStage(i).m_BumpEnvMat00 = 1.0f; + TextureStage(i).m_BumpEnvMat01 = 0.0f; + TextureStage(i).m_BumpEnvMat10 = 0.0f; + TextureStage(i).m_BumpEnvMat11 = 1.0f; + + SetTextureStageState( i, D3DTSS_TEXTURETRANSFORMFLAGS, TextureStage(i).m_TextureTransformFlags ); + SetTextureStageState( i, D3DTSS_BUMPENVMAT00, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat00) ) ); + SetTextureStageState( i, D3DTSS_BUMPENVMAT01, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat01) ) ); + SetTextureStageState( i, D3DTSS_BUMPENVMAT10, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat10) ) ); + SetTextureStageState( i, D3DTSS_BUMPENVMAT11, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat11) ) ); + } + + for ( i = 0; i < g_pHardwareConfig->ActualCaps().m_NumSamplers; ++i ) + { + SamplerState(i).m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE; + SamplerState(i).m_UTexWrap = D3DTADDRESS_WRAP; + SamplerState(i).m_VTexWrap = D3DTADDRESS_WRAP; + SamplerState(i).m_WTexWrap = D3DTADDRESS_WRAP; + SamplerState(i).m_MagFilter = D3DTEXF_POINT; + SamplerState(i).m_MinFilter = D3DTEXF_POINT; + SamplerState(i).m_MipFilter = D3DTEXF_NONE; + SamplerState(i).m_FinestMipmapLevel = 0; + SamplerState(i).m_LodBias = 0.0f; + SamplerState(i).m_TextureEnable = false; + SamplerState(i).m_SRGBReadEnable = false; + + // Just some initial state... + Dx9Device()->SetTexture( i, 0 ); + + SetSamplerState( i, D3DSAMP_ADDRESSU, SamplerState(i).m_UTexWrap ); + SetSamplerState( i, D3DSAMP_ADDRESSV, SamplerState(i).m_VTexWrap ); + SetSamplerState( i, D3DSAMP_ADDRESSW, SamplerState(i).m_WTexWrap ); + SetSamplerState( i, D3DSAMP_MINFILTER, SamplerState(i).m_MinFilter ); + SetSamplerState( i, D3DSAMP_MAGFILTER, SamplerState(i).m_MagFilter ); + SetSamplerState( i, D3DSAMP_MIPFILTER, SamplerState(i).m_MipFilter ); + SetSamplerState( i, D3DSAMP_MAXMIPLEVEL, SamplerState(i).m_FinestMipmapLevel ); + SetSamplerState( i, D3DSAMP_MIPMAPLODBIAS, SamplerState(i).m_LodBias ); + + SetSamplerState( i, D3DSAMP_BORDERCOLOR, RGB( 0,0,0 ) ); + } + + // FIXME!!!!! : This barfs with the debug runtime on 6800. + for( i = 0; i < g_pHardwareConfig->ActualCaps().m_nVertexTextureCount; i++ ) + { + m_DynamicState.m_VertexTextureState[i].m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE; + Dx9Device()->SetTexture( D3DVERTEXTEXTURESAMPLER0 + i, NULL ); + + m_DynamicState.m_VertexTextureState[i].m_UTexWrap = D3DTADDRESS_CLAMP; + m_DynamicState.m_VertexTextureState[i].m_VTexWrap = D3DTADDRESS_CLAMP; +// m_DynamicState.m_VertexTextureState[i].m_WTexWrap = D3DTADDRESS_CLAMP; + m_DynamicState.m_VertexTextureState[i].m_MinFilter = D3DTEXF_POINT; + m_DynamicState.m_VertexTextureState[i].m_MagFilter = D3DTEXF_POINT; + m_DynamicState.m_VertexTextureState[i].m_MipFilter = D3DTEXF_POINT; + SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSU, m_DynamicState.m_VertexTextureState[i].m_UTexWrap ); + SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSV, m_DynamicState.m_VertexTextureState[i].m_VTexWrap ); +// SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSW, m_DynamicState.m_VertexTextureState[i].m_WTexWrap ); + SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MINFILTER, m_DynamicState.m_VertexTextureState[i].m_MinFilter ); + SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MAGFILTER, m_DynamicState.m_VertexTextureState[i].m_MagFilter ); + SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MIPFILTER, m_DynamicState.m_VertexTextureState[i].m_MipFilter ); + } + + m_DynamicState.m_NumLights = 0; + for ( i = 0; i < MAX_NUM_LIGHTS; ++i) + { + m_DynamicState.m_LightEnable[i] = false; + m_DynamicState.m_LightChanged[i] = STATE_CHANGED; + m_DynamicState.m_LightEnableChanged[i] = STATE_CHANGED; + } + + for ( i = 0; i < NUM_MATRIX_MODES; ++i) + { + // By setting this to *not* be identity, we force an update... + m_DynamicState.m_TransformType[i] = TRANSFORM_IS_GENERAL; + m_DynamicState.m_TransformChanged[i] = STATE_CHANGED; + } + + // set the board state to match the default state + m_TransitionTable.UseDefaultState(); + + // Set the default render state + SetDefaultState(); + + // Constant for all time + SetSupportedRenderState( D3DRS_CLIPPING, TRUE ); + SetSupportedRenderState( D3DRS_LOCALVIEWER, TRUE ); + SetSupportedRenderState( D3DRS_POINTSCALEENABLE, FALSE ); + SetSupportedRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL ); + SetSupportedRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_COLOR2 ); + SetSupportedRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL ); + SetSupportedRenderState( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL ); + SetSupportedRenderState( D3DRS_COLORVERTEX, TRUE ); // This defaults to true anyways. . . + + // Set a default identity material. + SetDefaultMaterial(); + +#if 0 + float fBias = -1.0f; + SetTextureStageState( 0, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) ); + SetTextureStageState( 1, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) ); + SetTextureStageState( 2, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) ); + SetTextureStageState( 3, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) ); +#endif + + if ( bFullReset ) + { + // Set the modelview matrix to identity too + for ( i = 0; i < NUM_MODEL_TRANSFORMS; ++i ) + { + SetIdentityMatrix( m_boneMatrix[i] ); + } + MatrixMode( MATERIAL_VIEW ); + LoadIdentity(); + MatrixMode( MATERIAL_PROJECTION ); + LoadIdentity(); + } + +#ifdef _X360 + m_DynamicState.m_bBuffer2Frames = m_bBuffer2FramesAhead; + SetRenderState( D3DRS_BUFFER2FRAMES, m_DynamicState.m_bBuffer2Frames ); +#endif + + m_DynamicState.m_Viewport.X = m_DynamicState.m_Viewport.Y = + m_DynamicState.m_Viewport.Width = m_DynamicState.m_Viewport.Height = 0xFFFFFFFF; + m_DynamicState.m_Viewport.MinZ = m_DynamicState.m_Viewport.MaxZ = 0.0; + + // Be sure scissoring is off + m_DynamicState.m_RenderState[D3DRS_SCISSORTESTENABLE] = FALSE; + SetRenderState( D3DRS_SCISSORTESTENABLE, FALSE ); + m_DynamicState.m_ScissorRect.left = -1; + m_DynamicState.m_ScissorRect.top = -1; + m_DynamicState.m_ScissorRect.right = -1; + m_DynamicState.m_ScissorRect.bottom = -1; + + //SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE ); + EnableFastClip( false ); + float fFakePlane[4]; + unsigned int iFakePlaneVal = 0xFFFFFFFF; + fFakePlane[0] = fFakePlane[1] = fFakePlane[2] = fFakePlane[3] = *((float *)&iFakePlaneVal); + SetFastClipPlane( fFakePlane ); //doing this to better wire up plane change detection + + float zero[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + + // Make sure that our state is dirty. + m_DynamicState.m_UserClipPlaneEnabled = 0; + m_DynamicState.m_UserClipPlaneChanged = 0; + m_DynamicState.m_UserClipLastUpdatedUsingFixedFunction = false; + for( i = 0; i < g_pHardwareConfig->MaxUserClipPlanes(); i++ ) + { + // Make sure that our state is dirty. + m_DynamicState.m_UserClipPlaneWorld[i][0] = -1.0f; + m_DynamicState.m_UserClipPlaneProj[i][0] = -9999.0f; + m_DynamicState.m_UserClipPlaneEnabled |= ( 1 << i ); + SetClipPlane( i, zero ); + EnableClipPlane( i, false ); + Assert( m_DynamicState.m_UserClipPlaneEnabled == 0 ); + } + Assert( m_DynamicState.m_UserClipPlaneChanged == ((1 << g_pHardwareConfig->MaxUserClipPlanes()) - 1) ); + + m_DynamicState.m_FastClipEnabled = false; + m_DynamicState.m_bFastClipPlaneChanged = true; + + // User clip override + m_DynamicState.m_bUserClipTransformOverride = false; + D3DXMatrixIdentity( &m_DynamicState.m_UserClipTransform ); + + // Viewport defaults to the window size + RECT windowRect; +#if !defined( DX_TO_GL_ABSTRACTION ) + GetClientRect( (HWND)m_hWnd, &windowRect ); +#else + toglGetClientRect( (VD3DHWND)m_hWnd, &windowRect ); +#endif + + ShaderViewport_t viewport; + viewport.Init( windowRect.left, windowRect.top, + windowRect.right - windowRect.left, windowRect.bottom - windowRect.top ); + SetViewports( 1, &viewport ); + + // No render mesh + m_pRenderMesh = 0; + + // Reset cached vertex decl + m_DynamicState.m_pVertexDecl = NULL; + + // Reset the render target to be the normal backbuffer + if ( IsX360() ) + { + m_hCachedRenderTarget = INVALID_SHADERAPI_TEXTURE_HANDLE; + m_bUsingSRGBRenderTarget = false; + } + AcquireInternalRenderTargets(); + SetRenderTarget(); + + // Maintain vertex + pixel shader constant buffers + Vector4D *pVectorPixelShaderConstants = m_DesiredState.m_pVectorPixelShaderConstant; + int *pBooleanPixelShaderConstants = m_DesiredState.m_pBooleanPixelShaderConstant; + IntVector4D *pIntegerPixelShaderConstants = m_DesiredState.m_pIntegerPixelShaderConstant; + Vector4D *pVectorVertexShaderConstants = m_DesiredState.m_pVectorVertexShaderConstant; + int *pBooleanVertexShaderConstants = m_DesiredState.m_pBooleanVertexShaderConstant; + IntVector4D *pIntegerVertexShaderConstants = m_DesiredState.m_pIntegerVertexShaderConstant; + m_DesiredState = m_DynamicState; + m_DesiredState.m_pVectorPixelShaderConstant = pVectorPixelShaderConstants; + m_DesiredState.m_pBooleanPixelShaderConstant = pBooleanPixelShaderConstants; + m_DesiredState.m_pIntegerPixelShaderConstant = pIntegerPixelShaderConstants; + m_DesiredState.m_pVectorVertexShaderConstant = pVectorVertexShaderConstants; + m_DesiredState.m_pBooleanVertexShaderConstant = pBooleanVertexShaderConstants; + m_DesiredState.m_pIntegerVertexShaderConstant = pIntegerVertexShaderConstants; + if ( g_pHardwareConfig->Caps().m_SupportsPixelShaders ) + { + if ( !bFullReset ) + { + //Full resets init the values to defaults. Normal resets just leave them dirty. + if( g_pHardwareConfig->Caps().m_NumVertexShaderConstants != 0 ) + SetVertexShaderConstant( 0, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), IsX360() ? 217 : g_pHardwareConfig->Caps().m_NumVertexShaderConstants, true ); //217 on X360 to play nice with fast blatting code + + if( g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants != 0 ) + SetIntegerVertexShaderConstant( 0, (int *)m_DesiredState.m_pIntegerVertexShaderConstant, g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants, true ); + + if( g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants != 0 ) + SetBooleanVertexShaderConstant( 0, m_DesiredState.m_pBooleanVertexShaderConstant, g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants, true ); + + + if( g_pHardwareConfig->Caps().m_NumPixelShaderConstants != 0 ) + SetPixelShaderConstant( 0, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), g_pHardwareConfig->Caps().m_NumPixelShaderConstants, true ); + + if( g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants != 0 ) + SetIntegerPixelShaderConstant( 0, (int *)m_DesiredState.m_pIntegerPixelShaderConstant, g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants, true ); + + if( g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants != 0 ) + SetBooleanPixelShaderConstant( 0, m_DesiredState.m_pBooleanPixelShaderConstant, g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants, true ); + } + } + + RECORD_DEBUG_STRING( "END ResetRenderState" ); + + m_bResettingRenderState = false; +} + +//----------------------------------------------------------------------------- +// Sets the default render state +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetDefaultState() +{ + LOCK_SHADERAPI(); + + // NOTE: This used to be in the material system, but I want to avoid all the per pass/batch + // virtual function calls. + int numTextureStages = g_pHardwareConfig->GetTextureStageCount(); + + // FIXME: This is a brutal hack. We only need to load these transforms for fixed-function + // hardware. Cap the max here to 4. + if ( IsPC() ) + { + numTextureStages = min( numTextureStages, 4 ); + int i; + for( i = 0; i < numTextureStages; i++ ) + { + CShaderAPIDx8::DisableTextureTransform( (TextureStage_t)i ); + CShaderAPIDx8::MatrixMode( (MaterialMatrixMode_t)(MATERIAL_TEXTURE0 + i) ); + CShaderAPIDx8::LoadIdentity( ); + } + } + CShaderAPIDx8::MatrixMode( MATERIAL_MODEL ); + + CShaderAPIDx8::Color4ub( 255, 255, 255, 255 ); + CShaderAPIDx8::ShadeMode( SHADER_SMOOTH ); + CShaderAPIDx8::SetVertexShaderIndex( ); + CShaderAPIDx8::SetPixelShaderIndex( ); + + MeshMgr()->MarkUnusedVertexFields( 0, 0, NULL ); +} + + + +//----------------------------------------------------------------------------- +// +// Methods related to vertex format +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Sets the vertex +//----------------------------------------------------------------------------- +inline void CShaderAPIDx8::SetVertexDecl( VertexFormat_t vertexFormat, bool bHasColorMesh, bool bUsingFlex, bool bUsingMorph ) +{ + VPROF("CShaderAPIDx8::SetVertexDecl"); + IDirect3DVertexDeclaration9 *pDecl = FindOrCreateVertexDecl( vertexFormat, bHasColorMesh, bUsingFlex, bUsingMorph ); + Assert( pDecl ); + + if ( ( pDecl != m_DynamicState.m_pVertexDecl ) && pDecl ) + { + Dx9Device()->SetVertexDeclaration( pDecl ); + m_DynamicState.m_pVertexDecl = pDecl; + } +} + + + +//----------------------------------------------------------------------------- +// +// Methods related to vertex buffers +// +//----------------------------------------------------------------------------- + +IMesh *CShaderAPIDx8::GetFlexMesh() +{ + LOCK_SHADERAPI(); + return MeshMgr()->GetFlexMesh(); +} + +//----------------------------------------------------------------------------- +// Gets the dynamic mesh +//----------------------------------------------------------------------------- +IMesh* CShaderAPIDx8::GetDynamicMesh( IMaterial* pMaterial, int nHWSkinBoneCount, bool buffered, + IMesh* pVertexOverride, IMesh* pIndexOverride ) +{ + Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() ); + + LOCK_SHADERAPI(); + return MeshMgr()->GetDynamicMesh( pMaterial, 0, nHWSkinBoneCount, buffered, pVertexOverride, pIndexOverride ); +} + +IMesh* CShaderAPIDx8::GetDynamicMeshEx( IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount, + bool bBuffered, IMesh* pVertexOverride, IMesh* pIndexOverride ) +{ + Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() ); + + LOCK_SHADERAPI(); + return MeshMgr()->GetDynamicMesh( pMaterial, vertexFormat, nHWSkinBoneCount, bBuffered, pVertexOverride, pIndexOverride ); +} + +//----------------------------------------------------------------------------- +// Returns the number of vertices we can render using the dynamic mesh +//----------------------------------------------------------------------------- +void CShaderAPIDx8::GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices ) +{ + LOCK_SHADERAPI(); + MeshMgr()->GetMaxToRender( pMesh, bMaxUntilFlush, pMaxVerts, pMaxIndices ); +} + +int CShaderAPIDx8::GetMaxVerticesToRender( IMaterial *pMaterial ) +{ + pMaterial = ((IMaterialInternal *)pMaterial)->GetRealTimeVersion(); //always work with the realtime version internally + + LOCK_SHADERAPI(); + return MeshMgr()->GetMaxVerticesToRender( pMaterial ); +} + +int CShaderAPIDx8::GetMaxIndicesToRender( ) +{ + LOCK_SHADERAPI(); + return MeshMgr()->GetMaxIndicesToRender( ); +} + +void CShaderAPIDx8::MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords ) +{ + LOCK_SHADERAPI(); + MeshMgr()->MarkUnusedVertexFields( nFlags, nTexCoordCount, pUnusedTexCoords ); +} + +//----------------------------------------------------------------------------- +// Draws the mesh +//----------------------------------------------------------------------------- +void CShaderAPIDx8::DrawMesh( CMeshBase *pMesh ) +{ + VPROF("CShaderAPIDx8::DrawMesh"); + if ( ShaderUtil()->GetConfig().m_bSuppressRendering ) + return; + +#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) + PIXifyName( s_pPIXMaterialName, sizeof( s_pPIXMaterialName ), m_pMaterial->GetName() ); + BeginPIXEvent( PIX_VALVE_ORANGE, s_pPIXMaterialName ); +#endif + + m_pRenderMesh = pMesh; + VertexFormat_t vertexFormat = m_pRenderMesh->GetVertexFormat(); + SetVertexDecl( vertexFormat, m_pRenderMesh->HasColorMesh(), m_pRenderMesh->HasFlexMesh(), m_pMaterial->IsUsingVertexID() ); + CommitStateChanges(); + Assert( m_pRenderMesh && m_pMaterial ); + m_pMaterial->DrawMesh( CompressionType( vertexFormat ) ); + m_pRenderMesh = NULL; + +#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) + EndPIXEvent(); +#endif +} + +void CShaderAPIDx8::DrawWithVertexAndIndexBuffers( void ) +{ + VPROF("CShaderAPIDx8::DrawWithVertexAndIndexBuffers"); + if ( ShaderUtil()->GetConfig().m_bSuppressRendering ) + return; + +#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) + PIXifyName( s_pPIXMaterialName, sizeof( s_pPIXMaterialName ), m_pMaterial->GetName()); + BeginPIXEvent( PIX_VALVE_ORANGE, s_pPIXMaterialName ); +#endif + +// m_pRenderMesh = pMesh; + // FIXME: need to make this deal with multiple streams, etc. + VertexFormat_t vertexFormat = MeshMgr()->GetCurrentVertexFormat(); + SetVertexDecl( vertexFormat, false /*m_pRenderMesh->HasColorMesh()*/, + false /*m_pRenderMesh->HasFlexMesh()*/, false /*m_pRenderMesh->IsUsingMorphData()*/ ); + CommitStateChanges(); + if ( m_pMaterial ) + { + m_pMaterial->DrawMesh( CompressionType( vertexFormat ) ); + } + else + { + MeshMgr()->RenderPassWithVertexAndIndexBuffers(); + } +// m_pRenderMesh = NULL; + +#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) + EndPIXEvent(); +#endif +} + +//----------------------------------------------------------------------------- +// Discards the vertex buffers +//----------------------------------------------------------------------------- +void CShaderAPIDx8::DiscardVertexBuffers() +{ + MeshMgr()->DiscardVertexBuffers(); +} + +void CShaderAPIDx8::ForceHardwareSync_WithManagedTexture() +{ + if ( IsX360() || !m_pFrameSyncTexture ) + return; + + // Set the default state for everything so we don't get more than we ask for here! + SetDefaultState(); + + D3DLOCKED_RECT rect; + + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ ); + + HRESULT hr = m_pFrameSyncTexture->LockRect( 0, &rect, NULL, 0 ); + if ( SUCCEEDED( hr ) ) + { + // modify.. + unsigned long *pData = (unsigned long*)rect.pBits; + (*pData)++; + + m_pFrameSyncTexture->UnlockRect( 0 ); + + // Now draw something with this texture. + DWORD iStage = 0; + IDirect3DBaseTexture9 *pOldTexture; + hr = Dx9Device()->GetTexture( iStage, &pOldTexture ); + if ( SUCCEEDED( hr ) ) + { + Dx9Device()->SetTexture( iStage, m_pFrameSyncTexture ); + // Remember the old FVF. + DWORD oldFVF; + hr = Dx9Device()->GetFVF( &oldFVF ); + if ( SUCCEEDED( hr ) ) + { + // Set the new FVF. + Dx9Device()->SetFVF( D3DFVF_XYZ ); + // Now, draw the simplest primitive D3D has ever seen. + unsigned short indices[3] = { 0, 1, 2 }; + Vector verts[3] = {vec3_origin, vec3_origin, vec3_origin}; + + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "DrawIndexedPrimitiveUP" ); + + Dx9Device()->DrawIndexedPrimitiveUP( + D3DPT_TRIANGLELIST, + 0, // Min vertex index + 3, // Num vertices used + 1, // # primitives + indices, // indices + D3DFMT_INDEX16, // index format + verts, // Vertices + sizeof( Vector )// Vertex stride + ); + + Dx9Device()->SetFVF( oldFVF ); + } + Dx9Device()->SetTexture( iStage, pOldTexture ); + } + } + // If this assert fails, then we failed somewhere above. + AssertOnce( SUCCEEDED( hr ) ); +} + +void CShaderAPIDx8::UpdateFrameSyncQuery( int queryIndex, bool bIssue ) +{ + Assert(queryIndex < NUM_FRAME_SYNC_QUERIES); + // wait if already issued + if ( m_bQueryIssued[queryIndex] ) + { + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ ); + + double flStartTime = Plat_FloatTime(); + BOOL dummyData = 0; + HRESULT hr = S_OK; + // NOTE: This fix helps out motherboards that are a little freaky. + // On such boards, sometimes the driver has to reset itself (an event which takes several seconds) + // and when that happens, the frame sync query object gets lost + for (;;) + { + hr = m_pFrameSyncQueryObject[queryIndex]->GetData( &dummyData, sizeof( dummyData ), D3DGETDATA_FLUSH ); + if ( hr != S_FALSE ) + break; + double flCurrTime = Plat_FloatTime(); + // don't wait more than 200ms (5fps) for these + if ( flCurrTime - flStartTime > 0.200f ) + break; + // Avoid burning a full core while waiting for the query. Spinning can actually harm performance + // because there might be driver threads that are trying to do work that end up starved, and the + // power drawn by the CPU may take away from the power available to the integrated graphics chip. + // A sleep of one millisecond should never be long enough to affect performance, especially since + // this should only trigger when the CPU is already ahead of the GPU. + // On L4D2/TF2 in GL mode this spinning was causing slowdowns. + ThreadSleep( 1 ); + } + m_bQueryIssued[queryIndex] = false; + Assert(hr == S_OK || hr == D3DERR_DEVICELOST); + + if ( hr == D3DERR_DEVICELOST ) + { + MarkDeviceLost( ); + return; + } + } + if ( bIssue ) + { + m_pFrameSyncQueryObject[queryIndex]->Issue( D3DISSUE_END ); + m_bQueryIssued[queryIndex] = true; + } +} + +void CShaderAPIDx8::ForceHardwareSync( void ) +{ + LOCK_SHADERAPI(); + VPROF( "CShaderAPIDx8::ForceHardwareSync" ); + +#ifdef DX_TO_GL_ABSTRACTION + if ( true ) +#else + if ( !mat_frame_sync_enable.GetInt() ) +#endif + return; + + // need to flush the dynamic buffer and make sure the entire image is there + FlushBufferedPrimitives(); + + RECORD_COMMAND( DX8_HARDWARE_SYNC, 0 ); + +#if !defined( _X360 ) + // How do you query dx9 for how many frames behind the hardware is or, alternatively, how do you tell the hardware to never be more than N frames behind? + // 1) The old QueryPendingFrameCount design was removed. It was + // a simple transaction with the driver through the + // GetDriverState, trivial for the drivers to lie. We came up + // with a much better scheme for tracking pending frames where + // the driver can not lie without a possible performance loss: + // use the asynchronous query system with D3DQUERYTYPE_EVENT and + // data size 0. When GetData returns S_OK for the query, you + // know that frame has finished. + if ( mat_frame_sync_force_texture.GetBool() ) + { + ForceHardwareSync_WithManagedTexture(); + } + else if ( m_pFrameSyncQueryObject[0] ) + { + // FIXME: Could install a callback into the materialsystem to do something while waiting for + // the frame to finish (update sound, etc.) + + // Disable VCR mode here or else it'll screw up (and we don't really care if this part plays back in exactly the same amount of time). + VCRSetEnabled( false ); + + m_currentSyncQuery ++; + if ( m_currentSyncQuery >= ARRAYSIZE(m_pFrameSyncQueryObject) ) + { + m_currentSyncQuery = 0; + } + double fStart = Plat_FloatTime(); + int waitIndex = ((m_currentSyncQuery + NUM_FRAME_SYNC_QUERIES) - (NUM_FRAME_SYNC_FRAMES_LATENCY+1)) % NUM_FRAME_SYNC_QUERIES; + UpdateFrameSyncQuery( waitIndex, false ); + UpdateFrameSyncQuery( m_currentSyncQuery, true ); + VCRSetEnabled( true ); + } +#else + DWORD hFence = Dx9Device()->InsertFence(); + Dx9Device()->BlockOnFence( hFence ); +#endif +} + + +//----------------------------------------------------------------------------- +// Needs render state +//----------------------------------------------------------------------------- +void CShaderAPIDx8::QueueResetRenderState() +{ + m_bResetRenderStateNeeded = true; +} + + +//----------------------------------------------------------------------------- +// Use this to begin and end the frame +//----------------------------------------------------------------------------- +void CShaderAPIDx8::BeginFrame() +{ + LOCK_SHADERAPI(); + + if ( m_bResetRenderStateNeeded ) + { + ResetRenderState( false ); + m_bResetRenderStateNeeded = false; + } + +#if ALLOW_SMP_ACCESS + Dx9Device()->SetASyncMode( mat_use_smp.GetInt() != 0 ); +#endif + + ++m_CurrentFrame; + m_nTextureMemoryUsedLastFrame = 0; +} + +void CShaderAPIDx8::EndFrame() +{ + LOCK_SHADERAPI(); + +#if !defined( _X360 ) + MEMCHECK; +#endif + + ExportTextureList(); +} + + +void CShaderAPIDx8::AddBufferToTextureList( const char *pName, D3DSURFACE_DESC &desc ) +{ +// ImageFormat imageFormat; +// imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format ); +// if( imageFormat < 0 ) +// { +// Assert( 0 ); +// return; +// } + KeyValues *pSubKey = m_pDebugTextureList->CreateNewKey(); + pSubKey->SetString( "Name", pName ); + pSubKey->SetString( "TexGroup", TEXTURE_GROUP_RENDER_TARGET ); + pSubKey->SetInt( "Size", +// ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height ); + 4 * desc.Width * desc.Height ); + pSubKey->SetString( "Format", "32 bit buffer (hack)" );//ImageLoader::GetName( imageFormat ) ); + pSubKey->SetInt( "Width", desc.Width ); + pSubKey->SetInt( "Height", desc.Height ); + + pSubKey->SetInt( "BindsMax", 1 ); + pSubKey->SetInt( "BindsFrame", 1 ); +} + +void CShaderAPIDx8::ExportTextureList() +{ + if ( !m_bEnableDebugTextureList ) + return; + + if ( !m_pBackBufferSurface || !m_pZBufferSurface ) + // Device vanished... + return; + + m_nDebugDataExportFrame = m_CurrentFrame; + + if ( IsPC() || !IsX360() ) + { + if ( m_pDebugTextureList ) + m_pDebugTextureList->deleteThis(); + + m_pDebugTextureList = new KeyValues( "TextureList" ); + + m_nTextureMemoryUsedTotal = 0; + m_nTextureMemoryUsedPicMip1 = 0; + m_nTextureMemoryUsedPicMip2 = 0; + for ( ShaderAPITextureHandle_t hTexture = m_Textures.Head() ; hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) ) + { + Texture_t &tex = m_Textures[hTexture]; + + if ( !( tex.m_Flags & Texture_t::IS_ALLOCATED ) ) + continue; + + // Compute total texture memory usage + m_nTextureMemoryUsedTotal += tex.GetMemUsage(); + + // Compute picmip memory usage + { + int numBytes = tex.GetMemUsage(); + + if ( tex.m_NumLevels > 1 ) + { + if ( tex.GetWidth() > 4 || tex.GetHeight() > 4 || tex.GetDepth() > 4 ) + { + int topmipsize = ImageLoader::GetMemRequired( tex.GetWidth(), tex.GetHeight(), tex.GetDepth(), tex.GetImageFormat(), false ); + numBytes -= topmipsize; + + m_nTextureMemoryUsedPicMip1 += numBytes; + + if ( tex.GetWidth() > 8 || tex.GetHeight() > 8 || tex.GetDepth() > 8 ) + { + int othermipsizeRatio = ( ( tex.GetWidth() > 8 ) ? 2 : 1 ) * ( ( tex.GetHeight() > 8 ) ? 2 : 1 ) * ( ( tex.GetDepth() > 8 ) ? 2 : 1 ); + int othermipsize = topmipsize / othermipsizeRatio; + numBytes -= othermipsize; + } + + m_nTextureMemoryUsedPicMip1 += numBytes; + } + else + { + m_nTextureMemoryUsedPicMip1 += numBytes; + m_nTextureMemoryUsedPicMip2 += numBytes; + } + } + else + { + m_nTextureMemoryUsedPicMip1 += numBytes; + m_nTextureMemoryUsedPicMip2 += numBytes; + } + } + + if ( !m_bDebugGetAllTextures && + tex.m_LastBoundFrame != m_CurrentFrame ) + continue; + + if ( tex.m_LastBoundFrame != m_CurrentFrame ) + tex.m_nTimesBoundThisFrame = 0; + + KeyValues *pSubKey = m_pDebugTextureList->CreateNewKey(); + pSubKey->SetString( "Name", tex.m_DebugName.String() ); + pSubKey->SetString( "TexGroup", tex.m_TextureGroupName.String() ); + pSubKey->SetInt( "Size", tex.GetMemUsage() ); + if ( tex.GetCount() > 1 ) + pSubKey->SetInt( "Count", tex.GetCount() ); + pSubKey->SetString( "Format", ImageLoader::GetName( tex.GetImageFormat() ) ); + pSubKey->SetInt( "Width", tex.GetWidth() ); + pSubKey->SetInt( "Height", tex.GetHeight() ); + + pSubKey->SetInt( "BindsMax", tex.m_nTimesBoundMax ); + pSubKey->SetInt( "BindsFrame", tex.m_nTimesBoundThisFrame ); + } + + D3DSURFACE_DESC desc; + m_pBackBufferSurface->GetDesc( &desc ); + AddBufferToTextureList( "BACKBUFFER", desc ); + AddBufferToTextureList( "FRONTBUFFER", desc ); + // ImageFormat imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format ); + // if( imageFormat >= 0 ) + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_frame_" TEXTURE_GROUP_RENDER_TARGET, + COUNTER_GROUP_TEXTURE_PER_FRAME, + // ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height ); + 2 * 4 * desc.Width * desc.Height ); // hack (times 2 for front and back buffer) + } + + m_pZBufferSurface->GetDesc( &desc ); + AddBufferToTextureList( "DEPTHBUFFER", desc ); + // imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format ); + // if( imageFormat >= 0 ) + { + VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_frame_" TEXTURE_GROUP_RENDER_TARGET, + COUNTER_GROUP_TEXTURE_PER_FRAME, + // ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height ); + 4 * desc.Width * desc.Height ); // hack + } + } + +#if defined( _X360 ) + // toggle to do one shot transmission + m_bEnableDebugTextureList = false; + + int numTextures = m_Textures.Count() + 3; + xTextureList_t* pXTextureList = (xTextureList_t *)_alloca( numTextures * sizeof( xTextureList_t ) ); + memset( pXTextureList, 0, numTextures * sizeof( xTextureList_t ) ); + + numTextures = 0; + for ( ShaderAPITextureHandle_t hTexture = m_Textures.Head() ; hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) ) + { + Texture_t &tex = m_Textures[hTexture]; + + if ( !m_bDebugGetAllTextures && tex.m_LastBoundFrame != m_CurrentFrame ) + { + continue; + } + if ( !( tex.m_Flags & Texture_t::IS_ALLOCATED ) ) + { + continue; + } + + int refCount; + if ( tex.m_Flags & Texture_t::IS_DEPTH_STENCIL ) + { + // interface forces us to ignore these + refCount = -1; + } + else + { + refCount = GetD3DTextureRefCount( CShaderAPIDx8::GetD3DTexture( hTexture ) ); + } + + pXTextureList[numTextures].pName = tex.m_DebugName.String(); + pXTextureList[numTextures].size = tex.m_SizeBytes * tex.m_NumCopies; + pXTextureList[numTextures].pGroupName = tex.m_TextureGroupName.String(); + pXTextureList[numTextures].pFormatName = D3DFormatName( ImageLoader::ImageFormatToD3DFormat( tex.GetImageFormat() ) ); + pXTextureList[numTextures].width = tex.GetWidth(); + pXTextureList[numTextures].height = tex.GetHeight(); + pXTextureList[numTextures].depth = tex.GetDepth(); + pXTextureList[numTextures].numLevels = tex.m_NumLevels; + pXTextureList[numTextures].binds = tex.m_nTimesBoundThisFrame; + pXTextureList[numTextures].refCount = refCount; + pXTextureList[numTextures].edram = ( tex.m_Flags & Texture_t::IS_RENDER_TARGET_SURFACE ) != 0; + pXTextureList[numTextures].procedural = tex.m_NumCopies > 1; + pXTextureList[numTextures].final = ( tex.m_Flags & Texture_t::IS_FINALIZED ) != 0; + pXTextureList[numTextures].failed = ( tex.m_Flags & Texture_t::IS_FAILED ) != 0; + numTextures++; + } + + // build special entries for implicit surfaces/textures + D3DSURFACE_DESC desc; + m_pBackBufferSurface->GetDesc( &desc ); + int size = ImageLoader::GetMemRequired( + desc.Width, + desc.Height, + 0, + ImageLoader::D3DFormatToImageFormat( desc.Format ), + false ); + pXTextureList[numTextures].pName = "_rt_BackBuffer"; + pXTextureList[numTextures].size = size; + pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET_SURFACE; + pXTextureList[numTextures].pFormatName = D3DFormatName( desc.Format ); + pXTextureList[numTextures].width = desc.Width; + pXTextureList[numTextures].height = desc.Height; + pXTextureList[numTextures].depth = 1; + pXTextureList[numTextures].binds = 1; + pXTextureList[numTextures].refCount = 1; + pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB( desc.Format ); + pXTextureList[numTextures].edram = true; + numTextures++; + + m_pZBufferSurface->GetDesc( &desc ); + pXTextureList[numTextures].pName = "_rt_DepthBuffer"; + pXTextureList[numTextures].size = size; + pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET_SURFACE; + pXTextureList[numTextures].pFormatName = D3DFormatName( desc.Format ); + pXTextureList[numTextures].width = desc.Width; + pXTextureList[numTextures].height = desc.Height; + pXTextureList[numTextures].depth = 1; + pXTextureList[numTextures].binds = 1; + pXTextureList[numTextures].refCount = 1; + pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB( desc.Format ); + pXTextureList[numTextures].edram = true; + numTextures++; + + // front buffer resides in DDR + pXTextureList[numTextures].pName = "_rt_FrontBuffer"; + pXTextureList[numTextures].size = size; + pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET; + pXTextureList[numTextures].pFormatName = D3DFormatName( desc.Format ); + pXTextureList[numTextures].width = desc.Width; + pXTextureList[numTextures].height = desc.Height; + pXTextureList[numTextures].depth = 1; + pXTextureList[numTextures].binds = 1; + pXTextureList[numTextures].refCount = 1; + pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB( desc.Format ); + numTextures++; + + int totalMemory = 0; + for ( int i = 0; i < numTextures; i++ ) + { + if ( pXTextureList[i].edram ) + { + // skip edram based items + continue; + } + totalMemory += pXTextureList[i].size; + } + Msg( "Total D3D Texture Memory: %.2f MB\n", (float)totalMemory/( 1024.0f * 1024.0f ) ); + + // transmit to console + XBX_rTextureList( numTextures, pXTextureList ); +#endif +} + + +//----------------------------------------------------------------------------- +// Releases/reloads resources when other apps want some memory +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ReleaseShaderObjects() +{ + ReleaseInternalRenderTargets(); + EvictManagedResourcesInternal(); + + // FIXME: Move into shaderdevice when textures move over. + +#ifdef _DEBUG + // Helps to find the unreleased textures. + if ( TextureCount() > 0 ) + { + ShaderAPITextureHandle_t hTexture; + for ( hTexture = m_Textures.Head(); hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) ) + { + if ( GetTexture( hTexture ).m_NumCopies == 1 ) + { + if ( GetTexture( hTexture ).GetTexture() ) + { + Warning( "Didn't correctly clean up texture 0x%8.8x (%s)\n", hTexture, GetTexture( hTexture ).m_DebugName.String() ); + } + } + else + { + for ( int k = GetTexture( hTexture ).m_NumCopies; --k >= 0; ) + { + if ( GetTexture( hTexture ).GetTexture( k ) != 0 ) + { + Warning( "Didn't correctly clean up texture 0x%8.8x (%s)\n", hTexture, GetTexture( hTexture ).m_DebugName.String() ); + break; + } + } + } + } + } +#endif + + Assert( TextureCount() == 0 ); +} + +void CShaderAPIDx8::RestoreShaderObjects() +{ + AcquireInternalRenderTargets(); + SetRenderTarget(); +} + + +//-------------------------------------------------------------------- +// PIX instrumentation routines +// Windows only for now. Turn these on with PIX_INSTRUMENTATION above +//-------------------------------------------------------------------- + +#if 0 // hack versions for OSX to be able to PIX log even when not built debug... +void CShaderAPIDx8::BeginPIXEvent( unsigned long color, const char* szName ) + { + LOCK_SHADERAPI(); + GLMBeginPIXEvent( szName ); // direct call no macro + return; + } + + void CShaderAPIDx8::EndPIXEvent( void ) + { + LOCK_SHADERAPI(); + GLMEndPIXEvent(); // direct call no macro + return; + } + +#else + +#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) +ConVar pix_break_on_event( "pix_break_on_event", "" ); +#endif + +void CShaderAPIDx8::BeginPIXEvent( unsigned long color, const char* szName ) +{ +#if ( defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) ) + //LOCK_SHADERAPI(); + + const char *p = pix_break_on_event.GetString(); + if ( p && V_strlen( p ) ) + { + if ( V_stristr( szName, p ) != NULL ) + { + DebuggerBreak(); + } + } + +#if defined ( DX_TO_GL_ABSTRACTION ) + GLMBeginPIXEvent( szName ); + +#if defined( _WIN32 ) + // AMD PerfStudio integration: Call into D3D9.DLL's D3DPERF_BeginEvent() (this gets intercepted by PerfStudio even in GL mode). + if ( g_pShaderDeviceMgrDx8->m_pBeginEvent ) + { + wchar_t wszName[128]; + mbstowcs( wszName, szName, 128 ); + + g_pShaderDeviceMgrDx8->m_pBeginEvent( 0x2F2F2F2F, wszName ); + } +#endif +#elif defined(_X360 ) +#ifndef _DEBUG + char szPIXEventName[32]; + PIXifyName( szPIXEventName, szName ); + PIXBeginNamedEvent( color, szPIXEventName ); +#endif +#else // PC + if ( PIXError() ) + return; + + wchar_t wszName[128]; + mbstowcs( wszName, szName, 128 ); + + // Fire the PIX event, trapping for errors... + if ( D3DPERF_BeginEvent( color, wszName ) < 0 ) + { + Warning( "PIX error Beginning %s event\n", szName ); + IncrementPIXError(); + } +#endif +#endif // #if defined( PIX_INSTRUMENTATION ) +} + +void CShaderAPIDx8::EndPIXEvent( void ) +{ +#if ( defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) ) + //LOCK_SHADERAPI(); + +#if defined ( DX_TO_GL_ABSTRACTION ) + GLMEndPIXEvent(); + +#if defined( _WIN32 ) + // AMD PerfStudio integration: Call into D3D9.DLL's D3DPERF_EndEvent() (this gets intercepted by PerfStudio even in GL mode). + if ( g_pShaderDeviceMgrDx8->m_pEndEvent ) + { + g_pShaderDeviceMgrDx8->m_pEndEvent(); + } +#endif +#elif defined( _X360 ) +#ifndef _DEBUG + PIXEndNamedEvent(); +#endif +#else // PC + if ( PIXError() ) + return; + +#if !defined( NVPERFHUD ) + // Fire the PIX event, trapping for errors... + if ( D3DPERF_EndEvent() < 0 ) + { + Warning("PIX error ending event\n"); + IncrementPIXError(); + } +#endif +#endif +#endif // #if defined( PIX_INSTRUMENTATION ) +} + +#endif + +void CShaderAPIDx8::AdvancePIXFrame() +{ +#if defined( PIX_INSTRUMENTATION ) + // Ping PIX when this bool goes from false to true + if ( r_pix_start.GetBool() && (!m_bPixCapturing) ) + { + StartPIXInstrumentation(); + m_bPixCapturing = true; + } + + // If we want to record frames... + if ( r_pix_recordframes.GetInt() ) + { + if ( m_nPixFrame == 0 ) // First frame to record + { + StartPIXInstrumentation(); + m_nPixFrame++; + } + else if( m_nPixFrame == r_pix_recordframes.GetInt() ) // Last frame to record + { + EndPIXInstrumentation(); + r_pix_recordframes.SetValue(0); + m_nPixFrame = 0; + } + else + { + m_nPixFrame++; // Recording frames... + } + } +#endif +} + +// No begin-end for this...use this to put discrete markers in the PIX stream +void CShaderAPIDx8::SetPIXMarker( unsigned long color, const char* szName ) +{ +#if defined( PIX_INSTRUMENTATION ) + LOCK_SHADERAPI(); + +#if defined( DX_TO_GL_ABSTRACTION ) + if ( g_pShaderDeviceMgrDx8->m_pSetMarker ) + { + wchar_t wszName[128]; + mbstowcs(wszName, szName, 128 ); + g_pShaderDeviceMgrDx8->m_pSetMarker( 0x2F2F2F2F, wszName ); + } +#elif defined( _X360 ) +#ifndef _DEBUG + char szPIXMarkerName[32]; + PIXifyName( szPIXMarkerName, szName ); + PIXSetMarker( color, szPIXMarkerName ); +#endif +#else // PC + if ( PIXError() ) + return; + wchar_t wszName[128]; + mbstowcs(wszName, szName, 128 ); + D3DPERF_SetMarker( color, wszName ); +#endif + +#endif // PIX_INSTRUMENTATION +} + +void CShaderAPIDx8::StartPIXInstrumentation() +{ +#if defined( PIX_INSTRUMENTATION ) + SetPIXMarker( PIX_VALVE_ORANGE, "Valve_PIX_Capture_Start" ); +#endif +} + +void CShaderAPIDx8::EndPIXInstrumentation() +{ +#if defined( PIX_INSTRUMENTATION ) + SetPIXMarker( PIX_VALVE_ORANGE, "Valve_PIX_Capture_End" ); +#endif +} + +void CShaderAPIDx8::IncrementPIXError() +{ +#if defined( PIX_INSTRUMENTATION ) && !defined( NVPERFHUD ) + m_nPIXErrorCount++; + if ( m_nPIXErrorCount >= MAX_PIX_ERRORS ) + { + Warning( "Source engine built with PIX instrumentation, but PIX doesn't seem to have been used to instantiate the game, which is necessary on PC.\n" ); + } +#endif +} + +// Have we already hit several PIX errors? +bool CShaderAPIDx8::PIXError() +{ +#if defined( PIX_INSTRUMENTATION ) && !defined( NVPERFHUD ) + return m_nPIXErrorCount >= MAX_PIX_ERRORS; +#else + return false; +#endif +} + + +//----------------------------------------------------------------------------- +// Check for device lost +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ChangeVideoMode( const ShaderDeviceInfo_t &info ) +{ + if ( IsX360() ) + return; + + LOCK_SHADERAPI(); + + m_PendingVideoModeChangeConfig = info; + m_bPendingVideoModeChange = true; + + if ( info.m_DisplayMode.m_nWidth != 0 && info.m_DisplayMode.m_nHeight != 0 ) + { + m_nWindowWidth = info.m_DisplayMode.m_nWidth; + m_nWindowHeight = info.m_DisplayMode.m_nHeight; + } +} + + +//----------------------------------------------------------------------------- +// Compute fill rate +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ComputeFillRate() +{ + if ( IsX360() ) + { + // not valid + return; + } + + static unsigned char* pBuf = 0; + + int width, height; + GetWindowSize( width, height ); + // Snapshot; look at total # pixels drawn... + if ( !pBuf ) + { + int memSize = ShaderUtil()->GetMemRequired( + width, + height, + 1, + IMAGE_FORMAT_RGB888, + false ) + 4; + + pBuf = (unsigned char*)malloc( memSize ); + } + + ReadPixels( + 0, + 0, + width, + height, + pBuf, + IMAGE_FORMAT_RGB888 ); + + int mask = 0xFF; + int count = 0; + unsigned char* pRead = pBuf; + for (int i = 0; i < height; ++i) + { + for (int j = 0; j < width; ++j) + { + int val = *(int*)pRead; + count += (val & mask); + pRead += 3; + } + } +} + +//----------------------------------------------------------------------------- +// Use this to get the mesh builder that allows us to modify vertex data +//----------------------------------------------------------------------------- +CMeshBuilder* CShaderAPIDx8::GetVertexModifyBuilder() +{ + return &m_ModifyBuilder; +} + +bool CShaderAPIDx8::InFlashlightMode() const +{ + return ShaderUtil()->InFlashlightMode(); +} + +bool CShaderAPIDx8::InEditorMode() const +{ + return ShaderUtil()->InEditorMode(); +} + +//----------------------------------------------------------------------------- +// Gets the bound morph's vertex format; returns 0 if no morph is bound +//----------------------------------------------------------------------------- +MorphFormat_t CShaderAPIDx8::GetBoundMorphFormat() +{ + return ShaderUtil()->GetBoundMorphFormat(); +} + +//----------------------------------------------------------------------------- +// returns the current time in seconds... +//----------------------------------------------------------------------------- +double CShaderAPIDx8::CurrentTime() const +{ + // FIXME: Return game time instead of real time! + // Or eliminate this altogether and put it into a material var + // (this is used by vertex modifiers in shader code at the moment) + return Plat_FloatTime(); +} + + +//----------------------------------------------------------------------------- +// Methods called by the transition table that use dynamic state... +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ApplyZBias( const ShadowState_t& shaderState ) +{ + MaterialSystem_Config_t &config = ShaderUtil()->GetConfig(); + float a = (config.m_SlopeScaleDepthBias_Decal != 0.0f) ? 1.0f / config.m_SlopeScaleDepthBias_Decal : 0.0f; + float b = (config.m_SlopeScaleDepthBias_Normal != 0.0f) ? 1.0f / config.m_SlopeScaleDepthBias_Normal : 0.0f; + float c = (config.m_DepthBias_Decal != 0.0f) ? 1.0f / config.m_DepthBias_Decal : 0.0f; + float d = (config.m_DepthBias_Normal != 0.0f) ? 1.0f / config.m_DepthBias_Normal : 0.0f; + + // FIXME: No longer necessary; may be necessary if you want to use cat 4.3 drivers? + // GR - hack for R200 + bool bPS14Only = g_pHardwareConfig->Caps().m_SupportsPixelShaders_1_4 && !g_pHardwareConfig->Caps().m_SupportsPixelShaders_2_0; + if( ( g_pHardwareConfig->Caps().m_VendorID == 0x1002 ) && bPS14Only ) + { + // Slam to m_SlopeScaleDepthBias_Decal = 0, m_DepthBias_Decal = -4096 + // which empirically is what appears to look good on r200 + // NOTE: Slamming to 0 instead of -1.0 / 4096 because on Cat 4.9, WinXP, 8500, + // this causes the z values to be more than 50 units away from the original z values + + a = 0.0f; + c = -1.0/4096.0; + } + + // bias = (s * D3DRS_SLOPESCALEDEPTHBIAS) + D3DRS_DEPTHBIAS, where s is the maximum depth slope of the triangle being rendered + if ( g_pHardwareConfig->Caps().m_ZBiasAndSlopeScaledDepthBiasSupported ) + { + float fSlopeScaleDepthBias, fDepthBias; + if ( shaderState.m_ZBias == SHADER_POLYOFFSET_DECAL ) + { + fSlopeScaleDepthBias = a; + fDepthBias = c; + } + else if ( shaderState.m_ZBias == SHADER_POLYOFFSET_SHADOW_BIAS ) + { + fSlopeScaleDepthBias = m_fShadowSlopeScaleDepthBias; + fDepthBias = m_fShadowDepthBias; + } + else // assume SHADER_POLYOFFSET_DISABLE + { + fSlopeScaleDepthBias = b; + fDepthBias = d; + } + + if( ReverseDepthOnX360() ) + { + fSlopeScaleDepthBias = -fSlopeScaleDepthBias; + fDepthBias = -fDepthBias; + } + + SetRenderStateConstMacro( this, D3DRS_SLOPESCALEDEPTHBIAS, *((DWORD*) (&fSlopeScaleDepthBias)) ); + SetRenderStateConstMacro( this, D3DRS_DEPTHBIAS, *((DWORD*) (&fDepthBias)) ); + } + else + { + MarkAllUserClipPlanesDirty(); + m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |= + STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION; + } +} + +void CShaderAPIDx8::ApplyTextureEnable( const ShadowState_t& state, int nSampler ) +{ + if ( state.m_SamplerState[nSampler].m_TextureEnable == SamplerState(nSampler).m_TextureEnable ) + return; + + if ( state.m_SamplerState[nSampler].m_TextureEnable ) + { + SamplerState( nSampler ).m_TextureEnable = true; + + // Should not be necessary/possible (SetTextureState() calls D3D9/DXAbstract, so the calling thread must already own the device. + //LOCK_SHADERAPI(); + + // Don't do this here!! It ends up giving us extra texture sets. + // We'll Assert in debug mode if you enable a texture stage + // but don't bind a texture. + // see CShaderAPIDx8::RenderPass() for this check. + // NOTE: We aren't doing this optimization quite yet. There are situations + // where you want a texture stage enabled for its texture coordinates, but + // you don't actually bind a texture (texmvspec for example.) + SetTextureState( (Sampler_t)nSampler, SamplerState(nSampler).m_BoundTexture, true ); + } + else + { + SamplerState( nSampler ).m_TextureEnable = false; + SetTextureState( (Sampler_t)nSampler, INVALID_SHADERAPI_TEXTURE_HANDLE ); + } +} + + +//----------------------------------------------------------------------------- +// Used to clear the transition table when we know it's become invalid. +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ClearSnapshots() +{ + LOCK_SHADERAPI(); + FlushBufferedPrimitives(); + m_TransitionTable.Reset(); + InitRenderState(); +} + + +static void KillTranslation( D3DXMATRIX& mat ) +{ + mat[3] = 0.0f; + mat[7] = 0.0f; + mat[11] = 0.0f; + mat[12] = 0.0f; + mat[13] = 0.0f; + mat[14] = 0.0f; + mat[15] = 1.0f; +} + +static void PrintMatrix( const char *name, const D3DXMATRIX& mat ) +{ + int row, col; + char buf[128]; + + Plat_DebugString( name ); + Plat_DebugString( "\n" ); + for( row = 0; row < 4; row++ ) + { + Plat_DebugString( " " ); + for( col = 0; col < 4; col++ ) + { + sprintf( buf, "%f ", ( float )mat( row, col ) ); + Plat_DebugString( buf ); + } + Plat_DebugString( "\n" ); + } + Plat_DebugString( "\n" ); +} + + +//----------------------------------------------------------------------------- +// Gets the vertex format for a particular snapshot id +//----------------------------------------------------------------------------- +VertexFormat_t CShaderAPIDx8::ComputeVertexUsage( int num, StateSnapshot_t* pIds ) const +{ + LOCK_SHADERAPI(); + if (num == 0) + return 0; + + // We don't have to all sorts of crazy stuff if there's only one snapshot + if ( num == 1 ) + { + const ShadowShaderState_t& state = m_TransitionTable.GetSnapshotShader( pIds[0] ); + return state.m_VertexUsage; + } + + Assert( pIds ); + + // Aggregating vertex formats is a little tricky; + // For example, what do we do when two passes want user data? + // Can we assume they are the same? For now, I'm going to + // just print a warning in debug. + + VertexCompressionType_t compression = VERTEX_COMPRESSION_INVALID; + int userDataSize = 0; + int numBones = 0; + int texCoordSize[VERTEX_MAX_TEXTURE_COORDINATES] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int flags = 0; + + for (int i = num; --i >= 0; ) + { + const ShadowShaderState_t& state = m_TransitionTable.GetSnapshotShader( pIds[i] ); + VertexFormat_t fmt = state.m_VertexUsage; + flags |= VertexFlags(fmt); + + VertexCompressionType_t newCompression = CompressionType( fmt ); + if ( ( compression != newCompression ) && ( compression != VERTEX_COMPRESSION_INVALID ) ) + { + Warning("Encountered a material with two passes that specify different vertex compression types!\n"); + compression = VERTEX_COMPRESSION_NONE; // Be safe, disable compression + } + + int newNumBones = NumBoneWeights(fmt); + if ((numBones != newNumBones) && (newNumBones != 0)) + { + if (numBones != 0) + { + Warning("Encountered a material with two passes that use different numbers of bones!\n"); + } + numBones = newNumBones; + } + + int newUserSize = UserDataSize(fmt); + if ((userDataSize != newUserSize) && (newUserSize != 0)) + { + if (userDataSize != 0) + { + Warning("Encountered a material with two passes that use different user data sizes!\n"); + } + userDataSize = newUserSize; + } + + for ( int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j ) + { + int newSize = TexCoordSize( (TextureStage_t)j, fmt ); + if ( ( texCoordSize[j] != newSize ) && ( newSize != 0 ) ) + { + if ( texCoordSize[j] != 0 ) + { + Warning("Encountered a material with two passes that use different texture coord sizes!\n"); + } + if ( texCoordSize[j] < newSize ) + { + texCoordSize[j] = newSize; + } + } + } + } + + return MeshMgr()->ComputeVertexFormat( flags, VERTEX_MAX_TEXTURE_COORDINATES, + texCoordSize, numBones, userDataSize ); +} + +VertexFormat_t CShaderAPIDx8::ComputeVertexFormat( int num, StateSnapshot_t* pIds ) const +{ + LOCK_SHADERAPI(); + VertexFormat_t fmt = ComputeVertexUsage( num, pIds ); + return fmt; +} + + +//----------------------------------------------------------------------------- +// What fields in the morph do we actually use? +//----------------------------------------------------------------------------- +MorphFormat_t CShaderAPIDx8::ComputeMorphFormat( int numSnapshots, StateSnapshot_t* pIds ) const +{ + LOCK_SHADERAPI(); + MorphFormat_t format = 0; + for ( int i = 0; i < numSnapshots; ++i ) + { + MorphFormat_t fmt = m_TransitionTable.GetSnapshotShader( pIds[i] ).m_MorphUsage; + format |= VertexFlags(fmt); + } + return format; +} + +//----------------------------------------------------------------------------- +// Commits a range of vertex shader constants +//----------------------------------------------------------------------------- +static void CommitVertexShaderConstantRange( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, + DynamicState_t ¤tState, 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( ¤tState.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( ¤tState.m_pVectorVertexShaderConstant[nFirstCommit], + &desiredState.m_pVectorVertexShaderConstant[nFirstCommit], nCommitCount * 4 * sizeof(float) ); + } +} + + +//----------------------------------------------------------------------------- +// Gets the current buffered state... (debug only) +//----------------------------------------------------------------------------- +void CShaderAPIDx8::GetBufferedState( BufferedState_t& state ) +{ + memcpy( &state.m_Transform[0], &GetTransform(MATERIAL_MODEL), sizeof(D3DXMATRIX) ); + memcpy( &state.m_Transform[1], &GetTransform(MATERIAL_VIEW), sizeof(D3DXMATRIX) ); + memcpy( &state.m_Transform[2], &GetTransform(MATERIAL_PROJECTION), sizeof(D3DXMATRIX) ); + memcpy( &state.m_Viewport, &m_DynamicState.m_Viewport, sizeof(state.m_Viewport) ); + state.m_PixelShader = ShaderManager()->GetCurrentPixelShader(); + state.m_VertexShader = ShaderManager()->GetCurrentVertexShader(); + for (int i = 0; i < g_pHardwareConfig->GetSamplerCount(); ++i) + { + state.m_BoundTexture[i] = m_DynamicState.m_SamplerState[i].m_BoundTexture; + } +} + + +//----------------------------------------------------------------------------- +// constant color methods +//----------------------------------------------------------------------------- + +void CShaderAPIDx8::Color3f( float r, float g, float b ) +{ + unsigned int color = D3DCOLOR_ARGB( 255, (int)(r * 255), + (int)(g * 255), (int)(b * 255) ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + +void CShaderAPIDx8::Color4f( float r, float g, float b, float a ) +{ + unsigned int color = D3DCOLOR_ARGB( (int)(a * 255), (int)(r * 255), + (int)(g * 255), (int)(b * 255) ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + +void CShaderAPIDx8::Color3fv( float const *c ) +{ + Assert( c ); + unsigned int color = D3DCOLOR_ARGB( 255, (int)(c[0] * 255), + (int)(c[1] * 255), (int)(c[2] * 255) ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + +void CShaderAPIDx8::Color4fv( float const *c ) +{ + Assert( c ); + unsigned int color = D3DCOLOR_ARGB( (int)(c[3] * 255), (int)(c[0] * 255), + (int)(c[1] * 255), (int)(c[2] * 255) ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + +void CShaderAPIDx8::Color3ub( unsigned char r, unsigned char g, unsigned char b ) +{ + unsigned int color = D3DCOLOR_ARGB( 255, r, g, b ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + +void CShaderAPIDx8::Color3ubv( unsigned char const* pColor ) +{ + Assert( pColor ); + unsigned int color = D3DCOLOR_ARGB( 255, pColor[0], pColor[1], pColor[2] ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + +void CShaderAPIDx8::Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) +{ + unsigned int color = D3DCOLOR_ARGB( a, r, g, b ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + +void CShaderAPIDx8::Color4ubv( unsigned char const* pColor ) +{ + Assert( pColor ); + unsigned int color = D3DCOLOR_ARGB( pColor[3], pColor[0], pColor[1], pColor[2] ); + if (color != m_DynamicState.m_ConstantColor) + { + m_DynamicState.m_ConstantColor = color; + SetSupportedRenderState( D3DRS_TEXTUREFACTOR, color ); + } +} + + +//----------------------------------------------------------------------------- +// The shade mode +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ShadeMode( ShaderShadeMode_t mode ) +{ + LOCK_SHADERAPI(); + D3DSHADEMODE shadeMode = (mode == SHADER_FLAT) ? D3DSHADE_FLAT : D3DSHADE_GOURAUD; + if (m_DynamicState.m_ShadeMode != shadeMode) + { + m_DynamicState.m_ShadeMode = shadeMode; + SetRenderStateConstMacro( this, D3DRS_SHADEMODE, shadeMode ); + } +} + + +//----------------------------------------------------------------------------- +// Buffering 2 frames ahead +//----------------------------------------------------------------------------- +void CShaderAPIDx8::EnableBuffer2FramesAhead( bool bEnable ) +{ +#ifdef _X360 + m_bBuffer2FramesAhead = bEnable; + if ( bEnable != m_DynamicState.m_bBuffer2Frames ) + { + SetRenderState( D3DRS_BUFFER2FRAMES, bEnable ); + m_DynamicState.m_bBuffer2Frames = bEnable; + } +#endif +} + +void CShaderAPIDx8::SetDepthFeatheringPixelShaderConstant( int iConstant, float fDepthBlendScale ) +{ + float fConstantValues[4]; + + if( IsX360() ) + { + const D3DMATRIX &projMatrix = GetProjectionMatrix(); + + fConstantValues[0] = 50.0f / fDepthBlendScale; + fConstantValues[1] = 1.0f / projMatrix.m[2][2]; + fConstantValues[2] = 1.0f / projMatrix.m[3][2]; + fConstantValues[3] = projMatrix.m[2][2]; + + /* + D3DXMATRIX invProjMatrix; + D3DXMatrixInverse( &invProjMatrix, NULL, (D3DXMATRIX *)&projMatrix ); + fConstantValues[1] = invProjMatrix.m[3][2]; + fConstantValues[2] = invProjMatrix.m[3][3]; + fConstantValues[3] = invProjMatrix.m[2][2]; + */ + } + else + { + fConstantValues[0] = m_DynamicState.m_DestAlphaDepthRange / fDepthBlendScale; + fConstantValues[1] = fConstantValues[2] = fConstantValues[3] = 0.0f; //empty + } + + SetPixelShaderConstant( iConstant, fConstantValues ); +} + + +//----------------------------------------------------------------------------- +// Cull mode.. +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetCullModeState( bool bEnable, D3DCULL nDesiredCullMode ) +{ + D3DCULL nCullMode = bEnable ? nDesiredCullMode : D3DCULL_NONE; + if ( nCullMode != m_DynamicState.m_CullMode ) + { + SetRenderStateConstMacro( this, D3DRS_CULLMODE, nCullMode ); + m_DynamicState.m_CullMode = nCullMode; + } +} + +void CShaderAPIDx8::ApplyCullEnable( bool bEnable ) +{ + m_DynamicState.m_bCullEnabled = bEnable; + SetCullModeState( m_DynamicState.m_bCullEnabled, m_DynamicState.m_DesiredCullMode ); +} + +void CShaderAPIDx8::CullMode( MaterialCullMode_t nCullMode ) +{ + LOCK_SHADERAPI(); + D3DCULL nNewCullMode; + switch( nCullMode ) + { + case MATERIAL_CULLMODE_CCW: + // Culls backfacing polys (normal) + nNewCullMode = D3DCULL_CCW; + break; + + case MATERIAL_CULLMODE_CW: + // Culls frontfacing polys + nNewCullMode = D3DCULL_CW; + break; + + default: + Warning( "CullMode: invalid cullMode\n" ); + return; + } + + if (m_DynamicState.m_DesiredCullMode != nNewCullMode) + { + FlushBufferedPrimitives(); + m_DynamicState.m_DesiredCullMode = nNewCullMode; + SetCullModeState( m_DynamicState.m_bCullEnabled, m_DynamicState.m_DesiredCullMode ); + } +} + +static ConVar mat_alphacoverage( "mat_alphacoverage", "1" ); +void CShaderAPIDx8::ApplyAlphaToCoverage( bool bEnable ) +{ + if ( mat_alphacoverage.GetBool() ) + { + if ( bEnable ) + EnableAlphaToCoverage(); + else + DisableAlphaToCoverage(); + } + else + { + DisableAlphaToCoverage(); + } +} + +//----------------------------------------------------------------------------- +// Returns the current cull mode of the current material (for selection mode only) +//----------------------------------------------------------------------------- +D3DCULL CShaderAPIDx8::GetCullMode() const +{ + Assert( m_pMaterial ); + if ( m_pMaterial->GetMaterialVarFlag( MATERIAL_VAR_NOCULL ) ) + return D3DCULL_NONE; + return m_DynamicState.m_DesiredCullMode; +} + +void CShaderAPIDx8::SetRasterState( const ShaderRasterState_t& state ) +{ + // FIXME: Implement! +} + + +void CShaderAPIDx8::ForceDepthFuncEquals( bool bEnable ) +{ + LOCK_SHADERAPI(); + if ( !g_pShaderDeviceDx8->IsDeactivated() ) + { + m_TransitionTable.ForceDepthFuncEquals( bEnable ); + } +} + +void CShaderAPIDx8::OverrideDepthEnable( bool bEnable, bool bDepthEnable ) +{ + LOCK_SHADERAPI(); + if ( !g_pShaderDeviceDx8->IsDeactivated() ) + { + m_TransitionTable.OverrideDepthEnable( bEnable, bDepthEnable ); + } +} + +void CShaderAPIDx8::OverrideAlphaWriteEnable( bool bOverrideEnable, bool bAlphaWriteEnable ) +{ + LOCK_SHADERAPI(); + if ( !g_pShaderDeviceDx8->IsDeactivated() ) + { + m_TransitionTable.OverrideAlphaWriteEnable( bOverrideEnable, bAlphaWriteEnable ); + } +} + +void CShaderAPIDx8::OverrideColorWriteEnable( bool bOverrideEnable, bool bColorWriteEnable ) +{ + LOCK_SHADERAPI(); + if ( !g_pShaderDeviceDx8->IsDeactivated() ) + { + m_TransitionTable.OverrideColorWriteEnable( bOverrideEnable, bColorWriteEnable ); + } +} + +void CShaderAPIDx8::UpdateFastClipUserClipPlane( void ) +{ + float plane[4]; + switch( m_DynamicState.m_HeightClipMode ) + { + case MATERIAL_HEIGHTCLIPMODE_DISABLE: + EnableFastClip( false ); + break; + case MATERIAL_HEIGHTCLIPMODE_RENDER_ABOVE_HEIGHT: + plane[0] = 0.0f; + plane[1] = 0.0f; + plane[2] = 1.0f; + plane[3] = m_DynamicState.m_HeightClipZ; + EnableFastClip( true ); + SetFastClipPlane(plane); + break; + case MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT: + plane[0] = 0.0f; + plane[1] = 0.0f; + plane[2] = -1.0f; + plane[3] = -m_DynamicState.m_HeightClipZ; + EnableFastClip( true ); + SetFastClipPlane(plane); + break; + } +} + +void CShaderAPIDx8::SetHeightClipZ( float z ) +{ + LOCK_SHADERAPI(); + if( z != m_DynamicState.m_HeightClipZ ) + { + FlushBufferedPrimitives(); + m_DynamicState.m_HeightClipZ = z; + UpdateVertexShaderFogParams(); + UpdateFastClipUserClipPlane(); + m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |= + STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION; + } +} + +void CShaderAPIDx8::SetHeightClipMode( MaterialHeightClipMode_t heightClipMode ) +{ + LOCK_SHADERAPI(); + if( heightClipMode != m_DynamicState.m_HeightClipMode ) + { + FlushBufferedPrimitives(); + m_DynamicState.m_HeightClipMode = heightClipMode; + UpdateVertexShaderFogParams(); + UpdateFastClipUserClipPlane(); + m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |= + STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION; + } +} + +void CShaderAPIDx8::SetClipPlane( int index, const float *pPlane ) +{ + LOCK_SHADERAPI(); + Assert( index < g_pHardwareConfig->MaxUserClipPlanes() && index >= 0 ); + + // NOTE: The plane here is specified in *world space* + // NOTE: This is done because they assume Ax+By+Cz+Dw = 0 (where w = 1 in real space) + // while we use Ax+By+Cz=D + D3DXPLANE plane; + plane.a = pPlane[0]; + plane.b = pPlane[1]; + plane.c = pPlane[2]; + plane.d = -pPlane[3]; + + if ( plane != m_DynamicState.m_UserClipPlaneWorld[index] ) + { + FlushBufferedPrimitives(); + + m_DynamicState.m_UserClipPlaneChanged |= ( 1 << index ); + m_DynamicState.m_UserClipPlaneWorld[index] = plane; + } +} + + +//----------------------------------------------------------------------------- +// Converts a D3DXMatrix to a VMatrix and back +//----------------------------------------------------------------------------- +void CShaderAPIDx8::D3DXMatrixToVMatrix( const D3DXMATRIX &in, VMatrix &out ) +{ + MatrixTranspose( *(const VMatrix*)&in, out ); +} + +void CShaderAPIDx8::VMatrixToD3DXMatrix( const VMatrix &in, D3DXMATRIX &out ) +{ + MatrixTranspose( in, *(VMatrix*)&out ); +} + + +//----------------------------------------------------------------------------- +// Mark all user clip planes as being dirty +//----------------------------------------------------------------------------- +void CShaderAPIDx8::MarkAllUserClipPlanesDirty() +{ + m_DynamicState.m_UserClipPlaneChanged |= ( 1 << g_pHardwareConfig->MaxUserClipPlanes() ) - 1; + m_DynamicState.m_bFastClipPlaneChanged = true; +} + + +//----------------------------------------------------------------------------- +// User clip plane override +//----------------------------------------------------------------------------- +void CShaderAPIDx8::EnableUserClipTransformOverride( bool bEnable ) +{ + LOCK_SHADERAPI(); + if ( m_DynamicState.m_bUserClipTransformOverride != bEnable ) + { + FlushBufferedPrimitives(); + m_DynamicState.m_bUserClipTransformOverride = bEnable; + MarkAllUserClipPlanesDirty(); + } +} + + +//----------------------------------------------------------------------------- +// Specify user clip transform +//----------------------------------------------------------------------------- +void CShaderAPIDx8::UserClipTransform( const VMatrix &worldToProjection ) +{ + LOCK_SHADERAPI(); + D3DXMATRIX dxWorldToProjection; + VMatrixToD3DXMatrix( worldToProjection, dxWorldToProjection ); + + if ( m_DynamicState.m_UserClipTransform != dxWorldToProjection ) + { + m_DynamicState.m_UserClipTransform = dxWorldToProjection; + if ( m_DynamicState.m_bUserClipTransformOverride ) + { + FlushBufferedPrimitives(); + MarkAllUserClipPlanesDirty(); + } + } +} + + +//----------------------------------------------------------------------------- +// Enables a user clip plane +//----------------------------------------------------------------------------- +void CShaderAPIDx8::EnableClipPlane( int index, bool bEnable ) +{ + LOCK_SHADERAPI(); + Assert( index < g_pHardwareConfig->MaxUserClipPlanes() && index >= 0 ); + if( ( m_DynamicState.m_UserClipPlaneEnabled & ( 1 << index ) ? true : false ) != bEnable ) + { + FlushBufferedPrimitives(); + if( bEnable ) + { + m_DynamicState.m_UserClipPlaneEnabled |= ( 1 << index ); + } + else + { + m_DynamicState.m_UserClipPlaneEnabled &= ~( 1 << index ); + } + SetRenderStateConstMacro( this, D3DRS_CLIPPLANEENABLE, m_DynamicState.m_UserClipPlaneEnabled ); + } +} + +//----------------------------------------------------------------------------- +// Recomputes the fast-clip plane matrices based on the current fast-clip plane +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CommitFastClipPlane( ) +{ + // Don't bother recomputing if unchanged or disabled + if ( !m_DynamicState.m_bFastClipPlaneChanged || !m_DynamicState.m_FastClipEnabled ) + return; + + m_DynamicState.m_bFastClipPlaneChanged = false; + + D3DXMatrixIdentity( &m_CachedFastClipProjectionMatrix ); + + // Compute worldToProjection - need inv. transpose for transforming plane. + D3DXMATRIX viewToProjInvTrans, viewToProjInv, viewToProj = GetTransform(MATERIAL_PROJECTION); + viewToProj._43 *= 0.5f; // pull in zNear because the shear in effect + // moves it out: clipping artifacts when looking down at water + // could occur if this multiply is not done + + D3DXMATRIX worldToViewInvTrans, worldToViewInv, worldToView = GetUserClipTransform(); + + D3DXMatrixInverse( &worldToViewInv, NULL, &worldToView ); + D3DXMatrixTranspose( &worldToViewInvTrans, &worldToViewInv ); + + D3DXMatrixInverse( &viewToProjInv, NULL, &viewToProj ); + D3DXMatrixTranspose( &viewToProjInvTrans, &viewToProjInv ); + + D3DXPLANE plane; + D3DXPlaneNormalize( &plane, &m_DynamicState.m_FastClipPlane ); + D3DXVECTOR4 clipPlane( plane.a, plane.b, plane.c, plane.d ); + + // transform clip plane into view space + D3DXVec4Transform( &clipPlane, &clipPlane, &worldToViewInvTrans ); + + // transform clip plane into projection space + D3DXVec4Transform( &clipPlane, &clipPlane, &viewToProjInvTrans ); + +#define ALLOW_FOR_FASTCLIPDUMPS 0 + +#if (ALLOW_FOR_FASTCLIPDUMPS == 1) + static ConVar shader_dumpfastclipprojectioncoords( "shader_dumpfastclipprojectioncoords", "0", 0, "dump fast clip projected matrix" ); + if( shader_dumpfastclipprojectioncoords.GetBool() ) + DevMsg( "Fast clip plane projected coordinates: %f %f %f %f", clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w ); +#endif + + if( (clipPlane.z * clipPlane.w) <= -0.4f ) // a plane with (z*w) > -0.4 at this point is behind the camera and will cause graphical glitches. Toss it. (0.4 found through experimentation) + { +#if (ALLOW_FOR_FASTCLIPDUMPS == 1) + if( shader_dumpfastclipprojectioncoords.GetBool() ) + DevMsg( " %f %f %f %f\n", clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w ); +#endif + + D3DXVec4Normalize( &clipPlane, &clipPlane ); + + //if ((fabs(clipPlane.z) > 0.01) && (fabs(clipPlane.w) > 0.01f)) + { + // put projection space clip plane in Z column + m_CachedFastClipProjectionMatrix._13 = clipPlane.x; + m_CachedFastClipProjectionMatrix._23 = clipPlane.y; + m_CachedFastClipProjectionMatrix._33 = clipPlane.z; + m_CachedFastClipProjectionMatrix._43 = clipPlane.w; + } + } +#if (ALLOW_FOR_FASTCLIPDUMPS == 1) + else + { + if( shader_dumpfastclipprojectioncoords.GetBool() ) + DevMsg( "\n" ); //finish off the line above + } +#endif + + m_CachedFastClipProjectionMatrix = viewToProj * m_CachedFastClipProjectionMatrix; + + // Update the cached polyoffset matrix (with clip) too: + ComputePolyOffsetMatrix( m_CachedFastClipProjectionMatrix, m_CachedFastClipPolyOffsetProjectionMatrix ); +} + +//----------------------------------------------------------------------------- +// Sets the fast-clip plane; but doesn't update the matrices +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetFastClipPlane( const float *pPlane ) +{ + LOCK_SHADERAPI(); + D3DXPLANE plane; + plane.a = pPlane[0]; + plane.b = pPlane[1]; + plane.c = pPlane[2]; + plane.d = -pPlane[3]; + if ( plane != m_DynamicState.m_FastClipPlane ) + { + FlushBufferedPrimitives(); + UpdateVertexShaderFogParams(); + + m_DynamicState.m_FastClipPlane = plane; + + // Mark a dirty bit so when it comes time to commit view + projection transforms, + // we also update the fast clip matrices + m_DynamicState.m_bFastClipPlaneChanged = true; + + m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |= + STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION; + } +} + + +//----------------------------------------------------------------------------- +// Enables/disables fast-clip mode +//----------------------------------------------------------------------------- +void CShaderAPIDx8::EnableFastClip( bool bEnable ) +{ + LOCK_SHADERAPI(); + if( m_DynamicState.m_FastClipEnabled != bEnable ) + { + FlushBufferedPrimitives(); + UpdateVertexShaderFogParams(); + + m_DynamicState.m_FastClipEnabled = bEnable; + + m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |= + STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION; + } +} + +/* +// ----------------------------------------------------------------------------- +// SetInvariantClipVolume - This routine takes six planes as input and sets the +// appropriate Direct3D user clip plane state +// What we mean by "invariant clipping" here is that certain devices implement +// user clip planes at the raster level, which means that multi-pass rendering +// where one pass is unclipped (such as base geometry) and another pass *IS* +// clipped (such as flashlight geometry), there is no z-fighting since the +// clipping is implemented at the raster level in an "invariant" way +// ----------------------------------------------------------------------------- +void CShaderAPIDx8::SetInvariantClipVolume( Frustum_t *pFrustumPlanes ) +{ + // Only do this on modern nVidia hardware, which does invariant clipping + if ( m_VendorID == VENDORID_NVIDIA ) + { + if ( pFrustumPlanes ) + { +// if () +// { +// +// } + + for (int i=0; i<6; i++) + { + const cplane_t *pPlane = pFrustumPlanes->GetPlane(i); + + SetClipPlane( i, (float *) &pPlane->normal ); + EnableClipPlane( i, true ); + +// FRUSTUM_RIGHT = 0, +// FRUSTUM_LEFT = 1, +// FRUSTUM_TOP = 2, +// FRUSTUM_BOTTOM = 3, +// FRUSTUM_NEARZ = 4, +// FRUSTUM_FARZ = 5, + + } + } + else // NULL disables the invariant clip volume... + { + for (int i=0; i<6; i++) + { + EnableClipPlane( i, false ); + } + } + } +} +*/ + +//----------------------------------------------------------------------------- +// Vertex blending +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetVertexBlendState( int numBones ) +{ + if (numBones < 0) + { + numBones = m_DynamicState.m_NumBones; + } + + // For fixed-function, the number of weights is actually one less than + // the number of bones + if (numBones > 0) + --numBones; + + bool normalizeNormals = true; + D3DVERTEXBLENDFLAGS vertexBlend; + switch(numBones) + { + case 0: + vertexBlend = D3DVBF_DISABLE; + normalizeNormals = false; + break; + + case 1: + vertexBlend = D3DVBF_1WEIGHTS; + break; + + case 2: + vertexBlend = D3DVBF_2WEIGHTS; + break; + + case 3: + vertexBlend = D3DVBF_3WEIGHTS; + break; + + default: + vertexBlend = D3DVBF_DISABLE; + Assert(0); + break; + } + + if (m_DynamicState.m_VertexBlend != vertexBlend) + { + m_DynamicState.m_VertexBlend = vertexBlend; + SetSupportedRenderState( D3DRS_VERTEXBLEND, vertexBlend ); + } + + // Activate normalize normals when skinning is on + if (m_DynamicState.m_NormalizeNormals != normalizeNormals) + { + m_DynamicState.m_NormalizeNormals = normalizeNormals; + SetSupportedRenderState( D3DRS_NORMALIZENORMALS, normalizeNormals ); + } +} + + +//----------------------------------------------------------------------------- +// Vertex blending +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetNumBoneWeights( int numBones ) +{ + LOCK_SHADERAPI(); + if (m_DynamicState.m_NumBones != numBones) + { + FlushBufferedPrimitives(); + m_DynamicState.m_NumBones = numBones; + + if ( m_TransitionTable.CurrentShadowState() ) + { + SetVertexBlendState( m_TransitionTable.CurrentShadowState()->m_VertexBlendEnable ? -1 : 0 ); + } + } +} + +void CShaderAPIDx8::EnableHWMorphing( bool bEnable ) +{ + LOCK_SHADERAPI(); + if ( m_DynamicState.m_bHWMorphingEnabled != bEnable ) + { + FlushBufferedPrimitives(); + m_DynamicState.m_bHWMorphingEnabled = bEnable; + } +} + +void CShaderAPIDx8::EnabledSRGBWrite( bool bEnabled ) +{ + m_DynamicState.m_bSRGBWritesEnabled = bEnabled; + + if ( g_pHardwareConfig->SupportsPixelShaders_2_b() ) + { + UpdatePixelFogColorConstant(); + + if ( bEnabled && g_pHardwareConfig->NeedsShaderSRGBConversion() ) + BindTexture( SHADER_SAMPLER15, m_hLinearToGammaTableTexture ); + else + BindTexture( SHADER_SAMPLER15, m_hLinearToGammaTableIdentityTexture ); + } +} + +#if defined( _X360 ) +void CShaderAPIDx8::ApplySRGBReadState( int iTextureStage, bool bSRGBReadEnabled ) +{ + Sampler_t sampler = (Sampler_t)iTextureStage; + SamplerState_t &samplerState = SamplerState( sampler ); + samplerState.m_SRGBReadEnable = bSRGBReadEnabled; + + if ( ( samplerState.m_BoundTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) || !samplerState.m_TextureEnable ) + { + return; + } + + IDirect3DBaseTexture *pBindTexture = CShaderAPIDx8::GetD3DTexture( samplerState.m_BoundTexture ); + if ( !pBindTexture ) + { + return; + } + + DWORD linearFormatBackup = pBindTexture->Format.dword[0]; //if we convert to srgb format, we need the original format for reverting. We only need the first DWORD of GPUTEXTURE_FETCH_CONSTANT. + if ( bSRGBReadEnabled ) + { + pBindTexture->Format.SignX = pBindTexture->Format.SignY = pBindTexture->Format.SignZ = 3; //convert to srgb format for the bind. This effectively emulates the old srgb read sampler state + } + + Dx9Device()->SetTexture( sampler, pBindTexture ); + + // copy back the format in case we changed it + pBindTexture->Format.dword[0] = linearFormatBackup; +} +#endif + +//----------------------------------------------------------------------------- +// Fog methods... +//----------------------------------------------------------------------------- +void CShaderAPIDx8::UpdatePixelFogColorConstant( void ) +{ + Assert( HardwareConfig()->SupportsPixelShaders_2_b() ); + float fogColor[4]; + + switch( GetPixelFogMode() ) + { + case MATERIAL_FOG_NONE: + { + for( int i = 0; i != 3; ++i ) + fogColor[i] = 0.0f; + } + break; + + case MATERIAL_FOG_LINEAR: + { + //setup the fog for mixing linear fog in the pixel shader so that it emulates ff range fog + for( int i = 0; i != 3; ++i ) + fogColor[i] = m_DynamicState.m_PixelFogColor[i]; + + if( m_DynamicState.m_bSRGBWritesEnabled ) + { + //since the fog color will assuredly get converted from linear to gamma, we should probably convert it from gamma to linear + for( int i = 0; i != 3; ++i ) + fogColor[i] = GammaToLinear_HardwareSpecific( fogColor[i] ); + } + + if( (!m_DynamicState.m_bFogGammaCorrectionDisabled) && (g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER) ) + { + for( int i = 0; i != 3; ++i ) + fogColor[i] *= m_ToneMappingScale.x; // Linear + } + } + break; + + case MATERIAL_FOG_LINEAR_BELOW_FOG_Z: + { + //water fog has been around a while and has never tonemap scaled, and has always been in linear space + if( g_pHardwareConfig->NeedsShaderSRGBConversion() ) + { + //srgb in ps2b uses the 2.2 curve + for( int i = 0; i != 3; ++i ) + fogColor[i] = pow( m_DynamicState.m_PixelFogColor[i], 2.2f ); + } + else + { + for( int i = 0; i != 3; ++i ) + fogColor[i] = GammaToLinear_HardwareSpecific( m_DynamicState.m_PixelFogColor[i] ); //this is how water fog color has always been setup in the past + } + + if( (!m_DynamicState.m_bFogGammaCorrectionDisabled) && (g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER) ) + { + for( int i = 0; i != 3; ++i ) + fogColor[i] *= m_ToneMappingScale.x; // Linear + } + } + break; + + NO_DEFAULT; + }; + + fogColor[3] = 1.0f / m_DynamicState.m_DestAlphaDepthRange; + + SetPixelShaderConstant( LINEAR_FOG_COLOR, fogColor ); +} + + +void CShaderAPIDx8::ApplyFogMode( ShaderFogMode_t fogMode, bool bSRGBWritesEnabled, bool bDisableFogGammaCorrection ) +{ + HDRType_t hdrType = g_pHardwareConfig->GetHDRType(); + + if ( fogMode == SHADER_FOGMODE_DISABLED ) + { + if( hdrType != HDR_TYPE_FLOAT ) + { + FogMode( MATERIAL_FOG_NONE ); + } + + if( m_DelayedShaderConstants.iPixelShaderFogParams != -1 ) + SetPixelShaderFogParams( m_DelayedShaderConstants.iPixelShaderFogParams, fogMode ); + + return; + } + + bool bShouldGammaCorrect = true; // By default, we'll gamma correct. + unsigned char r = 0, g = 0, b = 0; // Black fog + + + if( hdrType != HDR_TYPE_FLOAT ) + { + FogMode( m_SceneFogMode ); + } + + if( m_DelayedShaderConstants.iPixelShaderFogParams != -1 ) + SetPixelShaderFogParams( m_DelayedShaderConstants.iPixelShaderFogParams, fogMode ); + + switch( fogMode ) + { + case SHADER_FOGMODE_BLACK: // Additive decals + bShouldGammaCorrect = false; + break; + case SHADER_FOGMODE_OO_OVERBRIGHT: + case SHADER_FOGMODE_GREY: // Mod2x decals + r = g = b = 128; + break; + case SHADER_FOGMODE_WHITE: // Multiplicative decals + r = g = b = 255; + bShouldGammaCorrect = false; + break; + case SHADER_FOGMODE_FOGCOLOR: + GetSceneFogColor( &r, &g, &b ); // Scene fog color + break; + NO_DEFAULT + } + + bShouldGammaCorrect &= !bDisableFogGammaCorrection; + m_DynamicState.m_bFogGammaCorrectionDisabled = !bShouldGammaCorrect; + + D3DCOLOR color; + if ( bShouldGammaCorrect ) + { + color = ComputeGammaCorrectedFogColor( r, g, b, bSRGBWritesEnabled ); + } + else + { + color = D3DCOLOR_ARGB( 255, r, g, b ); + } + + + const float fColorScale = 1.0f / 255.0f; + m_DynamicState.m_PixelFogColor[0] = (float)(r) * fColorScale; + m_DynamicState.m_PixelFogColor[1] = (float)(g) * fColorScale; + m_DynamicState.m_PixelFogColor[2] = (float)(b) * fColorScale; + + if( g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_b() ) + { + UpdatePixelFogColorConstant(); + } + + if ( color != m_DynamicState.m_FogColor ) + { + if( hdrType != HDR_TYPE_FLOAT ) + { + m_DynamicState.m_FogColor = color; + SetRenderStateConstMacro( this, D3DRS_FOGCOLOR, m_DynamicState.m_FogColor ); + } + } +} + +void CShaderAPIDx8::SceneFogMode( MaterialFogMode_t fogMode ) +{ + LOCK_SHADERAPI(); + if( m_SceneFogMode != fogMode ) + { + FlushBufferedPrimitives(); + m_SceneFogMode = fogMode; + + if ( m_TransitionTable.CurrentShadowState() ) + { + // Get the shadow state in sync since it depends on SceneFogMode. + ApplyFogMode( m_TransitionTable.CurrentShadowState()->m_FogMode, m_TransitionTable.CurrentShadowState()->m_SRGBWriteEnable, m_TransitionTable.CurrentShadowState()->m_bDisableFogGammaCorrection ); + } + } +} + +MaterialFogMode_t CShaderAPIDx8::GetSceneFogMode() +{ + return m_SceneFogMode; +} + +MaterialFogMode_t CShaderAPIDx8::GetPixelFogMode() +{ + if( ShouldUsePixelFogForMode( m_SceneFogMode ) ) + return m_SceneFogMode; + else + return MATERIAL_FOG_NONE; +} + +int CShaderAPIDx8::GetPixelFogCombo( void ) +{ + if( (m_SceneFogMode != MATERIAL_FOG_NONE) && ShouldUsePixelFogForMode( m_SceneFogMode ) ) + return m_SceneFogMode - 1; //PIXELFOGTYPE dynamic combos are shifted down one. MATERIAL_FOG_NONE is simulated by range fog with rigged parameters. Gets rid of a dynamic combo across the ps2x set + else + return MATERIAL_FOG_NONE; +} + + +static ConVar r_pixelfog( "r_pixelfog", "1" ); + +bool CShaderAPIDx8::ShouldUsePixelFogForMode( MaterialFogMode_t fogMode ) +{ + if( fogMode == MATERIAL_FOG_NONE ) + return false; + + if( IsX360() || IsPosix() ) // Always use pixel fog on X360 and Posix + return true; + + if( g_pHardwareConfig->Caps().m_nDXSupportLevel < 90 ) //pixel fog not available until at least ps2.0 + return false; + + switch( m_SceneFogMode ) + { + case MATERIAL_FOG_LINEAR: + return (g_pHardwareConfig->SupportsPixelShaders_2_b() && //lightmappedgeneric_ps20.fxc can't handle the instruction count + r_pixelfog.GetBool()); //use pixel fog if preferred + + case MATERIAL_FOG_LINEAR_BELOW_FOG_Z: + return true; + }; + + return false; +} + +//----------------------------------------------------------------------------- +// Fog methods... +//----------------------------------------------------------------------------- +void CShaderAPIDx8::FogMode( MaterialFogMode_t fogMode ) +{ + bool bFogEnable; + + if ( IsX360() ) + { + // FF fog not applicable on 360 + return; + } + + m_DynamicState.m_SceneFog = fogMode; + switch( fogMode ) + { + default: + Assert( 0 ); + // fall through + + case MATERIAL_FOG_NONE: + bFogEnable = false; + break; + + case MATERIAL_FOG_LINEAR: + // use vertex fog to achieve linear range fog + bFogEnable = true; + break; + + case MATERIAL_FOG_LINEAR_BELOW_FOG_Z: + // use pixel fog on 9.0 and greater for height fog + bFogEnable = g_pHardwareConfig->Caps().m_nDXSupportLevel < 90; + break; + } + + if( ShouldUsePixelFogForMode( fogMode ) ) + { + bFogEnable = false; //disable FF fog when doing fog in the pixel shader + } + +#if 0 + // HACK - do this to disable fog always + bFogEnable = false; + m_DynamicState.m_SceneFog = MATERIAL_FOG_NONE; +#endif + + // These two are always set to this, so don't bother setting them. + // We are always using vertex fog. +// SetRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE ); +// SetRenderState( D3DRS_RANGEFOGENABLE, false ); + + // Set fog enable if it's different than before. + if ( bFogEnable != m_DynamicState.m_FogEnable ) + { + SetSupportedRenderState( D3DRS_FOGENABLE, bFogEnable ); + m_DynamicState.m_FogEnable = bFogEnable; + } +} + + +void CShaderAPIDx8::FogStart( float fStart ) +{ + LOCK_SHADERAPI(); + if (fStart != m_DynamicState.m_FogStart) + { + // need to flush the dynamic buffer + FlushBufferedPrimitives(); + + SetRenderStateConstMacro( this, D3DRS_FOGSTART, *((DWORD*)(&fStart))); + m_VertexShaderFogParams[0] = fStart; + UpdateVertexShaderFogParams(); + m_DynamicState.m_FogStart = fStart; + } +} + +void CShaderAPIDx8::FogEnd( float fEnd ) +{ + LOCK_SHADERAPI(); + if (fEnd != m_DynamicState.m_FogEnd) + { + // need to flush the dynamic buffer + FlushBufferedPrimitives(); + + SetRenderStateConstMacro( this, D3DRS_FOGEND, *((DWORD*)(&fEnd))); + m_VertexShaderFogParams[1] = fEnd; + UpdateVertexShaderFogParams(); + m_DynamicState.m_FogEnd = fEnd; + } +} + +void CShaderAPIDx8::SetFogZ( float fogZ ) +{ + LOCK_SHADERAPI(); + if (fogZ != m_DynamicState.m_FogZ) + { + // need to flush the dynamic buffer + FlushBufferedPrimitives(); + m_DynamicState.m_FogZ = fogZ; + UpdateVertexShaderFogParams(); + } +} + +void CShaderAPIDx8::FogMaxDensity( float flMaxDensity ) +{ + LOCK_SHADERAPI(); + if (flMaxDensity != m_DynamicState.m_FogMaxDensity) + { + // need to flush the dynamic buffer + FlushBufferedPrimitives(); + +// SetRenderState(D3DRS_FOGDENSITY, *((DWORD*)(&flMaxDensity))); // ??? do I need to to this ??? + m_flFogMaxDensity = flMaxDensity; + UpdateVertexShaderFogParams(); + m_DynamicState.m_FogMaxDensity = flMaxDensity; + } +} + +void CShaderAPIDx8::GetFogDistances( float *fStart, float *fEnd, float *fFogZ ) +{ + LOCK_SHADERAPI(); + if( fStart ) + *fStart = m_DynamicState.m_FogStart; + + if( fEnd ) + *fEnd = m_DynamicState.m_FogEnd; + + if( fFogZ ) + *fFogZ = m_DynamicState.m_FogZ; +} + +void CShaderAPIDx8::SceneFogColor3ub( unsigned char r, unsigned char g, unsigned char b ) +{ + LOCK_SHADERAPI(); + if( m_SceneFogColor[0] != r || m_SceneFogColor[1] != g || m_SceneFogColor[2] != b ) + { + FlushBufferedPrimitives(); + m_SceneFogColor[0] = r; + m_SceneFogColor[1] = g; + m_SceneFogColor[2] = b; + + if ( m_TransitionTable.CurrentShadowState() ) + { + ApplyFogMode( m_TransitionTable.CurrentShadowState()->m_FogMode, m_TransitionTable.CurrentShadowState()->m_SRGBWriteEnable, m_TransitionTable.CurrentShadowState()->m_bDisableFogGammaCorrection ); + } + } +} + +void CShaderAPIDx8::GetSceneFogColor( unsigned char *rgb ) +{ + LOCK_SHADERAPI(); + rgb[0] = m_SceneFogColor[0]; + rgb[1] = m_SceneFogColor[1]; + rgb[2] = m_SceneFogColor[2]; +} + +void CShaderAPIDx8::GetSceneFogColor( unsigned char *r, unsigned char *g, unsigned char *b ) +{ + *r = m_SceneFogColor[0]; + *g = m_SceneFogColor[1]; + *b = m_SceneFogColor[2]; +} + + +//----------------------------------------------------------------------------- +// Gamma correction of fog color, or not... +//----------------------------------------------------------------------------- +D3DCOLOR CShaderAPIDx8::ComputeGammaCorrectedFogColor( unsigned char r, unsigned char g, unsigned char b, bool bSRGBWritesEnabled ) +{ +#ifdef _DEBUG + if( g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT && !bSRGBWritesEnabled ) + { +// Assert( 0 ); + } +#endif + bool bLinearSpace = g_pHardwareConfig->Caps().m_bFogColorAlwaysLinearSpace || + ( bSRGBWritesEnabled && ( g_pHardwareConfig->Caps().m_bFogColorSpecifiedInLinearSpace || g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT ) ); + + bool bScaleFogByToneMappingScale = g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER; + + float fr = ( r / 255.0f ); + float fg = ( g / 255.0f ); + float fb = ( b / 255.0f ); + if ( bLinearSpace ) + { + fr = GammaToLinear( fr ); + fg = GammaToLinear( fg ); + fb = GammaToLinear( fb ); + if ( bScaleFogByToneMappingScale ) + { + fr *= m_ToneMappingScale.x; // + fg *= m_ToneMappingScale.x; // Linear + fb *= m_ToneMappingScale.x; // + } + } + else if ( bScaleFogByToneMappingScale ) + { + fr *= m_ToneMappingScale.w; // + fg *= m_ToneMappingScale.w; // Gamma + fb *= m_ToneMappingScale.w; // + } + + fr = min( fr, 1.0f ); + fg = min( fg, 1.0f ); + fb = min( fb, 1.0f ); + r = (int)( fr * 255 ); + g = (int)( fg * 255 ); + b = (int)( fb * 255 ); + return D3DCOLOR_ARGB( 255, r, g, b ); +} + + +//----------------------------------------------------------------------------- +// Some methods chaining vertex + pixel shaders through to the shader manager +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetVertexShaderIndex( int vshIndex ) +{ + ShaderManager()->SetVertexShaderIndex( vshIndex ); +} + +void CShaderAPIDx8::SetPixelShaderIndex( int pshIndex ) +{ + ShaderManager()->SetPixelShaderIndex( pshIndex ); +} + +void CShaderAPIDx8::SyncToken( const char *pToken ) +{ + LOCK_SHADERAPI(); + RECORD_COMMAND( DX8_SYNC_TOKEN, 1 ); + RECORD_STRING( pToken ); +} + + +//----------------------------------------------------------------------------- +// Deals with vertex buffers +//----------------------------------------------------------------------------- +void CShaderAPIDx8::DestroyVertexBuffers( bool bExitingLevel ) +{ + LOCK_SHADERAPI(); + MeshMgr()->DestroyVertexBuffers( ); + // After a map is shut down, we switch to using smaller dynamic VBs + // (VGUI shouldn't need much), so that we have more memory free during map loading + m_nDynamicVBSize = bExitingLevel ? DYNAMIC_VERTEX_BUFFER_MEMORY_SMALL : DYNAMIC_VERTEX_BUFFER_MEMORY; +} + +int CShaderAPIDx8::GetCurrentDynamicVBSize( void ) +{ + return m_nDynamicVBSize; +} + +FORCEINLINE void CShaderAPIDx8::SetVertexShaderConstantInternal( int var, float const* pVec, int numVecs, bool bForce ) +{ + Assert( pVec ); + + // DX8 asm shaders use a constant mapping which has transforms and vertex shader + // specific constants shifted down by 10 constants (two 5-constant light structures) + if ( IsPC() ) + { + if ( (g_pHardwareConfig->Caps().m_nDXSupportLevel < 90) && (var >= VERTEX_SHADER_MODULATION_COLOR) ) + { + var -= 10; + } + Assert( var + numVecs <= g_pHardwareConfig->NumVertexShaderConstants() ); + + if ( !bForce ) + { + int skip = 0; + numVecs = AdjustUpdateRange( pVec, &m_DesiredState.m_pVectorVertexShaderConstant[var], numVecs, &skip ); + if ( !numVecs ) + return; + var += skip; + pVec += skip * 4; + } + Dx9Device()->SetVertexShaderConstantF( var, pVec, numVecs ); + memcpy( &m_DynamicState.m_pVectorVertexShaderConstant[var], pVec, numVecs * 4 * sizeof(float) ); + } + else + { + Assert( var + numVecs <= g_pHardwareConfig->NumVertexShaderConstants() ); + } + + memcpy( &m_DesiredState.m_pVectorVertexShaderConstant[var], pVec, numVecs * 4 * sizeof(float) ); + + if ( IsX360() ) + { + m_MaxVectorVertexShaderConstant = max( m_MaxVectorVertexShaderConstant, var + numVecs ); + } +} + + +//----------------------------------------------------------------------------- +// Sets the constant register for vertex and pixel shaders +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetVertexShaderConstant( int var, float const* pVec, int numVecs, bool bForce ) +{ + SetVertexShaderConstantInternal( var, pVec, numVecs, bForce ); +} + +//----------------------------------------------------------------------------- +// Sets the boolean registers for vertex shader control flow +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetBooleanVertexShaderConstant( int var, int const* pVec, int numBools, bool bForce ) +{ + Assert( pVec ); + Assert( var + numBools <= g_pHardwareConfig->NumBooleanVertexShaderConstants() ); + + if ( IsPC() && g_pHardwareConfig->GetDXSupportLevel() < 90 ) + { + return; + } + + if ( IsPC() && !bForce && memcmp( pVec, &m_DesiredState.m_pBooleanVertexShaderConstant[var], numBools * sizeof( BOOL ) ) == 0 ) + { + return; + } + + if ( IsPC() ) + { + Dx9Device()->SetVertexShaderConstantB( var, pVec, numBools ); + memcpy( &m_DynamicState.m_pBooleanVertexShaderConstant[var], pVec, numBools * sizeof(BOOL) ); + } + + memcpy( &m_DesiredState.m_pBooleanVertexShaderConstant[var], pVec, numBools * sizeof(BOOL) ); + + if ( IsX360() && var + numBools > m_MaxBooleanVertexShaderConstant ) + { + m_MaxBooleanVertexShaderConstant = var + numBools; + Assert( m_MaxBooleanVertexShaderConstant <= 16 ); + } +} + + +//----------------------------------------------------------------------------- +// Sets the integer registers for vertex shader control flow +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetIntegerVertexShaderConstant( int var, int const* pVec, int numIntVecs, bool bForce ) +{ + Assert( pVec ); + Assert( var + numIntVecs <= g_pHardwareConfig->NumIntegerVertexShaderConstants() ); + + if ( IsPC() && g_pHardwareConfig->GetDXSupportLevel() < 90 ) + { + return; + } + + if ( IsPC() && !bForce && memcmp( pVec, &m_DesiredState.m_pIntegerVertexShaderConstant[var], numIntVecs * sizeof( IntVector4D ) ) == 0 ) + { + return; + } + + if ( IsPC() ) + { + Dx9Device()->SetVertexShaderConstantI( var, pVec, numIntVecs ); + memcpy( &m_DynamicState.m_pIntegerVertexShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D) ); + } + memcpy( &m_DesiredState.m_pIntegerVertexShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D) ); + + if ( IsX360() && var + numIntVecs > m_MaxIntegerVertexShaderConstant ) + { + m_MaxIntegerVertexShaderConstant = var + numIntVecs; + Assert( m_MaxIntegerVertexShaderConstant <= 16 ); + } +} + +FORCEINLINE void CShaderAPIDx8::SetPixelShaderConstantInternal( int nStartConst, float const* pValues, int nNumConsts, bool bForce ) +{ + Assert( nStartConst + nNumConsts <= g_pHardwareConfig->NumPixelShaderConstants() ); + + if ( IsPC() ) + { + if ( ! bForce ) + { + int skip = 0; + nNumConsts = AdjustUpdateRange( pValues, &m_DesiredState.m_pVectorPixelShaderConstant[nStartConst], nNumConsts, &skip ); + if ( !nNumConsts ) + return; + nStartConst += skip; + pValues += skip * 4; + } + + Dx9Device()->SetPixelShaderConstantF( nStartConst, pValues, nNumConsts ); + memcpy( &m_DynamicState.m_pVectorPixelShaderConstant[nStartConst], pValues, nNumConsts * 4 * sizeof(float) ); + } + + memcpy( &m_DesiredState.m_pVectorPixelShaderConstant[nStartConst], pValues, nNumConsts * 4 * sizeof(float) ); + + if ( IsX360() ) + { + m_MaxVectorPixelShaderConstant = max( m_MaxVectorPixelShaderConstant, nStartConst + nNumConsts ); + Assert( m_MaxVectorPixelShaderConstant <= 32 ); + } +} + +void CShaderAPIDx8::SetPixelShaderConstant( int var, float const* pVec, int numVecs, bool bForce ) +{ + SetPixelShaderConstantInternal( var, pVec, numVecs, bForce ); +} + +template<class T> FORCEINLINE T GetData( uint8 const *pData ) +{ + return * ( reinterpret_cast< T const *>( pData ) ); +} + + + +void CShaderAPIDx8::SetStandardTextureHandle( StandardTextureId_t nId, ShaderAPITextureHandle_t nHandle ) +{ + Assert( nId < ARRAYSIZE( m_StdTextureHandles ) ); + m_StdTextureHandles[nId] = nHandle; +} + +void CShaderAPIDx8::ExecuteCommandBuffer( uint8 *pCmdBuf ) +{ + uint8 *pReturnStack[20]; + uint8 **pSP = &pReturnStack[ARRAYSIZE(pReturnStack)]; + uint8 *pLastCmd; + for(;;) + { + uint8 *pCmd=pCmdBuf; + int nCmd = GetData<int>( pCmdBuf ); + switch( nCmd ) + { + case CBCMD_END: + { + if ( pSP == &pReturnStack[ARRAYSIZE(pReturnStack)] ) + return; + else + { + // pop pc + pCmdBuf = *( pSP ++ ); + break; + } + } + + case CBCMD_JUMP: + pCmdBuf = GetData<uint8 *>( pCmdBuf + sizeof( int ) ); + break; + + case CBCMD_JSR: + { + Assert( pSP > &(pReturnStack[0] ) ); +// *(--pSP ) = pCmdBuf + sizeof( int ) + sizeof( uint8 *); +// pCmdBuf = GetData<uint8 *>( pCmdBuf + sizeof( int ) ); + ExecuteCommandBuffer( GetData<uint8 *>( pCmdBuf + sizeof( int ) ) ); + pCmdBuf = pCmdBuf + sizeof( int ) + sizeof( uint8 *); + break; + } + + case CBCMD_SET_PIXEL_SHADER_FLOAT_CONST: + { + int nStartConst = GetData<int>( pCmdBuf + sizeof( int ) ); + int nNumConsts = GetData<int>( pCmdBuf + 2 * sizeof( int ) ); + float const *pValues = reinterpret_cast< float const *> ( pCmdBuf + 3 * sizeof( int ) ); + pCmdBuf += nNumConsts * 4 * sizeof( float ) + 3 * sizeof( int ); + SetPixelShaderConstantInternal( nStartConst, pValues, nNumConsts, false ); + break; + } + + case CBCMD_SETPIXELSHADERFOGPARAMS: + { + int nReg = GetData<int>( pCmdBuf + sizeof( int ) ); + pCmdBuf += 2 * sizeof( int ); + SetPixelShaderFogParams( nReg ); // !! speed fixme + break; + } + case CBCMD_STORE_EYE_POS_IN_PSCONST: + { + int nReg = GetData<int>( pCmdBuf + sizeof( int ) ); + pCmdBuf += 2 * sizeof( int ); + SetPixelShaderConstantInternal( nReg, m_WorldSpaceCameraPositon.Base(), 1, false ); + break; + } + + case CBCMD_COMMITPIXELSHADERLIGHTING: + { + int nReg = GetData<int>( pCmdBuf + sizeof( int ) ); + pCmdBuf += 2 * sizeof( int ); + CommitPixelShaderLighting( nReg ); + break; + } + + case CBCMD_SETPIXELSHADERSTATEAMBIENTLIGHTCUBE: + { + int nReg = GetData<int>( pCmdBuf + sizeof( int ) ); + pCmdBuf += 2 * sizeof( int ); + float *pCubeBase = m_DynamicState.m_AmbientLightCube[0].Base(); + SetPixelShaderConstantInternal( nReg, pCubeBase, 6, false ); + break; + } + + case CBCMD_SETAMBIENTCUBEDYNAMICSTATEVERTEXSHADER: + { + pCmdBuf +=sizeof( int ); + SetVertexShaderStateAmbientLightCube(); + break; + } + + case CBCMD_SET_DEPTH_FEATHERING_CONST: + { + int nConst = GetData<int>( pCmdBuf + sizeof( int ) ); + float fDepthBlendScale = GetData<float>( pCmdBuf + 2 * sizeof( int ) ); + pCmdBuf += 2 * sizeof( int ) + sizeof( float ); + SetDepthFeatheringPixelShaderConstant( nConst, fDepthBlendScale ); + break; + } + + case CBCMD_SET_VERTEX_SHADER_FLOAT_CONST: + { + int nStartConst = GetData<int>( pCmdBuf + sizeof( int ) ); + int nNumConsts = GetData<int>( pCmdBuf + 2 * sizeof( int ) ); + float const *pValues = reinterpret_cast< float const *> ( pCmdBuf + 3 * sizeof( int ) ); + pCmdBuf += nNumConsts * 4 * sizeof( float ) + 3 * sizeof( int ); + SetVertexShaderConstantInternal( nStartConst, pValues, nNumConsts, false ); + break; + } + + case CBCMD_BIND_STANDARD_TEXTURE: + { + int nSampler = GetData<int>( pCmdBuf + sizeof( int ) ); + int nTextureID = GetData<int>( pCmdBuf + 2 * sizeof( int ) ); + pCmdBuf += 3 * sizeof( int ); + if ( m_StdTextureHandles[nTextureID] != INVALID_SHADERAPI_TEXTURE_HANDLE ) + { + BindTexture( (Sampler_t) nSampler, m_StdTextureHandles[nTextureID] ); + } + else + { + ShaderUtil()->BindStandardTexture( (Sampler_t) nSampler, (StandardTextureId_t ) nTextureID ); + } + break; + } + + case CBCMD_BIND_SHADERAPI_TEXTURE_HANDLE: + { + int nSampler = GetData<int>( pCmdBuf + sizeof( int ) ); + ShaderAPITextureHandle_t hTexture = GetData<ShaderAPITextureHandle_t>( pCmdBuf + 2 * sizeof( int ) ); + Assert( hTexture != INVALID_SHADERAPI_TEXTURE_HANDLE ); + pCmdBuf += 2 * sizeof( int ) + sizeof( ShaderAPITextureHandle_t ); + BindTexture( (Sampler_t) nSampler, hTexture ); + break; + } + + case CBCMD_SET_PSHINDEX: + { + int nIdx = GetData<int>( pCmdBuf + sizeof( int ) ); + ShaderManager()->SetPixelShaderIndex( nIdx ); + pCmdBuf += 2 * sizeof( int ); + break; + } + + case CBCMD_SET_VSHINDEX: + { + int nIdx = GetData<int>( pCmdBuf + sizeof( int ) ); + ShaderManager()->SetVertexShaderIndex( nIdx ); + pCmdBuf += 2 * sizeof( int ); + break; + } + + + +#ifndef NDEBUG + default: + { + Assert(0); + } +#endif + + + } + pLastCmd = pCmd; + } +} + + +//----------------------------------------------------------------------------- +// Sets the boolean registers for pixel shader control flow +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetBooleanPixelShaderConstant( int var, int const* pVec, int numBools, bool bForce ) +{ + Assert( pVec ); + Assert( var + numBools <= g_pHardwareConfig->NumBooleanPixelShaderConstants() ); + + if ( IsPC() && !bForce && memcmp( pVec, &m_DesiredState.m_pBooleanPixelShaderConstant[var], numBools * sizeof( BOOL ) ) == 0 ) + { + return; + } + + if ( IsPC() ) + { + Dx9Device()->SetPixelShaderConstantB( var, pVec, numBools ); + memcpy( &m_DynamicState.m_pBooleanPixelShaderConstant[var], pVec, numBools * sizeof(BOOL) ); + } + + memcpy( &m_DesiredState.m_pBooleanPixelShaderConstant[var], pVec, numBools * sizeof(BOOL) ); + + if ( IsX360() && var + numBools > m_MaxBooleanPixelShaderConstant ) + { + m_MaxBooleanPixelShaderConstant = var + numBools; + Assert( m_MaxBooleanPixelShaderConstant <= 16 ); + } +} + + +//----------------------------------------------------------------------------- +// Sets the integer registers for pixel shader control flow +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetIntegerPixelShaderConstant( int var, int const* pVec, int numIntVecs, bool bForce ) +{ + Assert( pVec ); + Assert( var + numIntVecs <= g_pHardwareConfig->NumIntegerPixelShaderConstants() ); + + if ( IsPC() && !bForce && memcmp( pVec, &m_DesiredState.m_pIntegerPixelShaderConstant[var], numIntVecs * sizeof( IntVector4D ) ) == 0 ) + { + return; + } + + if ( IsPC() ) + { + Dx9Device()->SetPixelShaderConstantI( var, pVec, numIntVecs ); + memcpy( &m_DynamicState.m_pIntegerPixelShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D) ); + } + + memcpy( &m_DesiredState.m_pIntegerPixelShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D) ); + + if ( IsX360() && var + numIntVecs > m_MaxIntegerPixelShaderConstant ) + { + m_MaxIntegerPixelShaderConstant = var + numIntVecs; + Assert( m_MaxBooleanPixelShaderConstant <= 16 ); + } +} + + +void CShaderAPIDx8::InvalidateDelayedShaderConstants( void ) +{ + m_DelayedShaderConstants.Invalidate(); +} + +//----------------------------------------------------------------------------- +// +// Methods dealing with texture stage state +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Gets the texture associated with a texture state... +//----------------------------------------------------------------------------- + +inline IDirect3DBaseTexture* CShaderAPIDx8::GetD3DTexture( ShaderAPITextureHandle_t hTexture ) +{ + if ( hTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + { + return NULL; + } + + AssertValidTextureHandle( hTexture ); + + Texture_t& tex = GetTexture( hTexture ); + if ( tex.m_NumCopies == 1 ) + { + return tex.GetTexture(); + } + else + { + return tex.GetTexture( tex.m_CurrentCopy ); + } +} + +//----------------------------------------------------------------------------- +// Inline methods +//----------------------------------------------------------------------------- +inline ShaderAPITextureHandle_t CShaderAPIDx8::GetModifyTextureHandle() const +{ + return m_ModifyTextureHandle; +} + +inline IDirect3DBaseTexture* CShaderAPIDx8::GetModifyTexture() +{ + return CShaderAPIDx8::GetD3DTexture( m_ModifyTextureHandle ); +} + +void CShaderAPIDx8::SetModifyTexture( IDirect3DBaseTexture* pTex ) +{ + if ( m_ModifyTextureHandle == INVALID_SHADERAPI_TEXTURE_HANDLE ) + return; + + Texture_t& tex = GetTexture( m_ModifyTextureHandle ); + if ( tex.m_NumCopies == 1 ) + { + tex.SetTexture( pTex ); + } + else + { + tex.SetTexture( tex.m_CurrentCopy, pTex ); + } +} + +inline ShaderAPITextureHandle_t CShaderAPIDx8::GetBoundTextureBindId( Sampler_t sampler ) const +{ + return SamplerState( sampler ).m_BoundTexture; +} + +inline bool CShaderAPIDx8::WouldBeOverTextureLimit( ShaderAPITextureHandle_t hTexture ) +{ + if ( IsPC() ) + { + if ( mat_texture_limit.GetInt() < 0 ) + return false; + + Texture_t &tex = GetTexture( hTexture ); + if ( tex.m_LastBoundFrame == m_CurrentFrame ) + return false; + + return m_nTextureMemoryUsedLastFrame + tex.GetMemUsage() > (mat_texture_limit.GetInt() * 1024); + } + return false; +} + + +#define SETSAMPLESTATEANDMIRROR( sampler, samplerState, state_type, mirror_field, value ) \ + if ( samplerState.mirror_field != value ) \ + { \ + samplerState.mirror_field = value; \ + ::SetSamplerState( g_pD3DDevice, sampler, state_type, value ); \ + } + +#define SETSAMPLESTATEANDMIRROR_FLOAT( sampler, samplerState, state_type, mirror_field, value ) \ + if ( samplerState.mirror_field != value ) \ + { \ + samplerState.mirror_field = value; \ + ::SetSamplerState( g_pD3DDevice, sampler, state_type, *(DWORD*)&value ); \ + } + + +//----------------------------------------------------------------------------- +// Sets state on the board related to the texture state +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetTextureState( Sampler_t sampler, ShaderAPITextureHandle_t hTexture, bool force ) +{ + // Get the dynamic texture info + SamplerState_t &samplerState = SamplerState( sampler ); + + // Set the texture state, but only if it changes + if ( ( samplerState.m_BoundTexture == hTexture ) && !force ) + return; + + // Disabling texturing + if ( hTexture == INVALID_SHADERAPI_TEXTURE_HANDLE || WouldBeOverTextureLimit( hTexture ) ) + { + Dx9Device()->SetTexture( sampler, 0 ); + return; + } + + samplerState.m_BoundTexture = hTexture; + + // Don't set this if we're disabled + if ( !samplerState.m_TextureEnable ) + return; + + RECORD_COMMAND( DX8_SET_TEXTURE, 3 ); + RECORD_INT( stage ); + RECORD_INT( hTexture ); + RECORD_INT( GetTexture( hTexture).m_CurrentCopy ); + + IDirect3DBaseTexture *pTexture = CShaderAPIDx8::GetD3DTexture( hTexture ); + +#if defined( _X360 ) + DWORD linearFormatBackup = pTexture->Format.dword[0]; + if ( samplerState.m_SRGBReadEnable ) + { + // convert to srgb format for the bind. This effectively emulates the old srgb read sampler state + pTexture->Format.SignX = + pTexture->Format.SignY = + pTexture->Format.SignZ = 3; + } +#endif + + Dx9Device()->SetTexture( sampler, pTexture ); + +#if defined( _X360 ) + // put the format back in linear space + pTexture->Format.dword[0] = linearFormatBackup; +#endif + + Texture_t &tex = GetTexture( hTexture ); + if ( tex.m_LastBoundFrame != m_CurrentFrame ) + { + tex.m_LastBoundFrame = m_CurrentFrame; + tex.m_nTimesBoundThisFrame = 0; + + if ( tex.m_pTextureGroupCounterFrame ) + { + // Update the per-frame texture group counter. + *tex.m_pTextureGroupCounterFrame += tex.GetMemUsage(); + } + + // Track memory usage. + m_nTextureMemoryUsedLastFrame += tex.GetMemUsage(); + } + + if ( !m_bDebugTexturesRendering ) + ++tex.m_nTimesBoundThisFrame; + + tex.m_nTimesBoundMax = MAX( tex.m_nTimesBoundMax, tex.m_nTimesBoundThisFrame ); + + static MaterialSystem_Config_t &materialSystemConfig = ShaderUtil()->GetConfig(); + + D3DTEXTUREFILTERTYPE minFilter = tex.m_MinFilter; + D3DTEXTUREFILTERTYPE magFilter = tex.m_MagFilter; + D3DTEXTUREFILTERTYPE mipFilter = tex.m_MipFilter; + int finestMipmapLevel = tex.m_FinestMipmapLevel; + float lodBias = tex.m_LodBias; + + if ( materialSystemConfig.bMipMapTextures == 0 ) + { + mipFilter = D3DTEXF_NONE; + } + + if ( materialSystemConfig.bFilterTextures == 0 && tex.m_NumLevels > 1 ) + { + minFilter = D3DTEXF_NONE; + magFilter = D3DTEXF_NONE; + mipFilter = D3DTEXF_POINT; + } + + D3DTEXTUREADDRESS uTexWrap = tex.m_UTexWrap; + D3DTEXTUREADDRESS vTexWrap = tex.m_VTexWrap; + D3DTEXTUREADDRESS wTexWrap = tex.m_WTexWrap; + + // For now do this the old way on OSX since the dxabstract layer doesn't support SetSamplerStates + // ###OSX### punting on OSX for now +#if DX_TO_GL_ABSTRACTION && !OSX + if ( ( samplerState.m_MinFilter != minFilter ) || ( samplerState.m_MagFilter != magFilter ) || ( samplerState.m_MipFilter != mipFilter ) || + ( samplerState.m_UTexWrap != uTexWrap ) || ( samplerState.m_VTexWrap != vTexWrap ) || ( samplerState.m_WTexWrap != wTexWrap ) || + ( samplerState.m_FinestMipmapLevel != finestMipmapLevel ) || ( samplerState.m_LodBias != lodBias ) ) + { + samplerState.m_UTexWrap = uTexWrap; + samplerState.m_VTexWrap = vTexWrap; + samplerState.m_WTexWrap = wTexWrap; + samplerState.m_MinFilter = minFilter; + samplerState.m_MagFilter = magFilter; + samplerState.m_MipFilter = mipFilter; + samplerState.m_FinestMipmapLevel = finestMipmapLevel; + samplerState.m_LodBias = lodBias; + Dx9Device()->SetSamplerStates( sampler, uTexWrap, vTexWrap, wTexWrap, minFilter, magFilter, mipFilter, finestMipmapLevel, lodBias ); + } +#else + SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_ADDRESSU, m_UTexWrap, uTexWrap ); + SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_ADDRESSV, m_VTexWrap, vTexWrap ); + SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_ADDRESSW, m_WTexWrap, wTexWrap ); + SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_MINFILTER, m_MinFilter, minFilter ); + SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_MAGFILTER, m_MagFilter, magFilter ); + SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_MIPFILTER, m_MipFilter, mipFilter ); + SETSAMPLESTATEANDMIRROR( sampler, samplerState, D3DSAMP_MAXMIPLEVEL, m_FinestMipmapLevel, finestMipmapLevel ); + SETSAMPLESTATEANDMIRROR_FLOAT( sampler, samplerState, D3DSAMP_MIPMAPLODBIAS, m_LodBias, lodBias ); + +#endif +} + +void CShaderAPIDx8::BindTexture( Sampler_t sampler, ShaderAPITextureHandle_t textureHandle ) +{ + LOCK_SHADERAPI(); + SetTextureState( sampler, textureHandle ); +} + +void CShaderAPIDx8::BindVertexTexture( VertexTextureSampler_t nStage, ShaderAPITextureHandle_t textureHandle ) +{ + Assert( g_pMaterialSystemHardwareConfig->GetVertexTextureCount() != 0 ); + LOCK_SHADERAPI(); + ADD_VERTEX_TEXTURE_FUNC( COMMIT_PER_PASS, COMMIT_VERTEX_SHADER, + CommitVertexTextures, nStage, m_BoundTexture, textureHandle ); +} + + +//----------------------------------------------------------------------------- +// Texture allocation/deallocation +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Computes stats info for a texture +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ComputeStatsInfo( ShaderAPITextureHandle_t hTexture, bool isCubeMap, bool isVolumeTexture ) +{ + Texture_t &textureData = GetTexture( hTexture ); + + textureData.m_SizeBytes = 0; + textureData.m_SizeTexels = 0; + textureData.m_LastBoundFrame = -1; + if ( IsX360() ) + { + textureData.m_nTimesBoundThisFrame = 0; + } + + IDirect3DBaseTexture* pD3DTex = CShaderAPIDx8::GetD3DTexture( hTexture ); + + if ( IsPC() || !IsX360() ) + { + if ( isCubeMap ) + { + IDirect3DCubeTexture* pTex = static_cast<IDirect3DCubeTexture*>(pD3DTex); + if ( !pTex ) + { + Assert( 0 ); + return; + } + + int numLevels = pTex->GetLevelCount(); + for (int i = 0; i < numLevels; ++i) + { + D3DSURFACE_DESC desc; + HRESULT hr = pTex->GetLevelDesc( i, &desc ); + Assert( !FAILED(hr) ); + textureData.m_SizeBytes += 6 * ImageLoader::GetMemRequired( desc.Width, desc.Height, 1, textureData.GetImageFormat(), false ); + textureData.m_SizeTexels += 6 * desc.Width * desc.Height; + } + } + else if ( isVolumeTexture ) + { + IDirect3DVolumeTexture9* pTex = static_cast<IDirect3DVolumeTexture9*>(pD3DTex); + if ( !pTex ) + { + Assert( 0 ); + return; + } + int numLevels = pTex->GetLevelCount(); + for (int i = 0; i < numLevels; ++i) + { + D3DVOLUME_DESC desc; + HRESULT hr = pTex->GetLevelDesc( i, &desc ); + Assert( !FAILED( hr ) ); + textureData.m_SizeBytes += ImageLoader::GetMemRequired( desc.Width, desc.Height, desc.Depth, textureData.GetImageFormat(), false ); + textureData.m_SizeTexels += desc.Width * desc.Height; + } + } + else + { + IDirect3DTexture* pTex = static_cast<IDirect3DTexture*>(pD3DTex); + if ( !pTex ) + { + Assert( 0 ); + return; + } + + int numLevels = pTex->GetLevelCount(); + for (int i = 0; i < numLevels; ++i) + { + D3DSURFACE_DESC desc; + HRESULT hr = pTex->GetLevelDesc( i, &desc ); + Assert( !FAILED( hr ) ); + textureData.m_SizeBytes += ImageLoader::GetMemRequired( desc.Width, desc.Height, 1, textureData.GetImageFormat(), false ); + textureData.m_SizeTexels += desc.Width * desc.Height; + } + } + } + +#if defined( _X360 ) + // 360 uses gpu storage size (which accounts for page alignment bloat), not format size + textureData.m_SizeBytes = g_TextureHeap.GetSize( pD3DTex ); +#endif +} + +static D3DFORMAT ComputeFormat( IDirect3DBaseTexture* pTexture, bool isCubeMap ) +{ + Assert( pTexture ); + D3DSURFACE_DESC desc; + if (isCubeMap) + { + IDirect3DCubeTexture* pTex = static_cast<IDirect3DCubeTexture*>(pTexture); + HRESULT hr = pTex->GetLevelDesc( 0, &desc ); + Assert( !FAILED(hr) ); + } + else + { + IDirect3DTexture* pTex = static_cast<IDirect3DTexture*>(pTexture); + HRESULT hr = pTex->GetLevelDesc( 0, &desc ); + Assert( !FAILED(hr) ); + } + return desc.Format; +} + +ShaderAPITextureHandle_t CShaderAPIDx8::CreateDepthTexture( + ImageFormat renderTargetFormat, + int width, + int height, + const char *pDebugName, + bool bTexture ) +{ + LOCK_SHADERAPI(); + + ShaderAPITextureHandle_t i = CreateTextureHandle(); + Texture_t *pTexture = &GetTexture( i ); + + pTexture->m_Flags = Texture_t::IS_ALLOCATED; + if( bTexture ) + { + pTexture->m_Flags |= Texture_t::IS_DEPTH_STENCIL_TEXTURE; + } + else + { + pTexture->m_Flags |= Texture_t::IS_DEPTH_STENCIL; + } + + pTexture->m_DebugName = pDebugName; + pTexture->m_Width = width; + pTexture->m_Height = height; + pTexture->m_Depth = 1; // fake + pTexture->m_Count = 1; // created single texture + pTexture->m_CountIndex = 0; // created single texture + pTexture->m_CreationFlags = 0; // fake + pTexture->m_NumCopies = 1; + pTexture->m_CurrentCopy = 0; + + ImageFormat renderFormat = FindNearestSupportedFormat( renderTargetFormat, false, true, false ); +#if defined( _X360 ) + D3DFORMAT nDepthFormat = ReverseDepthOnX360() ? D3DFMT_D24FS8 : D3DFMT_D24S8; +#else + D3DFORMAT nDepthFormat = m_bUsingStencil ? D3DFMT_D24S8 : D3DFMT_D24X8; +#endif + D3DFORMAT format = FindNearestSupportedDepthFormat( m_nAdapter, m_AdapterFormat, renderFormat, nDepthFormat ); + D3DMULTISAMPLE_TYPE multisampleType = D3DMULTISAMPLE_NONE; + + pTexture->m_NumLevels = 1; + pTexture->m_SizeTexels = width * height; + pTexture->m_SizeBytes = ImageLoader::GetMemRequired( width, height, 1, renderFormat, false ); + + RECORD_COMMAND( DX8_CREATE_DEPTH_TEXTURE, 5 ); + RECORD_INT( i ); + RECORD_INT( width ); + RECORD_INT( height ); + RECORD_INT( format ); + RECORD_INT( multisampleType ); + + HRESULT hr; + if ( !bTexture ) + { +#if defined( _X360 ) + int backWidth, backHeight; + ShaderAPI()->GetBackBufferDimensions( backWidth, backHeight ); + D3DFORMAT backBufferFormat = ImageLoader::ImageFormatToD3DFormat( g_pShaderDevice->GetBackBufferFormat() ); + // immediately follows back buffer in EDRAM + D3DSURFACE_PARAMETERS surfParameters; + surfParameters.Base = 2*XGSurfaceSize( backWidth, backHeight, backBufferFormat, D3DMULTISAMPLE_NONE ); + surfParameters.ColorExpBias = 0; + surfParameters.HierarchicalZBase = 0; + hr = Dx9Device()->CreateDepthStencilSurface( + width, height, format, multisampleType, 0, TRUE, &pTexture->GetDepthStencilSurface(), &surfParameters ); +#else + hr = Dx9Device()->CreateDepthStencilSurface( + width, height, format, multisampleType, 0, TRUE, &pTexture->GetDepthStencilSurface(), NULL ); +#endif + } + else + { + IDirect3DTexture9 *pTex; + hr = Dx9Device()->CreateTexture( width, height, 1, D3DUSAGE_DEPTHSTENCIL, format, D3DPOOL_DEFAULT, &pTex, NULL ); + pTexture->SetTexture( pTex ); + } + + if ( FAILED( hr ) ) + { + switch( hr ) + { + case D3DERR_INVALIDCALL: + Warning( "ShaderAPIDX8::CreateDepthStencilSurface: D3DERR_INVALIDCALL\n" ); + break; + case D3DERR_OUTOFVIDEOMEMORY: + Warning( "ShaderAPIDX8::CreateDepthStencilSurface: D3DERR_OUTOFVIDEOMEMORY\n" ); + break; + default: + break; + } + Assert( 0 ); + } + +#ifdef _XBOX + D3DSURFACE_DESC desc; + hr = pTexture->GetDepthStencilSurface()->GetDesc( &desc ); + Assert( !FAILED( hr ) ); + + pTexture->m_nTimesBoundThisFrame = 0; + pTexture->m_StaticSizeBytes = desc.Size; + pTexture->m_DynamicSizeBytes = 0; + pTexture->m_DebugName = pDebugName; + pTexture->m_TextureGroupName = TEXTURE_GROUP_RENDER_TARGET; + pTexture->SetImageFormat( IMAGE_FORMAT_UNKNOWN ); +#endif + + return i; +} + + +// FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// Could keep a free-list for this instead of linearly searching. We +// don't create textures all the time, so this is probably fine for now. +// FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +ShaderAPITextureHandle_t CShaderAPIDx8::CreateTextureHandle( void ) +{ + ShaderAPITextureHandle_t handle; + CreateTextureHandles( &handle, 1 ); + return handle; +} + +void CShaderAPIDx8::CreateTextureHandles( ShaderAPITextureHandle_t *handles, int count ) +{ + TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 ); + + if ( count <= 0 ) + return; + + MEM_ALLOC_CREDIT(); + + int idxCreating = 0; + ShaderAPITextureHandle_t hTexture; + + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Search", __FUNCTION__ ); + + for ( hTexture = m_Textures.Head(); hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) ) + { + if ( !( m_Textures[hTexture].m_Flags & Texture_t::IS_ALLOCATED ) ) + { + handles[ idxCreating ++ ] = hTexture; + if ( idxCreating >= count ) + return; + } + } + } + + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Add", __FUNCTION__ ); + while ( idxCreating < count ) + handles[ idxCreating ++ ] = m_Textures.AddToTail(); +} + +//----------------------------------------------------------------------------- +// Creates a lovely texture +//----------------------------------------------------------------------------- + +ShaderAPITextureHandle_t CShaderAPIDx8::CreateTexture( + int width, + int height, + int depth, + ImageFormat dstImageFormat, + int numMipLevels, + int numCopies, + int creationFlags, + const char *pDebugName, + const char *pTextureGroupName ) +{ + ShaderAPITextureHandle_t handle = 0; + CreateTextures( &handle, 1, width, height, depth, dstImageFormat, numMipLevels, numCopies, creationFlags, pDebugName, pTextureGroupName ); + return handle; +} + +void CShaderAPIDx8::CreateTextures( + ShaderAPITextureHandle_t *pHandles, + int count, + int width, + int height, + int depth, + ImageFormat dstImageFormat, + int numMipLevels, + int numCopies, + int creationFlags, + const char *pDebugName, + const char *pTextureGroupName ) +{ + TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 ); + + LOCK_SHADERAPI(); + + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - PostLock", __FUNCTION__ ); + + Assert( this == g_pShaderAPI ); + + if ( depth == 0 ) + { + depth = 1; + } + + bool isCubeMap = (creationFlags & TEXTURE_CREATE_CUBEMAP) != 0; + bool isRenderTarget = (creationFlags & TEXTURE_CREATE_RENDERTARGET) != 0; + bool managed = (creationFlags & TEXTURE_CREATE_MANAGED) != 0; + bool isDepthBuffer = (creationFlags & TEXTURE_CREATE_DEPTHBUFFER) != 0; + bool isDynamic = (creationFlags & TEXTURE_CREATE_DYNAMIC) != 0; + bool isSRGB = (creationFlags & TEXTURE_CREATE_SRGB) != 0; // for Posix/GL only... not used here ? + +#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) + extern bool g_ShaderDeviceUsingD3D9Ex; + if ( g_ShaderDeviceUsingD3D9Ex && managed ) + { + // Managed textures aren't available under D3D9Ex, but we never lose + // texture data, so it's ok to use the default pool. Really. We can't + // lock default-pool textures like we normally would to upload, but we + // have special logic to blit full updates via D3DX helper functions + // in D3D9Ex mode (see texturedx8.cpp) + managed = false; + creationFlags &= ~TEXTURE_CREATE_MANAGED; + } +#endif + + // Can't be both managed + dynamic. Dynamic is an optimization, but + // if it's not managed, then we gotta do special client-specific stuff + // So, managed wins out! + if ( managed ) + { + creationFlags &= ~TEXTURE_CREATE_DYNAMIC; + isDynamic = false; + } + + + + // Create a set of texture handles + CreateTextureHandles( pHandles, count ); + Texture_t **arrTxp = ( Texture_t ** ) stackalloc( count * sizeof( Texture_t * ) ); + + unsigned short usSetFlags = 0; + usSetFlags |= ( IsPosix() || ( creationFlags & (TEXTURE_CREATE_DYNAMIC | TEXTURE_CREATE_MANAGED) ) ) ? Texture_t::IS_LOCKABLE : 0; + usSetFlags |= ( creationFlags & TEXTURE_CREATE_VERTEXTEXTURE) ? Texture_t::IS_VERTEX_TEXTURE : 0; +#if defined( _X360 ) + usSetFlags |= ( creationFlags & TEXTURE_CREATE_RENDERTARGET ) ? Texture_t::IS_RENDER_TARGET : 0; + usSetFlags |= ( creationFlags & TEXTURE_CREATE_CANCONVERTFORMAT ) ? Texture_t::CAN_CONVERT_FORMAT : 0; +#endif + + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - CreateFrames", __FUNCTION__ ); + + for ( int idxFrame = 0; idxFrame < count; ++ idxFrame ) + { + arrTxp[ idxFrame ] = &GetTexture( pHandles[ idxFrame ] ); + Texture_t *pTexture = arrTxp[ idxFrame ]; + pTexture->m_Flags = Texture_t::IS_ALLOCATED; + pTexture->m_DebugName = pDebugName; + pTexture->m_Width = width; + pTexture->m_Height = height; + pTexture->m_Depth = depth; + pTexture->m_Count = count; + pTexture->m_CountIndex = idxFrame; + + pTexture->m_CreationFlags = creationFlags; + pTexture->m_Flags |= usSetFlags; + + RECORD_COMMAND( DX8_CREATE_TEXTURE, 12 ); + RECORD_INT( textureHandle ); + RECORD_INT( width ); + RECORD_INT( height ); + RECORD_INT( depth ); // depth for volume textures + RECORD_INT( ImageLoader::ImageFormatToD3DFormat( FindNearestSupportedFormat(dstImageFormat)) ); + RECORD_INT( numMipLevels ); + RECORD_INT( isCubeMap ); + RECORD_INT( numCopies <= 1 ? 1 : numCopies ); + RECORD_INT( isRenderTarget ? 1 : 0 ); + RECORD_INT( managed ); + RECORD_INT( isDepthBuffer ? 1 : 0 ); + RECORD_INT( isDynamic ? 1 : 0 ); + + IDirect3DBaseTexture* pD3DTex; + + // Set the initial texture state + if ( numCopies <= 1 ) + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - CreateD3DTexture", __FUNCTION__ ); + + pTexture->m_NumCopies = 1; + pD3DTex = CreateD3DTexture( width, height, depth, dstImageFormat, numMipLevels, creationFlags, (char*)pDebugName ); + pTexture->SetTexture( pD3DTex ); + } + else + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - CreateD3DTexture", __FUNCTION__ ); + + pTexture->m_NumCopies = numCopies; + { +// X360TEMP +// MEM_ALLOC_CREDIT(); + pTexture->GetTextureArray() = new IDirect3DBaseTexture* [numCopies]; + } + for (int k = 0; k < numCopies; ++k) + { + pD3DTex = CreateD3DTexture( width, height, depth, dstImageFormat, numMipLevels, creationFlags, (char*)pDebugName ); + pTexture->SetTexture( k, pD3DTex ); + } + } + pTexture->m_CurrentCopy = 0; + + pD3DTex = CShaderAPIDx8::GetD3DTexture( pHandles[ idxFrame ] ); + +#if defined( _X360 ) + if ( pD3DTex ) + { + D3DSURFACE_DESC desc; + HRESULT hr; + if ( creationFlags & TEXTURE_CREATE_CUBEMAP ) + { + hr = ((IDirect3DCubeTexture *)pD3DTex)->GetLevelDesc( 0, &desc ); + } + else + { + hr = ((IDirect3DTexture *)pD3DTex)->GetLevelDesc( 0, &desc ); + } + Assert( !FAILED( hr ) ); + + // for proper info get the actual format because the input format may have been redirected + dstImageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format ); + Assert( dstImageFormat != IMAGE_FORMAT_UNKNOWN ); + + // track linear or tiled + if ( !XGIsTiledFormat( desc.Format ) ) + { + pTexture->m_Flags |= Texture_t::IS_LINEAR; + } + } +#endif + + pTexture->SetImageFormat( dstImageFormat ); + pTexture->m_UTexWrap = D3DTADDRESS_CLAMP; + pTexture->m_VTexWrap = D3DTADDRESS_CLAMP; + pTexture->m_WTexWrap = D3DTADDRESS_CLAMP; + + if ( isRenderTarget ) + { +#if !defined( _X360 ) + if ( ( dstImageFormat == IMAGE_FORMAT_NV_INTZ ) || ( dstImageFormat == IMAGE_FORMAT_NV_RAWZ ) || + ( dstImageFormat == IMAGE_FORMAT_ATI_DST16 ) || ( dstImageFormat == IMAGE_FORMAT_ATI_DST24 ) ) + { + pTexture->m_MinFilter = pTexture->m_MagFilter = D3DTEXF_POINT; + } + else +#endif + { + pTexture->m_MinFilter = pTexture->m_MagFilter = D3DTEXF_LINEAR; + } + + pTexture->m_NumLevels = 1; + pTexture->m_MipFilter = D3DTEXF_NONE; + } + else + { + pTexture->m_NumLevels = pD3DTex ? pD3DTex->GetLevelCount() : 1; + pTexture->m_MipFilter = (pTexture->m_NumLevels != 1) ? D3DTEXF_LINEAR : D3DTEXF_NONE; + pTexture->m_MinFilter = pTexture->m_MagFilter = D3DTEXF_LINEAR; + } + pTexture->m_SwitchNeeded = false; + + ComputeStatsInfo( pHandles[idxFrame], isCubeMap, (depth > 1) ); + SetupTextureGroup( pHandles[idxFrame], pTextureGroupName ); + } +} + +void CShaderAPIDx8::SetupTextureGroup( ShaderAPITextureHandle_t hTexture, const char *pTextureGroupName ) +{ + Texture_t *pTexture = &GetTexture( hTexture ); + + Assert( !pTexture->m_pTextureGroupCounterGlobal ); + + // Setup the texture group stuff. + if ( pTextureGroupName && pTextureGroupName[0] != 0 ) + { + pTexture->m_TextureGroupName = pTextureGroupName; + } + else + { + pTexture->m_TextureGroupName = TEXTURE_GROUP_UNACCOUNTED; + } + + // 360 cannot vprof due to multicore loading until vprof is reentrant and these counters are real. +#if defined( VPROF_ENABLED ) && !defined( _X360 ) + char counterName[256]; + Q_snprintf( counterName, sizeof( counterName ), "TexGroup_global_%s", pTexture->m_TextureGroupName.String() ); + pTexture->m_pTextureGroupCounterGlobal = g_VProfCurrentProfile.FindOrCreateCounter( counterName, COUNTER_GROUP_TEXTURE_GLOBAL ); + + Q_snprintf( counterName, sizeof( counterName ), "TexGroup_frame_%s", pTexture->m_TextureGroupName.String() ); + pTexture->m_pTextureGroupCounterFrame = g_VProfCurrentProfile.FindOrCreateCounter( counterName, COUNTER_GROUP_TEXTURE_PER_FRAME ); +#else + pTexture->m_pTextureGroupCounterGlobal = NULL; + pTexture->m_pTextureGroupCounterFrame = NULL; +#endif + + if ( pTexture->m_pTextureGroupCounterGlobal ) + { + *pTexture->m_pTextureGroupCounterGlobal += pTexture->GetMemUsage(); + } +} + +//----------------------------------------------------------------------------- +// Deletes a texture... +//----------------------------------------------------------------------------- +void CShaderAPIDx8::DeleteD3DTexture( ShaderAPITextureHandle_t hTexture ) +{ + int numDeallocated = 0; + Texture_t &texture = GetTexture( hTexture ); + + if ( texture.m_Flags & Texture_t::IS_DEPTH_STENCIL ) + { + // garymcthack - need to make sure that playback knows how to deal with these. + RECORD_COMMAND( DX8_DESTROY_DEPTH_TEXTURE, 1 ); + RECORD_INT( hTexture ); + + if ( texture.GetDepthStencilSurface() ) + { + int nRetVal = texture.GetDepthStencilSurface()->Release(); + Assert( nRetVal == 0 ); + texture.GetDepthStencilSurface() = 0; + numDeallocated = 1; + } + else + { + // FIXME: we hit this on shutdown of HLMV on some machines + Assert( 0 ); + } + } + else if ( texture.m_NumCopies == 1 ) + { + if ( texture.GetTexture() ) + { + RECORD_COMMAND( DX8_DESTROY_TEXTURE, 1 ); + RECORD_INT( hTexture ); + + DestroyD3DTexture( texture.GetTexture() ); + texture.SetTexture( 0 ); + numDeallocated = 1; + } + } + else + { + if ( texture.GetTextureArray() ) + { + RECORD_COMMAND( DX8_DESTROY_TEXTURE, 1 ); + RECORD_INT( hTexture ); + + // Multiple copy texture + for (int j = 0; j < texture.m_NumCopies; ++j) + { + if (texture.GetTexture( j )) + { + DestroyD3DTexture( texture.GetTexture( j ) ); + texture.SetTexture( j, 0 ); + ++numDeallocated; + } + } + + delete [] texture.GetTextureArray(); + texture.GetTextureArray() = 0; + } + } + + texture.m_NumCopies = 0; + + // Remove this texture from its global texture group counter. + if ( texture.m_pTextureGroupCounterGlobal ) + { + *texture.m_pTextureGroupCounterGlobal -= texture.GetMemUsage(); + Assert( *texture.m_pTextureGroupCounterGlobal >= 0 ); + texture.m_pTextureGroupCounterGlobal = NULL; + } + + // remove this texture from std textures + for( int i=0 ; i < ARRAYSIZE( m_StdTextureHandles ) ; i++ ) + { + if ( m_StdTextureHandles[i] == hTexture ) + m_StdTextureHandles[i] = INVALID_SHADERAPI_TEXTURE_HANDLE; + } + +} + + +//----------------------------------------------------------------------------- +// Unbinds a texture from all texture stages +//----------------------------------------------------------------------------- +void CShaderAPIDx8::UnbindTexture( ShaderAPITextureHandle_t hTexture ) +{ + // Make sure no texture units are currently bound to it... + for ( int unit = 0; unit < g_pHardwareConfig->GetSamplerCount(); ++unit ) + { + if ( hTexture == SamplerState( unit ).m_BoundTexture ) + { + // Gotta set this here because INVALID_SHADERAPI_TEXTURE_HANDLE means don't actually + // set bound texture (it's used for disabling texturemapping) + SamplerState( unit ).m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE; + SetTextureState( (Sampler_t)unit, INVALID_SHADERAPI_TEXTURE_HANDLE ); + } + } + + int nVertexTextureCount = g_pHardwareConfig->GetVertexTextureCount(); + for ( int nSampler = 0; nSampler < nVertexTextureCount; ++nSampler ) + { + if ( hTexture == m_DynamicState.m_VertexTextureState[ nSampler ].m_BoundTexture ) + { + // Gotta set this here because INVALID_SHADERAPI_TEXTURE_HANDLE means don't actually + // set bound texture (it's used for disabling texturemapping) + BindVertexTexture( (VertexTextureSampler_t)nSampler, INVALID_SHADERAPI_TEXTURE_HANDLE ); + } + } +} + + +//----------------------------------------------------------------------------- +// Deletes a texture... +//----------------------------------------------------------------------------- +void CShaderAPIDx8::DeleteTexture( ShaderAPITextureHandle_t textureHandle ) +{ + LOCK_SHADERAPI(); + AssertValidTextureHandle( textureHandle ); + + if ( !TextureIsAllocated( textureHandle ) ) + { + // already deallocated + return; + } + + // Unbind it! + UnbindTexture( textureHandle ); + + // Delete it baby + DeleteD3DTexture( textureHandle ); + + // Now remove the texture from the list + // Mark as deallocated so that it can be reused. + GetTexture( textureHandle ).m_Flags = 0; +} + + +void CShaderAPIDx8::WriteTextureToFile( ShaderAPITextureHandle_t hTexture, const char *szFileName ) +{ + Texture_t *pTexInt = &GetTexture( hTexture ); + //Assert( pTexInt->IsCubeMap() == false ); + //Assert( pTexInt->IsVolumeTexture() == false ); + IDirect3DTexture *pD3DTexture = (IDirect3DTexture *)pTexInt->GetTexture(); + + // Get the level of the texture we want to read from + IDirect3DSurface* pTextureLevel; + HRESULT hr = pD3DTexture ->GetSurfaceLevel( 0, &pTextureLevel ); + if ( FAILED( hr ) ) + return; + + D3DSURFACE_DESC surfaceDesc; + pD3DTexture->GetLevelDesc( 0, &surfaceDesc ); + + D3DLOCKED_RECT lockedRect; + + + //if( pTexInt->m_Flags & Texture_t::IS_RENDER_TARGET ) +#if !defined( _X360 ) //TODO: x360 version + { + //render targets can't be locked, luckily we can copy the surface to system memory and lock that. + IDirect3DSurface *pSystemSurface; + + Assert( !IsX360() ); + + hr = Dx9Device()->CreateOffscreenPlainSurface( surfaceDesc.Width, surfaceDesc.Height, surfaceDesc.Format, D3DPOOL_SYSTEMMEM, &pSystemSurface, NULL ); + Assert( SUCCEEDED( hr ) ); + + pSystemSurface->GetDesc( &surfaceDesc ); + + hr = Dx9Device()->GetRenderTargetData( pTextureLevel, pSystemSurface ); + Assert( SUCCEEDED( hr ) ); + + //pretend this is the texture level we originally grabbed with GetSurfaceLevel() and continue on + pTextureLevel->Release(); + pTextureLevel = pSystemSurface; + } +#endif + + // lock the region + if ( FAILED( pTextureLevel->LockRect( &lockedRect, NULL, D3DLOCK_READONLY ) ) ) + { + Assert( 0 ); + pTextureLevel->Release(); + return; + } + + TGAWriter::WriteTGAFile( szFileName, surfaceDesc.Width, surfaceDesc.Height, pTexInt->GetImageFormat(), (const uint8 *)lockedRect.pBits, lockedRect.Pitch ); + + if ( FAILED( pTextureLevel->UnlockRect() ) ) + { + Assert( 0 ); + pTextureLevel->Release(); + return; + } + + pTextureLevel->Release(); +} + + +//----------------------------------------------------------------------------- +// Releases all textures +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ReleaseAllTextures() +{ + ClearStdTextureHandles(); + ShaderAPITextureHandle_t hTexture; + for ( hTexture = m_Textures.Head(); hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) ) + { + if ( TextureIsAllocated( hTexture ) ) + { + // Delete it baby + DeleteD3DTexture( hTexture ); + } + } + + // Make sure all texture units are pointing to nothing + for (int unit = 0; unit < g_pHardwareConfig->GetSamplerCount(); ++unit ) + { + SamplerState( unit ).m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE; + SetTextureState( (Sampler_t)unit, INVALID_SHADERAPI_TEXTURE_HANDLE ); + } +} + +void CShaderAPIDx8::DeleteAllTextures() +{ + ReleaseAllTextures(); + m_Textures.Purge(); +} + +bool CShaderAPIDx8::IsTexture( ShaderAPITextureHandle_t textureHandle ) +{ + LOCK_SHADERAPI(); + + if ( !TextureIsAllocated( textureHandle ) ) + { + return false; + } + +#if !defined( _X360 ) + if ( GetTexture( textureHandle ).m_Flags & Texture_t::IS_DEPTH_STENCIL ) + { + return GetTexture( textureHandle ).GetDepthStencilSurface() != 0; + } + else if ( ( GetTexture( textureHandle ).m_NumCopies == 1 && GetTexture( textureHandle ).GetTexture() != 0 ) || + ( GetTexture( textureHandle ).m_NumCopies > 1 && GetTexture( textureHandle ).GetTexture( 0 ) != 0 ) ) + { + return true; + } + else + { + return false; + } +#else + // query is about texture handle validity, not presence + // texture handle is allocated, texture may or may not be present + return true; +#endif +} + + + +//----------------------------------------------------------------------------- +// Gets the surface associated with a texture (refcount of surface is increased) +//----------------------------------------------------------------------------- +IDirect3DSurface* CShaderAPIDx8::GetTextureSurface( ShaderAPITextureHandle_t textureHandle ) +{ + MEM_ALLOC_D3D_CREDIT(); + + IDirect3DSurface* pSurface; + + // We'll be modifying this sucka + AssertValidTextureHandle( textureHandle ); + Texture_t &tex = GetTexture( textureHandle ); + if ( !( tex.m_Flags & Texture_t::IS_ALLOCATED ) ) + { + return NULL; + } + + if ( IsX360() && ( tex.m_Flags & Texture_t::IS_RENDER_TARGET_SURFACE ) ) + { + pSurface = tex.GetRenderTargetSurface( false ); + +#if POSIX + // dxabstract's AddRef/Release have optional args to help track usage + pSurface->AddRef( 0, "CShaderAPIDx8::GetTextureSurface public addref"); +#else + pSurface->AddRef(); +#endif + + return pSurface; + } + + IDirect3DBaseTexture* pD3DTex = CShaderAPIDx8::GetD3DTexture( textureHandle ); + IDirect3DTexture* pTex = static_cast<IDirect3DTexture*>( pD3DTex ); + Assert( pTex ); + if ( !pTex ) + { + return NULL; + } + + HRESULT hr = pTex->GetSurfaceLevel( 0, &pSurface ); + Assert( hr == D3D_OK ); + + return pSurface; +} + +//----------------------------------------------------------------------------- +// Gets the surface associated with a texture (refcount of surface is increased) +//----------------------------------------------------------------------------- +IDirect3DSurface* CShaderAPIDx8::GetDepthTextureSurface( ShaderAPITextureHandle_t textureHandle ) +{ + AssertValidTextureHandle( textureHandle ); + if ( !TextureIsAllocated( textureHandle ) ) + { + return NULL; + } + return GetTexture( textureHandle ).GetDepthStencilSurface(); +} + +//----------------------------------------------------------------------------- +// Changes the render target +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetRenderTargetEx( int nRenderTargetID, ShaderAPITextureHandle_t colorTextureHandle, ShaderAPITextureHandle_t depthTextureHandle ) +{ + LOCK_SHADERAPI(); + if ( IsDeactivated( ) ) + { + return; + } + + // GR - need to flush batched geometry + FlushBufferedPrimitives(); + +#if defined( PIX_INSTRUMENTATION ) + { + const char *pRT = "Backbuffer"; + const char *pDS = "DefaultDepthStencil"; + + if ( colorTextureHandle == SHADER_RENDERTARGET_NONE ) + { + pRT = "None"; + } + else if ( colorTextureHandle != SHADER_RENDERTARGET_BACKBUFFER ) + { + Texture_t &tex = GetTexture( colorTextureHandle ); + pRT = tex.m_DebugName.String(); + } + + if ( depthTextureHandle == SHADER_RENDERTARGET_NONE ) + { + pDS = "None"; + } + else if ( depthTextureHandle != SHADER_RENDERTARGET_DEPTHBUFFER ) + { + Texture_t &tex = GetTexture( depthTextureHandle ); + pDS = tex.m_DebugName.String(); + } + + char buf[256]; + sprintf( buf, "SRT:%s %s", pRT ? pRT : "?", pDS ? pDS : "?" ); + BeginPIXEvent( 0xFFFFFFFF, buf ); + EndPIXEvent(); + } +#endif + +#if !defined( _X360 ) + RECORD_COMMAND( DX8_TEST_COOPERATIVE_LEVEL, 0 ); + HRESULT hr = Dx9Device()->TestCooperativeLevel(); + if ( hr != D3D_OK ) + { + MarkDeviceLost(); + return; + } +#endif + + IDirect3DSurface* pColorSurface = NULL; + IDirect3DSurface* pZSurface = NULL; + + RECORD_COMMAND( DX8_SET_RENDER_TARGET, 3 ); + RECORD_INT( nRenderTargetID ); + RECORD_INT( colorTextureHandle ); + RECORD_INT( depthTextureHandle ); + + // The 0th render target defines which depth buffer we are using, so + // don't bother if we are another render target + if ( nRenderTargetID > 0 ) + { + depthTextureHandle = SHADER_RENDERTARGET_NONE; + } + + // NOTE!!!! If this code changes, also change Dx8SetRenderTarget in playback.cpp + bool usingTextureTarget = false; + if ( colorTextureHandle == SHADER_RENDERTARGET_BACKBUFFER ) + { + pColorSurface = m_pBackBufferSurface; + +#ifdef ENABLE_NULLREF_DEVICE_SUPPORT + if( pColorSurface ) +#endif + { + // This is just to make the code a little simpler... + // (simplifies the release logic) +#if POSIX + // dxabstract's AddRef/Release have optional args to help track usage + pColorSurface->AddRef( 0, "+C CShaderAPIDx8::SetRenderTargetEx public addref 1"); +#else + pColorSurface->AddRef(); +#endif + } + } + else + { + // get the texture (Refcount increases) + UnbindTexture( colorTextureHandle ); + pColorSurface = GetTextureSurface( colorTextureHandle ); + if ( !pColorSurface ) + { + return; + } + + usingTextureTarget = true; + } + + if ( depthTextureHandle == SHADER_RENDERTARGET_DEPTHBUFFER ) + { + // using the default depth buffer + pZSurface = m_pZBufferSurface; + +#ifdef ENABLE_NULLREF_DEVICE_SUPPORT + if( pZSurface ) +#endif + { + // simplify the prologue logic +#if POSIX + // dxabstract's AddRef/Release have optional args to help track usage + pZSurface->AddRef( 0, "+D CShaderAPIDx8::SetRenderTargetEx public addref 1"); +#else + pZSurface->AddRef(); +#endif + } + } + else if ( depthTextureHandle == SHADER_RENDERTARGET_NONE ) + { + // GR - disable depth buffer + pZSurface = NULL; + } + else + { + UnbindTexture( depthTextureHandle ); + + Texture_t &tex = GetTexture( depthTextureHandle ); + + //Cannot use a depth/stencil surface derived from a texture. + //Asserting helps get the whole call stack instead of letting the 360 report an error with a partial stack + Assert( !( IsX360() && (tex.m_Flags & Texture_t::IS_DEPTH_STENCIL_TEXTURE) ) ); + + if ( tex.m_Flags & Texture_t::IS_DEPTH_STENCIL ) + { + pZSurface = GetDepthTextureSurface( depthTextureHandle ); + if ( pZSurface ) + { +#if POSIX + // dxabstract's AddRef/Release have optional args to help track usage + pZSurface->AddRef( 0, "+D CShaderAPIDx8::SetRenderTargetEx public addref 2"); +#else + pZSurface->AddRef(); +#endif + } + } + else + { + HRESULT hr = ((IDirect3DTexture9*)tex.GetTexture())->GetSurfaceLevel( 0, &pZSurface ); + } + + if ( !pZSurface ) + { + // Refcount of color surface was increased above +#if POSIX + // dxabstract's AddRef/Release have optional args to help track usage + pColorSurface->Release( 0, "-C CShaderAPIDx8::SetRenderTargetEx public release 1" ); +#else + pColorSurface->Release(); +#endif + return; + } + usingTextureTarget = true; + } + +#ifdef _DEBUG + if ( pZSurface ) + { + D3DSURFACE_DESC zSurfaceDesc, colorSurfaceDesc; + pZSurface->GetDesc( &zSurfaceDesc ); + pColorSurface->GetDesc( &colorSurfaceDesc ); + + if ( !HushAsserts() ) + { + Assert( colorSurfaceDesc.Width <= zSurfaceDesc.Width ); + Assert( colorSurfaceDesc.Height <= zSurfaceDesc.Height ); + } + } +#endif + + // we only set this flag for the 0th render target so that NULL + // render targets 1-3 don't mess with the viewport on the main RT + if( nRenderTargetID == 0 ) + m_UsingTextureRenderTarget = usingTextureTarget; + + // NOTE: The documentation says that SetRenderTarget increases the refcount + // but it doesn't appear to in practice. If this somehow changes (perhaps + // in a device-specific manner, we're in trouble). + if ( IsPC() || !IsX360() ) + { + if ( pColorSurface == m_pBackBufferSurface && nRenderTargetID > 0 ) + { + // SetRenderTargetEx is overloaded so that if you pass NULL in for anything that + // isn't the zeroth render target, you effectively disable that MRT index. + // (Passing in NULL for the zeroth render target means that you want to use the backbuffer + // as the render target.) + // hack hack hack!!!!! If the render target id > 0 and the user passed in NULL, disable the render target + Dx9Device()->SetRenderTarget( nRenderTargetID, NULL ); + } + else + { + Dx9Device()->SetRenderTarget( nRenderTargetID, pColorSurface ); + } + } + else + { + Assert( nRenderTargetID == 0 ); + SetRenderTargetInternalXbox( colorTextureHandle ); + } + + // The 0th render target defines which depth buffer we are using, so + // don't bother if we are another render target + if ( nRenderTargetID == 0 ) + { + Dx9Device()->SetDepthStencilSurface( pZSurface ); + } + + // The 0th render target defines the viewport, therefore it also defines + // the viewport limits. + if ( m_UsingTextureRenderTarget && nRenderTargetID == 0 ) + { + D3DSURFACE_DESC desc; + HRESULT hr; + if ( !pZSurface ) + { + hr = pColorSurface->GetDesc( &desc ); + } + else + { + hr = pZSurface->GetDesc( &desc ); + } + Assert( !FAILED(hr) ); + m_ViewportMaxWidth = desc.Width; + m_ViewportMaxHeight = desc.Height; + } + + static bool assert_on_refzero = false; + int ref; + if ( pZSurface ) + { +#if POSIX + ref = pZSurface->Release( 0, "-D CShaderAPIDx8::SetRenderTargetEx public release (z surface)"); +#else + ref = pZSurface->Release(); +#endif + + if(assert_on_refzero) + { + Assert( ref != 0 ); + } + } + +#ifdef ENABLE_NULLREF_DEVICE_SUPPORT + if( pColorSurface ) +#endif + { +#if POSIX + ref = pColorSurface->Release( 0, "-C CShaderAPIDx8::SetRenderTargetEx public release (color surface)"); +#else + ref = pColorSurface->Release(); +#endif + if(assert_on_refzero) + { + Assert( ref != 0 ); + } + } + + // Changing the render target sets a default viewport. Force rewrite to preserve the current desired state. + m_DynamicState.m_Viewport.X = 0; + m_DynamicState.m_Viewport.Y = 0; + m_DynamicState.m_Viewport.Width = (DWORD)-1; + m_DynamicState.m_Viewport.Height = (DWORD)-1; + + ADD_COMMIT_FUNC( COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitSetViewports ); +} + + +//----------------------------------------------------------------------------- +// Changes the render target +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetRenderTarget( ShaderAPITextureHandle_t colorTextureHandle, ShaderAPITextureHandle_t depthTextureHandle ) +{ + LOCK_SHADERAPI(); + SetRenderTargetEx( 0, colorTextureHandle, depthTextureHandle ); +} + +//----------------------------------------------------------------------------- +// Returns the nearest supported format +//----------------------------------------------------------------------------- +ImageFormat CShaderAPIDx8::GetNearestSupportedFormat( ImageFormat fmt, bool bFilteringRequired /* = true */ ) const +{ + return FindNearestSupportedFormat( fmt, false, false, bFilteringRequired ); +} + + +ImageFormat CShaderAPIDx8::GetNearestRenderTargetFormat( ImageFormat fmt ) const +{ + return FindNearestSupportedFormat( fmt, false, true, false ); +} + + +bool CShaderAPIDx8::DoRenderTargetsNeedSeparateDepthBuffer() const +{ + LOCK_SHADERAPI(); + return m_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE; +} + + +//----------------------------------------------------------------------------- +// Indicates we're modifying a texture +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ModifyTexture( ShaderAPITextureHandle_t textureHandle ) +{ + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ ); + + LOCK_SHADERAPI(); + // Can't do this if we're locked! + Assert( m_ModifyTextureLockedLevel < 0 ); + + AssertValidTextureHandle( textureHandle ); + m_ModifyTextureHandle = textureHandle; + + // If we're got a multi-copy texture, we need to up the current copy count + Texture_t& tex = GetTexture( textureHandle ); + if (tex.m_NumCopies > 1) + { + // Each time we modify a texture, we'll want to switch texture + // as soon as a TexImage2D call is made... + tex.m_SwitchNeeded = true; + } +} + + +//----------------------------------------------------------------------------- +// Advances the current copy of a texture... +//----------------------------------------------------------------------------- +void CShaderAPIDx8::AdvanceCurrentCopy( ShaderAPITextureHandle_t hTexture ) +{ + // May need to switch textures.... + Texture_t& tex = GetTexture( hTexture ); + if (tex.m_NumCopies > 1) + { + if (++tex.m_CurrentCopy >= tex.m_NumCopies) + tex.m_CurrentCopy = 0; + + // When the current copy changes, we need to make sure this texture + // isn't bound to any stages any more; thereby guaranteeing the new + // copy will be re-bound. + UnbindTexture( hTexture ); + } +} + + +//----------------------------------------------------------------------------- +// Locks, unlocks the current texture +//----------------------------------------------------------------------------- + +bool CShaderAPIDx8::TexLock( int level, int cubeFaceID, int xOffset, int yOffset, + int width, int height, CPixelWriter& writer ) +{ + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ ); + + LOCK_SHADERAPI(); + + Assert( m_ModifyTextureLockedLevel < 0 ); + + ShaderAPITextureHandle_t hTexture = GetModifyTextureHandle(); + if ( !m_Textures.IsValidIndex( hTexture ) ) + return false; + + // Blow off mip levels if we don't support mipmapping + if ( !g_pHardwareConfig->SupportsMipmapping() && ( level > 0 ) ) + return false; + + // This test here just makes sure we don't try to download mipmap levels + // if we weren't able to create them in the first place + Texture_t& tex = GetTexture( hTexture ); + if ( level >= tex.m_NumLevels ) + { + return false; + } + + // May need to switch textures.... + if ( tex.m_SwitchNeeded ) + { + AdvanceCurrentCopy( hTexture ); + tex.m_SwitchNeeded = false; + } + + IDirect3DBaseTexture *pTexture = GetModifyTexture(); +#if defined( _X360 ) + // 360 can't lock a bound texture + if ( pTexture->IsSet( Dx9Device() ) ) + { + UnbindTexture( hTexture ); + } +#endif + + bool bOK = LockTexture( hTexture, tex.m_CurrentCopy, pTexture, + level, (D3DCUBEMAP_FACES)cubeFaceID, xOffset, yOffset, width, height, false, writer ); + if ( bOK ) + { + m_ModifyTextureLockedLevel = level; + m_ModifyTextureLockedFace = cubeFaceID; + } + return bOK; +} + +void CShaderAPIDx8::TexUnlock( ) +{ + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ ); + + LOCK_SHADERAPI(); + if ( m_ModifyTextureLockedLevel >= 0 ) + { + Texture_t& tex = GetTexture( GetModifyTextureHandle() ); + UnlockTexture( GetModifyTextureHandle(), tex.m_CurrentCopy, GetModifyTexture(), + m_ModifyTextureLockedLevel, (D3DCUBEMAP_FACES)m_ModifyTextureLockedFace ); + + m_ModifyTextureLockedLevel = -1; + } +} + +//----------------------------------------------------------------------------- +// Texture image upload +//----------------------------------------------------------------------------- +void CShaderAPIDx8::TexImage2D( + int level, + int cubeFaceID, + ImageFormat dstFormat, + int z, + int width, + int height, + ImageFormat srcFormat, + bool bSrcIsTiled, + void *pSrcData ) +{ + LOCK_SHADERAPI(); + + Assert( pSrcData ); + AssertValidTextureHandle( GetModifyTextureHandle() ); + + if ( !m_Textures.IsValidIndex( GetModifyTextureHandle() ) ) + { + return; + } + + Assert( (width <= g_pHardwareConfig->Caps().m_MaxTextureWidth) && (height <= g_pHardwareConfig->Caps().m_MaxTextureHeight) ); + + // Blow off mip levels if we don't support mipmapping + if ( !g_pHardwareConfig->SupportsMipmapping() && (level > 0)) + { + return; + } + + // This test here just makes sure we don't try to download mipmap levels + // if we weren't able to create them in the first place + Texture_t& tex = GetTexture( GetModifyTextureHandle() ); + if ( level >= tex.m_NumLevels ) + { + return; + } + + // May need to switch textures.... + if (tex.m_SwitchNeeded) + { + AdvanceCurrentCopy( GetModifyTextureHandle() ); + tex.m_SwitchNeeded = false; + } + + TextureLoadInfo_t info; + info.m_TextureHandle = GetModifyTextureHandle(); + info.m_pTexture = GetModifyTexture(); + info.m_nLevel = level; + info.m_nCopy = tex.m_CurrentCopy; + info.m_CubeFaceID = (D3DCUBEMAP_FACES)cubeFaceID; + info.m_nWidth = width; + info.m_nHeight = height; + info.m_nZOffset = z; + info.m_SrcFormat = srcFormat; + info.m_pSrcData = (unsigned char *)pSrcData; +#if defined( _X360 ) + info.m_bSrcIsTiled = bSrcIsTiled; + info.m_bCanConvertFormat = ( tex.m_Flags & Texture_t::CAN_CONVERT_FORMAT ) != 0; +#else + info.m_bTextureIsLockable = ( tex.m_Flags & Texture_t::IS_LOCKABLE ) != 0; +#endif + LoadTexture( info ); + SetModifyTexture( info.m_pTexture ); +} + +//----------------------------------------------------------------------------- +// Upload to a sub-piece of a texture +//----------------------------------------------------------------------------- +void CShaderAPIDx8::TexSubImage2D( + int level, + int cubeFaceID, + int xOffset, + int yOffset, + int zOffset, + int width, + int height, + ImageFormat srcFormat, + int srcStride, + bool bSrcIsTiled, + void *pSrcData ) +{ + LOCK_SHADERAPI(); + + Assert( pSrcData ); + AssertValidTextureHandle( GetModifyTextureHandle() ); + + if ( !m_Textures.IsValidIndex( GetModifyTextureHandle() ) ) + { + return; + } + + // Blow off mip levels if we don't support mipmapping + if ( !g_pHardwareConfig->SupportsMipmapping() && ( level > 0 ) ) + { + return; + } + + // NOTE: This can only be done with procedural textures if this method is + // being used to download the entire texture, cause last frame's partial update + // may be in a completely different texture! Sadly, I don't have all of the + // information I need, but I can at least check a couple things.... +#ifdef _DEBUG + if ( GetTexture( GetModifyTextureHandle() ).m_NumCopies > 1 ) + { + Assert( (xOffset == 0) && (yOffset == 0) ); + } +#endif + + // This test here just makes sure we don't try to download mipmap levels + // if we weren't able to create them in the first place + Texture_t& tex = GetTexture( GetModifyTextureHandle() ); + if ( level >= tex.m_NumLevels ) + { + return; + } + + // May need to switch textures.... + if ( tex.m_SwitchNeeded ) + { + AdvanceCurrentCopy( GetModifyTextureHandle() ); + tex.m_SwitchNeeded = false; + } + + TextureLoadInfo_t info; + info.m_TextureHandle = GetModifyTextureHandle(); + info.m_pTexture = GetModifyTexture(); + info.m_nLevel = level; + info.m_nCopy = tex.m_CurrentCopy; + info.m_CubeFaceID = (D3DCUBEMAP_FACES)cubeFaceID; + info.m_nWidth = width; + info.m_nHeight = height; + info.m_nZOffset = zOffset; + info.m_SrcFormat = srcFormat; + info.m_pSrcData = (unsigned char *)pSrcData; +#if defined( _X360 ) + info.m_bSrcIsTiled = bSrcIsTiled; + info.m_bCanConvertFormat = ( tex.m_Flags & Texture_t::CAN_CONVERT_FORMAT ) != 0; +#else + info.m_bTextureIsLockable = ( tex.m_Flags & Texture_t::IS_LOCKABLE ) != 0; +#endif + LoadSubTexture( info, xOffset, yOffset, srcStride ); +} + +//----------------------------------------------------------------------------- +// Volume texture upload +//----------------------------------------------------------------------------- +void CShaderAPIDx8::TexImageFromVTF( IVTFTexture *pVTF, int iVTFFrame ) +{ + LOCK_SHADERAPI(); + Assert( pVTF ); + AssertValidTextureHandle( GetModifyTextureHandle() ); + if ( !m_Textures.IsValidIndex( GetModifyTextureHandle() ) ) + { + return; + } + Texture_t& tex = GetTexture( GetModifyTextureHandle() ); + + // May need to switch textures.... + if (tex.m_SwitchNeeded) + { + AdvanceCurrentCopy( GetModifyTextureHandle() ); + tex.m_SwitchNeeded = false; + } + + TextureLoadInfo_t info; + info.m_TextureHandle = GetModifyTextureHandle(); + info.m_pTexture = GetModifyTexture(); + info.m_nLevel = 0; + info.m_nCopy = tex.m_CurrentCopy; + info.m_CubeFaceID = (D3DCUBEMAP_FACES)0; + info.m_nWidth = 0; + info.m_nHeight = 0; + info.m_nZOffset = 0; + info.m_SrcFormat = pVTF->Format(); + info.m_pSrcData = NULL; +#if defined( _X360 ) + info.m_bSrcIsTiled = pVTF->IsPreTiled(); + info.m_bCanConvertFormat = ( tex.m_Flags & Texture_t::CAN_CONVERT_FORMAT ) != 0; +#else + info.m_bTextureIsLockable = ( tex.m_Flags & Texture_t::IS_LOCKABLE ) != 0; +#endif + if ( pVTF->Depth() > 1 ) + { + LoadVolumeTextureFromVTF( info, pVTF, iVTFFrame ); + } + else if ( pVTF->IsCubeMap() ) + { + if ( HardwareConfig()->SupportsCubeMaps() ) + { + LoadCubeTextureFromVTF( info, pVTF, iVTFFrame ); + } + else + { + info.m_CubeFaceID = (D3DCUBEMAP_FACES)6; + LoadTextureFromVTF( info, pVTF, iVTFFrame ); + } + } + else + { + LoadTextureFromVTF( info, pVTF, iVTFFrame ); + } + SetModifyTexture( info.m_pTexture ); +} + + +//----------------------------------------------------------------------------- +// Is the texture resident? +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::IsTextureResident( ShaderAPITextureHandle_t textureHandle ) +{ + return true; +} + + +//----------------------------------------------------------------------------- +// Level of anisotropic filtering +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetAnisotropicLevel( int nAnisotropyLevel ) +{ + LOCK_SHADERAPI(); + + // NOTE: This must be called before the rest of the code in this function so + // anisotropic can be set per-texture to force it on! This will also avoid + // a possible infinite loop that existed before. + g_pShaderUtil->NoteAnisotropicLevel( nAnisotropyLevel ); + + // Never set this to 1. In the case we want it set to 1, we will use this to override + // aniso per-texture, so set it to something reasonable + if ( nAnisotropyLevel > g_pHardwareConfig->Caps().m_nMaxAnisotropy || nAnisotropyLevel <= 1 ) + { + // Set it to 1/4 the max but between 2-8 + nAnisotropyLevel = max( 2, min( 8, ( g_pHardwareConfig->Caps().m_nMaxAnisotropy / 4 ) ) ); + } + + // Set the D3D max insotropy state for all samplers + for ( int i = 0; i < g_pHardwareConfig->Caps().m_NumSamplers; ++i) + { + SamplerState(i).m_nAnisotropicLevel = nAnisotropyLevel; + SetSamplerState( i, D3DSAMP_MAXANISOTROPY, SamplerState(i).m_nAnisotropicLevel ); + } +} + + +//----------------------------------------------------------------------------- +// Sets the priority +//----------------------------------------------------------------------------- +void CShaderAPIDx8::TexSetPriority( int priority ) +{ +#if !defined( _X360 ) + LOCK_SHADERAPI(); + + // A hint to the cacher... + ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle(); + if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + return; + + Texture_t& tex = GetTexture( hModifyTexture ); + if ( tex.m_NumCopies > 1 ) + { + for (int i = 0; i < tex.m_NumCopies; ++i) + tex.GetTexture( i )->SetPriority( priority ); + } + else + { + tex.GetTexture()->SetPriority( priority ); + } +#endif +} + +void CShaderAPIDx8::TexLodClamp( int finest ) +{ + LOCK_SHADERAPI(); + + ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle(); + if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + return; + + Texture_t& tex = GetTexture( hModifyTexture ); + tex.m_FinestMipmapLevel = finest; +} + +void CShaderAPIDx8::TexLodBias( float bias ) +{ + LOCK_SHADERAPI(); + + ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle(); + if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + return; + + Texture_t& tex = GetTexture( hModifyTexture ); + tex.m_LodBias = bias; +} + + + +//----------------------------------------------------------------------------- +// Texturemapping state +//----------------------------------------------------------------------------- +void CShaderAPIDx8::TexWrap( ShaderTexCoordComponent_t coord, ShaderTexWrapMode_t wrapMode ) +{ + LOCK_SHADERAPI(); + + ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle(); + if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + return; + + D3DTEXTUREADDRESS address; + switch( wrapMode ) + { + case SHADER_TEXWRAPMODE_CLAMP: + address = D3DTADDRESS_CLAMP; + break; + case SHADER_TEXWRAPMODE_REPEAT: + address = D3DTADDRESS_WRAP; + break; + case SHADER_TEXWRAPMODE_BORDER: + address = D3DTADDRESS_BORDER; + break; + default: + address = D3DTADDRESS_CLAMP; + Warning( "CShaderAPIDx8::TexWrap: unknown wrapMode\n" ); + break; + } + + switch( coord ) + { + case SHADER_TEXCOORD_S: + GetTexture( hModifyTexture ).m_UTexWrap = address; + break; + case SHADER_TEXCOORD_T: + GetTexture( hModifyTexture ).m_VTexWrap = address; + break; + case SHADER_TEXCOORD_U: + GetTexture( hModifyTexture ).m_WTexWrap = address; + break; + default: + Warning( "CShaderAPIDx8::TexWrap: unknown coord\n" ); + break; + } +} + +void CShaderAPIDx8::TexMinFilter( ShaderTexFilterMode_t texFilterMode ) +{ + LOCK_SHADERAPI(); + + ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle(); + if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + return; + + switch( texFilterMode ) + { + case SHADER_TEXFILTERMODE_NEAREST: + GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_POINT; + GetTexture( hModifyTexture ).m_MipFilter = D3DTEXF_NONE; + break; + case SHADER_TEXFILTERMODE_LINEAR: + GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_LINEAR; + GetTexture( hModifyTexture ).m_MipFilter = D3DTEXF_NONE; + break; + case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_NEAREST: + GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_POINT; + GetTexture( hModifyTexture ).m_MipFilter = + GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_POINT : D3DTEXF_NONE; + break; + case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST: + GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_LINEAR; + GetTexture( hModifyTexture ).m_MipFilter = + GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_POINT : D3DTEXF_NONE; + break; + case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_LINEAR: + GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_POINT; + GetTexture( hModifyTexture ).m_MipFilter = + GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_LINEAR : D3DTEXF_NONE; + break; + case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR: + GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_LINEAR; + GetTexture( hModifyTexture ).m_MipFilter = + GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_LINEAR : D3DTEXF_NONE; + break; + case SHADER_TEXFILTERMODE_ANISOTROPIC: + GetTexture( hModifyTexture ).m_MinFilter = D3DTEXF_ANISOTROPIC; + GetTexture( hModifyTexture ).m_MipFilter = + GetTexture( hModifyTexture ).m_NumLevels != 1 ? D3DTEXF_LINEAR : D3DTEXF_NONE; + break; + default: + Warning( "CShaderAPIDx8::TexMinFilter: Unknown texFilterMode\n" ); + break; + } +} + +void CShaderAPIDx8::TexMagFilter( ShaderTexFilterMode_t texFilterMode ) +{ + LOCK_SHADERAPI(); + + ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle(); + if ( hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE ) + return; + + switch( texFilterMode ) + { + case SHADER_TEXFILTERMODE_NEAREST: + GetTexture( hModifyTexture ).m_MagFilter = D3DTEXF_POINT; + break; + case SHADER_TEXFILTERMODE_LINEAR: + GetTexture( hModifyTexture ).m_MagFilter = D3DTEXF_LINEAR; + break; + case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_NEAREST: + Warning( "CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_NEAREST_MIPMAP_NEAREST is invalid\n" ); + break; + case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST: + Warning( "CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST is invalid\n" ); + break; + case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_LINEAR: + Warning( "CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_NEAREST_MIPMAP_LINEAR is invalid\n" ); + break; + case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR: + Warning( "CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR is invalid\n" ); + break; + case SHADER_TEXFILTERMODE_ANISOTROPIC: + GetTexture( hModifyTexture ).m_MagFilter = g_pHardwareConfig->Caps().m_bSupportsMagAnisotropicFiltering ? D3DTEXF_ANISOTROPIC : D3DTEXF_LINEAR; + break; + default: + Warning( "CShaderAPIDx8::TexMAGFilter: Unknown texFilterMode\n" ); + break; + } +} + + +//----------------------------------------------------------------------------- +// Gets the matrix stack from the matrix mode +//----------------------------------------------------------------------------- + +int CShaderAPIDx8::GetMatrixStack( MaterialMatrixMode_t mode ) const +{ + Assert( mode >= 0 && mode < NUM_MATRIX_MODES ); + return mode; +} + +//----------------------------------------------------------------------------- +// Returns true if we're modulating constant color into the vertex color +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::IsModulatingVertexColor() const +{ + return m_TransitionTable.CurrentShadowShaderState()->m_ModulateConstantColor; +} + + +//----------------------------------------------------------------------------- +// Material property (used to deal with overbright for lights) +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SetDefaultMaterial() +{ +#if !defined( _X360 ) + D3DMATERIAL mat; + mat.Diffuse.r = mat.Diffuse.g = mat.Diffuse.b = mat.Diffuse.a = 1.0f; + mat.Ambient.r = mat.Ambient.g = mat.Ambient.b = mat.Ambient.a = 0.0f; + mat.Specular.r = mat.Specular.g = mat.Specular.b = mat.Specular.a = 0.0f; + mat.Emissive.r = mat.Emissive.g = mat.Emissive.b = mat.Emissive.a = 0.0f; + mat.Power = 1.0f; + Dx9Device()->SetMaterial( &mat ); +#endif +} + +//----------------------------------------------------------------------------- +// lighting related methods +//----------------------------------------------------------------------------- + +void CShaderAPIDx8::SetAmbientLight( float r, float g, float b ) +{ + LOCK_SHADERAPI(); + unsigned int ambient = D3DCOLOR_ARGB( 255, (int)(r * 255), + (int)(g * 255), (int)(b * 255) ); + if (ambient != m_DynamicState.m_Ambient) + { + m_DynamicState.m_Ambient = ambient; + SetSupportedRenderState( D3DRS_AMBIENT, ambient ); + } +} + +void CShaderAPIDx8::SetLightingOrigin( Vector vLightingOrigin ) +{ + if ( vLightingOrigin != m_DynamicState.m_vLightingOrigin ) + { + FlushBufferedPrimitives(); + m_DynamicState.m_vLightingOrigin = vLightingOrigin; + } +} + +//#define NO_LOCAL_LIGHTS +void CShaderAPIDx8::SetLight( int lightNum, const LightDesc_t& desc_ ) +{ + LOCK_SHADERAPI(); +#ifdef NO_LOCAL_LIGHTS + LightDesc_t desc = desc_; + desc.m_Type = MATERIAL_LIGHT_DISABLE; +#else + LightDesc_t &desc = const_cast<LightDesc_t &>(desc_); // to permit '&' +#endif + Assert( lightNum < g_pHardwareConfig->Caps().m_MaxNumLights && lightNum >= 0 ); + + if( lightNum >= g_pHardwareConfig->Caps().m_MaxNumLights || lightNum < 0 ) + return; + + m_DynamicState.m_LightDescs[lightNum] = desc; + + FlushBufferedPrimitives(); + + if (desc.m_Type == MATERIAL_LIGHT_DISABLE) + { + if (m_DynamicState.m_LightEnable[lightNum]) + { + m_DynamicState.m_LightEnableChanged[lightNum] = STATE_CHANGED; + m_DynamicState.m_LightEnable[lightNum] = false; + } + return; + } + + if (!m_DynamicState.m_LightEnable[lightNum]) + { + m_DynamicState.m_LightEnableChanged[lightNum] = STATE_CHANGED; + m_DynamicState.m_LightEnable[lightNum] = true; + } + + D3DLIGHT light; + switch( desc.m_Type ) + { + case MATERIAL_LIGHT_POINT: + light.Type = D3DLIGHT_POINT; + light.Range = desc.m_Range; + break; + + case MATERIAL_LIGHT_DIRECTIONAL: + light.Type = D3DLIGHT_DIRECTIONAL; + light.Range = 1e12; // This is supposed to be ignored + break; + + case MATERIAL_LIGHT_SPOT: + light.Type = D3DLIGHT_SPOT; + light.Range = desc.m_Range; + break; + + default: + m_DynamicState.m_LightEnable[lightNum] = false; + return; + } + + // This is a D3D limitation + Assert( (light.Range >= 0) && (light.Range <= sqrt(FLT_MAX)) ); + + memcpy( &light.Diffuse, &desc.m_Color[0], 3*sizeof(float) ); + memcpy( &light.Specular, &desc.m_Color[0], 3*sizeof(float) ); + light.Diffuse.a = 1.0f; + light.Specular.a = 1.0f; + light.Ambient.a = light.Ambient.b = light.Ambient.g = light.Ambient.r = 0; + memcpy( &light.Position, &desc.m_Position[0], 3 * sizeof(float) ); + memcpy( &light.Direction, &desc.m_Direction[0], 3 * sizeof(float) ); + light.Falloff = desc.m_Falloff; + light.Attenuation0 = desc.m_Attenuation0; + light.Attenuation1 = desc.m_Attenuation1; + light.Attenuation2 = desc.m_Attenuation2; + + // normalize light color... + light.Theta = desc.m_Theta; + light.Phi = desc.m_Phi; + if (light.Phi > M_PI) + light.Phi = M_PI; + + // This piece of crap line of code is because if theta gets too close to phi, + // we get no light at all. + if (light.Theta - light.Phi > -1e-3) + light.Theta = light.Phi - 1e-3; + + m_DynamicState.m_LightChanged[lightNum] = STATE_CHANGED; + memcpy( &m_DynamicState.m_Lights[lightNum], &light, sizeof(light) ); +} + +void CShaderAPIDx8::DisableAllLocalLights() +{ + LOCK_SHADERAPI(); + bool bFlushed = false; + for ( int lightNum = 0; lightNum < MAX_NUM_LIGHTS; lightNum++ ) + { + if (m_DynamicState.m_LightEnable[lightNum]) + { + if ( !bFlushed ) + { + FlushBufferedPrimitives(); + bFlushed = true; + } + m_DynamicState.m_LightDescs[lightNum].m_Type = MATERIAL_LIGHT_DISABLE; + m_DynamicState.m_LightEnableChanged[lightNum] = STATE_CHANGED; + m_DynamicState.m_LightEnable[lightNum] = false; + } + } +} + +int CShaderAPIDx8::GetMaxLights( void ) const +{ + return g_pHardwareConfig->Caps().m_MaxNumLights; +} + +const LightDesc_t& CShaderAPIDx8::GetLight( int lightNum ) const +{ + Assert( lightNum < g_pHardwareConfig->Caps().m_MaxNumLights && lightNum >= 0 ); + return m_DynamicState.m_LightDescs[lightNum]; +} + +//----------------------------------------------------------------------------- +// Ambient cube +//----------------------------------------------------------------------------- + +//#define NO_AMBIENT_CUBE 1 +void CShaderAPIDx8::SetAmbientLightCube( Vector4D cube[6] ) +{ + LOCK_SHADERAPI(); +/* + int i; + for( i = 0; i < 6; i++ ) + { + ColorClamp( cube[i].AsVector3D() ); +// if( i == 0 ) +// { +// Warning( "%d: %f %f %f\n", i, cube[i][0], cube[i][1], cube[i][2] ); +// } + } +*/ + if (memcmp(&m_DynamicState.m_AmbientLightCube[0][0], cube, 6 * sizeof(Vector4D))) + { + memcpy( &m_DynamicState.m_AmbientLightCube[0][0], cube, 6 * sizeof(Vector4D) ); + +#ifdef NO_AMBIENT_CUBE + memset( &m_DynamicState.m_AmbientLightCube[0][0], 0, 6 * sizeof(Vector4D) ); +#endif + +//#define DEBUG_AMBIENT_CUBE + +#ifdef DEBUG_AMBIENT_CUBE + m_DynamicState.m_AmbientLightCube[0][0] = 1.0f; + m_DynamicState.m_AmbientLightCube[0][1] = 0.0f; + m_DynamicState.m_AmbientLightCube[0][2] = 0.0f; + + m_DynamicState.m_AmbientLightCube[1][0] = 0.0f; + m_DynamicState.m_AmbientLightCube[1][1] = 1.0f; + m_DynamicState.m_AmbientLightCube[1][2] = 0.0f; + + m_DynamicState.m_AmbientLightCube[2][0] = 0.0f; + m_DynamicState.m_AmbientLightCube[2][1] = 0.0f; + m_DynamicState.m_AmbientLightCube[2][2] = 1.0f; + + m_DynamicState.m_AmbientLightCube[3][0] = 1.0f; + m_DynamicState.m_AmbientLightCube[3][1] = 0.0f; + m_DynamicState.m_AmbientLightCube[3][2] = 1.0f; + + m_DynamicState.m_AmbientLightCube[4][0] = 1.0f; + m_DynamicState.m_AmbientLightCube[4][1] = 1.0f; + m_DynamicState.m_AmbientLightCube[4][2] = 0.0f; + + m_DynamicState.m_AmbientLightCube[5][0] = 0.0f; + m_DynamicState.m_AmbientLightCube[5][1] = 1.0f; + m_DynamicState.m_AmbientLightCube[5][2] = 1.0f; +#endif + + m_CachedAmbientLightCube = STATE_CHANGED; + } +} + +void CShaderAPIDx8::SetVertexShaderStateAmbientLightCube() +{ + if (m_CachedAmbientLightCube & STATE_CHANGED_VERTEX_SHADER) + { + SetVertexShaderConstant( VERTEX_SHADER_AMBIENT_LIGHT, m_DynamicState.m_AmbientLightCube[0].Base(), 6 ); + m_CachedAmbientLightCube &= ~STATE_CHANGED_VERTEX_SHADER; + } +} + + +void CShaderAPIDx8::SetPixelShaderStateAmbientLightCube( int pshReg, bool bForceToBlack ) +{ + float *pCubeBase; + Vector4D tempCube[6]; + + if( bForceToBlack ) + { + for ( int i=0; i<6 ; i++ ) + tempCube[i].Init(); + + pCubeBase = tempCube[0].Base(); + } + else + { + pCubeBase = m_DynamicState.m_AmbientLightCube[0].Base(); + } + + SetPixelShaderConstant( pshReg, pCubeBase, 6 ); +} + +float CShaderAPIDx8::GetAmbientLightCubeLuminance( void ) +{ + Vector4D vLuminance( 0.3f, 0.59f, 0.11f, 0.0f ); + float fLuminance = 0.0f; + + for (int i=0; i<6; i++) + { + fLuminance += vLuminance.Dot( m_DynamicState.m_AmbientLightCube[i] ); + } + + return fLuminance / 6.0f; +} + +static inline RECT* RectToRECT( Rect_t *pSrcRect, RECT &dstRect ) +{ + if ( !pSrcRect ) + return NULL; + + dstRect.left = pSrcRect->x; + dstRect.top = pSrcRect->y; + dstRect.right = pSrcRect->x + pSrcRect->width; + dstRect.bottom = pSrcRect->y + pSrcRect->height; + return &dstRect; +} + +void CShaderAPIDx8::CopyRenderTargetToTextureEx( ShaderAPITextureHandle_t textureHandle, int nRenderTargetID, Rect_t *pSrcRect, Rect_t *pDstRect ) +{ + LOCK_SHADERAPI(); + VPROF_BUDGET( "CShaderAPIDx8::CopyRenderTargetToTexture", "Refraction overhead" ); + + if ( !TextureIsAllocated( textureHandle ) ) + return; + +#if defined( PIX_INSTRUMENTATION ) + { + const char *pRT = ( nRenderTargetID < 0 ) ? "DS" : "RT"; + + if ( textureHandle == SHADER_RENDERTARGET_NONE ) + { + pRT = "None"; + } + else if ( textureHandle != SHADER_RENDERTARGET_BACKBUFFER ) + { + Texture_t &tex = GetTexture( textureHandle ); + pRT = tex.m_DebugName.String(); + } + + char buf[256]; + sprintf( buf, "CopyRTToTexture:%s", pRT ? pRT : "?" ); + BeginPIXEvent( 0xFFFFFFFF, buf ); + EndPIXEvent(); + } +#endif + + // Don't flush here!! If you have to flush here, then there is a driver bug. + // FlushHardware( ); + + AssertValidTextureHandle( textureHandle ); + Texture_t *pTexture = &GetTexture( textureHandle ); + Assert( pTexture ); + IDirect3DTexture *pD3DTexture = (IDirect3DTexture *)pTexture->GetTexture(); + Assert( pD3DTexture ); + +#if !defined( _X360 ) + IDirect3DSurface* pRenderTargetSurface; + HRESULT hr = Dx9Device()->GetRenderTarget( nRenderTargetID, &pRenderTargetSurface ); + if ( FAILED( hr ) ) + { + Assert( 0 ); + return; + } + + IDirect3DSurface *pDstSurf; + hr = pD3DTexture->GetSurfaceLevel( 0, &pDstSurf ); + Assert( !FAILED( hr ) ); + if ( FAILED( hr ) ) + { + pRenderTargetSurface->Release(); + return; + } + + bool tryblit = true; + if ( tryblit ) + { + RECORD_COMMAND( DX8_COPY_FRAMEBUFFER_TO_TEXTURE, 1 ); + RECORD_INT( textureHandle ); + + RECT srcRect, dstRect; + hr = Dx9Device()->StretchRect( pRenderTargetSurface, RectToRECT( pSrcRect, srcRect ), + pDstSurf, RectToRECT( pDstRect, dstRect ), D3DTEXF_LINEAR ); + Assert( !FAILED( hr ) ); + } + + pDstSurf->Release(); + pRenderTargetSurface->Release(); +#else + DWORD flags = 0; + switch( nRenderTargetID ) + { + case -1: + flags = D3DRESOLVE_DEPTHSTENCIL | D3DRESOLVE_FRAGMENT0; + break; + case 0: + flags = D3DRESOLVE_RENDERTARGET0; + break; + case 1: + case 2: + case 3: + // not supporting MRT + Assert( 0 ); + return; + NO_DEFAULT + }; + + // not prepared to handle mip mapping yet + Assert( pD3DTexture->GetLevelCount() == 1 ); + + D3DPOINT dstPoint = { 0 }; + if ( pDstRect ) + { + dstPoint.x = pDstRect->x; + dstPoint.y = pDstRect->y; + } + + int destWidth, destHeight; + if( pDstRect ) + { + destWidth = pDstRect->width; + destHeight = pDstRect->height; + + Assert( (destWidth <= pTexture->GetWidth()) && (destHeight <= pTexture->GetHeight()) ); + } + else + { + destWidth = pTexture->GetWidth(); + destHeight = pTexture->GetHeight(); + } + + RECT srcRect; + RECT *pResolveRect = NULL; + int srcWidth, srcHeight; + if ( pSrcRect ) + { + RectToRECT( pSrcRect, srcRect ); + pResolveRect = &srcRect; + + // resolve has no stretching ability, and we can only compensate when doing a resolve to a whole texture larger than the source + Assert( !pDstRect || ( pSrcRect->width <= pDstRect->width && pSrcRect->height <= pDstRect->height ) ); + + srcWidth = pSrcRect->width; + srcHeight = pSrcRect->height; + } + else + { + srcRect.left = srcRect.top = 0; + srcRect.right = m_DynamicState.m_Viewport.Width; + srcRect.bottom = m_DynamicState.m_Viewport.Height; + if( (srcRect.right < 0) || (srcRect.bottom < 0) ) + { + if( m_UsingTextureRenderTarget ) + { + srcRect.right = m_ViewportMaxWidth; + srcRect.bottom = m_ViewportMaxHeight; + } + else + { + int w,h; + GetBackBufferDimensions( w, h ); + srcRect.right = w; + srcRect.bottom = h; + } + } + srcWidth = srcRect.right; + srcHeight = srcRect.bottom; + } + + if( (srcWidth != destWidth) || (srcHeight != destHeight) ) + { + //Not a 1:1 resolve, we should only have gotten this far if we can downsize the target texture to compensate + Assert( (destWidth > srcWidth) && (destHeight > srcHeight) && (dstPoint.x == 0) && (dstPoint.y == 0) ); + + //What we're doing is telling D3D that this texture is smaller than it is so the resolve is 1:1. + //We leave the texture in this state until it resolves from something bigger. + //All outside code still thinks this texture is it's original size. And it still owns enough memory to go back to it's original size. + pD3DTexture->Format.Size.TwoD.Width = srcWidth - 1; + pD3DTexture->Format.Size.TwoD.Height = srcHeight - 1; //no idea why they store it as size-1, but they do + pResolveRect = NULL; + } + else + { + //restore D3D texture to full size in case it was previously downsized + pD3DTexture->Format.Size.TwoD.Width = pTexture->GetWidth() - 1; + pD3DTexture->Format.Size.TwoD.Height = pTexture->GetHeight() - 1; //no idea why they store it as size-1, but they do + } + + // if we convert to srgb format, we need the original format for reverting. We only need the first DWORD of GPUTEXTURE_FETCH_CONSTANT. + DWORD linearFormatBackup = pD3DTexture->Format.dword[0]; + if ( !( flags & D3DRESOLVE_DEPTHSTENCIL ) && ( m_DynamicState.m_bSRGBWritesEnabled ) ) + { + // we need a matched resolve regarding sRGB to get values transfered as-is + // when the surface is sRGB, use the corresponding sRGB texture + pD3DTexture->Format.SignX = pD3DTexture->Format.SignY = pD3DTexture->Format.SignZ = 3; + } + + HRESULT hr = Dx9Device()->Resolve( flags, (D3DRECT*)pResolveRect, pD3DTexture, &dstPoint, 0, 0, NULL, 0, 0, NULL ); + Assert( !FAILED( hr ) ); + + pD3DTexture->Format.dword[0] = linearFormatBackup; +#endif +} + +void CShaderAPIDx8::CopyRenderTargetToScratchTexture( ShaderAPITextureHandle_t srcRt, ShaderAPITextureHandle_t dstTex, Rect_t *pSrcRect, Rect_t *pDstRect ) +{ + LOCK_SHADERAPI(); + + if ( !TextureIsAllocated( srcRt ) || !TextureIsAllocated( dstTex ) ) + { + Assert( !"Fix that render target or dest texture aren't allocated." ); + return; + } + + HRESULT hr = D3D_OK; + + IDirect3DSurface9* srcSurf = NULL; + AssertValidTextureHandle( srcRt ); + Texture_t *pSrcRt = &GetTexture( srcRt ); + Assert( pSrcRt ); + IDirect3DTexture *pD3DSrcRt = ( IDirect3DTexture * ) pSrcRt->GetTexture(); + Assert( pD3DSrcRt ); + hr = pD3DSrcRt->GetSurfaceLevel( 0, &srcSurf ); + Assert( SUCCEEDED( hr ) && srcSurf ); + + IDirect3DSurface9* dstSurf = NULL; + AssertValidTextureHandle( dstTex ); + Texture_t *pDstTex = &GetTexture( dstTex ); + Assert( pDstTex ); + IDirect3DTexture *pD3DDstTex = ( IDirect3DTexture * ) pDstTex->GetTexture(); + Assert( pD3DDstTex ); + hr = pD3DDstTex->GetSurfaceLevel( 0, &dstSurf ); + Assert( SUCCEEDED( hr ) && dstSurf ); + + // This does it. + hr = Dx9Device()->GetRenderTargetData( srcSurf, dstSurf ); + Assert( SUCCEEDED( hr ) ); + + srcSurf->Release(); + dstSurf->Release(); +} + +//------------------------------------------------------------------------- +// Allows locking and unlocking of very specific surface types. pOutBits and pOutPitch will not be touched if +// the lock fails. +//------------------------------------------------------------------------- +void CShaderAPIDx8::LockRect( void** pOutBits, int* pOutPitch, ShaderAPITextureHandle_t texHandle, int mipmap, int x, int y, int w, int h, bool bWrite, bool bRead ) +{ + LOCK_SHADERAPI(); + + Assert( pOutBits ); + Assert( pOutPitch ); + + if ( !TextureIsAllocated( texHandle ) ) + { + Assert( !"Fix that texture isn't allocated." ); + return; + } + + HRESULT hr = D3D_OK; + IDirect3DSurface9* surf = NULL; + AssertValidTextureHandle( texHandle ); + Texture_t *pTex = &GetTexture( texHandle ); + Assert( pTex ); + IDirect3DTexture *pD3DTex = ( IDirect3DTexture * ) pTex->GetTexture(); + Assert( pD3DTex ); + + hr = pD3DTex->GetSurfaceLevel( mipmap, &surf ); + Assert( SUCCEEDED( hr ) && surf ); + + D3DLOCKED_RECT lockRect = { 0 }; + RECT srcRect = { x, y, w, h }; + DWORD flags = 0; + + if ( !bRead && !bWrite ) + { + Assert( !"Asking to neither read nor write? Probably a caller bug." ); + goto cleanup; + } + + if ( bRead && !bWrite ) + flags = D3DLOCK_READONLY; + + hr = surf->LockRect( &lockRect, &srcRect, flags ); + if ( FAILED( hr ) ) + { + Assert( !"Lock failed, look into why." ); + goto cleanup; + } + + (*pOutBits) = lockRect.pBits; + (*pOutPitch) = lockRect.Pitch; + +cleanup: + surf->Release(); +} + +void CShaderAPIDx8::UnlockRect( ShaderAPITextureHandle_t texHandle, int mipmap ) +{ + LOCK_SHADERAPI(); + + if ( !TextureIsAllocated( texHandle ) ) + { + Assert( !"Fix that texture isn't allocated." ); + return; + } + + HRESULT hr = D3D_OK; + IDirect3DSurface9* surf = NULL; + AssertValidTextureHandle( texHandle ); + Texture_t *pTex = &GetTexture( texHandle ); + Assert( pTex ); + IDirect3DTexture *pD3DTex = ( IDirect3DTexture * ) pTex->GetTexture(); + Assert( pD3DTex ); + + hr = pD3DTex->GetSurfaceLevel( mipmap, &surf ); + Assert( SUCCEEDED( hr ) && surf ); + + hr = surf->UnlockRect(); + Assert( SUCCEEDED( hr ) ); + + surf->Release(); +} + +static float GetAspectRatio( const Texture_t* pTex ) +{ + Assert( pTex ); + if ( pTex->m_Height != 0 ) + return float( pTex->m_Width ) / float( pTex->m_Height ); + + Assert( !"Height of texture is 0, that seems like a bug." ); + return 0.0f; +} + +struct TextureExtents_t +{ + int width; + int height; + int depth; + int mipmaps; + + int fine; + int coarse; + + TextureExtents_t() : width( 0 ), height( 0 ), depth( 0 ), mipmaps( 0 ), fine( 0 ), coarse( 0 ) { } +}; + +// Returns positive integer on success, 0 or <0 on failure. +static int FindCommonMipmapRange( int *pOutSrcFine, int *pOutDstFine, Texture_t *pSrcTex, Texture_t *pDstTex ) +{ + Assert( pOutSrcFine && pOutDstFine ); + Assert( pSrcTex && pDstTex ); + + if ( GetAspectRatio( pSrcTex ) != GetAspectRatio( pDstTex ) ) + return 0; + + TextureExtents_t src, + dst; + + // LOD Clamp indicates that there's no actual data in the finer mipmap levels yet, so respect it when determining + // the source and destination levels that could have data. + const int srcLodClamp = pSrcTex->GetLodClamp(); + src.width = Max( 1, pSrcTex->GetWidth() >> srcLodClamp ); + src.height = Max( 1, pSrcTex->GetHeight() >> srcLodClamp ); + src.depth = Max( 1, pSrcTex->GetDepth() >> srcLodClamp ); + src.mipmaps = pSrcTex->m_NumLevels - srcLodClamp; + Assert( src.mipmaps >= 1 ); + + + const int dstLodClamp = pDstTex->GetLodClamp(); + dst.width = Max( 1, pDstTex->GetWidth() >> dstLodClamp ); + dst.height = Max( 1, pDstTex->GetHeight() >> dstLodClamp ); + dst.depth = Max( 1, pDstTex->GetDepth() >> dstLodClamp ); + dst.mipmaps = pDstTex->m_NumLevels - dstLodClamp; + Assert( dst.mipmaps >= 1 ); + + TextureExtents_t *pLarger = NULL, + *pSmaller = NULL; + + if ( src.width >= dst.width && src.height >= dst.height && src.depth >= dst.depth ) + { + pLarger = &src; + pSmaller = &dst; + } + else + { + pLarger = &dst; + pSmaller = &src; + } + + // Since we are same aspect ratio, only need to test one dimension + while ( ( pLarger->width >> pLarger->fine ) > pSmaller->width ) + { + ++pLarger->fine; + --pLarger->mipmaps; + } + + ( *pOutSrcFine ) = src.fine; + ( *pOutDstFine ) = dst.fine; + + return Min( src.mipmaps, dst.mipmaps ); +} + +void CShaderAPIDx8::CopyTextureToTexture( ShaderAPITextureHandle_t srcTex, ShaderAPITextureHandle_t dstTex ) +{ + LOCK_SHADERAPI(); + + AssertValidTextureHandle( srcTex ); + AssertValidTextureHandle( dstTex ); + + Assert( TextureIsAllocated( srcTex ) && TextureIsAllocated( dstTex ) ); + + Texture_t *pSrcTex = &GetTexture( srcTex ); + Texture_t *pDstTex = &GetTexture( dstTex ); + + Assert( pSrcTex && pDstTex ); + + // Must have same image format + Assert( pSrcTex->GetImageFormat() == pDstTex->GetImageFormat() ); + + int srcFine = 0, + dstFine = 0; + + int mipmapCount = FindCommonMipmapRange( &srcFine, &dstFine, pSrcTex, pDstTex ); + + if ( mipmapCount <= 0 ) + { + // This is legit for things that are streamed in that are very small (near the 32x32 cutoff we do at the + // tip of the mipmap pyramid). But leaving it here because it's useful if you're tracking a specific bug. + // Warning( "Attempted to copy textures that had non-overlapping mipmap pyramids. This has failed and no copy has taken place.\n" ); + return; + } + + IDirect3DTexture* pSrcD3DTex = ( IDirect3DTexture * ) pSrcTex->GetTexture(); + IDirect3DTexture* pDstD3DTex = ( IDirect3DTexture * ) pDstTex->GetTexture(); + + HRESULT hr = S_OK; + for ( int i = 0; i < mipmapCount; ++i ) + { + int srcMipmap = srcFine + i; + int dstMipmap = dstFine + i; + + IDirect3DSurface9* pSrcSurf = NULL; + IDirect3DSurface9* pDstSurf = NULL; + + hr = pSrcD3DTex->GetSurfaceLevel( srcMipmap, &pSrcSurf ); + Assert( SUCCEEDED( hr ) && pSrcSurf ); + + hr = pDstD3DTex->GetSurfaceLevel( dstMipmap, &pDstSurf ); + Assert( SUCCEEDED( hr ) && pDstSurf ); + + hr = g_pD3DDevice->StretchRect( pSrcSurf, NULL, pDstSurf, NULL, D3DTEXF_NONE ); + Assert( SUCCEEDED( hr ) ); + + pSrcSurf->Release(); + pDstSurf->Release(); + } +} + + + +void CShaderAPIDx8::CopyRenderTargetToTexture( ShaderAPITextureHandle_t textureHandle ) +{ + LOCK_SHADERAPI(); + CopyRenderTargetToTextureEx( textureHandle, 0 ); +} + + +void CShaderAPIDx8::CopyTextureToRenderTargetEx( int nRenderTargetID, ShaderAPITextureHandle_t textureHandle, Rect_t *pSrcRect, Rect_t *pDstRect ) +{ + LOCK_SHADERAPI(); + VPROF( "CShaderAPIDx8::CopyRenderTargetToTexture" ); + + if ( !TextureIsAllocated( textureHandle ) ) + return; + + // Don't flush here!! If you have to flush here, then there is a driver bug. + // FlushHardware( ); + + AssertValidTextureHandle( textureHandle ); + Texture_t *pTexture = &GetTexture( textureHandle ); + Assert( pTexture ); + IDirect3DTexture *pD3DTexture = (IDirect3DTexture *)pTexture->GetTexture(); + Assert( pD3DTexture ); + +#if !defined( _X360 ) + IDirect3DSurface* pRenderTargetSurface; + HRESULT hr = Dx9Device()->GetRenderTarget( nRenderTargetID, &pRenderTargetSurface ); + if ( FAILED( hr ) ) + { + Assert( 0 ); + return; + } + + IDirect3DSurface *pDstSurf; + hr = pD3DTexture->GetSurfaceLevel( 0, &pDstSurf ); + Assert( !FAILED( hr ) ); + if ( FAILED( hr ) ) + { + pRenderTargetSurface->Release(); + return; + } + + bool tryblit = true; + if ( tryblit ) + { + RECORD_COMMAND( DX8_COPY_FRAMEBUFFER_TO_TEXTURE, 1 ); + RECORD_INT( textureHandle ); + + RECT srcRect, dstRect; + hr = Dx9Device()->StretchRect( pDstSurf, RectToRECT( pSrcRect, srcRect ), + pRenderTargetSurface, RectToRECT( pDstRect, dstRect ), D3DTEXF_LINEAR ); + Assert( !FAILED( hr ) ); + } + + pDstSurf->Release(); + pRenderTargetSurface->Release(); +#else + Assert( 0 ); +#endif +} + + +//----------------------------------------------------------------------------- +// modifies the vertex data when necessary +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ModifyVertexData( ) +{ + // this should be a dead code path + Assert( 0 ); +#if 0 + // We have to modulate the vertex color by the constant color sometimes + if (IsModulatingVertexColor()) + { + m_ModifyBuilder.Reset(); + + float factor[4]; + unsigned char* pColor = (unsigned char*)&m_DynamicState.m_ConstantColor; + factor[0] = pColor[0] / 255.0f; + factor[1] = pColor[1] / 255.0f; + factor[2] = pColor[2] / 255.0f; + factor[3] = pColor[3] / 255.0f; + + for ( int i = 0; i < m_ModifyBuilder.VertexCount(); ++i ) + { + unsigned int color = m_ModifyBuilder.Color(); + unsigned char* pVertexColor = (unsigned char*)&color; + + pVertexColor[0] = (unsigned char)((float)pVertexColor[0] * factor[0]); + pVertexColor[1] = (unsigned char)((float)pVertexColor[1] * factor[1]); + pVertexColor[2] = (unsigned char)((float)pVertexColor[2] * factor[2]); + pVertexColor[3] = (unsigned char)((float)pVertexColor[3] * factor[3]); + m_ModifyBuilder.Color4ubv( pVertexColor ); + + m_ModifyBuilder.AdvanceVertex(); + } + } +#endif +} + +static const char *TextureArgToString( int arg ) +{ + static char buf[128]; + switch( arg & D3DTA_SELECTMASK ) + { + case D3DTA_DIFFUSE: + strcpy( buf, "D3DTA_DIFFUSE" ); + break; + case D3DTA_CURRENT: + strcpy( buf, "D3DTA_CURRENT" ); + break; + case D3DTA_TEXTURE: + strcpy( buf, "D3DTA_TEXTURE" ); + break; + case D3DTA_TFACTOR: + strcpy( buf, "D3DTA_TFACTOR" ); + break; + case D3DTA_SPECULAR: + strcpy( buf, "D3DTA_SPECULAR" ); + break; + case D3DTA_TEMP: + strcpy( buf, "D3DTA_TEMP" ); + break; + default: + strcpy( buf, "<ERROR>" ); + break; + } + + if( arg & D3DTA_COMPLEMENT ) + { + strcat( buf, "|D3DTA_COMPLEMENT" ); + } + if( arg & D3DTA_ALPHAREPLICATE ) + { + strcat( buf, "|D3DTA_ALPHAREPLICATE" ); + } + return buf; +} + +static const char *TextureOpToString( D3DTEXTUREOP op ) +{ + switch( op ) + { + case D3DTOP_DISABLE: + return "D3DTOP_DISABLE"; + case D3DTOP_SELECTARG1: + return "D3DTOP_SELECTARG1"; + case D3DTOP_SELECTARG2: + return "D3DTOP_SELECTARG2"; + case D3DTOP_MODULATE: + return "D3DTOP_MODULATE"; + case D3DTOP_MODULATE2X: + return "D3DTOP_MODULATE2X"; + case D3DTOP_MODULATE4X: + return "D3DTOP_MODULATE4X"; + case D3DTOP_ADD: + return "D3DTOP_ADD"; + case D3DTOP_ADDSIGNED: + return "D3DTOP_ADDSIGNED"; + case D3DTOP_ADDSIGNED2X: + return "D3DTOP_ADDSIGNED2X"; + case D3DTOP_SUBTRACT: + return "D3DTOP_SUBTRACT"; + case D3DTOP_ADDSMOOTH: + return "D3DTOP_ADDSMOOTH"; + case D3DTOP_BLENDDIFFUSEALPHA: + return "D3DTOP_BLENDDIFFUSEALPHA"; + case D3DTOP_BLENDTEXTUREALPHA: + return "D3DTOP_BLENDTEXTUREALPHA"; + case D3DTOP_BLENDFACTORALPHA: + return "D3DTOP_BLENDFACTORALPHA"; + case D3DTOP_BLENDTEXTUREALPHAPM: + return "D3DTOP_BLENDTEXTUREALPHAPM"; + case D3DTOP_BLENDCURRENTALPHA: + return "D3DTOP_BLENDCURRENTALPHA"; + case D3DTOP_PREMODULATE: + return "D3DTOP_PREMODULATE"; + case D3DTOP_MODULATEALPHA_ADDCOLOR: + return "D3DTOP_MODULATEALPHA_ADDCOLOR"; + case D3DTOP_MODULATECOLOR_ADDALPHA: + return "D3DTOP_MODULATECOLOR_ADDALPHA"; + case D3DTOP_MODULATEINVALPHA_ADDCOLOR: + return "D3DTOP_MODULATEINVALPHA_ADDCOLOR"; + case D3DTOP_MODULATEINVCOLOR_ADDALPHA: + return "D3DTOP_MODULATEINVCOLOR_ADDALPHA"; + case D3DTOP_BUMPENVMAP: + return "D3DTOP_BUMPENVMAP"; + case D3DTOP_BUMPENVMAPLUMINANCE: + return "D3DTOP_BUMPENVMAPLUMINANCE"; + case D3DTOP_DOTPRODUCT3: + return "D3DTOP_DOTPRODUCT3"; + case D3DTOP_MULTIPLYADD: + return "D3DTOP_MULTIPLYADD"; + case D3DTOP_LERP: + return "D3DTOP_LERP"; + default: + return "<ERROR>"; + } +} + +static const char *BlendModeToString( int blendMode ) +{ + switch( blendMode ) + { + case D3DBLEND_ZERO: + return "D3DBLEND_ZERO"; + case D3DBLEND_ONE: + return "D3DBLEND_ONE"; + case D3DBLEND_SRCCOLOR: + return "D3DBLEND_SRCCOLOR"; + case D3DBLEND_INVSRCCOLOR: + return "D3DBLEND_INVSRCCOLOR"; + case D3DBLEND_SRCALPHA: + return "D3DBLEND_SRCALPHA"; + case D3DBLEND_INVSRCALPHA: + return "D3DBLEND_INVSRCALPHA"; + case D3DBLEND_DESTALPHA: + return "D3DBLEND_DESTALPHA"; + case D3DBLEND_INVDESTALPHA: + return "D3DBLEND_INVDESTALPHA"; + case D3DBLEND_DESTCOLOR: + return "D3DBLEND_DESTCOLOR"; + case D3DBLEND_INVDESTCOLOR: + return "D3DBLEND_INVDESTCOLOR"; + case D3DBLEND_SRCALPHASAT: + return "D3DBLEND_SRCALPHASAT"; +#if !defined( _X360 ) + case D3DBLEND_BOTHSRCALPHA: + return "D3DBLEND_BOTHSRCALPHA"; + case D3DBLEND_BOTHINVSRCALPHA: + return "D3DBLEND_BOTHINVSRCALPHA"; +#endif + default: + return "<ERROR>"; + } +} + +//----------------------------------------------------------------------------- +// Spew Board State +//----------------------------------------------------------------------------- +void CShaderAPIDx8::SpewBoardState() +{ + // FIXME: This has regressed + return; +#ifdef DEBUG_BOARD_STATE +/* + { + static ID3DXFont* pFont = 0; + if (!pFont) + { + HFONT hFont = CreateFont( 0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, + ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, + DEFAULT_PITCH | FF_MODERN, 0 ); + Assert( hFont != 0 ); + + HRESULT hr = D3DXCreateFont( Dx9Device(), hFont, &pFont ); + } + + static char buf[1024]; + static RECT r = { 0, 0, 640, 480 }; + + if (m_DynamicState.m_VertexBlend == 0) + return; + +#if 1 + D3DXMATRIX* m = &GetTransform(MATERIAL_MODEL); + D3DXMATRIX* m2 = &GetTransform(MATERIAL_MODEL + 1); + sprintf(buf,"FVF %x\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n", + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n", + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n", + ShaderManager()->GetCurrentVertexShader(), + m->m[0][0], m->m[0][1], m->m[0][2], m->m[0][3], + m->m[1][0], m->m[1][1], m->m[1][2], m->m[1][3], + m->m[2][0], m->m[2][1], m->m[2][2], m->m[2][3], + m->m[3][0], m->m[3][1], m->m[3][2], m->m[3][3], + m2->m[0][0], m2->m[0][1], m2->m[0][2], m2->m[0][3], + m2->m[1][0], m2->m[1][1], m2->m[1][2], m2->m[1][3], + m2->m[2][0], m2->m[2][1], m2->m[2][2], m2->m[2][3], + m2->m[3][0], m2->m[3][1], m2->m[3][2], m2->m[3][3] + ); +#else + Vector4D *pVec2 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODELVIEWPROJ]; + Vector4D *pVec3 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_VIEWPROJ]; + Vector4D *pVec4 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODEL]; + + sprintf(buf,"\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n" + + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n" + + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n" + + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n", + + pVec1[0][0], pVec1[0][1], pVec1[0][2], pVec1[0][3], + pVec1[1][0], pVec1[1][1], pVec1[1][2], pVec1[1][3], + pVec1[2][0], pVec1[2][1], pVec1[2][2], pVec1[2][3], + pVec1[3][0], pVec1[3][1], pVec1[3][2], pVec1[3][3], + + pVec2[0][0], pVec2[0][1], pVec2[0][2], pVec2[0][3], + pVec2[1][0], pVec2[1][1], pVec2[1][2], pVec2[1][3], + pVec2[2][0], pVec2[2][1], pVec2[2][2], pVec2[2][3], + pVec2[3][0], pVec2[3][1], pVec2[3][2], pVec2[3][3], + + pVec3[0][0], pVec3[0][1], pVec3[0][2], pVec3[0][3], + pVec3[1][0], pVec3[1][1], pVec3[1][2], pVec3[1][3], + pVec3[2][0], pVec3[2][1], pVec3[2][2], pVec3[2][3], + pVec3[3][0], pVec3[3][1], pVec3[3][2], pVec3[3][3], + + pVec4[0][0], pVec4[0][1], pVec4[0][2], pVec4[0][3], + pVec4[1][0], pVec4[1][1], pVec4[1][2], pVec4[1][3], + pVec4[2][0], pVec4[2][1], pVec4[2][2], pVec4[2][3], + 0, 0, 0, 1 + ); +#endif + pFont->Begin(); + pFont->DrawText( buf, -1, &r, DT_LEFT | DT_TOP, + D3DCOLOR_RGBA( 255, 255, 255, 255 ) ); + pFont->End(); + + return; + } + +#if 0 + Vector4D *pVec2 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODELVIEWPROJ]; + Vector4D *pVec3 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_VIEWPROJ]; + Vector4D *pVec4 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODEL]; + + static char buf2[1024]; + sprintf(buf2,"\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n" + + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n" + + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n" + + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n" + "[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n", + + pVec1[0][0], pVec1[0][1], pVec1[0][2], pVec1[0][3], + pVec1[1][0], pVec1[1][1], pVec1[1][2], pVec1[1][3], + pVec1[2][0], pVec1[2][1], pVec1[2][2], pVec1[2][3], + pVec1[3][0], pVec1[3][1], pVec1[3][2], pVec1[3][3], + + pVec2[0][0], pVec2[0][1], pVec2[0][2], pVec2[0][3], + pVec2[1][0], pVec2[1][1], pVec2[1][2], pVec2[1][3], + pVec2[2][0], pVec2[2][1], pVec2[2][2], pVec2[2][3], + pVec2[3][0], pVec2[3][1], pVec2[3][2], pVec2[3][3], + + pVec3[0][0], pVec3[0][1], pVec3[0][2], pVec3[0][3], + pVec3[1][0], pVec3[1][1], pVec3[1][2], pVec3[1][3], + pVec3[2][0], pVec3[2][1], pVec3[2][2], pVec3[2][3], + pVec3[3][0], pVec3[3][1], pVec3[3][2], pVec3[3][3], + + pVec4[0][0], pVec4[0][1], pVec4[0][2], pVec4[0][3], + pVec4[1][0], pVec4[1][1], pVec4[1][2], pVec4[1][3], + pVec4[2][0], pVec4[2][1], pVec4[2][2], pVec4[2][3], + 0, 0, 0, 1.0f + ); + Plat_DebugString(buf2); + return; +#endif +*/ + + char buf[256]; + sprintf(buf, "\nSnapshot id %d : \n", m_TransitionTable.CurrentSnapshot() ); + Plat_DebugString(buf); + + ShadowState_t &boardState = m_TransitionTable.BoardState(); + ShadowShaderState_t &boardShaderState = m_TransitionTable.BoardShaderState(); + + sprintf(buf,"Depth States: ZFunc %d, ZWrite %d, ZEnable %d, ZBias %d\n", + boardState.m_ZFunc, boardState.m_ZWriteEnable, + boardState.m_ZEnable, boardState.m_ZBias ); + Plat_DebugString(buf); + sprintf(buf,"Cull Enable %d Cull Mode %d Color Write %d Fill %d Const Color Mod %d sRGBWriteEnable %d\n", + boardState.m_CullEnable, m_DynamicState.m_CullMode, boardState.m_ColorWriteEnable, + boardState.m_FillMode, boardShaderState.m_ModulateConstantColor, boardState.m_SRGBWriteEnable ); + Plat_DebugString(buf); + sprintf(buf,"Blend States: Blend Enable %d Test Enable %d Func %d SrcBlend %d (%s) DstBlend %d (%s)\n", + boardState.m_AlphaBlendEnable, boardState.m_AlphaTestEnable, + boardState.m_AlphaFunc, boardState.m_SrcBlend, BlendModeToString( boardState.m_SrcBlend ), + boardState.m_DestBlend, BlendModeToString( boardState.m_DestBlend ) ); + Plat_DebugString(buf); + int len = sprintf(buf,"Alpha Ref %d, Lighting: %d, Ambient Color %x, LightsEnabled ", + boardState.m_AlphaRef, boardState.m_Lighting, m_DynamicState.m_Ambient); + + int i; + for ( i = 0; i < g_pHardwareConfig->Caps().m_MaxNumLights; ++i) + { + len += sprintf(buf+len,"%d ", m_DynamicState.m_LightEnable[i] ); + } + sprintf(buf+len,"\n"); + Plat_DebugString(buf); + sprintf(buf,"Fixed Function: %d, VertexBlend %d\n", + boardState.m_UsingFixedFunction, m_DynamicState.m_VertexBlend ); + Plat_DebugString(buf); + + sprintf(buf,"Pass Vertex Usage: %llx Pixel Shader %p Vertex Shader %p\n", + boardShaderState.m_VertexUsage, ShaderManager()->GetCurrentPixelShader(), + ShaderManager()->GetCurrentVertexShader() ); + Plat_DebugString(buf); + + // REGRESSED!!!! + /* + D3DXMATRIX* m = &GetTransform(MATERIAL_MODEL); + sprintf(buf,"WorldMat [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n", + m->m[0][0], m->m[0][1], m->m[0][2], m->m[0][3], + m->m[1][0], m->m[1][1], m->m[1][2], m->m[1][3], + m->m[2][0], m->m[2][1], m->m[2][2], m->m[2][3], + m->m[3][0], m->m[3][1], m->m[3][2], m->m[3][3] ); + Plat_DebugString(buf); + + m = &GetTransform(MATERIAL_MODEL + 1); + sprintf(buf,"WorldMat2 [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n", + m->m[0][0], m->m[0][1], m->m[0][2], m->m[0][3], + m->m[1][0], m->m[1][1], m->m[1][2], m->m[1][3], + m->m[2][0], m->m[2][1], m->m[2][2], m->m[2][3], + m->m[3][0], m->m[3][1], m->m[3][2], m->m[3][3] ); + Plat_DebugString(buf); + + m = &GetTransform(MATERIAL_VIEW); + sprintf(buf,"ViewMat [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n", + m->m[0][0], m->m[0][1], m->m[0][2], + m->m[1][0], m->m[1][1], m->m[1][2], + m->m[2][0], m->m[2][1], m->m[2][2], + m->m[3][0], m->m[3][1], m->m[3][2] ); + Plat_DebugString(buf); + + m = &GetTransform(MATERIAL_PROJECTION); + sprintf(buf,"ProjMat [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n", + m->m[0][0], m->m[0][1], m->m[0][2], + m->m[1][0], m->m[1][1], m->m[1][2], + m->m[2][0], m->m[2][1], m->m[2][2], + m->m[3][0], m->m[3][1], m->m[3][2] ); + Plat_DebugString(buf); + + for (i = 0; i < GetTextureStageCount(); ++i) + { + m = &GetTransform(MATERIAL_TEXTURE0 + i); + sprintf(buf,"TexMat%d [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n", + i, m->m[0][0], m->m[0][1], m->m[0][2], + m->m[1][0], m->m[1][1], m->m[1][2], + m->m[2][0], m->m[2][1], m->m[2][2], + m->m[3][0], m->m[3][1], m->m[3][2] ); + Plat_DebugString(buf); + } + */ + + sprintf(buf,"Viewport (%d %d) [%d %d] %4.3f %4.3f\n", + m_DynamicState.m_Viewport.X, m_DynamicState.m_Viewport.Y, + m_DynamicState.m_Viewport.Width, m_DynamicState.m_Viewport.Height, + m_DynamicState.m_Viewport.MinZ, m_DynamicState.m_Viewport.MaxZ); + Plat_DebugString(buf); + + for (i = 0; i < MAX_TEXTURE_STAGES; ++i) + { + sprintf(buf,"Stage %d :\n", i); + Plat_DebugString(buf); + sprintf(buf," Color Op: %d (%s) Color Arg1: %d (%s)", + boardState.m_TextureStage[i].m_ColorOp, + TextureOpToString( boardState.m_TextureStage[i].m_ColorOp ), + boardState.m_TextureStage[i].m_ColorArg1, + TextureArgToString( boardState.m_TextureStage[i].m_ColorArg1 ) ); + Plat_DebugString(buf); + sprintf( buf, " Color Arg2: %d (%s)\n", + boardState.m_TextureStage[i].m_ColorArg2, + TextureArgToString( boardState.m_TextureStage[i].m_ColorArg2 ) ); + Plat_DebugString(buf); + sprintf(buf," Alpha Op: %d (%s) Alpha Arg1: %d (%s)", + boardState.m_TextureStage[i].m_AlphaOp, + TextureOpToString( boardState.m_TextureStage[i].m_AlphaOp ), + boardState.m_TextureStage[i].m_AlphaArg1, + TextureArgToString( boardState.m_TextureStage[i].m_AlphaArg1 ) ); + Plat_DebugString(buf); + sprintf(buf," Alpha Arg2: %d (%s)\n", + boardState.m_TextureStage[i].m_AlphaArg2, + TextureArgToString( boardState.m_TextureStage[i].m_AlphaArg2 ) ); + Plat_DebugString(buf); + } + + for ( int i = 0; i < MAX_SAMPLERS; ++i ) + { + sprintf(buf," Texture Enabled: %d Bound Texture: %d UWrap: %d VWrap: %d\n", + SamplerState(i).m_TextureEnable, GetBoundTextureBindId( (Sampler_t)i ), + SamplerState(i).m_UTexWrap, SamplerState(i).m_VTexWrap ); + Plat_DebugString(buf); + sprintf(buf," Mag Filter: %d Min Filter: %d Mip Filter: %d\n", + SamplerState(i).m_MagFilter, SamplerState(i).m_MinFilter, + SamplerState(i).m_MipFilter ); + sprintf(buf," MaxMipLevel: %d\n", SamplerState(i).m_FinestMipmapLevel ); + Plat_DebugString(buf); + } +#else + Plat_DebugString("::SpewBoardState() Not Implemented Yet"); +#endif +} + +//----------------------------------------------------------------------------- +// Begin a render pass +//----------------------------------------------------------------------------- +void CShaderAPIDx8::BeginPass( StateSnapshot_t snapshot ) +{ + LOCK_SHADERAPI(); + VPROF("CShaderAPIDx8::BeginPass"); + if (IsDeactivated()) + return; + + m_nCurrentSnapshot = snapshot; +// Assert( m_pRenderMesh ); + // FIXME: This only does anything with temp meshes, so don't bother yet for the new code. + if( m_pRenderMesh ) + { + m_pRenderMesh->BeginPass( ); + } +} + + +//----------------------------------------------------------------------------- +// Render da polygon! +//----------------------------------------------------------------------------- +void CShaderAPIDx8::RenderPass( int nPass, int nPassCount ) +{ + if ( IsDeactivated() ) + return; + + Assert( m_nCurrentSnapshot != -1 ); +// Assert( m_pRenderMesh ); MESHFIXME + + m_TransitionTable.UseSnapshot( m_nCurrentSnapshot ); + CommitPerPassStateChanges( m_nCurrentSnapshot ); + + // Make sure that we bound a texture for every stage that is enabled + // NOTE: not enabled/finished yet... see comment in CShaderAPIDx8::ApplyTextureEnable +// int nSampler; +// for ( nSampler = 0; nSampler < g_pHardwareConfig->GetSamplerCount(); nSampler++ ) +// { +// if ( SamplerState( nSampler ).m_TextureEnable ) +// { +// } +// } + +#ifdef DEBUG_BOARD_STATE + // Spew out render state... + if ( m_pMaterial->PerformDebugTrace() ) + { + SpewBoardState(); + } +#endif + +#ifdef TEST_CACHE_LOCKS + g_pDataCache->Flush(); +#endif + +// Assert( m_pRenderMesh ); MESHFIXME + if ( m_pRenderMesh ) + { + m_pRenderMesh->RenderPass(); + } + else + { + MeshMgr()->RenderPassWithVertexAndIndexBuffers(); + } + m_nCurrentSnapshot = -1; +} + + +//----------------------------------------------------------------------------- +// Matrix mode +//----------------------------------------------------------------------------- +void CShaderAPIDx8::MatrixMode( MaterialMatrixMode_t matrixMode ) +{ + // NOTE!!!!!! + // The only time that m_MatrixMode is used is for texture matrices. Do not use + // it for anything else unless you change this code! + if ( matrixMode >= MATERIAL_TEXTURE0 && matrixMode <= MATERIAL_TEXTURE7 ) + { + m_MatrixMode = ( D3DTRANSFORMSTATETYPE )( matrixMode - MATERIAL_TEXTURE0 + D3DTS_TEXTURE0 ); + } + else + { + m_MatrixMode = (D3DTRANSFORMSTATETYPE)-1; + } + + m_CurrStack = GetMatrixStack( matrixMode ); +} + +//----------------------------------------------------------------------------- +// the current camera position in world space. +//----------------------------------------------------------------------------- +void CShaderAPIDx8::GetWorldSpaceCameraPosition( float* pPos ) const +{ + memcpy( pPos, m_WorldSpaceCameraPositon.Base(), sizeof( float[3] ) ); +} + +void CShaderAPIDx8::CacheWorldSpaceCameraPosition() +{ + D3DXMATRIX& view = GetTransform(MATERIAL_VIEW); + m_WorldSpaceCameraPositon[0] = + -( view( 3, 0 ) * view( 0, 0 ) + + view( 3, 1 ) * view( 0, 1 ) + + view( 3, 2 ) * view( 0, 2 ) ); + m_WorldSpaceCameraPositon[1] = + -( view( 3, 0 ) * view( 1, 0 ) + + view( 3, 1 ) * view( 1, 1 ) + + view( 3, 2 ) * view( 1, 2 ) ); + m_WorldSpaceCameraPositon[2] = + -( view( 3, 0 ) * view( 2, 0 ) + + view( 3, 1 ) * view( 2, 1 ) + + view( 3, 2 ) * view( 2, 2 ) ); + m_WorldSpaceCameraPositon[3] = 1.0f; + + // Protect against zero, as some pixel shaders will divide by this in CalcWaterFogAlpha() in common_ps_fxc.h + if ( fabs( m_WorldSpaceCameraPositon[2] ) <= 0.00001f ) + { + m_WorldSpaceCameraPositon[2] = 0.01f; + } +} + + +//----------------------------------------------------------------------------- +// Computes a matrix which includes the poly offset given an initial projection matrix +//----------------------------------------------------------------------------- +void CShaderAPIDx8::ComputePolyOffsetMatrix( const D3DXMATRIX& matProjection, D3DXMATRIX &matProjectionOffset ) +{ + // We never need to do this on hardware that can handle zbias + if ( g_pHardwareConfig->Caps().m_ZBiasAndSlopeScaledDepthBiasSupported ) + return; + + float offsetVal = + -1.0f * (m_DesiredState.m_Viewport.MaxZ - m_DesiredState.m_Viewport.MinZ) / + 16384.0f; + + D3DXMATRIX offset; + D3DXMatrixTranslation( &offset, 0.0f, 0.0f, offsetVal ); + D3DXMatrixMultiply( &matProjectionOffset, &matProjection, &offset ); +} + + +//----------------------------------------------------------------------------- +// Caches off the poly-offset projection matrix +//----------------------------------------------------------------------------- +void CShaderAPIDx8::CachePolyOffsetProjectionMatrix() +{ + ComputePolyOffsetMatrix( GetTransform(MATERIAL_PROJECTION), m_CachedPolyOffsetProjectionMatrix ); +} + + +//----------------------------------------------------------------------------- +// Performs a flush on the matrix state if necessary +//----------------------------------------------------------------------------- +bool CShaderAPIDx8::MatrixIsChanging( TransformType_t type ) +{ + if ( IsDeactivated() ) + { + return false; + } + + // early out if the transform is already one of our standard types + if ((type != TRANSFORM_IS_GENERAL) && (type == m_DynamicState.m_TransformType[m_CurrStack])) + return false; + + // Only flush state if we're changing something other than a texture transform + int textureMatrix = m_CurrStack - MATERIAL_TEXTURE0; + if (( textureMatrix < 0 ) || (textureMatrix >= NUM_TEXTURE_TRANSFORMS)) + FlushBufferedPrimitivesInternal(); + + return true; +} + +void CShaderAPIDx8::SetTextureTransformDimension( TextureStage_t textureMatrix, int dimension, bool projected ) +{ + D3DTEXTURETRANSFORMFLAGS textureTransformFlags = ( D3DTEXTURETRANSFORMFLAGS )dimension; + if( projected ) + { + Assert( sizeof( int ) == sizeof( D3DTEXTURETRANSFORMFLAGS ) ); + ( *( int * )&textureTransformFlags ) |= D3DTTFF_PROJECTED; + } + + if (TextureStage(textureMatrix).m_TextureTransformFlags != textureTransformFlags ) + { + SetTextureStageState( textureMatrix, D3DTSS_TEXTURETRANSFORMFLAGS, textureTransformFlags ); + TextureStage(textureMatrix).m_TextureTransformFlags = textureTransformFlags; + } +} + +void CShaderAPIDx8::DisableTextureTransform( TextureStage_t textureMatrix ) +{ + if (TextureStage(textureMatrix).m_TextureTransformFlags != D3DTTFF_DISABLE ) + { + SetTextureStageState( textureMatrix, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE ); + TextureStage(textureMatrix).m_TextureTransformFlags = D3DTTFF_DISABLE; + } +} + +void CShaderAPIDx8::SetBumpEnvMatrix( TextureStage_t textureStage, float m00, float m01, float m10, float m11 ) +{ + TextureStageState_t &textureStageState = TextureStage( textureStage ); + + if( textureStageState.m_BumpEnvMat00 != m00 || + textureStageState.m_BumpEnvMat01 != m01 || + textureStageState.m_BumpEnvMat10 != m10 || + textureStageState.m_BumpEnvMat11 != m11 ) + { + SetTextureStageState( textureStage, D3DTSS_BUMPENVMAT00, *( ( LPDWORD ) (&m00) ) ); + SetTextureStageState( textureStage, D3DTSS_BUMPENVMAT01, *( ( LPDWORD ) (&m01) ) ); + SetTextureStageState( textureStage, D3DTSS_BUMPENVMAT10, *( ( LPDWORD ) (&m10) ) ); + SetTextureStageState( textureStage, D3DTSS_BUMPENVMAT11, *( ( LPDWORD ) (&m11) ) ); + textureStageState.m_BumpEnvMat00 = m00; + textureStageState.m_BumpEnvMat01 = m01; + textureStageState.m_BumpEnvMat10 = m10; + textureStageState.m_BumpEnvMat11 = m11; + } +} + +//----------------------------------------------------------------------------- +// Sets the actual matrix state +//----------------------------------------------------------------------------- +void CShaderAPIDx8::UpdateMatrixTransform( TransformType_t type ) +{ + int textureMatrix = m_CurrStack - MATERIAL_TEXTURE0; + if (( textureMatrix >= 0 ) && (textureMatrix < NUM_TEXTURE_TRANSFORMS)) + { + // NOTE: Flush shouldn't happen here because we + // expect that texture transforms will be set within the shader + + // FIXME: We only want to use D3DTTFF_COUNT3 for cubemaps + // D3DTFF_COUNT2 is used for non-cubemaps. Of course, if there's + // no performance penalty for COUNT3, we should just use that. + D3DTEXTURETRANSFORMFLAGS transformFlags; + transformFlags = (type == TRANSFORM_IS_IDENTITY) ? D3DTTFF_DISABLE : D3DTTFF_COUNT3; + + if (TextureStage(textureMatrix).m_TextureTransformFlags != transformFlags ) + { + SetTextureStageState( textureMatrix, D3DTSS_TEXTURETRANSFORMFLAGS, transformFlags ); + TextureStage(textureMatrix).m_TextureTransformFlags = transformFlags; + } + } + + m_DynamicState.m_TransformType[m_CurrStack] = type; + m_DynamicState.m_TransformChanged[m_CurrStack] = STATE_CHANGED; + +#ifdef _DEBUG + // Store off the board state + D3DXMATRIX *pSrc = &GetTransform(m_CurrStack); + D3DXMATRIX *pDst = &m_DynamicState.m_Transform[m_CurrStack]; +// Assert( *pSrc != *pDst ); + memcpy( pDst, pSrc, sizeof(D3DXMATRIX) ); +#endif + + if ( m_CurrStack == MATERIAL_VIEW ) + { + CacheWorldSpaceCameraPosition(); + } + + if ( !IsX360() && m_CurrStack == MATERIAL_PROJECTION ) + { + CachePolyOffsetProjectionMatrix(); + } + + // Any time the view or projection matrix changes, the user clip planes need recomputing.... + // Assuming we're not overriding the user clip transform + if ( ( m_CurrStack == MATERIAL_PROJECTION ) || + ( ( m_CurrStack == MATERIAL_VIEW ) && ( !m_DynamicState.m_bUserClipTransformOverride ) ) ) + { + MarkAllUserClipPlanesDirty(); + } + + // Set the state if it's a texture transform + if ( (m_CurrStack >= MATERIAL_TEXTURE0) && (m_CurrStack <= MATERIAL_TEXTURE7) ) + { + SetTransform( m_MatrixMode, &GetTransform(m_CurrStack) ); + } +} + + +//-------------------------------------------------------------------------------- +// deformations +//-------------------------------------------------------------------------------- +void CShaderAPIDx8::PushDeformation( DeformationBase_t const *pDef ) +{ + Assert( m_pDeformationStackPtr > m_DeformationStack ); + --m_pDeformationStackPtr; + m_pDeformationStackPtr->m_nDeformationType = pDef->m_eType; + + switch( pDef->m_eType ) + { + case DEFORMATION_CLAMP_TO_BOX_IN_WORLDSPACE: + { + BoxDeformation_t const *pBox = reinterpret_cast< const BoxDeformation_t *>( pDef ); + m_pDeformationStackPtr->m_nNumParameters = 16; + memcpy( m_pDeformationStackPtr->m_flDeformationParameters, &( pBox->m_SourceMins.x ), 16 * sizeof( float ) ); + break; + } + break; + + default: + Assert( 0 ); + } +} + + +void CShaderAPIDx8::PopDeformation( ) +{ + Assert( m_pDeformationStackPtr != m_DeformationStack + DEFORMATION_STACK_DEPTH ); + ++m_pDeformationStackPtr; +} + +int CShaderAPIDx8::GetNumActiveDeformations( void ) const +{ + return ( m_DeformationStack + DEFORMATION_STACK_DEPTH ) - m_pDeformationStackPtr; +} + + +// for shaders to set vertex shader constants. returns a packed state which can be used to set the dynamic combo +int CShaderAPIDx8::GetPackedDeformationInformation( int nMaskOfUnderstoodDeformations, + float *pConstantValuesOut, + int nBufferSize, + int nMaximumDeformations, + int *pDefCombosOut ) const +{ + int nCombosFound = 0; + memset( pDefCombosOut, 0, sizeof( pDefCombosOut[0] ) * nMaximumDeformations ); + size_t nRemainingBufferSize = nBufferSize; + + for( const Deformation_t *i = m_DeformationStack + DEFORMATION_STACK_DEPTH -1; i >= m_pDeformationStackPtr; i-- ) + { + int nFloatsOut = 4 * ( ( i->m_nNumParameters + 3 )>> 2 ); + if ( + ( ( 1 << i->m_nDeformationType ) & nMaskOfUnderstoodDeformations ) && + ( nRemainingBufferSize >= ( nFloatsOut * sizeof( float ) ) ) ) + { + memcpy( pConstantValuesOut, i->m_flDeformationParameters, nFloatsOut * sizeof( float ) ); + pConstantValuesOut += nFloatsOut; + nRemainingBufferSize -= nFloatsOut * sizeof( float ); + ( *pDefCombosOut++ ) = i->m_nDeformationType; + nCombosFound++; + } + } + return nCombosFound; +} + + + + +//----------------------------------------------------------------------------- +// Matrix stack operations +//----------------------------------------------------------------------------- + +void CShaderAPIDx8::PushMatrix() +{ + // NOTE: No matrix transform update needed here. + m_pMatrixStack[m_CurrStack]->Push(); +} + +void CShaderAPIDx8::PopMatrix() +{ + if (MatrixIsChanging()) + { + m_pMatrixStack[m_CurrStack]->Pop(); + UpdateMatrixTransform(); + } +} + +void CShaderAPIDx8::LoadIdentity( ) +{ + if (MatrixIsChanging(TRANSFORM_IS_IDENTITY)) + { + m_pMatrixStack[m_CurrStack]->LoadIdentity( ); + UpdateMatrixTransform( TRANSFORM_IS_IDENTITY ); + } +} + +void CShaderAPIDx8::LoadCameraToWorld( ) +{ + if (MatrixIsChanging(TRANSFORM_IS_CAMERA_TO_WORLD)) + { + // could just use the transpose instead, if we know there's no scale + float det; + D3DXMATRIX inv; + D3DXMatrixInverse( &inv, &det, &GetTransform(MATERIAL_VIEW) ); + + // Kill translation + inv.m[3][0] = inv.m[3][1] = inv.m[3][2] = 0.0f; + + m_pMatrixStack[m_CurrStack]->LoadMatrix( &inv ); + UpdateMatrixTransform( TRANSFORM_IS_CAMERA_TO_WORLD ); + } +} + +void CShaderAPIDx8::LoadMatrix( float *m ) +{ + // Check for identity... + if ( (fabs(m[0] - 1.0f) < 1e-3) && (fabs(m[5] - 1.0f) < 1e-3) && (fabs(m[10] - 1.0f) < 1e-3) && (fabs(m[15] - 1.0f) < 1e-3) && + (fabs(m[1]) < 1e-3) && (fabs(m[2]) < 1e-3) && (fabs(m[3]) < 1e-3) && + (fabs(m[4]) < 1e-3) && (fabs(m[6]) < 1e-3) && (fabs(m[7]) < 1e-3) && + (fabs(m[8]) < 1e-3) && (fabs(m[9]) < 1e-3) && (fabs(m[11]) < 1e-3) && + (fabs(m[12]) < 1e-3) && (fabs(m[13]) < 1e-3) && (fabs(m[14]) < 1e-3) ) + { + LoadIdentity(); + return; + } + + if (MatrixIsChanging()) + { + m_pMatrixStack[m_CurrStack]->LoadMatrix( (D3DXMATRIX*)m ); + UpdateMatrixTransform(); + } +} + +void CShaderAPIDx8::LoadBoneMatrix( int boneIndex, const float *m ) +{ + if ( IsDeactivated() ) + return; + + memcpy( m_boneMatrix[boneIndex].Base(), m, sizeof(float)*12 ); + if ( boneIndex > m_maxBoneLoaded ) + { + m_maxBoneLoaded = boneIndex; + } + if ( boneIndex == 0 ) + { + MatrixMode( MATERIAL_MODEL ); + VMatrix transposeMatrix; + transposeMatrix.Init( *(matrix3x4_t *)m ); + MatrixTranspose( transposeMatrix, transposeMatrix ); + LoadMatrix( (float*)transposeMatrix.m ); + } +} + +//----------------------------------------------------------------------------- +// Commits morph target factors +//----------------------------------------------------------------------------- +static void CommitFlexWeights( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, + DynamicState_t ¤tState, 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 ¤tState, bool bForce ) +{ + bool bChanged = bForce || memcmp( &desiredState.m_Viewport, ¤tState.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( ¤tState.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, ©Rect, 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 ¤tState, 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 + |