//========= 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 #include "interface.h" #include "utlrbtree.h" #include "utlsymbol.h" #include "tier1/strtools.h" #include "recording.h" #ifndef _X360 #include #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 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 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( pCmdBuf ); switch( nCmd ) { case CBCMD_END: { if ( pSP == &pReturnStack[ARRAYSIZE(pReturnStack)] ) return; else { // pop pc pCmdBuf = *( pSP ++ ); break; } } case CBCMD_JUMP: pCmdBuf = GetData( pCmdBuf + sizeof( int ) ); break; case CBCMD_JSR: { Assert( pSP > &(pReturnStack[0] ) ); // *(--pSP ) = pCmdBuf + sizeof( int ) + sizeof( uint8 *); // pCmdBuf = GetData( pCmdBuf + sizeof( int ) ); ExecuteCommandBuffer( GetData( pCmdBuf + sizeof( int ) ) ); pCmdBuf = pCmdBuf + sizeof( int ) + sizeof( uint8 *); break; } case CBCMD_SET_PIXEL_SHADER_FLOAT_CONST: { int nStartConst = GetData( pCmdBuf + sizeof( int ) ); int nNumConsts = GetData( 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( pCmdBuf + sizeof( int ) ); pCmdBuf += 2 * sizeof( int ); SetPixelShaderFogParams( nReg ); // !! speed fixme break; } case CBCMD_STORE_EYE_POS_IN_PSCONST: { int nReg = GetData( pCmdBuf + sizeof( int ) ); pCmdBuf += 2 * sizeof( int ); SetPixelShaderConstantInternal( nReg, m_WorldSpaceCameraPositon.Base(), 1, false ); break; } case CBCMD_COMMITPIXELSHADERLIGHTING: { int nReg = GetData( pCmdBuf + sizeof( int ) ); pCmdBuf += 2 * sizeof( int ); CommitPixelShaderLighting( nReg ); break; } case CBCMD_SETPIXELSHADERSTATEAMBIENTLIGHTCUBE: { int nReg = GetData( 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( pCmdBuf + sizeof( int ) ); float fDepthBlendScale = GetData( pCmdBuf + 2 * sizeof( int ) ); pCmdBuf += 2 * sizeof( int ) + sizeof( float ); SetDepthFeatheringPixelShaderConstant( nConst, fDepthBlendScale ); break; } case CBCMD_SET_VERTEX_SHADER_FLOAT_CONST: { int nStartConst = GetData( pCmdBuf + sizeof( int ) ); int nNumConsts = GetData( 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( pCmdBuf + sizeof( int ) ); int nTextureID = GetData( 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( pCmdBuf + sizeof( int ) ); ShaderAPITextureHandle_t hTexture = GetData( 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( pCmdBuf + sizeof( int ) ); ShaderManager()->SetPixelShaderIndex( nIdx ); pCmdBuf += 2 * sizeof( int ); break; } case CBCMD_SET_VSHINDEX: { int nIdx = GetData( 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(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(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(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(pTexture); HRESULT hr = pTex->GetLevelDesc( 0, &desc ); Assert( !FAILED(hr) ); } else { IDirect3DTexture* pTex = static_cast(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( 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(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, "" ); 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 ""; } } 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 ""; } } //----------------------------------------------------------------------------- // 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( 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( "" ); } #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; iszTypeface, 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