//========= 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 "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 const D3DSHADEMODE D3DSHADE_NONE = (D3DSHADEMODE)0; m_DynamicState.m_ShadeMode = D3DSHADE_NONE; ShadeMode( SHADER_SMOOTH ); m_DynamicState.m_bHWMorphingEnabled = false; // Skinning... m_DynamicState.m_NumBones = 0; m_DynamicState.m_VertexBlend = (D3DVERTEXBLENDFLAGS)-1; SetSupportedRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE ); SetSupportedRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE ); // No normal normalization m_DynamicState.m_NormalizeNormals = false; SetSupportedRenderState( D3DRS_NORMALIZENORMALS, m_DynamicState.m_NormalizeNormals ); bool bAntialiasing = ( m_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE ); if ( g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT ) { bAntialiasing = false; } SetRenderState( D3DRS_MULTISAMPLEANTIALIAS, bAntialiasing ); // Anisotropic filtering is disabled by default if ( bFullReset ) { SetAnisotropicLevel( 1 ); } int i; for ( i = 0; i < g_pHardwareConfig->ActualCaps().m_NumTextureStages; ++i ) { TextureStage(i).m_TextureTransformFlags = D3DTTFF_DISABLE; TextureStage(i).m_BumpEnvMat00 = 1.0f; TextureStage(i).m_BumpEnvMat01 = 0.0f; TextureStage(i).m_BumpEnvMat10 = 0.0f; TextureStage(i).m_BumpEnvMat11 = 1.0f; SetTextureStageState( i, D3DTSS_TEXTURETRANSFORMFLAGS, TextureStage(i).m_TextureTransformFlags ); SetTextureStageState( i, D3DTSS_BUMPENVMAT00, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat00) ) ); SetTextureStageState( i, D3DTSS_BUMPENVMAT01, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat01) ) ); SetTextureStageState( i, D3DTSS_BUMPENVMAT10, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat10) ) ); SetTextureStageState( i, D3DTSS_BUMPENVMAT11, *( ( LPDWORD ) (&TextureStage(i).m_BumpEnvMat11) ) ); } for ( i = 0; i < g_pHardwareConfig->ActualCaps().m_NumSamplers; ++i ) { SamplerState(i).m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE; SamplerState(i).m_UTexWrap = D3DTADDRESS_WRAP; SamplerState(i).m_VTexWrap = D3DTADDRESS_WRAP; SamplerState(i).m_WTexWrap = D3DTADDRESS_WRAP; SamplerState(i).m_MagFilter = D3DTEXF_POINT; SamplerState(i).m_MinFilter = D3DTEXF_POINT; SamplerState(i).m_MipFilter = D3DTEXF_NONE; SamplerState(i).m_FinestMipmapLevel = 0; SamplerState(i).m_LodBias = 0.0f; SamplerState(i).m_TextureEnable = false; SamplerState(i).m_SRGBReadEnable = false; // Just some initial state... Dx9Device()->SetTexture( i, 0 ); SetSamplerState( i, D3DSAMP_ADDRESSU, SamplerState(i).m_UTexWrap ); SetSamplerState( i, D3DSAMP_ADDRESSV, SamplerState(i).m_VTexWrap ); SetSamplerState( i, D3DSAMP_ADDRESSW, SamplerState(i).m_WTexWrap ); SetSamplerState( i, D3DSAMP_MINFILTER, SamplerState(i).m_MinFilter ); SetSamplerState( i, D3DSAMP_MAGFILTER, SamplerState(i).m_MagFilter ); SetSamplerState( i, D3DSAMP_MIPFILTER, SamplerState(i).m_MipFilter ); SetSamplerState( i, D3DSAMP_MAXMIPLEVEL, SamplerState(i).m_FinestMipmapLevel ); SetSamplerState( i, D3DSAMP_MIPMAPLODBIAS, SamplerState(i).m_LodBias ); SetSamplerState( i, D3DSAMP_BORDERCOLOR, RGB( 0,0,0 ) ); } // FIXME!!!!! : This barfs with the debug runtime on 6800. for( i = 0; i < g_pHardwareConfig->ActualCaps().m_nVertexTextureCount; i++ ) { m_DynamicState.m_VertexTextureState[i].m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE; Dx9Device()->SetTexture( D3DVERTEXTEXTURESAMPLER0 + i, NULL ); m_DynamicState.m_VertexTextureState[i].m_UTexWrap = D3DTADDRESS_CLAMP; m_DynamicState.m_VertexTextureState[i].m_VTexWrap = D3DTADDRESS_CLAMP; // m_DynamicState.m_VertexTextureState[i].m_WTexWrap = D3DTADDRESS_CLAMP; m_DynamicState.m_VertexTextureState[i].m_MinFilter = D3DTEXF_POINT; m_DynamicState.m_VertexTextureState[i].m_MagFilter = D3DTEXF_POINT; m_DynamicState.m_VertexTextureState[i].m_MipFilter = D3DTEXF_POINT; SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSU, m_DynamicState.m_VertexTextureState[i].m_UTexWrap ); SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSV, m_DynamicState.m_VertexTextureState[i].m_VTexWrap ); // SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSW, m_DynamicState.m_VertexTextureState[i].m_WTexWrap ); SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MINFILTER, m_DynamicState.m_VertexTextureState[i].m_MinFilter ); SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MAGFILTER, m_DynamicState.m_VertexTextureState[i].m_MagFilter ); SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MIPFILTER, m_DynamicState.m_VertexTextureState[i].m_MipFilter ); } m_DynamicState.m_NumLights = 0; for ( i = 0; i < MAX_NUM_LIGHTS; ++i) { m_DynamicState.m_LightEnable[i] = false; m_DynamicState.m_LightChanged[i] = STATE_CHANGED; m_DynamicState.m_LightEnableChanged[i] = STATE_CHANGED; } for ( i = 0; i < NUM_MATRIX_MODES; ++i) { // By setting this to *not* be identity, we force an update... m_DynamicState.m_TransformType[i] = TRANSFORM_IS_GENERAL; m_DynamicState.m_TransformChanged[i] = STATE_CHANGED; } // set the board state to match the default state m_TransitionTable.UseDefaultState(); // Set the default render state SetDefaultState(); // Constant for all time SetSupportedRenderState( D3DRS_CLIPPING, TRUE ); SetSupportedRenderState( D3DRS_LOCALVIEWER, TRUE ); SetSupportedRenderState( D3DRS_POINTSCALEENABLE, FALSE ); SetSupportedRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL ); SetSupportedRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_COLOR2 ); SetSupportedRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL ); SetSupportedRenderState( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL ); SetSupportedRenderState( D3DRS_COLORVERTEX, TRUE ); // This defaults to true anyways. . . // Set a default identity material. SetDefaultMaterial(); #if 0 float fBias = -1.0f; SetTextureStageState( 0, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) ); SetTextureStageState( 1, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) ); SetTextureStageState( 2, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) ); SetTextureStageState( 3, D3DTSS_MIPMAPLODBIAS, *( ( LPDWORD ) (&fBias) ) ); #endif if ( bFullReset ) { // Set the modelview matrix to identity too for ( i = 0; i < NUM_MODEL_TRANSFORMS; ++i ) { SetIdentityMatrix( m_boneMatrix[i] ); } MatrixMode( MATERIAL_VIEW ); LoadIdentity(); MatrixMode( MATERIAL_PROJECTION ); LoadIdentity(); } #ifdef _X360 m_DynamicState.m_bBuffer2Frames = m_bBuffer2FramesAhead; SetRenderState( D3DRS_BUFFER2FRAMES, m_DynamicState.m_bBuffer2Frames ); #endif m_DynamicState.m_Viewport.X = m_DynamicState.m_Viewport.Y = m_DynamicState.m_Viewport.Width = m_DynamicState.m_Viewport.Height = 0xFFFFFFFF; m_DynamicState.m_Viewport.MinZ = m_DynamicState.m_Viewport.MaxZ = 0.0; // Be sure scissoring is off m_DynamicState.m_RenderState[D3DRS_SCISSORTESTENABLE] = FALSE; SetRenderState( D3DRS_SCISSORTESTENABLE, FALSE ); m_DynamicState.m_ScissorRect.left = -1; m_DynamicState.m_ScissorRect.top = -1; m_DynamicState.m_ScissorRect.right = -1; m_DynamicState.m_ScissorRect.bottom = -1; //SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE ); EnableFastClip( false ); float fFakePlane[4]; unsigned int iFakePlaneVal = 0xFFFFFFFF; fFakePlane[0] = fFakePlane[1] = fFakePlane[2] = fFakePlane[3] = *((float *)&iFakePlaneVal); SetFastClipPlane( fFakePlane ); //doing this to better wire up plane change detection float zero[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; // Make sure that our state is dirty. m_DynamicState.m_UserClipPlaneEnabled = 0; m_DynamicState.m_UserClipPlaneChanged = 0; m_DynamicState.m_UserClipLastUpdatedUsingFixedFunction = false; for( i = 0; i < g_pHardwareConfig->MaxUserClipPlanes(); i++ ) { // Make sure that our state is dirty. m_DynamicState.m_UserClipPlaneWorld[i][0] = -1.0f; m_DynamicState.m_UserClipPlaneProj[i][0] = -9999.0f; m_DynamicState.m_UserClipPlaneEnabled |= ( 1 << i ); SetClipPlane( i, zero ); EnableClipPlane( i, false ); Assert( m_DynamicState.m_UserClipPlaneEnabled == 0 ); } Assert( m_DynamicState.m_UserClipPlaneChanged == ((1 << g_pHardwareConfig->MaxUserClipPlanes()) - 1) ); m_DynamicState.m_FastClipEnabled = false; m_DynamicState.m_bFastClipPlaneChanged = true; // User clip override m_DynamicState.m_bUserClipTransformOverride = false; D3DXMatrixIdentity( &m_DynamicState.m_UserClipTransform ); // Viewport defaults to the window size RECT windowRect; #if !defined( DX_TO_GL_ABSTRACTION ) GetClientRect( (HWND)m_hWnd, &windowRect ); #else toglGetClientRect( (VD3DHWND)m_hWnd, &windowRect ); #endif ShaderViewport_t viewport; viewport.Init( windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top ); SetViewports( 1, &viewport ); // No render mesh m_pRenderMesh = 0; // Reset cached vertex decl m_DynamicState.m_pVertexDecl = NULL; // Reset the render target to be the normal backbuffer if ( IsX360() ) { m_hCachedRenderTarget = INVALID_SHADERAPI_TEXTURE_HANDLE; m_bUsingSRGBRenderTarget = false; } AcquireInternalRenderTargets(); SetRenderTarget(); // Maintain vertex + pixel shader constant buffers Vector4D *pVectorPixelShaderConstants = m_DesiredState.m_pVectorPixelShaderConstant; int *pBooleanPixelShaderConstants = m_DesiredState.m_pBooleanPixelShaderConstant; IntVector4D *pIntegerPixelShaderConstants = m_DesiredState.m_pIntegerPixelShaderConstant; Vector4D *pVectorVertexShaderConstants = m_DesiredState.m_pVectorVertexShaderConstant; int *pBooleanVertexShaderConstants = m_DesiredState.m_pBooleanVertexShaderConstant; IntVector4D *pIntegerVertexShaderConstants = m_DesiredState.m_pIntegerVertexShaderConstant; m_DesiredState = m_DynamicState; m_DesiredState.m_pVectorPixelShaderConstant = pVectorPixelShaderConstants; m_DesiredState.m_pBooleanPixelShaderConstant = pBooleanPixelShaderConstants; m_DesiredState.m_pIntegerPixelShaderConstant = pIntegerPixelShaderConstants; m_DesiredState.m_pVectorVertexShaderConstant = pVectorVertexShaderConstants; m_DesiredState.m_pBooleanVertexShaderConstant = pBooleanVertexShaderConstants; m_DesiredState.m_pIntegerVertexShaderConstant = pIntegerVertexShaderConstants; if ( g_pHardwareConfig->Caps().m_SupportsPixelShaders ) { if ( !bFullReset ) { //Full resets init the values to defaults. Normal resets just leave them dirty. if( g_pHardwareConfig->Caps().m_NumVertexShaderConstants != 0 ) SetVertexShaderConstant( 0, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), IsX360() ? 217 : g_pHardwareConfig->Caps().m_NumVertexShaderConstants, true ); //217 on X360 to play nice with fast blatting code if( g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants != 0 ) SetIntegerVertexShaderConstant( 0, (int *)m_DesiredState.m_pIntegerVertexShaderConstant, g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants, true ); if( g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants != 0 ) SetBooleanVertexShaderConstant( 0, m_DesiredState.m_pBooleanVertexShaderConstant, g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants, true ); if( g_pHardwareConfig->Caps().m_NumPixelShaderConstants != 0 ) SetPixelShaderConstant( 0, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), g_pHardwareConfig->Caps().m_NumPixelShaderConstants, true ); if( g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants != 0 ) SetIntegerPixelShaderConstant( 0, (int *)m_DesiredState.m_pIntegerPixelShaderConstant, g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants, true ); if( g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants != 0 ) SetBooleanPixelShaderConstant( 0, m_DesiredState.m_pBooleanPixelShaderConstant, g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants, true ); } } RECORD_DEBUG_STRING( "END ResetRenderState" ); m_bResettingRenderState = false; } //----------------------------------------------------------------------------- // Sets the default render state //----------------------------------------------------------------------------- void CShaderAPIDx8::SetDefaultState() { LOCK_SHADERAPI(); // NOTE: This used to be in the material system, but I want to avoid all the per pass/batch // virtual function calls. int numTextureStages = g_pHardwareConfig->GetTextureStageCount(); // FIXME: This is a brutal hack. We only need to load these transforms for fixed-function // hardware. Cap the max here to 4. if ( IsPC() ) { numTextureStages = min( numTextureStages, 4 ); int i; for( i = 0; i < numTextureStages; i++ ) { CShaderAPIDx8::DisableTextureTransform( (TextureStage_t)i ); CShaderAPIDx8::MatrixMode( (MaterialMatrixMode_t)(MATERIAL_TEXTURE0 + i) ); CShaderAPIDx8::LoadIdentity( ); } } CShaderAPIDx8::MatrixMode( MATERIAL_MODEL ); CShaderAPIDx8::Color4ub( 255, 255, 255, 255 ); CShaderAPIDx8::ShadeMode( SHADER_SMOOTH ); CShaderAPIDx8::SetVertexShaderIndex( ); CShaderAPIDx8::SetPixelShaderIndex( ); MeshMgr()->MarkUnusedVertexFields( 0, 0, NULL ); } //----------------------------------------------------------------------------- // // Methods related to vertex format // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Sets the vertex //----------------------------------------------------------------------------- inline void CShaderAPIDx8::SetVertexDecl( VertexFormat_t vertexFormat, bool bHasColorMesh, bool bUsingFlex, bool bUsingMorph ) { VPROF("CShaderAPIDx8::SetVertexDecl"); IDirect3DVertexDeclaration9 *pDecl = FindOrCreateVertexDecl( vertexFormat, bHasColorMesh, bUsingFlex, bUsingMorph ); Assert( pDecl ); if ( ( pDecl != m_DynamicState.m_pVertexDecl ) && pDecl ) { Dx9Device()->SetVertexDeclaration( pDecl ); m_DynamicState.m_pVertexDecl = pDecl; } } //----------------------------------------------------------------------------- // // Methods related to vertex buffers // //----------------------------------------------------------------------------- IMesh *CShaderAPIDx8::GetFlexMesh() { LOCK_SHADERAPI(); return MeshMgr()->GetFlexMesh(); } //----------------------------------------------------------------------------- // Gets the dynamic mesh //----------------------------------------------------------------------------- IMesh* CShaderAPIDx8::GetDynamicMesh( IMaterial* pMaterial, int nHWSkinBoneCount, bool buffered, IMesh* pVertexOverride, IMesh* pIndexOverride ) { Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() ); LOCK_SHADERAPI(); return MeshMgr()->GetDynamicMesh( pMaterial, 0, nHWSkinBoneCount, buffered, pVertexOverride, pIndexOverride ); } IMesh* CShaderAPIDx8::GetDynamicMeshEx( IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount, bool bBuffered, IMesh* pVertexOverride, IMesh* pIndexOverride ) { Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() ); LOCK_SHADERAPI(); return MeshMgr()->GetDynamicMesh( pMaterial, vertexFormat, nHWSkinBoneCount, bBuffered, pVertexOverride, pIndexOverride ); } //----------------------------------------------------------------------------- // Returns the number of vertices we can render using the dynamic mesh //----------------------------------------------------------------------------- void CShaderAPIDx8::GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices ) { LOCK_SHADERAPI(); MeshMgr()->GetMaxToRender( pMesh, bMaxUntilFlush, pMaxVerts, pMaxIndices ); } int CShaderAPIDx8::GetMaxVerticesToRender( IMaterial *pMaterial ) { pMaterial = ((IMaterialInternal *)pMaterial)->GetRealTimeVersion(); //always work with the realtime version internally LOCK_SHADERAPI(); return MeshMgr()->GetMaxVerticesToRender( pMaterial ); } int CShaderAPIDx8::GetMaxIndicesToRender( ) { LOCK_SHADERAPI(); return MeshMgr()->GetMaxIndicesToRender( ); } void CShaderAPIDx8::MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords ) { LOCK_SHADERAPI(); MeshMgr()->MarkUnusedVertexFields( nFlags, nTexCoordCount, pUnusedTexCoords ); } //----------------------------------------------------------------------------- // Draws the mesh //----------------------------------------------------------------------------- void CShaderAPIDx8::DrawMesh( CMeshBase *pMesh ) { VPROF("CShaderAPIDx8::DrawMesh"); if ( ShaderUtil()->GetConfig().m_bSuppressRendering ) return; #if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) PIXifyName( s_pPIXMaterialName, sizeof( s_pPIXMaterialName ), m_pMaterial->GetName() ); BeginPIXEvent( PIX_VALVE_ORANGE, s_pPIXMaterialName ); #endif m_pRenderMesh = pMesh; VertexFormat_t vertexFormat = m_pRenderMesh->GetVertexFormat(); SetVertexDecl( vertexFormat, m_pRenderMesh->HasColorMesh(), m_pRenderMesh->HasFlexMesh(), m_pMaterial->IsUsingVertexID() ); CommitStateChanges(); Assert( m_pRenderMesh && m_pMaterial ); m_pMaterial->DrawMesh( CompressionType( vertexFormat ) ); m_pRenderMesh = NULL; #if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) EndPIXEvent(); #endif } void CShaderAPIDx8::DrawWithVertexAndIndexBuffers( void ) { VPROF("CShaderAPIDx8::DrawWithVertexAndIndexBuffers"); if ( ShaderUtil()->GetConfig().m_bSuppressRendering ) return; #if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) PIXifyName( s_pPIXMaterialName, sizeof( s_pPIXMaterialName ), m_pMaterial->GetName()); BeginPIXEvent( PIX_VALVE_ORANGE, s_pPIXMaterialName ); #endif // m_pRenderMesh = pMesh; // FIXME: need to make this deal with multiple streams, etc. VertexFormat_t vertexFormat = MeshMgr()->GetCurrentVertexFormat(); SetVertexDecl( vertexFormat, false /*m_pRenderMesh->HasColorMesh()*/, false /*m_pRenderMesh->HasFlexMesh()*/, false /*m_pRenderMesh->IsUsingMorphData()*/ ); CommitStateChanges(); if ( m_pMaterial ) { m_pMaterial->DrawMesh( CompressionType( vertexFormat ) ); } else { MeshMgr()->RenderPassWithVertexAndIndexBuffers(); } // m_pRenderMesh = NULL; #if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) EndPIXEvent(); #endif } //----------------------------------------------------------------------------- // Discards the vertex buffers //----------------------------------------------------------------------------- void CShaderAPIDx8::DiscardVertexBuffers() { MeshMgr()->DiscardVertexBuffers(); } void CShaderAPIDx8::ForceHardwareSync_WithManagedTexture() { if ( IsX360() || !m_pFrameSyncTexture ) return; // Set the default state for everything so we don't get more than we ask for here! SetDefaultState(); D3DLOCKED_RECT rect; tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ ); HRESULT hr = m_pFrameSyncTexture->LockRect( 0, &rect, NULL, 0 ); if ( SUCCEEDED( hr ) ) { // modify.. unsigned long *pData = (unsigned long*)rect.pBits; (*pData)++; m_pFrameSyncTexture->UnlockRect( 0 ); // Now draw something with this texture. DWORD iStage = 0; IDirect3DBaseTexture9 *pOldTexture; hr = Dx9Device()->GetTexture( iStage, &pOldTexture ); if ( SUCCEEDED( hr ) ) { Dx9Device()->SetTexture( iStage, m_pFrameSyncTexture ); // Remember the old FVF. DWORD oldFVF; hr = Dx9Device()->GetFVF( &oldFVF ); if ( SUCCEEDED( hr ) ) { // Set the new FVF. Dx9Device()->SetFVF( D3DFVF_XYZ ); // Now, draw the simplest primitive D3D has ever seen. unsigned short indices[3] = { 0, 1, 2 }; Vector verts[3] = {vec3_origin, vec3_origin, vec3_origin}; tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "DrawIndexedPrimitiveUP" ); Dx9Device()->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST, 0, // Min vertex index 3, // Num vertices used 1, // # primitives indices, // indices D3DFMT_INDEX16, // index format verts, // Vertices sizeof( Vector )// Vertex stride ); Dx9Device()->SetFVF( oldFVF ); } Dx9Device()->SetTexture( iStage, pOldTexture ); } } // If this assert fails, then we failed somewhere above. AssertOnce( SUCCEEDED( hr ) ); } void CShaderAPIDx8::UpdateFrameSyncQuery( int queryIndex, bool bIssue ) { Assert(queryIndex < NUM_FRAME_SYNC_QUERIES); // wait if already issued if ( m_bQueryIssued[queryIndex] ) { tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ ); double flStartTime = Plat_FloatTime(); BOOL dummyData = 0; HRESULT hr = S_OK; // NOTE: This fix helps out motherboards that are a little freaky. // On such boards, sometimes the driver has to reset itself (an event which takes several seconds) // and when that happens, the frame sync query object gets lost for (;;) { hr = m_pFrameSyncQueryObject[queryIndex]->GetData( &dummyData, sizeof( dummyData ), D3DGETDATA_FLUSH ); if ( hr != S_FALSE ) break; double flCurrTime = Plat_FloatTime(); // don't wait more than 200ms (5fps) for these if ( flCurrTime - flStartTime > 0.200f ) break; // Avoid burning a full core while waiting for the query. Spinning can actually harm performance // because there might be driver threads that are trying to do work that end up starved, and the // power drawn by the CPU may take away from the power available to the integrated graphics chip. // A sleep of one millisecond should never be long enough to affect performance, especially since // this should only trigger when the CPU is already ahead of the GPU. // On L4D2/TF2 in GL mode this spinning was causing slowdowns. ThreadSleep( 1 ); } m_bQueryIssued[queryIndex] = false; Assert(hr == S_OK || hr == D3DERR_DEVICELOST); if ( hr == D3DERR_DEVICELOST ) { MarkDeviceLost( ); return; } } if ( bIssue ) { m_pFrameSyncQueryObject[queryIndex]->Issue( D3DISSUE_END ); m_bQueryIssued[queryIndex] = true; } } void CShaderAPIDx8::ForceHardwareSync( void ) { LOCK_SHADERAPI(); VPROF( "CShaderAPIDx8::ForceHardwareSync" ); #ifdef DX_TO_GL_ABSTRACTION if ( true ) #else if ( !mat_frame_sync_enable.GetInt() ) #endif return; // need to flush the dynamic buffer and make sure the entire image is there FlushBufferedPrimitives(); RECORD_COMMAND( DX8_HARDWARE_SYNC, 0 ); #if !defined( _X360 ) // How do you query dx9 for how many frames behind the hardware is or, alternatively, how do you tell the hardware to never be more than N frames behind? // 1) The old QueryPendingFrameCount design was removed. It was // a simple transaction with the driver through the // GetDriverState, trivial for the drivers to lie. We came up // with a much better scheme for tracking pending frames where // the driver can not lie without a possible performance loss: // use the asynchronous query system with D3DQUERYTYPE_EVENT and // data size 0. When GetData returns S_OK for the query, you // know that frame has finished. if ( mat_frame_sync_force_texture.GetBool() ) { ForceHardwareSync_WithManagedTexture(); } else if ( m_pFrameSyncQueryObject[0] ) { // FIXME: Could install a callback into the materialsystem to do something while waiting for // the frame to finish (update sound, etc.) // Disable VCR mode here or else it'll screw up (and we don't really care if this part plays back in exactly the same amount of time). VCRSetEnabled( false ); m_currentSyncQuery ++; if ( m_currentSyncQuery >= ARRAYSIZE(m_pFrameSyncQueryObject) ) { m_currentSyncQuery = 0; } double fStart = Plat_FloatTime(); int waitIndex = ((m_currentSyncQuery + NUM_FRAME_SYNC_QUERIES) - (NUM_FRAME_SYNC_FRAMES_LATENCY+1)) % NUM_FRAME_SYNC_QUERIES; UpdateFrameSyncQuery( waitIndex, false ); UpdateFrameSyncQuery( m_currentSyncQuery, true ); VCRSetEnabled( true ); } #else DWORD hFence = Dx9Device()->InsertFence(); Dx9Device()->BlockOnFence( hFence ); #endif } //----------------------------------------------------------------------------- // Needs render state //----------------------------------------------------------------------------- void CShaderAPIDx8::QueueResetRenderState() { m_bResetRenderStateNeeded = true; } //----------------------------------------------------------------------------- // Use this to begin and end the frame //----------------------------------------------------------------------------- void CShaderAPIDx8::BeginFrame() { LOCK_SHADERAPI(); if ( m_bResetRenderStateNeeded ) { ResetRenderState( false ); m_bResetRenderStateNeeded = false; } #if ALLOW_SMP_ACCESS Dx9Device()->SetASyncMode( mat_use_smp.GetInt() != 0 ); #endif ++m_CurrentFrame; m_nTextureMemoryUsedLastFrame = 0; } void CShaderAPIDx8::EndFrame() { LOCK_SHADERAPI(); #if !defined( _X360 ) MEMCHECK; #endif ExportTextureList(); } void CShaderAPIDx8::AddBufferToTextureList( const char *pName, D3DSURFACE_DESC &desc ) { // ImageFormat imageFormat; // imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format ); // if( imageFormat < 0 ) // { // Assert( 0 ); // return; // } KeyValues *pSubKey = m_pDebugTextureList->CreateNewKey(); pSubKey->SetString( "Name", pName ); pSubKey->SetString( "TexGroup", TEXTURE_GROUP_RENDER_TARGET ); pSubKey->SetInt( "Size", // ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height ); 4 * desc.Width * desc.Height ); pSubKey->SetString( "Format", "32 bit buffer (hack)" );//ImageLoader::GetName( imageFormat ) ); pSubKey->SetInt( "Width", desc.Width ); pSubKey->SetInt( "Height", desc.Height ); pSubKey->SetInt( "BindsMax", 1 ); pSubKey->SetInt( "BindsFrame", 1 ); } void CShaderAPIDx8::ExportTextureList() { if ( !m_bEnableDebugTextureList ) return; if ( !m_pBackBufferSurface || !m_pZBufferSurface ) // Device vanished... return; m_nDebugDataExportFrame = m_CurrentFrame; if ( IsPC() || !IsX360() ) { if ( m_pDebugTextureList ) m_pDebugTextureList->deleteThis(); m_pDebugTextureList = new KeyValues( "TextureList" ); m_nTextureMemoryUsedTotal = 0; m_nTextureMemoryUsedPicMip1 = 0; m_nTextureMemoryUsedPicMip2 = 0; for ( ShaderAPITextureHandle_t hTexture = m_Textures.Head() ; hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) ) { Texture_t &tex = m_Textures[hTexture]; if ( !( tex.m_Flags & Texture_t::IS_ALLOCATED ) ) continue; // Compute total texture memory usage m_nTextureMemoryUsedTotal += tex.GetMemUsage(); // Compute picmip memory usage { int numBytes = tex.GetMemUsage(); if ( tex.m_NumLevels > 1 ) { if ( tex.GetWidth() > 4 || tex.GetHeight() > 4 || tex.GetDepth() > 4 ) { int topmipsize = ImageLoader::GetMemRequired( tex.GetWidth(), tex.GetHeight(), tex.GetDepth(), tex.GetImageFormat(), false ); numBytes -= topmipsize; m_nTextureMemoryUsedPicMip1 += numBytes; if ( tex.GetWidth() > 8 || tex.GetHeight() > 8 || tex.GetDepth() > 8 ) { int othermipsizeRatio = ( ( tex.GetWidth() > 8 ) ? 2 : 1 ) * ( ( tex.GetHeight() > 8 ) ? 2 : 1 ) * ( ( tex.GetDepth() > 8 ) ? 2 : 1 ); int othermipsize = topmipsize / othermipsizeRatio; numBytes -= othermipsize; } m_nTextureMemoryUsedPicMip1 += numBytes; } else { m_nTextureMemoryUsedPicMip1 += numBytes; m_nTextureMemoryUsedPicMip2 += numBytes; } } else { m_nTextureMemoryUsedPicMip1 += numBytes; m_nTextureMemoryUsedPicMip2 += numBytes; } } if ( !m_bDebugGetAllTextures && tex.m_LastBoundFrame != m_CurrentFrame ) continue; if ( tex.m_LastBoundFrame != m_CurrentFrame ) tex.m_nTimesBoundThisFrame = 0; KeyValues *pSubKey = m_pDebugTextureList->CreateNewKey(); pSubKey->SetString( "Name", tex.m_DebugName.String() ); pSubKey->SetString( "TexGroup", tex.m_TextureGroupName.String() ); pSubKey->SetInt( "Size", tex.GetMemUsage() ); if ( tex.GetCount() > 1 ) pSubKey->SetInt( "Count", tex.GetCount() ); pSubKey->SetString( "Format", ImageLoader::GetName( tex.GetImageFormat() ) ); pSubKey->SetInt( "Width", tex.GetWidth() ); pSubKey->SetInt( "Height", tex.GetHeight() ); pSubKey->SetInt( "BindsMax", tex.m_nTimesBoundMax ); pSubKey->SetInt( "BindsFrame", tex.m_nTimesBoundThisFrame ); } D3DSURFACE_DESC desc; m_pBackBufferSurface->GetDesc( &desc ); AddBufferToTextureList( "BACKBUFFER", desc ); AddBufferToTextureList( "FRONTBUFFER", desc ); // ImageFormat imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format ); // if( imageFormat >= 0 ) { VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_frame_" TEXTURE_GROUP_RENDER_TARGET, COUNTER_GROUP_TEXTURE_PER_FRAME, // ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height ); 2 * 4 * desc.Width * desc.Height ); // hack (times 2 for front and back buffer) } m_pZBufferSurface->GetDesc( &desc ); AddBufferToTextureList( "DEPTHBUFFER", desc ); // imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format ); // if( imageFormat >= 0 ) { VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_frame_" TEXTURE_GROUP_RENDER_TARGET, COUNTER_GROUP_TEXTURE_PER_FRAME, // ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height ); 4 * desc.Width * desc.Height ); // hack } } #if defined( _X360 ) // toggle to do one shot transmission m_bEnableDebugTextureList = false; int numTextures = m_Textures.Count() + 3; xTextureList_t* pXTextureList = (xTextureList_t *)_alloca( numTextures * sizeof( xTextureList_t ) ); memset( pXTextureList, 0, numTextures * sizeof( xTextureList_t ) ); numTextures = 0; for ( ShaderAPITextureHandle_t hTexture = m_Textures.Head() ; hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) ) { Texture_t &tex = m_Textures[hTexture]; if ( !m_bDebugGetAllTextures && tex.m_LastBoundFrame != m_CurrentFrame ) { continue; } if ( !( tex.m_Flags & Texture_t::IS_ALLOCATED ) ) { continue; } int refCount; if ( tex.m_Flags & Texture_t::IS_DEPTH_STENCIL ) { // interface forces us to ignore these refCount = -1; } else { refCount = GetD3DTextureRefCount( CShaderAPIDx8::GetD3DTexture( hTexture ) ); } pXTextureList[numTextures].pName = tex.m_DebugName.String(); pXTextureList[numTextures].size = tex.m_SizeBytes * tex.m_NumCopies; pXTextureList[numTextures].pGroupName = tex.m_TextureGroupName.String(); pXTextureList[numTextures].pFormatName = D3DFormatName( ImageLoader::ImageFormatToD3DFormat( tex.GetImageFormat() ) ); pXTextureList[numTextures].width = tex.GetWidth(); pXTextureList[numTextures].height = tex.GetHeight(); pXTextureList[numTextures].depth = tex.GetDepth(); pXTextureList[numTextures].numLevels = tex.m_NumLevels; pXTextureList[numTextures].binds = tex.m_nTimesBoundThisFrame; pXTextureList[numTextures].refCount = refCount; pXTextureList[numTextures].edram = ( tex.m_Flags & Texture_t::IS_RENDER_TARGET_SURFACE ) != 0; pXTextureList[numTextures].procedural = tex.m_NumCopies > 1; pXTextureList[numTextures].final = ( tex.m_Flags & Texture_t::IS_FINALIZED ) != 0; pXTextureList[numTextures].failed = ( tex.m_Flags & Texture_t::IS_FAILED ) != 0; numTextures++; } // build special entries for implicit surfaces/textures D3DSURFACE_DESC desc; m_pBackBufferSurface->GetDesc( &desc ); int size = ImageLoader::GetMemRequired( desc.Width, desc.Height, 0, ImageLoader::D3DFormatToImageFormat( desc.Format ), false ); pXTextureList[numTextures].pName = "_rt_BackBuffer"; pXTextureList[numTextures].size = size; pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET_SURFACE; pXTextureList[numTextures].pFormatName = D3DFormatName( desc.Format ); pXTextureList[numTextures].width = desc.Width; pXTextureList[numTextures].height = desc.Height; pXTextureList[numTextures].depth = 1; pXTextureList[numTextures].binds = 1; pXTextureList[numTextures].refCount = 1; pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB( desc.Format ); pXTextureList[numTextures].edram = true; numTextures++; m_pZBufferSurface->GetDesc( &desc ); pXTextureList[numTextures].pName = "_rt_DepthBuffer"; pXTextureList[numTextures].size = size; pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET_SURFACE; pXTextureList[numTextures].pFormatName = D3DFormatName( desc.Format ); pXTextureList[numTextures].width = desc.Width; pXTextureList[numTextures].height = desc.Height; pXTextureList[numTextures].depth = 1; pXTextureList[numTextures].binds = 1; pXTextureList[numTextures].refCount = 1; pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB( desc.Format ); pXTextureList[numTextures].edram = true; numTextures++; // front buffer resides in DDR pXTextureList[numTextures].pName = "_rt_FrontBuffer"; pXTextureList[numTextures].size = size; pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET; pXTextureList[numTextures].pFormatName = D3DFormatName( desc.Format ); pXTextureList[numTextures].width = desc.Width; pXTextureList[numTextures].height = desc.Height; pXTextureList[numTextures].depth = 1; pXTextureList[numTextures].binds = 1; pXTextureList[numTextures].refCount = 1; pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB( desc.Format ); numTextures++; int totalMemory = 0; for ( int i = 0; i < numTextures; i++ ) { if ( pXTextureList[i].edram ) { // skip edram based items continue; } totalMemory += pXTextureList[i].size; } Msg( "Total D3D Texture Memory: %.2f MB\n", (float)totalMemory/( 1024.0f * 1024.0f ) ); // transmit to console XBX_rTextureList( numTextures, pXTextureList ); #endif } //----------------------------------------------------------------------------- // Releases/reloads resources when other apps want some memory //----------------------------------------------------------------------------- void CShaderAPIDx8::ReleaseShaderObjects() { ReleaseInternalRenderTargets(); EvictManagedResourcesInternal(); // FIXME: Move into shaderdevice when textures move over. #ifdef _DEBUG // Helps to find the unreleased textures. if ( TextureCount() > 0 ) { ShaderAPITextureHandle_t hTexture; for ( hTexture = m_Textures.Head(); hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next( hTexture ) ) { if ( GetTexture( hTexture ).m_NumCopies == 1 ) { if ( GetTexture( hTexture ).GetTexture() ) { Warning( "Didn't correctly clean up texture 0x%8.8x (%s)\n", hTexture, GetTexture( hTexture ).m_DebugName.String() ); } } else { for ( int k = GetTexture( hTexture ).m_NumCopies; --k >= 0; ) { if ( GetTexture( hTexture ).GetTexture( k ) != 0 ) { Warning( "Didn't correctly clean up texture 0x%8.8x (%s)\n", hTexture, GetTexture( hTexture ).m_DebugName.String() ); break; } } } } } #endif Assert( TextureCount() == 0 ); } void CShaderAPIDx8::RestoreShaderObjects() { AcquireInternalRenderTargets(); SetRenderTarget(); } //-------------------------------------------------------------------- // PIX instrumentation routines // Windows only for now. Turn these on with PIX_INSTRUMENTATION above //-------------------------------------------------------------------- #if 0 // hack versions for OSX to be able to PIX log even when not built debug... void CShaderAPIDx8::BeginPIXEvent( unsigned long color, const char* szName ) { LOCK_SHADERAPI(); GLMBeginPIXEvent( szName ); // direct call no macro return; } void CShaderAPIDx8::EndPIXEvent( void ) { LOCK_SHADERAPI(); GLMEndPIXEvent(); // direct call no macro return; } #else #if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) ConVar pix_break_on_event( "pix_break_on_event", "" ); #endif void CShaderAPIDx8::BeginPIXEvent( unsigned long color, const char* szName ) { #if ( defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) ) //LOCK_SHADERAPI(); const char *p = pix_break_on_event.GetString(); if ( p && V_strlen( p ) ) { if ( V_stristr( szName, p ) != NULL ) { DebuggerBreak(); } } #if defined ( DX_TO_GL_ABSTRACTION ) GLMBeginPIXEvent( szName ); #if defined( _WIN32 ) // AMD PerfStudio integration: Call into D3D9.DLL's D3DPERF_BeginEvent() (this gets intercepted by PerfStudio even in GL mode). if ( g_pShaderDeviceMgrDx8->m_pBeginEvent ) { wchar_t wszName[128]; mbstowcs( wszName, szName, 128 ); g_pShaderDeviceMgrDx8->m_pBeginEvent( 0x2F2F2F2F, wszName ); } #endif #elif defined(_X360 ) #ifndef _DEBUG char szPIXEventName[32]; PIXifyName( szPIXEventName, szName ); PIXBeginNamedEvent( color, szPIXEventName ); #endif #else // PC if ( PIXError() ) return; wchar_t wszName[128]; mbstowcs( wszName, szName, 128 ); // Fire the PIX event, trapping for errors... if ( D3DPERF_BeginEvent( color, wszName ) < 0 ) { Warning( "PIX error Beginning %s event\n", szName ); IncrementPIXError(); } #endif #endif // #if defined( PIX_INSTRUMENTATION ) } void CShaderAPIDx8::EndPIXEvent( void ) { #if ( defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) ) //LOCK_SHADERAPI(); #if defined ( DX_TO_GL_ABSTRACTION ) GLMEndPIXEvent(); #if defined( _WIN32 ) // AMD PerfStudio integration: Call into D3D9.DLL's D3DPERF_EndEvent() (this gets intercepted by PerfStudio even in GL mode). if ( g_pShaderDeviceMgrDx8->m_pEndEvent ) { g_pShaderDeviceMgrDx8->m_pEndEvent(); } #endif #elif defined( _X360 ) #ifndef _DEBUG PIXEndNamedEvent(); #endif #else // PC if ( PIXError() ) return; #if !defined( NVPERFHUD ) // Fire the PIX event, trapping for errors... if ( D3DPERF_EndEvent() < 0 ) { Warning("PIX error ending event\n"); IncrementPIXError(); } #endif #endif #endif // #if defined( PIX_INSTRUMENTATION ) } #endif void CShaderAPIDx8::AdvancePIXFrame() { #if defined( PIX_INSTRUMENTATION ) // Ping PIX when this bool goes from false to true if ( r_pix_start.GetBool() && (!m_bPixCapturing) ) { StartPIXInstrumentation(); m_bPixCapturing = true; } // If we want to record frames... if ( r_pix_recordframes.GetInt() ) { if ( m_nPixFrame == 0 ) // First frame to record { StartPIXInstrumentation(); m_nPixFrame++; } else if( m_nPixFrame == r_pix_recordframes.GetInt() ) // Last frame to record { EndPIXInstrumentation(); r_pix_recordframes.SetValue(0); m_nPixFrame = 0; } else { m_nPixFrame++; // Recording frames... } } #endif } // No begin-end for this...use this to put discrete markers in the PIX stream void CShaderAPIDx8::SetPIXMarker( unsigned long color, const char* szName ) { #if defined( PIX_INSTRUMENTATION ) LOCK_SHADERAPI(); #if defined( DX_TO_GL_ABSTRACTION ) if ( g_pShaderDeviceMgrDx8->m_pSetMarker ) { wchar_t wszName[128]; mbstowcs(wszName, szName, 128 ); g_pShaderDeviceMgrDx8->m_pSetMarker( 0x2F2F2F2F, wszName ); } #elif defined( _X360 ) #ifndef _DEBUG char szPIXMarkerName[32]; PIXifyName( szPIXMarkerName, szName ); PIXSetMarker( color, szPIXMarkerName ); #endif #else // PC if ( PIXError() ) return; wchar_t wszName[128]; mbstowcs(wszName, szName, 128 ); D3DPERF_SetMarker( color, wszName ); #endif #endif // PIX_INSTRUMENTATION } void CShaderAPIDx8::StartPIXInstrumentation() { #if defined( PIX_INSTRUMENTATION ) SetPIXMarker( PIX_VALVE_ORANGE, "Valve_PIX_Capture_Start" ); #endif } void CShaderAPIDx8::EndPIXInstrumentation() { #if defined( PIX_INSTRUMENTATION ) SetPIXMarker( PIX_VALVE_ORANGE, "Valve_PIX_Capture_End" ); #endif } void CShaderAPIDx8::IncrementPIXError() { #if defined( PIX_INSTRUMENTATION ) && !defined( NVPERFHUD ) m_nPIXErrorCount++; if ( m_nPIXErrorCount >= MAX_PIX_ERRORS ) { Warning( "Source engine built with PIX instrumentation, but PIX doesn't seem to have been used to instantiate the game, which is necessary on PC.\n" ); } #endif } // Have we already hit several PIX errors? bool CShaderAPIDx8::PIXError() { #if defined( PIX_INSTRUMENTATION ) && !defined( NVPERFHUD ) return m_nPIXErrorCount >= MAX_PIX_ERRORS; #else return false; #endif } //----------------------------------------------------------------------------- // Check for device lost //----------------------------------------------------------------------------- void CShaderAPIDx8::ChangeVideoMode( const ShaderDeviceInfo_t &info ) { if ( IsX360() ) return; LOCK_SHADERAPI(); m_PendingVideoModeChangeConfig = info; m_bPendingVideoModeChange = true; if ( info.m_DisplayMode.m_nWidth != 0 && info.m_DisplayMode.m_nHeight != 0 ) { m_nWindowWidth = info.m_DisplayMode.m_nWidth; m_nWindowHeight = info.m_DisplayMode.m_nHeight; } } //----------------------------------------------------------------------------- // Compute fill rate //----------------------------------------------------------------------------- void CShaderAPIDx8::ComputeFillRate() { if ( IsX360() ) { // not valid return; } static unsigned char* pBuf = 0; int width, height; GetWindowSize( width, height ); // Snapshot; look at total # pixels drawn... if ( !pBuf ) { int memSize = ShaderUtil()->GetMemRequired( width, height, 1, IMAGE_FORMAT_RGB888, false ) + 4; pBuf = (unsigned char*)malloc( memSize ); } ReadPixels( 0, 0, width, height, pBuf, IMAGE_FORMAT_RGB888 ); int mask = 0xFF; int count = 0; unsigned char* pRead = pBuf; for (int i = 0; i < height; ++i) { for (int j = 0; j < width; ++j) { int val = *(int*)pRead; count += (val & mask); pRead += 3; } } } //----------------------------------------------------------------------------- // Use this to get the mesh builder that allows us to modify vertex data //----------------------------------------------------------------------------- CMeshBuilder* CShaderAPIDx8::GetVertexModifyBuilder() { return &m_ModifyBuilder; } bool CShaderAPIDx8::InFlashlightMode() const { return ShaderUtil()->InFlashlightMode(); } bool CShaderAPIDx8::InEditorMode() const { return ShaderUtil()->InEditorMode(); } //----------------------------------------------------------------------------- // Gets the bound morph's vertex format; returns 0 if no morph is bound //----------------------------------------------------------------------------- MorphFormat_t CShaderAPIDx8::GetBoundMorphFormat() { return ShaderUtil()->GetBoundMorphFormat(); } //----------------------------------------------------------------------------- // returns the current time in seconds... //----------------------------------------------------------------------------- double CShaderAPIDx8::CurrentTime() const { // FIXME: Return game time instead of real time! // Or eliminate this altogether and put it into a material var // (this is used by vertex modifiers in shader code at the moment) return Plat_FloatTime(); } //----------------------------------------------------------------------------- // Methods called by the transition table that use dynamic state... //----------------------------------------------------------------------------- void CShaderAPIDx8::ApplyZBias( const ShadowState_t& shaderState ) { MaterialSystem_Config_t &config = ShaderUtil()->GetConfig(); float a = (config.m_SlopeScaleDepthBias_Decal != 0.0f) ? 1.0f / config.m_SlopeScaleDepthBias_Decal : 0.0f; float b = (config.m_SlopeScaleDepthBias_Normal != 0.0f) ? 1.0f / config.m_SlopeScaleDepthBias_Normal : 0.0f; float c = (config.m_DepthBias_Decal != 0.0f) ? 1.0f / config.m_DepthBias_Decal : 0.0f; float d = (config.m_DepthBias_Normal != 0.0f) ? 1.0f / config.m_DepthBias_Normal : 0.0f; // FIXME: No longer necessary; may be necessary if you want to use cat 4.3 drivers? // GR - hack for R200 bool bPS14Only = g_pHardwareConfig->Caps().m_SupportsPixelShaders_1_4 && !g_pHardwareConfig->Caps().m_SupportsPixelShaders_2_0; if( ( g_pHardwareConfig->Caps().m_VendorID == 0x1002 ) && bPS14Only ) { // Slam to m_SlopeScaleDepthBias_Decal = 0, m_DepthBias_Decal = -4096 // which empirically is what appears to look good on r200 // NOTE: Slamming to 0 instead of -1.0 / 4096 because on Cat 4.9, WinXP, 8500, // this causes the z values to be more than 50 units away from the original z values a = 0.0f; c = -1.0/4096.0; } // bias = (s * D3DRS_SLOPESCALEDEPTHBIAS) + D3DRS_DEPTHBIAS, where s is the maximum depth slope of the triangle being rendered if ( g_pHardwareConfig->Caps().m_ZBiasAndSlopeScaledDepthBiasSupported ) { float fSlopeScaleDepthBias, fDepthBias; if ( shaderState.m_ZBias == SHADER_POLYOFFSET_DECAL ) { fSlopeScaleDepthBias = a; fDepthBias = c; } else if ( shaderState.m_ZBias == SHADER_POLYOFFSET_SHADOW_BIAS ) { fSlopeScaleDepthBias = m_fShadowSlopeScaleDepthBias; fDepthBias = m_fShadowDepthBias; } else // assume SHADER_POLYOFFSET_DISABLE { fSlopeScaleDepthBias = b; fDepthBias = d; } if( ReverseDepthOnX360() ) { fSlopeScaleDepthBias = -fSlopeScaleDepthBias; fDepthBias = -fDepthBias; } SetRenderStateConstMacro( this, D3DRS_SLOPESCALEDEPTHBIAS, *((DWORD*) (&fSlopeScaleDepthBias)) ); SetRenderStateConstMacro( this, D3DRS_DEPTHBIAS, *((DWORD*) (&fDepthBias)) ); } else { MarkAllUserClipPlanesDirty(); m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |= STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION; } } void CShaderAPIDx8::ApplyTextureEnable( const ShadowState_t& state, int nSampler ) { if ( state.m_SamplerState[nSampler].m_TextureEnable == SamplerState(nSampler).m_TextureEnable ) return; if ( state.m_SamplerState[nSampler].m_TextureEnable ) { SamplerState( nSampler ).m_TextureEnable = true; // Should not be necessary/possible (SetTextureState() calls D3D9/DXAbstract, so the calling thread must already own the device. //LOCK_SHADERAPI(); // Don't do this here!! It ends up giving us extra texture sets. // We'll Assert in debug mode if you enable a texture stage // but don't bind a texture. // see CShaderAPIDx8::RenderPass() for this check. // NOTE: We aren't doing this optimization quite yet. There are situations // where you want a texture stage enabled for its texture coordinates, but // you don't actually bind a texture (texmvspec for example.) SetTextureState( (Sampler_t)nSampler, SamplerState(nSampler).m_BoundTexture, true ); } else { SamplerState( nSampler ).m_TextureEnable = false; SetTextureState( (Sampler_t)nSampler, INVALID_SHADERAPI_TEXTURE_HANDLE ); } } //----------------------------------------------------------------------------- // Used to clear the transition table when we know it's become invalid. //----------------------------------------------------------------------------- void CShaderAPIDx8::ClearSnapshots() { LOCK_SHADERAPI(); FlushBufferedPrimitives(); m_TransitionTable.Reset(); InitRenderState(); } static void KillTranslation( D3DXMATRIX& mat ) { mat[3] = 0.0f; mat[7] = 0.0f; mat[11] = 0.0f; mat[12] = 0.0f; mat[13] = 0.0f; mat[14] = 0.0f; mat[15] = 1.0f; } static void PrintMatrix( const char *name, const D3DXMATRIX& mat ) { int row, col; char buf[128]; Plat_DebugString( name ); Plat_DebugString( "\n" ); for( row = 0; row < 4; row++ ) { Plat_DebugString( " " ); for( col = 0; col < 4; col++ ) { sprintf( buf, "%f ", ( float )mat( row, col ) ); Plat_DebugString( buf ); } Plat_DebugString( "\n" ); } Plat_DebugString( "\n" ); } //----------------------------------------------------------------------------- // Gets the vertex format for a particular snapshot id //----------------------------------------------------------------------------- VertexFormat_t CShaderAPIDx8::ComputeVertexUsage( int num, StateSnapshot_t* pIds ) const { LOCK_SHADERAPI(); if (num == 0) return 0; // We don't have to all sorts of crazy stuff if there's only one snapshot if ( num == 1 ) { const ShadowShaderState_t& state = m_TransitionTable.GetSnapshotShader( pIds[0] ); return state.m_VertexUsage; } Assert( pIds ); // Aggregating vertex formats is a little tricky; // For example, what do we do when two passes want user data? // Can we assume they are the same? For now, I'm going to // just print a warning in debug. VertexCompressionType_t compression = VERTEX_COMPRESSION_INVALID; int userDataSize = 0; int numBones = 0; int texCoordSize[VERTEX_MAX_TEXTURE_COORDINATES] = { 0, 0, 0, 0, 0, 0, 0, 0 }; int flags = 0; for (int i = num; --i >= 0; ) { const ShadowShaderState_t& state = m_TransitionTable.GetSnapshotShader( pIds[i] ); VertexFormat_t fmt = state.m_VertexUsage; flags |= VertexFlags(fmt); VertexCompressionType_t newCompression = CompressionType( fmt ); if ( ( compression != newCompression ) && ( compression != VERTEX_COMPRESSION_INVALID ) ) { Warning("Encountered a material with two passes that specify different vertex compression types!\n"); compression = VERTEX_COMPRESSION_NONE; // Be safe, disable compression } int newNumBones = NumBoneWeights(fmt); if ((numBones != newNumBones) && (newNumBones != 0)) { if (numBones != 0) { Warning("Encountered a material with two passes that use different numbers of bones!\n"); } numBones = newNumBones; } int newUserSize = UserDataSize(fmt); if ((userDataSize != newUserSize) && (newUserSize != 0)) { if (userDataSize != 0) { Warning("Encountered a material with two passes that use different user data sizes!\n"); } userDataSize = newUserSize; } for ( int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j ) { int newSize = TexCoordSize( (TextureStage_t)j, fmt ); if ( ( texCoordSize[j] != newSize ) && ( newSize != 0 ) ) { if ( texCoordSize[j] != 0 ) { Warning("Encountered a material with two passes that use different texture coord sizes!\n"); } if ( texCoordSize[j] < newSize ) { texCoordSize[j] = newSize; } } } } return MeshMgr()->ComputeVertexFormat( flags, VERTEX_MAX_TEXTURE_COORDINATES, texCoordSize, numBones, userDataSize ); } VertexFormat_t CShaderAPIDx8::ComputeVertexFormat( int num, StateSnapshot_t* pIds ) const { LOCK_SHADERAPI(); VertexFormat_t fmt = ComputeVertexUsage( num, pIds ); return fmt; } //----------------------------------------------------------------------------- // What fields in the morph do we actually use? //----------------------------------------------------------------------------- MorphFormat_t CShaderAPIDx8::ComputeMorphFormat( int numSnapshots, StateSnapshot_t* pIds ) const { LOCK_SHADERAPI(); MorphFormat_t format = 0; for ( int i = 0; i < numSnapshots; ++i ) { MorphFormat_t fmt = m_TransitionTable.GetSnapshotShader( pIds[i] ).m_MorphUsage; format |= VertexFlags(fmt); } return format; } //----------------------------------------------------------------------------- // Commits a range of vertex shader constants //----------------------------------------------------------------------------- static void CommitVertexShaderConstantRange( IDirect3DDevice9 *pDevice, const DynamicState_t &desiredState, DynamicState_t ¤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( numVecs > 0 ); Assert( pVec ); if ( IsPC() || IsPS3() ) { Assert( var + numVecs <= g_pHardwareConfig->NumVertexShaderConstants() ); if ( !bForce && memcmp( pVec, &m_DynamicState.m_pVectorVertexShaderConstant[var], numVecs * 4 * sizeof( float ) ) == 0 ) return; Dx9Device()->SetVertexShaderConstantF( var, pVec, numVecs ); memcpy( &m_DynamicState.m_pVectorVertexShaderConstant[var], pVec, numVecs * 4 * sizeof(float) ); } else { Assert( var + numVecs <= g_pHardwareConfig->NumVertexShaderConstants() ); } if ( IsX360() && var + numVecs > m_MaxVectorVertexShaderConstant ) m_MaxVectorVertexShaderConstant = var + numVecs; memcpy( &m_DesiredState.m_pVectorVertexShaderConstant[var], pVec, numVecs * 4 * sizeof(float) ); } //----------------------------------------------------------------------------- // Sets the constant register for vertex and pixel shaders //----------------------------------------------------------------------------- void CShaderAPIDx8::SetVertexShaderConstant( int var, float const* pVec, int numVecs, bool bForce ) { SetVertexShaderConstantInternal( var, pVec, numVecs, bForce ); } //----------------------------------------------------------------------------- // Sets the boolean registers for vertex shader control flow //----------------------------------------------------------------------------- void CShaderAPIDx8::SetBooleanVertexShaderConstant( int var, int const* pVec, int numBools, bool bForce ) { Assert( pVec ); Assert( var + numBools <= g_pHardwareConfig->NumBooleanVertexShaderConstants() ); if ( IsPC() && g_pHardwareConfig->GetDXSupportLevel() < 90 ) { return; } if ( IsPC() && !bForce && memcmp( pVec, &m_DesiredState.m_pBooleanVertexShaderConstant[var], numBools * sizeof( BOOL ) ) == 0 ) { return; } if ( IsPC() ) { Dx9Device()->SetVertexShaderConstantB( var, pVec, numBools ); memcpy( &m_DynamicState.m_pBooleanVertexShaderConstant[var], pVec, numBools * sizeof(BOOL) ); } memcpy( &m_DesiredState.m_pBooleanVertexShaderConstant[var], pVec, numBools * sizeof(BOOL) ); if ( IsX360() && var + numBools > m_MaxBooleanVertexShaderConstant ) { m_MaxBooleanVertexShaderConstant = var + numBools; Assert( m_MaxBooleanVertexShaderConstant <= 16 ); } } //----------------------------------------------------------------------------- // Sets the integer registers for vertex shader control flow //----------------------------------------------------------------------------- void CShaderAPIDx8::SetIntegerVertexShaderConstant( int var, int const* pVec, int numIntVecs, bool bForce ) { Assert( pVec ); Assert( var + numIntVecs <= g_pHardwareConfig->NumIntegerVertexShaderConstants() ); if ( IsPC() && g_pHardwareConfig->GetDXSupportLevel() < 90 ) { return; } if ( IsPC() && !bForce && memcmp( pVec, &m_DesiredState.m_pIntegerVertexShaderConstant[var], numIntVecs * sizeof( IntVector4D ) ) == 0 ) { return; } if ( IsPC() ) { Dx9Device()->SetVertexShaderConstantI( var, pVec, numIntVecs ); memcpy( &m_DynamicState.m_pIntegerVertexShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D) ); } memcpy( &m_DesiredState.m_pIntegerVertexShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D) ); if ( IsX360() && var + numIntVecs > m_MaxIntegerVertexShaderConstant ) { m_MaxIntegerVertexShaderConstant = var + numIntVecs; Assert( m_MaxIntegerVertexShaderConstant <= 16 ); } } FORCEINLINE void CShaderAPIDx8::SetPixelShaderConstantInternal( int nStartConst, float const* pValues, int nNumConsts, bool bForce ) { Assert( nStartConst + nNumConsts <= g_pHardwareConfig->NumPixelShaderConstants() ); if ( IsPC() || IsPS3() ) { if ( !bForce ) { DWORD* pSrc = (DWORD*)pValues; DWORD* pDst = (DWORD*)&m_DesiredState.m_pVectorPixelShaderConstant[nStartConst]; while( nNumConsts && ( pSrc[0] == pDst[0] ) && ( pSrc[1] == pDst[1] ) && ( pSrc[2] == pDst[2] ) && ( pSrc[3] == pDst[3] ) ) { pSrc += 4; pDst += 4; nNumConsts--; nStartConst++; } if ( !nNumConsts ) return; pValues = reinterpret_cast< float const * >( pSrc ); } Dx9Device()->SetPixelShaderConstantF( nStartConst, pValues, nNumConsts ); memcpy( &m_DynamicState.m_pVectorPixelShaderConstant[nStartConst], pValues, nNumConsts * 4 * sizeof(float) ); } if ( IsX360() && nStartConst + nNumConsts > m_MaxVectorPixelShaderConstant ) { m_MaxVectorPixelShaderConstant = nStartConst + nNumConsts; Assert( m_MaxVectorPixelShaderConstant <= 32 ); if ( m_MaxVectorPixelShaderConstant > 32 ) { // NOTE! There really are 224 pixel shader constants on the 360, but we do an optimization that only blasts the first 32 always. Error( "Don't use more then the first 32 pixel shader constants on the 360!" ); } } memcpy( &m_DesiredState.m_pVectorPixelShaderConstant[nStartConst], pValues, nNumConsts * 4 * sizeof(float) ); } void CShaderAPIDx8::SetPixelShaderConstant( int var, float const* pVec, int numVecs, bool bForce ) { SetPixelShaderConstantInternal( var, pVec, numVecs, bForce ); } template 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