Files
cool-source-archive/materialsystem/shaderapidxvk/shaderapidxvk.cpp
T
2022-02-16 09:39:20 +07:00

14351 lines
436 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//
// The dx8 implementation of the shader API
//===========================================================================//
/*
DX9 todo:
-make the transforms in the older shaders match the transforms in lightmappedgeneric
-fix polyoffset for hardware that doesn't support D3DRS_SLOPESCALEDEPTHBIAS and D3DRS_DEPTHBIAS
- code is there, I think matrix offset just needs tweaking
-fix forcehardwaresync - implement texture locking for hardware that doesn't support async query
-get the format for GetAdapterModeCount and EnumAdapterModes from somewhere (shaderapidx8.cpp, GetModeCount, GetModeInfo)
-record frame sync objects (allocframesyncobjects, free framesync objects, ForceHardwareSync)
-Need to fix ENVMAPMASKSCALE, BUMPOFFSET in lightmappedgeneric*.cpp and vertexlitgeneric*.cpp
fix this:
// FIXME: This also depends on the vertex format and whether or not we are static lit in dx9
#ifndef SHADERAPIDX9
if (m_DynamicState.m_VertexShader != shader) // garymcthack
#endif // !SHADERAPIDX9
unrelated to dx9:
mat_fullbright 1 doesn't work properly on alpha materials in testroom_standards
*/
#define DISABLE_PROTECTED_THINGS
#include "shaderapidx8.h"
#include "shaderapidx8_global.h"
#include "shadershadowdx8.h"
#include "locald3dtypes.h"
#include "utlvector.h"
#include "IHardwareConfigInternal.h"
#include "utlstack.h"
#include "shaderapi/ishaderutil.h"
#include "shaderapi/commandbuffer.h"
#include "shaderapidx8_global.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/itexture.h"
#include "imaterialinternal.h"
#include "imeshdx8.h"
#include "materialsystem/imorph.h"
#include "colorformatdx8.h"
#include "texturedx8.h"
#include "textureheap.h"
#include <malloc.h>
#include "interface.h"
#include "utlrbtree.h"
#include "utlsymbol.h"
#include "tier1/strtools.h"
#include "recording.h"
#ifndef _X360
#include <crtmemdebug.h>
#endif
#include "vertexshaderdx8.h"
#include "filesystem.h"
#include "mathlib/mathlib.h"
#include "materialsystem/materialsystem_config.h"
#include "worldsize.h"
#include "TransitionTable.h"
#include "tier0/vcrmode.h"
#include "tier0/vprof.h"
#include "tier1/tier1.h"
#include "tier1/utlbuffer.h"
#include "vertexdecl.h"
#include "tier0/icommandline.h"
#include "IShaderSystem.h"
#include "tier1/convar.h"
#include "tier1/KeyValues.h"
#include "Color.h"
#ifdef RECORDING
#include "materialsystem/IShader.h"
#endif
#include "../stdshaders/common_hlsl_cpp_consts.h" // hack hack hack!
#include "KeyValues.h"
#include "bitmap/imageformat.h"
#include "materialsystem/idebugtextureinfo.h"
#include "tier1/utllinkedlist.h"
#include "vtf/vtf.h"
#include "datacache/idatacache.h"
#include "renderparm.h"
#include "tier2/tier2.h"
#include "materialsystem/deformations.h"
#include "bitmap/tgawriter.h"
#include "tier0/icommandline.h"
#include "togl/rendermechanism.h" // provides GLMPRINTF/GLMPRINTSTR / GLMPRINTEXT macros which only activate if GLMDEBUG is nonzero and POSIX is defined.
#if defined( _X360 )
#include "xbox/xbox_console.h"
#include "xbox/xbox_win32stubs.h"
#include "xbox/xbox_launch.h"
#endif
#include "tier0/tslist.h"
#ifndef _X360
#include "wmi.h"
#endif
#include "filesystem/IQueuedLoader.h"
#include "shaderdevicedx8.h"
#include "togl/rendermechanism.h"
// Define this if you want to use a stubbed d3d.
//#define STUBD3D
#ifdef STUBD3D
#include "stubd3ddevice.h"
#endif
#include "winutils.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#if defined( OSX )
typedef unsigned int DWORD;
typedef DWORD* LPDWORD;
#endif
#ifdef _WIN32
#pragma warning (disable:4189)
#endif
ConVar mat_texture_limit("mat_texture_limit", "-1", FCVAR_NEVER_AS_STRING,
"If this value is not -1, the material system will limit the amount of texture memory it uses in a frame."
" Useful for identifying performance cliffs. The value is in kilobytes.");
ConVar mat_frame_sync_enable("mat_frame_sync_enable", "1", FCVAR_CHEAT);
ConVar mat_frame_sync_force_texture("mat_frame_sync_force_texture", "0", FCVAR_CHEAT, "Force frame syncing to lock a managed texture.");
#if defined( _X360 )
ConVar mat_texturecachesize("mat_texturecachesize", "176");
ConVar mat_force_flush_texturecache("mat_force_flush_texturecache", "0");
#endif
extern ConVar mat_debugalttab;
#define ALLOW_SMP_ACCESS 0
#if ALLOW_SMP_ACCESS
static ConVar mat_use_smp("mat_use_smp", "0");
#endif
// Convars for driving PIX (not all hooked up yet...JasonM)
static ConVar r_pix_start("r_pix_start", "0");
static ConVar r_pix_recordframes("r_pix_recordframes", "0");
#define D3DDeviceWrapper IDirect3DDevice9
//-----------------------------------------------------------------------------
// Some important enumerations
//-----------------------------------------------------------------------------
enum
{
MAX_VERTEX_TEXTURE_COUNT = 4,
};
//-----------------------------------------------------------------------------
// These board states change with high frequency; are not shadowed
//-----------------------------------------------------------------------------
struct TextureStageState_t
{
D3DTEXTURETRANSFORMFLAGS m_TextureTransformFlags;
float m_BumpEnvMat00;
float m_BumpEnvMat01;
float m_BumpEnvMat10;
float m_BumpEnvMat11;
};
struct SamplerState_t
{
ShaderAPITextureHandle_t m_BoundTexture;
D3DTEXTUREADDRESS m_UTexWrap;
D3DTEXTUREADDRESS m_VTexWrap;
D3DTEXTUREADDRESS m_WTexWrap;
D3DTEXTUREFILTERTYPE m_MagFilter;
D3DTEXTUREFILTERTYPE m_MinFilter;
D3DTEXTUREFILTERTYPE m_MipFilter;
int m_FinestMipmapLevel;
float m_LodBias;
int m_nAnisotropicLevel;
bool m_TextureEnable;
bool m_SRGBReadEnable;
};
//-----------------------------------------------------------------------------
// State related to vertex textures
//-----------------------------------------------------------------------------
struct VertexTextureState_t
{
ShaderAPITextureHandle_t m_BoundTexture;
D3DTEXTUREADDRESS m_UTexWrap;
D3DTEXTUREADDRESS m_VTexWrap;
D3DTEXTUREFILTERTYPE m_MagFilter;
D3DTEXTUREFILTERTYPE m_MinFilter;
D3DTEXTUREFILTERTYPE m_MipFilter;
};
enum TransformType_t
{
TRANSFORM_IS_IDENTITY = 0,
TRANSFORM_IS_CAMERA_TO_WORLD,
TRANSFORM_IS_GENERAL,
};
enum TransformDirtyBits_t
{
STATE_CHANGED_VERTEX_SHADER = 0x1,
STATE_CHANGED_FIXED_FUNCTION = 0x2,
STATE_CHANGED = 0x3
};
enum
{
#if !defined( _X360 )
MAX_NUM_RENDERSTATES = (D3DRS_BLENDOPALPHA + 1),
#else
MAX_NUM_RENDERSTATES = D3DRS_MAX,
#endif
// MORPH_TARGET_FACTOR_COUNT = VERTEX_SHADER_MORPH_TARGET_FACTOR_COUNT * 4,
};
struct DynamicState_t
{
// Constant color
unsigned int m_ConstantColor;
// Normalize normals?
bool m_NormalizeNormals;
// Viewport state
D3DVIEWPORT9 m_Viewport;
// Transform state
D3DXMATRIX m_Transform[NUM_MATRIX_MODES];
unsigned char m_TransformType[NUM_MATRIX_MODES];
unsigned char m_TransformChanged[NUM_MATRIX_MODES];
// Ambient light color
D3DCOLOR m_Ambient;
D3DLIGHT m_Lights[MAX_NUM_LIGHTS];
LightDesc_t m_LightDescs[MAX_NUM_LIGHTS];
bool m_LightEnable[MAX_NUM_LIGHTS];
Vector4D m_AmbientLightCube[6];
unsigned char m_LightChanged[MAX_NUM_LIGHTS];
unsigned char m_LightEnableChanged[MAX_NUM_LIGHTS];
VertexShaderLightTypes_t m_LightType[MAX_NUM_LIGHTS];
Vector m_vLightingOrigin;
int m_NumLights;
// Shade mode
D3DSHADEMODE m_ShadeMode;
// Clear color
D3DCOLOR m_ClearColor;
// Fog
D3DCOLOR m_FogColor;
float m_PixelFogColor[4];
bool m_bFogGammaCorrectionDisabled;
bool m_FogEnable;
MaterialFogMode_t m_SceneFog;
D3DFOGMODE m_FogMode;
float m_FogStart;
float m_FogEnd;
float m_FogZ;
float m_FogMaxDensity;
float m_HeightClipZ;
MaterialHeightClipMode_t m_HeightClipMode;
// user clip planes
int m_UserClipPlaneEnabled;
int m_UserClipPlaneChanged;
D3DXPLANE m_UserClipPlaneWorld[MAXUSERCLIPPLANES];
D3DXPLANE m_UserClipPlaneProj[MAXUSERCLIPPLANES];
bool m_UserClipLastUpdatedUsingFixedFunction;
bool m_FastClipEnabled;
bool m_bFastClipPlaneChanged;
D3DXPLANE m_FastClipPlane;
// Used when overriding the user clip plane
bool m_bUserClipTransformOverride;
D3DXMATRIX m_UserClipTransform;
// Cull mode
D3DCULL m_DesiredCullMode;
D3DCULL m_CullMode;
bool m_bCullEnabled;
// Skinning
D3DVERTEXBLENDFLAGS m_VertexBlend;
int m_NumBones;
// Pixel and vertex shader constants...
Vector4D* m_pVectorVertexShaderConstant;
BOOL* m_pBooleanVertexShaderConstant;
IntVector4D* m_pIntegerVertexShaderConstant;
Vector4D* m_pVectorPixelShaderConstant;
BOOL* m_pBooleanPixelShaderConstant;
IntVector4D* m_pIntegerPixelShaderConstant;
// Texture stage state
TextureStageState_t m_TextureStage[MAX_TEXTURE_STAGES];
SamplerState_t m_SamplerState[MAX_SAMPLERS];
// Vertex texture stage state
VertexTextureState_t m_VertexTextureState[MAX_VERTEX_TEXTURE_COUNT];
DWORD m_RenderState[MAX_NUM_RENDERSTATES];
RECT m_ScissorRect;
IDirect3DVertexDeclaration9* m_pVertexDecl;
bool m_bSRGBWritesEnabled;
bool m_bHWMorphingEnabled;
float m_DestAlphaDepthRange; //Dest alpha writes compress the depth to get better results. This holds the default setting that can be overriden with r_destalpharange
#if defined( _X360 )
int m_iVertexShaderGPRAllocation; //only need to track vertex shader
bool m_bBuffer2Frames;
#endif
DynamicState_t() {}
private:
DynamicState_t(DynamicState_t const&);
};
//-----------------------------------------------------------------------------
// Method to queue up dirty dynamic state change calls
//-----------------------------------------------------------------------------
typedef void (*StateCommitFunc_t)(IDirect3DDevice9* pDevice, const DynamicState_t& desiredState, DynamicState_t& currentState, bool bForce);
static void CommitSetViewports(IDirect3DDevice9* pDevice, const DynamicState_t& desiredState, DynamicState_t& currentState, bool bForce);
// NOTE: It's slightly memory inefficient, and definitely not typesafe,
// to put all commit funcs into the same table (vs, ff, per-draw, per-pass),
// but it makes the code a heck of a lot simpler and smaller.
enum CommitFunc_t
{
COMMIT_FUNC_CommitVertexTextures = 0,
COMMIT_FUNC_CommitFlexWeights,
COMMIT_FUNC_CommitSetScissorRect,
COMMIT_FUNC_CommitSetViewports,
#if defined( _X360 )
COMMIT_FUNC_CommitShaderGPRs,
#endif
COMMIT_FUNC_COUNT,
COMMIT_FUNC_BYTE_COUNT = (COMMIT_FUNC_COUNT + 0x7) >> 3,
};
enum CommitFuncType_t
{
COMMIT_PER_DRAW = 0,
COMMIT_PER_PASS,
COMMIT_FUNC_TYPE_COUNT,
};
enum CommitShaderType_t
{
COMMIT_FIXED_FUNCTION = 0,
COMMIT_VERTEX_SHADER,
COMMIT_ALWAYS,
COMMIT_SHADER_TYPE_COUNT,
};
#define ADD_COMMIT_FUNC( _func, _shader, _func_name ) \
if ( !IsCommitFuncInUse( _func, _shader, COMMIT_FUNC_ ## _func_name ) ) \
{ \
AddCommitFunc( _func, _shader, _func_name ); \
MarkCommitFuncInUse( _func, _shader, COMMIT_FUNC_ ## _func_name ); \
}
#define ADD_RENDERSTATE_FUNC( _func, _shader, _func_name, _state, _val ) \
if ( m_bResettingRenderState || (m_DesiredState._state != _val) ) \
{ \
m_DesiredState._state = _val; \
ADD_COMMIT_FUNC( _func, _shader, _func_name ) \
}
#define ADD_VERTEX_TEXTURE_FUNC( _func, _shader, _func_name, _stage, _state, _val ) \
Assert( ( int )_stage < MAX_VERTEX_TEXTURE_COUNT ); \
if ( m_bResettingRenderState || (m_DesiredState.m_VertexTextureState[_stage]._state != _val) ) \
{ \
m_DesiredState.m_VertexTextureState[_stage]._state = _val; \
ADD_COMMIT_FUNC( _func, _shader, _func_name ) \
}
//-----------------------------------------------------------------------------
// Check render state support at compile time instead of runtime
//-----------------------------------------------------------------------------
#define SetSupportedRenderState( _state, _val ) \
{ \
if( _state != D3DRS_NOTSUPPORTED ) \
{ \
SetRenderState( _state, _val, false ); \
} \
}
#define SetSupportedRenderStateForce( _state, _val ) \
{ \
if( _state != D3DRS_NOTSUPPORTED ) \
{ \
SetRenderStateForce( _state, _val ); \
} \
}
//-----------------------------------------------------------------------------
// Allocated textures
//-----------------------------------------------------------------------------
struct Texture_t
{
Texture_t()
{
m_Flags = 0;
m_Count = 1;
m_CountIndex = 0;
m_nTimesBoundMax = 0;
m_nTimesBoundThisFrame = 0;
m_pTexture = NULL;
m_ppTexture = NULL;
m_ImageFormat = IMAGE_FORMAT_RGBA8888;
m_pTextureGroupCounterGlobal = NULL;
m_pTextureGroupCounterFrame = NULL;
m_FinestMipmapLevel = 0;
m_LodBias = 0.0f;
}
// FIXME: Compress this info
D3DTEXTUREADDRESS m_UTexWrap;
D3DTEXTUREADDRESS m_VTexWrap;
D3DTEXTUREADDRESS m_WTexWrap;
D3DTEXTUREFILTERTYPE m_MagFilter;
D3DTEXTUREFILTERTYPE m_MinFilter;
D3DTEXTUREFILTERTYPE m_MipFilter;
int m_FinestMipmapLevel;
float m_LodBias;
unsigned char m_NumLevels;
unsigned char m_SwitchNeeded; // Do we need to advance the current copy?
unsigned char m_NumCopies; // copies are used to optimize procedural textures
unsigned char m_CurrentCopy; // the current copy we're using...
int m_CreationFlags;
CUtlSymbol m_DebugName;
CUtlSymbol m_TextureGroupName;
int* m_pTextureGroupCounterGlobal; // Global counter for this texture's group.
int* m_pTextureGroupCounterFrame; // Per-frame global counter for this texture's group.
// stats stuff
int m_SizeBytes;
int m_SizeTexels;
int m_LastBoundFrame;
int m_nTimesBoundMax;
int m_nTimesBoundThisFrame;
enum Flags_t
{
IS_ALLOCATED = 0x0001,
IS_DEPTH_STENCIL = 0x0002,
IS_DEPTH_STENCIL_TEXTURE = 0x0004, // depth stencil texture, not surface
IS_RENDERABLE = (IS_DEPTH_STENCIL | IS_ALLOCATED),
IS_LOCKABLE = 0x0008,
IS_FINALIZED = 0x0010, // 360: completed async hi-res load
IS_FAILED = 0x0020, // 360: failed during load
CAN_CONVERT_FORMAT = 0x0040, // 360: allow format conversion
IS_LINEAR = 0x0080, // 360: unswizzled linear format
IS_RENDER_TARGET = 0x0100, // 360: marks a render target texture source
IS_RENDER_TARGET_SURFACE = 0x0200, // 360: marks a render target surface target
IS_VERTEX_TEXTURE = 0x0800,
};
short m_Width;
short m_Height;
short m_Depth;
unsigned short m_Flags;
typedef IDirect3DBaseTexture* IDirect3DBaseTexturePtr;
typedef IDirect3DBaseTexture** IDirect3DBaseTexturePtrPtr;
typedef IDirect3DSurface* IDirect3DSurfacePtr;
IDirect3DBaseTexturePtr GetTexture(void)
{
Assert(m_NumCopies == 1);
Assert(!(m_Flags & IS_DEPTH_STENCIL));
return m_pTexture;
}
IDirect3DBaseTexturePtr GetTexture(int copy)
{
Assert(m_NumCopies > 1);
Assert(!(m_Flags & IS_DEPTH_STENCIL));
return m_ppTexture[copy];
}
IDirect3DBaseTexturePtrPtr& GetTextureArray(void)
{
Assert(m_NumCopies > 1);
Assert(!(m_Flags & IS_DEPTH_STENCIL));
return m_ppTexture;
}
IDirect3DSurfacePtr& GetDepthStencilSurface(void)
{
Assert(m_NumCopies == 1);
Assert((m_Flags & IS_DEPTH_STENCIL));
return m_pDepthStencilSurface;
}
IDirect3DSurfacePtr& GetRenderTargetSurface(bool bSRGB)
{
Assert(m_NumCopies == 1);
Assert(m_Flags & IS_RENDER_TARGET_SURFACE);
return m_pRenderTargetSurface[bSRGB];
}
void SetTexture(IDirect3DBaseTexturePtr pPtr)
{
m_pTexture = pPtr;
}
void SetTexture(int copy, IDirect3DBaseTexturePtr pPtr)
{
m_ppTexture[copy] = pPtr;
}
int GetMemUsage() const
{
return m_SizeBytes;
}
int GetWidth() const
{
return (int)m_Width;
}
int GetHeight() const
{
return (int)m_Height;
}
int GetDepth() const
{
return (int)m_Depth;
}
int GetLodClamp() const
{
return m_FinestMipmapLevel;
}
void SetImageFormat(ImageFormat format)
{
m_ImageFormat = format;
}
ImageFormat GetImageFormat() const
{
return m_ImageFormat;
}
private:
union
{
IDirect3DBaseTexture* m_pTexture; // used when there's one copy
IDirect3DBaseTexture** m_ppTexture; // used when there are more than one copies
IDirect3DSurface* m_pDepthStencilSurface; // used when there's one depth stencil surface
IDirect3DSurface* m_pRenderTargetSurface[2];
};
ImageFormat m_ImageFormat;
public:
short m_Count;
short m_CountIndex;
short GetCount() const
{
return m_Count;
}
};
#define MAX_DEFORMATION_PARAMETERS 16
#define DEFORMATION_STACK_DEPTH 10
struct Deformation_t
{
int m_nDeformationType;
int m_nNumParameters;
float m_flDeformationParameters[MAX_DEFORMATION_PARAMETERS];
};
//-----------------------------------------------------------------------------
// The DX8 implementation of the shader API
//-----------------------------------------------------------------------------
class CShaderAPIDx8 : public CShaderDeviceDx8, public IShaderAPIDX8, public IDebugTextureInfo
{
typedef CShaderDeviceDx8 BaseClass;
public:
// constructor, destructor
CShaderAPIDx8();
virtual ~CShaderAPIDx8();
// Methods of IShaderAPI
public:
virtual void SetViewports(int nCount, const ShaderViewport_t* pViewports);
virtual int GetViewports(ShaderViewport_t* pViewports, int nMax) const;
virtual void ClearBuffers(bool bClearColor, bool bClearDepth, bool bClearStencil, int renderTargetWidth, int renderTargetHeight);
virtual void ClearColor3ub(unsigned char r, unsigned char g, unsigned char b);
virtual void ClearColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
virtual void BindVertexShader(VertexShaderHandle_t hVertexShader);
virtual void BindGeometryShader(GeometryShaderHandle_t hGeometryShader);
virtual void BindPixelShader(PixelShaderHandle_t hPixelShader);
virtual void SetRasterState(const ShaderRasterState_t& state);
virtual void SetFlexWeights(int nFirstWeight, int nCount, const MorphWeight_t* pWeights);
// Methods of IShaderDynamicAPI
public:
virtual void GetBackBufferDimensions(int& nWidth, int& nHeight) const
{
// Chain to the device
BaseClass::GetBackBufferDimensions(nWidth, nHeight);
}
virtual void MarkUnusedVertexFields(unsigned int nFlags, int nTexCoordCount, bool* pUnusedTexCoords);
public:
// Methods of CShaderAPIBase
virtual bool OnDeviceInit();
virtual void OnDeviceShutdown();
virtual void ReleaseShaderObjects();
virtual void RestoreShaderObjects();
virtual void BeginPIXEvent(unsigned long color, const char* szName);
virtual void EndPIXEvent();
virtual void AdvancePIXFrame();
public:
// Methods of IShaderAPIDX8
virtual void QueueResetRenderState();
//
// Abandon all hope ye who pass below this line which hasn't been ported.
//
// Sets the mode...
bool SetMode(void* VD3DHWND, int nAdapter, const ShaderDeviceInfo_t& info);
// Change the video mode after it's already been set.
void ChangeVideoMode(const ShaderDeviceInfo_t& info);
// Sets the default render state
void SetDefaultState();
// Methods to ask about particular state snapshots
virtual bool IsTranslucent(StateSnapshot_t id) const;
virtual bool IsAlphaTested(StateSnapshot_t id) const;
virtual bool UsesVertexAndPixelShaders(StateSnapshot_t id) const;
virtual int CompareSnapshots(StateSnapshot_t snapshot0, StateSnapshot_t snapshot1);
// Computes the vertex format for a particular set of snapshot ids
VertexFormat_t ComputeVertexFormat(int num, StateSnapshot_t* pIds) const;
VertexFormat_t ComputeVertexUsage(int num, StateSnapshot_t* pIds) const;
// What fields in the morph do we actually use?
virtual MorphFormat_t ComputeMorphFormat(int numSnapshots, StateSnapshot_t* pIds) const;
// Uses a state snapshot
void UseSnapshot(StateSnapshot_t snapshot);
// Color state
void Color3f(float r, float g, float b);
void Color4f(float r, float g, float b, float a);
void Color3fv(float const* c);
void Color4fv(float const* c);
void Color3ub(unsigned char r, unsigned char g, unsigned char b);
void Color3ubv(unsigned char const* pColor);
void Color4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
void Color4ubv(unsigned char const* pColor);
// Set the number of bone weights
virtual void SetNumBoneWeights(int numBones);
virtual void EnableHWMorphing(bool bEnable);
// Sets the vertex and pixel shaders
virtual void SetVertexShaderIndex(int vshIndex = -1);
virtual void SetPixelShaderIndex(int pshIndex = 0);
// Matrix state
void MatrixMode(MaterialMatrixMode_t matrixMode);
void PushMatrix();
void PopMatrix();
void LoadMatrix(float* m);
void LoadBoneMatrix(int boneIndex, const float* m);
void MultMatrix(float* m);
void MultMatrixLocal(float* m);
void GetMatrix(MaterialMatrixMode_t matrixMode, float* dst);
void LoadIdentity(void);
void LoadCameraToWorld(void);
void Ortho(double left, double top, double right, double bottom, double zNear, double zFar);
void PerspectiveX(double fovx, double aspect, double zNear, double zFar);
void PerspectiveOffCenterX(double fovx, double aspect, double zNear, double zFar, double bottom, double top, double left, double right);
void PickMatrix(int x, int y, int width, int height);
void Rotate(float angle, float x, float y, float z);
void Translate(float x, float y, float z);
void Scale(float x, float y, float z);
void ScaleXY(float x, float y);
// Binds a particular material to render with
void Bind(IMaterial* pMaterial);
IMaterialInternal* GetBoundMaterial();
// Level of anisotropic filtering
virtual void SetAnisotropicLevel(int nAnisotropyLevel);
virtual void SyncToken(const char* pToken);
// Cull mode
void CullMode(MaterialCullMode_t cullMode);
// Force writes only when z matches. . . useful for stenciling things out
// by rendering the desired Z values ahead of time.
void ForceDepthFuncEquals(bool bEnable);
// Turns off Z buffering
void OverrideDepthEnable(bool bEnable, bool bDepthEnable);
void OverrideAlphaWriteEnable(bool bOverrideEnable, bool bAlphaWriteEnable);
void OverrideColorWriteEnable(bool bOverrideEnable, bool bColorWriteEnable);
void SetHeightClipZ(float z);
void SetHeightClipMode(enum MaterialHeightClipMode_t heightClipMode);
void SetClipPlane(int index, const float* pPlane);
void EnableClipPlane(int index, bool bEnable);
void SetFastClipPlane(const float* pPlane);
void EnableFastClip(bool bEnable);
// The shade mode
void ShadeMode(ShaderShadeMode_t mode);
// Vertex blend state
void SetVertexBlendState(int numBones);
// Gets the dynamic mesh
IMesh* GetDynamicMesh(IMaterial* pMaterial, int nHWSkinBoneCount, bool buffered,
IMesh* pVertexOverride, IMesh* pIndexOverride);
IMesh* GetDynamicMeshEx(IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount,
bool bBuffered, IMesh* pVertexOverride, IMesh* pIndexOverride);
IMesh* GetFlexMesh();
// Returns the number of vertices we can render using the dynamic mesh
virtual void GetMaxToRender(IMesh* pMesh, bool bMaxUntilFlush, int* pMaxVerts, int* pMaxIndices);
virtual int GetMaxVerticesToRender(IMaterial* pMaterial);
virtual int GetMaxIndicesToRender();
// Draws the mesh
void DrawMesh(CMeshBase* mesh);
// modifies the vertex data when necessary
void ModifyVertexData();
// Draws
void BeginPass(StateSnapshot_t snapshot);
void RenderPass(int nPass, int nPassCount);
// We use smaller dynamic VBs during level transitions, to free up memory
virtual int GetCurrentDynamicVBSize(void);
virtual void DestroyVertexBuffers(bool bExitingLevel = false);
void SetVertexDecl(VertexFormat_t vertexFormat, bool bHasColorMesh, bool bUsingFlex, bool bUsingMorph);
// Sets the constant register for vertex and pixel shaders
FORCEINLINE void SetVertexShaderConstantInternal(int var, float const* pVec, int numVecs = 1, bool bForce = false);
void SetVertexShaderConstant(int var, float const* pVec, int numVecs = 1, bool bForce = false);
void SetBooleanVertexShaderConstant(int var, BOOL const* pVec, int numBools = 1, bool bForce = false);
void SetIntegerVertexShaderConstant(int var, int const* pVec, int numIntVecs = 1, bool bForce = false);
void SetPixelShaderConstant(int var, float const* pVec, int numVecs = 1, bool bForce = false);
FORCEINLINE void SetPixelShaderConstantInternal(int var, float const* pValues, int nNumConsts, bool bForce);
void SetBooleanPixelShaderConstant(int var, BOOL const* pVec, int numBools = 1, bool bForce = false);
void SetIntegerPixelShaderConstant(int var, int const* pVec, int numIntVecs = 1, bool bForce = false);
void InvalidateDelayedShaderConstants(void);
// Returns the nearest supported format
ImageFormat GetNearestSupportedFormat(ImageFormat fmt, bool bFilteringRequired = true) const;
ImageFormat GetNearestRenderTargetFormat(ImageFormat format) const;
virtual bool DoRenderTargetsNeedSeparateDepthBuffer() const;
// stuff that shouldn't be used from within a shader
void ModifyTexture(ShaderAPITextureHandle_t textureHandle);
void BindTexture(Sampler_t sampler, ShaderAPITextureHandle_t textureHandle);
virtual void BindVertexTexture(VertexTextureSampler_t nStage, ShaderAPITextureHandle_t textureHandle);
void DeleteTexture(ShaderAPITextureHandle_t textureHandle);
void WriteTextureToFile(ShaderAPITextureHandle_t hTexture, const char* szFileName);
bool IsTexture(ShaderAPITextureHandle_t textureHandle);
bool IsTextureResident(ShaderAPITextureHandle_t textureHandle);
FORCEINLINE bool TextureIsAllocated(ShaderAPITextureHandle_t hTexture)
{
return m_Textures.IsValidIndex(hTexture) && (GetTexture(hTexture).m_Flags & Texture_t::IS_ALLOCATED);
}
FORCEINLINE void AssertValidTextureHandle(ShaderAPITextureHandle_t textureHandle)
{
#ifdef _DEBUG
Assert(TextureIsAllocated(textureHandle));
#endif
}
// Lets the shader know about the full-screen texture so it can
virtual void SetFullScreenTextureHandle(ShaderAPITextureHandle_t h);
virtual void SetLinearToGammaConversionTextures(ShaderAPITextureHandle_t hSRGBWriteEnabledTexture, ShaderAPITextureHandle_t hIdentityTexture);
// Set the render target to a texID.
// Set to SHADER_RENDERTARGET_BACKBUFFER if you want to use the regular framebuffer.
void SetRenderTarget(ShaderAPITextureHandle_t colorTextureHandle = SHADER_RENDERTARGET_BACKBUFFER,
ShaderAPITextureHandle_t depthTextureHandle = SHADER_RENDERTARGET_DEPTHBUFFER);
// Set the render target to a texID.
// Set to SHADER_RENDERTARGET_BACKBUFFER if you want to use the regular framebuffer.
void SetRenderTargetEx(int nRenderTargetID, ShaderAPITextureHandle_t colorTextureHandle = SHADER_RENDERTARGET_BACKBUFFER,
ShaderAPITextureHandle_t depthTextureHandle = SHADER_RENDERTARGET_DEPTHBUFFER);
// These are bound to the texture, not the texture environment
void TexMinFilter(ShaderTexFilterMode_t texFilterMode);
void TexMagFilter(ShaderTexFilterMode_t texFilterMode);
void TexWrap(ShaderTexCoordComponent_t coord, ShaderTexWrapMode_t wrapMode);
void TexSetPriority(int priority);
void TexLodClamp(int finest);
void TexLodBias(float bias);
ShaderAPITextureHandle_t CreateTextureHandle(void);
void CreateTextureHandles(ShaderAPITextureHandle_t* handles, int count);
ShaderAPITextureHandle_t CreateTexture(
int width,
int height,
int depth,
ImageFormat dstImageFormat,
int numMipLevels,
int numCopies,
int creationFlags,
const char* pDebugName,
const char* pTextureGroupName);
// Create a multi-frame texture (equivalent to calling "CreateTexture" multiple times, but more efficient)
void CreateTextures(
ShaderAPITextureHandle_t* pHandles,
int count,
int width,
int height,
int depth,
ImageFormat dstImageFormat,
int numMipLevels,
int numCopies,
int flags,
const char* pDebugName,
const char* pTextureGroupName);
ShaderAPITextureHandle_t CreateDepthTexture(
ImageFormat renderTargetFormat,
int width,
int height,
const char* pDebugName,
bool bTexture);
void TexImage2D(
int level,
int cubeFaceID,
ImageFormat dstFormat,
int zOffset,
int width,
int height,
ImageFormat srcFormat,
bool bSrcIsTiled,
void* imageData);
void TexSubImage2D(
int level,
int cubeFaceID,
int xOffset,
int yOffset,
int zOffset,
int width,
int height,
ImageFormat srcFormat,
int srcStride,
bool bSrcIsTiled,
void* imageData);
void TexImageFromVTF(IVTFTexture* pVTF, int iVTFFrame);
bool TexLock(int level, int cubeFaceID, int xOffset, int yOffset, int width, int height, CPixelWriter& writer);
void TexUnlock();
// stuff that isn't to be used from within a shader
// what's the best way to hide this? subclassing?
virtual void ClearBuffersObeyStencil(bool bClearColor, bool bClearDepth);
virtual void ClearBuffersObeyStencilEx(bool bClearColor, bool bClearAlpha, bool bClearDepth);
virtual void PerformFullScreenStencilOperation(void);
void ReadPixels(int x, int y, int width, int height, unsigned char* data, ImageFormat dstFormat);
virtual void ReadPixels(Rect_t* pSrcRect, Rect_t* pDstRect, unsigned char* data, ImageFormat dstFormat, int nDstStride);
// Gets the current buffered state... (debug only)
void GetBufferedState(BufferedState_t& state);
// Buffered primitives
void FlushBufferedPrimitives();
void FlushBufferedPrimitivesInternal();
// Make sure we finish drawing everything that has been requested
void FlushHardware();
// Use this to begin and end the frame
void BeginFrame();
void EndFrame();
// Used to clear the transition table when we know it's become invalid.
void ClearSnapshots();
// Backward compat
virtual int GetActualTextureStageCount() const;
virtual int GetActualSamplerCount() const;
virtual int StencilBufferBits() const;
virtual bool IsAAEnabled() const; // Is antialiasing being used?
virtual bool OnAdapterSet();
bool m_bAdapterSet;
void UpdateFastClipUserClipPlane(void);
bool ReadPixelsFromFrontBuffer() const;
// returns the current time in seconds....
double CurrentTime() const;
// Get the current camera position in world space.
void GetWorldSpaceCameraPosition(float* pPos) const;
// Fog methods
void FogMode(MaterialFogMode_t fogMode);
void FogStart(float fStart);
void FogEnd(float fEnd);
void FogMaxDensity(float flMaxDensity);
void SetFogZ(float fogZ);
void GetFogDistances(float* fStart, float* fEnd, float* fFogZ);
void SceneFogMode(MaterialFogMode_t fogMode);
MaterialFogMode_t GetSceneFogMode();
MaterialFogMode_t GetPixelFogMode();
int GetPixelFogCombo();//0 is either range fog, or no fog simulated with rigged range fog values. 1 is height fog
bool ShouldUsePixelFogForMode(MaterialFogMode_t fogMode);
void SceneFogColor3ub(unsigned char r, unsigned char g, unsigned char b);
void GetSceneFogColor(unsigned char* rgb);
void GetSceneFogColor(unsigned char* r, unsigned char* g, unsigned char* b);
// Selection mode methods
int SelectionMode(bool selectionMode);
void SelectionBuffer(unsigned int* pBuffer, int size);
void ClearSelectionNames();
void LoadSelectionName(int name);
void PushSelectionName(int name);
void PopSelectionName();
bool IsInSelectionMode() const;
void RegisterSelectionHit(float minz, float maxz);
void WriteHitRecord();
// Binds a standard texture
virtual void BindStandardTexture(Sampler_t sampler, StandardTextureId_t id);
virtual void BindStandardVertexTexture(VertexTextureSampler_t sampler, StandardTextureId_t id);
virtual void GetStandardTextureDimensions(int* pWidth, int* pHeight, StandardTextureId_t id);
// Gets the lightmap dimensions
virtual void GetLightmapDimensions(int* w, int* h);
// Use this to get the mesh builder that allows us to modify vertex data
CMeshBuilder* GetVertexModifyBuilder();
virtual bool InFlashlightMode() const;
virtual bool InEditorMode() const;
// Gets the bound morph's vertex format; returns 0 if no morph is bound
virtual MorphFormat_t GetBoundMorphFormat();
// Helper to get at the texture state stage
TextureStageState_t& TextureStage(int stage) { return m_DynamicState.m_TextureStage[stage]; }
const TextureStageState_t& TextureStage(int stage) const { return m_DynamicState.m_TextureStage[stage]; }
SamplerState_t& SamplerState(int nSampler) { return m_DynamicState.m_SamplerState[nSampler]; }
const SamplerState_t& SamplerState(int nSampler) const { return m_DynamicState.m_SamplerState[nSampler]; }
void SetAmbientLight(float r, float g, float b);
void SetLight(int lightNum, const LightDesc_t& desc);
void SetLightingOrigin(Vector vLightingOrigin);
void DisableAllLocalLights();
void SetAmbientLightCube(Vector4D colors[6]);
float GetAmbientLightCubeLuminance(void);
int GetMaxLights(void) const;
const LightDesc_t& GetLight(int lightNum) const;
void SetVertexShaderStateAmbientLightCube();
void SetPixelShaderStateAmbientLightCube(int pshReg, bool bForceToBlack = false);
void CopyRenderTargetToTexture(ShaderAPITextureHandle_t textureHandle);
void CopyRenderTargetToTextureEx(ShaderAPITextureHandle_t textureHandle, int nRenderTargetID, Rect_t* pSrcRect = NULL, Rect_t* pDstRect = NULL);
void CopyTextureToRenderTargetEx(int nRenderTargetID, ShaderAPITextureHandle_t textureHandle, Rect_t* pSrcRect = NULL, Rect_t* pDstRect = NULL);
void CopyRenderTargetToScratchTexture(ShaderAPITextureHandle_t srcHandle, ShaderAPITextureHandle_t dstHandle, Rect_t* pSrcRect = NULL, Rect_t* pDstRect = NULL);
virtual void LockRect(void** pOutBits, int* pOutPitch, ShaderAPITextureHandle_t texHandle, int mipmap, int x, int y, int w, int h, bool bWrite, bool bRead);
virtual void UnlockRect(ShaderAPITextureHandle_t texHandle, int mipmap);
virtual void CopyTextureToTexture(ShaderAPITextureHandle_t srcTex, ShaderAPITextureHandle_t dstTex);
// Returns the cull mode (for fill rate computation)
D3DCULL GetCullMode() const;
void SetCullModeState(bool bEnable, D3DCULL nDesiredCullMode);
void ApplyCullEnable(bool bEnable);
// Alpha to coverage
void ApplyAlphaToCoverage(bool bEnable);
#if defined( _X360 )
void ApplySRGBReadState(int iTextureStage, bool bSRGBReadEnabled);
#endif
// Applies Z Bias
void ApplyZBias(const ShadowState_t& shaderState);
// Applies texture enable
void ApplyTextureEnable(const ShadowState_t& state, int stage);
void ApplyFogMode(ShaderFogMode_t fogMode, bool bSRGBWritesEnabled, bool bDisableFogGammaCorrection);
void UpdatePixelFogColorConstant(void);
void EnabledSRGBWrite(bool bEnabled);
// Gamma<->Linear conversions according to the video hardware we're running on
float GammaToLinear_HardwareSpecific(float fGamma) const;
float LinearToGamma_HardwareSpecific(float fLinear) const;
// Applies alpha blending
void ApplyAlphaBlend(bool bEnable, D3DBLEND srcBlend, D3DBLEND destBlend);
// Applies alpha texture op
void ApplyColorTextureStage(int stage, D3DTEXTUREOP op, int arg1, int arg2);
void ApplyAlphaTextureStage(int stage, D3DTEXTUREOP op, int arg1, int arg2);
// Sets texture stage stage + render stage state
void SetSamplerState(int stage, D3DSAMPLERSTATETYPE state, DWORD val);
void SetTextureStageState(int stage, D3DTEXTURESTAGESTATETYPE state, DWORD val);
void SetRenderStateForce(D3DRENDERSTATETYPE state, DWORD val);
void SetRenderState(D3DRENDERSTATETYPE state, DWORD val,
bool bFlushBufferedPrimitivesIfChanged = false);
// Scissor Rect
void SetScissorRect(const int nLeft, const int nTop, const int nRight, const int nBottom, const bool bEnableScissor);
// Can we download textures?
virtual bool CanDownloadTextures() const;
void ForceHardwareSync_WithManagedTexture();
void ForceHardwareSync(void);
void UpdateFrameSyncQuery(int queryIndex, bool bIssue);
void EvictManagedResources();
virtual void EvictManagedResourcesInternal();
// Gets at a particular transform
inline D3DXMATRIX& GetTransform(int i)
{
return *m_pMatrixStack[i]->GetTop();
}
int GetCurrentNumBones(void) const;
bool IsHWMorphingEnabled() const;
int GetCurrentLightCombo(void) const; // Used for DX8 only
void GetDX9LightState(LightState_t* state) const; // Used for DX9 only
MaterialFogMode_t GetCurrentFogType(void) const;
void RecordString(const char* pStr);
virtual bool IsRenderingMesh() const { return m_pRenderMesh != 0; }
void SetTextureTransformDimension(TextureStage_t textureStage, int dimension, bool projected);
void DisableTextureTransform(TextureStage_t textureMatrix);
void SetBumpEnvMatrix(TextureStage_t textureStage, float m00, float m01, float m10, float m11);
int GetCurrentFrameCounter(void) const
{
return m_CurrentFrame;
}
// Workaround hack for visualization of selection mode
virtual void SetupSelectionModeVisualizationState();
// Allocate and delete query objects.
virtual ShaderAPIOcclusionQuery_t CreateOcclusionQueryObject(void);
virtual void DestroyOcclusionQueryObject(ShaderAPIOcclusionQuery_t h);
// Bracket drawing with begin and end so that we can get counts next frame.
virtual void BeginOcclusionQueryDrawing(ShaderAPIOcclusionQuery_t h);
virtual void EndOcclusionQueryDrawing(ShaderAPIOcclusionQuery_t h);
// Get the number of pixels rendered between begin and end on an earlier frame.
// Calling this in the same frame is a huge perf hit!
virtual int OcclusionQuery_GetNumPixelsRendered(ShaderAPIOcclusionQuery_t h, bool bFlush);
void SetFlashlightState(const FlashlightState_t& state, const VMatrix& worldToTexture);
void SetFlashlightStateEx(const FlashlightState_t& state, const VMatrix& worldToTexture, ITexture* pFlashlightDepthTexture);
const FlashlightState_t& GetFlashlightState(VMatrix& worldToTexture) const;
const FlashlightState_t& GetFlashlightStateEx(VMatrix& worldToTexture, ITexture** pFlashlightDepthTexture) const;
// Gets at the shadow state for a particular state snapshot
virtual bool IsDepthWriteEnabled(StateSnapshot_t id) const;
// IDebugTextureInfo implementation.
virtual bool IsDebugTextureListFresh(int numFramesAllowed = 1);
virtual void EnableDebugTextureList(bool bEnable);
virtual bool SetDebugTextureRendering(bool bEnable);
virtual void EnableGetAllTextures(bool bEnable);
virtual KeyValues* GetDebugTextureList();
virtual int GetTextureMemoryUsed(TextureMemoryType eTextureMemory);
virtual void ClearVertexAndPixelShaderRefCounts();
virtual void PurgeUnusedVertexAndPixelShaders();
// Called when the dx support level has changed
virtual void DXSupportLevelChanged();
// User clip plane override
virtual void EnableUserClipTransformOverride(bool bEnable);
virtual void UserClipTransform(const VMatrix& worldToProjection);
bool UsingSoftwareVertexProcessing() const;
// Mark all user clip planes as being dirty
void MarkAllUserClipPlanesDirty();
// Converts a D3DXMatrix to a VMatrix and back
void D3DXMatrixToVMatrix(const D3DXMATRIX& in, VMatrix& out);
void VMatrixToD3DXMatrix(const VMatrix& in, D3DXMATRIX& out);
ITexture* GetRenderTargetEx(int nRenderTargetID);
virtual void SetToneMappingScaleLinear(const Vector& scale);
virtual const Vector& GetToneMappingScaleLinear(void) const;
float GetLightMapScaleFactor(void) const;
void SetFloatRenderingParameter(int parm_number, float value);
void SetIntRenderingParameter(int parm_number, int value);
void SetVectorRenderingParameter(int parm_number, Vector const& value);
float GetFloatRenderingParameter(int parm_number) const;
int GetIntRenderingParameter(int parm_number) const;
Vector GetVectorRenderingParameter(int parm_number) const;
// For dealing with device lost in cases where Present isn't called all the time (Hammer)
virtual void HandleDeviceLost();
virtual void EnableLinearColorSpaceFrameBuffer(bool bEnable);
virtual void SetPSNearAndFarZ(int pshReg);
// stencil methods
void SetStencilEnable(bool onoff);
void SetStencilFailOperation(StencilOperation_t op);
void SetStencilZFailOperation(StencilOperation_t op);
void SetStencilPassOperation(StencilOperation_t op);
void SetStencilCompareFunction(StencilComparisonFunction_t cmpfn);
void SetStencilReferenceValue(int ref);
void SetStencilTestMask(uint32 msk);
void SetStencilWriteMask(uint32 msk);
void ClearStencilBufferRectangle(int xmin, int ymin, int xmax, int ymax, int value);
virtual void GetDXLevelDefaults(uint& max_dxlevel, uint& recommended_dxlevel);
#if defined( _X360 )
HXUIFONT OpenTrueTypeFont(const char* pFontname, int tall, int style);
void CloseTrueTypeFont(HXUIFONT hFont);
bool GetTrueTypeFontMetrics(HXUIFONT hFont, XUIFontMetrics* pFontMetrics, XUICharMetrics charMetrics[256]);
// Render a sequence of characters and extract the data into a buffer
// For each character, provide the width+height of the font texture subrect,
// an offset to apply when rendering the glyph, and an offset into a buffer to receive the RGBA data
bool GetTrueTypeGlyphs(HXUIFONT hFont, int numChars, wchar_t* pWch, int* pOffsetX, int* pOffsetY, int* pWidth, int* pHeight, unsigned char* pRGBA, int* pRGBAOffset);
ShaderAPITextureHandle_t CreateRenderTargetSurface(int width, int height, ImageFormat format, const char* pDebugName, const char* pTextureGroupName);
void PersistDisplay();
bool PostQueuedTexture(const void* pData, int nSize, ShaderAPITextureHandle_t* pHandles, int nHandles, int nWidth, int nHeight, int nDepth, int nMips, int* pRefCount);
void* GetD3DDevice();
void PushVertexShaderGPRAllocation(int iVertexShaderCount = 64);
void PopVertexShaderGPRAllocation(void);
void EnableVSync_360(bool bEnable);
#endif
virtual bool OwnGPUResources(bool bEnable);
// ------------ New Vertex/Index Buffer interface ----------------------------
void BindVertexBuffer(int streamID, IVertexBuffer* pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions = 1);
void BindIndexBuffer(IIndexBuffer* pIndexBuffer, int nOffsetInBytes);
void Draw(MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount);
// Draw the mesh with the currently bound vertex and index buffers.
void DrawWithVertexAndIndexBuffers(void);
// ------------ End ----------------------------
// deformations
virtual void PushDeformation(const DeformationBase_t* pDeformation);
virtual void PopDeformation();
virtual int GetNumActiveDeformations() const;
// for shaders to set vertex shader constants. returns a packed state which can be used to set the dynamic combo
virtual int GetPackedDeformationInformation(int nMaskOfUnderstoodDeformations,
float* pConstantValuesOut,
int nBufferSize,
int nMaximumDeformations,
int* pNumDefsOut) const;
inline Texture_t& GetTexture(ShaderAPITextureHandle_t hTexture)
{
return m_Textures[hTexture];
}
// Gets the texture
IDirect3DBaseTexture* GetD3DTexture(ShaderAPITextureHandle_t hTexture);
virtual bool ShouldWriteDepthToDestAlpha(void) const;
virtual void AcquireThreadOwnership();
virtual void ReleaseThreadOwnership();
private:
enum
{
SMALL_BACK_BUFFER_SURFACE_WIDTH = 256,
SMALL_BACK_BUFFER_SURFACE_HEIGHT = 256,
};
bool m_bEnableDebugTextureList;
bool m_bDebugGetAllTextures;
bool m_bDebugTexturesRendering;
KeyValues* m_pDebugTextureList;
int m_nTextureMemoryUsedLastFrame, m_nTextureMemoryUsedTotal;
int m_nTextureMemoryUsedPicMip1, m_nTextureMemoryUsedPicMip2;
int m_nDebugDataExportFrame;
FlashlightState_t m_FlashlightState;
VMatrix m_FlashlightWorldToTexture;
ITexture* m_pFlashlightDepthTexture;
CShaderAPIDx8(CShaderAPIDx8 const&);
enum
{
INVALID_TRANSITION_OP = 0xFFFF
};
// State transition table for the device is as follows:
// Other app init causes transition from OK to OtherAppInit, during transition we must release resources
// !Other app init causes transition from OtherAppInit to OK, during transition we must restore resources
// Minimized or device lost or device not reset causes transition from OK to LOST_DEVICE, during transition we must release resources
// Minimized or device lost or device not reset causes transition from OtherAppInit to LOST_DEVICE
// !minimized AND !device lost causes transition from LOST_DEVICE to NEEDS_RESET
// minimized or device lost causes transition from NEEDS_RESET to LOST_DEVICE
// Successful TryDeviceReset and !Other app init causes transition from NEEDS_RESET to OK, during transition we must restore resources
// Successful TryDeviceReset and Other app init causes transition from NEEDS_RESET to OtherAppInit
void ExportTextureList();
void AddBufferToTextureList(const char* pName, D3DSURFACE_DESC& desc);
void SetupTextureGroup(ShaderAPITextureHandle_t hTexture, const char* pTextureGroupName);
// Creates the matrix stack
void CreateMatrixStacks();
// Initializes the render state
void InitRenderState();
// Resets all dx renderstates to dx default so that our shadows are correct.
void ResetDXRenderState();
// Resets the render state
void ResetRenderState(bool bFullReset = true);
// Setup standard vertex shader constants (that don't change)
void SetStandardVertexShaderConstants(float fOverbright);
// Initializes vertex and pixel shaders
void InitVertexAndPixelShaders();
// Discards the vertex and index buffers
void DiscardVertexBuffers();
// Computes the fill rate
void ComputeFillRate();
// Takes a snapshot
virtual StateSnapshot_t TakeSnapshot();
// Converts the clear color to be appropriate for HDR
D3DCOLOR GetActualClearColor(D3DCOLOR clearColor);
// We lost the device
void OnDeviceLost();
// Gets the matrix stack from the matrix mode
int GetMatrixStack(MaterialMatrixMode_t mode) const;
// Flushes the matrix state, returns false if we don't need to
// do any more work
bool MatrixIsChanging(TransformType_t transform = TRANSFORM_IS_GENERAL);
// Updates the matrix transform state
void UpdateMatrixTransform(TransformType_t transform = TRANSFORM_IS_GENERAL);
// Sets the vertex shader modelView state..
// NOTE: GetProjectionMatrix should only be called from the Commit functions!
const D3DXMATRIX& GetProjectionMatrix(void);
void SetVertexShaderViewProj();
void SetVertexShaderModelViewProjAndModelView();
void SetPixelShaderFogParams(int reg);
void SetPixelShaderFogParams(int reg, ShaderFogMode_t fogMode);
FORCEINLINE void UpdateVertexShaderFogParams(void)
{
if (g_pHardwareConfig->Caps().m_SupportsPixelShaders)
{
float ooFogRange = 1.0f;
float fStart = m_VertexShaderFogParams[0];
float fEnd = m_VertexShaderFogParams[1];
// Check for divide by zero
if (fEnd != fStart)
{
ooFogRange = 1.0f / (fEnd - fStart);
}
float fogParams[4];
fogParams[0] = ooFogRange * fEnd;
fogParams[1] = 1.0f;
fogParams[2] = 1.0f - clamp(m_flFogMaxDensity, 0.0f, 1.0f); // Max fog density
fogParams[3] = ooFogRange;
float vertexShaderCameraPos[4];
vertexShaderCameraPos[0] = m_WorldSpaceCameraPositon[0];
vertexShaderCameraPos[1] = m_WorldSpaceCameraPositon[1];
vertexShaderCameraPos[2] = m_WorldSpaceCameraPositon[2];
vertexShaderCameraPos[3] = m_DynamicState.m_FogZ; // waterheight
// cFogEndOverFogRange, cFogOne, unused, cOOFogRange
SetVertexShaderConstant(VERTEX_SHADER_FOG_PARAMS, fogParams, 1);
// eyepos.x eyepos.y eyepos.z cWaterZ
SetVertexShaderConstant(VERTEX_SHADER_CAMERA_POS, vertexShaderCameraPos);
}
}
// Compute stats info for a texture
void ComputeStatsInfo(ShaderAPITextureHandle_t hTexture, bool isCubeMap, bool isVolumeTexture);
// For procedural textures
void AdvanceCurrentCopy(ShaderAPITextureHandle_t hTexture);
// Deletes a D3D texture
void DeleteD3DTexture(ShaderAPITextureHandle_t hTexture);
// Unbinds a texture
void UnbindTexture(ShaderAPITextureHandle_t hTexture);
// Releases all D3D textures
void ReleaseAllTextures();
// Deletes all textures
void DeleteAllTextures();
// Gets the currently modified texture handle
ShaderAPITextureHandle_t GetModifyTextureHandle() const;
// Gets the bind id
ShaderAPITextureHandle_t GetBoundTextureBindId(Sampler_t sampler) const;
// If mat_texture_limit is enabled, then this tells us if binding the specified texture would
// take us over the limit.
bool WouldBeOverTextureLimit(ShaderAPITextureHandle_t hTexture);
// Sets the texture state
void SetTextureState(Sampler_t sampler, ShaderAPITextureHandle_t hTexture, bool force = false);
// Grab/release the internal render targets such as the back buffer and the save game thumbnail
void AcquireInternalRenderTargets();
void ReleaseInternalRenderTargets();
// create/release linear->gamma table texture lookups. Only used by hardware supporting pixel shader 2b and up
void AcquireLinearToGammaTableTextures();
void ReleaseLinearToGammaTableTextures();
// Gets the texture being modified
IDirect3DBaseTexture* GetModifyTexture();
void SetModifyTexture(IDirect3DBaseTexture* pTex);
// returns true if we're using texture coordinates at a given stage
bool IsUsingTextureCoordinates(int stage, int flags) const;
// Returns true if the board thinks we're generating spheremap coordinates
bool IsSpheremapRenderStateActive(int stage) const;
// Returns true if we're modulating constant color into the vertex color
bool IsModulatingVertexColor() const;
// Recomputes ambient light cube
void RecomputeAmbientLightCube();
// Debugging spew
void SpewBoardState();
// Compute and save the world space camera position.
void CacheWorldSpaceCameraPosition();
// Compute and save the projection atrix with polyoffset built in if we need it.
void CachePolyOffsetProjectionMatrix();
// Vertex shader helper functions
int FindVertexShader(VertexFormat_t fmt, char const* pFileName) const;
int FindPixelShader(char const* pFileName) const;
// Returns copies of the front and back buffers
IDirect3DSurface* GetFrontBufferImage(ImageFormat& format);
IDirect3DSurface* GetBackBufferImage(Rect_t* pSrcRect, Rect_t* pDstRect, ImageFormat& format);
IDirect3DSurface* GetBackBufferImageHDR(Rect_t* pSrcRect, Rect_t* pDstRect, ImageFormat& format);
// Copy bits from a host-memory surface
void CopyBitsFromHostSurface(IDirect3DSurface* pSurfaceBits,
const Rect_t& dstRect, unsigned char* pData, ImageFormat srcFormat, ImageFormat dstFormat, int nDstStride);
FORCEINLINE void SetTransform(D3DTRANSFORMSTATETYPE State, CONST D3DXMATRIX* pMatrix)
{
#if !defined( _X360 )
Dx9Device()->SetTransform(State, pMatrix);
#endif
}
FORCEINLINE void SetLight(DWORD Index, CONST D3DLIGHT9* pLight)
{
#if !defined( _X360 )
Dx9Device()->SetLight(Index, pLight);
#endif
}
FORCEINLINE void LightEnable(DWORD LightIndex, bool bEnable)
{
#if !defined( _X360 )
Dx9Device()->LightEnable(LightIndex, bEnable);
#endif
}
void ExecuteCommandBuffer(uint8* pCmdBuffer);
void SetStandardTextureHandle(StandardTextureId_t nId, ShaderAPITextureHandle_t);
// Methods related to queuing functions to be called per-(pMesh->Draw call) or per-pass
void ClearAllCommitFuncs(CommitFuncType_t func, CommitShaderType_t shader);
void CallCommitFuncs(CommitFuncType_t func, CommitShaderType_t shader, bool bForce);
bool IsCommitFuncInUse(CommitFuncType_t func, CommitShaderType_t shader, int nFunc) const;
void MarkCommitFuncInUse(CommitFuncType_t func, CommitShaderType_t shader, int nFunc);
void AddCommitFunc(CommitFuncType_t func, CommitShaderType_t shader, StateCommitFunc_t f);
void CallCommitFuncs(CommitFuncType_t func, bool bUsingFixedFunction, bool bForce = false);
// Commits transforms and lighting
void CommitStateChanges();
// Commits transforms that have to be dealt with on a per pass basis (ie. projection matrix for polyoffset)
void CommitPerPassStateChanges(StateSnapshot_t id);
// Need to handle fog mode on a per-pass basis
void CommitPerPassFogMode(bool bUsingVertexAndPixelShaders);
void CommitPerPassXboxFixups();
// Commits user clip planes
void CommitUserClipPlanes(bool bUsingFixedFunction);
// Gets the user clip transform (world->view)
D3DXMATRIX& GetUserClipTransform();
// transform commit
bool VertexShaderTransformChanged(int i);
bool FixedFunctionTransformChanged(int i);
void UpdateVertexShaderMatrix(int iMatrix);
void SetVertexShaderStateSkinningMatrices();
void CommitVertexShaderTransforms();
void CommitPerPassVertexShaderTransforms();
void UpdateFixedFunctionMatrix(int iMatrix);
void SetFixedFunctionStateSkinningMatrices();
void CommitFixedFunctionTransforms();
void CommitPerPassFixedFunctionTransforms();
// Recomputes the fast-clip plane matrices based on the current fast-clip plane
void CommitFastClipPlane();
// Computes a matrix which includes the poly offset given an initial projection matrix
void ComputePolyOffsetMatrix(const D3DXMATRIX& matProjection, D3DXMATRIX& matProjectionOffset);
void SetSkinningMatrices();
// lighting commit
bool VertexShaderLightingChanged(int i);
bool VertexShaderLightingEnableChanged(int i);
bool FixedFunctionLightingChanged(int i);
bool FixedFunctionLightingEnableChanged(int i);
VertexShaderLightTypes_t ComputeLightType(int i) const;
void SortLights(int* index);
void CommitVertexShaderLighting();
void CommitPixelShaderLighting(int pshReg);
void CommitFixedFunctionLighting();
// Gets the surface associated with a texture (refcount of surface is increased)
IDirect3DSurface* GetTextureSurface(ShaderAPITextureHandle_t textureHandle);
IDirect3DSurface* GetDepthTextureSurface(ShaderAPITextureHandle_t textureHandle);
//
// Methods related to hardware config
//
void SetDefaultConfigValuesForDxLevel(int dxLevelFromCaps, ShaderDeviceInfo_t& info, unsigned int nFlagsUsed);
// Determines hardware capabilities
bool DetermineHardwareCaps();
// Alpha To Coverage entrypoints and states - much of this involves vendor-dependent paths and states...
bool CheckVendorDependentAlphaToCoverage();
void EnableAlphaToCoverage();
void DisableAlphaToCoverage();
// Vendor-dependent shadow mapping detection
void CheckVendorDependentShadowMappingSupport(bool& bSupportsShadowDepthTextures, bool& bSupportsFetch4);
// Override caps based on a requested dx level
void OverrideCaps(int nForcedDXLevel);
// Reports support for a given MSAA mode
bool SupportsMSAAMode(int nMSAAMode);
// Reports support for a given CSAA mode
bool SupportsCSAAMode(int nNumSamples, int nQualityLevel);
// Gamma correction of fog color, or not...
D3DCOLOR ComputeGammaCorrectedFogColor(unsigned char r, unsigned char g, unsigned char b, bool bSRGBWritesEnabled);
void SetDefaultMaterial();
bool RestorePersistedDisplay(bool bUseFrontBuffer);
void ClearStdTextureHandles(void);
// debug logging
void PrintfVA(char* fmt, va_list vargs);
void Printf(const char* fmt, ...);
float Knob(char* knobname, float* setvalue = NULL);
// "normal" back buffer and depth buffer. Need to keep this around so that we
// know what to set the render target to when we are done rendering to a texture.
IDirect3DSurface* m_pBackBufferSurface;
IDirect3DSurface* m_pBackBufferSurfaceSRGB;
IDirect3DSurface* m_pZBufferSurface;
// Optimization for screenshots
IDirect3DSurface* m_pSmallBackBufferFP16TempSurface;
ShaderAPITextureHandle_t m_hFullScreenTexture;
ShaderAPITextureHandle_t m_hLinearToGammaTableTexture;
ShaderAPITextureHandle_t m_hLinearToGammaTableIdentityTexture;
//
// State needed at the time of rendering (after snapshots have been applied)
//
// Interface for the D3DXMatrixStack
ID3DXMatrixStack* m_pMatrixStack[NUM_MATRIX_MODES];
matrix3x4_t m_boneMatrix[NUM_MODEL_TRANSFORMS];
int m_maxBoneLoaded;
// Current matrix mode
D3DTRANSFORMSTATETYPE m_MatrixMode;
int m_CurrStack;
// The current camera position in world space.
Vector4D m_WorldSpaceCameraPositon;
// The current projection matrix with polyoffset baked into it.
D3DXMATRIX m_CachedPolyOffsetProjectionMatrix;
D3DXMATRIX m_CachedFastClipProjectionMatrix;
D3DXMATRIX m_CachedFastClipPolyOffsetProjectionMatrix;
// The texture stage state that changes frequently
DynamicState_t m_DynamicState;
// The *desired* dynamic state. Most dynamic state is committed into actual hardware state
// at either per-pass or per-material time. This can also be used to force the hardware
// to match the desired state after returning from alt-tab.
DynamicState_t m_DesiredState;
// A list of state commit functions to run as per-draw call commit time
unsigned char m_pCommitFlags[COMMIT_FUNC_TYPE_COUNT][COMMIT_SHADER_TYPE_COUNT][COMMIT_FUNC_BYTE_COUNT];
CUtlVector< StateCommitFunc_t > m_CommitFuncs[COMMIT_FUNC_TYPE_COUNT][COMMIT_SHADER_TYPE_COUNT];
// Render data
CMeshBase* m_pRenderMesh;
int m_nDynamicVBSize;
IMaterialInternal* m_pMaterial;
// Shadow depth bias states
float m_fShadowSlopeScaleDepthBias;
float m_fShadowDepthBias;
bool m_bReadPixelsEnabled;
// Render-to-texture stuff...
bool m_UsingTextureRenderTarget;
int m_ViewportMaxWidth;
int m_ViewportMaxHeight;
ShaderAPITextureHandle_t m_hCachedRenderTarget;
bool m_bUsingSRGBRenderTarget;
// Ambient cube map ok?
int m_CachedAmbientLightCube;
// The current frame
int m_CurrentFrame;
// The texture we're currently modifying
ShaderAPITextureHandle_t m_ModifyTextureHandle;
char m_ModifyTextureLockedLevel;
unsigned char m_ModifyTextureLockedFace;
// Stores all textures
CUtlFixedLinkedList< Texture_t > m_Textures;
// Mesh builder used to modify vertex data
CMeshBuilder m_ModifyBuilder;
float m_VertexShaderFogParams[2];
float m_flFogMaxDensity;
// Shadow state transition table
CTransitionTable m_TransitionTable;
StateSnapshot_t m_nCurrentSnapshot;
// Depth test override...
bool m_bOverrideMaterialIgnoreZ;
bool m_bIgnoreZValue;
// Are we in the middle of resetting the render state?
bool m_bResettingRenderState;
// Can we buffer 2 frames ahead?
bool m_bBuffer2FramesAhead;
// Selection name stack
CUtlStack< int > m_SelectionNames;
bool m_InSelectionMode;
unsigned int* m_pSelectionBufferEnd;
unsigned int* m_pSelectionBuffer;
unsigned int* m_pCurrSelectionRecord;
float m_SelectionMinZ;
float m_SelectionMaxZ;
int m_NumHits;
// fog
unsigned char m_SceneFogColor[3];
MaterialFogMode_t m_SceneFogMode;
// Tone Mapping state ( w is gamma scale )
Vector4D m_ToneMappingScale;
Deformation_t m_DeformationStack[DEFORMATION_STACK_DEPTH];
Deformation_t* m_pDeformationStackPtr;
void WriteShaderConstantsToGPU();
// rendering parameter storage
int IntRenderingParameters[MAX_INT_RENDER_PARMS];
float FloatRenderingParameters[MAX_FLOAT_RENDER_PARMS];
Vector VectorRenderingParameters[MAX_VECTOR_RENDER_PARMS];
ShaderAPITextureHandle_t m_StdTextureHandles[TEXTURE_MAX_STD_TEXTURES];
// PIX instrumentation utlities...enable these with PIX_INSTRUMENTATION
void StartPIXInstrumentation();
void EndPIXInstrumentation();
void SetPIXMarker(unsigned long color, const char* szName);
void IncrementPIXError();
bool PIXError();
int m_nPIXErrorCount;
int m_nPixFrame;
bool m_bPixCapturing;
void ComputeVertexDescription(unsigned char* pBuffer, VertexFormat_t vertexFormat, MeshDesc_t& desc) const
{
return MeshMgr()->ComputeVertexDescription(pBuffer, vertexFormat, desc);
}
// Reports support for shadow depth texturing
bool SupportsShadowDepthTextures(void);
bool SupportsFetch4(void);
void SetShadowDepthBiasFactors(float fShadowSlopeScaleDepthBias, float fShadowDepthBias);
// Vendor-dependent depth stencil texture format
ImageFormat GetShadowDepthTextureFormat(void);
bool SupportsNormalMapCompression(void) const;
bool SupportsBorderColor(void) const;
bool SupportsFetch4(void) const;
void EnableBuffer2FramesAhead(bool bEnable);
void SetDepthFeatheringPixelShaderConstant(int iConstant, float fDepthBlendScale);
void SetDisallowAccess(bool b)
{
g_bShaderAccessDisallowed = b;
}
void EnableShaderShaderMutex(bool b)
{
Assert(g_ShaderMutex.GetOwnerId() == 0);
g_bUseShaderMutex = b;
}
void ShaderLock()
{
g_ShaderMutex.Lock();
}
void ShaderUnlock()
{
g_ShaderMutex.Unlock();
}
// Vendor-dependent slim texture format
ImageFormat GetNullTextureFormat(void);
//The idea behind a delayed constant is this.
// Some shaders set constants based on rendering states, and some rendering states aren't updated until after a shader's already called Draw().
// So, for some functions that are state based, we save the constant we set and if the state changes between when it's set in the shader setup code
// and when the shader is drawn, we update that constant.
struct DelayedConstants_t
{
int iPixelShaderFogParams;
void Invalidate(void)
{
iPixelShaderFogParams = -1;
}
DelayedConstants_t(void) { this->Invalidate(); }
};
DelayedConstants_t m_DelayedShaderConstants;
bool SetRenderTargetInternalXbox(ShaderAPITextureHandle_t hTexture, bool bForce = false);
#if defined( _X360 )
CUtlStack<int> m_VertexShaderGPRAllocationStack;
#endif
int m_MaxVectorVertexShaderConstant;
int m_MaxBooleanVertexShaderConstant;
int m_MaxIntegerVertexShaderConstant;
int m_MaxVectorPixelShaderConstant;
int m_MaxBooleanPixelShaderConstant;
int m_MaxIntegerPixelShaderConstant;
bool m_bGPUOwned;
bool m_bResetRenderStateNeeded;
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
bool m_NullDevice;
#endif
};
//-----------------------------------------------------------------------------
// Class Factory
//-----------------------------------------------------------------------------
static CShaderAPIDx8 g_ShaderAPIDX8;
IShaderAPIDX8* g_pShaderAPIDX8 = &g_ShaderAPIDX8;
CShaderDeviceDx8* g_pShaderDeviceDx8 = &g_ShaderAPIDX8;
// FIXME: Remove IShaderAPI + IShaderDevice; they change after SetMode
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CShaderAPIDx8, IShaderAPI,
SHADERAPI_INTERFACE_VERSION, g_ShaderAPIDX8)
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CShaderAPIDx8, IShaderDevice,
SHADER_DEVICE_INTERFACE_VERSION, g_ShaderAPIDX8)
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CShaderAPIDx8, IDebugTextureInfo,
DEBUG_TEXTURE_INFO_VERSION, g_ShaderAPIDX8)
//-----------------------------------------------------------------------------
// Accessors for major interfaces
//-----------------------------------------------------------------------------
// Pix wants a max of 32 characters
// We'll give it the right-most substrings separated by slashes
static char s_pPIXMaterialName[32];
void PIXifyName(char* pDst, int destSize, const char* pSrc)
{
char* pSrcWalk = (char*)pSrc;
while (V_strlen(pSrcWalk) > 31) // While we still have too many characters
{
char* pTok = strpbrk(pSrcWalk, "/\\"); // Find next token
if (pTok)
pSrcWalk = pTok + 1;
else
break;
}
V_strncpy(pDst, pSrcWalk, min(32, destSize));
}
static int AdjustUpdateRange(float const* pVec, void const* pOut, int numVecs, int* pSkip)
{
int skip = 0;
uint32* pSrc = (uint32*)pVec;
uint32* pDst = (uint32*)pOut;
while (numVecs && !((pSrc[0] ^ pDst[0]) | (pSrc[1] ^ pDst[1]) | (pSrc[2] ^ pDst[2]) | (pSrc[3] ^ pDst[3])))
{
pSrc += 4;
pDst += 4;
numVecs--;
skip++;
}
*pSkip = skip;
if (!numVecs)
return 0;
uint32* pSrcLast = pSrc + numVecs * 4 - 4;
uint32* pDstLast = pDst + numVecs * 4 - 4;
while (numVecs > 1 && !((pSrcLast[0] ^ pDstLast[0]) | (pSrcLast[1] ^ pDstLast[1]) | (pSrcLast[2] ^ pDstLast[2]) | (pSrcLast[3] ^ pDstLast[3])))
{
pSrcLast -= 4;
pDstLast -= 4;
numVecs--;
}
return numVecs;
}
//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CShaderAPIDx8::CShaderAPIDx8() :
m_Textures(32),
m_CurrStack(-1),
m_ModifyTextureHandle(INVALID_SHADERAPI_TEXTURE_HANDLE),
m_pRenderMesh(0),
m_nDynamicVBSize(DYNAMIC_VERTEX_BUFFER_MEMORY),
m_pMaterial(NULL),
m_CurrentFrame(0),
m_CachedAmbientLightCube(STATE_CHANGED),
m_InSelectionMode(false),
m_SelectionMinZ(FLT_MAX),
m_SelectionMaxZ(FLT_MIN),
m_pSelectionBuffer(0),
m_pSelectionBufferEnd(0),
m_bResetRenderStateNeeded(false),
m_ModifyTextureLockedLevel(-1),
m_nPixFrame(0),
m_bPixCapturing(false),
m_nPIXErrorCount(0),
m_pBackBufferSurface(0),
m_pBackBufferSurfaceSRGB(0),
m_pZBufferSurface(0),
m_bResettingRenderState(false),
m_bReadPixelsEnabled(false),
m_ToneMappingScale(1.0f, 1.0f, 1.0f, 1.0f),
m_hFullScreenTexture(INVALID_SHADERAPI_TEXTURE_HANDLE),
m_hLinearToGammaTableTexture(INVALID_SHADERAPI_TEXTURE_HANDLE),
m_hLinearToGammaTableIdentityTexture(INVALID_SHADERAPI_TEXTURE_HANDLE),
m_fShadowSlopeScaleDepthBias(16.0f),
m_fShadowDepthBias(0.00008f),
m_hCachedRenderTarget(INVALID_SHADERAPI_TEXTURE_HANDLE),
m_bUsingSRGBRenderTarget(false)
{
// FIXME: Remove! Backward compat
m_bAdapterSet = false;
m_bBuffer2FramesAhead = false;
m_bReadPixelsEnabled = true;
memset(m_pMatrixStack, 0, sizeof(ID3DXMatrixStack*) * NUM_MATRIX_MODES);
memset(&m_DynamicState, 0, sizeof(m_DynamicState));
//m_DynamicState.m_HeightClipMode = MATERIAL_HEIGHTCLIPMODE_DISABLE;
m_nWindowHeight = m_nWindowWidth = 0;
m_maxBoneLoaded = 0;
m_bEnableDebugTextureList = 0;
m_bDebugTexturesRendering = 0;
m_pDebugTextureList = NULL;
m_nTextureMemoryUsedLastFrame = 0;
m_nTextureMemoryUsedTotal = 0;
m_nTextureMemoryUsedPicMip1 = 0;
m_nTextureMemoryUsedPicMip2 = 0;
m_nDebugDataExportFrame = 0;
m_SceneFogColor[0] = 0;
m_SceneFogColor[1] = 0;
m_SceneFogColor[2] = 0;
m_SceneFogMode = MATERIAL_FOG_NONE;
// We haven't yet detected whether we support CreateQuery or not yet.
memset(IntRenderingParameters, 0, sizeof(IntRenderingParameters));
memset(FloatRenderingParameters, 0, sizeof(FloatRenderingParameters));
memset(VectorRenderingParameters, 0, sizeof(VectorRenderingParameters));
m_pDeformationStackPtr = m_DeformationStack + DEFORMATION_STACK_DEPTH;
m_bGPUOwned = false;
m_MaxVectorVertexShaderConstant = 0;
m_MaxBooleanVertexShaderConstant = 0;
m_MaxIntegerVertexShaderConstant = 0;
m_MaxVectorPixelShaderConstant = 0;
m_MaxBooleanPixelShaderConstant = 0;
m_MaxIntegerPixelShaderConstant = 0;
ClearStdTextureHandles();
//Debugger();
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
m_NullDevice = !!CommandLine()->FindParm("-nulldevice");
#endif
}
CShaderAPIDx8::~CShaderAPIDx8()
{
if (m_DynamicState.m_pVectorVertexShaderConstant)
{
delete[] m_DynamicState.m_pVectorVertexShaderConstant;
m_DynamicState.m_pVectorVertexShaderConstant = NULL;
}
if (m_DynamicState.m_pBooleanVertexShaderConstant)
{
delete[] m_DynamicState.m_pBooleanVertexShaderConstant;
m_DynamicState.m_pBooleanVertexShaderConstant = NULL;
}
if (m_DynamicState.m_pIntegerVertexShaderConstant)
{
delete[] m_DynamicState.m_pIntegerVertexShaderConstant;
m_DynamicState.m_pIntegerVertexShaderConstant = NULL;
}
if (m_DynamicState.m_pVectorPixelShaderConstant)
{
delete[] m_DynamicState.m_pVectorPixelShaderConstant;
m_DynamicState.m_pVectorPixelShaderConstant = NULL;
}
if (m_DynamicState.m_pBooleanPixelShaderConstant)
{
delete[] m_DynamicState.m_pBooleanPixelShaderConstant;
m_DynamicState.m_pBooleanPixelShaderConstant = NULL;
}
if (m_DynamicState.m_pIntegerPixelShaderConstant)
{
delete[] m_DynamicState.m_pIntegerPixelShaderConstant;
m_DynamicState.m_pIntegerPixelShaderConstant = NULL;
}
if (m_pDebugTextureList)
{
m_pDebugTextureList->deleteThis();
m_pDebugTextureList = NULL;
}
}
void CShaderAPIDx8::ClearStdTextureHandles(void)
{
for (int i = 0; i < ARRAYSIZE(m_StdTextureHandles); i++)
m_StdTextureHandles[i] = INVALID_SHADERAPI_TEXTURE_HANDLE;
}
//-----------------------------------------------------------------------------
// FIXME: Remove! Backward compat.
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::OnAdapterSet()
{
if (!DetermineHardwareCaps())
return false;
// Modify the caps based on requested DXlevels
int nForcedDXLevel = CommandLine()->ParmValue("-dxlevel", 0);
if (nForcedDXLevel > 0)
{
nForcedDXLevel = MAX(nForcedDXLevel, ABSOLUTE_MINIMUM_DXLEVEL);
}
// FIXME: Check g_pHardwareConfig->ActualCaps() for a preferred DX level
OverrideCaps(nForcedDXLevel);
m_bAdapterSet = true;
return true;
}
void CShaderAPIDx8::GetDXLevelDefaults(uint& max_dxlevel, uint& recommended_dxlevel)
{
max_dxlevel = g_pHardwareConfig->ActualCaps().m_nMaxDXSupportLevel;
recommended_dxlevel = g_pHardwareConfig->ActualCaps().m_nDXSupportLevel;
}
//-----------------------------------------------------------------------------
// Can we download textures?
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::CanDownloadTextures() const
{
if (IsDeactivated())
return false;
return IsActive();
}
//-----------------------------------------------------------------------------
// Grab the render targets
//-----------------------------------------------------------------------------
void CShaderAPIDx8::AcquireInternalRenderTargets()
{
GLMPRINTF((">-A- CShaderAPIDx8::AcquireInternalRenderTargets... "));
if (mat_debugalttab.GetBool())
{
Warning("mat_debugalttab: CShaderAPIDx8::AcquireInternalRenderTargets\n");
}
if (!m_pBackBufferSurface)
{
Dx9Device()->GetRenderTarget(0, &m_pBackBufferSurface);
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
if (!m_NullDevice)
#endif
{
Assert(m_pBackBufferSurface);
}
}
#if defined( _X360 )
if (!m_pBackBufferSurfaceSRGB)
{
// create a SRGB back buffer clone
int backWidth, backHeight;
ShaderAPI()->GetBackBufferDimensions(backWidth, backHeight);
D3DFORMAT backBufferFormat = ImageLoader::ImageFormatToD3DFormat(g_pShaderDevice->GetBackBufferFormat());
m_pBackBufferSurfaceSRGB = g_TextureHeap.AllocRenderTargetSurface(backWidth, backHeight, (D3DFORMAT)MAKESRGBFMT(backBufferFormat), true, 0);
}
#endif
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
if (!m_pZBufferSurface && !m_NullDevice)
#else
if (!m_pZBufferSurface)
#endif
{
Dx9Device()->GetDepthStencilSurface(&m_pZBufferSurface);
Assert(m_pZBufferSurface);
}
GLMPRINTF(("<-A- CShaderAPIDx8::AcquireInternalRenderTargets...complete "));
}
//-----------------------------------------------------------------------------
// Release the render targets
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ReleaseInternalRenderTargets()
{
GLMPRINTF((">-A- CShaderAPIDx8::ReleaseInternalRenderTargets... "));
if (mat_debugalttab.GetBool())
{
Warning("mat_debugalttab: CShaderAPIDx8::ReleaseInternalRenderTargets\n");
}
// Note: This function does not release renderable textures created elsewhere
// Those should be released separately via the texure manager
if (m_pBackBufferSurface)
{
#if POSIX
// dxabstract's AddRef/Release have optional args to help track usage
int nRetVal = m_pBackBufferSurface->Release(0, "-B CShaderAPIDx8::ReleaseInternalRenderTargets public release color buffer");
#else
int nRetVal = m_pBackBufferSurface->Release();
#endif
//Assert( nRetVal == 0 );
m_pBackBufferSurface = NULL;
}
if (m_pZBufferSurface)
{
#if POSIX
// dxabstract's AddRef/Release have optional args to help track usage
int nRetVal = m_pZBufferSurface->Release(0, "-B CShaderAPIDx8::ReleaseInternalRenderTargets public release zbuffer");
#else
int nRetVal = m_pZBufferSurface->Release();
#endif
//Assert( nRetVal == 0 ); //FIXME not sure why we're seeing a refcount of 3 here
m_pZBufferSurface = NULL;
}
GLMPRINTF(("<-A- CShaderAPIDx8::ReleaseInternalRenderTargets... complete"));
}
//-----------------------------------------------------------------------------
// During init, places the persisted texture back into the back buffer.
// The initial 360 fixup present will then not flash. This routine has to be
// self contained, no other shader api systems are viable during init.
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::RestorePersistedDisplay(bool bUseFrontBuffer)
{
#if defined( _X360 )
if (!(XboxLaunch()->GetLaunchFlags() & LF_INTERNALLAUNCH))
{
// there is no persisted screen
return false;
}
OwnGPUResources(false);
const char* strVertexShaderProgram =
" float4x4 matWVP : register(c0);"
" struct VS_IN"
" {"
" float4 ObjPos : POSITION;"
" float2 TexCoord : TEXCOORD;"
" };"
" struct VS_OUT"
" {"
" float4 ProjPos : POSITION;"
" float2 TexCoord : TEXCOORD;"
" };"
" VS_OUT main( VS_IN In )"
" {"
" VS_OUT Out; "
" Out.ProjPos = mul( matWVP, In.ObjPos );"
" Out.TexCoord = In.TexCoord;"
" return Out;"
" }";
const char* strPixelShaderProgram =
" struct PS_IN"
" {"
" float2 TexCoord : TEXCOORD;"
" };"
" sampler detail;"
" float4 main( PS_IN In ) : COLOR"
" {"
" return tex2D( detail, In.TexCoord );"
" }";
D3DVERTEXELEMENT9 VertexElements[3] =
{
{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
{ 0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
D3DDECL_END()
};
IDirect3DTexture* pTexture;
if (bUseFrontBuffer)
{
Dx9Device()->GetFrontBuffer(&pTexture);
}
else
{
// 360 holds a persistent image across restarts
Dx9Device()->GetPersistedTexture(&pTexture);
}
ID3DXBuffer* pErrorMsg = NULL;
ID3DXBuffer* pShaderCode = NULL;
HRESULT hr = D3DXCompileShader(strVertexShaderProgram, (UINT)strlen(strVertexShaderProgram), NULL, NULL, "main", "vs_2_0", 0, &pShaderCode, &pErrorMsg, NULL);
if (FAILED(hr))
{
return false;
}
IDirect3DVertexShader9* pVertexShader;
Dx9Device()->CreateVertexShader((DWORD*)pShaderCode->GetBufferPointer(), &pVertexShader);
pShaderCode->Release();
pErrorMsg = NULL;
pShaderCode = NULL;
hr = D3DXCompileShader(strPixelShaderProgram, (UINT)strlen(strPixelShaderProgram), NULL, NULL, "main", "ps_2_0", 0, &pShaderCode, &pErrorMsg, NULL);
if (FAILED(hr))
{
return false;
}
IDirect3DPixelShader9* pPixelShader;
Dx9Device()->CreatePixelShader((DWORD*)pShaderCode->GetBufferPointer(), &pPixelShader);
pShaderCode->Release();
int w, h;
GetBackBufferDimensions(w, h);
// Create a vertex declaration from the element descriptions.
IDirect3DVertexDeclaration9* pVertexDecl;
Dx9Device()->CreateVertexDeclaration(VertexElements, &pVertexDecl);
XMMATRIX matWVP = XMMatrixOrthographicOffCenterLH(0, (FLOAT)w, (FLOAT)h, 0, 0, 1);
ConVarRef mat_monitorgamma("mat_monitorgamma");
ConVarRef mat_monitorgamma_tv_range_min("mat_monitorgamma_tv_range_min");
ConVarRef mat_monitorgamma_tv_range_max("mat_monitorgamma_tv_range_max");
ConVarRef mat_monitorgamma_tv_exp("mat_monitorgamma_tv_exp");
ConVarRef mat_monitorgamma_tv_enabled("mat_monitorgamma_tv_enabled");
g_pShaderDeviceDx8->SetHardwareGammaRamp(mat_monitorgamma.GetFloat(), mat_monitorgamma_tv_range_min.GetFloat(), mat_monitorgamma_tv_range_max.GetFloat(),
mat_monitorgamma_tv_exp.GetFloat(), mat_monitorgamma_tv_enabled.GetBool());
// Structure to hold vertex data.
struct COLORVERTEX
{
FLOAT Position[3];
float TexCoord[2];
};
COLORVERTEX Vertices[4];
Vertices[0].Position[0] = 0;
Vertices[0].Position[1] = 0;
Vertices[0].Position[2] = 0;
Vertices[0].TexCoord[0] = 0;
Vertices[0].TexCoord[1] = 0;
Vertices[1].Position[0] = w - 1;
Vertices[1].Position[1] = 0;
Vertices[1].Position[2] = 0;
Vertices[1].TexCoord[0] = 1;
Vertices[1].TexCoord[1] = 0;
Vertices[2].Position[0] = w - 1;
Vertices[2].Position[1] = h - 1;
Vertices[2].Position[2] = 0;
Vertices[2].TexCoord[0] = 1;
Vertices[2].TexCoord[1] = 1;
Vertices[3].Position[0] = 0;
Vertices[3].Position[1] = h - 1;
Vertices[3].Position[2] = 0;
Vertices[3].TexCoord[0] = 0;
Vertices[3].TexCoord[1] = 1;
Dx9Device()->SetTexture(0, pTexture);
Dx9Device()->SetVertexShader(pVertexShader);
Dx9Device()->SetPixelShader(pPixelShader);
Dx9Device()->SetVertexShaderConstantF(0, (FLOAT*)&matWVP, 4);
Dx9Device()->SetVertexDeclaration(pVertexDecl);
Dx9Device()->DrawPrimitiveUP(D3DPT_QUADLIST, 1, Vertices, sizeof(COLORVERTEX));
Dx9Device()->SetVertexShader(NULL);
Dx9Device()->SetPixelShader(NULL);
Dx9Device()->SetTexture(0, NULL);
Dx9Device()->SetVertexDeclaration(NULL);
pVertexShader->Release();
pPixelShader->Release();
pVertexDecl->Release();
pTexture->Release();
OwnGPUResources(true);
return true;
#else
return false;
#endif
}
//-----------------------------------------------------------------------------
// Initialize, shutdown the Device....
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::OnDeviceInit()
{
AcquireInternalRenderTargets();
g_pHardwareConfig->CapsForEdit().m_TextureMemorySize = g_pShaderDeviceMgrDx8->GetVidMemBytes(m_nAdapter);
CreateMatrixStacks();
// Hide the cursor
RECORD_COMMAND(DX8_SHOW_CURSOR, 1);
RECORD_INT(false);
#if !defined( _X360 )
Dx9Device()->ShowCursor(false);
#endif
// Initialize the shader manager
ShaderManager()->Init();
// Initialize the shader shadow
ShaderShadow()->Init();
// Initialize the mesh manager
MeshMgr()->Init();
bool bToolsMode = IsWindows() && (CommandLine()->CheckParm("-tools") != NULL);
// Use fat vertices when running in tools
MeshMgr()->UseFatVertices(bToolsMode);
// Initialize the transition table.
m_TransitionTable.Init();
// Initialize the render state
InitRenderState();
// Clear the z and color buffers
ClearBuffers(true, true, true, -1, -1);
AllocFrameSyncObjects();
AllocNonInteractiveRefreshObjects();
RECORD_COMMAND(DX8_BEGIN_SCENE, 0);
// Apply mandatory initialization HW fixups, GPU state will be left as expected
if (IsX360())
{
// place the possible persisted display into the back buffer, ready for present()
RestorePersistedDisplay(false);
// 360 MUST perform an initial swap to stabilize the state
// this ensures any states (e.g. gamma) are respected
// without this, the 360 resets to internal default state on the first swap
OwnGPUResources(false);
Dx9Device()->Present(0, 0, 0, 0);
// present corrupts the GPU state and back buffer (according to docs)
// re-clear the back buffer in order to re-establish the expected contents
ResetRenderState(false);
ClearBuffers(true, true, true, -1, -1);
// place the front buffer image in the back buffer, later systems will detect and grab
// other systems will detect and grab
RestorePersistedDisplay(true);
}
Dx9Device()->BeginScene();
return true;
}
void CShaderAPIDx8::OnDeviceShutdown()
{
if (!IsPC() || !IsActive())
return;
// Deallocate all textures
DeleteAllTextures();
// Release render targets
ReleaseInternalRenderTargets();
// Free objects that are used for frame syncing.
FreeFrameSyncObjects();
FreeNonInteractiveRefreshObjects();
for (int i = 0; i < NUM_MATRIX_MODES; ++i)
{
if (m_pMatrixStack[i])
{
int ref = m_pMatrixStack[i]->Release();
Assert(ref == 0);
}
}
// Shutdown the transition table.
m_TransitionTable.Shutdown();
MeshMgr()->Shutdown();
ShaderManager()->Shutdown();
ReleaseAllVertexDecl();
}
//-----------------------------------------------------------------------------
// Sets the mode...
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::SetMode(void* VD3DHWND, int nAdapter, const ShaderDeviceInfo_t& info)
{
//
// FIXME: Note that this entire function is backward compat and will go soon
//
bool bRestoreNeeded = false;
if (IsActive())
{
ReleaseResources();
OnDeviceShutdown();
ShutdownDevice();
bRestoreNeeded = true;
}
LOCK_SHADERAPI();
Assert(D3D());
Assert(nAdapter < g_pShaderDeviceMgr->GetAdapterCount());
const HardwareCaps_t& actualCaps = g_pShaderDeviceMgr->GetHardwareCaps(nAdapter);
ShaderDeviceInfo_t actualInfo = info;
int nDXLevel = actualInfo.m_nDXLevel ? actualInfo.m_nDXLevel : actualCaps.m_nDXSupportLevel;
static bool bSetModeOnce = false;
if (!bSetModeOnce)
{
nDXLevel = MAX(ABSOLUTE_MINIMUM_DXLEVEL, CommandLine()->ParmValue("-dxlevel", nDXLevel));
bSetModeOnce = true;
}
if (nDXLevel > actualCaps.m_nMaxDXSupportLevel)
{
nDXLevel = actualCaps.m_nMaxDXSupportLevel;
}
actualInfo.m_nDXLevel = g_pShaderDeviceMgr->GetClosestActualDXLevel(nDXLevel);
if (!g_pShaderDeviceMgrDx8->ValidateMode(nAdapter, actualInfo))
return false;
g_pShaderAPI = this;
g_pShaderDevice = this;
g_pShaderShadow = ShaderShadow();
bool bOk = InitDevice(VD3DHWND, nAdapter, actualInfo);
if (!bOk)
return false;
if (!OnDeviceInit())
return false;
if (bRestoreNeeded && IsPC())
{
ReacquireResources();
}
return true;
}
//-----------------------------------------------------------------------------
// Creates the matrix stack
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CreateMatrixStacks()
{
MEM_ALLOC_D3D_CREDIT();
for (int i = 0; i < NUM_MATRIX_MODES; ++i)
{
HRESULT hr = D3DXCreateMatrixStack(0, &m_pMatrixStack[i]);
Assert(hr == D3D_OK);
}
}
//-----------------------------------------------------------------------------
// Vendor-dependent code to turn on alpha to coverage
//-----------------------------------------------------------------------------
void CShaderAPIDx8::EnableAlphaToCoverage(void)
{
if (!g_pHardwareConfig->ActualCaps().m_bSupportsAlphaToCoverage || !IsAAEnabled())
return;
D3DRENDERSTATETYPE renderState = (D3DRENDERSTATETYPE)g_pHardwareConfig->Caps().m_AlphaToCoverageState;
SetRenderState(renderState, g_pHardwareConfig->Caps().m_AlphaToCoverageEnableValue); // Vendor dependent state
}
//-----------------------------------------------------------------------------
// Vendor-dependent code to turn off alpha to coverage
//-----------------------------------------------------------------------------
void CShaderAPIDx8::DisableAlphaToCoverage()
{
if (!g_pHardwareConfig->ActualCaps().m_bSupportsAlphaToCoverage || !IsAAEnabled())
return;
D3DRENDERSTATETYPE renderState = (D3DRENDERSTATETYPE)g_pHardwareConfig->Caps().m_AlphaToCoverageState;
SetRenderState(renderState, g_pHardwareConfig->Caps().m_AlphaToCoverageDisableValue); // Vendor dependent state
}
//-----------------------------------------------------------------------------
// Determine capabilities
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::DetermineHardwareCaps()
{
HardwareCaps_t& actualCaps = g_pHardwareConfig->ActualCapsForEdit();
if (!g_pShaderDeviceMgrDx8->ComputeCapsFromD3D(&actualCaps, m_DisplayAdapter))
return false;
// See if the file tells us otherwise
g_pShaderDeviceMgrDx8->ReadDXSupportLevels(actualCaps);
// Read dxsupport.cfg which has config overrides for particular cards.
g_pShaderDeviceMgrDx8->ReadHardwareCaps(actualCaps, actualCaps.m_nMaxDXSupportLevel);
// What's in "-shader" overrides dxsupport.cfg
const char* pShaderParam = CommandLine()->ParmValue("-shader");
if (pShaderParam)
{
Q_strncpy(actualCaps.m_pShaderDLL, pShaderParam, sizeof(actualCaps.m_pShaderDLL));
}
return true;
}
//-----------------------------------------------------------------------------
// Override caps based on a particular dx level
//-----------------------------------------------------------------------------
void CShaderAPIDx8::OverrideCaps(int nForcedDXLevel)
{
// Just use the actual caps if we can't use what was requested or if the default is requested
if (nForcedDXLevel <= 0)
{
nForcedDXLevel = g_pHardwareConfig->ActualCaps().m_nDXSupportLevel;
}
nForcedDXLevel = g_pShaderDeviceMgr->GetClosestActualDXLevel(nForcedDXLevel);
g_pHardwareConfig->SetupHardwareCaps(nForcedDXLevel, g_pHardwareConfig->ActualCaps());
}
//-----------------------------------------------------------------------------
// Called when the dx support level has changed
//-----------------------------------------------------------------------------
void CShaderAPIDx8::DXSupportLevelChanged()
{
LOCK_SHADERAPI();
if (IsPC())
{
OverrideCaps(ShaderUtil()->GetConfig().dxSupportLevel);
}
else
{
Assert(0);
}
}
//-----------------------------------------------------------------------------
// FIXME: Remove! Backward compat only
//-----------------------------------------------------------------------------
int CShaderAPIDx8::GetActualTextureStageCount() const
{
return g_pHardwareConfig->GetActualTextureStageCount();
}
int CShaderAPIDx8::GetActualSamplerCount() const
{
return g_pHardwareConfig->GetActualSamplerCount();
}
int CShaderAPIDx8::StencilBufferBits() const
{
return m_bUsingStencil ? m_iStencilBufferBits : 0;
}
bool CShaderAPIDx8::IsAAEnabled() const
{
bool bAntialiasing = (m_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE);
return bAntialiasing;
}
//-----------------------------------------------------------------------------
// Methods related to queuing functions to be called per-(pMesh->Draw call) or per-pass
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::IsCommitFuncInUse(CommitFuncType_t func, CommitShaderType_t shader, int nFunc) const
{
Assert(nFunc < COMMIT_FUNC_COUNT);
return (m_pCommitFlags[func][shader][nFunc >> 3] & (1 << (nFunc & 0x7))) != 0;
}
void CShaderAPIDx8::MarkCommitFuncInUse(CommitFuncType_t func, CommitShaderType_t shader, int nFunc)
{
m_pCommitFlags[func][shader][nFunc >> 3] |= 1 << (nFunc & 0x7);
}
void CShaderAPIDx8::AddCommitFunc(CommitFuncType_t func, CommitShaderType_t shader, StateCommitFunc_t f)
{
m_CommitFuncs[func][shader].AddToTail(f);
}
//-----------------------------------------------------------------------------
// Clears all commit functions
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ClearAllCommitFuncs(CommitFuncType_t func, CommitShaderType_t shader)
{
memset(m_pCommitFlags[func][shader], 0, COMMIT_FUNC_BYTE_COUNT);
m_CommitFuncs[func][shader].RemoveAll();
}
//-----------------------------------------------------------------------------
// Calls all commit functions in a particular list
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CallCommitFuncs(CommitFuncType_t func, CommitShaderType_t shader, bool bForce)
{
// 360 does not have have a FF pipe
Assert(IsPC() || (IsX360() && shader != COMMIT_FIXED_FUNCTION));
// Don't bother committing anything if we're deactivated
if (IsDeactivated())
return;
CUtlVector< StateCommitFunc_t >& funcList = m_CommitFuncs[func][shader];
int nCount = funcList.Count();
if (nCount == 0)
return;
for (int i = 0; i < nCount; ++i)
{
funcList[i](Dx9Device(), m_DesiredState, m_DynamicState, bForce);
}
ClearAllCommitFuncs(func, shader);
}
//-----------------------------------------------------------------------------
// Calls all per-mesh draw commit functions
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CallCommitFuncs(CommitFuncType_t func, bool bUsingFixedFunction, bool bForce)
{
// Fixed-Function only funcs
if (IsPC() && (bUsingFixedFunction || bForce))
{
CallCommitFuncs(func, COMMIT_FIXED_FUNCTION, bForce);
}
// Vertex-shader only funcs
if (!bUsingFixedFunction || bForce)
{
CallCommitFuncs(func, COMMIT_VERTEX_SHADER, bForce);
}
// State set in both FF + VS
CallCommitFuncs(func, COMMIT_ALWAYS, bForce);
}
//-----------------------------------------------------------------------------
// Sets the sampler state
//-----------------------------------------------------------------------------
static FORCEINLINE void SetSamplerState(IDirect3DDevice9* pDevice, int stage, D3DSAMPLERSTATETYPE state, DWORD val)
{
RECORD_SAMPLER_STATE(stage, state, val);
#if defined( _X360 )
if (state == D3DSAMP_NOTSUPPORTED)
return;
#endif
pDevice->SetSamplerState(stage, state, val);
}
inline void CShaderAPIDx8::SetSamplerState(int stage, D3DSAMPLERSTATETYPE state, DWORD val)
{
#ifndef DX_TO_GL_ABSTRACTION
if (IsDeactivated())
return;
#endif
::SetSamplerState(Dx9Device(), stage, state, val);
}
//-----------------------------------------------------------------------------
// Sets the texture stage state
//-----------------------------------------------------------------------------
inline void CShaderAPIDx8::SetTextureStageState(int stage, D3DTEXTURESTAGESTATETYPE state, DWORD val)
{
#if !defined( _X360 )
if (IsDeactivated())
return;
Dx9Device()->SetTextureStageState(stage, state, val);
#endif
}
inline void CShaderAPIDx8::SetRenderState(D3DRENDERSTATETYPE state, DWORD val, bool bFlushIfChanged)
{
#if ( !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) )
{
if (IsDeactivated())
return;
}
#else
{
Assert(state != D3DRS_NOTSUPPORTED); //Use SetSupportedRenderState() macro to avoid this at compile time
//if ( state == D3DRS_NOTSUPPORTED )
// return;
}
#endif
Assert(state >= 0 && (int)state < MAX_NUM_RENDERSTATES);
if (m_DynamicState.m_RenderState[state] != val)
{
if (bFlushIfChanged)
{
FlushBufferedPrimitives();
}
#ifdef DX_TO_GL_ABSTRACTION
Dx9Device()->SetRenderStateInline(state, val);
#else
Dx9Device()->SetRenderState(state, val);
#endif
m_DynamicState.m_RenderState[state] = val;
}
}
#ifdef DX_TO_GL_ABSTRACTION
// Purposely always writing the new state (even if it's not changed), in case SetRenderStateConstInline() compiles away to nothing (it sometimes does)
#define SetRenderStateConstMacro(t, state, val ) \
do \
{ \
Assert( state >= 0 && ( int )state < MAX_NUM_RENDERSTATES ); \
if ( t->m_DynamicState.m_RenderState[state] != (DWORD)val ) \
{ \
Dx9Device()->SetRenderStateConstInline( state, val ); \
} \
t->m_DynamicState.m_RenderState[state] = val; \
} while(0)
#else
#define SetRenderStateConstMacro(t, state, val ) t->SetRenderState( state, val );
#endif
//-----------------------------------------------------------------------------
// Commits viewports
//-----------------------------------------------------------------------------
static void CommitSetScissorRect(IDirect3DDevice9* pDevice, const DynamicState_t& desiredState, DynamicState_t& currentState, bool bForce)
{
// Set the enable/disable renderstate
bool bEnableChanged = desiredState.m_RenderState[D3DRS_SCISSORTESTENABLE] != currentState.m_RenderState[D3DRS_SCISSORTESTENABLE];
if (bEnableChanged)
{
Dx9Device()->SetRenderState(D3DRS_SCISSORTESTENABLE, desiredState.m_RenderState[D3DRS_SCISSORTESTENABLE]);
currentState.m_RenderState[D3DRS_SCISSORTESTENABLE] = desiredState.m_RenderState[D3DRS_SCISSORTESTENABLE];
}
// Only bother with the rect if we're enabling
if (desiredState.m_RenderState[D3DRS_SCISSORTESTENABLE])
{
int nWidth, nHeight;
ITexture* pTexture = ShaderAPI()->GetRenderTargetEx(0);
if (pTexture == NULL)
{
ShaderAPI()->GetBackBufferDimensions(nWidth, nHeight);
}
else
{
nWidth = pTexture->GetActualWidth();
nHeight = pTexture->GetActualHeight();
}
Assert((desiredState.m_ScissorRect.left <= nWidth) && (desiredState.m_ScissorRect.bottom <= nHeight) &&
(desiredState.m_ScissorRect.top >= 0) && (desiredState.m_ScissorRect.left >= 0));
clamp(desiredState.m_ScissorRect.right, 0, nWidth);
clamp(desiredState.m_ScissorRect.left, 0, nWidth);
clamp(desiredState.m_ScissorRect.top, 0, nHeight);
clamp(desiredState.m_ScissorRect.bottom, 0, nHeight);
Dx9Device()->SetScissorRect(&desiredState.m_ScissorRect);
currentState.m_ScissorRect = desiredState.m_ScissorRect;
}
}
// Routine for setting scissor rect
// If pScissorRect is NULL, disable scissoring by setting the render state
// If pScissorRect is non-NULL, set the RECT state in Direct3D AND set the renderstate
inline void CShaderAPIDx8::SetScissorRect(const int nLeft, const int nTop, const int nRight, const int nBottom, const bool bEnableScissor)
{
Assert((nLeft <= nRight) && (nTop <= nBottom)); //360 craps itself if this isn't true
if (!g_pHardwareConfig->Caps().m_bScissorSupported)
return;
// If we're turning it on, check the validity of the rect
if (bEnableScissor)
{
int nWidth, nHeight;
ITexture* pTexture = GetRenderTargetEx(0);
if (pTexture == NULL)
{
GetBackBufferDimensions(nWidth, nHeight);
}
else
{
nWidth = pTexture->GetActualWidth();
nHeight = pTexture->GetActualHeight();
}
Assert((nRight <= nWidth) && (nBottom <= nHeight) && (nTop >= 0) && (nLeft >= 0));
clamp(nRight, 0, nWidth);
clamp(nLeft, 0, nWidth);
clamp(nTop, 0, nHeight);
clamp(nBottom, 0, nHeight);
}
DWORD dwEnableScissor = bEnableScissor ? TRUE : FALSE;
RECT newScissorRect;
newScissorRect.left = nLeft;
newScissorRect.top = nTop;
newScissorRect.right = nRight;
newScissorRect.bottom = nBottom;
if (!m_bResettingRenderState)
{
bool bEnableChanged = m_DesiredState.m_RenderState[D3DRS_SCISSORTESTENABLE] != dwEnableScissor;
bool bRectChanged = memcmp(&newScissorRect, &m_DesiredState.m_ScissorRect, sizeof(RECT)) != 0;
if (!bEnableChanged && !bRectChanged)
return;
}
if (!IsDeactivated())
{
// Do we need to do this always?
FlushBufferedPrimitives();
}
m_DesiredState.m_RenderState[D3DRS_SCISSORTESTENABLE] = dwEnableScissor;
memcpy(&m_DesiredState.m_ScissorRect, &newScissorRect, sizeof(RECT));
ADD_COMMIT_FUNC(COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitSetScissorRect);
}
inline void CShaderAPIDx8::SetRenderStateForce(D3DRENDERSTATETYPE state, DWORD val)
{
#if ( !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) )
{
if (IsDeactivated())
return;
}
#else
{
Assert(state != D3DRS_NOTSUPPORTED); //Use SetSupportedRenderStateForce() macro to avoid this at compile time
//if ( state == D3DRS_NOTSUPPORTED )
// return;
}
#endif
Dx9Device()->SetRenderState(state, val);
m_DynamicState.m_RenderState[state] = val;
}
//-----------------------------------------------------------------------------
// Set the values for pixel shader constants that don't change.
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetStandardVertexShaderConstants(float fOverbright)
{
LOCK_SHADERAPI();
if (g_pHardwareConfig->GetDXSupportLevel() < 80)
return;
// Set a couple standard constants....
Vector4D standardVertexShaderConstant(0.0f, 1.0f, 2.0f, 0.5f);
SetVertexShaderConstant(VERTEX_SHADER_MATH_CONSTANTS0, standardVertexShaderConstant.Base(), 1);
// [ gamma, overbright, 1/3, 1/overbright ]
standardVertexShaderConstant.Init(1.0f / 2.2f, fOverbright, 1.0f / 3.0f, 1.0f / fOverbright);
SetVertexShaderConstant(VERTEX_SHADER_MATH_CONSTANTS1, standardVertexShaderConstant.Base(), 1);
int nModelIndex = g_pHardwareConfig->Caps().m_nDXSupportLevel < 90 ? VERTEX_SHADER_MODEL - 10 : VERTEX_SHADER_MODEL;
/*
if ( g_pHardwareConfig->Caps().m_SupportsVertexShaders_3_0 )
{
Vector4D factors[4];
factors[0].Init( 1, 0, 0, 0 );
factors[1].Init( 0, 1, 0, 0 );
factors[2].Init( 0, 0, 1, 0 );
factors[3].Init( 0, 0, 0, 1 );
SetVertexShaderConstant( VERTEX_SHADER_DOT_PRODUCT_FACTORS, factors[0].Base(), 4 );
}
*/
if (g_pHardwareConfig->Caps().m_SupportsVertexShaders_2_0)
{
float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
SetVertexShaderConstant(VERTEX_SHADER_FLEXSCALE, c, 1);
}
else
{
// These point to the lighting and the transforms
standardVertexShaderConstant.Init(
VERTEX_SHADER_LIGHTS,
VERTEX_SHADER_LIGHTS + 5,
// Use COLOR instead of UBYTE4 since Geforce3 does not support it
// vConst.w should be 3, but due to about hack, mul by 255 and add epsilon
// 360 supports UBYTE4, so no fixup required
(IsPC() || !IsX360()) ? 765.01f : 3.0f,
nModelIndex); // DX8 has different constant packing
SetVertexShaderConstant(VERTEX_SHADER_LIGHT_INDEX, standardVertexShaderConstant.Base(), 1);
}
}
//-----------------------------------------------------------------------------
// Initialize vertex and pixel shaders
//-----------------------------------------------------------------------------
void CShaderAPIDx8::InitVertexAndPixelShaders()
{
// Allocate space for the pixel and vertex shader constants...
if (g_pHardwareConfig->Caps().m_SupportsPixelShaders)
{
// pixel shaders
{
if (m_DynamicState.m_pVectorPixelShaderConstant)
{
delete[] m_DynamicState.m_pVectorPixelShaderConstant;
}
m_DynamicState.m_pVectorPixelShaderConstant = new Vector4D[g_pHardwareConfig->Caps().m_NumPixelShaderConstants];
if (m_DesiredState.m_pVectorPixelShaderConstant)
{
delete[] m_DesiredState.m_pVectorPixelShaderConstant;
}
m_DesiredState.m_pVectorPixelShaderConstant = new Vector4D[g_pHardwareConfig->Caps().m_NumPixelShaderConstants];
if (m_DynamicState.m_pBooleanPixelShaderConstant)
{
delete[] m_DynamicState.m_pBooleanPixelShaderConstant;
}
m_DynamicState.m_pBooleanPixelShaderConstant = new BOOL[g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants];
if (m_DesiredState.m_pBooleanPixelShaderConstant)
{
delete[] m_DesiredState.m_pBooleanPixelShaderConstant;
}
m_DesiredState.m_pBooleanPixelShaderConstant = new BOOL[g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants];
if (m_DynamicState.m_pIntegerPixelShaderConstant)
{
delete[] m_DynamicState.m_pIntegerPixelShaderConstant;
}
m_DynamicState.m_pIntegerPixelShaderConstant = new IntVector4D[g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants];
if (m_DesiredState.m_pIntegerPixelShaderConstant)
{
delete[] m_DesiredState.m_pIntegerPixelShaderConstant;
}
m_DesiredState.m_pIntegerPixelShaderConstant = new IntVector4D[g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants];
// force reset vector pixel constants
int i;
for (i = 0; i < g_pHardwareConfig->Caps().m_NumPixelShaderConstants; ++i)
{
m_DesiredState.m_pVectorPixelShaderConstant[i].Init();
}
SetPixelShaderConstant(0, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), g_pHardwareConfig->Caps().m_NumPixelShaderConstants, true);
// force reset boolean pixel constants
int nNumBooleanPixelShaderConstants = g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants;
if (nNumBooleanPixelShaderConstants)
{
for (i = 0; i < nNumBooleanPixelShaderConstants; ++i)
{
m_DesiredState.m_pBooleanPixelShaderConstant[i] = 0;
}
SetBooleanPixelShaderConstant(0, m_DesiredState.m_pBooleanPixelShaderConstant, nNumBooleanPixelShaderConstants, true);
}
// force reset integer pixel constants
int nNumIntegerPixelShaderConstants = g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants;
if (nNumIntegerPixelShaderConstants)
{
for (i = 0; i < nNumIntegerPixelShaderConstants; ++i)
{
m_DesiredState.m_pIntegerPixelShaderConstant[i].Init();
}
SetIntegerPixelShaderConstant(0, m_DesiredState.m_pIntegerPixelShaderConstant[0].Base(), nNumIntegerPixelShaderConstants, true);
}
}
// vertex shaders
{
if (m_DynamicState.m_pVectorVertexShaderConstant)
{
delete[] m_DynamicState.m_pVectorVertexShaderConstant;
}
m_DynamicState.m_pVectorVertexShaderConstant = new Vector4D[g_pHardwareConfig->Caps().m_NumVertexShaderConstants];
if (m_DesiredState.m_pVectorVertexShaderConstant)
{
delete[] m_DesiredState.m_pVectorVertexShaderConstant;
}
m_DesiredState.m_pVectorVertexShaderConstant = new Vector4D[g_pHardwareConfig->Caps().m_NumVertexShaderConstants];
if (m_DynamicState.m_pBooleanVertexShaderConstant)
{
delete[] m_DynamicState.m_pBooleanVertexShaderConstant;
}
m_DynamicState.m_pBooleanVertexShaderConstant = new BOOL[g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants];
if (m_DesiredState.m_pBooleanVertexShaderConstant)
{
delete[] m_DesiredState.m_pBooleanVertexShaderConstant;
}
m_DesiredState.m_pBooleanVertexShaderConstant = new BOOL[g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants];
if (m_DynamicState.m_pIntegerVertexShaderConstant)
{
delete[] m_DynamicState.m_pIntegerVertexShaderConstant;
}
m_DynamicState.m_pIntegerVertexShaderConstant = new IntVector4D[g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants];
if (m_DesiredState.m_pIntegerVertexShaderConstant)
{
delete[] m_DesiredState.m_pIntegerVertexShaderConstant;
}
m_DesiredState.m_pIntegerVertexShaderConstant = new IntVector4D[g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants];
// force reset vector vertex constants
int i;
for (i = 0; i < g_pHardwareConfig->Caps().m_NumVertexShaderConstants; ++i)
{
m_DesiredState.m_pVectorVertexShaderConstant[i].Init();
}
SetVertexShaderConstant(0, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), g_pHardwareConfig->Caps().m_NumVertexShaderConstants, true);
// force reset boolean vertex constants
for (i = 0; i < g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants; ++i)
{
m_DesiredState.m_pBooleanVertexShaderConstant[i] = 0;
}
SetBooleanVertexShaderConstant(0, m_DesiredState.m_pBooleanVertexShaderConstant, g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants, true);
// force reset integer vertex constants
for (i = 0; i < g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants; ++i)
{
m_DesiredState.m_pIntegerVertexShaderConstant[i].Init();
}
SetIntegerVertexShaderConstant(0, m_DesiredState.m_pIntegerVertexShaderConstant[0].Base(), g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants, true);
}
if (IsX360())
{
// to init/update all constants, must disable ownership
bool bPreviousState = OwnGPUResources(false);
WriteShaderConstantsToGPU();
OwnGPUResources(bPreviousState);
}
SetStandardVertexShaderConstants(OVERBRIGHT);
}
// Set up the vertex and pixel shader stuff
ShaderManager()->ResetShaderState();
}
//-----------------------------------------------------------------------------
// Initialize render state
//-----------------------------------------------------------------------------
void CShaderAPIDx8::InitRenderState()
{
// Set the default shadow state
g_pShaderShadowDx8->SetDefaultState();
// Grab a snapshot of this state; we'll be using it to set the board
// state to something well defined.
m_TransitionTable.TakeDefaultStateSnapshot();
if (!IsDeactivated())
{
ResetRenderState();
}
}
//-----------------------------------------------------------------------------
// Commits vertex textures
//-----------------------------------------------------------------------------
static void CommitVertexTextures(IDirect3DDevice9* pDevice, const DynamicState_t& desiredState, DynamicState_t& currentState, bool bForce)
{
int nCount = g_pMaterialSystemHardwareConfig->GetVertexTextureCount();
for (int i = 0; i < nCount; ++i)
{
VertexTextureState_t& currentVTState = currentState.m_VertexTextureState[i];
ShaderAPITextureHandle_t textureHandle = desiredState.m_VertexTextureState[i].m_BoundTexture;
Texture_t* pTexture = (textureHandle != INVALID_SHADERAPI_TEXTURE_HANDLE) ? &g_ShaderAPIDX8.GetTexture(textureHandle) : NULL;
if (pTexture && (pTexture->m_Flags & Texture_t::IS_VERTEX_TEXTURE) == 0)
{
Warning("Attempting to bind a vertex texture (%s) which was not created as a vertex texture!\n", pTexture->m_DebugName.String());
}
if (bForce || (currentVTState.m_BoundTexture != textureHandle))
{
currentVTState.m_BoundTexture = textureHandle;
// RECORD_COMMAND( DX8_SET_VERTEXTEXTURE, 3 );
// RECORD_INT( D3DVERTEXTEXTURESAMPLER0 + nStage );
// RECORD_INT( pTexture ? pTexture->GetUniqueID() : 0xFFFF );
// RECORD_INT( 0 );
IDirect3DBaseTexture* pD3DTexture = (textureHandle >= 0) ? g_ShaderAPIDX8.GetD3DTexture(textureHandle) : NULL;
pDevice->SetTexture(D3DVERTEXTEXTURESAMPLER0 + i, pD3DTexture);
}
if (!pTexture)
continue;
D3DTEXTUREADDRESS nNewUTexWrap = pTexture->m_UTexWrap;
if (bForce || (currentVTState.m_UTexWrap != nNewUTexWrap))
{
currentVTState.m_UTexWrap = nNewUTexWrap;
SetSamplerState(pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSU, currentVTState.m_UTexWrap);
}
D3DTEXTUREADDRESS nNewVTexWrap = pTexture->m_VTexWrap;
if (bForce || (currentVTState.m_VTexWrap != nNewVTexWrap))
{
currentVTState.m_VTexWrap = nNewVTexWrap;
SetSamplerState(pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSV, currentVTState.m_VTexWrap);
}
/*
D3DTEXTUREADDRESS nNewWTexWrap = pTexture->GetWTexWrap();
if ( bForce || ( currentVTState.m_WTexWrap != nNewWTexWrap ) )
{
currentVTState.m_WTexWrap = nNewWTexWrap;
SetSamplerState( pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSW, currentVTState.m_WTexWrap );
}
*/
D3DTEXTUREFILTERTYPE nNewMinFilter = pTexture->m_MinFilter;
if (bForce || (currentVTState.m_MinFilter != nNewMinFilter))
{
currentVTState.m_MinFilter = nNewMinFilter;
SetSamplerState(pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MINFILTER, currentVTState.m_MinFilter);
}
D3DTEXTUREFILTERTYPE nNewMagFilter = pTexture->m_MagFilter;
if (bForce || (currentVTState.m_MagFilter != nNewMagFilter))
{
currentVTState.m_MagFilter = nNewMagFilter;
SetSamplerState(pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MAGFILTER, currentVTState.m_MagFilter);
}
D3DTEXTUREFILTERTYPE nNewMipFilter = pTexture->m_MipFilter;
if (bForce || (currentVTState.m_MipFilter != nNewMipFilter))
{
currentVTState.m_MipFilter = nNewMipFilter;
SetSamplerState(pDevice, D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MIPFILTER, currentVTState.m_MipFilter);
}
}
}
//-----------------------------------------------------------------------------
// Returns true if the state snapshot is translucent
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::IsTranslucent(StateSnapshot_t id) const
{
LOCK_SHADERAPI();
return m_TransitionTable.GetSnapshot(id).m_AlphaBlendEnable;
}
//-----------------------------------------------------------------------------
// Returns true if the state snapshot is alpha tested
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::IsAlphaTested(StateSnapshot_t id) const
{
LOCK_SHADERAPI();
return m_TransitionTable.GetSnapshot(id).m_AlphaTestEnable;
}
//-----------------------------------------------------------------------------
// Gets at the shadow state for a particular state snapshot
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::IsDepthWriteEnabled(StateSnapshot_t id) const
{
LOCK_SHADERAPI();
return m_TransitionTable.GetSnapshot(id).m_ZWriteEnable;
}
//-----------------------------------------------------------------------------
// Returns true if the state snapshot uses shaders
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::UsesVertexAndPixelShaders(StateSnapshot_t id) const
{
LOCK_SHADERAPI();
return m_TransitionTable.GetSnapshotShader(id).m_VertexShader != INVALID_SHADER;
}
//-----------------------------------------------------------------------------
// Takes a snapshot
//-----------------------------------------------------------------------------
StateSnapshot_t CShaderAPIDx8::TakeSnapshot()
{
LOCK_SHADERAPI();
return m_TransitionTable.TakeSnapshot();
}
void CShaderAPIDx8::ResetDXRenderState(void)
{
float zero = 0.0f;
float one = 1.0f;
DWORD dZero = *((DWORD*)(&zero));
DWORD dOne = *((DWORD*)(&one));
// Set default values for all dx render states.
// NOTE: this does not include states encapsulated by the transition table,
// which are reset in CTransitionTable::UseDefaultState
SetSupportedRenderStateForce(D3DRS_FILLMODE, D3DFILL_SOLID);
SetSupportedRenderStateForce(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
SetSupportedRenderStateForce(D3DRS_LASTPIXEL, TRUE);
SetSupportedRenderStateForce(D3DRS_CULLMODE, D3DCULL_CCW);
SetSupportedRenderStateForce(D3DRS_DITHERENABLE, FALSE);
SetSupportedRenderStateForce(D3DRS_FOGENABLE, FALSE);
SetSupportedRenderStateForce(D3DRS_SPECULARENABLE, FALSE);
SetSupportedRenderStateForce(D3DRS_FOGCOLOR, 0);
SetSupportedRenderStateForce(D3DRS_FOGTABLEMODE, D3DFOG_NONE);
SetSupportedRenderStateForce(D3DRS_FOGSTART, dZero);
SetSupportedRenderStateForce(D3DRS_FOGEND, dOne);
SetSupportedRenderStateForce(D3DRS_FOGDENSITY, dZero);
SetSupportedRenderStateForce(D3DRS_RANGEFOGENABLE, FALSE);
SetSupportedRenderStateForce(D3DRS_STENCILENABLE, FALSE);
SetSupportedRenderStateForce(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
SetSupportedRenderStateForce(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);
SetSupportedRenderStateForce(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);
SetSupportedRenderStateForce(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
SetSupportedRenderStateForce(D3DRS_STENCILREF, 0);
SetSupportedRenderStateForce(D3DRS_STENCILMASK, 0xFFFFFFFF);
SetSupportedRenderStateForce(D3DRS_STENCILWRITEMASK, 0xFFFFFFFF);
SetSupportedRenderStateForce(D3DRS_TEXTUREFACTOR, 0xFFFFFFFF);
SetSupportedRenderStateForce(D3DRS_WRAP0, 0);
SetSupportedRenderStateForce(D3DRS_WRAP1, 0);
SetSupportedRenderStateForce(D3DRS_WRAP2, 0);
SetSupportedRenderStateForce(D3DRS_WRAP3, 0);
SetSupportedRenderStateForce(D3DRS_WRAP4, 0);
SetSupportedRenderStateForce(D3DRS_WRAP5, 0);
SetSupportedRenderStateForce(D3DRS_WRAP6, 0);
SetSupportedRenderStateForce(D3DRS_WRAP7, 0);
SetSupportedRenderStateForce(D3DRS_CLIPPING, TRUE);
SetSupportedRenderStateForce(D3DRS_LIGHTING, TRUE);
SetSupportedRenderStateForce(D3DRS_AMBIENT, 0);
SetSupportedRenderStateForce(D3DRS_FOGVERTEXMODE, D3DFOG_NONE);
SetSupportedRenderStateForce(D3DRS_COLORVERTEX, TRUE);
SetSupportedRenderStateForce(D3DRS_LOCALVIEWER, TRUE);
SetSupportedRenderStateForce(D3DRS_NORMALIZENORMALS, FALSE);
SetSupportedRenderStateForce(D3DRS_SPECULARMATERIALSOURCE, D3DMCS_COLOR2);
SetSupportedRenderStateForce(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
SetSupportedRenderStateForce(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL);
SetSupportedRenderStateForce(D3DRS_VERTEXBLEND, D3DVBF_DISABLE);
SetSupportedRenderStateForce(D3DRS_CLIPPLANEENABLE, 0);
// -disable_d3d9_hacks is for debugging. For example, the "CENT" driver hack thing causes the flashlight pass to appear much brighter on NVidia drivers.
if (IsPC() && !IsOpenGL() && !CommandLine()->CheckParm("-disable_d3d9_hacks"))
{
if (g_pHardwareConfig->Caps().m_bNeedsATICentroidHack && (g_pHardwareConfig->Caps().m_VendorID == VENDORID_ATI))
{
SetSupportedRenderStateForce(D3DRS_POINTSIZE, MAKEFOURCC('C', 'E', 'N', 'T'));
}
if (g_pHardwareConfig->Caps().m_bDisableShaderOptimizations)
{
SetSupportedRenderStateForce(D3DRS_ADAPTIVETESS_Y, MAKEFOURCC('C', 'O', 'P', 'M'));
}
}
SetSupportedRenderStateForce(D3DRS_POINTSIZE, dOne);
SetSupportedRenderStateForce(D3DRS_POINTSIZE_MIN, dOne);
SetSupportedRenderStateForce(D3DRS_POINTSPRITEENABLE, FALSE);
SetSupportedRenderStateForce(D3DRS_POINTSCALEENABLE, FALSE);
SetSupportedRenderStateForce(D3DRS_POINTSCALE_A, dOne);
SetSupportedRenderStateForce(D3DRS_POINTSCALE_B, dZero);
SetSupportedRenderStateForce(D3DRS_POINTSCALE_C, dZero);
SetSupportedRenderStateForce(D3DRS_MULTISAMPLEANTIALIAS, TRUE);
SetSupportedRenderStateForce(D3DRS_MULTISAMPLEMASK, 0xFFFFFFFF);
SetSupportedRenderStateForce(D3DRS_PATCHEDGESTYLE, D3DPATCHEDGE_DISCRETE);
SetSupportedRenderStateForce(D3DRS_DEBUGMONITORTOKEN, D3DDMT_ENABLE);
float sixtyFour = 64.0f;
SetSupportedRenderStateForce(D3DRS_POINTSIZE_MAX, *((DWORD*)(&sixtyFour)));
SetSupportedRenderStateForce(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE);
SetSupportedRenderStateForce(D3DRS_TWEENFACTOR, dZero);
SetSupportedRenderStateForce(D3DRS_POSITIONDEGREE, D3DDEGREE_CUBIC);
SetSupportedRenderStateForce(D3DRS_NORMALDEGREE, D3DDEGREE_LINEAR);
SetSupportedRenderStateForce(D3DRS_SCISSORTESTENABLE, FALSE);
SetSupportedRenderStateForce(D3DRS_SLOPESCALEDEPTHBIAS, dZero);
SetSupportedRenderStateForce(D3DRS_ANTIALIASEDLINEENABLE, FALSE);
SetSupportedRenderStateForce(D3DRS_MINTESSELLATIONLEVEL, dOne);
SetSupportedRenderStateForce(D3DRS_MAXTESSELLATIONLEVEL, dOne);
SetSupportedRenderStateForce(D3DRS_ADAPTIVETESS_X, dZero);
SetSupportedRenderStateForce(D3DRS_ADAPTIVETESS_Y, dZero);
SetSupportedRenderStateForce(D3DRS_ADAPTIVETESS_Z, dOne);
SetSupportedRenderStateForce(D3DRS_ADAPTIVETESS_W, dZero);
SetSupportedRenderStateForce(D3DRS_ENABLEADAPTIVETESSELLATION, FALSE);
SetSupportedRenderStateForce(D3DRS_TWOSIDEDSTENCILMODE, FALSE);
SetSupportedRenderStateForce(D3DRS_CCW_STENCILFAIL, D3DSTENCILOP_KEEP);
SetSupportedRenderStateForce(D3DRS_CCW_STENCILZFAIL, D3DSTENCILOP_KEEP);
SetSupportedRenderStateForce(D3DRS_CCW_STENCILPASS, D3DSTENCILOP_KEEP);
SetSupportedRenderStateForce(D3DRS_CCW_STENCILFUNC, D3DCMP_ALWAYS);
SetSupportedRenderStateForce(D3DRS_COLORWRITEENABLE1, 0x0000000f);
SetSupportedRenderStateForce(D3DRS_COLORWRITEENABLE2, 0x0000000f);
SetSupportedRenderStateForce(D3DRS_COLORWRITEENABLE3, 0x0000000f);
SetSupportedRenderStateForce(D3DRS_BLENDFACTOR, 0xffffffff);
SetSupportedRenderStateForce(D3DRS_SRGBWRITEENABLE, 0);
SetSupportedRenderStateForce(D3DRS_DEPTHBIAS, dZero);
SetSupportedRenderStateForce(D3DRS_WRAP8, 0);
SetSupportedRenderStateForce(D3DRS_WRAP9, 0);
SetSupportedRenderStateForce(D3DRS_WRAP10, 0);
SetSupportedRenderStateForce(D3DRS_WRAP11, 0);
SetSupportedRenderStateForce(D3DRS_WRAP12, 0);
SetSupportedRenderStateForce(D3DRS_WRAP13, 0);
SetSupportedRenderStateForce(D3DRS_WRAP14, 0);
SetSupportedRenderStateForce(D3DRS_WRAP15, 0);
SetSupportedRenderStateForce(D3DRS_BLENDOP, D3DBLENDOP_ADD);
SetSupportedRenderStateForce(D3DRS_BLENDOPALPHA, D3DBLENDOP_ADD);
#if defined( _X360 )
SetSupportedRenderStateForce(D3DRS_HIZENABLE, D3DHIZ_AUTOMATIC);
SetSupportedRenderStateForce(D3DRS_HIZWRITEENABLE, D3DHIZ_AUTOMATIC);
SetSupportedRenderStateForce(D3DRS_HISTENCILENABLE, FALSE);
SetSupportedRenderStateForce(D3DRS_HISTENCILWRITEENABLE, FALSE);
SetSupportedRenderStateForce(D3DRS_HISTENCILFUNC, D3DHSCMP_EQUAL);
SetSupportedRenderStateForce(D3DRS_HISTENCILREF, 0);
#endif
}
//-----------------------------------------------------------------------------
// Own GPU Resources. Return previous state.
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::OwnGPUResources(bool bEnable)
{
#if defined( _X360 )
if (m_bGPUOwned == bEnable)
{
return m_bGPUOwned;
}
if (!bEnable)
{
Dx9Device()->GpuDisownAll();
}
else
{
// owned GPU constants can be set very fast, and must be in blocks of 4
// there are 256, but the game only uses 217 (snapped to 220), leaving just enough room for shader literals
COMPILE_TIME_ASSERT(VERTEX_SHADER_MODEL + 3 * NUM_MODEL_TRANSFORMS == 217);
Dx9Device()->GpuOwnVertexShaderConstantF(0, AlignValue(VERTEX_SHADER_MODEL + 3 * NUM_MODEL_TRANSFORMS, 4));
// there are 256, but the game only utilizes 32, leaving lots of room for shader literals
Dx9Device()->GpuOwnPixelShaderConstantF(0, 32);
}
bool bPrevious = m_bGPUOwned;
m_bGPUOwned = bEnable;
return bPrevious;
#else
return false;
#endif
}
//-----------------------------------------------------------------------------
// Reset render state (to its initial value)
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ResetRenderState(bool bFullReset)
{
LOCK_SHADERAPI();
RECORD_DEBUG_STRING("BEGIN ResetRenderState");
if (!CanDownloadTextures())
{
QueueResetRenderState();
return;
}
m_bResettingRenderState = true;
OwnGPUResources(true);
ResetDXRenderState();
// We're not currently rendering anything
m_nCurrentSnapshot = -1;
D3DXMatrixIdentity(&m_CachedPolyOffsetProjectionMatrix);
D3DXMatrixIdentity(&m_CachedFastClipProjectionMatrix);
D3DXMatrixIdentity(&m_CachedFastClipPolyOffsetProjectionMatrix);
m_UsingTextureRenderTarget = false;
m_SceneFogColor[0] = 0;
m_SceneFogColor[1] = 0;
m_SceneFogColor[2] = 0;
m_SceneFogMode = MATERIAL_FOG_NONE;
// This is state that isn't part of the snapshot per-se, because we
// don't need it when it's actually time to render. This just helps us
// to construct the shadow state.
m_DynamicState.m_ClearColor = D3DCOLOR_XRGB(0, 0, 0);
if (bFullReset)
{
InitVertexAndPixelShaders();
}
else
{
// just need to dirty the dynamic state, desired state gets copied into below
Q_memset(m_DynamicState.m_pVectorPixelShaderConstant, 0, g_pHardwareConfig->Caps().m_NumPixelShaderConstants * sizeof(Vector4D));
Q_memset(m_DynamicState.m_pBooleanPixelShaderConstant, 0, g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants * sizeof(BOOL));
Q_memset(m_DynamicState.m_pIntegerPixelShaderConstant, 0, g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants * sizeof(IntVector4D));
Q_memset(m_DynamicState.m_pVectorVertexShaderConstant, 0, g_pHardwareConfig->Caps().m_NumVertexShaderConstants * sizeof(Vector4D));
Q_memset(m_DynamicState.m_pBooleanVertexShaderConstant, 0, g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants * sizeof(BOOL));
Q_memset(m_DynamicState.m_pIntegerVertexShaderConstant, 0, g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants * sizeof(IntVector4D));
SetStandardVertexShaderConstants(OVERBRIGHT);
}
//Set the default compressed depth range written to dest alpha. Only need to compress it for 8bit alpha to get a useful gradient.
m_DynamicState.m_DestAlphaDepthRange = (g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT) ? 2048.0f : 192.0f;
m_CachedAmbientLightCube = STATE_CHANGED;
// Set the constant color
m_DynamicState.m_ConstantColor = 0xFFFFFFFF;
Color4ub(255, 255, 255, 255);
// Ambient light color
m_DynamicState.m_Ambient = 0;
SetSupportedRenderState(D3DRS_AMBIENT, m_DynamicState.m_Ambient);
// Fog
m_VertexShaderFogParams[0] = m_VertexShaderFogParams[1] = 0.0f;
m_WorldSpaceCameraPositon.Init(0, 0, 0.01f, 0); // Don't let z be zero, as some pixel shaders will divide by this
m_DynamicState.m_FogColor = 0xFFFFFFFF;
m_DynamicState.m_PixelFogColor[0] = m_DynamicState.m_PixelFogColor[1] =
m_DynamicState.m_PixelFogColor[2] = m_DynamicState.m_PixelFogColor[3] = 0.0f;
m_DynamicState.m_bFogGammaCorrectionDisabled = false;
m_DynamicState.m_FogEnable = false;
m_DynamicState.m_SceneFog = MATERIAL_FOG_NONE;
m_DynamicState.m_FogMode = D3DFOG_NONE;
m_DynamicState.m_FogStart = -1;
m_DynamicState.m_FogEnd = -1;
m_DynamicState.m_FogMaxDensity = -1.0f;
m_DynamicState.m_FogZ = 0.0f;
SetSupportedRenderState(D3DRS_FOGCOLOR, m_DynamicState.m_FogColor);
SetSupportedRenderState(D3DRS_FOGENABLE, m_DynamicState.m_FogEnable);
SetSupportedRenderState(D3DRS_FOGTABLEMODE, D3DFOG_NONE);
SetSupportedRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_NONE);
SetSupportedRenderState(D3DRS_RANGEFOGENABLE, false);
FogStart(0);
FogEnd(0);
FogMaxDensity(1.0f);
m_DynamicState.m_bSRGBWritesEnabled = false;
// Set the cull mode
m_DynamicState.m_bCullEnabled = true;
m_DynamicState.m_CullMode = D3DCULL_CCW;
m_DynamicState.m_DesiredCullMode = D3DCULL_CCW;
SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
// No shade mode yet
m_DynamicState.m_ShadeMode = (D3DSHADEMODE)-1;
ShadeMode(SHADER_SMOOTH);
m_DynamicState.m_bHWMorphingEnabled = false;
// Skinning...
m_DynamicState.m_NumBones = 0;
m_DynamicState.m_VertexBlend = (D3DVERTEXBLENDFLAGS)-1;
SetSupportedRenderState(D3DRS_VERTEXBLEND, D3DVBF_DISABLE);
SetSupportedRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE);
// No normal normalization
m_DynamicState.m_NormalizeNormals = false;
SetSupportedRenderState(D3DRS_NORMALIZENORMALS, m_DynamicState.m_NormalizeNormals);
bool bAntialiasing = (m_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE);
SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, bAntialiasing);
// Anisotropic filtering is disabled by default
if (bFullReset)
{
SetAnisotropicLevel(1);
}
int i;
for (i = 0; i < g_pHardwareConfig->ActualCaps().m_NumTextureStages; ++i)
{
TextureStage(i).m_TextureTransformFlags = D3DTTFF_DISABLE;
TextureStage(i).m_BumpEnvMat00 = 1.0f;
TextureStage(i).m_BumpEnvMat01 = 0.0f;
TextureStage(i).m_BumpEnvMat10 = 0.0f;
TextureStage(i).m_BumpEnvMat11 = 1.0f;
SetTextureStageState(i, D3DTSS_TEXTURETRANSFORMFLAGS, TextureStage(i).m_TextureTransformFlags);
SetTextureStageState(i, D3DTSS_BUMPENVMAT00, *((LPDWORD)(&TextureStage(i).m_BumpEnvMat00)));
SetTextureStageState(i, D3DTSS_BUMPENVMAT01, *((LPDWORD)(&TextureStage(i).m_BumpEnvMat01)));
SetTextureStageState(i, D3DTSS_BUMPENVMAT10, *((LPDWORD)(&TextureStage(i).m_BumpEnvMat10)));
SetTextureStageState(i, D3DTSS_BUMPENVMAT11, *((LPDWORD)(&TextureStage(i).m_BumpEnvMat11)));
}
for (i = 0; i < g_pHardwareConfig->ActualCaps().m_NumSamplers; ++i)
{
SamplerState(i).m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE;
SamplerState(i).m_UTexWrap = D3DTADDRESS_WRAP;
SamplerState(i).m_VTexWrap = D3DTADDRESS_WRAP;
SamplerState(i).m_WTexWrap = D3DTADDRESS_WRAP;
SamplerState(i).m_MagFilter = D3DTEXF_POINT;
SamplerState(i).m_MinFilter = D3DTEXF_POINT;
SamplerState(i).m_MipFilter = D3DTEXF_NONE;
SamplerState(i).m_FinestMipmapLevel = 0;
SamplerState(i).m_LodBias = 0.0f;
SamplerState(i).m_TextureEnable = false;
SamplerState(i).m_SRGBReadEnable = false;
// Just some initial state...
Dx9Device()->SetTexture(i, 0);
SetSamplerState(i, D3DSAMP_ADDRESSU, SamplerState(i).m_UTexWrap);
SetSamplerState(i, D3DSAMP_ADDRESSV, SamplerState(i).m_VTexWrap);
SetSamplerState(i, D3DSAMP_ADDRESSW, SamplerState(i).m_WTexWrap);
SetSamplerState(i, D3DSAMP_MINFILTER, SamplerState(i).m_MinFilter);
SetSamplerState(i, D3DSAMP_MAGFILTER, SamplerState(i).m_MagFilter);
SetSamplerState(i, D3DSAMP_MIPFILTER, SamplerState(i).m_MipFilter);
SetSamplerState(i, D3DSAMP_MAXMIPLEVEL, SamplerState(i).m_FinestMipmapLevel);
SetSamplerState(i, D3DSAMP_MIPMAPLODBIAS, SamplerState(i).m_LodBias);
SetSamplerState(i, D3DSAMP_BORDERCOLOR, RGB(0, 0, 0));
}
// FIXME!!!!! : This barfs with the debug runtime on 6800.
for (i = 0; i < g_pHardwareConfig->ActualCaps().m_nVertexTextureCount; i++)
{
m_DynamicState.m_VertexTextureState[i].m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE;
Dx9Device()->SetTexture(D3DVERTEXTEXTURESAMPLER0 + i, NULL);
m_DynamicState.m_VertexTextureState[i].m_UTexWrap = D3DTADDRESS_CLAMP;
m_DynamicState.m_VertexTextureState[i].m_VTexWrap = D3DTADDRESS_CLAMP;
// m_DynamicState.m_VertexTextureState[i].m_WTexWrap = D3DTADDRESS_CLAMP;
m_DynamicState.m_VertexTextureState[i].m_MinFilter = D3DTEXF_POINT;
m_DynamicState.m_VertexTextureState[i].m_MagFilter = D3DTEXF_POINT;
m_DynamicState.m_VertexTextureState[i].m_MipFilter = D3DTEXF_POINT;
SetSamplerState(D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSU, m_DynamicState.m_VertexTextureState[i].m_UTexWrap);
SetSamplerState(D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSV, m_DynamicState.m_VertexTextureState[i].m_VTexWrap);
// SetSamplerState( D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_ADDRESSW, m_DynamicState.m_VertexTextureState[i].m_WTexWrap );
SetSamplerState(D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MINFILTER, m_DynamicState.m_VertexTextureState[i].m_MinFilter);
SetSamplerState(D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MAGFILTER, m_DynamicState.m_VertexTextureState[i].m_MagFilter);
SetSamplerState(D3DVERTEXTEXTURESAMPLER0 + i, D3DSAMP_MIPFILTER, m_DynamicState.m_VertexTextureState[i].m_MipFilter);
}
m_DynamicState.m_NumLights = 0;
for (i = 0; i < MAX_NUM_LIGHTS; ++i)
{
m_DynamicState.m_LightEnable[i] = false;
m_DynamicState.m_LightChanged[i] = STATE_CHANGED;
m_DynamicState.m_LightEnableChanged[i] = STATE_CHANGED;
}
for (i = 0; i < NUM_MATRIX_MODES; ++i)
{
// By setting this to *not* be identity, we force an update...
m_DynamicState.m_TransformType[i] = TRANSFORM_IS_GENERAL;
m_DynamicState.m_TransformChanged[i] = STATE_CHANGED;
}
// set the board state to match the default state
m_TransitionTable.UseDefaultState();
// Set the default render state
SetDefaultState();
// Constant for all time
SetSupportedRenderState(D3DRS_CLIPPING, TRUE);
SetSupportedRenderState(D3DRS_LOCALVIEWER, TRUE);
SetSupportedRenderState(D3DRS_POINTSCALEENABLE, FALSE);
SetSupportedRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
SetSupportedRenderState(D3DRS_SPECULARMATERIALSOURCE, D3DMCS_COLOR2);
SetSupportedRenderState(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
SetSupportedRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL);
SetSupportedRenderState(D3DRS_COLORVERTEX, TRUE); // This defaults to true anyways. . .
// Set a default identity material.
SetDefaultMaterial();
#if 0
float fBias = -1.0f;
SetTextureStageState(0, D3DTSS_MIPMAPLODBIAS, *((LPDWORD)(&fBias)));
SetTextureStageState(1, D3DTSS_MIPMAPLODBIAS, *((LPDWORD)(&fBias)));
SetTextureStageState(2, D3DTSS_MIPMAPLODBIAS, *((LPDWORD)(&fBias)));
SetTextureStageState(3, D3DTSS_MIPMAPLODBIAS, *((LPDWORD)(&fBias)));
#endif
if (bFullReset)
{
// Set the modelview matrix to identity too
for (i = 0; i < NUM_MODEL_TRANSFORMS; ++i)
{
SetIdentityMatrix(m_boneMatrix[i]);
}
MatrixMode(MATERIAL_VIEW);
LoadIdentity();
MatrixMode(MATERIAL_PROJECTION);
LoadIdentity();
}
#ifdef _X360
m_DynamicState.m_bBuffer2Frames = m_bBuffer2FramesAhead;
SetRenderState(D3DRS_BUFFER2FRAMES, m_DynamicState.m_bBuffer2Frames);
#endif
m_DynamicState.m_Viewport.X = m_DynamicState.m_Viewport.Y =
m_DynamicState.m_Viewport.Width = m_DynamicState.m_Viewport.Height = 0xFFFFFFFF;
m_DynamicState.m_Viewport.MinZ = m_DynamicState.m_Viewport.MaxZ = 0.0;
// Be sure scissoring is off
m_DynamicState.m_RenderState[D3DRS_SCISSORTESTENABLE] = FALSE;
SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
m_DynamicState.m_ScissorRect.left = -1;
m_DynamicState.m_ScissorRect.top = -1;
m_DynamicState.m_ScissorRect.right = -1;
m_DynamicState.m_ScissorRect.bottom = -1;
//SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE );
EnableFastClip(false);
float fFakePlane[4];
unsigned int iFakePlaneVal = 0xFFFFFFFF;
fFakePlane[0] = fFakePlane[1] = fFakePlane[2] = fFakePlane[3] = *((float*)&iFakePlaneVal);
SetFastClipPlane(fFakePlane); //doing this to better wire up plane change detection
float zero[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
// Make sure that our state is dirty.
m_DynamicState.m_UserClipPlaneEnabled = 0;
m_DynamicState.m_UserClipPlaneChanged = 0;
m_DynamicState.m_UserClipLastUpdatedUsingFixedFunction = false;
for (i = 0; i < g_pHardwareConfig->MaxUserClipPlanes(); i++)
{
// Make sure that our state is dirty.
m_DynamicState.m_UserClipPlaneWorld[i][0] = -1.0f;
m_DynamicState.m_UserClipPlaneProj[i][0] = -9999.0f;
m_DynamicState.m_UserClipPlaneEnabled |= (1 << i);
SetClipPlane(i, zero);
EnableClipPlane(i, false);
Assert(m_DynamicState.m_UserClipPlaneEnabled == 0);
}
Assert(m_DynamicState.m_UserClipPlaneChanged == ((1 << g_pHardwareConfig->MaxUserClipPlanes()) - 1));
m_DynamicState.m_FastClipEnabled = false;
m_DynamicState.m_bFastClipPlaneChanged = true;
// User clip override
m_DynamicState.m_bUserClipTransformOverride = false;
D3DXMatrixIdentity(&m_DynamicState.m_UserClipTransform);
// Viewport defaults to the window size
RECT windowRect;
#if !defined( DX_TO_GL_ABSTRACTION )
GetClientRect((HWND)m_hWnd, &windowRect);
#else
toglGetClientRect((VD3DHWND)m_hWnd, &windowRect);
#endif
ShaderViewport_t viewport;
viewport.Init(windowRect.left, windowRect.top,
windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
SetViewports(1, &viewport);
// No render mesh
m_pRenderMesh = 0;
// Reset cached vertex decl
m_DynamicState.m_pVertexDecl = NULL;
// Reset the render target to be the normal backbuffer
if (IsX360())
{
m_hCachedRenderTarget = INVALID_SHADERAPI_TEXTURE_HANDLE;
m_bUsingSRGBRenderTarget = false;
}
AcquireInternalRenderTargets();
SetRenderTarget();
// Maintain vertex + pixel shader constant buffers
Vector4D* pVectorPixelShaderConstants = m_DesiredState.m_pVectorPixelShaderConstant;
int* pBooleanPixelShaderConstants = m_DesiredState.m_pBooleanPixelShaderConstant;
IntVector4D* pIntegerPixelShaderConstants = m_DesiredState.m_pIntegerPixelShaderConstant;
Vector4D* pVectorVertexShaderConstants = m_DesiredState.m_pVectorVertexShaderConstant;
int* pBooleanVertexShaderConstants = m_DesiredState.m_pBooleanVertexShaderConstant;
IntVector4D* pIntegerVertexShaderConstants = m_DesiredState.m_pIntegerVertexShaderConstant;
m_DesiredState = m_DynamicState;
m_DesiredState.m_pVectorPixelShaderConstant = pVectorPixelShaderConstants;
m_DesiredState.m_pBooleanPixelShaderConstant = pBooleanPixelShaderConstants;
m_DesiredState.m_pIntegerPixelShaderConstant = pIntegerPixelShaderConstants;
m_DesiredState.m_pVectorVertexShaderConstant = pVectorVertexShaderConstants;
m_DesiredState.m_pBooleanVertexShaderConstant = pBooleanVertexShaderConstants;
m_DesiredState.m_pIntegerVertexShaderConstant = pIntegerVertexShaderConstants;
if (g_pHardwareConfig->Caps().m_SupportsPixelShaders)
{
if (!bFullReset)
{
//Full resets init the values to defaults. Normal resets just leave them dirty.
if (g_pHardwareConfig->Caps().m_NumVertexShaderConstants != 0)
SetVertexShaderConstant(0, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), IsX360() ? 217 : g_pHardwareConfig->Caps().m_NumVertexShaderConstants, true); //217 on X360 to play nice with fast blatting code
if (g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants != 0)
SetIntegerVertexShaderConstant(0, (int*)m_DesiredState.m_pIntegerVertexShaderConstant, g_pHardwareConfig->Caps().m_NumIntegerVertexShaderConstants, true);
if (g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants != 0)
SetBooleanVertexShaderConstant(0, m_DesiredState.m_pBooleanVertexShaderConstant, g_pHardwareConfig->Caps().m_NumBooleanVertexShaderConstants, true);
if (g_pHardwareConfig->Caps().m_NumPixelShaderConstants != 0)
SetPixelShaderConstant(0, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), g_pHardwareConfig->Caps().m_NumPixelShaderConstants, true);
if (g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants != 0)
SetIntegerPixelShaderConstant(0, (int*)m_DesiredState.m_pIntegerPixelShaderConstant, g_pHardwareConfig->Caps().m_NumIntegerPixelShaderConstants, true);
if (g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants != 0)
SetBooleanPixelShaderConstant(0, m_DesiredState.m_pBooleanPixelShaderConstant, g_pHardwareConfig->Caps().m_NumBooleanPixelShaderConstants, true);
}
}
RECORD_DEBUG_STRING("END ResetRenderState");
m_bResettingRenderState = false;
}
//-----------------------------------------------------------------------------
// Sets the default render state
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetDefaultState()
{
LOCK_SHADERAPI();
// NOTE: This used to be in the material system, but I want to avoid all the per pass/batch
// virtual function calls.
int numTextureStages = g_pHardwareConfig->GetTextureStageCount();
// FIXME: This is a brutal hack. We only need to load these transforms for fixed-function
// hardware. Cap the max here to 4.
if (IsPC())
{
numTextureStages = min(numTextureStages, 4);
int i;
for (i = 0; i < numTextureStages; i++)
{
CShaderAPIDx8::DisableTextureTransform((TextureStage_t)i);
CShaderAPIDx8::MatrixMode((MaterialMatrixMode_t)(MATERIAL_TEXTURE0 + i));
CShaderAPIDx8::LoadIdentity();
}
}
CShaderAPIDx8::MatrixMode(MATERIAL_MODEL);
CShaderAPIDx8::Color4ub(255, 255, 255, 255);
CShaderAPIDx8::ShadeMode(SHADER_SMOOTH);
CShaderAPIDx8::SetVertexShaderIndex();
CShaderAPIDx8::SetPixelShaderIndex();
MeshMgr()->MarkUnusedVertexFields(0, 0, NULL);
}
//-----------------------------------------------------------------------------
//
// Methods related to vertex format
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Sets the vertex
//-----------------------------------------------------------------------------
inline void CShaderAPIDx8::SetVertexDecl(VertexFormat_t vertexFormat, bool bHasColorMesh, bool bUsingFlex, bool bUsingMorph)
{
VPROF("CShaderAPIDx8::SetVertexDecl");
IDirect3DVertexDeclaration9* pDecl = FindOrCreateVertexDecl(vertexFormat, bHasColorMesh, bUsingFlex, bUsingMorph);
Assert(pDecl);
if ((pDecl != m_DynamicState.m_pVertexDecl) && pDecl)
{
Dx9Device()->SetVertexDeclaration(pDecl);
m_DynamicState.m_pVertexDecl = pDecl;
}
}
//-----------------------------------------------------------------------------
//
// Methods related to vertex buffers
//
//-----------------------------------------------------------------------------
IMesh* CShaderAPIDx8::GetFlexMesh()
{
LOCK_SHADERAPI();
return MeshMgr()->GetFlexMesh();
}
//-----------------------------------------------------------------------------
// Gets the dynamic mesh
//-----------------------------------------------------------------------------
IMesh* CShaderAPIDx8::GetDynamicMesh(IMaterial* pMaterial, int nHWSkinBoneCount, bool buffered,
IMesh* pVertexOverride, IMesh* pIndexOverride)
{
Assert((pMaterial == NULL) || ((IMaterialInternal*)pMaterial)->IsRealTimeVersion());
LOCK_SHADERAPI();
return MeshMgr()->GetDynamicMesh(pMaterial, 0, nHWSkinBoneCount, buffered, pVertexOverride, pIndexOverride);
}
IMesh* CShaderAPIDx8::GetDynamicMeshEx(IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount,
bool bBuffered, IMesh* pVertexOverride, IMesh* pIndexOverride)
{
Assert((pMaterial == NULL) || ((IMaterialInternal*)pMaterial)->IsRealTimeVersion());
LOCK_SHADERAPI();
return MeshMgr()->GetDynamicMesh(pMaterial, vertexFormat, nHWSkinBoneCount, bBuffered, pVertexOverride, pIndexOverride);
}
//-----------------------------------------------------------------------------
// Returns the number of vertices we can render using the dynamic mesh
//-----------------------------------------------------------------------------
void CShaderAPIDx8::GetMaxToRender(IMesh* pMesh, bool bMaxUntilFlush, int* pMaxVerts, int* pMaxIndices)
{
LOCK_SHADERAPI();
MeshMgr()->GetMaxToRender(pMesh, bMaxUntilFlush, pMaxVerts, pMaxIndices);
}
int CShaderAPIDx8::GetMaxVerticesToRender(IMaterial* pMaterial)
{
pMaterial = ((IMaterialInternal*)pMaterial)->GetRealTimeVersion(); //always work with the realtime version internally
LOCK_SHADERAPI();
return MeshMgr()->GetMaxVerticesToRender(pMaterial);
}
int CShaderAPIDx8::GetMaxIndicesToRender()
{
LOCK_SHADERAPI();
return MeshMgr()->GetMaxIndicesToRender();
}
void CShaderAPIDx8::MarkUnusedVertexFields(unsigned int nFlags, int nTexCoordCount, bool* pUnusedTexCoords)
{
LOCK_SHADERAPI();
MeshMgr()->MarkUnusedVertexFields(nFlags, nTexCoordCount, pUnusedTexCoords);
}
//-----------------------------------------------------------------------------
// Draws the mesh
//-----------------------------------------------------------------------------
void CShaderAPIDx8::DrawMesh(CMeshBase* pMesh)
{
VPROF("CShaderAPIDx8::DrawMesh");
if (ShaderUtil()->GetConfig().m_bSuppressRendering)
return;
#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD )
PIXifyName(s_pPIXMaterialName, sizeof(s_pPIXMaterialName), m_pMaterial->GetName());
BeginPIXEvent(PIX_VALVE_ORANGE, s_pPIXMaterialName);
#endif
m_pRenderMesh = pMesh;
VertexFormat_t vertexFormat = m_pRenderMesh->GetVertexFormat();
SetVertexDecl(vertexFormat, m_pRenderMesh->HasColorMesh(), m_pRenderMesh->HasFlexMesh(), m_pMaterial->IsUsingVertexID());
CommitStateChanges();
Assert(m_pRenderMesh && m_pMaterial);
m_pMaterial->DrawMesh(CompressionType(vertexFormat));
m_pRenderMesh = NULL;
#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD )
EndPIXEvent();
#endif
}
void CShaderAPIDx8::DrawWithVertexAndIndexBuffers(void)
{
VPROF("CShaderAPIDx8::DrawWithVertexAndIndexBuffers");
if (ShaderUtil()->GetConfig().m_bSuppressRendering)
return;
#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD )
PIXifyName(s_pPIXMaterialName, sizeof(s_pPIXMaterialName), m_pMaterial->GetName());
BeginPIXEvent(PIX_VALVE_ORANGE, s_pPIXMaterialName);
#endif
// m_pRenderMesh = pMesh;
// FIXME: need to make this deal with multiple streams, etc.
VertexFormat_t vertexFormat = MeshMgr()->GetCurrentVertexFormat();
SetVertexDecl(vertexFormat, false /*m_pRenderMesh->HasColorMesh()*/,
false /*m_pRenderMesh->HasFlexMesh()*/, false /*m_pRenderMesh->IsUsingMorphData()*/);
CommitStateChanges();
if (m_pMaterial)
{
m_pMaterial->DrawMesh(CompressionType(vertexFormat));
}
else
{
MeshMgr()->RenderPassWithVertexAndIndexBuffers();
}
// m_pRenderMesh = NULL;
#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD )
EndPIXEvent();
#endif
}
//-----------------------------------------------------------------------------
// Discards the vertex buffers
//-----------------------------------------------------------------------------
void CShaderAPIDx8::DiscardVertexBuffers()
{
MeshMgr()->DiscardVertexBuffers();
}
void CShaderAPIDx8::ForceHardwareSync_WithManagedTexture()
{
if (IsX360() || !m_pFrameSyncTexture)
return;
// Set the default state for everything so we don't get more than we ask for here!
SetDefaultState();
D3DLOCKED_RECT rect;
tmZone(TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__);
HRESULT hr = m_pFrameSyncTexture->LockRect(0, &rect, NULL, 0);
if (SUCCEEDED(hr))
{
// modify..
unsigned long* pData = (unsigned long*)rect.pBits;
(*pData)++;
m_pFrameSyncTexture->UnlockRect(0);
// Now draw something with this texture.
DWORD iStage = 0;
IDirect3DBaseTexture9* pOldTexture;
hr = Dx9Device()->GetTexture(iStage, &pOldTexture);
if (SUCCEEDED(hr))
{
Dx9Device()->SetTexture(iStage, m_pFrameSyncTexture);
// Remember the old FVF.
DWORD oldFVF;
hr = Dx9Device()->GetFVF(&oldFVF);
if (SUCCEEDED(hr))
{
// Set the new FVF.
Dx9Device()->SetFVF(D3DFVF_XYZ);
// Now, draw the simplest primitive D3D has ever seen.
unsigned short indices[3] = { 0, 1, 2 };
Vector verts[3] = { vec3_origin, vec3_origin, vec3_origin };
tmZone(TELEMETRY_LEVEL1, TMZF_NONE, "DrawIndexedPrimitiveUP");
Dx9Device()->DrawIndexedPrimitiveUP(
D3DPT_TRIANGLELIST,
0, // Min vertex index
3, // Num vertices used
1, // # primitives
indices, // indices
D3DFMT_INDEX16, // index format
verts, // Vertices
sizeof(Vector)// Vertex stride
);
Dx9Device()->SetFVF(oldFVF);
}
Dx9Device()->SetTexture(iStage, pOldTexture);
}
}
// If this assert fails, then we failed somewhere above.
AssertOnce(SUCCEEDED(hr));
}
void CShaderAPIDx8::UpdateFrameSyncQuery(int queryIndex, bool bIssue)
{
Assert(queryIndex < NUM_FRAME_SYNC_QUERIES);
// wait if already issued
if (m_bQueryIssued[queryIndex])
{
tmZone(TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__);
double flStartTime = Plat_FloatTime();
BOOL dummyData = 0;
HRESULT hr = S_OK;
// NOTE: This fix helps out motherboards that are a little freaky.
// On such boards, sometimes the driver has to reset itself (an event which takes several seconds)
// and when that happens, the frame sync query object gets lost
for (;;)
{
hr = m_pFrameSyncQueryObject[queryIndex]->GetData(&dummyData, sizeof(dummyData), D3DGETDATA_FLUSH);
if (hr != S_FALSE)
break;
double flCurrTime = Plat_FloatTime();
// don't wait more than 200ms (5fps) for these
if (flCurrTime - flStartTime > 0.200f)
break;
// Avoid burning a full core while waiting for the query. Spinning can actually harm performance
// because there might be driver threads that are trying to do work that end up starved, and the
// power drawn by the CPU may take away from the power available to the integrated graphics chip.
// A sleep of one millisecond should never be long enough to affect performance, especially since
// this should only trigger when the CPU is already ahead of the GPU.
// On L4D2/TF2 in GL mode this spinning was causing slowdowns.
ThreadSleep(1);
}
m_bQueryIssued[queryIndex] = false;
Assert(hr == S_OK || hr == D3DERR_DEVICELOST);
if (hr == D3DERR_DEVICELOST)
{
MarkDeviceLost();
return;
}
}
if (bIssue)
{
m_pFrameSyncQueryObject[queryIndex]->Issue(D3DISSUE_END);
m_bQueryIssued[queryIndex] = true;
}
}
void CShaderAPIDx8::ForceHardwareSync(void)
{
LOCK_SHADERAPI();
VPROF("CShaderAPIDx8::ForceHardwareSync");
#ifdef DX_TO_GL_ABSTRACTION
if (true)
#else
if (!mat_frame_sync_enable.GetInt())
#endif
return;
// need to flush the dynamic buffer and make sure the entire image is there
FlushBufferedPrimitives();
RECORD_COMMAND(DX8_HARDWARE_SYNC, 0);
#if !defined( _X360 )
// How do you query dx9 for how many frames behind the hardware is or, alternatively, how do you tell the hardware to never be more than N frames behind?
// 1) The old QueryPendingFrameCount design was removed. It was
// a simple transaction with the driver through the
// GetDriverState, trivial for the drivers to lie. We came up
// with a much better scheme for tracking pending frames where
// the driver can not lie without a possible performance loss:
// use the asynchronous query system with D3DQUERYTYPE_EVENT and
// data size 0. When GetData returns S_OK for the query, you
// know that frame has finished.
if (mat_frame_sync_force_texture.GetBool())
{
ForceHardwareSync_WithManagedTexture();
}
else if (m_pFrameSyncQueryObject[0])
{
// FIXME: Could install a callback into the materialsystem to do something while waiting for
// the frame to finish (update sound, etc.)
// Disable VCR mode here or else it'll screw up (and we don't really care if this part plays back in exactly the same amount of time).
VCRSetEnabled(false);
m_currentSyncQuery++;
if (m_currentSyncQuery >= ARRAYSIZE(m_pFrameSyncQueryObject))
{
m_currentSyncQuery = 0;
}
double fStart = Plat_FloatTime();
int waitIndex = ((m_currentSyncQuery + NUM_FRAME_SYNC_QUERIES) - (NUM_FRAME_SYNC_FRAMES_LATENCY + 1)) % NUM_FRAME_SYNC_QUERIES;
UpdateFrameSyncQuery(waitIndex, false);
UpdateFrameSyncQuery(m_currentSyncQuery, true);
VCRSetEnabled(true);
}
#else
DWORD hFence = Dx9Device()->InsertFence();
Dx9Device()->BlockOnFence(hFence);
#endif
}
//-----------------------------------------------------------------------------
// Needs render state
//-----------------------------------------------------------------------------
void CShaderAPIDx8::QueueResetRenderState()
{
m_bResetRenderStateNeeded = true;
}
//-----------------------------------------------------------------------------
// Use this to begin and end the frame
//-----------------------------------------------------------------------------
void CShaderAPIDx8::BeginFrame()
{
LOCK_SHADERAPI();
if (m_bResetRenderStateNeeded)
{
ResetRenderState(false);
m_bResetRenderStateNeeded = false;
}
#if ALLOW_SMP_ACCESS
Dx9Device()->SetASyncMode(mat_use_smp.GetInt() != 0);
#endif
++m_CurrentFrame;
m_nTextureMemoryUsedLastFrame = 0;
}
void CShaderAPIDx8::EndFrame()
{
LOCK_SHADERAPI();
#if !defined( _X360 )
MEMCHECK;
#endif
ExportTextureList();
}
void CShaderAPIDx8::AddBufferToTextureList(const char* pName, D3DSURFACE_DESC& desc)
{
// ImageFormat imageFormat;
// imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format );
// if( imageFormat < 0 )
// {
// Assert( 0 );
// return;
// }
KeyValues* pSubKey = m_pDebugTextureList->CreateNewKey();
pSubKey->SetString("Name", pName);
pSubKey->SetString("TexGroup", TEXTURE_GROUP_RENDER_TARGET);
pSubKey->SetInt("Size",
// ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height );
4 * desc.Width * desc.Height);
pSubKey->SetString("Format", "32 bit buffer (hack)");//ImageLoader::GetName( imageFormat ) );
pSubKey->SetInt("Width", desc.Width);
pSubKey->SetInt("Height", desc.Height);
pSubKey->SetInt("BindsMax", 1);
pSubKey->SetInt("BindsFrame", 1);
}
void CShaderAPIDx8::ExportTextureList()
{
if (!m_bEnableDebugTextureList)
return;
if (!m_pBackBufferSurface || !m_pZBufferSurface)
// Device vanished...
return;
m_nDebugDataExportFrame = m_CurrentFrame;
if (IsPC() || !IsX360())
{
if (m_pDebugTextureList)
m_pDebugTextureList->deleteThis();
m_pDebugTextureList = new KeyValues("TextureList");
m_nTextureMemoryUsedTotal = 0;
m_nTextureMemoryUsedPicMip1 = 0;
m_nTextureMemoryUsedPicMip2 = 0;
for (ShaderAPITextureHandle_t hTexture = m_Textures.Head(); hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next(hTexture))
{
Texture_t& tex = m_Textures[hTexture];
if (!(tex.m_Flags & Texture_t::IS_ALLOCATED))
continue;
// Compute total texture memory usage
m_nTextureMemoryUsedTotal += tex.GetMemUsage();
// Compute picmip memory usage
{
int numBytes = tex.GetMemUsage();
if (tex.m_NumLevels > 1)
{
if (tex.GetWidth() > 4 || tex.GetHeight() > 4 || tex.GetDepth() > 4)
{
int topmipsize = ImageLoader::GetMemRequired(tex.GetWidth(), tex.GetHeight(), tex.GetDepth(), tex.GetImageFormat(), false);
numBytes -= topmipsize;
m_nTextureMemoryUsedPicMip1 += numBytes;
if (tex.GetWidth() > 8 || tex.GetHeight() > 8 || tex.GetDepth() > 8)
{
int othermipsizeRatio = ((tex.GetWidth() > 8) ? 2 : 1) * ((tex.GetHeight() > 8) ? 2 : 1) * ((tex.GetDepth() > 8) ? 2 : 1);
int othermipsize = topmipsize / othermipsizeRatio;
numBytes -= othermipsize;
}
m_nTextureMemoryUsedPicMip1 += numBytes;
}
else
{
m_nTextureMemoryUsedPicMip1 += numBytes;
m_nTextureMemoryUsedPicMip2 += numBytes;
}
}
else
{
m_nTextureMemoryUsedPicMip1 += numBytes;
m_nTextureMemoryUsedPicMip2 += numBytes;
}
}
if (!m_bDebugGetAllTextures &&
tex.m_LastBoundFrame != m_CurrentFrame)
continue;
if (tex.m_LastBoundFrame != m_CurrentFrame)
tex.m_nTimesBoundThisFrame = 0;
KeyValues* pSubKey = m_pDebugTextureList->CreateNewKey();
pSubKey->SetString("Name", tex.m_DebugName.String());
pSubKey->SetString("TexGroup", tex.m_TextureGroupName.String());
pSubKey->SetInt("Size", tex.GetMemUsage());
if (tex.GetCount() > 1)
pSubKey->SetInt("Count", tex.GetCount());
pSubKey->SetString("Format", ImageLoader::GetName(tex.GetImageFormat()));
pSubKey->SetInt("Width", tex.GetWidth());
pSubKey->SetInt("Height", tex.GetHeight());
pSubKey->SetInt("BindsMax", tex.m_nTimesBoundMax);
pSubKey->SetInt("BindsFrame", tex.m_nTimesBoundThisFrame);
}
D3DSURFACE_DESC desc;
m_pBackBufferSurface->GetDesc(&desc);
AddBufferToTextureList("BACKBUFFER", desc);
AddBufferToTextureList("FRONTBUFFER", desc);
// ImageFormat imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format );
// if( imageFormat >= 0 )
{
VPROF_INCREMENT_GROUP_COUNTER("TexGroup_frame_" TEXTURE_GROUP_RENDER_TARGET,
COUNTER_GROUP_TEXTURE_PER_FRAME,
// ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height );
2 * 4 * desc.Width * desc.Height); // hack (times 2 for front and back buffer)
}
m_pZBufferSurface->GetDesc(&desc);
AddBufferToTextureList("DEPTHBUFFER", desc);
// imageFormat = ImageLoader::D3DFormatToImageFormat( desc.Format );
// if( imageFormat >= 0 )
{
VPROF_INCREMENT_GROUP_COUNTER("TexGroup_frame_" TEXTURE_GROUP_RENDER_TARGET,
COUNTER_GROUP_TEXTURE_PER_FRAME,
// ImageLoader::SizeInBytes( imageFormat ) * desc.Width * desc.Height );
4 * desc.Width * desc.Height); // hack
}
}
#if defined( _X360 )
// toggle to do one shot transmission
m_bEnableDebugTextureList = false;
int numTextures = m_Textures.Count() + 3;
xTextureList_t* pXTextureList = (xTextureList_t*)_alloca(numTextures * sizeof(xTextureList_t));
memset(pXTextureList, 0, numTextures * sizeof(xTextureList_t));
numTextures = 0;
for (ShaderAPITextureHandle_t hTexture = m_Textures.Head(); hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next(hTexture))
{
Texture_t& tex = m_Textures[hTexture];
if (!m_bDebugGetAllTextures && tex.m_LastBoundFrame != m_CurrentFrame)
{
continue;
}
if (!(tex.m_Flags & Texture_t::IS_ALLOCATED))
{
continue;
}
int refCount;
if (tex.m_Flags & Texture_t::IS_DEPTH_STENCIL)
{
// interface forces us to ignore these
refCount = -1;
}
else
{
refCount = GetD3DTextureRefCount(CShaderAPIDx8::GetD3DTexture(hTexture));
}
pXTextureList[numTextures].pName = tex.m_DebugName.String();
pXTextureList[numTextures].size = tex.m_SizeBytes * tex.m_NumCopies;
pXTextureList[numTextures].pGroupName = tex.m_TextureGroupName.String();
pXTextureList[numTextures].pFormatName = D3DFormatName(ImageLoader::ImageFormatToD3DFormat(tex.GetImageFormat()));
pXTextureList[numTextures].width = tex.GetWidth();
pXTextureList[numTextures].height = tex.GetHeight();
pXTextureList[numTextures].depth = tex.GetDepth();
pXTextureList[numTextures].numLevels = tex.m_NumLevels;
pXTextureList[numTextures].binds = tex.m_nTimesBoundThisFrame;
pXTextureList[numTextures].refCount = refCount;
pXTextureList[numTextures].edram = (tex.m_Flags & Texture_t::IS_RENDER_TARGET_SURFACE) != 0;
pXTextureList[numTextures].procedural = tex.m_NumCopies > 1;
pXTextureList[numTextures].final = (tex.m_Flags & Texture_t::IS_FINALIZED) != 0;
pXTextureList[numTextures].failed = (tex.m_Flags & Texture_t::IS_FAILED) != 0;
numTextures++;
}
// build special entries for implicit surfaces/textures
D3DSURFACE_DESC desc;
m_pBackBufferSurface->GetDesc(&desc);
int size = ImageLoader::GetMemRequired(
desc.Width,
desc.Height,
0,
ImageLoader::D3DFormatToImageFormat(desc.Format),
false);
pXTextureList[numTextures].pName = "_rt_BackBuffer";
pXTextureList[numTextures].size = size;
pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET_SURFACE;
pXTextureList[numTextures].pFormatName = D3DFormatName(desc.Format);
pXTextureList[numTextures].width = desc.Width;
pXTextureList[numTextures].height = desc.Height;
pXTextureList[numTextures].depth = 1;
pXTextureList[numTextures].binds = 1;
pXTextureList[numTextures].refCount = 1;
pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB(desc.Format);
pXTextureList[numTextures].edram = true;
numTextures++;
m_pZBufferSurface->GetDesc(&desc);
pXTextureList[numTextures].pName = "_rt_DepthBuffer";
pXTextureList[numTextures].size = size;
pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET_SURFACE;
pXTextureList[numTextures].pFormatName = D3DFormatName(desc.Format);
pXTextureList[numTextures].width = desc.Width;
pXTextureList[numTextures].height = desc.Height;
pXTextureList[numTextures].depth = 1;
pXTextureList[numTextures].binds = 1;
pXTextureList[numTextures].refCount = 1;
pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB(desc.Format);
pXTextureList[numTextures].edram = true;
numTextures++;
// front buffer resides in DDR
pXTextureList[numTextures].pName = "_rt_FrontBuffer";
pXTextureList[numTextures].size = size;
pXTextureList[numTextures].pGroupName = TEXTURE_GROUP_RENDER_TARGET;
pXTextureList[numTextures].pFormatName = D3DFormatName(desc.Format);
pXTextureList[numTextures].width = desc.Width;
pXTextureList[numTextures].height = desc.Height;
pXTextureList[numTextures].depth = 1;
pXTextureList[numTextures].binds = 1;
pXTextureList[numTextures].refCount = 1;
pXTextureList[numTextures].sRGB = IS_D3DFORMAT_SRGB(desc.Format);
numTextures++;
int totalMemory = 0;
for (int i = 0; i < numTextures; i++)
{
if (pXTextureList[i].edram)
{
// skip edram based items
continue;
}
totalMemory += pXTextureList[i].size;
}
Msg("Total D3D Texture Memory: %.2f MB\n", (float)totalMemory / (1024.0f * 1024.0f));
// transmit to console
XBX_rTextureList(numTextures, pXTextureList);
#endif
}
//-----------------------------------------------------------------------------
// Releases/reloads resources when other apps want some memory
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ReleaseShaderObjects()
{
ReleaseInternalRenderTargets();
EvictManagedResourcesInternal();
// FIXME: Move into shaderdevice when textures move over.
#ifdef _DEBUG
// Helps to find the unreleased textures.
if (TextureCount() > 0)
{
ShaderAPITextureHandle_t hTexture;
for (hTexture = m_Textures.Head(); hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next(hTexture))
{
if (GetTexture(hTexture).m_NumCopies == 1)
{
if (GetTexture(hTexture).GetTexture())
{
Warning("Didn't correctly clean up texture 0x%8.8x (%s)\n", hTexture, GetTexture(hTexture).m_DebugName.String());
}
}
else
{
for (int k = GetTexture(hTexture).m_NumCopies; --k >= 0; )
{
if (GetTexture(hTexture).GetTexture(k) != 0)
{
Warning("Didn't correctly clean up texture 0x%8.8x (%s)\n", hTexture, GetTexture(hTexture).m_DebugName.String());
break;
}
}
}
}
}
#endif
Assert(TextureCount() == 0);
}
void CShaderAPIDx8::RestoreShaderObjects()
{
AcquireInternalRenderTargets();
SetRenderTarget();
}
//--------------------------------------------------------------------
// PIX instrumentation routines
// Windows only for now. Turn these on with PIX_INSTRUMENTATION above
//--------------------------------------------------------------------
#if 0 // hack versions for OSX to be able to PIX log even when not built debug...
void CShaderAPIDx8::BeginPIXEvent(unsigned long color, const char* szName)
{
LOCK_SHADERAPI();
GLMBeginPIXEvent(szName); // direct call no macro
return;
}
void CShaderAPIDx8::EndPIXEvent(void)
{
LOCK_SHADERAPI();
GLMEndPIXEvent(); // direct call no macro
return;
}
#else
#if defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD )
ConVar pix_break_on_event("pix_break_on_event", "");
#endif
void CShaderAPIDx8::BeginPIXEvent(unsigned long color, const char* szName)
{
#if ( defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) )
//LOCK_SHADERAPI();
const char* p = pix_break_on_event.GetString();
if (p && V_strlen(p))
{
if (V_stristr(szName, p) != NULL)
{
DebuggerBreak();
}
}
#if defined ( DX_TO_GL_ABSTRACTION )
GLMBeginPIXEvent(szName);
#if defined( _WIN32 )
// AMD PerfStudio integration: Call into D3D9.DLL's D3DPERF_BeginEvent() (this gets intercepted by PerfStudio even in GL mode).
if (g_pShaderDeviceMgrDx8->m_pBeginEvent)
{
wchar_t wszName[128];
mbstowcs(wszName, szName, 128);
g_pShaderDeviceMgrDx8->m_pBeginEvent(0x2F2F2F2F, wszName);
}
#endif
#elif defined(_X360 )
#ifndef _DEBUG
char szPIXEventName[32];
PIXifyName(szPIXEventName, szName);
PIXBeginNamedEvent(color, szPIXEventName);
#endif
#else // PC
if (PIXError())
return;
wchar_t wszName[128];
mbstowcs(wszName, szName, 128);
// Fire the PIX event, trapping for errors...
if (D3DPERF_BeginEvent(color, wszName) < 0)
{
Warning("PIX error Beginning %s event\n", szName);
IncrementPIXError();
}
#endif
#endif // #if defined( PIX_INSTRUMENTATION )
}
void CShaderAPIDx8::EndPIXEvent(void)
{
#if ( defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) )
//LOCK_SHADERAPI();
#if defined ( DX_TO_GL_ABSTRACTION )
GLMEndPIXEvent();
#if defined( _WIN32 )
// AMD PerfStudio integration: Call into D3D9.DLL's D3DPERF_EndEvent() (this gets intercepted by PerfStudio even in GL mode).
if (g_pShaderDeviceMgrDx8->m_pEndEvent)
{
g_pShaderDeviceMgrDx8->m_pEndEvent();
}
#endif
#elif defined( _X360 )
#ifndef _DEBUG
PIXEndNamedEvent();
#endif
#else // PC
if (PIXError())
return;
#if !defined( NVPERFHUD )
// Fire the PIX event, trapping for errors...
if (D3DPERF_EndEvent() < 0)
{
Warning("PIX error ending event\n");
IncrementPIXError();
}
#endif
#endif
#endif // #if defined( PIX_INSTRUMENTATION )
}
#endif
void CShaderAPIDx8::AdvancePIXFrame()
{
#if defined( PIX_INSTRUMENTATION )
// Ping PIX when this bool goes from false to true
if (r_pix_start.GetBool() && (!m_bPixCapturing))
{
StartPIXInstrumentation();
m_bPixCapturing = true;
}
// If we want to record frames...
if (r_pix_recordframes.GetInt())
{
if (m_nPixFrame == 0) // First frame to record
{
StartPIXInstrumentation();
m_nPixFrame++;
}
else if (m_nPixFrame == r_pix_recordframes.GetInt()) // Last frame to record
{
EndPIXInstrumentation();
r_pix_recordframes.SetValue(0);
m_nPixFrame = 0;
}
else
{
m_nPixFrame++; // Recording frames...
}
}
#endif
}
// No begin-end for this...use this to put discrete markers in the PIX stream
void CShaderAPIDx8::SetPIXMarker(unsigned long color, const char* szName)
{
#if defined( PIX_INSTRUMENTATION )
LOCK_SHADERAPI();
#if defined( DX_TO_GL_ABSTRACTION )
if (g_pShaderDeviceMgrDx8->m_pSetMarker)
{
wchar_t wszName[128];
mbstowcs(wszName, szName, 128);
g_pShaderDeviceMgrDx8->m_pSetMarker(0x2F2F2F2F, wszName);
}
#elif defined( _X360 )
#ifndef _DEBUG
char szPIXMarkerName[32];
PIXifyName(szPIXMarkerName, szName);
PIXSetMarker(color, szPIXMarkerName);
#endif
#else // PC
if (PIXError())
return;
wchar_t wszName[128];
mbstowcs(wszName, szName, 128);
D3DPERF_SetMarker(color, wszName);
#endif
#endif // PIX_INSTRUMENTATION
}
void CShaderAPIDx8::StartPIXInstrumentation()
{
#if defined( PIX_INSTRUMENTATION )
SetPIXMarker(PIX_VALVE_ORANGE, "Valve_PIX_Capture_Start");
#endif
}
void CShaderAPIDx8::EndPIXInstrumentation()
{
#if defined( PIX_INSTRUMENTATION )
SetPIXMarker(PIX_VALVE_ORANGE, "Valve_PIX_Capture_End");
#endif
}
void CShaderAPIDx8::IncrementPIXError()
{
#if defined( PIX_INSTRUMENTATION ) && !defined( NVPERFHUD )
m_nPIXErrorCount++;
if (m_nPIXErrorCount >= MAX_PIX_ERRORS)
{
Warning("Source engine built with PIX instrumentation, but PIX doesn't seem to have been used to instantiate the game, which is necessary on PC.\n");
}
#endif
}
// Have we already hit several PIX errors?
bool CShaderAPIDx8::PIXError()
{
#if defined( PIX_INSTRUMENTATION ) && !defined( NVPERFHUD )
return m_nPIXErrorCount >= MAX_PIX_ERRORS;
#else
return false;
#endif
}
//-----------------------------------------------------------------------------
// Check for device lost
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ChangeVideoMode(const ShaderDeviceInfo_t& info)
{
if (IsX360())
return;
LOCK_SHADERAPI();
m_PendingVideoModeChangeConfig = info;
m_bPendingVideoModeChange = true;
if (info.m_DisplayMode.m_nWidth != 0 && info.m_DisplayMode.m_nHeight != 0)
{
m_nWindowWidth = info.m_DisplayMode.m_nWidth;
m_nWindowHeight = info.m_DisplayMode.m_nHeight;
}
}
//-----------------------------------------------------------------------------
// Compute fill rate
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ComputeFillRate()
{
if (IsX360())
{
// not valid
return;
}
static unsigned char* pBuf = 0;
int width, height;
GetWindowSize(width, height);
// Snapshot; look at total # pixels drawn...
if (!pBuf)
{
int memSize = ShaderUtil()->GetMemRequired(
width,
height,
1,
IMAGE_FORMAT_RGB888,
false) + 4;
pBuf = (unsigned char*)malloc(memSize);
}
ReadPixels(
0,
0,
width,
height,
pBuf,
IMAGE_FORMAT_RGB888);
int mask = 0xFF;
int count = 0;
unsigned char* pRead = pBuf;
for (int i = 0; i < height; ++i)
{
for (int j = 0; j < width; ++j)
{
int val = *(int*)pRead;
count += (val & mask);
pRead += 3;
}
}
}
//-----------------------------------------------------------------------------
// Use this to get the mesh builder that allows us to modify vertex data
//-----------------------------------------------------------------------------
CMeshBuilder* CShaderAPIDx8::GetVertexModifyBuilder()
{
return &m_ModifyBuilder;
}
bool CShaderAPIDx8::InFlashlightMode() const
{
return ShaderUtil()->InFlashlightMode();
}
bool CShaderAPIDx8::InEditorMode() const
{
return ShaderUtil()->InEditorMode();
}
//-----------------------------------------------------------------------------
// Gets the bound morph's vertex format; returns 0 if no morph is bound
//-----------------------------------------------------------------------------
MorphFormat_t CShaderAPIDx8::GetBoundMorphFormat()
{
return ShaderUtil()->GetBoundMorphFormat();
}
//-----------------------------------------------------------------------------
// returns the current time in seconds...
//-----------------------------------------------------------------------------
double CShaderAPIDx8::CurrentTime() const
{
// FIXME: Return game time instead of real time!
// Or eliminate this altogether and put it into a material var
// (this is used by vertex modifiers in shader code at the moment)
return Plat_FloatTime();
}
//-----------------------------------------------------------------------------
// Methods called by the transition table that use dynamic state...
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ApplyZBias(const ShadowState_t& shaderState)
{
MaterialSystem_Config_t& config = ShaderUtil()->GetConfig();
float a = (config.m_SlopeScaleDepthBias_Decal != 0.0f) ? 1.0f / config.m_SlopeScaleDepthBias_Decal : 0.0f;
float b = (config.m_SlopeScaleDepthBias_Normal != 0.0f) ? 1.0f / config.m_SlopeScaleDepthBias_Normal : 0.0f;
float c = (config.m_DepthBias_Decal != 0.0f) ? 1.0f / config.m_DepthBias_Decal : 0.0f;
float d = (config.m_DepthBias_Normal != 0.0f) ? 1.0f / config.m_DepthBias_Normal : 0.0f;
// FIXME: No longer necessary; may be necessary if you want to use cat 4.3 drivers?
// GR - hack for R200
bool bPS14Only = g_pHardwareConfig->Caps().m_SupportsPixelShaders_1_4 && !g_pHardwareConfig->Caps().m_SupportsPixelShaders_2_0;
if ((g_pHardwareConfig->Caps().m_VendorID == 0x1002) && bPS14Only)
{
// Slam to m_SlopeScaleDepthBias_Decal = 0, m_DepthBias_Decal = -4096
// which empirically is what appears to look good on r200
// NOTE: Slamming to 0 instead of -1.0 / 4096 because on Cat 4.9, WinXP, 8500,
// this causes the z values to be more than 50 units away from the original z values
a = 0.0f;
c = -1.0 / 4096.0;
}
// bias = (s * D3DRS_SLOPESCALEDEPTHBIAS) + D3DRS_DEPTHBIAS, where s is the maximum depth slope of the triangle being rendered
if (g_pHardwareConfig->Caps().m_ZBiasAndSlopeScaledDepthBiasSupported)
{
float fSlopeScaleDepthBias, fDepthBias;
if (shaderState.m_ZBias == SHADER_POLYOFFSET_DECAL)
{
fSlopeScaleDepthBias = a;
fDepthBias = c;
}
else if (shaderState.m_ZBias == SHADER_POLYOFFSET_SHADOW_BIAS)
{
fSlopeScaleDepthBias = m_fShadowSlopeScaleDepthBias;
fDepthBias = m_fShadowDepthBias;
}
else // assume SHADER_POLYOFFSET_DISABLE
{
fSlopeScaleDepthBias = b;
fDepthBias = d;
}
if (ReverseDepthOnX360())
{
fSlopeScaleDepthBias = -fSlopeScaleDepthBias;
fDepthBias = -fDepthBias;
}
SetRenderStateConstMacro(this, D3DRS_SLOPESCALEDEPTHBIAS, *((DWORD*)(&fSlopeScaleDepthBias)));
SetRenderStateConstMacro(this, D3DRS_DEPTHBIAS, *((DWORD*)(&fDepthBias)));
}
else
{
MarkAllUserClipPlanesDirty();
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |=
STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION;
}
}
void CShaderAPIDx8::ApplyTextureEnable(const ShadowState_t& state, int nSampler)
{
if (state.m_SamplerState[nSampler].m_TextureEnable == SamplerState(nSampler).m_TextureEnable)
return;
if (state.m_SamplerState[nSampler].m_TextureEnable)
{
SamplerState(nSampler).m_TextureEnable = true;
// Should not be necessary/possible (SetTextureState() calls D3D9/DXAbstract, so the calling thread must already own the device.
//LOCK_SHADERAPI();
// Don't do this here!! It ends up giving us extra texture sets.
// We'll Assert in debug mode if you enable a texture stage
// but don't bind a texture.
// see CShaderAPIDx8::RenderPass() for this check.
// NOTE: We aren't doing this optimization quite yet. There are situations
// where you want a texture stage enabled for its texture coordinates, but
// you don't actually bind a texture (texmvspec for example.)
SetTextureState((Sampler_t)nSampler, SamplerState(nSampler).m_BoundTexture, true);
}
else
{
SamplerState(nSampler).m_TextureEnable = false;
SetTextureState((Sampler_t)nSampler, INVALID_SHADERAPI_TEXTURE_HANDLE);
}
}
//-----------------------------------------------------------------------------
// Used to clear the transition table when we know it's become invalid.
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ClearSnapshots()
{
LOCK_SHADERAPI();
FlushBufferedPrimitives();
m_TransitionTable.Reset();
InitRenderState();
}
static void KillTranslation(D3DXMATRIX& mat)
{
mat[3] = 0.0f;
mat[7] = 0.0f;
mat[11] = 0.0f;
mat[12] = 0.0f;
mat[13] = 0.0f;
mat[14] = 0.0f;
mat[15] = 1.0f;
}
static void PrintMatrix(const char* name, const D3DXMATRIX& mat)
{
int row, col;
char buf[128];
Plat_DebugString(name);
Plat_DebugString("\n");
for (row = 0; row < 4; row++)
{
Plat_DebugString(" ");
for (col = 0; col < 4; col++)
{
sprintf(buf, "%f ", (float)mat(row, col));
Plat_DebugString(buf);
}
Plat_DebugString("\n");
}
Plat_DebugString("\n");
}
//-----------------------------------------------------------------------------
// Gets the vertex format for a particular snapshot id
//-----------------------------------------------------------------------------
VertexFormat_t CShaderAPIDx8::ComputeVertexUsage(int num, StateSnapshot_t* pIds) const
{
LOCK_SHADERAPI();
if (num == 0)
return 0;
// We don't have to all sorts of crazy stuff if there's only one snapshot
if (num == 1)
{
const ShadowShaderState_t& state = m_TransitionTable.GetSnapshotShader(pIds[0]);
return state.m_VertexUsage;
}
Assert(pIds);
// Aggregating vertex formats is a little tricky;
// For example, what do we do when two passes want user data?
// Can we assume they are the same? For now, I'm going to
// just print a warning in debug.
VertexCompressionType_t compression = VERTEX_COMPRESSION_INVALID;
int userDataSize = 0;
int numBones = 0;
int texCoordSize[VERTEX_MAX_TEXTURE_COORDINATES] = { 0, 0, 0, 0, 0, 0, 0, 0 };
int flags = 0;
for (int i = num; --i >= 0; )
{
const ShadowShaderState_t& state = m_TransitionTable.GetSnapshotShader(pIds[i]);
VertexFormat_t fmt = state.m_VertexUsage;
flags |= VertexFlags(fmt);
VertexCompressionType_t newCompression = CompressionType(fmt);
if ((compression != newCompression) && (compression != VERTEX_COMPRESSION_INVALID))
{
Warning("Encountered a material with two passes that specify different vertex compression types!\n");
compression = VERTEX_COMPRESSION_NONE; // Be safe, disable compression
}
int newNumBones = NumBoneWeights(fmt);
if ((numBones != newNumBones) && (newNumBones != 0))
{
if (numBones != 0)
{
Warning("Encountered a material with two passes that use different numbers of bones!\n");
}
numBones = newNumBones;
}
int newUserSize = UserDataSize(fmt);
if ((userDataSize != newUserSize) && (newUserSize != 0))
{
if (userDataSize != 0)
{
Warning("Encountered a material with two passes that use different user data sizes!\n");
}
userDataSize = newUserSize;
}
for (int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j)
{
int newSize = TexCoordSize((TextureStage_t)j, fmt);
if ((texCoordSize[j] != newSize) && (newSize != 0))
{
if (texCoordSize[j] != 0)
{
Warning("Encountered a material with two passes that use different texture coord sizes!\n");
}
if (texCoordSize[j] < newSize)
{
texCoordSize[j] = newSize;
}
}
}
}
return MeshMgr()->ComputeVertexFormat(flags, VERTEX_MAX_TEXTURE_COORDINATES,
texCoordSize, numBones, userDataSize);
}
VertexFormat_t CShaderAPIDx8::ComputeVertexFormat(int num, StateSnapshot_t* pIds) const
{
LOCK_SHADERAPI();
VertexFormat_t fmt = ComputeVertexUsage(num, pIds);
return fmt;
}
//-----------------------------------------------------------------------------
// What fields in the morph do we actually use?
//-----------------------------------------------------------------------------
MorphFormat_t CShaderAPIDx8::ComputeMorphFormat(int numSnapshots, StateSnapshot_t* pIds) const
{
LOCK_SHADERAPI();
MorphFormat_t format = 0;
for (int i = 0; i < numSnapshots; ++i)
{
MorphFormat_t fmt = m_TransitionTable.GetSnapshotShader(pIds[i]).m_MorphUsage;
format |= VertexFlags(fmt);
}
return format;
}
//-----------------------------------------------------------------------------
// Commits a range of vertex shader constants
//-----------------------------------------------------------------------------
static void CommitVertexShaderConstantRange(IDirect3DDevice9* pDevice, const DynamicState_t& desiredState,
DynamicState_t& currentState, bool bForce, int nFirstConstant, int nCount)
{
if (IsX360())
{
// invalid code path for 360, not coded for 360 GPU contant awareness
Assert(0);
return;
}
int nFirstCommit = nFirstConstant;
int nCommitCount = 0;
for (int i = 0; i < nCount; ++i)
{
int nVar = nFirstConstant + i;
bool bDifferentValue = bForce || (desiredState.m_pVectorVertexShaderConstant[nVar] != currentState.m_pVectorVertexShaderConstant[nVar]);
if (!bDifferentValue)
{
if (nCommitCount != 0)
{
// flush the prior range
pDevice->SetVertexShaderConstantF(nFirstCommit, desiredState.m_pVectorVertexShaderConstant[nFirstCommit].Base(), nCommitCount);
memcpy(&currentState.m_pVectorVertexShaderConstant[nFirstCommit],
&desiredState.m_pVectorVertexShaderConstant[nFirstCommit], nCommitCount * 4 * sizeof(float));
}
// start of new range
nFirstCommit = nVar + 1;
nCommitCount = 0;
}
else
{
++nCommitCount;
}
}
if (nCommitCount != 0)
{
// flush range
pDevice->SetVertexShaderConstantF(nFirstCommit, desiredState.m_pVectorVertexShaderConstant[nFirstCommit].Base(), nCommitCount);
memcpy(&currentState.m_pVectorVertexShaderConstant[nFirstCommit],
&desiredState.m_pVectorVertexShaderConstant[nFirstCommit], nCommitCount * 4 * sizeof(float));
}
}
//-----------------------------------------------------------------------------
// Gets the current buffered state... (debug only)
//-----------------------------------------------------------------------------
void CShaderAPIDx8::GetBufferedState(BufferedState_t& state)
{
memcpy(&state.m_Transform[0], &GetTransform(MATERIAL_MODEL), sizeof(D3DXMATRIX));
memcpy(&state.m_Transform[1], &GetTransform(MATERIAL_VIEW), sizeof(D3DXMATRIX));
memcpy(&state.m_Transform[2], &GetTransform(MATERIAL_PROJECTION), sizeof(D3DXMATRIX));
memcpy(&state.m_Viewport, &m_DynamicState.m_Viewport, sizeof(state.m_Viewport));
state.m_PixelShader = ShaderManager()->GetCurrentPixelShader();
state.m_VertexShader = ShaderManager()->GetCurrentVertexShader();
for (int i = 0; i < g_pHardwareConfig->GetSamplerCount(); ++i)
{
state.m_BoundTexture[i] = m_DynamicState.m_SamplerState[i].m_BoundTexture;
}
}
//-----------------------------------------------------------------------------
// constant color methods
//-----------------------------------------------------------------------------
void CShaderAPIDx8::Color3f(float r, float g, float b)
{
unsigned int color = D3DCOLOR_ARGB(255, (int)(r * 255),
(int)(g * 255), (int)(b * 255));
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
}
}
void CShaderAPIDx8::Color4f(float r, float g, float b, float a)
{
unsigned int color = D3DCOLOR_ARGB((int)(a * 255), (int)(r * 255),
(int)(g * 255), (int)(b * 255));
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
}
}
void CShaderAPIDx8::Color3fv(float const* c)
{
Assert(c);
unsigned int color = D3DCOLOR_ARGB(255, (int)(c[0] * 255),
(int)(c[1] * 255), (int)(c[2] * 255));
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
}
}
void CShaderAPIDx8::Color4fv(float const* c)
{
Assert(c);
unsigned int color = D3DCOLOR_ARGB((int)(c[3] * 255), (int)(c[0] * 255),
(int)(c[1] * 255), (int)(c[2] * 255));
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
}
}
void CShaderAPIDx8::Color3ub(unsigned char r, unsigned char g, unsigned char b)
{
unsigned int color = D3DCOLOR_ARGB(255, r, g, b);
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
}
}
void CShaderAPIDx8::Color3ubv(unsigned char const* pColor)
{
Assert(pColor);
unsigned int color = D3DCOLOR_ARGB(255, pColor[0], pColor[1], pColor[2]);
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
}
}
void CShaderAPIDx8::Color4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
unsigned int color = D3DCOLOR_ARGB(a, r, g, b);
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
}
}
void CShaderAPIDx8::Color4ubv(unsigned char const* pColor)
{
Assert(pColor);
unsigned int color = D3DCOLOR_ARGB(pColor[3], pColor[0], pColor[1], pColor[2]);
if (color != m_DynamicState.m_ConstantColor)
{
m_DynamicState.m_ConstantColor = color;
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
}
}
//-----------------------------------------------------------------------------
// The shade mode
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ShadeMode(ShaderShadeMode_t mode)
{
LOCK_SHADERAPI();
D3DSHADEMODE shadeMode = (mode == SHADER_FLAT) ? D3DSHADE_FLAT : D3DSHADE_GOURAUD;
if (m_DynamicState.m_ShadeMode != shadeMode)
{
m_DynamicState.m_ShadeMode = shadeMode;
SetRenderStateConstMacro(this, D3DRS_SHADEMODE, shadeMode);
}
}
//-----------------------------------------------------------------------------
// Buffering 2 frames ahead
//-----------------------------------------------------------------------------
void CShaderAPIDx8::EnableBuffer2FramesAhead(bool bEnable)
{
#ifdef _X360
m_bBuffer2FramesAhead = bEnable;
if (bEnable != m_DynamicState.m_bBuffer2Frames)
{
SetRenderState(D3DRS_BUFFER2FRAMES, bEnable);
m_DynamicState.m_bBuffer2Frames = bEnable;
}
#endif
}
void CShaderAPIDx8::SetDepthFeatheringPixelShaderConstant(int iConstant, float fDepthBlendScale)
{
float fConstantValues[4];
if (IsX360())
{
const D3DMATRIX& projMatrix = GetProjectionMatrix();
fConstantValues[0] = 50.0f / fDepthBlendScale;
fConstantValues[1] = 1.0f / projMatrix.m[2][2];
fConstantValues[2] = 1.0f / projMatrix.m[3][2];
fConstantValues[3] = projMatrix.m[2][2];
/*
D3DXMATRIX invProjMatrix;
D3DXMatrixInverse( &invProjMatrix, NULL, (D3DXMATRIX *)&projMatrix );
fConstantValues[1] = invProjMatrix.m[3][2];
fConstantValues[2] = invProjMatrix.m[3][3];
fConstantValues[3] = invProjMatrix.m[2][2];
*/
}
else
{
fConstantValues[0] = m_DynamicState.m_DestAlphaDepthRange / fDepthBlendScale;
fConstantValues[1] = fConstantValues[2] = fConstantValues[3] = 0.0f; //empty
}
SetPixelShaderConstant(iConstant, fConstantValues);
}
//-----------------------------------------------------------------------------
// Cull mode..
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetCullModeState(bool bEnable, D3DCULL nDesiredCullMode)
{
D3DCULL nCullMode = bEnable ? nDesiredCullMode : D3DCULL_NONE;
if (nCullMode != m_DynamicState.m_CullMode)
{
SetRenderStateConstMacro(this, D3DRS_CULLMODE, nCullMode);
m_DynamicState.m_CullMode = nCullMode;
}
}
void CShaderAPIDx8::ApplyCullEnable(bool bEnable)
{
m_DynamicState.m_bCullEnabled = bEnable;
SetCullModeState(m_DynamicState.m_bCullEnabled, m_DynamicState.m_DesiredCullMode);
}
void CShaderAPIDx8::CullMode(MaterialCullMode_t nCullMode)
{
LOCK_SHADERAPI();
D3DCULL nNewCullMode;
switch (nCullMode)
{
case MATERIAL_CULLMODE_CCW:
// Culls backfacing polys (normal)
nNewCullMode = D3DCULL_CCW;
break;
case MATERIAL_CULLMODE_CW:
// Culls frontfacing polys
nNewCullMode = D3DCULL_CW;
break;
default:
Warning("CullMode: invalid cullMode\n");
return;
}
if (m_DynamicState.m_DesiredCullMode != nNewCullMode)
{
FlushBufferedPrimitives();
m_DynamicState.m_DesiredCullMode = nNewCullMode;
SetCullModeState(m_DynamicState.m_bCullEnabled, m_DynamicState.m_DesiredCullMode);
}
}
static ConVar mat_alphacoverage("mat_alphacoverage", "1");
void CShaderAPIDx8::ApplyAlphaToCoverage(bool bEnable)
{
if (mat_alphacoverage.GetBool())
{
if (bEnable)
EnableAlphaToCoverage();
else
DisableAlphaToCoverage();
}
else
{
DisableAlphaToCoverage();
}
}
//-----------------------------------------------------------------------------
// Returns the current cull mode of the current material (for selection mode only)
//-----------------------------------------------------------------------------
D3DCULL CShaderAPIDx8::GetCullMode() const
{
Assert(m_pMaterial);
if (m_pMaterial->GetMaterialVarFlag(MATERIAL_VAR_NOCULL))
return D3DCULL_NONE;
return m_DynamicState.m_DesiredCullMode;
}
void CShaderAPIDx8::SetRasterState(const ShaderRasterState_t& state)
{
// FIXME: Implement!
}
void CShaderAPIDx8::ForceDepthFuncEquals(bool bEnable)
{
LOCK_SHADERAPI();
if (!g_pShaderDeviceDx8->IsDeactivated())
{
m_TransitionTable.ForceDepthFuncEquals(bEnable);
}
}
void CShaderAPIDx8::OverrideDepthEnable(bool bEnable, bool bDepthEnable)
{
LOCK_SHADERAPI();
if (!g_pShaderDeviceDx8->IsDeactivated())
{
m_TransitionTable.OverrideDepthEnable(bEnable, bDepthEnable);
}
}
void CShaderAPIDx8::OverrideAlphaWriteEnable(bool bOverrideEnable, bool bAlphaWriteEnable)
{
LOCK_SHADERAPI();
if (!g_pShaderDeviceDx8->IsDeactivated())
{
m_TransitionTable.OverrideAlphaWriteEnable(bOverrideEnable, bAlphaWriteEnable);
}
}
void CShaderAPIDx8::OverrideColorWriteEnable(bool bOverrideEnable, bool bColorWriteEnable)
{
LOCK_SHADERAPI();
if (!g_pShaderDeviceDx8->IsDeactivated())
{
m_TransitionTable.OverrideColorWriteEnable(bOverrideEnable, bColorWriteEnable);
}
}
void CShaderAPIDx8::UpdateFastClipUserClipPlane(void)
{
float plane[4];
switch (m_DynamicState.m_HeightClipMode)
{
case MATERIAL_HEIGHTCLIPMODE_DISABLE:
EnableFastClip(false);
break;
case MATERIAL_HEIGHTCLIPMODE_RENDER_ABOVE_HEIGHT:
plane[0] = 0.0f;
plane[1] = 0.0f;
plane[2] = 1.0f;
plane[3] = m_DynamicState.m_HeightClipZ;
EnableFastClip(true);
SetFastClipPlane(plane);
break;
case MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT:
plane[0] = 0.0f;
plane[1] = 0.0f;
plane[2] = -1.0f;
plane[3] = -m_DynamicState.m_HeightClipZ;
EnableFastClip(true);
SetFastClipPlane(plane);
break;
}
}
void CShaderAPIDx8::SetHeightClipZ(float z)
{
LOCK_SHADERAPI();
if (z != m_DynamicState.m_HeightClipZ)
{
FlushBufferedPrimitives();
m_DynamicState.m_HeightClipZ = z;
UpdateVertexShaderFogParams();
UpdateFastClipUserClipPlane();
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |=
STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION;
}
}
void CShaderAPIDx8::SetHeightClipMode(MaterialHeightClipMode_t heightClipMode)
{
LOCK_SHADERAPI();
if (heightClipMode != m_DynamicState.m_HeightClipMode)
{
FlushBufferedPrimitives();
m_DynamicState.m_HeightClipMode = heightClipMode;
UpdateVertexShaderFogParams();
UpdateFastClipUserClipPlane();
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |=
STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION;
}
}
void CShaderAPIDx8::SetClipPlane(int index, const float* pPlane)
{
LOCK_SHADERAPI();
Assert(index < g_pHardwareConfig->MaxUserClipPlanes() && index >= 0);
// NOTE: The plane here is specified in *world space*
// NOTE: This is done because they assume Ax+By+Cz+Dw = 0 (where w = 1 in real space)
// while we use Ax+By+Cz=D
D3DXPLANE plane;
plane.a = pPlane[0];
plane.b = pPlane[1];
plane.c = pPlane[2];
plane.d = -pPlane[3];
if (plane != m_DynamicState.m_UserClipPlaneWorld[index])
{
FlushBufferedPrimitives();
m_DynamicState.m_UserClipPlaneChanged |= (1 << index);
m_DynamicState.m_UserClipPlaneWorld[index] = plane;
}
}
//-----------------------------------------------------------------------------
// Converts a D3DXMatrix to a VMatrix and back
//-----------------------------------------------------------------------------
void CShaderAPIDx8::D3DXMatrixToVMatrix(const D3DXMATRIX& in, VMatrix& out)
{
MatrixTranspose(*(const VMatrix*)&in, out);
}
void CShaderAPIDx8::VMatrixToD3DXMatrix(const VMatrix& in, D3DXMATRIX& out)
{
MatrixTranspose(in, *(VMatrix*)&out);
}
//-----------------------------------------------------------------------------
// Mark all user clip planes as being dirty
//-----------------------------------------------------------------------------
void CShaderAPIDx8::MarkAllUserClipPlanesDirty()
{
m_DynamicState.m_UserClipPlaneChanged |= (1 << g_pHardwareConfig->MaxUserClipPlanes()) - 1;
m_DynamicState.m_bFastClipPlaneChanged = true;
}
//-----------------------------------------------------------------------------
// User clip plane override
//-----------------------------------------------------------------------------
void CShaderAPIDx8::EnableUserClipTransformOverride(bool bEnable)
{
LOCK_SHADERAPI();
if (m_DynamicState.m_bUserClipTransformOverride != bEnable)
{
FlushBufferedPrimitives();
m_DynamicState.m_bUserClipTransformOverride = bEnable;
MarkAllUserClipPlanesDirty();
}
}
//-----------------------------------------------------------------------------
// Specify user clip transform
//-----------------------------------------------------------------------------
void CShaderAPIDx8::UserClipTransform(const VMatrix& worldToProjection)
{
LOCK_SHADERAPI();
D3DXMATRIX dxWorldToProjection;
VMatrixToD3DXMatrix(worldToProjection, dxWorldToProjection);
if (m_DynamicState.m_UserClipTransform != dxWorldToProjection)
{
m_DynamicState.m_UserClipTransform = dxWorldToProjection;
if (m_DynamicState.m_bUserClipTransformOverride)
{
FlushBufferedPrimitives();
MarkAllUserClipPlanesDirty();
}
}
}
//-----------------------------------------------------------------------------
// Enables a user clip plane
//-----------------------------------------------------------------------------
void CShaderAPIDx8::EnableClipPlane(int index, bool bEnable)
{
LOCK_SHADERAPI();
Assert(index < g_pHardwareConfig->MaxUserClipPlanes() && index >= 0);
if ((m_DynamicState.m_UserClipPlaneEnabled & (1 << index) ? true : false) != bEnable)
{
FlushBufferedPrimitives();
if (bEnable)
{
m_DynamicState.m_UserClipPlaneEnabled |= (1 << index);
}
else
{
m_DynamicState.m_UserClipPlaneEnabled &= ~(1 << index);
}
SetRenderStateConstMacro(this, D3DRS_CLIPPLANEENABLE, m_DynamicState.m_UserClipPlaneEnabled);
}
}
//-----------------------------------------------------------------------------
// Recomputes the fast-clip plane matrices based on the current fast-clip plane
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitFastClipPlane()
{
// Don't bother recomputing if unchanged or disabled
if (!m_DynamicState.m_bFastClipPlaneChanged || !m_DynamicState.m_FastClipEnabled)
return;
m_DynamicState.m_bFastClipPlaneChanged = false;
D3DXMatrixIdentity(&m_CachedFastClipProjectionMatrix);
// Compute worldToProjection - need inv. transpose for transforming plane.
D3DXMATRIX viewToProjInvTrans, viewToProjInv, viewToProj = GetTransform(MATERIAL_PROJECTION);
viewToProj._43 *= 0.5f; // pull in zNear because the shear in effect
// moves it out: clipping artifacts when looking down at water
// could occur if this multiply is not done
D3DXMATRIX worldToViewInvTrans, worldToViewInv, worldToView = GetUserClipTransform();
D3DXMatrixInverse(&worldToViewInv, NULL, &worldToView);
D3DXMatrixTranspose(&worldToViewInvTrans, &worldToViewInv);
D3DXMatrixInverse(&viewToProjInv, NULL, &viewToProj);
D3DXMatrixTranspose(&viewToProjInvTrans, &viewToProjInv);
D3DXPLANE plane;
D3DXPlaneNormalize(&plane, &m_DynamicState.m_FastClipPlane);
D3DXVECTOR4 clipPlane(plane.a, plane.b, plane.c, plane.d);
// transform clip plane into view space
D3DXVec4Transform(&clipPlane, &clipPlane, &worldToViewInvTrans);
// transform clip plane into projection space
D3DXVec4Transform(&clipPlane, &clipPlane, &viewToProjInvTrans);
#define ALLOW_FOR_FASTCLIPDUMPS 0
#if (ALLOW_FOR_FASTCLIPDUMPS == 1)
static ConVar shader_dumpfastclipprojectioncoords("shader_dumpfastclipprojectioncoords", "0", 0, "dump fast clip projected matrix");
if (shader_dumpfastclipprojectioncoords.GetBool())
DevMsg("Fast clip plane projected coordinates: %f %f %f %f", clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w);
#endif
if ((clipPlane.z * clipPlane.w) <= -0.4f) // a plane with (z*w) > -0.4 at this point is behind the camera and will cause graphical glitches. Toss it. (0.4 found through experimentation)
{
#if (ALLOW_FOR_FASTCLIPDUMPS == 1)
if (shader_dumpfastclipprojectioncoords.GetBool())
DevMsg(" %f %f %f %f\n", clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w);
#endif
D3DXVec4Normalize(&clipPlane, &clipPlane);
//if ((fabs(clipPlane.z) > 0.01) && (fabs(clipPlane.w) > 0.01f))
{
// put projection space clip plane in Z column
m_CachedFastClipProjectionMatrix._13 = clipPlane.x;
m_CachedFastClipProjectionMatrix._23 = clipPlane.y;
m_CachedFastClipProjectionMatrix._33 = clipPlane.z;
m_CachedFastClipProjectionMatrix._43 = clipPlane.w;
}
}
#if (ALLOW_FOR_FASTCLIPDUMPS == 1)
else
{
if (shader_dumpfastclipprojectioncoords.GetBool())
DevMsg("\n"); //finish off the line above
}
#endif
m_CachedFastClipProjectionMatrix = viewToProj * m_CachedFastClipProjectionMatrix;
// Update the cached polyoffset matrix (with clip) too:
ComputePolyOffsetMatrix(m_CachedFastClipProjectionMatrix, m_CachedFastClipPolyOffsetProjectionMatrix);
}
//-----------------------------------------------------------------------------
// Sets the fast-clip plane; but doesn't update the matrices
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetFastClipPlane(const float* pPlane)
{
LOCK_SHADERAPI();
D3DXPLANE plane;
plane.a = pPlane[0];
plane.b = pPlane[1];
plane.c = pPlane[2];
plane.d = -pPlane[3];
if (plane != m_DynamicState.m_FastClipPlane)
{
FlushBufferedPrimitives();
UpdateVertexShaderFogParams();
m_DynamicState.m_FastClipPlane = plane;
// Mark a dirty bit so when it comes time to commit view + projection transforms,
// we also update the fast clip matrices
m_DynamicState.m_bFastClipPlaneChanged = true;
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |=
STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION;
}
}
//-----------------------------------------------------------------------------
// Enables/disables fast-clip mode
//-----------------------------------------------------------------------------
void CShaderAPIDx8::EnableFastClip(bool bEnable)
{
LOCK_SHADERAPI();
if (m_DynamicState.m_FastClipEnabled != bEnable)
{
FlushBufferedPrimitives();
UpdateVertexShaderFogParams();
m_DynamicState.m_FastClipEnabled = bEnable;
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |=
STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION;
}
}
/*
// -----------------------------------------------------------------------------
// SetInvariantClipVolume - This routine takes six planes as input and sets the
// appropriate Direct3D user clip plane state
// What we mean by "invariant clipping" here is that certain devices implement
// user clip planes at the raster level, which means that multi-pass rendering
// where one pass is unclipped (such as base geometry) and another pass *IS*
// clipped (such as flashlight geometry), there is no z-fighting since the
// clipping is implemented at the raster level in an "invariant" way
// -----------------------------------------------------------------------------
void CShaderAPIDx8::SetInvariantClipVolume( Frustum_t *pFrustumPlanes )
{
// Only do this on modern nVidia hardware, which does invariant clipping
if ( m_VendorID == VENDORID_NVIDIA )
{
if ( pFrustumPlanes )
{
// if ()
// {
//
// }
for (int i=0; i<6; i++)
{
const cplane_t *pPlane = pFrustumPlanes->GetPlane(i);
SetClipPlane( i, (float *) &pPlane->normal );
EnableClipPlane( i, true );
// FRUSTUM_RIGHT = 0,
// FRUSTUM_LEFT = 1,
// FRUSTUM_TOP = 2,
// FRUSTUM_BOTTOM = 3,
// FRUSTUM_NEARZ = 4,
// FRUSTUM_FARZ = 5,
}
}
else // NULL disables the invariant clip volume...
{
for (int i=0; i<6; i++)
{
EnableClipPlane( i, false );
}
}
}
}
*/
//-----------------------------------------------------------------------------
// Vertex blending
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetVertexBlendState(int numBones)
{
if (numBones < 0)
{
numBones = m_DynamicState.m_NumBones;
}
// For fixed-function, the number of weights is actually one less than
// the number of bones
if (numBones > 0)
--numBones;
bool normalizeNormals = true;
D3DVERTEXBLENDFLAGS vertexBlend;
switch (numBones)
{
case 0:
vertexBlend = D3DVBF_DISABLE;
normalizeNormals = false;
break;
case 1:
vertexBlend = D3DVBF_1WEIGHTS;
break;
case 2:
vertexBlend = D3DVBF_2WEIGHTS;
break;
case 3:
vertexBlend = D3DVBF_3WEIGHTS;
break;
default:
vertexBlend = D3DVBF_DISABLE;
Assert(0);
break;
}
if (m_DynamicState.m_VertexBlend != vertexBlend)
{
m_DynamicState.m_VertexBlend = vertexBlend;
SetSupportedRenderState(D3DRS_VERTEXBLEND, vertexBlend);
}
// Activate normalize normals when skinning is on
if (m_DynamicState.m_NormalizeNormals != normalizeNormals)
{
m_DynamicState.m_NormalizeNormals = normalizeNormals;
SetSupportedRenderState(D3DRS_NORMALIZENORMALS, normalizeNormals);
}
}
//-----------------------------------------------------------------------------
// Vertex blending
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetNumBoneWeights(int numBones)
{
LOCK_SHADERAPI();
if (m_DynamicState.m_NumBones != numBones)
{
FlushBufferedPrimitives();
m_DynamicState.m_NumBones = numBones;
if (m_TransitionTable.CurrentShadowState())
{
SetVertexBlendState(m_TransitionTable.CurrentShadowState()->m_VertexBlendEnable ? -1 : 0);
}
}
}
void CShaderAPIDx8::EnableHWMorphing(bool bEnable)
{
LOCK_SHADERAPI();
if (m_DynamicState.m_bHWMorphingEnabled != bEnable)
{
FlushBufferedPrimitives();
m_DynamicState.m_bHWMorphingEnabled = bEnable;
}
}
void CShaderAPIDx8::EnabledSRGBWrite(bool bEnabled)
{
m_DynamicState.m_bSRGBWritesEnabled = bEnabled;
if (g_pHardwareConfig->SupportsPixelShaders_2_b())
{
UpdatePixelFogColorConstant();
if (bEnabled && g_pHardwareConfig->NeedsShaderSRGBConversion())
BindTexture(SHADER_SAMPLER15, m_hLinearToGammaTableTexture);
else
BindTexture(SHADER_SAMPLER15, m_hLinearToGammaTableIdentityTexture);
}
}
#if defined( _X360 )
void CShaderAPIDx8::ApplySRGBReadState(int iTextureStage, bool bSRGBReadEnabled)
{
Sampler_t sampler = (Sampler_t)iTextureStage;
SamplerState_t& samplerState = SamplerState(sampler);
samplerState.m_SRGBReadEnable = bSRGBReadEnabled;
if ((samplerState.m_BoundTexture == INVALID_SHADERAPI_TEXTURE_HANDLE) || !samplerState.m_TextureEnable)
{
return;
}
IDirect3DBaseTexture* pBindTexture = CShaderAPIDx8::GetD3DTexture(samplerState.m_BoundTexture);
if (!pBindTexture)
{
return;
}
DWORD linearFormatBackup = pBindTexture->Format.dword[0]; //if we convert to srgb format, we need the original format for reverting. We only need the first DWORD of GPUTEXTURE_FETCH_CONSTANT.
if (bSRGBReadEnabled)
{
pBindTexture->Format.SignX = pBindTexture->Format.SignY = pBindTexture->Format.SignZ = 3; //convert to srgb format for the bind. This effectively emulates the old srgb read sampler state
}
Dx9Device()->SetTexture(sampler, pBindTexture);
// copy back the format in case we changed it
pBindTexture->Format.dword[0] = linearFormatBackup;
}
#endif
//-----------------------------------------------------------------------------
// Fog methods...
//-----------------------------------------------------------------------------
void CShaderAPIDx8::UpdatePixelFogColorConstant(void)
{
Assert(HardwareConfig()->SupportsPixelShaders_2_b());
float fogColor[4];
switch (GetPixelFogMode())
{
case MATERIAL_FOG_NONE:
{
for (int i = 0; i != 3; ++i)
fogColor[i] = 0.0f;
}
break;
case MATERIAL_FOG_LINEAR:
{
//setup the fog for mixing linear fog in the pixel shader so that it emulates ff range fog
for (int i = 0; i != 3; ++i)
fogColor[i] = m_DynamicState.m_PixelFogColor[i];
if (m_DynamicState.m_bSRGBWritesEnabled)
{
//since the fog color will assuredly get converted from linear to gamma, we should probably convert it from gamma to linear
for (int i = 0; i != 3; ++i)
fogColor[i] = GammaToLinear_HardwareSpecific(fogColor[i]);
}
if ((!m_DynamicState.m_bFogGammaCorrectionDisabled) && (g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER))
{
for (int i = 0; i != 3; ++i)
fogColor[i] *= m_ToneMappingScale.x; // Linear
}
}
break;
case MATERIAL_FOG_LINEAR_BELOW_FOG_Z:
{
//water fog has been around a while and has never tonemap scaled, and has always been in linear space
if (g_pHardwareConfig->NeedsShaderSRGBConversion())
{
//srgb in ps2b uses the 2.2 curve
for (int i = 0; i != 3; ++i)
fogColor[i] = pow(m_DynamicState.m_PixelFogColor[i], 2.2f);
}
else
{
for (int i = 0; i != 3; ++i)
fogColor[i] = GammaToLinear_HardwareSpecific(m_DynamicState.m_PixelFogColor[i]); //this is how water fog color has always been setup in the past
}
if ((!m_DynamicState.m_bFogGammaCorrectionDisabled) && (g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER))
{
for (int i = 0; i != 3; ++i)
fogColor[i] *= m_ToneMappingScale.x; // Linear
}
}
break;
NO_DEFAULT;
};
fogColor[3] = 1.0f / m_DynamicState.m_DestAlphaDepthRange;
SetPixelShaderConstant(LINEAR_FOG_COLOR, fogColor);
}
void CShaderAPIDx8::ApplyFogMode(ShaderFogMode_t fogMode, bool bSRGBWritesEnabled, bool bDisableFogGammaCorrection)
{
HDRType_t hdrType = g_pHardwareConfig->GetHDRType();
if (fogMode == SHADER_FOGMODE_DISABLED)
{
if (hdrType != HDR_TYPE_FLOAT)
{
FogMode(MATERIAL_FOG_NONE);
}
if (m_DelayedShaderConstants.iPixelShaderFogParams != -1)
SetPixelShaderFogParams(m_DelayedShaderConstants.iPixelShaderFogParams, fogMode);
return;
}
bool bShouldGammaCorrect = true; // By default, we'll gamma correct.
unsigned char r = 0, g = 0, b = 0; // Black fog
if (hdrType != HDR_TYPE_FLOAT)
{
FogMode(m_SceneFogMode);
}
if (m_DelayedShaderConstants.iPixelShaderFogParams != -1)
SetPixelShaderFogParams(m_DelayedShaderConstants.iPixelShaderFogParams, fogMode);
switch (fogMode)
{
case SHADER_FOGMODE_BLACK: // Additive decals
bShouldGammaCorrect = false;
break;
case SHADER_FOGMODE_OO_OVERBRIGHT:
case SHADER_FOGMODE_GREY: // Mod2x decals
r = g = b = 128;
break;
case SHADER_FOGMODE_WHITE: // Multiplicative decals
r = g = b = 255;
bShouldGammaCorrect = false;
break;
case SHADER_FOGMODE_FOGCOLOR:
GetSceneFogColor(&r, &g, &b); // Scene fog color
break;
NO_DEFAULT
}
bShouldGammaCorrect &= !bDisableFogGammaCorrection;
m_DynamicState.m_bFogGammaCorrectionDisabled = !bShouldGammaCorrect;
D3DCOLOR color;
if (bShouldGammaCorrect)
{
color = ComputeGammaCorrectedFogColor(r, g, b, bSRGBWritesEnabled);
}
else
{
color = D3DCOLOR_ARGB(255, r, g, b);
}
const float fColorScale = 1.0f / 255.0f;
m_DynamicState.m_PixelFogColor[0] = (float)(r)*fColorScale;
m_DynamicState.m_PixelFogColor[1] = (float)(g)*fColorScale;
m_DynamicState.m_PixelFogColor[2] = (float)(b)*fColorScale;
if (g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_b())
{
UpdatePixelFogColorConstant();
}
if (color != m_DynamicState.m_FogColor)
{
if (hdrType != HDR_TYPE_FLOAT)
{
m_DynamicState.m_FogColor = color;
SetRenderStateConstMacro(this, D3DRS_FOGCOLOR, m_DynamicState.m_FogColor);
}
}
}
void CShaderAPIDx8::SceneFogMode(MaterialFogMode_t fogMode)
{
LOCK_SHADERAPI();
if (m_SceneFogMode != fogMode)
{
FlushBufferedPrimitives();
m_SceneFogMode = fogMode;
if (m_TransitionTable.CurrentShadowState())
{
// Get the shadow state in sync since it depends on SceneFogMode.
ApplyFogMode(m_TransitionTable.CurrentShadowState()->m_FogMode, m_TransitionTable.CurrentShadowState()->m_SRGBWriteEnable, m_TransitionTable.CurrentShadowState()->m_bDisableFogGammaCorrection);
}
}
}
MaterialFogMode_t CShaderAPIDx8::GetSceneFogMode()
{
return m_SceneFogMode;
}
MaterialFogMode_t CShaderAPIDx8::GetPixelFogMode()
{
if (ShouldUsePixelFogForMode(m_SceneFogMode))
return m_SceneFogMode;
else
return MATERIAL_FOG_NONE;
}
int CShaderAPIDx8::GetPixelFogCombo(void)
{
if ((m_SceneFogMode != MATERIAL_FOG_NONE) && ShouldUsePixelFogForMode(m_SceneFogMode))
return m_SceneFogMode - 1; //PIXELFOGTYPE dynamic combos are shifted down one. MATERIAL_FOG_NONE is simulated by range fog with rigged parameters. Gets rid of a dynamic combo across the ps2x set
else
return MATERIAL_FOG_NONE;
}
static ConVar r_pixelfog("r_pixelfog", "1");
bool CShaderAPIDx8::ShouldUsePixelFogForMode(MaterialFogMode_t fogMode)
{
if (fogMode == MATERIAL_FOG_NONE)
return false;
if (IsX360() || IsPosix()) // Always use pixel fog on X360 and Posix
return true;
if (g_pHardwareConfig->Caps().m_nDXSupportLevel < 90) //pixel fog not available until at least ps2.0
return false;
switch (m_SceneFogMode)
{
case MATERIAL_FOG_LINEAR:
return (g_pHardwareConfig->SupportsPixelShaders_2_b() && //lightmappedgeneric_ps20.fxc can't handle the instruction count
r_pixelfog.GetBool()); //use pixel fog if preferred
case MATERIAL_FOG_LINEAR_BELOW_FOG_Z:
return true;
};
return false;
}
//-----------------------------------------------------------------------------
// Fog methods...
//-----------------------------------------------------------------------------
void CShaderAPIDx8::FogMode(MaterialFogMode_t fogMode)
{
bool bFogEnable;
if (IsX360())
{
// FF fog not applicable on 360
return;
}
m_DynamicState.m_SceneFog = fogMode;
switch (fogMode)
{
default:
Assert(0);
// fall through
case MATERIAL_FOG_NONE:
bFogEnable = false;
break;
case MATERIAL_FOG_LINEAR:
// use vertex fog to achieve linear range fog
bFogEnable = true;
break;
case MATERIAL_FOG_LINEAR_BELOW_FOG_Z:
// use pixel fog on 9.0 and greater for height fog
bFogEnable = g_pHardwareConfig->Caps().m_nDXSupportLevel < 90;
break;
}
if (ShouldUsePixelFogForMode(fogMode))
{
bFogEnable = false; //disable FF fog when doing fog in the pixel shader
}
#if 0
// HACK - do this to disable fog always
bFogEnable = false;
m_DynamicState.m_SceneFog = MATERIAL_FOG_NONE;
#endif
// These two are always set to this, so don't bother setting them.
// We are always using vertex fog.
// SetRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE );
// SetRenderState( D3DRS_RANGEFOGENABLE, false );
// Set fog enable if it's different than before.
if (bFogEnable != m_DynamicState.m_FogEnable)
{
SetSupportedRenderState(D3DRS_FOGENABLE, bFogEnable);
m_DynamicState.m_FogEnable = bFogEnable;
}
}
void CShaderAPIDx8::FogStart(float fStart)
{
LOCK_SHADERAPI();
if (fStart != m_DynamicState.m_FogStart)
{
// need to flush the dynamic buffer
FlushBufferedPrimitives();
SetRenderStateConstMacro(this, D3DRS_FOGSTART, *((DWORD*)(&fStart)));
m_VertexShaderFogParams[0] = fStart;
UpdateVertexShaderFogParams();
m_DynamicState.m_FogStart = fStart;
}
}
void CShaderAPIDx8::FogEnd(float fEnd)
{
LOCK_SHADERAPI();
if (fEnd != m_DynamicState.m_FogEnd)
{
// need to flush the dynamic buffer
FlushBufferedPrimitives();
SetRenderStateConstMacro(this, D3DRS_FOGEND, *((DWORD*)(&fEnd)));
m_VertexShaderFogParams[1] = fEnd;
UpdateVertexShaderFogParams();
m_DynamicState.m_FogEnd = fEnd;
}
}
void CShaderAPIDx8::SetFogZ(float fogZ)
{
LOCK_SHADERAPI();
if (fogZ != m_DynamicState.m_FogZ)
{
// need to flush the dynamic buffer
FlushBufferedPrimitives();
m_DynamicState.m_FogZ = fogZ;
UpdateVertexShaderFogParams();
}
}
void CShaderAPIDx8::FogMaxDensity(float flMaxDensity)
{
LOCK_SHADERAPI();
if (flMaxDensity != m_DynamicState.m_FogMaxDensity)
{
// need to flush the dynamic buffer
FlushBufferedPrimitives();
// SetRenderState(D3DRS_FOGDENSITY, *((DWORD*)(&flMaxDensity))); // ??? do I need to to this ???
m_flFogMaxDensity = flMaxDensity;
UpdateVertexShaderFogParams();
m_DynamicState.m_FogMaxDensity = flMaxDensity;
}
}
void CShaderAPIDx8::GetFogDistances(float* fStart, float* fEnd, float* fFogZ)
{
LOCK_SHADERAPI();
if (fStart)
*fStart = m_DynamicState.m_FogStart;
if (fEnd)
*fEnd = m_DynamicState.m_FogEnd;
if (fFogZ)
*fFogZ = m_DynamicState.m_FogZ;
}
void CShaderAPIDx8::SceneFogColor3ub(unsigned char r, unsigned char g, unsigned char b)
{
LOCK_SHADERAPI();
if (m_SceneFogColor[0] != r || m_SceneFogColor[1] != g || m_SceneFogColor[2] != b)
{
FlushBufferedPrimitives();
m_SceneFogColor[0] = r;
m_SceneFogColor[1] = g;
m_SceneFogColor[2] = b;
if (m_TransitionTable.CurrentShadowState())
{
ApplyFogMode(m_TransitionTable.CurrentShadowState()->m_FogMode, m_TransitionTable.CurrentShadowState()->m_SRGBWriteEnable, m_TransitionTable.CurrentShadowState()->m_bDisableFogGammaCorrection);
}
}
}
void CShaderAPIDx8::GetSceneFogColor(unsigned char* rgb)
{
LOCK_SHADERAPI();
rgb[0] = m_SceneFogColor[0];
rgb[1] = m_SceneFogColor[1];
rgb[2] = m_SceneFogColor[2];
}
void CShaderAPIDx8::GetSceneFogColor(unsigned char* r, unsigned char* g, unsigned char* b)
{
*r = m_SceneFogColor[0];
*g = m_SceneFogColor[1];
*b = m_SceneFogColor[2];
}
//-----------------------------------------------------------------------------
// Gamma correction of fog color, or not...
//-----------------------------------------------------------------------------
D3DCOLOR CShaderAPIDx8::ComputeGammaCorrectedFogColor(unsigned char r, unsigned char g, unsigned char b, bool bSRGBWritesEnabled)
{
#ifdef _DEBUG
if (g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT && !bSRGBWritesEnabled)
{
// Assert( 0 );
}
#endif
bool bLinearSpace = g_pHardwareConfig->Caps().m_bFogColorAlwaysLinearSpace ||
(bSRGBWritesEnabled && (g_pHardwareConfig->Caps().m_bFogColorSpecifiedInLinearSpace || g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT));
bool bScaleFogByToneMappingScale = g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER;
float fr = (r / 255.0f);
float fg = (g / 255.0f);
float fb = (b / 255.0f);
if (bLinearSpace)
{
fr = GammaToLinear(fr);
fg = GammaToLinear(fg);
fb = GammaToLinear(fb);
if (bScaleFogByToneMappingScale)
{
fr *= m_ToneMappingScale.x; //
fg *= m_ToneMappingScale.x; // Linear
fb *= m_ToneMappingScale.x; //
}
}
else if (bScaleFogByToneMappingScale)
{
fr *= m_ToneMappingScale.w; //
fg *= m_ToneMappingScale.w; // Gamma
fb *= m_ToneMappingScale.w; //
}
fr = min(fr, 1.0f);
fg = min(fg, 1.0f);
fb = min(fb, 1.0f);
r = (int)(fr * 255);
g = (int)(fg * 255);
b = (int)(fb * 255);
return D3DCOLOR_ARGB(255, r, g, b);
}
//-----------------------------------------------------------------------------
// Some methods chaining vertex + pixel shaders through to the shader manager
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetVertexShaderIndex(int vshIndex)
{
ShaderManager()->SetVertexShaderIndex(vshIndex);
}
void CShaderAPIDx8::SetPixelShaderIndex(int pshIndex)
{
ShaderManager()->SetPixelShaderIndex(pshIndex);
}
void CShaderAPIDx8::SyncToken(const char* pToken)
{
LOCK_SHADERAPI();
RECORD_COMMAND(DX8_SYNC_TOKEN, 1);
RECORD_STRING(pToken);
}
//-----------------------------------------------------------------------------
// Deals with vertex buffers
//-----------------------------------------------------------------------------
void CShaderAPIDx8::DestroyVertexBuffers(bool bExitingLevel)
{
LOCK_SHADERAPI();
MeshMgr()->DestroyVertexBuffers();
// After a map is shut down, we switch to using smaller dynamic VBs
// (VGUI shouldn't need much), so that we have more memory free during map loading
m_nDynamicVBSize = bExitingLevel ? DYNAMIC_VERTEX_BUFFER_MEMORY_SMALL : DYNAMIC_VERTEX_BUFFER_MEMORY;
}
int CShaderAPIDx8::GetCurrentDynamicVBSize(void)
{
return m_nDynamicVBSize;
}
FORCEINLINE void CShaderAPIDx8::SetVertexShaderConstantInternal(int var, float const* pVec, int numVecs, bool bForce)
{
Assert(pVec);
// DX8 asm shaders use a constant mapping which has transforms and vertex shader
// specific constants shifted down by 10 constants (two 5-constant light structures)
if (IsPC())
{
if ((g_pHardwareConfig->Caps().m_nDXSupportLevel < 90) && (var >= VERTEX_SHADER_MODULATION_COLOR))
{
var -= 10;
}
Assert(var + numVecs <= g_pHardwareConfig->NumVertexShaderConstants());
if (!bForce)
{
int skip = 0;
numVecs = AdjustUpdateRange(pVec, &m_DesiredState.m_pVectorVertexShaderConstant[var], numVecs, &skip);
if (!numVecs)
return;
var += skip;
pVec += skip * 4;
}
Dx9Device()->SetVertexShaderConstantF(var, pVec, numVecs);
memcpy(&m_DynamicState.m_pVectorVertexShaderConstant[var], pVec, numVecs * 4 * sizeof(float));
}
else
{
Assert(var + numVecs <= g_pHardwareConfig->NumVertexShaderConstants());
}
memcpy(&m_DesiredState.m_pVectorVertexShaderConstant[var], pVec, numVecs * 4 * sizeof(float));
if (IsX360())
{
m_MaxVectorVertexShaderConstant = max(m_MaxVectorVertexShaderConstant, var + numVecs);
}
}
//-----------------------------------------------------------------------------
// Sets the constant register for vertex and pixel shaders
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetVertexShaderConstant(int var, float const* pVec, int numVecs, bool bForce)
{
SetVertexShaderConstantInternal(var, pVec, numVecs, bForce);
}
//-----------------------------------------------------------------------------
// Sets the boolean registers for vertex shader control flow
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetBooleanVertexShaderConstant(int var, int const* pVec, int numBools, bool bForce)
{
Assert(pVec);
Assert(var + numBools <= g_pHardwareConfig->NumBooleanVertexShaderConstants());
if (IsPC() && g_pHardwareConfig->GetDXSupportLevel() < 90)
{
return;
}
if (IsPC() && !bForce && memcmp(pVec, &m_DesiredState.m_pBooleanVertexShaderConstant[var], numBools * sizeof(BOOL)) == 0)
{
return;
}
if (IsPC())
{
Dx9Device()->SetVertexShaderConstantB(var, pVec, numBools);
memcpy(&m_DynamicState.m_pBooleanVertexShaderConstant[var], pVec, numBools * sizeof(BOOL));
}
memcpy(&m_DesiredState.m_pBooleanVertexShaderConstant[var], pVec, numBools * sizeof(BOOL));
if (IsX360() && var + numBools > m_MaxBooleanVertexShaderConstant)
{
m_MaxBooleanVertexShaderConstant = var + numBools;
Assert(m_MaxBooleanVertexShaderConstant <= 16);
}
}
//-----------------------------------------------------------------------------
// Sets the integer registers for vertex shader control flow
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetIntegerVertexShaderConstant(int var, int const* pVec, int numIntVecs, bool bForce)
{
Assert(pVec);
Assert(var + numIntVecs <= g_pHardwareConfig->NumIntegerVertexShaderConstants());
if (IsPC() && g_pHardwareConfig->GetDXSupportLevel() < 90)
{
return;
}
if (IsPC() && !bForce && memcmp(pVec, &m_DesiredState.m_pIntegerVertexShaderConstant[var], numIntVecs * sizeof(IntVector4D)) == 0)
{
return;
}
if (IsPC())
{
Dx9Device()->SetVertexShaderConstantI(var, pVec, numIntVecs);
memcpy(&m_DynamicState.m_pIntegerVertexShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D));
}
memcpy(&m_DesiredState.m_pIntegerVertexShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D));
if (IsX360() && var + numIntVecs > m_MaxIntegerVertexShaderConstant)
{
m_MaxIntegerVertexShaderConstant = var + numIntVecs;
Assert(m_MaxIntegerVertexShaderConstant <= 16);
}
}
FORCEINLINE void CShaderAPIDx8::SetPixelShaderConstantInternal(int nStartConst, float const* pValues, int nNumConsts, bool bForce)
{
Assert(nStartConst + nNumConsts <= g_pHardwareConfig->NumPixelShaderConstants());
if (IsPC())
{
if (!bForce)
{
int skip = 0;
nNumConsts = AdjustUpdateRange(pValues, &m_DesiredState.m_pVectorPixelShaderConstant[nStartConst], nNumConsts, &skip);
if (!nNumConsts)
return;
nStartConst += skip;
pValues += skip * 4;
}
Dx9Device()->SetPixelShaderConstantF(nStartConst, pValues, nNumConsts);
memcpy(&m_DynamicState.m_pVectorPixelShaderConstant[nStartConst], pValues, nNumConsts * 4 * sizeof(float));
}
memcpy(&m_DesiredState.m_pVectorPixelShaderConstant[nStartConst], pValues, nNumConsts * 4 * sizeof(float));
if (IsX360())
{
m_MaxVectorPixelShaderConstant = max(m_MaxVectorPixelShaderConstant, nStartConst + nNumConsts);
Assert(m_MaxVectorPixelShaderConstant <= 32);
}
}
void CShaderAPIDx8::SetPixelShaderConstant(int var, float const* pVec, int numVecs, bool bForce)
{
SetPixelShaderConstantInternal(var, pVec, numVecs, bForce);
}
template<class T> FORCEINLINE T GetData(uint8 const* pData)
{
return *(reinterpret_cast<T const*>(pData));
}
void CShaderAPIDx8::SetStandardTextureHandle(StandardTextureId_t nId, ShaderAPITextureHandle_t nHandle)
{
Assert(nId < ARRAYSIZE(m_StdTextureHandles));
m_StdTextureHandles[nId] = nHandle;
}
void CShaderAPIDx8::ExecuteCommandBuffer(uint8* pCmdBuf)
{
uint8* pReturnStack[20];
uint8** pSP = &pReturnStack[ARRAYSIZE(pReturnStack)];
uint8* pLastCmd;
for (;;)
{
uint8* pCmd = pCmdBuf;
int nCmd = GetData<int>(pCmdBuf);
switch (nCmd)
{
case CBCMD_END:
{
if (pSP == &pReturnStack[ARRAYSIZE(pReturnStack)])
return;
else
{
// pop pc
pCmdBuf = *(pSP++);
break;
}
}
case CBCMD_JUMP:
pCmdBuf = GetData<uint8*>(pCmdBuf + sizeof(int));
break;
case CBCMD_JSR:
{
Assert(pSP > &(pReturnStack[0]));
// *(--pSP ) = pCmdBuf + sizeof( int ) + sizeof( uint8 *);
// pCmdBuf = GetData<uint8 *>( pCmdBuf + sizeof( int ) );
ExecuteCommandBuffer(GetData<uint8*>(pCmdBuf + sizeof(int)));
pCmdBuf = pCmdBuf + sizeof(int) + sizeof(uint8*);
break;
}
case CBCMD_SET_PIXEL_SHADER_FLOAT_CONST:
{
int nStartConst = GetData<int>(pCmdBuf + sizeof(int));
int nNumConsts = GetData<int>(pCmdBuf + 2 * sizeof(int));
float const* pValues = reinterpret_cast<float const*> (pCmdBuf + 3 * sizeof(int));
pCmdBuf += nNumConsts * 4 * sizeof(float) + 3 * sizeof(int);
SetPixelShaderConstantInternal(nStartConst, pValues, nNumConsts, false);
break;
}
case CBCMD_SETPIXELSHADERFOGPARAMS:
{
int nReg = GetData<int>(pCmdBuf + sizeof(int));
pCmdBuf += 2 * sizeof(int);
SetPixelShaderFogParams(nReg); // !! speed fixme
break;
}
case CBCMD_STORE_EYE_POS_IN_PSCONST:
{
int nReg = GetData<int>(pCmdBuf + sizeof(int));
pCmdBuf += 2 * sizeof(int);
SetPixelShaderConstantInternal(nReg, m_WorldSpaceCameraPositon.Base(), 1, false);
break;
}
case CBCMD_COMMITPIXELSHADERLIGHTING:
{
int nReg = GetData<int>(pCmdBuf + sizeof(int));
pCmdBuf += 2 * sizeof(int);
CommitPixelShaderLighting(nReg);
break;
}
case CBCMD_SETPIXELSHADERSTATEAMBIENTLIGHTCUBE:
{
int nReg = GetData<int>(pCmdBuf + sizeof(int));
pCmdBuf += 2 * sizeof(int);
float* pCubeBase = m_DynamicState.m_AmbientLightCube[0].Base();
SetPixelShaderConstantInternal(nReg, pCubeBase, 6, false);
break;
}
case CBCMD_SETAMBIENTCUBEDYNAMICSTATEVERTEXSHADER:
{
pCmdBuf += sizeof(int);
SetVertexShaderStateAmbientLightCube();
break;
}
case CBCMD_SET_DEPTH_FEATHERING_CONST:
{
int nConst = GetData<int>(pCmdBuf + sizeof(int));
float fDepthBlendScale = GetData<float>(pCmdBuf + 2 * sizeof(int));
pCmdBuf += 2 * sizeof(int) + sizeof(float);
SetDepthFeatheringPixelShaderConstant(nConst, fDepthBlendScale);
break;
}
case CBCMD_SET_VERTEX_SHADER_FLOAT_CONST:
{
int nStartConst = GetData<int>(pCmdBuf + sizeof(int));
int nNumConsts = GetData<int>(pCmdBuf + 2 * sizeof(int));
float const* pValues = reinterpret_cast<float const*> (pCmdBuf + 3 * sizeof(int));
pCmdBuf += nNumConsts * 4 * sizeof(float) + 3 * sizeof(int);
SetVertexShaderConstantInternal(nStartConst, pValues, nNumConsts, false);
break;
}
case CBCMD_BIND_STANDARD_TEXTURE:
{
int nSampler = GetData<int>(pCmdBuf + sizeof(int));
int nTextureID = GetData<int>(pCmdBuf + 2 * sizeof(int));
pCmdBuf += 3 * sizeof(int);
if (m_StdTextureHandles[nTextureID] != INVALID_SHADERAPI_TEXTURE_HANDLE)
{
BindTexture((Sampler_t)nSampler, m_StdTextureHandles[nTextureID]);
}
else
{
ShaderUtil()->BindStandardTexture((Sampler_t)nSampler, (StandardTextureId_t)nTextureID);
}
break;
}
case CBCMD_BIND_SHADERAPI_TEXTURE_HANDLE:
{
int nSampler = GetData<int>(pCmdBuf + sizeof(int));
ShaderAPITextureHandle_t hTexture = GetData<ShaderAPITextureHandle_t>(pCmdBuf + 2 * sizeof(int));
Assert(hTexture != INVALID_SHADERAPI_TEXTURE_HANDLE);
pCmdBuf += 2 * sizeof(int) + sizeof(ShaderAPITextureHandle_t);
BindTexture((Sampler_t)nSampler, hTexture);
break;
}
case CBCMD_SET_PSHINDEX:
{
int nIdx = GetData<int>(pCmdBuf + sizeof(int));
ShaderManager()->SetPixelShaderIndex(nIdx);
pCmdBuf += 2 * sizeof(int);
break;
}
case CBCMD_SET_VSHINDEX:
{
int nIdx = GetData<int>(pCmdBuf + sizeof(int));
ShaderManager()->SetVertexShaderIndex(nIdx);
pCmdBuf += 2 * sizeof(int);
break;
}
#ifndef NDEBUG
default:
{
Assert(0);
}
#endif
}
pLastCmd = pCmd;
}
}
//-----------------------------------------------------------------------------
// Sets the boolean registers for pixel shader control flow
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetBooleanPixelShaderConstant(int var, int const* pVec, int numBools, bool bForce)
{
Assert(pVec);
Assert(var + numBools <= g_pHardwareConfig->NumBooleanPixelShaderConstants());
if (IsPC() && !bForce && memcmp(pVec, &m_DesiredState.m_pBooleanPixelShaderConstant[var], numBools * sizeof(BOOL)) == 0)
{
return;
}
if (IsPC())
{
Dx9Device()->SetPixelShaderConstantB(var, pVec, numBools);
memcpy(&m_DynamicState.m_pBooleanPixelShaderConstant[var], pVec, numBools * sizeof(BOOL));
}
memcpy(&m_DesiredState.m_pBooleanPixelShaderConstant[var], pVec, numBools * sizeof(BOOL));
if (IsX360() && var + numBools > m_MaxBooleanPixelShaderConstant)
{
m_MaxBooleanPixelShaderConstant = var + numBools;
Assert(m_MaxBooleanPixelShaderConstant <= 16);
}
}
//-----------------------------------------------------------------------------
// Sets the integer registers for pixel shader control flow
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetIntegerPixelShaderConstant(int var, int const* pVec, int numIntVecs, bool bForce)
{
Assert(pVec);
Assert(var + numIntVecs <= g_pHardwareConfig->NumIntegerPixelShaderConstants());
if (IsPC() && !bForce && memcmp(pVec, &m_DesiredState.m_pIntegerPixelShaderConstant[var], numIntVecs * sizeof(IntVector4D)) == 0)
{
return;
}
if (IsPC())
{
Dx9Device()->SetPixelShaderConstantI(var, pVec, numIntVecs);
memcpy(&m_DynamicState.m_pIntegerPixelShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D));
}
memcpy(&m_DesiredState.m_pIntegerPixelShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D));
if (IsX360() && var + numIntVecs > m_MaxIntegerPixelShaderConstant)
{
m_MaxIntegerPixelShaderConstant = var + numIntVecs;
Assert(m_MaxBooleanPixelShaderConstant <= 16);
}
}
void CShaderAPIDx8::InvalidateDelayedShaderConstants(void)
{
m_DelayedShaderConstants.Invalidate();
}
//-----------------------------------------------------------------------------
//
// Methods dealing with texture stage state
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Gets the texture associated with a texture state...
//-----------------------------------------------------------------------------
inline IDirect3DBaseTexture* CShaderAPIDx8::GetD3DTexture(ShaderAPITextureHandle_t hTexture)
{
if (hTexture == INVALID_SHADERAPI_TEXTURE_HANDLE)
{
return NULL;
}
AssertValidTextureHandle(hTexture);
Texture_t& tex = GetTexture(hTexture);
if (tex.m_NumCopies == 1)
{
return tex.GetTexture();
}
else
{
return tex.GetTexture(tex.m_CurrentCopy);
}
}
//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
inline ShaderAPITextureHandle_t CShaderAPIDx8::GetModifyTextureHandle() const
{
return m_ModifyTextureHandle;
}
inline IDirect3DBaseTexture* CShaderAPIDx8::GetModifyTexture()
{
return CShaderAPIDx8::GetD3DTexture(m_ModifyTextureHandle);
}
void CShaderAPIDx8::SetModifyTexture(IDirect3DBaseTexture* pTex)
{
if (m_ModifyTextureHandle == INVALID_SHADERAPI_TEXTURE_HANDLE)
return;
Texture_t& tex = GetTexture(m_ModifyTextureHandle);
if (tex.m_NumCopies == 1)
{
tex.SetTexture(pTex);
}
else
{
tex.SetTexture(tex.m_CurrentCopy, pTex);
}
}
inline ShaderAPITextureHandle_t CShaderAPIDx8::GetBoundTextureBindId(Sampler_t sampler) const
{
return SamplerState(sampler).m_BoundTexture;
}
inline bool CShaderAPIDx8::WouldBeOverTextureLimit(ShaderAPITextureHandle_t hTexture)
{
if (IsPC())
{
if (mat_texture_limit.GetInt() < 0)
return false;
Texture_t& tex = GetTexture(hTexture);
if (tex.m_LastBoundFrame == m_CurrentFrame)
return false;
return m_nTextureMemoryUsedLastFrame + tex.GetMemUsage() > (mat_texture_limit.GetInt() * 1024);
}
return false;
}
#define SETSAMPLESTATEANDMIRROR( sampler, samplerState, state_type, mirror_field, value ) \
if ( samplerState.mirror_field != value ) \
{ \
samplerState.mirror_field = value; \
::SetSamplerState( g_pD3DDevice, sampler, state_type, value ); \
}
#define SETSAMPLESTATEANDMIRROR_FLOAT( sampler, samplerState, state_type, mirror_field, value ) \
if ( samplerState.mirror_field != value ) \
{ \
samplerState.mirror_field = value; \
::SetSamplerState( g_pD3DDevice, sampler, state_type, *(DWORD*)&value ); \
}
//-----------------------------------------------------------------------------
// Sets state on the board related to the texture state
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetTextureState(Sampler_t sampler, ShaderAPITextureHandle_t hTexture, bool force)
{
// Get the dynamic texture info
SamplerState_t& samplerState = SamplerState(sampler);
// Set the texture state, but only if it changes
if ((samplerState.m_BoundTexture == hTexture) && !force)
return;
// Disabling texturing
if (hTexture == INVALID_SHADERAPI_TEXTURE_HANDLE || WouldBeOverTextureLimit(hTexture))
{
Dx9Device()->SetTexture(sampler, 0);
return;
}
samplerState.m_BoundTexture = hTexture;
// Don't set this if we're disabled
if (!samplerState.m_TextureEnable)
return;
RECORD_COMMAND(DX8_SET_TEXTURE, 3);
RECORD_INT(stage);
RECORD_INT(hTexture);
RECORD_INT(GetTexture(hTexture).m_CurrentCopy);
IDirect3DBaseTexture* pTexture = CShaderAPIDx8::GetD3DTexture(hTexture);
#if defined( _X360 )
DWORD linearFormatBackup = pTexture->Format.dword[0];
if (samplerState.m_SRGBReadEnable)
{
// convert to srgb format for the bind. This effectively emulates the old srgb read sampler state
pTexture->Format.SignX =
pTexture->Format.SignY =
pTexture->Format.SignZ = 3;
}
#endif
Dx9Device()->SetTexture(sampler, pTexture);
#if defined( _X360 )
// put the format back in linear space
pTexture->Format.dword[0] = linearFormatBackup;
#endif
Texture_t& tex = GetTexture(hTexture);
if (tex.m_LastBoundFrame != m_CurrentFrame)
{
tex.m_LastBoundFrame = m_CurrentFrame;
tex.m_nTimesBoundThisFrame = 0;
if (tex.m_pTextureGroupCounterFrame)
{
// Update the per-frame texture group counter.
*tex.m_pTextureGroupCounterFrame += tex.GetMemUsage();
}
// Track memory usage.
m_nTextureMemoryUsedLastFrame += tex.GetMemUsage();
}
if (!m_bDebugTexturesRendering)
++tex.m_nTimesBoundThisFrame;
tex.m_nTimesBoundMax = MAX(tex.m_nTimesBoundMax, tex.m_nTimesBoundThisFrame);
static MaterialSystem_Config_t& materialSystemConfig = ShaderUtil()->GetConfig();
D3DTEXTUREFILTERTYPE minFilter = tex.m_MinFilter;
D3DTEXTUREFILTERTYPE magFilter = tex.m_MagFilter;
D3DTEXTUREFILTERTYPE mipFilter = tex.m_MipFilter;
int finestMipmapLevel = tex.m_FinestMipmapLevel;
float lodBias = tex.m_LodBias;
if (materialSystemConfig.bMipMapTextures == 0)
{
mipFilter = D3DTEXF_NONE;
}
if (materialSystemConfig.bFilterTextures == 0 && tex.m_NumLevels > 1)
{
minFilter = D3DTEXF_NONE;
magFilter = D3DTEXF_NONE;
mipFilter = D3DTEXF_POINT;
}
D3DTEXTUREADDRESS uTexWrap = tex.m_UTexWrap;
D3DTEXTUREADDRESS vTexWrap = tex.m_VTexWrap;
D3DTEXTUREADDRESS wTexWrap = tex.m_WTexWrap;
// For now do this the old way on OSX since the dxabstract layer doesn't support SetSamplerStates
// ###OSX### punting on OSX for now
#if DX_TO_GL_ABSTRACTION && !OSX
if ((samplerState.m_MinFilter != minFilter) || (samplerState.m_MagFilter != magFilter) || (samplerState.m_MipFilter != mipFilter) ||
(samplerState.m_UTexWrap != uTexWrap) || (samplerState.m_VTexWrap != vTexWrap) || (samplerState.m_WTexWrap != wTexWrap) ||
(samplerState.m_FinestMipmapLevel != finestMipmapLevel) || (samplerState.m_LodBias != lodBias))
{
samplerState.m_UTexWrap = uTexWrap;
samplerState.m_VTexWrap = vTexWrap;
samplerState.m_WTexWrap = wTexWrap;
samplerState.m_MinFilter = minFilter;
samplerState.m_MagFilter = magFilter;
samplerState.m_MipFilter = mipFilter;
samplerState.m_FinestMipmapLevel = finestMipmapLevel;
samplerState.m_LodBias = lodBias;
Dx9Device()->SetSamplerStates(sampler, uTexWrap, vTexWrap, wTexWrap, minFilter, magFilter, mipFilter, finestMipmapLevel, lodBias);
}
#else
SETSAMPLESTATEANDMIRROR(sampler, samplerState, D3DSAMP_ADDRESSU, m_UTexWrap, uTexWrap);
SETSAMPLESTATEANDMIRROR(sampler, samplerState, D3DSAMP_ADDRESSV, m_VTexWrap, vTexWrap);
SETSAMPLESTATEANDMIRROR(sampler, samplerState, D3DSAMP_ADDRESSW, m_WTexWrap, wTexWrap);
SETSAMPLESTATEANDMIRROR(sampler, samplerState, D3DSAMP_MINFILTER, m_MinFilter, minFilter);
SETSAMPLESTATEANDMIRROR(sampler, samplerState, D3DSAMP_MAGFILTER, m_MagFilter, magFilter);
SETSAMPLESTATEANDMIRROR(sampler, samplerState, D3DSAMP_MIPFILTER, m_MipFilter, mipFilter);
SETSAMPLESTATEANDMIRROR(sampler, samplerState, D3DSAMP_MAXMIPLEVEL, m_FinestMipmapLevel, finestMipmapLevel);
SETSAMPLESTATEANDMIRROR_FLOAT(sampler, samplerState, D3DSAMP_MIPMAPLODBIAS, m_LodBias, lodBias);
#endif
}
void CShaderAPIDx8::BindTexture(Sampler_t sampler, ShaderAPITextureHandle_t textureHandle)
{
LOCK_SHADERAPI();
SetTextureState(sampler, textureHandle);
}
void CShaderAPIDx8::BindVertexTexture(VertexTextureSampler_t nStage, ShaderAPITextureHandle_t textureHandle)
{
Assert(g_pMaterialSystemHardwareConfig->GetVertexTextureCount() != 0);
LOCK_SHADERAPI();
ADD_VERTEX_TEXTURE_FUNC(COMMIT_PER_PASS, COMMIT_VERTEX_SHADER,
CommitVertexTextures, nStage, m_BoundTexture, textureHandle);
}
//-----------------------------------------------------------------------------
// Texture allocation/deallocation
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Computes stats info for a texture
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ComputeStatsInfo(ShaderAPITextureHandle_t hTexture, bool isCubeMap, bool isVolumeTexture)
{
Texture_t& textureData = GetTexture(hTexture);
textureData.m_SizeBytes = 0;
textureData.m_SizeTexels = 0;
textureData.m_LastBoundFrame = -1;
if (IsX360())
{
textureData.m_nTimesBoundThisFrame = 0;
}
IDirect3DBaseTexture* pD3DTex = CShaderAPIDx8::GetD3DTexture(hTexture);
if (IsPC() || !IsX360())
{
if (isCubeMap)
{
IDirect3DCubeTexture* pTex = static_cast<IDirect3DCubeTexture*>(pD3DTex);
if (!pTex)
{
Assert(0);
return;
}
int numLevels = pTex->GetLevelCount();
for (int i = 0; i < numLevels; ++i)
{
D3DSURFACE_DESC desc;
HRESULT hr = pTex->GetLevelDesc(i, &desc);
Assert(!FAILED(hr));
textureData.m_SizeBytes += 6 * ImageLoader::GetMemRequired(desc.Width, desc.Height, 1, textureData.GetImageFormat(), false);
textureData.m_SizeTexels += 6 * desc.Width * desc.Height;
}
}
else if (isVolumeTexture)
{
IDirect3DVolumeTexture9* pTex = static_cast<IDirect3DVolumeTexture9*>(pD3DTex);
if (!pTex)
{
Assert(0);
return;
}
int numLevels = pTex->GetLevelCount();
for (int i = 0; i < numLevels; ++i)
{
D3DVOLUME_DESC desc;
HRESULT hr = pTex->GetLevelDesc(i, &desc);
Assert(!FAILED(hr));
textureData.m_SizeBytes += ImageLoader::GetMemRequired(desc.Width, desc.Height, desc.Depth, textureData.GetImageFormat(), false);
textureData.m_SizeTexels += desc.Width * desc.Height;
}
}
else
{
IDirect3DTexture* pTex = static_cast<IDirect3DTexture*>(pD3DTex);
if (!pTex)
{
Assert(0);
return;
}
int numLevels = pTex->GetLevelCount();
for (int i = 0; i < numLevels; ++i)
{
D3DSURFACE_DESC desc;
HRESULT hr = pTex->GetLevelDesc(i, &desc);
Assert(!FAILED(hr));
textureData.m_SizeBytes += ImageLoader::GetMemRequired(desc.Width, desc.Height, 1, textureData.GetImageFormat(), false);
textureData.m_SizeTexels += desc.Width * desc.Height;
}
}
}
#if defined( _X360 )
// 360 uses gpu storage size (which accounts for page alignment bloat), not format size
textureData.m_SizeBytes = g_TextureHeap.GetSize(pD3DTex);
#endif
}
static D3DFORMAT ComputeFormat(IDirect3DBaseTexture* pTexture, bool isCubeMap)
{
Assert(pTexture);
D3DSURFACE_DESC desc;
if (isCubeMap)
{
IDirect3DCubeTexture* pTex = static_cast<IDirect3DCubeTexture*>(pTexture);
HRESULT hr = pTex->GetLevelDesc(0, &desc);
Assert(!FAILED(hr));
}
else
{
IDirect3DTexture* pTex = static_cast<IDirect3DTexture*>(pTexture);
HRESULT hr = pTex->GetLevelDesc(0, &desc);
Assert(!FAILED(hr));
}
return desc.Format;
}
ShaderAPITextureHandle_t CShaderAPIDx8::CreateDepthTexture(
ImageFormat renderTargetFormat,
int width,
int height,
const char* pDebugName,
bool bTexture)
{
LOCK_SHADERAPI();
ShaderAPITextureHandle_t i = CreateTextureHandle();
Texture_t* pTexture = &GetTexture(i);
pTexture->m_Flags = Texture_t::IS_ALLOCATED;
if (bTexture)
{
pTexture->m_Flags |= Texture_t::IS_DEPTH_STENCIL_TEXTURE;
}
else
{
pTexture->m_Flags |= Texture_t::IS_DEPTH_STENCIL;
}
pTexture->m_DebugName = pDebugName;
pTexture->m_Width = width;
pTexture->m_Height = height;
pTexture->m_Depth = 1; // fake
pTexture->m_Count = 1; // created single texture
pTexture->m_CountIndex = 0; // created single texture
pTexture->m_CreationFlags = 0; // fake
pTexture->m_NumCopies = 1;
pTexture->m_CurrentCopy = 0;
ImageFormat renderFormat = FindNearestSupportedFormat(renderTargetFormat, false, true, false);
#if defined( _X360 )
D3DFORMAT nDepthFormat = ReverseDepthOnX360() ? D3DFMT_D24FS8 : D3DFMT_D24S8;
#else
D3DFORMAT nDepthFormat = m_bUsingStencil ? D3DFMT_D24S8 : D3DFMT_D24X8;
#endif
D3DFORMAT format = FindNearestSupportedDepthFormat(m_nAdapter, m_AdapterFormat, renderFormat, nDepthFormat);
D3DMULTISAMPLE_TYPE multisampleType = D3DMULTISAMPLE_NONE;
pTexture->m_NumLevels = 1;
pTexture->m_SizeTexels = width * height;
pTexture->m_SizeBytes = ImageLoader::GetMemRequired(width, height, 1, renderFormat, false);
RECORD_COMMAND(DX8_CREATE_DEPTH_TEXTURE, 5);
RECORD_INT(i);
RECORD_INT(width);
RECORD_INT(height);
RECORD_INT(format);
RECORD_INT(multisampleType);
HRESULT hr;
if (!bTexture)
{
#if defined( _X360 )
int backWidth, backHeight;
ShaderAPI()->GetBackBufferDimensions(backWidth, backHeight);
D3DFORMAT backBufferFormat = ImageLoader::ImageFormatToD3DFormat(g_pShaderDevice->GetBackBufferFormat());
// immediately follows back buffer in EDRAM
D3DSURFACE_PARAMETERS surfParameters;
surfParameters.Base = 2 * XGSurfaceSize(backWidth, backHeight, backBufferFormat, D3DMULTISAMPLE_NONE);
surfParameters.ColorExpBias = 0;
surfParameters.HierarchicalZBase = 0;
hr = Dx9Device()->CreateDepthStencilSurface(
width, height, format, multisampleType, 0, TRUE, &pTexture->GetDepthStencilSurface(), &surfParameters);
#else
hr = Dx9Device()->CreateDepthStencilSurface(
width, height, format, multisampleType, 0, TRUE, &pTexture->GetDepthStencilSurface(), NULL);
#endif
}
else
{
IDirect3DTexture9* pTex;
hr = Dx9Device()->CreateTexture(width, height, 1, D3DUSAGE_DEPTHSTENCIL, format, D3DPOOL_DEFAULT, &pTex, NULL);
pTexture->SetTexture(pTex);
}
if (FAILED(hr))
{
switch (hr)
{
case D3DERR_INVALIDCALL:
Warning("ShaderAPIDX8::CreateDepthStencilSurface: D3DERR_INVALIDCALL\n");
break;
case D3DERR_OUTOFVIDEOMEMORY:
Warning("ShaderAPIDX8::CreateDepthStencilSurface: D3DERR_OUTOFVIDEOMEMORY\n");
break;
default:
break;
}
Assert(0);
}
#ifdef _XBOX
D3DSURFACE_DESC desc;
hr = pTexture->GetDepthStencilSurface()->GetDesc(&desc);
Assert(!FAILED(hr));
pTexture->m_nTimesBoundThisFrame = 0;
pTexture->m_StaticSizeBytes = desc.Size;
pTexture->m_DynamicSizeBytes = 0;
pTexture->m_DebugName = pDebugName;
pTexture->m_TextureGroupName = TEXTURE_GROUP_RENDER_TARGET;
pTexture->SetImageFormat(IMAGE_FORMAT_UNKNOWN);
#endif
return i;
}
// FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Could keep a free-list for this instead of linearly searching. We
// don't create textures all the time, so this is probably fine for now.
// FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ShaderAPITextureHandle_t CShaderAPIDx8::CreateTextureHandle(void)
{
ShaderAPITextureHandle_t handle;
CreateTextureHandles(&handle, 1);
return handle;
}
void CShaderAPIDx8::CreateTextureHandles(ShaderAPITextureHandle_t* handles, int count)
{
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)
{
LOCK_SHADERAPI();
tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "%s - PostLock", __FUNCTION__);
Assert(this == g_pShaderAPI);
if (depth == 0)
{
depth = 1;
}
bool isCubeMap = (creationFlags & TEXTURE_CREATE_CUBEMAP) != 0;
bool isRenderTarget = (creationFlags & TEXTURE_CREATE_RENDERTARGET) != 0;
bool managed = (creationFlags & TEXTURE_CREATE_MANAGED) != 0;
bool isDepthBuffer = (creationFlags & TEXTURE_CREATE_DEPTHBUFFER) != 0;
bool isDynamic = (creationFlags & TEXTURE_CREATE_DYNAMIC) != 0;
bool isSRGB = (creationFlags & TEXTURE_CREATE_SRGB) != 0; // for Posix/GL only... not used here ?
#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9)
extern bool g_ShaderDeviceUsingD3D9Ex;
if (g_ShaderDeviceUsingD3D9Ex && managed)
{
// Managed textures aren't available under D3D9Ex, but we never lose
// texture data, so it's ok to use the default pool. Really. We can't
// lock default-pool textures like we normally would to upload, but we
// have special logic to blit full updates via D3DX helper functions
// in D3D9Ex mode (see texturedx8.cpp)
managed = false;
creationFlags &= ~TEXTURE_CREATE_MANAGED;
}
#endif
// Can't be both managed + dynamic. Dynamic is an optimization, but
// if it's not managed, then we gotta do special client-specific stuff
// So, managed wins out!
if (managed)
{
creationFlags &= ~TEXTURE_CREATE_DYNAMIC;
isDynamic = false;
}
// Create a set of texture handles
CreateTextureHandles(pHandles, count);
Texture_t** arrTxp = (Texture_t**)stackalloc(count * sizeof(Texture_t*));
unsigned short usSetFlags = 0;
usSetFlags |= (IsPosix() || (creationFlags & (TEXTURE_CREATE_DYNAMIC | TEXTURE_CREATE_MANAGED))) ? Texture_t::IS_LOCKABLE : 0;
usSetFlags |= (creationFlags & TEXTURE_CREATE_VERTEXTEXTURE) ? Texture_t::IS_VERTEX_TEXTURE : 0;
#if defined( _X360 )
usSetFlags |= (creationFlags & TEXTURE_CREATE_RENDERTARGET) ? Texture_t::IS_RENDER_TARGET : 0;
usSetFlags |= (creationFlags & TEXTURE_CREATE_CANCONVERTFORMAT) ? Texture_t::CAN_CONVERT_FORMAT : 0;
#endif
tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "%s - CreateFrames", __FUNCTION__);
for (int idxFrame = 0; idxFrame < count; ++idxFrame)
{
arrTxp[idxFrame] = &GetTexture(pHandles[idxFrame]);
Texture_t* pTexture = arrTxp[idxFrame];
pTexture->m_Flags = Texture_t::IS_ALLOCATED;
pTexture->m_DebugName = pDebugName;
pTexture->m_Width = width;
pTexture->m_Height = height;
pTexture->m_Depth = depth;
pTexture->m_Count = count;
pTexture->m_CountIndex = idxFrame;
pTexture->m_CreationFlags = creationFlags;
pTexture->m_Flags |= usSetFlags;
RECORD_COMMAND(DX8_CREATE_TEXTURE, 12);
RECORD_INT(textureHandle);
RECORD_INT(width);
RECORD_INT(height);
RECORD_INT(depth); // depth for volume textures
RECORD_INT(ImageLoader::ImageFormatToD3DFormat(FindNearestSupportedFormat(dstImageFormat)));
RECORD_INT(numMipLevels);
RECORD_INT(isCubeMap);
RECORD_INT(numCopies <= 1 ? 1 : numCopies);
RECORD_INT(isRenderTarget ? 1 : 0);
RECORD_INT(managed);
RECORD_INT(isDepthBuffer ? 1 : 0);
RECORD_INT(isDynamic ? 1 : 0);
IDirect3DBaseTexture* pD3DTex;
// Set the initial texture state
if (numCopies <= 1)
{
tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "%s - CreateD3DTexture", __FUNCTION__);
pTexture->m_NumCopies = 1;
pD3DTex = CreateD3DTexture(width, height, depth, dstImageFormat, numMipLevels, creationFlags, (char*)pDebugName);
pTexture->SetTexture(pD3DTex);
}
else
{
tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "%s - CreateD3DTexture", __FUNCTION__);
pTexture->m_NumCopies = numCopies;
{
// X360TEMP
// MEM_ALLOC_CREDIT();
pTexture->GetTextureArray() = new IDirect3DBaseTexture * [numCopies];
}
for (int k = 0; k < numCopies; ++k)
{
pD3DTex = CreateD3DTexture(width, height, depth, dstImageFormat, numMipLevels, creationFlags, (char*)pDebugName);
pTexture->SetTexture(k, pD3DTex);
}
}
pTexture->m_CurrentCopy = 0;
pD3DTex = CShaderAPIDx8::GetD3DTexture(pHandles[idxFrame]);
#if defined( _X360 )
if (pD3DTex)
{
D3DSURFACE_DESC desc;
HRESULT hr;
if (creationFlags & TEXTURE_CREATE_CUBEMAP)
{
hr = ((IDirect3DCubeTexture*)pD3DTex)->GetLevelDesc(0, &desc);
}
else
{
hr = ((IDirect3DTexture*)pD3DTex)->GetLevelDesc(0, &desc);
}
Assert(!FAILED(hr));
// for proper info get the actual format because the input format may have been redirected
dstImageFormat = ImageLoader::D3DFormatToImageFormat(desc.Format);
Assert(dstImageFormat != IMAGE_FORMAT_UNKNOWN);
// track linear or tiled
if (!XGIsTiledFormat(desc.Format))
{
pTexture->m_Flags |= Texture_t::IS_LINEAR;
}
}
#endif
pTexture->SetImageFormat(dstImageFormat);
pTexture->m_UTexWrap = D3DTADDRESS_CLAMP;
pTexture->m_VTexWrap = D3DTADDRESS_CLAMP;
pTexture->m_WTexWrap = D3DTADDRESS_CLAMP;
if (isRenderTarget)
{
#if !defined( _X360 )
if ((dstImageFormat == IMAGE_FORMAT_NV_INTZ) || (dstImageFormat == IMAGE_FORMAT_NV_RAWZ) ||
(dstImageFormat == IMAGE_FORMAT_ATI_DST16) || (dstImageFormat == IMAGE_FORMAT_ATI_DST24))
{
pTexture->m_MinFilter = pTexture->m_MagFilter = D3DTEXF_POINT;
}
else
#endif
{
pTexture->m_MinFilter = pTexture->m_MagFilter = D3DTEXF_LINEAR;
}
pTexture->m_NumLevels = 1;
pTexture->m_MipFilter = D3DTEXF_NONE;
}
else
{
pTexture->m_NumLevels = pD3DTex ? pD3DTex->GetLevelCount() : 1;
pTexture->m_MipFilter = (pTexture->m_NumLevels != 1) ? D3DTEXF_LINEAR : D3DTEXF_NONE;
pTexture->m_MinFilter = pTexture->m_MagFilter = D3DTEXF_LINEAR;
}
pTexture->m_SwitchNeeded = false;
ComputeStatsInfo(pHandles[idxFrame], isCubeMap, (depth > 1));
SetupTextureGroup(pHandles[idxFrame], pTextureGroupName);
}
}
void CShaderAPIDx8::SetupTextureGroup(ShaderAPITextureHandle_t hTexture, const char* pTextureGroupName)
{
Texture_t* pTexture = &GetTexture(hTexture);
Assert(!pTexture->m_pTextureGroupCounterGlobal);
// Setup the texture group stuff.
if (pTextureGroupName && pTextureGroupName[0] != 0)
{
pTexture->m_TextureGroupName = pTextureGroupName;
}
else
{
pTexture->m_TextureGroupName = TEXTURE_GROUP_UNACCOUNTED;
}
// 360 cannot vprof due to multicore loading until vprof is reentrant and these counters are real.
#if defined( VPROF_ENABLED ) && !defined( _X360 )
char counterName[256];
Q_snprintf(counterName, sizeof(counterName), "TexGroup_global_%s", pTexture->m_TextureGroupName.String());
pTexture->m_pTextureGroupCounterGlobal = g_VProfCurrentProfile.FindOrCreateCounter(counterName, COUNTER_GROUP_TEXTURE_GLOBAL);
Q_snprintf(counterName, sizeof(counterName), "TexGroup_frame_%s", pTexture->m_TextureGroupName.String());
pTexture->m_pTextureGroupCounterFrame = g_VProfCurrentProfile.FindOrCreateCounter(counterName, COUNTER_GROUP_TEXTURE_PER_FRAME);
#else
pTexture->m_pTextureGroupCounterGlobal = NULL;
pTexture->m_pTextureGroupCounterFrame = NULL;
#endif
if (pTexture->m_pTextureGroupCounterGlobal)
{
*pTexture->m_pTextureGroupCounterGlobal += pTexture->GetMemUsage();
}
}
//-----------------------------------------------------------------------------
// Deletes a texture...
//-----------------------------------------------------------------------------
void CShaderAPIDx8::DeleteD3DTexture(ShaderAPITextureHandle_t hTexture)
{
int numDeallocated = 0;
Texture_t& texture = GetTexture(hTexture);
if (texture.m_Flags & Texture_t::IS_DEPTH_STENCIL)
{
// garymcthack - need to make sure that playback knows how to deal with these.
RECORD_COMMAND(DX8_DESTROY_DEPTH_TEXTURE, 1);
RECORD_INT(hTexture);
if (texture.GetDepthStencilSurface())
{
int nRetVal = texture.GetDepthStencilSurface()->Release();
Assert(nRetVal == 0);
texture.GetDepthStencilSurface() = 0;
numDeallocated = 1;
}
else
{
// FIXME: we hit this on shutdown of HLMV on some machines
Assert(0);
}
}
else if (texture.m_NumCopies == 1)
{
if (texture.GetTexture())
{
RECORD_COMMAND(DX8_DESTROY_TEXTURE, 1);
RECORD_INT(hTexture);
DestroyD3DTexture(texture.GetTexture());
texture.SetTexture(0);
numDeallocated = 1;
}
}
else
{
if (texture.GetTextureArray())
{
RECORD_COMMAND(DX8_DESTROY_TEXTURE, 1);
RECORD_INT(hTexture);
// Multiple copy texture
for (int j = 0; j < texture.m_NumCopies; ++j)
{
if (texture.GetTexture(j))
{
DestroyD3DTexture(texture.GetTexture(j));
texture.SetTexture(j, 0);
++numDeallocated;
}
}
delete[] texture.GetTextureArray();
texture.GetTextureArray() = 0;
}
}
texture.m_NumCopies = 0;
// Remove this texture from its global texture group counter.
if (texture.m_pTextureGroupCounterGlobal)
{
*texture.m_pTextureGroupCounterGlobal -= texture.GetMemUsage();
Assert(*texture.m_pTextureGroupCounterGlobal >= 0);
texture.m_pTextureGroupCounterGlobal = NULL;
}
// remove this texture from std textures
for (int i = 0; i < ARRAYSIZE(m_StdTextureHandles); i++)
{
if (m_StdTextureHandles[i] == hTexture)
m_StdTextureHandles[i] = INVALID_SHADERAPI_TEXTURE_HANDLE;
}
}
//-----------------------------------------------------------------------------
// Unbinds a texture from all texture stages
//-----------------------------------------------------------------------------
void CShaderAPIDx8::UnbindTexture(ShaderAPITextureHandle_t hTexture)
{
// Make sure no texture units are currently bound to it...
for (int unit = 0; unit < g_pHardwareConfig->GetSamplerCount(); ++unit)
{
if (hTexture == SamplerState(unit).m_BoundTexture)
{
// Gotta set this here because INVALID_SHADERAPI_TEXTURE_HANDLE means don't actually
// set bound texture (it's used for disabling texturemapping)
SamplerState(unit).m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE;
SetTextureState((Sampler_t)unit, INVALID_SHADERAPI_TEXTURE_HANDLE);
}
}
int nVertexTextureCount = g_pHardwareConfig->GetVertexTextureCount();
for (int nSampler = 0; nSampler < nVertexTextureCount; ++nSampler)
{
if (hTexture == m_DynamicState.m_VertexTextureState[nSampler].m_BoundTexture)
{
// Gotta set this here because INVALID_SHADERAPI_TEXTURE_HANDLE means don't actually
// set bound texture (it's used for disabling texturemapping)
BindVertexTexture((VertexTextureSampler_t)nSampler, INVALID_SHADERAPI_TEXTURE_HANDLE);
}
}
}
//-----------------------------------------------------------------------------
// Deletes a texture...
//-----------------------------------------------------------------------------
void CShaderAPIDx8::DeleteTexture(ShaderAPITextureHandle_t textureHandle)
{
LOCK_SHADERAPI();
AssertValidTextureHandle(textureHandle);
if (!TextureIsAllocated(textureHandle))
{
// already deallocated
return;
}
// Unbind it!
UnbindTexture(textureHandle);
// Delete it baby
DeleteD3DTexture(textureHandle);
// Now remove the texture from the list
// Mark as deallocated so that it can be reused.
GetTexture(textureHandle).m_Flags = 0;
}
void CShaderAPIDx8::WriteTextureToFile(ShaderAPITextureHandle_t hTexture, const char* szFileName)
{
Texture_t* pTexInt = &GetTexture(hTexture);
//Assert( pTexInt->IsCubeMap() == false );
//Assert( pTexInt->IsVolumeTexture() == false );
IDirect3DTexture* pD3DTexture = (IDirect3DTexture*)pTexInt->GetTexture();
// Get the level of the texture we want to read from
IDirect3DSurface* pTextureLevel;
HRESULT hr = pD3DTexture->GetSurfaceLevel(0, &pTextureLevel);
if (FAILED(hr))
return;
D3DSURFACE_DESC surfaceDesc;
pD3DTexture->GetLevelDesc(0, &surfaceDesc);
D3DLOCKED_RECT lockedRect;
//if( pTexInt->m_Flags & Texture_t::IS_RENDER_TARGET )
#if !defined( _X360 ) //TODO: x360 version
{
//render targets can't be locked, luckily we can copy the surface to system memory and lock that.
IDirect3DSurface* pSystemSurface;
Assert(!IsX360());
hr = Dx9Device()->CreateOffscreenPlainSurface(surfaceDesc.Width, surfaceDesc.Height, surfaceDesc.Format, D3DPOOL_SYSTEMMEM, &pSystemSurface, NULL);
Assert(SUCCEEDED(hr));
pSystemSurface->GetDesc(&surfaceDesc);
hr = Dx9Device()->GetRenderTargetData(pTextureLevel, pSystemSurface);
Assert(SUCCEEDED(hr));
//pretend this is the texture level we originally grabbed with GetSurfaceLevel() and continue on
pTextureLevel->Release();
pTextureLevel = pSystemSurface;
}
#endif
// lock the region
if (FAILED(pTextureLevel->LockRect(&lockedRect, NULL, D3DLOCK_READONLY)))
{
Assert(0);
pTextureLevel->Release();
return;
}
TGAWriter::WriteTGAFile(szFileName, surfaceDesc.Width, surfaceDesc.Height, pTexInt->GetImageFormat(), (const uint8*)lockedRect.pBits, lockedRect.Pitch);
if (FAILED(pTextureLevel->UnlockRect()))
{
Assert(0);
pTextureLevel->Release();
return;
}
pTextureLevel->Release();
}
//-----------------------------------------------------------------------------
// Releases all textures
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ReleaseAllTextures()
{
ClearStdTextureHandles();
ShaderAPITextureHandle_t hTexture;
for (hTexture = m_Textures.Head(); hTexture != m_Textures.InvalidIndex(); hTexture = m_Textures.Next(hTexture))
{
if (TextureIsAllocated(hTexture))
{
// Delete it baby
DeleteD3DTexture(hTexture);
}
}
// Make sure all texture units are pointing to nothing
for (int unit = 0; unit < g_pHardwareConfig->GetSamplerCount(); ++unit)
{
SamplerState(unit).m_BoundTexture = INVALID_SHADERAPI_TEXTURE_HANDLE;
SetTextureState((Sampler_t)unit, INVALID_SHADERAPI_TEXTURE_HANDLE);
}
}
void CShaderAPIDx8::DeleteAllTextures()
{
ReleaseAllTextures();
m_Textures.Purge();
}
bool CShaderAPIDx8::IsTexture(ShaderAPITextureHandle_t textureHandle)
{
LOCK_SHADERAPI();
if (!TextureIsAllocated(textureHandle))
{
return false;
}
#if !defined( _X360 )
if (GetTexture(textureHandle).m_Flags & Texture_t::IS_DEPTH_STENCIL)
{
return GetTexture(textureHandle).GetDepthStencilSurface() != 0;
}
else if ((GetTexture(textureHandle).m_NumCopies == 1 && GetTexture(textureHandle).GetTexture() != 0) ||
(GetTexture(textureHandle).m_NumCopies > 1 && GetTexture(textureHandle).GetTexture(0) != 0))
{
return true;
}
else
{
return false;
}
#else
// query is about texture handle validity, not presence
// texture handle is allocated, texture may or may not be present
return true;
#endif
}
//-----------------------------------------------------------------------------
// Gets the surface associated with a texture (refcount of surface is increased)
//-----------------------------------------------------------------------------
IDirect3DSurface* CShaderAPIDx8::GetTextureSurface(ShaderAPITextureHandle_t textureHandle)
{
MEM_ALLOC_D3D_CREDIT();
IDirect3DSurface* pSurface;
// We'll be modifying this sucka
AssertValidTextureHandle(textureHandle);
Texture_t& tex = GetTexture(textureHandle);
if (!(tex.m_Flags & Texture_t::IS_ALLOCATED))
{
return NULL;
}
if (IsX360() && (tex.m_Flags & Texture_t::IS_RENDER_TARGET_SURFACE))
{
pSurface = tex.GetRenderTargetSurface(false);
#if POSIX
// dxabstract's AddRef/Release have optional args to help track usage
pSurface->AddRef(0, "CShaderAPIDx8::GetTextureSurface public addref");
#else
pSurface->AddRef();
#endif
return pSurface;
}
IDirect3DBaseTexture* pD3DTex = CShaderAPIDx8::GetD3DTexture(textureHandle);
IDirect3DTexture* pTex = static_cast<IDirect3DTexture*>(pD3DTex);
Assert(pTex);
if (!pTex)
{
return NULL;
}
HRESULT hr = pTex->GetSurfaceLevel(0, &pSurface);
Assert(hr == D3D_OK);
return pSurface;
}
//-----------------------------------------------------------------------------
// Gets the surface associated with a texture (refcount of surface is increased)
//-----------------------------------------------------------------------------
IDirect3DSurface* CShaderAPIDx8::GetDepthTextureSurface(ShaderAPITextureHandle_t textureHandle)
{
AssertValidTextureHandle(textureHandle);
if (!TextureIsAllocated(textureHandle))
{
return NULL;
}
return GetTexture(textureHandle).GetDepthStencilSurface();
}
//-----------------------------------------------------------------------------
// Changes the render target
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetRenderTargetEx(int nRenderTargetID, ShaderAPITextureHandle_t colorTextureHandle, ShaderAPITextureHandle_t depthTextureHandle)
{
LOCK_SHADERAPI();
if (IsDeactivated())
{
return;
}
// GR - need to flush batched geometry
FlushBufferedPrimitives();
#if defined( PIX_INSTRUMENTATION )
{
const char* pRT = "Backbuffer";
const char* pDS = "DefaultDepthStencil";
if (colorTextureHandle == SHADER_RENDERTARGET_NONE)
{
pRT = "None";
}
else if (colorTextureHandle != SHADER_RENDERTARGET_BACKBUFFER)
{
Texture_t& tex = GetTexture(colorTextureHandle);
pRT = tex.m_DebugName.String();
}
if (depthTextureHandle == SHADER_RENDERTARGET_NONE)
{
pDS = "None";
}
else if (depthTextureHandle != SHADER_RENDERTARGET_DEPTHBUFFER)
{
Texture_t& tex = GetTexture(depthTextureHandle);
pDS = tex.m_DebugName.String();
}
char buf[256];
sprintf(buf, "SRT:%s %s", pRT ? pRT : "?", pDS ? pDS : "?");
BeginPIXEvent(0xFFFFFFFF, buf);
EndPIXEvent();
}
#endif
#if !defined( _X360 )
RECORD_COMMAND(DX8_TEST_COOPERATIVE_LEVEL, 0);
HRESULT hr = Dx9Device()->TestCooperativeLevel();
if (hr != D3D_OK)
{
MarkDeviceLost();
return;
}
#endif
IDirect3DSurface* pColorSurface = NULL;
IDirect3DSurface* pZSurface = NULL;
RECORD_COMMAND(DX8_SET_RENDER_TARGET, 3);
RECORD_INT(nRenderTargetID);
RECORD_INT(colorTextureHandle);
RECORD_INT(depthTextureHandle);
// The 0th render target defines which depth buffer we are using, so
// don't bother if we are another render target
if (nRenderTargetID > 0)
{
depthTextureHandle = SHADER_RENDERTARGET_NONE;
}
// NOTE!!!! If this code changes, also change Dx8SetRenderTarget in playback.cpp
bool usingTextureTarget = false;
if (colorTextureHandle == SHADER_RENDERTARGET_BACKBUFFER)
{
pColorSurface = m_pBackBufferSurface;
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
if (pColorSurface)
#endif
{
// This is just to make the code a little simpler...
// (simplifies the release logic)
#if POSIX
// dxabstract's AddRef/Release have optional args to help track usage
pColorSurface->AddRef(0, "+C CShaderAPIDx8::SetRenderTargetEx public addref 1");
#else
pColorSurface->AddRef();
#endif
}
}
else
{
// get the texture (Refcount increases)
UnbindTexture(colorTextureHandle);
pColorSurface = GetTextureSurface(colorTextureHandle);
if (!pColorSurface)
{
return;
}
usingTextureTarget = true;
}
if (depthTextureHandle == SHADER_RENDERTARGET_DEPTHBUFFER)
{
// using the default depth buffer
pZSurface = m_pZBufferSurface;
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
if (pZSurface)
#endif
{
// simplify the prologue logic
#if POSIX
// dxabstract's AddRef/Release have optional args to help track usage
pZSurface->AddRef(0, "+D CShaderAPIDx8::SetRenderTargetEx public addref 1");
#else
pZSurface->AddRef();
#endif
}
}
else if (depthTextureHandle == SHADER_RENDERTARGET_NONE)
{
// GR - disable depth buffer
pZSurface = NULL;
}
else
{
UnbindTexture(depthTextureHandle);
Texture_t& tex = GetTexture(depthTextureHandle);
//Cannot use a depth/stencil surface derived from a texture.
//Asserting helps get the whole call stack instead of letting the 360 report an error with a partial stack
Assert(!(IsX360() && (tex.m_Flags & Texture_t::IS_DEPTH_STENCIL_TEXTURE)));
if (tex.m_Flags & Texture_t::IS_DEPTH_STENCIL)
{
pZSurface = GetDepthTextureSurface(depthTextureHandle);
if (pZSurface)
{
#if POSIX
// dxabstract's AddRef/Release have optional args to help track usage
pZSurface->AddRef(0, "+D CShaderAPIDx8::SetRenderTargetEx public addref 2");
#else
pZSurface->AddRef();
#endif
}
}
else
{
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;
if (!pZSurface)
{
hr = pColorSurface->GetDesc(&desc);
}
else
{
hr = pZSurface->GetDesc(&desc);
}
Assert(!FAILED(hr));
m_ViewportMaxWidth = desc.Width;
m_ViewportMaxHeight = desc.Height;
}
static bool assert_on_refzero = false;
int ref;
if (pZSurface)
{
#if POSIX
ref = pZSurface->Release(0, "-D CShaderAPIDx8::SetRenderTargetEx public release (z surface)");
#else
ref = pZSurface->Release();
#endif
if (assert_on_refzero)
{
Assert(ref != 0);
}
}
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
if (pColorSurface)
#endif
{
#if POSIX
ref = pColorSurface->Release(0, "-C CShaderAPIDx8::SetRenderTargetEx public release (color surface)");
#else
ref = pColorSurface->Release();
#endif
if (assert_on_refzero)
{
Assert(ref != 0);
}
}
// Changing the render target sets a default viewport. Force rewrite to preserve the current desired state.
m_DynamicState.m_Viewport.X = 0;
m_DynamicState.m_Viewport.Y = 0;
m_DynamicState.m_Viewport.Width = (DWORD)-1;
m_DynamicState.m_Viewport.Height = (DWORD)-1;
ADD_COMMIT_FUNC(COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitSetViewports);
}
//-----------------------------------------------------------------------------
// Changes the render target
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetRenderTarget(ShaderAPITextureHandle_t colorTextureHandle, ShaderAPITextureHandle_t depthTextureHandle)
{
LOCK_SHADERAPI();
SetRenderTargetEx(0, colorTextureHandle, depthTextureHandle);
}
//-----------------------------------------------------------------------------
// Returns the nearest supported format
//-----------------------------------------------------------------------------
ImageFormat CShaderAPIDx8::GetNearestSupportedFormat(ImageFormat fmt, bool bFilteringRequired /* = true */) const
{
return FindNearestSupportedFormat(fmt, false, false, bFilteringRequired);
}
ImageFormat CShaderAPIDx8::GetNearestRenderTargetFormat(ImageFormat fmt) const
{
return FindNearestSupportedFormat(fmt, false, true, false);
}
bool CShaderAPIDx8::DoRenderTargetsNeedSeparateDepthBuffer() const
{
LOCK_SHADERAPI();
return m_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE;
}
//-----------------------------------------------------------------------------
// Indicates we're modifying a texture
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ModifyTexture(ShaderAPITextureHandle_t textureHandle)
{
tmZone(TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__);
LOCK_SHADERAPI();
// Can't do this if we're locked!
Assert(m_ModifyTextureLockedLevel < 0);
AssertValidTextureHandle(textureHandle);
m_ModifyTextureHandle = textureHandle;
// If we're got a multi-copy texture, we need to up the current copy count
Texture_t& tex = GetTexture(textureHandle);
if (tex.m_NumCopies > 1)
{
// Each time we modify a texture, we'll want to switch texture
// as soon as a TexImage2D call is made...
tex.m_SwitchNeeded = true;
}
}
//-----------------------------------------------------------------------------
// Advances the current copy of a texture...
//-----------------------------------------------------------------------------
void CShaderAPIDx8::AdvanceCurrentCopy(ShaderAPITextureHandle_t hTexture)
{
// May need to switch textures....
Texture_t& tex = GetTexture(hTexture);
if (tex.m_NumCopies > 1)
{
if (++tex.m_CurrentCopy >= tex.m_NumCopies)
tex.m_CurrentCopy = 0;
// When the current copy changes, we need to make sure this texture
// isn't bound to any stages any more; thereby guaranteeing the new
// copy will be re-bound.
UnbindTexture(hTexture);
}
}
//-----------------------------------------------------------------------------
// Locks, unlocks the current texture
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::TexLock(int level, int cubeFaceID, int xOffset, int yOffset,
int width, int height, CPixelWriter& writer)
{
tmZone(TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__);
LOCK_SHADERAPI();
Assert(m_ModifyTextureLockedLevel < 0);
ShaderAPITextureHandle_t hTexture = GetModifyTextureHandle();
if (!m_Textures.IsValidIndex(hTexture))
return false;
// Blow off mip levels if we don't support mipmapping
if (!g_pHardwareConfig->SupportsMipmapping() && (level > 0))
return false;
// This test here just makes sure we don't try to download mipmap levels
// if we weren't able to create them in the first place
Texture_t& tex = GetTexture(hTexture);
if (level >= tex.m_NumLevels)
{
return false;
}
// May need to switch textures....
if (tex.m_SwitchNeeded)
{
AdvanceCurrentCopy(hTexture);
tex.m_SwitchNeeded = false;
}
IDirect3DBaseTexture* pTexture = GetModifyTexture();
#if defined( _X360 )
// 360 can't lock a bound texture
if (pTexture->IsSet(Dx9Device()))
{
UnbindTexture(hTexture);
}
#endif
bool bOK = LockTexture(hTexture, tex.m_CurrentCopy, pTexture,
level, (D3DCUBEMAP_FACES)cubeFaceID, xOffset, yOffset, width, height, false, writer);
if (bOK)
{
m_ModifyTextureLockedLevel = level;
m_ModifyTextureLockedFace = cubeFaceID;
}
return bOK;
}
void CShaderAPIDx8::TexUnlock()
{
tmZone(TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__);
LOCK_SHADERAPI();
if (m_ModifyTextureLockedLevel >= 0)
{
Texture_t& tex = GetTexture(GetModifyTextureHandle());
UnlockTexture(GetModifyTextureHandle(), tex.m_CurrentCopy, GetModifyTexture(),
m_ModifyTextureLockedLevel, (D3DCUBEMAP_FACES)m_ModifyTextureLockedFace);
m_ModifyTextureLockedLevel = -1;
}
}
//-----------------------------------------------------------------------------
// Texture image upload
//-----------------------------------------------------------------------------
void CShaderAPIDx8::TexImage2D(
int level,
int cubeFaceID,
ImageFormat dstFormat,
int z,
int width,
int height,
ImageFormat srcFormat,
bool bSrcIsTiled,
void* pSrcData)
{
LOCK_SHADERAPI();
Assert(pSrcData);
AssertValidTextureHandle(GetModifyTextureHandle());
if (!m_Textures.IsValidIndex(GetModifyTextureHandle()))
{
return;
}
Assert((width <= g_pHardwareConfig->Caps().m_MaxTextureWidth) && (height <= g_pHardwareConfig->Caps().m_MaxTextureHeight));
// Blow off mip levels if we don't support mipmapping
if (!g_pHardwareConfig->SupportsMipmapping() && (level > 0))
{
return;
}
// This test here just makes sure we don't try to download mipmap levels
// if we weren't able to create them in the first place
Texture_t& tex = GetTexture(GetModifyTextureHandle());
if (level >= tex.m_NumLevels)
{
return;
}
// May need to switch textures....
if (tex.m_SwitchNeeded)
{
AdvanceCurrentCopy(GetModifyTextureHandle());
tex.m_SwitchNeeded = false;
}
TextureLoadInfo_t info;
info.m_TextureHandle = GetModifyTextureHandle();
info.m_pTexture = GetModifyTexture();
info.m_nLevel = level;
info.m_nCopy = tex.m_CurrentCopy;
info.m_CubeFaceID = (D3DCUBEMAP_FACES)cubeFaceID;
info.m_nWidth = width;
info.m_nHeight = height;
info.m_nZOffset = z;
info.m_SrcFormat = srcFormat;
info.m_pSrcData = (unsigned char*)pSrcData;
#if defined( _X360 )
info.m_bSrcIsTiled = bSrcIsTiled;
info.m_bCanConvertFormat = (tex.m_Flags & Texture_t::CAN_CONVERT_FORMAT) != 0;
#else
info.m_bTextureIsLockable = (tex.m_Flags & Texture_t::IS_LOCKABLE) != 0;
#endif
LoadTexture(info);
SetModifyTexture(info.m_pTexture);
}
//-----------------------------------------------------------------------------
// Upload to a sub-piece of a texture
//-----------------------------------------------------------------------------
void CShaderAPIDx8::TexSubImage2D(
int level,
int cubeFaceID,
int xOffset,
int yOffset,
int zOffset,
int width,
int height,
ImageFormat srcFormat,
int srcStride,
bool bSrcIsTiled,
void* pSrcData)
{
LOCK_SHADERAPI();
Assert(pSrcData);
AssertValidTextureHandle(GetModifyTextureHandle());
if (!m_Textures.IsValidIndex(GetModifyTextureHandle()))
{
return;
}
// Blow off mip levels if we don't support mipmapping
if (!g_pHardwareConfig->SupportsMipmapping() && (level > 0))
{
return;
}
// NOTE: This can only be done with procedural textures if this method is
// being used to download the entire texture, cause last frame's partial update
// may be in a completely different texture! Sadly, I don't have all of the
// information I need, but I can at least check a couple things....
#ifdef _DEBUG
if (GetTexture(GetModifyTextureHandle()).m_NumCopies > 1)
{
Assert((xOffset == 0) && (yOffset == 0));
}
#endif
// This test here just makes sure we don't try to download mipmap levels
// if we weren't able to create them in the first place
Texture_t& tex = GetTexture(GetModifyTextureHandle());
if (level >= tex.m_NumLevels)
{
return;
}
// May need to switch textures....
if (tex.m_SwitchNeeded)
{
AdvanceCurrentCopy(GetModifyTextureHandle());
tex.m_SwitchNeeded = false;
}
TextureLoadInfo_t info;
info.m_TextureHandle = GetModifyTextureHandle();
info.m_pTexture = GetModifyTexture();
info.m_nLevel = level;
info.m_nCopy = tex.m_CurrentCopy;
info.m_CubeFaceID = (D3DCUBEMAP_FACES)cubeFaceID;
info.m_nWidth = width;
info.m_nHeight = height;
info.m_nZOffset = zOffset;
info.m_SrcFormat = srcFormat;
info.m_pSrcData = (unsigned char*)pSrcData;
#if defined( _X360 )
info.m_bSrcIsTiled = bSrcIsTiled;
info.m_bCanConvertFormat = (tex.m_Flags & Texture_t::CAN_CONVERT_FORMAT) != 0;
#else
info.m_bTextureIsLockable = (tex.m_Flags & Texture_t::IS_LOCKABLE) != 0;
#endif
LoadSubTexture(info, xOffset, yOffset, srcStride);
}
//-----------------------------------------------------------------------------
// Volume texture upload
//-----------------------------------------------------------------------------
void CShaderAPIDx8::TexImageFromVTF(IVTFTexture* pVTF, int iVTFFrame)
{
LOCK_SHADERAPI();
Assert(pVTF);
AssertValidTextureHandle(GetModifyTextureHandle());
if (!m_Textures.IsValidIndex(GetModifyTextureHandle()))
{
return;
}
Texture_t& tex = GetTexture(GetModifyTextureHandle());
// May need to switch textures....
if (tex.m_SwitchNeeded)
{
AdvanceCurrentCopy(GetModifyTextureHandle());
tex.m_SwitchNeeded = false;
}
TextureLoadInfo_t info;
info.m_TextureHandle = GetModifyTextureHandle();
info.m_pTexture = GetModifyTexture();
info.m_nLevel = 0;
info.m_nCopy = tex.m_CurrentCopy;
info.m_CubeFaceID = (D3DCUBEMAP_FACES)0;
info.m_nWidth = 0;
info.m_nHeight = 0;
info.m_nZOffset = 0;
info.m_SrcFormat = pVTF->Format();
info.m_pSrcData = NULL;
#if defined( _X360 )
info.m_bSrcIsTiled = pVTF->IsPreTiled();
info.m_bCanConvertFormat = (tex.m_Flags & Texture_t::CAN_CONVERT_FORMAT) != 0;
#else
info.m_bTextureIsLockable = (tex.m_Flags & Texture_t::IS_LOCKABLE) != 0;
#endif
if (pVTF->Depth() > 1)
{
LoadVolumeTextureFromVTF(info, pVTF, iVTFFrame);
}
else if (pVTF->IsCubeMap())
{
if (HardwareConfig()->SupportsCubeMaps())
{
LoadCubeTextureFromVTF(info, pVTF, iVTFFrame);
}
else
{
info.m_CubeFaceID = (D3DCUBEMAP_FACES)6;
LoadTextureFromVTF(info, pVTF, iVTFFrame);
}
}
else
{
LoadTextureFromVTF(info, pVTF, iVTFFrame);
}
SetModifyTexture(info.m_pTexture);
}
//-----------------------------------------------------------------------------
// Is the texture resident?
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::IsTextureResident(ShaderAPITextureHandle_t textureHandle)
{
return true;
}
//-----------------------------------------------------------------------------
// Level of anisotropic filtering
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetAnisotropicLevel(int nAnisotropyLevel)
{
LOCK_SHADERAPI();
// NOTE: This must be called before the rest of the code in this function so
// anisotropic can be set per-texture to force it on! This will also avoid
// a possible infinite loop that existed before.
g_pShaderUtil->NoteAnisotropicLevel(nAnisotropyLevel);
// Never set this to 1. In the case we want it set to 1, we will use this to override
// aniso per-texture, so set it to something reasonable
if (nAnisotropyLevel > g_pHardwareConfig->Caps().m_nMaxAnisotropy || nAnisotropyLevel <= 1)
{
// Set it to 1/4 the max but between 2-8
nAnisotropyLevel = max(2, min(8, (g_pHardwareConfig->Caps().m_nMaxAnisotropy / 4)));
}
// Set the D3D max insotropy state for all samplers
for (int i = 0; i < g_pHardwareConfig->Caps().m_NumSamplers; ++i)
{
SamplerState(i).m_nAnisotropicLevel = nAnisotropyLevel;
SetSamplerState(i, D3DSAMP_MAXANISOTROPY, SamplerState(i).m_nAnisotropicLevel);
}
}
//-----------------------------------------------------------------------------
// Sets the priority
//-----------------------------------------------------------------------------
void CShaderAPIDx8::TexSetPriority(int priority)
{
#if !defined( _X360 )
LOCK_SHADERAPI();
// A hint to the cacher...
ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle();
if (hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE)
return;
Texture_t& tex = GetTexture(hModifyTexture);
if (tex.m_NumCopies > 1)
{
for (int i = 0; i < tex.m_NumCopies; ++i)
tex.GetTexture(i)->SetPriority(priority);
}
else
{
tex.GetTexture()->SetPriority(priority);
}
#endif
}
void CShaderAPIDx8::TexLodClamp(int finest)
{
LOCK_SHADERAPI();
ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle();
if (hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE)
return;
Texture_t& tex = GetTexture(hModifyTexture);
tex.m_FinestMipmapLevel = finest;
}
void CShaderAPIDx8::TexLodBias(float bias)
{
LOCK_SHADERAPI();
ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle();
if (hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE)
return;
Texture_t& tex = GetTexture(hModifyTexture);
tex.m_LodBias = bias;
}
//-----------------------------------------------------------------------------
// Texturemapping state
//-----------------------------------------------------------------------------
void CShaderAPIDx8::TexWrap(ShaderTexCoordComponent_t coord, ShaderTexWrapMode_t wrapMode)
{
LOCK_SHADERAPI();
ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle();
if (hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE)
return;
D3DTEXTUREADDRESS address;
switch (wrapMode)
{
case SHADER_TEXWRAPMODE_CLAMP:
address = D3DTADDRESS_CLAMP;
break;
case SHADER_TEXWRAPMODE_REPEAT:
address = D3DTADDRESS_WRAP;
break;
case SHADER_TEXWRAPMODE_BORDER:
address = D3DTADDRESS_BORDER;
break;
default:
address = D3DTADDRESS_CLAMP;
Warning("CShaderAPIDx8::TexWrap: unknown wrapMode\n");
break;
}
switch (coord)
{
case SHADER_TEXCOORD_S:
GetTexture(hModifyTexture).m_UTexWrap = address;
break;
case SHADER_TEXCOORD_T:
GetTexture(hModifyTexture).m_VTexWrap = address;
break;
case SHADER_TEXCOORD_U:
GetTexture(hModifyTexture).m_WTexWrap = address;
break;
default:
Warning("CShaderAPIDx8::TexWrap: unknown coord\n");
break;
}
}
void CShaderAPIDx8::TexMinFilter(ShaderTexFilterMode_t texFilterMode)
{
LOCK_SHADERAPI();
ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle();
if (hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE)
return;
switch (texFilterMode)
{
case SHADER_TEXFILTERMODE_NEAREST:
GetTexture(hModifyTexture).m_MinFilter = D3DTEXF_POINT;
GetTexture(hModifyTexture).m_MipFilter = D3DTEXF_NONE;
break;
case SHADER_TEXFILTERMODE_LINEAR:
GetTexture(hModifyTexture).m_MinFilter = D3DTEXF_LINEAR;
GetTexture(hModifyTexture).m_MipFilter = D3DTEXF_NONE;
break;
case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_NEAREST:
GetTexture(hModifyTexture).m_MinFilter = D3DTEXF_POINT;
GetTexture(hModifyTexture).m_MipFilter =
GetTexture(hModifyTexture).m_NumLevels != 1 ? D3DTEXF_POINT : D3DTEXF_NONE;
break;
case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST:
GetTexture(hModifyTexture).m_MinFilter = D3DTEXF_LINEAR;
GetTexture(hModifyTexture).m_MipFilter =
GetTexture(hModifyTexture).m_NumLevels != 1 ? D3DTEXF_POINT : D3DTEXF_NONE;
break;
case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_LINEAR:
GetTexture(hModifyTexture).m_MinFilter = D3DTEXF_POINT;
GetTexture(hModifyTexture).m_MipFilter =
GetTexture(hModifyTexture).m_NumLevels != 1 ? D3DTEXF_LINEAR : D3DTEXF_NONE;
break;
case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR:
GetTexture(hModifyTexture).m_MinFilter = D3DTEXF_LINEAR;
GetTexture(hModifyTexture).m_MipFilter =
GetTexture(hModifyTexture).m_NumLevels != 1 ? D3DTEXF_LINEAR : D3DTEXF_NONE;
break;
case SHADER_TEXFILTERMODE_ANISOTROPIC:
GetTexture(hModifyTexture).m_MinFilter = D3DTEXF_ANISOTROPIC;
GetTexture(hModifyTexture).m_MipFilter =
GetTexture(hModifyTexture).m_NumLevels != 1 ? D3DTEXF_LINEAR : D3DTEXF_NONE;
break;
default:
Warning("CShaderAPIDx8::TexMinFilter: Unknown texFilterMode\n");
break;
}
}
void CShaderAPIDx8::TexMagFilter(ShaderTexFilterMode_t texFilterMode)
{
LOCK_SHADERAPI();
ShaderAPITextureHandle_t hModifyTexture = GetModifyTextureHandle();
if (hModifyTexture == INVALID_SHADERAPI_TEXTURE_HANDLE)
return;
switch (texFilterMode)
{
case SHADER_TEXFILTERMODE_NEAREST:
GetTexture(hModifyTexture).m_MagFilter = D3DTEXF_POINT;
break;
case SHADER_TEXFILTERMODE_LINEAR:
GetTexture(hModifyTexture).m_MagFilter = D3DTEXF_LINEAR;
break;
case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_NEAREST:
Warning("CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_NEAREST_MIPMAP_NEAREST is invalid\n");
break;
case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST:
Warning("CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST is invalid\n");
break;
case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_LINEAR:
Warning("CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_NEAREST_MIPMAP_LINEAR is invalid\n");
break;
case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR:
Warning("CShaderAPIDx8::TexMagFilter: SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR is invalid\n");
break;
case SHADER_TEXFILTERMODE_ANISOTROPIC:
GetTexture(hModifyTexture).m_MagFilter = g_pHardwareConfig->Caps().m_bSupportsMagAnisotropicFiltering ? D3DTEXF_ANISOTROPIC : D3DTEXF_LINEAR;
break;
default:
Warning("CShaderAPIDx8::TexMAGFilter: Unknown texFilterMode\n");
break;
}
}
//-----------------------------------------------------------------------------
// Gets the matrix stack from the matrix mode
//-----------------------------------------------------------------------------
int CShaderAPIDx8::GetMatrixStack(MaterialMatrixMode_t mode) const
{
Assert(mode >= 0 && mode < NUM_MATRIX_MODES);
return mode;
}
//-----------------------------------------------------------------------------
// Returns true if we're modulating constant color into the vertex color
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::IsModulatingVertexColor() const
{
return m_TransitionTable.CurrentShadowShaderState()->m_ModulateConstantColor;
}
//-----------------------------------------------------------------------------
// Material property (used to deal with overbright for lights)
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetDefaultMaterial()
{
#if !defined( _X360 )
D3DMATERIAL mat;
mat.Diffuse.r = mat.Diffuse.g = mat.Diffuse.b = mat.Diffuse.a = 1.0f;
mat.Ambient.r = mat.Ambient.g = mat.Ambient.b = mat.Ambient.a = 0.0f;
mat.Specular.r = mat.Specular.g = mat.Specular.b = mat.Specular.a = 0.0f;
mat.Emissive.r = mat.Emissive.g = mat.Emissive.b = mat.Emissive.a = 0.0f;
mat.Power = 1.0f;
Dx9Device()->SetMaterial(&mat);
#endif
}
//-----------------------------------------------------------------------------
// lighting related methods
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetAmbientLight(float r, float g, float b)
{
LOCK_SHADERAPI();
unsigned int ambient = D3DCOLOR_ARGB(255, (int)(r * 255),
(int)(g * 255), (int)(b * 255));
if (ambient != m_DynamicState.m_Ambient)
{
m_DynamicState.m_Ambient = ambient;
SetSupportedRenderState(D3DRS_AMBIENT, ambient);
}
}
void CShaderAPIDx8::SetLightingOrigin(Vector vLightingOrigin)
{
if (vLightingOrigin != m_DynamicState.m_vLightingOrigin)
{
FlushBufferedPrimitives();
m_DynamicState.m_vLightingOrigin = vLightingOrigin;
}
}
//#define NO_LOCAL_LIGHTS
void CShaderAPIDx8::SetLight(int lightNum, const LightDesc_t& desc_)
{
LOCK_SHADERAPI();
#ifdef NO_LOCAL_LIGHTS
LightDesc_t desc = desc_;
desc.m_Type = MATERIAL_LIGHT_DISABLE;
#else
LightDesc_t& desc = const_cast<LightDesc_t&>(desc_); // to permit '&'
#endif
Assert(lightNum < g_pHardwareConfig->Caps().m_MaxNumLights&& lightNum >= 0);
if (lightNum >= g_pHardwareConfig->Caps().m_MaxNumLights || lightNum < 0)
return;
m_DynamicState.m_LightDescs[lightNum] = desc;
FlushBufferedPrimitives();
if (desc.m_Type == MATERIAL_LIGHT_DISABLE)
{
if (m_DynamicState.m_LightEnable[lightNum])
{
m_DynamicState.m_LightEnableChanged[lightNum] = STATE_CHANGED;
m_DynamicState.m_LightEnable[lightNum] = false;
}
return;
}
if (!m_DynamicState.m_LightEnable[lightNum])
{
m_DynamicState.m_LightEnableChanged[lightNum] = STATE_CHANGED;
m_DynamicState.m_LightEnable[lightNum] = true;
}
D3DLIGHT light;
switch (desc.m_Type)
{
case MATERIAL_LIGHT_POINT:
light.Type = D3DLIGHT_POINT;
light.Range = desc.m_Range;
break;
case MATERIAL_LIGHT_DIRECTIONAL:
light.Type = D3DLIGHT_DIRECTIONAL;
light.Range = 1e12; // This is supposed to be ignored
break;
case MATERIAL_LIGHT_SPOT:
light.Type = D3DLIGHT_SPOT;
light.Range = desc.m_Range;
break;
default:
m_DynamicState.m_LightEnable[lightNum] = false;
return;
}
// This is a D3D limitation
Assert((light.Range >= 0) && (light.Range <= sqrt(FLT_MAX)));
memcpy(&light.Diffuse, &desc.m_Color[0], 3 * sizeof(float));
memcpy(&light.Specular, &desc.m_Color[0], 3 * sizeof(float));
light.Diffuse.a = 1.0f;
light.Specular.a = 1.0f;
light.Ambient.a = light.Ambient.b = light.Ambient.g = light.Ambient.r = 0;
memcpy(&light.Position, &desc.m_Position[0], 3 * sizeof(float));
memcpy(&light.Direction, &desc.m_Direction[0], 3 * sizeof(float));
light.Falloff = desc.m_Falloff;
light.Attenuation0 = desc.m_Attenuation0;
light.Attenuation1 = desc.m_Attenuation1;
light.Attenuation2 = desc.m_Attenuation2;
// normalize light color...
light.Theta = desc.m_Theta;
light.Phi = desc.m_Phi;
if (light.Phi > M_PI)
light.Phi = M_PI;
// This piece of crap line of code is because if theta gets too close to phi,
// we get no light at all.
if (light.Theta - light.Phi > -1e-3)
light.Theta = light.Phi - 1e-3;
m_DynamicState.m_LightChanged[lightNum] = STATE_CHANGED;
memcpy(&m_DynamicState.m_Lights[lightNum], &light, sizeof(light));
}
void CShaderAPIDx8::DisableAllLocalLights()
{
LOCK_SHADERAPI();
bool bFlushed = false;
for (int lightNum = 0; lightNum < MAX_NUM_LIGHTS; lightNum++)
{
if (m_DynamicState.m_LightEnable[lightNum])
{
if (!bFlushed)
{
FlushBufferedPrimitives();
bFlushed = true;
}
m_DynamicState.m_LightDescs[lightNum].m_Type = MATERIAL_LIGHT_DISABLE;
m_DynamicState.m_LightEnableChanged[lightNum] = STATE_CHANGED;
m_DynamicState.m_LightEnable[lightNum] = false;
}
}
}
int CShaderAPIDx8::GetMaxLights(void) const
{
return g_pHardwareConfig->Caps().m_MaxNumLights;
}
const LightDesc_t& CShaderAPIDx8::GetLight(int lightNum) const
{
Assert(lightNum < g_pHardwareConfig->Caps().m_MaxNumLights&& lightNum >= 0);
return m_DynamicState.m_LightDescs[lightNum];
}
//-----------------------------------------------------------------------------
// Ambient cube
//-----------------------------------------------------------------------------
//#define NO_AMBIENT_CUBE 1
void CShaderAPIDx8::SetAmbientLightCube(Vector4D cube[6])
{
LOCK_SHADERAPI();
/*
int i;
for( i = 0; i < 6; i++ )
{
ColorClamp( cube[i].AsVector3D() );
// if( i == 0 )
// {
// Warning( "%d: %f %f %f\n", i, cube[i][0], cube[i][1], cube[i][2] );
// }
}
*/
if (memcmp(&m_DynamicState.m_AmbientLightCube[0][0], cube, 6 * sizeof(Vector4D)))
{
memcpy(&m_DynamicState.m_AmbientLightCube[0][0], cube, 6 * sizeof(Vector4D));
#ifdef NO_AMBIENT_CUBE
memset(&m_DynamicState.m_AmbientLightCube[0][0], 0, 6 * sizeof(Vector4D));
#endif
//#define DEBUG_AMBIENT_CUBE
#ifdef DEBUG_AMBIENT_CUBE
m_DynamicState.m_AmbientLightCube[0][0] = 1.0f;
m_DynamicState.m_AmbientLightCube[0][1] = 0.0f;
m_DynamicState.m_AmbientLightCube[0][2] = 0.0f;
m_DynamicState.m_AmbientLightCube[1][0] = 0.0f;
m_DynamicState.m_AmbientLightCube[1][1] = 1.0f;
m_DynamicState.m_AmbientLightCube[1][2] = 0.0f;
m_DynamicState.m_AmbientLightCube[2][0] = 0.0f;
m_DynamicState.m_AmbientLightCube[2][1] = 0.0f;
m_DynamicState.m_AmbientLightCube[2][2] = 1.0f;
m_DynamicState.m_AmbientLightCube[3][0] = 1.0f;
m_DynamicState.m_AmbientLightCube[3][1] = 0.0f;
m_DynamicState.m_AmbientLightCube[3][2] = 1.0f;
m_DynamicState.m_AmbientLightCube[4][0] = 1.0f;
m_DynamicState.m_AmbientLightCube[4][1] = 1.0f;
m_DynamicState.m_AmbientLightCube[4][2] = 0.0f;
m_DynamicState.m_AmbientLightCube[5][0] = 0.0f;
m_DynamicState.m_AmbientLightCube[5][1] = 1.0f;
m_DynamicState.m_AmbientLightCube[5][2] = 1.0f;
#endif
m_CachedAmbientLightCube = STATE_CHANGED;
}
}
void CShaderAPIDx8::SetVertexShaderStateAmbientLightCube()
{
if (m_CachedAmbientLightCube & STATE_CHANGED_VERTEX_SHADER)
{
SetVertexShaderConstant(VERTEX_SHADER_AMBIENT_LIGHT, m_DynamicState.m_AmbientLightCube[0].Base(), 6);
m_CachedAmbientLightCube &= ~STATE_CHANGED_VERTEX_SHADER;
}
}
void CShaderAPIDx8::SetPixelShaderStateAmbientLightCube(int pshReg, bool bForceToBlack)
{
float* pCubeBase;
Vector4D tempCube[6];
if (bForceToBlack)
{
for (int i = 0; i < 6; i++)
tempCube[i].Init();
pCubeBase = tempCube[0].Base();
}
else
{
pCubeBase = m_DynamicState.m_AmbientLightCube[0].Base();
}
SetPixelShaderConstant(pshReg, pCubeBase, 6);
}
float CShaderAPIDx8::GetAmbientLightCubeLuminance(void)
{
Vector4D vLuminance(0.3f, 0.59f, 0.11f, 0.0f);
float fLuminance = 0.0f;
for (int i = 0; i < 6; i++)
{
fLuminance += vLuminance.Dot(m_DynamicState.m_AmbientLightCube[i]);
}
return fLuminance / 6.0f;
}
static inline RECT* RectToRECT(Rect_t* pSrcRect, RECT& dstRect)
{
if (!pSrcRect)
return NULL;
dstRect.left = pSrcRect->x;
dstRect.top = pSrcRect->y;
dstRect.right = pSrcRect->x + pSrcRect->width;
dstRect.bottom = pSrcRect->y + pSrcRect->height;
return &dstRect;
}
void CShaderAPIDx8::CopyRenderTargetToTextureEx(ShaderAPITextureHandle_t textureHandle, int nRenderTargetID, Rect_t* pSrcRect, Rect_t* pDstRect)
{
LOCK_SHADERAPI();
VPROF_BUDGET("CShaderAPIDx8::CopyRenderTargetToTexture", "Refraction overhead");
if (!TextureIsAllocated(textureHandle))
return;
#if defined( PIX_INSTRUMENTATION )
{
const char* pRT = (nRenderTargetID < 0) ? "DS" : "RT";
if (textureHandle == SHADER_RENDERTARGET_NONE)
{
pRT = "None";
}
else if (textureHandle != SHADER_RENDERTARGET_BACKBUFFER)
{
Texture_t& tex = GetTexture(textureHandle);
pRT = tex.m_DebugName.String();
}
char buf[256];
sprintf(buf, "CopyRTToTexture:%s", pRT ? pRT : "?");
BeginPIXEvent(0xFFFFFFFF, buf);
EndPIXEvent();
}
#endif
// Don't flush here!! If you have to flush here, then there is a driver bug.
// FlushHardware( );
AssertValidTextureHandle(textureHandle);
Texture_t* pTexture = &GetTexture(textureHandle);
Assert(pTexture);
IDirect3DTexture* pD3DTexture = (IDirect3DTexture*)pTexture->GetTexture();
Assert(pD3DTexture);
#if !defined( _X360 )
IDirect3DSurface* pRenderTargetSurface;
HRESULT hr = Dx9Device()->GetRenderTarget(nRenderTargetID, &pRenderTargetSurface);
if (FAILED(hr))
{
Assert(0);
return;
}
IDirect3DSurface* pDstSurf;
hr = pD3DTexture->GetSurfaceLevel(0, &pDstSurf);
Assert(!FAILED(hr));
if (FAILED(hr))
{
pRenderTargetSurface->Release();
return;
}
bool tryblit = true;
if (tryblit)
{
RECORD_COMMAND(DX8_COPY_FRAMEBUFFER_TO_TEXTURE, 1);
RECORD_INT(textureHandle);
RECT srcRect, dstRect;
hr = Dx9Device()->StretchRect(pRenderTargetSurface, RectToRECT(pSrcRect, srcRect),
pDstSurf, RectToRECT(pDstRect, dstRect), D3DTEXF_LINEAR);
Assert(!FAILED(hr));
}
pDstSurf->Release();
pRenderTargetSurface->Release();
#else
DWORD flags = 0;
switch (nRenderTargetID)
{
case -1:
flags = D3DRESOLVE_DEPTHSTENCIL | D3DRESOLVE_FRAGMENT0;
break;
case 0:
flags = D3DRESOLVE_RENDERTARGET0;
break;
case 1:
case 2:
case 3:
// not supporting MRT
Assert(0);
return;
NO_DEFAULT
};
// not prepared to handle mip mapping yet
Assert(pD3DTexture->GetLevelCount() == 1);
D3DPOINT dstPoint = { 0 };
if (pDstRect)
{
dstPoint.x = pDstRect->x;
dstPoint.y = pDstRect->y;
}
int destWidth, destHeight;
if (pDstRect)
{
destWidth = pDstRect->width;
destHeight = pDstRect->height;
Assert((destWidth <= pTexture->GetWidth()) && (destHeight <= pTexture->GetHeight()));
}
else
{
destWidth = pTexture->GetWidth();
destHeight = pTexture->GetHeight();
}
RECT srcRect;
RECT* pResolveRect = NULL;
int srcWidth, srcHeight;
if (pSrcRect)
{
RectToRECT(pSrcRect, srcRect);
pResolveRect = &srcRect;
// resolve has no stretching ability, and we can only compensate when doing a resolve to a whole texture larger than the source
Assert(!pDstRect || (pSrcRect->width <= pDstRect->width && pSrcRect->height <= pDstRect->height));
srcWidth = pSrcRect->width;
srcHeight = pSrcRect->height;
}
else
{
srcRect.left = srcRect.top = 0;
srcRect.right = m_DynamicState.m_Viewport.Width;
srcRect.bottom = m_DynamicState.m_Viewport.Height;
if ((srcRect.right < 0) || (srcRect.bottom < 0))
{
if (m_UsingTextureRenderTarget)
{
srcRect.right = m_ViewportMaxWidth;
srcRect.bottom = m_ViewportMaxHeight;
}
else
{
int w, h;
GetBackBufferDimensions(w, h);
srcRect.right = w;
srcRect.bottom = h;
}
}
srcWidth = srcRect.right;
srcHeight = srcRect.bottom;
}
if ((srcWidth != destWidth) || (srcHeight != destHeight))
{
//Not a 1:1 resolve, we should only have gotten this far if we can downsize the target texture to compensate
Assert((destWidth > srcWidth) && (destHeight > srcHeight) && (dstPoint.x == 0) && (dstPoint.y == 0));
//What we're doing is telling D3D that this texture is smaller than it is so the resolve is 1:1.
//We leave the texture in this state until it resolves from something bigger.
//All outside code still thinks this texture is it's original size. And it still owns enough memory to go back to it's original size.
pD3DTexture->Format.Size.TwoD.Width = srcWidth - 1;
pD3DTexture->Format.Size.TwoD.Height = srcHeight - 1; //no idea why they store it as size-1, but they do
pResolveRect = NULL;
}
else
{
//restore D3D texture to full size in case it was previously downsized
pD3DTexture->Format.Size.TwoD.Width = pTexture->GetWidth() - 1;
pD3DTexture->Format.Size.TwoD.Height = pTexture->GetHeight() - 1; //no idea why they store it as size-1, but they do
}
// if we convert to srgb format, we need the original format for reverting. We only need the first DWORD of GPUTEXTURE_FETCH_CONSTANT.
DWORD linearFormatBackup = pD3DTexture->Format.dword[0];
if (!(flags & D3DRESOLVE_DEPTHSTENCIL) && (m_DynamicState.m_bSRGBWritesEnabled))
{
// we need a matched resolve regarding sRGB to get values transfered as-is
// when the surface is sRGB, use the corresponding sRGB texture
pD3DTexture->Format.SignX = pD3DTexture->Format.SignY = pD3DTexture->Format.SignZ = 3;
}
HRESULT hr = Dx9Device()->Resolve(flags, (D3DRECT*)pResolveRect, pD3DTexture, &dstPoint, 0, 0, NULL, 0, 0, NULL);
Assert(!FAILED(hr));
pD3DTexture->Format.dword[0] = linearFormatBackup;
#endif
}
void CShaderAPIDx8::CopyRenderTargetToScratchTexture(ShaderAPITextureHandle_t srcRt, ShaderAPITextureHandle_t dstTex, Rect_t* pSrcRect, Rect_t* pDstRect)
{
LOCK_SHADERAPI();
if (!TextureIsAllocated(srcRt) || !TextureIsAllocated(dstTex))
{
Assert(!"Fix that render target or dest texture aren't allocated.");
return;
}
HRESULT hr = D3D_OK;
IDirect3DSurface9* srcSurf = NULL;
AssertValidTextureHandle(srcRt);
Texture_t* pSrcRt = &GetTexture(srcRt);
Assert(pSrcRt);
IDirect3DTexture* pD3DSrcRt = (IDirect3DTexture*)pSrcRt->GetTexture();
Assert(pD3DSrcRt);
hr = pD3DSrcRt->GetSurfaceLevel(0, &srcSurf);
Assert(SUCCEEDED(hr) && srcSurf);
IDirect3DSurface9* dstSurf = NULL;
AssertValidTextureHandle(dstTex);
Texture_t* pDstTex = &GetTexture(dstTex);
Assert(pDstTex);
IDirect3DTexture* pD3DDstTex = (IDirect3DTexture*)pDstTex->GetTexture();
Assert(pD3DDstTex);
hr = pD3DDstTex->GetSurfaceLevel(0, &dstSurf);
Assert(SUCCEEDED(hr) && dstSurf);
// This does it.
hr = Dx9Device()->GetRenderTargetData(srcSurf, dstSurf);
Assert(SUCCEEDED(hr));
srcSurf->Release();
dstSurf->Release();
}
//-------------------------------------------------------------------------
// Allows locking and unlocking of very specific surface types. pOutBits and pOutPitch will not be touched if
// the lock fails.
//-------------------------------------------------------------------------
void CShaderAPIDx8::LockRect(void** pOutBits, int* pOutPitch, ShaderAPITextureHandle_t texHandle, int mipmap, int x, int y, int w, int h, bool bWrite, bool bRead)
{
LOCK_SHADERAPI();
Assert(pOutBits);
Assert(pOutPitch);
if (!TextureIsAllocated(texHandle))
{
Assert(!"Fix that texture isn't allocated.");
return;
}
HRESULT hr = D3D_OK;
IDirect3DSurface9* surf = NULL;
AssertValidTextureHandle(texHandle);
Texture_t* pTex = &GetTexture(texHandle);
Assert(pTex);
IDirect3DTexture* pD3DTex = (IDirect3DTexture*)pTex->GetTexture();
Assert(pD3DTex);
hr = pD3DTex->GetSurfaceLevel(mipmap, &surf);
Assert(SUCCEEDED(hr) && surf);
D3DLOCKED_RECT lockRect = { 0 };
RECT srcRect = { x, y, w, h };
DWORD flags = 0;
if (!bRead && !bWrite)
{
Assert(!"Asking to neither read nor write? Probably a caller bug.");
goto cleanup;
}
if (bRead && !bWrite)
flags = D3DLOCK_READONLY;
hr = surf->LockRect(&lockRect, &srcRect, flags);
if (FAILED(hr))
{
Assert(!"Lock failed, look into why.");
goto cleanup;
}
(*pOutBits) = lockRect.pBits;
(*pOutPitch) = lockRect.Pitch;
cleanup:
surf->Release();
}
void CShaderAPIDx8::UnlockRect(ShaderAPITextureHandle_t texHandle, int mipmap)
{
LOCK_SHADERAPI();
if (!TextureIsAllocated(texHandle))
{
Assert(!"Fix that texture isn't allocated.");
return;
}
HRESULT hr = D3D_OK;
IDirect3DSurface9* surf = NULL;
AssertValidTextureHandle(texHandle);
Texture_t* pTex = &GetTexture(texHandle);
Assert(pTex);
IDirect3DTexture* pD3DTex = (IDirect3DTexture*)pTex->GetTexture();
Assert(pD3DTex);
hr = pD3DTex->GetSurfaceLevel(mipmap, &surf);
Assert(SUCCEEDED(hr) && surf);
hr = surf->UnlockRect();
Assert(SUCCEEDED(hr));
surf->Release();
}
static float GetAspectRatio(const Texture_t* pTex)
{
Assert(pTex);
if (pTex->m_Height != 0)
return float(pTex->m_Width) / float(pTex->m_Height);
Assert(!"Height of texture is 0, that seems like a bug.");
return 0.0f;
}
struct TextureExtents_t
{
int width;
int height;
int depth;
int mipmaps;
int fine;
int coarse;
TextureExtents_t() : width(0), height(0), depth(0), mipmaps(0), fine(0), coarse(0) { }
};
// Returns positive integer on success, 0 or <0 on failure.
static int FindCommonMipmapRange(int* pOutSrcFine, int* pOutDstFine, Texture_t* pSrcTex, Texture_t* pDstTex)
{
Assert(pOutSrcFine && pOutDstFine);
Assert(pSrcTex && pDstTex);
if (GetAspectRatio(pSrcTex) != GetAspectRatio(pDstTex))
return 0;
TextureExtents_t src,
dst;
// LOD Clamp indicates that there's no actual data in the finer mipmap levels yet, so respect it when determining
// the source and destination levels that could have data.
const int srcLodClamp = pSrcTex->GetLodClamp();
src.width = Max(1, pSrcTex->GetWidth() >> srcLodClamp);
src.height = Max(1, pSrcTex->GetHeight() >> srcLodClamp);
src.depth = Max(1, pSrcTex->GetDepth() >> srcLodClamp);
src.mipmaps = pSrcTex->m_NumLevels - srcLodClamp;
Assert(src.mipmaps >= 1);
const int dstLodClamp = pDstTex->GetLodClamp();
dst.width = Max(1, pDstTex->GetWidth() >> dstLodClamp);
dst.height = Max(1, pDstTex->GetHeight() >> dstLodClamp);
dst.depth = Max(1, pDstTex->GetDepth() >> dstLodClamp);
dst.mipmaps = pDstTex->m_NumLevels - dstLodClamp;
Assert(dst.mipmaps >= 1);
TextureExtents_t* pLarger = NULL,
* pSmaller = NULL;
if (src.width >= dst.width && src.height >= dst.height && src.depth >= dst.depth)
{
pLarger = &src;
pSmaller = &dst;
}
else
{
pLarger = &dst;
pSmaller = &src;
}
// Since we are same aspect ratio, only need to test one dimension
while ((pLarger->width >> pLarger->fine) > pSmaller->width)
{
++pLarger->fine;
--pLarger->mipmaps;
}
(*pOutSrcFine) = src.fine;
(*pOutDstFine) = dst.fine;
return Min(src.mipmaps, dst.mipmaps);
}
void CShaderAPIDx8::CopyTextureToTexture(ShaderAPITextureHandle_t srcTex, ShaderAPITextureHandle_t dstTex)
{
LOCK_SHADERAPI();
AssertValidTextureHandle(srcTex);
AssertValidTextureHandle(dstTex);
Assert(TextureIsAllocated(srcTex) && TextureIsAllocated(dstTex));
Texture_t* pSrcTex = &GetTexture(srcTex);
Texture_t* pDstTex = &GetTexture(dstTex);
Assert(pSrcTex && pDstTex);
// Must have same image format
Assert(pSrcTex->GetImageFormat() == pDstTex->GetImageFormat());
int srcFine = 0,
dstFine = 0;
int mipmapCount = FindCommonMipmapRange(&srcFine, &dstFine, pSrcTex, pDstTex);
if (mipmapCount <= 0)
{
// This is legit for things that are streamed in that are very small (near the 32x32 cutoff we do at the
// tip of the mipmap pyramid). But leaving it here because it's useful if you're tracking a specific bug.
// Warning( "Attempted to copy textures that had non-overlapping mipmap pyramids. This has failed and no copy has taken place.\n" );
return;
}
IDirect3DTexture* pSrcD3DTex = (IDirect3DTexture*)pSrcTex->GetTexture();
IDirect3DTexture* pDstD3DTex = (IDirect3DTexture*)pDstTex->GetTexture();
HRESULT hr = S_OK;
for (int i = 0; i < mipmapCount; ++i)
{
int srcMipmap = srcFine + i;
int dstMipmap = dstFine + i;
IDirect3DSurface9* pSrcSurf = NULL;
IDirect3DSurface9* pDstSurf = NULL;
hr = pSrcD3DTex->GetSurfaceLevel(srcMipmap, &pSrcSurf);
Assert(SUCCEEDED(hr) && pSrcSurf);
hr = pDstD3DTex->GetSurfaceLevel(dstMipmap, &pDstSurf);
Assert(SUCCEEDED(hr) && pDstSurf);
hr = g_pD3DDevice->StretchRect(pSrcSurf, NULL, pDstSurf, NULL, D3DTEXF_NONE);
Assert(SUCCEEDED(hr));
pSrcSurf->Release();
pDstSurf->Release();
}
}
void CShaderAPIDx8::CopyRenderTargetToTexture(ShaderAPITextureHandle_t textureHandle)
{
LOCK_SHADERAPI();
CopyRenderTargetToTextureEx(textureHandle, 0);
}
void CShaderAPIDx8::CopyTextureToRenderTargetEx(int nRenderTargetID, ShaderAPITextureHandle_t textureHandle, Rect_t* pSrcRect, Rect_t* pDstRect)
{
LOCK_SHADERAPI();
VPROF("CShaderAPIDx8::CopyRenderTargetToTexture");
if (!TextureIsAllocated(textureHandle))
return;
// Don't flush here!! If you have to flush here, then there is a driver bug.
// FlushHardware( );
AssertValidTextureHandle(textureHandle);
Texture_t* pTexture = &GetTexture(textureHandle);
Assert(pTexture);
IDirect3DTexture* pD3DTexture = (IDirect3DTexture*)pTexture->GetTexture();
Assert(pD3DTexture);
#if !defined( _X360 )
IDirect3DSurface* pRenderTargetSurface;
HRESULT hr = Dx9Device()->GetRenderTarget(nRenderTargetID, &pRenderTargetSurface);
if (FAILED(hr))
{
Assert(0);
return;
}
IDirect3DSurface* pDstSurf;
hr = pD3DTexture->GetSurfaceLevel(0, &pDstSurf);
Assert(!FAILED(hr));
if (FAILED(hr))
{
pRenderTargetSurface->Release();
return;
}
bool tryblit = true;
if (tryblit)
{
RECORD_COMMAND(DX8_COPY_FRAMEBUFFER_TO_TEXTURE, 1);
RECORD_INT(textureHandle);
RECT srcRect, dstRect;
hr = Dx9Device()->StretchRect(pDstSurf, RectToRECT(pSrcRect, srcRect),
pRenderTargetSurface, RectToRECT(pDstRect, dstRect), D3DTEXF_LINEAR);
Assert(!FAILED(hr));
}
pDstSurf->Release();
pRenderTargetSurface->Release();
#else
Assert(0);
#endif
}
//-----------------------------------------------------------------------------
// modifies the vertex data when necessary
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ModifyVertexData()
{
// this should be a dead code path
Assert(0);
#if 0
// We have to modulate the vertex color by the constant color sometimes
if (IsModulatingVertexColor())
{
m_ModifyBuilder.Reset();
float factor[4];
unsigned char* pColor = (unsigned char*)&m_DynamicState.m_ConstantColor;
factor[0] = pColor[0] / 255.0f;
factor[1] = pColor[1] / 255.0f;
factor[2] = pColor[2] / 255.0f;
factor[3] = pColor[3] / 255.0f;
for (int i = 0; i < m_ModifyBuilder.VertexCount(); ++i)
{
unsigned int color = m_ModifyBuilder.Color();
unsigned char* pVertexColor = (unsigned char*)&color;
pVertexColor[0] = (unsigned char)((float)pVertexColor[0] * factor[0]);
pVertexColor[1] = (unsigned char)((float)pVertexColor[1] * factor[1]);
pVertexColor[2] = (unsigned char)((float)pVertexColor[2] * factor[2]);
pVertexColor[3] = (unsigned char)((float)pVertexColor[3] * factor[3]);
m_ModifyBuilder.Color4ubv(pVertexColor);
m_ModifyBuilder.AdvanceVertex();
}
}
#endif
}
static const char* TextureArgToString(int arg)
{
static char buf[128];
switch (arg & D3DTA_SELECTMASK)
{
case D3DTA_DIFFUSE:
strcpy(buf, "D3DTA_DIFFUSE");
break;
case D3DTA_CURRENT:
strcpy(buf, "D3DTA_CURRENT");
break;
case D3DTA_TEXTURE:
strcpy(buf, "D3DTA_TEXTURE");
break;
case D3DTA_TFACTOR:
strcpy(buf, "D3DTA_TFACTOR");
break;
case D3DTA_SPECULAR:
strcpy(buf, "D3DTA_SPECULAR");
break;
case D3DTA_TEMP:
strcpy(buf, "D3DTA_TEMP");
break;
default:
strcpy(buf, "<ERROR>");
break;
}
if (arg & D3DTA_COMPLEMENT)
{
strcat(buf, "|D3DTA_COMPLEMENT");
}
if (arg & D3DTA_ALPHAREPLICATE)
{
strcat(buf, "|D3DTA_ALPHAREPLICATE");
}
return buf;
}
static const char* TextureOpToString(D3DTEXTUREOP op)
{
switch (op)
{
case D3DTOP_DISABLE:
return "D3DTOP_DISABLE";
case D3DTOP_SELECTARG1:
return "D3DTOP_SELECTARG1";
case D3DTOP_SELECTARG2:
return "D3DTOP_SELECTARG2";
case D3DTOP_MODULATE:
return "D3DTOP_MODULATE";
case D3DTOP_MODULATE2X:
return "D3DTOP_MODULATE2X";
case D3DTOP_MODULATE4X:
return "D3DTOP_MODULATE4X";
case D3DTOP_ADD:
return "D3DTOP_ADD";
case D3DTOP_ADDSIGNED:
return "D3DTOP_ADDSIGNED";
case D3DTOP_ADDSIGNED2X:
return "D3DTOP_ADDSIGNED2X";
case D3DTOP_SUBTRACT:
return "D3DTOP_SUBTRACT";
case D3DTOP_ADDSMOOTH:
return "D3DTOP_ADDSMOOTH";
case D3DTOP_BLENDDIFFUSEALPHA:
return "D3DTOP_BLENDDIFFUSEALPHA";
case D3DTOP_BLENDTEXTUREALPHA:
return "D3DTOP_BLENDTEXTUREALPHA";
case D3DTOP_BLENDFACTORALPHA:
return "D3DTOP_BLENDFACTORALPHA";
case D3DTOP_BLENDTEXTUREALPHAPM:
return "D3DTOP_BLENDTEXTUREALPHAPM";
case D3DTOP_BLENDCURRENTALPHA:
return "D3DTOP_BLENDCURRENTALPHA";
case D3DTOP_PREMODULATE:
return "D3DTOP_PREMODULATE";
case D3DTOP_MODULATEALPHA_ADDCOLOR:
return "D3DTOP_MODULATEALPHA_ADDCOLOR";
case D3DTOP_MODULATECOLOR_ADDALPHA:
return "D3DTOP_MODULATECOLOR_ADDALPHA";
case D3DTOP_MODULATEINVALPHA_ADDCOLOR:
return "D3DTOP_MODULATEINVALPHA_ADDCOLOR";
case D3DTOP_MODULATEINVCOLOR_ADDALPHA:
return "D3DTOP_MODULATEINVCOLOR_ADDALPHA";
case D3DTOP_BUMPENVMAP:
return "D3DTOP_BUMPENVMAP";
case D3DTOP_BUMPENVMAPLUMINANCE:
return "D3DTOP_BUMPENVMAPLUMINANCE";
case D3DTOP_DOTPRODUCT3:
return "D3DTOP_DOTPRODUCT3";
case D3DTOP_MULTIPLYADD:
return "D3DTOP_MULTIPLYADD";
case D3DTOP_LERP:
return "D3DTOP_LERP";
default:
return "<ERROR>";
}
}
static const char* BlendModeToString(int blendMode)
{
switch (blendMode)
{
case D3DBLEND_ZERO:
return "D3DBLEND_ZERO";
case D3DBLEND_ONE:
return "D3DBLEND_ONE";
case D3DBLEND_SRCCOLOR:
return "D3DBLEND_SRCCOLOR";
case D3DBLEND_INVSRCCOLOR:
return "D3DBLEND_INVSRCCOLOR";
case D3DBLEND_SRCALPHA:
return "D3DBLEND_SRCALPHA";
case D3DBLEND_INVSRCALPHA:
return "D3DBLEND_INVSRCALPHA";
case D3DBLEND_DESTALPHA:
return "D3DBLEND_DESTALPHA";
case D3DBLEND_INVDESTALPHA:
return "D3DBLEND_INVDESTALPHA";
case D3DBLEND_DESTCOLOR:
return "D3DBLEND_DESTCOLOR";
case D3DBLEND_INVDESTCOLOR:
return "D3DBLEND_INVDESTCOLOR";
case D3DBLEND_SRCALPHASAT:
return "D3DBLEND_SRCALPHASAT";
#if !defined( _X360 )
case D3DBLEND_BOTHSRCALPHA:
return "D3DBLEND_BOTHSRCALPHA";
case D3DBLEND_BOTHINVSRCALPHA:
return "D3DBLEND_BOTHINVSRCALPHA";
#endif
default:
return "<ERROR>";
}
}
//-----------------------------------------------------------------------------
// Spew Board State
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SpewBoardState()
{
// FIXME: This has regressed
return;
#ifdef DEBUG_BOARD_STATE
/*
{
static ID3DXFont* pFont = 0;
if (!pFont)
{
HFONT hFont = CreateFont( 0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_MODERN, 0 );
Assert( hFont != 0 );
HRESULT hr = D3DXCreateFont( Dx9Device(), hFont, &pFont );
}
static char buf[1024];
static RECT r = { 0, 0, 640, 480 };
if (m_DynamicState.m_VertexBlend == 0)
return;
#if 1
D3DXMATRIX* m = &GetTransform(MATERIAL_MODEL);
D3DXMATRIX* m2 = &GetTransform(MATERIAL_MODEL + 1);
sprintf(buf,"FVF %x\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n",
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n",
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n",
ShaderManager()->GetCurrentVertexShader(),
m->m[0][0], m->m[0][1], m->m[0][2], m->m[0][3],
m->m[1][0], m->m[1][1], m->m[1][2], m->m[1][3],
m->m[2][0], m->m[2][1], m->m[2][2], m->m[2][3],
m->m[3][0], m->m[3][1], m->m[3][2], m->m[3][3],
m2->m[0][0], m2->m[0][1], m2->m[0][2], m2->m[0][3],
m2->m[1][0], m2->m[1][1], m2->m[1][2], m2->m[1][3],
m2->m[2][0], m2->m[2][1], m2->m[2][2], m2->m[2][3],
m2->m[3][0], m2->m[3][1], m2->m[3][2], m2->m[3][3]
);
#else
Vector4D *pVec2 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODELVIEWPROJ];
Vector4D *pVec3 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_VIEWPROJ];
Vector4D *pVec4 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODEL];
sprintf(buf,"\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n",
pVec1[0][0], pVec1[0][1], pVec1[0][2], pVec1[0][3],
pVec1[1][0], pVec1[1][1], pVec1[1][2], pVec1[1][3],
pVec1[2][0], pVec1[2][1], pVec1[2][2], pVec1[2][3],
pVec1[3][0], pVec1[3][1], pVec1[3][2], pVec1[3][3],
pVec2[0][0], pVec2[0][1], pVec2[0][2], pVec2[0][3],
pVec2[1][0], pVec2[1][1], pVec2[1][2], pVec2[1][3],
pVec2[2][0], pVec2[2][1], pVec2[2][2], pVec2[2][3],
pVec2[3][0], pVec2[3][1], pVec2[3][2], pVec2[3][3],
pVec3[0][0], pVec3[0][1], pVec3[0][2], pVec3[0][3],
pVec3[1][0], pVec3[1][1], pVec3[1][2], pVec3[1][3],
pVec3[2][0], pVec3[2][1], pVec3[2][2], pVec3[2][3],
pVec3[3][0], pVec3[3][1], pVec3[3][2], pVec3[3][3],
pVec4[0][0], pVec4[0][1], pVec4[0][2], pVec4[0][3],
pVec4[1][0], pVec4[1][1], pVec4[1][2], pVec4[1][3],
pVec4[2][0], pVec4[2][1], pVec4[2][2], pVec4[2][3],
0, 0, 0, 1
);
#endif
pFont->Begin();
pFont->DrawText( buf, -1, &r, DT_LEFT | DT_TOP,
D3DCOLOR_RGBA( 255, 255, 255, 255 ) );
pFont->End();
return;
}
#if 0
Vector4D *pVec2 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODELVIEWPROJ];
Vector4D *pVec3 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_VIEWPROJ];
Vector4D *pVec4 = &m_DynamicState.m_pVectorVertexShaderConstant[VERTEX_SHADER_MODEL];
static char buf2[1024];
sprintf(buf2,"\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n"
"[%6.3f %6.3f %6.3f %6.3f]\n[%6.3f %6.3f %6.3f %6.3f]\n",
pVec1[0][0], pVec1[0][1], pVec1[0][2], pVec1[0][3],
pVec1[1][0], pVec1[1][1], pVec1[1][2], pVec1[1][3],
pVec1[2][0], pVec1[2][1], pVec1[2][2], pVec1[2][3],
pVec1[3][0], pVec1[3][1], pVec1[3][2], pVec1[3][3],
pVec2[0][0], pVec2[0][1], pVec2[0][2], pVec2[0][3],
pVec2[1][0], pVec2[1][1], pVec2[1][2], pVec2[1][3],
pVec2[2][0], pVec2[2][1], pVec2[2][2], pVec2[2][3],
pVec2[3][0], pVec2[3][1], pVec2[3][2], pVec2[3][3],
pVec3[0][0], pVec3[0][1], pVec3[0][2], pVec3[0][3],
pVec3[1][0], pVec3[1][1], pVec3[1][2], pVec3[1][3],
pVec3[2][0], pVec3[2][1], pVec3[2][2], pVec3[2][3],
pVec3[3][0], pVec3[3][1], pVec3[3][2], pVec3[3][3],
pVec4[0][0], pVec4[0][1], pVec4[0][2], pVec4[0][3],
pVec4[1][0], pVec4[1][1], pVec4[1][2], pVec4[1][3],
pVec4[2][0], pVec4[2][1], pVec4[2][2], pVec4[2][3],
0, 0, 0, 1.0f
);
Plat_DebugString(buf2);
return;
#endif
*/
char buf[256];
sprintf(buf, "\nSnapshot id %d : \n", m_TransitionTable.CurrentSnapshot());
Plat_DebugString(buf);
ShadowState_t& boardState = m_TransitionTable.BoardState();
ShadowShaderState_t& boardShaderState = m_TransitionTable.BoardShaderState();
sprintf(buf, "Depth States: ZFunc %d, ZWrite %d, ZEnable %d, ZBias %d\n",
boardState.m_ZFunc, boardState.m_ZWriteEnable,
boardState.m_ZEnable, boardState.m_ZBias);
Plat_DebugString(buf);
sprintf(buf, "Cull Enable %d Cull Mode %d Color Write %d Fill %d Const Color Mod %d sRGBWriteEnable %d\n",
boardState.m_CullEnable, m_DynamicState.m_CullMode, boardState.m_ColorWriteEnable,
boardState.m_FillMode, boardShaderState.m_ModulateConstantColor, boardState.m_SRGBWriteEnable);
Plat_DebugString(buf);
sprintf(buf, "Blend States: Blend Enable %d Test Enable %d Func %d SrcBlend %d (%s) DstBlend %d (%s)\n",
boardState.m_AlphaBlendEnable, boardState.m_AlphaTestEnable,
boardState.m_AlphaFunc, boardState.m_SrcBlend, BlendModeToString(boardState.m_SrcBlend),
boardState.m_DestBlend, BlendModeToString(boardState.m_DestBlend));
Plat_DebugString(buf);
int len = sprintf(buf, "Alpha Ref %d, Lighting: %d, Ambient Color %x, LightsEnabled ",
boardState.m_AlphaRef, boardState.m_Lighting, m_DynamicState.m_Ambient);
int i;
for (i = 0; i < g_pHardwareConfig->Caps().m_MaxNumLights; ++i)
{
len += sprintf(buf + len, "%d ", m_DynamicState.m_LightEnable[i]);
}
sprintf(buf + len, "\n");
Plat_DebugString(buf);
sprintf(buf, "Fixed Function: %d, VertexBlend %d\n",
boardState.m_UsingFixedFunction, m_DynamicState.m_VertexBlend);
Plat_DebugString(buf);
sprintf(buf, "Pass Vertex Usage: %llx Pixel Shader %p Vertex Shader %p\n",
boardShaderState.m_VertexUsage, ShaderManager()->GetCurrentPixelShader(),
ShaderManager()->GetCurrentVertexShader());
Plat_DebugString(buf);
// REGRESSED!!!!
/*
D3DXMATRIX* m = &GetTransform(MATERIAL_MODEL);
sprintf(buf,"WorldMat [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n",
m->m[0][0], m->m[0][1], m->m[0][2], m->m[0][3],
m->m[1][0], m->m[1][1], m->m[1][2], m->m[1][3],
m->m[2][0], m->m[2][1], m->m[2][2], m->m[2][3],
m->m[3][0], m->m[3][1], m->m[3][2], m->m[3][3] );
Plat_DebugString(buf);
m = &GetTransform(MATERIAL_MODEL + 1);
sprintf(buf,"WorldMat2 [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n",
m->m[0][0], m->m[0][1], m->m[0][2], m->m[0][3],
m->m[1][0], m->m[1][1], m->m[1][2], m->m[1][3],
m->m[2][0], m->m[2][1], m->m[2][2], m->m[2][3],
m->m[3][0], m->m[3][1], m->m[3][2], m->m[3][3] );
Plat_DebugString(buf);
m = &GetTransform(MATERIAL_VIEW);
sprintf(buf,"ViewMat [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n",
m->m[0][0], m->m[0][1], m->m[0][2],
m->m[1][0], m->m[1][1], m->m[1][2],
m->m[2][0], m->m[2][1], m->m[2][2],
m->m[3][0], m->m[3][1], m->m[3][2] );
Plat_DebugString(buf);
m = &GetTransform(MATERIAL_PROJECTION);
sprintf(buf,"ProjMat [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n",
m->m[0][0], m->m[0][1], m->m[0][2],
m->m[1][0], m->m[1][1], m->m[1][2],
m->m[2][0], m->m[2][1], m->m[2][2],
m->m[3][0], m->m[3][1], m->m[3][2] );
Plat_DebugString(buf);
for (i = 0; i < GetTextureStageCount(); ++i)
{
m = &GetTransform(MATERIAL_TEXTURE0 + i);
sprintf(buf,"TexMat%d [%4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f]\n",
i, m->m[0][0], m->m[0][1], m->m[0][2],
m->m[1][0], m->m[1][1], m->m[1][2],
m->m[2][0], m->m[2][1], m->m[2][2],
m->m[3][0], m->m[3][1], m->m[3][2] );
Plat_DebugString(buf);
}
*/
sprintf(buf, "Viewport (%d %d) [%d %d] %4.3f %4.3f\n",
m_DynamicState.m_Viewport.X, m_DynamicState.m_Viewport.Y,
m_DynamicState.m_Viewport.Width, m_DynamicState.m_Viewport.Height,
m_DynamicState.m_Viewport.MinZ, m_DynamicState.m_Viewport.MaxZ);
Plat_DebugString(buf);
for (i = 0; i < MAX_TEXTURE_STAGES; ++i)
{
sprintf(buf, "Stage %d :\n", i);
Plat_DebugString(buf);
sprintf(buf, " Color Op: %d (%s) Color Arg1: %d (%s)",
boardState.m_TextureStage[i].m_ColorOp,
TextureOpToString(boardState.m_TextureStage[i].m_ColorOp),
boardState.m_TextureStage[i].m_ColorArg1,
TextureArgToString(boardState.m_TextureStage[i].m_ColorArg1));
Plat_DebugString(buf);
sprintf(buf, " Color Arg2: %d (%s)\n",
boardState.m_TextureStage[i].m_ColorArg2,
TextureArgToString(boardState.m_TextureStage[i].m_ColorArg2));
Plat_DebugString(buf);
sprintf(buf, " Alpha Op: %d (%s) Alpha Arg1: %d (%s)",
boardState.m_TextureStage[i].m_AlphaOp,
TextureOpToString(boardState.m_TextureStage[i].m_AlphaOp),
boardState.m_TextureStage[i].m_AlphaArg1,
TextureArgToString(boardState.m_TextureStage[i].m_AlphaArg1));
Plat_DebugString(buf);
sprintf(buf, " Alpha Arg2: %d (%s)\n",
boardState.m_TextureStage[i].m_AlphaArg2,
TextureArgToString(boardState.m_TextureStage[i].m_AlphaArg2));
Plat_DebugString(buf);
}
for (int i = 0; i < MAX_SAMPLERS; ++i)
{
sprintf(buf, " Texture Enabled: %d Bound Texture: %d UWrap: %d VWrap: %d\n",
SamplerState(i).m_TextureEnable, GetBoundTextureBindId((Sampler_t)i),
SamplerState(i).m_UTexWrap, SamplerState(i).m_VTexWrap);
Plat_DebugString(buf);
sprintf(buf, " Mag Filter: %d Min Filter: %d Mip Filter: %d\n",
SamplerState(i).m_MagFilter, SamplerState(i).m_MinFilter,
SamplerState(i).m_MipFilter);
sprintf(buf, " MaxMipLevel: %d\n", SamplerState(i).m_FinestMipmapLevel);
Plat_DebugString(buf);
}
#else
Plat_DebugString("::SpewBoardState() Not Implemented Yet");
#endif
}
//-----------------------------------------------------------------------------
// Begin a render pass
//-----------------------------------------------------------------------------
void CShaderAPIDx8::BeginPass(StateSnapshot_t snapshot)
{
LOCK_SHADERAPI();
VPROF("CShaderAPIDx8::BeginPass");
if (IsDeactivated())
return;
m_nCurrentSnapshot = snapshot;
// Assert( m_pRenderMesh );
// FIXME: This only does anything with temp meshes, so don't bother yet for the new code.
if (m_pRenderMesh)
{
m_pRenderMesh->BeginPass();
}
}
//-----------------------------------------------------------------------------
// Render da polygon!
//-----------------------------------------------------------------------------
void CShaderAPIDx8::RenderPass(int nPass, int nPassCount)
{
if (IsDeactivated())
return;
Assert(m_nCurrentSnapshot != -1);
// Assert( m_pRenderMesh ); MESHFIXME
m_TransitionTable.UseSnapshot(m_nCurrentSnapshot);
CommitPerPassStateChanges(m_nCurrentSnapshot);
// Make sure that we bound a texture for every stage that is enabled
// NOTE: not enabled/finished yet... see comment in CShaderAPIDx8::ApplyTextureEnable
// int nSampler;
// for ( nSampler = 0; nSampler < g_pHardwareConfig->GetSamplerCount(); nSampler++ )
// {
// if ( SamplerState( nSampler ).m_TextureEnable )
// {
// }
// }
#ifdef DEBUG_BOARD_STATE
// Spew out render state...
if (m_pMaterial->PerformDebugTrace())
{
SpewBoardState();
}
#endif
#ifdef TEST_CACHE_LOCKS
g_pDataCache->Flush();
#endif
// Assert( m_pRenderMesh ); MESHFIXME
if (m_pRenderMesh)
{
m_pRenderMesh->RenderPass();
}
else
{
MeshMgr()->RenderPassWithVertexAndIndexBuffers();
}
m_nCurrentSnapshot = -1;
}
//-----------------------------------------------------------------------------
// Matrix mode
//-----------------------------------------------------------------------------
void CShaderAPIDx8::MatrixMode(MaterialMatrixMode_t matrixMode)
{
// NOTE!!!!!!
// The only time that m_MatrixMode is used is for texture matrices. Do not use
// it for anything else unless you change this code!
if (matrixMode >= MATERIAL_TEXTURE0 && matrixMode <= MATERIAL_TEXTURE7)
{
m_MatrixMode = (D3DTRANSFORMSTATETYPE)(matrixMode - MATERIAL_TEXTURE0 + D3DTS_TEXTURE0);
}
else
{
m_MatrixMode = (D3DTRANSFORMSTATETYPE)-1;
}
m_CurrStack = GetMatrixStack(matrixMode);
}
//-----------------------------------------------------------------------------
// the current camera position in world space.
//-----------------------------------------------------------------------------
void CShaderAPIDx8::GetWorldSpaceCameraPosition(float* pPos) const
{
memcpy(pPos, m_WorldSpaceCameraPositon.Base(), sizeof(float[3]));
}
void CShaderAPIDx8::CacheWorldSpaceCameraPosition()
{
D3DXMATRIX& view = GetTransform(MATERIAL_VIEW);
m_WorldSpaceCameraPositon[0] =
-(view(3, 0) * view(0, 0) +
view(3, 1) * view(0, 1) +
view(3, 2) * view(0, 2));
m_WorldSpaceCameraPositon[1] =
-(view(3, 0) * view(1, 0) +
view(3, 1) * view(1, 1) +
view(3, 2) * view(1, 2));
m_WorldSpaceCameraPositon[2] =
-(view(3, 0) * view(2, 0) +
view(3, 1) * view(2, 1) +
view(3, 2) * view(2, 2));
m_WorldSpaceCameraPositon[3] = 1.0f;
// Protect against zero, as some pixel shaders will divide by this in CalcWaterFogAlpha() in common_ps_fxc.h
if (fabs(m_WorldSpaceCameraPositon[2]) <= 0.00001f)
{
m_WorldSpaceCameraPositon[2] = 0.01f;
}
}
//-----------------------------------------------------------------------------
// Computes a matrix which includes the poly offset given an initial projection matrix
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ComputePolyOffsetMatrix(const D3DXMATRIX& matProjection, D3DXMATRIX& matProjectionOffset)
{
// We never need to do this on hardware that can handle zbias
if (g_pHardwareConfig->Caps().m_ZBiasAndSlopeScaledDepthBiasSupported)
return;
float offsetVal =
-1.0f * (m_DesiredState.m_Viewport.MaxZ - m_DesiredState.m_Viewport.MinZ) /
16384.0f;
D3DXMATRIX offset;
D3DXMatrixTranslation(&offset, 0.0f, 0.0f, offsetVal);
D3DXMatrixMultiply(&matProjectionOffset, &matProjection, &offset);
}
//-----------------------------------------------------------------------------
// Caches off the poly-offset projection matrix
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CachePolyOffsetProjectionMatrix()
{
ComputePolyOffsetMatrix(GetTransform(MATERIAL_PROJECTION), m_CachedPolyOffsetProjectionMatrix);
}
//-----------------------------------------------------------------------------
// Performs a flush on the matrix state if necessary
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::MatrixIsChanging(TransformType_t type)
{
if (IsDeactivated())
{
return false;
}
// early out if the transform is already one of our standard types
if ((type != TRANSFORM_IS_GENERAL) && (type == m_DynamicState.m_TransformType[m_CurrStack]))
return false;
// Only flush state if we're changing something other than a texture transform
int textureMatrix = m_CurrStack - MATERIAL_TEXTURE0;
if ((textureMatrix < 0) || (textureMatrix >= NUM_TEXTURE_TRANSFORMS))
FlushBufferedPrimitivesInternal();
return true;
}
void CShaderAPIDx8::SetTextureTransformDimension(TextureStage_t textureMatrix, int dimension, bool projected)
{
D3DTEXTURETRANSFORMFLAGS textureTransformFlags = (D3DTEXTURETRANSFORMFLAGS)dimension;
if (projected)
{
Assert(sizeof(int) == sizeof(D3DTEXTURETRANSFORMFLAGS));
(*(int*)&textureTransformFlags) |= D3DTTFF_PROJECTED;
}
if (TextureStage(textureMatrix).m_TextureTransformFlags != textureTransformFlags)
{
SetTextureStageState(textureMatrix, D3DTSS_TEXTURETRANSFORMFLAGS, textureTransformFlags);
TextureStage(textureMatrix).m_TextureTransformFlags = textureTransformFlags;
}
}
void CShaderAPIDx8::DisableTextureTransform(TextureStage_t textureMatrix)
{
if (TextureStage(textureMatrix).m_TextureTransformFlags != D3DTTFF_DISABLE)
{
SetTextureStageState(textureMatrix, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
TextureStage(textureMatrix).m_TextureTransformFlags = D3DTTFF_DISABLE;
}
}
void CShaderAPIDx8::SetBumpEnvMatrix(TextureStage_t textureStage, float m00, float m01, float m10, float m11)
{
TextureStageState_t& textureStageState = TextureStage(textureStage);
if (textureStageState.m_BumpEnvMat00 != m00 ||
textureStageState.m_BumpEnvMat01 != m01 ||
textureStageState.m_BumpEnvMat10 != m10 ||
textureStageState.m_BumpEnvMat11 != m11)
{
SetTextureStageState(textureStage, D3DTSS_BUMPENVMAT00, *((LPDWORD)(&m00)));
SetTextureStageState(textureStage, D3DTSS_BUMPENVMAT01, *((LPDWORD)(&m01)));
SetTextureStageState(textureStage, D3DTSS_BUMPENVMAT10, *((LPDWORD)(&m10)));
SetTextureStageState(textureStage, D3DTSS_BUMPENVMAT11, *((LPDWORD)(&m11)));
textureStageState.m_BumpEnvMat00 = m00;
textureStageState.m_BumpEnvMat01 = m01;
textureStageState.m_BumpEnvMat10 = m10;
textureStageState.m_BumpEnvMat11 = m11;
}
}
//-----------------------------------------------------------------------------
// Sets the actual matrix state
//-----------------------------------------------------------------------------
void CShaderAPIDx8::UpdateMatrixTransform(TransformType_t type)
{
int textureMatrix = m_CurrStack - MATERIAL_TEXTURE0;
if ((textureMatrix >= 0) && (textureMatrix < NUM_TEXTURE_TRANSFORMS))
{
// NOTE: Flush shouldn't happen here because we
// expect that texture transforms will be set within the shader
// FIXME: We only want to use D3DTTFF_COUNT3 for cubemaps
// D3DTFF_COUNT2 is used for non-cubemaps. Of course, if there's
// no performance penalty for COUNT3, we should just use that.
D3DTEXTURETRANSFORMFLAGS transformFlags;
transformFlags = (type == TRANSFORM_IS_IDENTITY) ? D3DTTFF_DISABLE : D3DTTFF_COUNT3;
if (TextureStage(textureMatrix).m_TextureTransformFlags != transformFlags)
{
SetTextureStageState(textureMatrix, D3DTSS_TEXTURETRANSFORMFLAGS, transformFlags);
TextureStage(textureMatrix).m_TextureTransformFlags = transformFlags;
}
}
m_DynamicState.m_TransformType[m_CurrStack] = type;
m_DynamicState.m_TransformChanged[m_CurrStack] = STATE_CHANGED;
#ifdef _DEBUG
// Store off the board state
D3DXMATRIX* pSrc = &GetTransform(m_CurrStack);
D3DXMATRIX* pDst = &m_DynamicState.m_Transform[m_CurrStack];
// Assert( *pSrc != *pDst );
memcpy(pDst, pSrc, sizeof(D3DXMATRIX));
#endif
if (m_CurrStack == MATERIAL_VIEW)
{
CacheWorldSpaceCameraPosition();
}
if (!IsX360() && m_CurrStack == MATERIAL_PROJECTION)
{
CachePolyOffsetProjectionMatrix();
}
// Any time the view or projection matrix changes, the user clip planes need recomputing....
// Assuming we're not overriding the user clip transform
if ((m_CurrStack == MATERIAL_PROJECTION) ||
((m_CurrStack == MATERIAL_VIEW) && (!m_DynamicState.m_bUserClipTransformOverride)))
{
MarkAllUserClipPlanesDirty();
}
// Set the state if it's a texture transform
if ((m_CurrStack >= MATERIAL_TEXTURE0) && (m_CurrStack <= MATERIAL_TEXTURE7))
{
SetTransform(m_MatrixMode, &GetTransform(m_CurrStack));
}
}
//--------------------------------------------------------------------------------
// deformations
//--------------------------------------------------------------------------------
void CShaderAPIDx8::PushDeformation(DeformationBase_t const* pDef)
{
Assert(m_pDeformationStackPtr > m_DeformationStack);
--m_pDeformationStackPtr;
m_pDeformationStackPtr->m_nDeformationType = pDef->m_eType;
switch (pDef->m_eType)
{
case DEFORMATION_CLAMP_TO_BOX_IN_WORLDSPACE:
{
BoxDeformation_t const* pBox = reinterpret_cast<const BoxDeformation_t*>(pDef);
m_pDeformationStackPtr->m_nNumParameters = 16;
memcpy(m_pDeformationStackPtr->m_flDeformationParameters, &(pBox->m_SourceMins.x), 16 * sizeof(float));
break;
}
break;
default:
Assert(0);
}
}
void CShaderAPIDx8::PopDeformation()
{
Assert(m_pDeformationStackPtr != m_DeformationStack + DEFORMATION_STACK_DEPTH);
++m_pDeformationStackPtr;
}
int CShaderAPIDx8::GetNumActiveDeformations(void) const
{
return (m_DeformationStack + DEFORMATION_STACK_DEPTH) - m_pDeformationStackPtr;
}
// for shaders to set vertex shader constants. returns a packed state which can be used to set the dynamic combo
int CShaderAPIDx8::GetPackedDeformationInformation(int nMaskOfUnderstoodDeformations,
float* pConstantValuesOut,
int nBufferSize,
int nMaximumDeformations,
int* pDefCombosOut) const
{
int nCombosFound = 0;
memset(pDefCombosOut, 0, sizeof(pDefCombosOut[0]) * nMaximumDeformations);
size_t nRemainingBufferSize = nBufferSize;
for (const Deformation_t* i = m_DeformationStack + DEFORMATION_STACK_DEPTH - 1; i >= m_pDeformationStackPtr; i--)
{
int nFloatsOut = 4 * ((i->m_nNumParameters + 3) >> 2);
if (
((1 << i->m_nDeformationType) & nMaskOfUnderstoodDeformations) &&
(nRemainingBufferSize >= (nFloatsOut * sizeof(float))))
{
memcpy(pConstantValuesOut, i->m_flDeformationParameters, nFloatsOut * sizeof(float));
pConstantValuesOut += nFloatsOut;
nRemainingBufferSize -= nFloatsOut * sizeof(float);
(*pDefCombosOut++) = i->m_nDeformationType;
nCombosFound++;
}
}
return nCombosFound;
}
//-----------------------------------------------------------------------------
// Matrix stack operations
//-----------------------------------------------------------------------------
void CShaderAPIDx8::PushMatrix()
{
// NOTE: No matrix transform update needed here.
m_pMatrixStack[m_CurrStack]->Push();
}
void CShaderAPIDx8::PopMatrix()
{
if (MatrixIsChanging())
{
m_pMatrixStack[m_CurrStack]->Pop();
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::LoadIdentity()
{
if (MatrixIsChanging(TRANSFORM_IS_IDENTITY))
{
m_pMatrixStack[m_CurrStack]->LoadIdentity();
UpdateMatrixTransform(TRANSFORM_IS_IDENTITY);
}
}
void CShaderAPIDx8::LoadCameraToWorld()
{
if (MatrixIsChanging(TRANSFORM_IS_CAMERA_TO_WORLD))
{
// could just use the transpose instead, if we know there's no scale
float det;
D3DXMATRIX inv;
D3DXMatrixInverse(&inv, &det, &GetTransform(MATERIAL_VIEW));
// Kill translation
inv.m[3][0] = inv.m[3][1] = inv.m[3][2] = 0.0f;
m_pMatrixStack[m_CurrStack]->LoadMatrix(&inv);
UpdateMatrixTransform(TRANSFORM_IS_CAMERA_TO_WORLD);
}
}
void CShaderAPIDx8::LoadMatrix(float* m)
{
// Check for identity...
if ((fabs(m[0] - 1.0f) < 1e-3) && (fabs(m[5] - 1.0f) < 1e-3) && (fabs(m[10] - 1.0f) < 1e-3) && (fabs(m[15] - 1.0f) < 1e-3) &&
(fabs(m[1]) < 1e-3) && (fabs(m[2]) < 1e-3) && (fabs(m[3]) < 1e-3) &&
(fabs(m[4]) < 1e-3) && (fabs(m[6]) < 1e-3) && (fabs(m[7]) < 1e-3) &&
(fabs(m[8]) < 1e-3) && (fabs(m[9]) < 1e-3) && (fabs(m[11]) < 1e-3) &&
(fabs(m[12]) < 1e-3) && (fabs(m[13]) < 1e-3) && (fabs(m[14]) < 1e-3))
{
LoadIdentity();
return;
}
if (MatrixIsChanging())
{
m_pMatrixStack[m_CurrStack]->LoadMatrix((D3DXMATRIX*)m);
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::LoadBoneMatrix(int boneIndex, const float* m)
{
if (IsDeactivated())
return;
memcpy(m_boneMatrix[boneIndex].Base(), m, sizeof(float) * 12);
if (boneIndex > m_maxBoneLoaded)
{
m_maxBoneLoaded = boneIndex;
}
if (boneIndex == 0)
{
MatrixMode(MATERIAL_MODEL);
VMatrix transposeMatrix;
transposeMatrix.Init(*(matrix3x4_t*)m);
MatrixTranspose(transposeMatrix, transposeMatrix);
LoadMatrix((float*)transposeMatrix.m);
}
}
//-----------------------------------------------------------------------------
// Commits morph target factors
//-----------------------------------------------------------------------------
static void CommitFlexWeights(IDirect3DDevice9* pDevice, const DynamicState_t& desiredState,
DynamicState_t& currentState, bool bForce)
{
if (IsX360())
{
// not supporting for 360
return;
}
CommitVertexShaderConstantRange(pDevice, desiredState, currentState, bForce,
VERTEX_SHADER_FLEX_WEIGHTS, VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT);
}
void CShaderAPIDx8::SetFlexWeights(int nFirstWeight, int nCount, const MorphWeight_t* pWeights)
{
if (IsX360())
{
// not supported for 360
return;
}
LOCK_SHADERAPI();
if (g_pHardwareConfig->Caps().m_NumVertexShaderConstants < VERTEX_SHADER_FLEX_WEIGHTS + VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT)
return;
if (nFirstWeight + nCount > VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT)
{
Warning("Attempted to set too many flex weights! Max is %d\n", VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT);
nCount = VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT - nFirstWeight;
}
if (nCount <= 0)
return;
float* pDest = m_DesiredState.m_pVectorVertexShaderConstant[VERTEX_SHADER_FLEX_WEIGHTS + nFirstWeight].Base();
memcpy(pDest, pWeights, nCount * sizeof(MorphWeight_t));
ADD_COMMIT_FUNC(COMMIT_PER_DRAW, COMMIT_VERTEX_SHADER, CommitFlexWeights);
}
void CShaderAPIDx8::MultMatrix(float* m)
{
if (MatrixIsChanging())
{
m_pMatrixStack[m_CurrStack]->MultMatrix((D3DXMATRIX*)m);
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::MultMatrixLocal(float* m)
{
if (MatrixIsChanging())
{
m_pMatrixStack[m_CurrStack]->MultMatrixLocal((D3DXMATRIX*)m);
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::Rotate(float angle, float x, float y, float z)
{
if (MatrixIsChanging())
{
D3DXVECTOR3 axis(x, y, z);
m_pMatrixStack[m_CurrStack]->RotateAxisLocal(&axis, M_PI * angle / 180.0f);
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::Translate(float x, float y, float z)
{
if (MatrixIsChanging())
{
m_pMatrixStack[m_CurrStack]->TranslateLocal(x, y, z);
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::Scale(float x, float y, float z)
{
if (MatrixIsChanging())
{
m_pMatrixStack[m_CurrStack]->ScaleLocal(x, y, z);
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::ScaleXY(float x, float y)
{
if (MatrixIsChanging())
{
m_pMatrixStack[m_CurrStack]->ScaleLocal(x, y, 1.0f);
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::Ortho(double left, double top, double right, double bottom, double zNear, double zFar)
{
if (MatrixIsChanging())
{
D3DXMATRIX matrix;
// FIXME: This is being used incorrectly! Should read:
// D3DXMatrixOrthoOffCenterRH( &matrix, left, right, bottom, top, zNear, zFar );
// Which is certainly why we need these extra -1 scales in y. Bleah
// NOTE: The camera can be imagined as the following diagram:
// /z
// /
// /____ x Z is going into the screen
// |
// |
// |y
//
// (0,0,z) represents the upper-left corner of the screen.
// Our projection transform needs to transform from this space to a LH coordinate
// system that looks thusly:
//
// y| /z
// | /
// |/____ x Z is going into the screen
//
// Where x,y lies between -1 and 1, and z lies from 0 to 1
// This is because the viewport transformation from projection space to pixels
// introduces a -1 scale in the y coordinates
// D3DXMatrixOrthoOffCenterLH( &matrix, left, right, bottom, top, zNear, zFar );
D3DXMatrixOrthoOffCenterRH(&matrix, left, right, top, bottom, zNear, zFar);
m_pMatrixStack[m_CurrStack]->MultMatrixLocal(&matrix);
Assert(m_CurrStack == MATERIAL_PROJECTION);
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::PerspectiveX(double fovx, double aspect, double zNear, double zFar)
{
if (MatrixIsChanging())
{
float width = 2 * zNear * tan(fovx * M_PI / 360.0);
float height = width / aspect;
Assert(m_CurrStack == MATERIAL_PROJECTION);
D3DXMATRIX rh;
D3DXMatrixPerspectiveRH(&rh, width, height, zNear, zFar);
m_pMatrixStack[m_CurrStack]->MultMatrixLocal(&rh);
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::PerspectiveOffCenterX(double fovx, double aspect, double zNear, double zFar, double bottom, double top, double left, double right)
{
if (MatrixIsChanging())
{
float width = 2 * zNear * tan(fovx * M_PI / 360.0);
float height = width / aspect;
// bottom, top, left, right are 0..1 so convert to -1..1
float flFrontPlaneLeft = -(width / 2.0f) * (1.0f - left) + left * (width / 2.0f);
float flFrontPlaneRight = -(width / 2.0f) * (1.0f - right) + right * (width / 2.0f);
float flFrontPlaneBottom = -(height / 2.0f) * (1.0f - bottom) + bottom * (height / 2.0f);
float flFrontPlaneTop = -(height / 2.0f) * (1.0f - top) + top * (height / 2.0f);
Assert(m_CurrStack == MATERIAL_PROJECTION);
D3DXMATRIX rh;
D3DXMatrixPerspectiveOffCenterRH(&rh, flFrontPlaneLeft, flFrontPlaneRight, flFrontPlaneBottom, flFrontPlaneTop, zNear, zFar);
m_pMatrixStack[m_CurrStack]->MultMatrixLocal(&rh);
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::PickMatrix(int x, int y, int width, int height)
{
if (MatrixIsChanging())
{
Assert(m_CurrStack == MATERIAL_PROJECTION);
// This is going to create a matrix to append to the standard projection.
// Projection space goes from -1 to 1 in x and y. This matrix we append
// will transform the pick region to -1 to 1 in projection space
ShaderViewport_t viewport;
GetViewports(&viewport, 1);
int vx = viewport.m_nTopLeftX;
int vy = viewport.m_nTopLeftX;
int vwidth = viewport.m_nWidth;
int vheight = viewport.m_nHeight;
// Compute the location of the pick region in projection space...
float px = 2.0 * (float)(x - vx) / (float)vwidth - 1;
float py = 2.0 * (float)(y - vy) / (float)vheight - 1;
float pw = 2.0 * (float)width / (float)vwidth;
float ph = 2.0 * (float)height / (float)vheight;
// we need to translate (px, py) to the origin
// and scale so (pw,ph) -> (2, 2)
D3DXMATRIX matrix;
D3DXMatrixIdentity(&matrix);
matrix.m[0][0] = 2.0 / pw;
matrix.m[1][1] = 2.0 / ph;
matrix.m[3][0] = -2.0 * px / pw;
matrix.m[3][1] = -2.0 * py / ph;
m_pMatrixStack[m_CurrStack]->MultMatrixLocal(&matrix);
UpdateMatrixTransform();
}
}
void CShaderAPIDx8::GetMatrix(MaterialMatrixMode_t matrixMode, float* dst)
{
memcpy(dst, (void*)(FLOAT*)GetTransform(matrixMode), sizeof(D3DXMATRIX));
}
//-----------------------------------------------------------------------------
// Did a transform change?
//-----------------------------------------------------------------------------
inline bool CShaderAPIDx8::VertexShaderTransformChanged(int i)
{
return (m_DynamicState.m_TransformChanged[i] & STATE_CHANGED_VERTEX_SHADER) != 0;
}
inline bool CShaderAPIDx8::FixedFunctionTransformChanged(int i)
{
return (m_DynamicState.m_TransformChanged[i] & STATE_CHANGED_FIXED_FUNCTION) != 0;
}
const D3DXMATRIX& CShaderAPIDx8::GetProjectionMatrix(void)
{
bool bUsingZBiasProjectionMatrix =
!g_pHardwareConfig->Caps().m_ZBiasAndSlopeScaledDepthBiasSupported &&
(m_TransitionTable.CurrentSnapshot() != -1) &&
m_TransitionTable.CurrentShadowState() &&
m_TransitionTable.CurrentShadowState()->m_ZBias;
if (!m_DynamicState.m_FastClipEnabled)
{
if (bUsingZBiasProjectionMatrix)
return m_CachedPolyOffsetProjectionMatrix;
return GetTransform(MATERIAL_PROJECTION);
}
if (bUsingZBiasProjectionMatrix)
return m_CachedFastClipPolyOffsetProjectionMatrix;
return m_CachedFastClipProjectionMatrix;
}
//-----------------------------------------------------------------------------
// Workaround hack for visualization of selection mode
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetupSelectionModeVisualizationState()
{
Dx9Device()->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
D3DXMATRIX ident;
D3DXMatrixIdentity(&ident);
SetTransform(D3DTS_WORLD, &ident);
SetTransform(D3DTS_VIEW, &ident);
SetTransform(D3DTS_PROJECTION, &ident);
if (g_pHardwareConfig->Caps().m_SupportsPixelShaders)
{
SetVertexShaderConstant(VERTEX_SHADER_VIEWPROJ, ident, 4);
SetVertexShaderConstant(VERTEX_SHADER_MODELVIEWPROJ, ident, 4);
float* pRowTwo = (float*)ident + 8;
SetVertexShaderConstant(VERTEX_SHADER_MODELVIEWPROJ_THIRD_ROW, pRowTwo, 1); // Row two of an identity matrix
SetVertexShaderConstant(VERTEX_SHADER_MODEL, ident, 3 * NUM_MODEL_TRANSFORMS);
}
}
//-----------------------------------------------------------------------------
// Set view transforms
//-----------------------------------------------------------------------------
static void printmat4x4(char* label, float* m00)
{
// print label..
// fetch 4 from row, print as a row
// fetch 4 from column, print as a row
#ifdef DX_TO_GL_ABSTRACTION
float row[4];
float col[4];
GLMPRINTF(("-M- -- %s --", label));
for (int n = 0; n < 4; n++)
{
// extract row and column floats
for (int i = 0; i < 4; i++)
{
row[i] = m00[(n * 4) + i];
col[i] = m00[(i * 4) + n];
}
GLMPRINTF(("-M- [ %7.4f %7.4f %7.4f %7.4f ] T=> [ %7.4f %7.4f %7.4f %7.4f ]",
row[0], row[1], row[2], row[3],
col[0], col[1], col[2], col[3]
));
}
GLMPRINTF(("-M-"));
#endif
}
void CShaderAPIDx8::SetVertexShaderViewProj()
{
//GLM_FUNC;
//GLMPRINTF(( ">-M- SetVertexShaderViewProj" ));
if (g_pHardwareConfig->Caps().m_SupportsPixelShaders)
{
D3DXMATRIX transpose;
if (0)
{
transpose = GetTransform(MATERIAL_VIEW) * GetProjectionMatrix();
D3DXMatrixTranspose(&transpose, &transpose);
}
else
{
// show work
D3DXMATRIX matView, matProj;
matView = GetTransform(MATERIAL_VIEW);
matProj = GetProjectionMatrix();
transpose = matView * matProj;
//printmat4x4( "matView", (float*)&matView );
//printmat4x4( "matProj", (float*)&matProj );
//printmat4x4( "result (view * proj) pre-transpose", (float*)&transpose );
D3DXMatrixTranspose(&transpose, &transpose);
#if 0 // turned off while we try to do fixup-Y in shader translate
if (IsPosix()) // flip all shader projection matrices for Y on GL since you can't have an upside-down viewport specification
{
// flip Y
transpose._21 *= -1.0f;
transpose._22 *= -1.0f;
transpose._23 *= -1.0f;
transpose._24 *= -1.0f;
}
#endif
//printmat4x4( "result (view * proj) post-transpose", (float*)&transpose );
}
SetVertexShaderConstant(VERTEX_SHADER_VIEWPROJ, transpose, 4);
// If we're doing FastClip, the above viewproj matrix won't work well for
// vertex shaders which compute projPos.z, hence we'll compute a more useful
// viewproj and put the third row of it in another constant
transpose = GetTransform(MATERIAL_VIEW) * GetTransform(MATERIAL_PROJECTION); // Get the non-FastClip projection matrix
D3DXMatrixTranspose(&transpose, &transpose);
float* pRowTwo = (float*)transpose + 8;
SetVertexShaderConstant(VERTEX_SHADER_VIEWPROJ_THIRD_ROW, pRowTwo, 1);
}
//GLMPRINTF(( "<-M- SetVertexShaderViewProj" ));
}
void CShaderAPIDx8::SetVertexShaderModelViewProjAndModelView(void)
{
//GLM_FUNC;
//GLMPRINTF(( ">-M- SetVertexShaderModelViewProjAndModelView" ));
if (g_pHardwareConfig->Caps().m_SupportsPixelShaders)
{
D3DXMATRIX modelView, transpose;
if (0)
{
D3DXMatrixMultiply(&modelView, &GetTransform(MATERIAL_MODEL), &GetTransform(MATERIAL_VIEW));
D3DXMatrixMultiply(&transpose, &modelView, &GetProjectionMatrix());
}
else
{
// show work
D3DXMATRIX matView, matProj, matModel;
matModel = GetTransform(MATERIAL_MODEL);
matView = GetTransform(MATERIAL_VIEW);
matProj = GetProjectionMatrix();
D3DXMatrixMultiply(&modelView, &matModel, &matView);
D3DXMatrixMultiply(&transpose, &modelView, &matProj);
//printmat4x4( "matModel", (float*)&matModel );
//printmat4x4( "matView", (float*)&matView );
//printmat4x4( "matProj", (float*)&matProj );
//printmat4x4( "result (model * view * proj) pre-transpose", (float*)&transpose );
}
D3DXMatrixTranspose(&transpose, &transpose);
#if 0 // turned off while we try to do fixup-Y in shader translate
if (IsPosix()) // flip all shader projection matrices for Y on GL since you can't have an upside-down viewport specification
{
// flip Y
transpose._21 *= -1.0f;
transpose._22 *= -1.0f;
transpose._23 *= -1.0f;
transpose._24 *= -1.0f;
}
#endif
SetVertexShaderConstant(VERTEX_SHADER_MODELVIEWPROJ, transpose, 4);
// If we're doing FastClip, the above modelviewproj matrix won't work well for
// vertex shaders which compute projPos.z, hence we'll compute a more useful
// modelviewproj and put the third row of it in another constant
D3DXMatrixMultiply(&transpose, &modelView, &GetTransform(MATERIAL_PROJECTION)); // Get the non-FastClip projection matrix
D3DXMatrixTranspose(&transpose, &transpose);
float* pRowTwo = (float*)transpose + 8;
SetVertexShaderConstant(VERTEX_SHADER_MODELVIEWPROJ_THIRD_ROW, pRowTwo, 1);
}
//GLMPRINTF(( "<-M- SetVertexShaderModelViewProjAndModelView" ));
}
void CShaderAPIDx8::UpdateVertexShaderMatrix(int iMatrix)
{
//GLM_FUNC;
if (iMatrix == 0)
{
int matrix = MATERIAL_MODEL;
if (VertexShaderTransformChanged(matrix))
{
int vertexShaderConstant = VERTEX_SHADER_MODEL + iMatrix * 3;
// Put the transform into the vertex shader constants...
D3DXMATRIX transpose;
D3DXMatrixTranspose(&transpose, &GetTransform(matrix));
SetVertexShaderConstant(vertexShaderConstant, transpose, 3);
// clear the change flag
m_DynamicState.m_TransformChanged[matrix] &= ~STATE_CHANGED_VERTEX_SHADER;
}
}
else
{
SetVertexShaderConstant(VERTEX_SHADER_MODEL + iMatrix, m_boneMatrix[iMatrix].Base(), 3);
}
}
void CShaderAPIDx8::SetVertexShaderStateSkinningMatrices()
{
//GLM_FUNC;
// casting from 4x3 matrices to a 4x4 D3DXMATRIX, need 4 floats of overflow
float results[12 + 4];
// get the first one from the MATERIAL_MODEL matrix stack
D3DXMatrixTranspose((D3DXMATRIX*)&results[0], &GetTransform(MATERIAL_MODEL));
memcpy(m_boneMatrix[0].Base(), results, 12 * sizeof(float));
m_maxBoneLoaded++;
int matricesLoaded = max(1, m_maxBoneLoaded);
m_maxBoneLoaded = 0;
m_DynamicState.m_TransformChanged[MATERIAL_MODEL] &= ~STATE_CHANGED_VERTEX_SHADER;
SetVertexShaderConstant(VERTEX_SHADER_MODEL, m_boneMatrix[0].Base(), matricesLoaded * 3, true);
// ###OSX### punting on OSX for now
#if defined( DX_TO_GL_ABSTRACTION ) && !defined( OSX )
Dx9Device()->SetMaxUsedVertexShaderConstantsHint(VERTEX_SHADER_MODEL + (matricesLoaded * 3));
#endif
}
//-----------------------------------------------------------------------------
// Commits vertex shader transforms that can change on a per pass basis
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitPerPassVertexShaderTransforms()
{
//GLMPRINTF(( ">-M- CommitPerPassVertexShaderTransforms" ));
Assert(g_pHardwareConfig->Caps().m_SupportsPixelShaders);
bool projChanged = VertexShaderTransformChanged(MATERIAL_PROJECTION);
//projChanged = true; //only for debug
if (projChanged)
{
//GLMPRINTF(( "-M- projChanged=true in CommitPerPassVertexShaderTransforms" ));
SetVertexShaderViewProj();
SetVertexShaderModelViewProjAndModelView();
// Clear change flags
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] &= ~STATE_CHANGED_VERTEX_SHADER;
}
else
{
//GLMPRINTF(( "-M- projChanged=false in CommitPerPassVertexShaderTransforms" ));
}
//GLMPRINTF(( "<-M- CommitPerPassVertexShaderTransforms" ));
}
//-----------------------------------------------------------------------------
// Commits vertex shader transforms
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitVertexShaderTransforms()
{
//GLMPRINTF(( ">-M- CommitVertexShaderTransforms" ));
Assert(g_pHardwareConfig->Caps().m_SupportsPixelShaders);
bool viewChanged = VertexShaderTransformChanged(MATERIAL_VIEW);
bool projChanged = VertexShaderTransformChanged(MATERIAL_PROJECTION);
bool modelChanged = VertexShaderTransformChanged(MATERIAL_MODEL) && (m_DynamicState.m_NumBones < 1);
//GLMPRINTF(( "-M- viewChanged=%s projChanged=%s modelChanged = %s in CommitVertexShaderTransforms", viewChanged?"Y":"N",projChanged?"Y":"N",modelChanged?"Y":"N" ));
if (viewChanged)
{
//GLMPRINTF(( "-M- viewChanged --> UpdateVertexShaderFogParams" ));
UpdateVertexShaderFogParams();
}
if (viewChanged || projChanged)
{
// NOTE: We have to deal with fast-clip *before*
//GLMPRINTF(( "-M- viewChanged||projChanged --> SetVertexShaderViewProj" ));
SetVertexShaderViewProj();
}
if (viewChanged || modelChanged || projChanged)
{
//GLMPRINTF(( "-M- viewChanged||projChanged||modelChanged --> SetVertexShaderModelViewProjAndModelView" ));
SetVertexShaderModelViewProjAndModelView();
}
if (modelChanged && m_DynamicState.m_NumBones < 1)
{
UpdateVertexShaderMatrix(0);
}
// Clear change flags
m_DynamicState.m_TransformChanged[MATERIAL_MODEL] &= ~STATE_CHANGED_VERTEX_SHADER;
m_DynamicState.m_TransformChanged[MATERIAL_VIEW] &= ~STATE_CHANGED_VERTEX_SHADER;
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] &= ~STATE_CHANGED_VERTEX_SHADER;
//GLMPRINTF(( "<-M- CommitVertexShaderTransforms" ));
}
void CShaderAPIDx8::UpdateFixedFunctionMatrix(int iMatrix)
{
if (IsX360())
return;
int matrix = MATERIAL_MODEL + iMatrix;
if (FixedFunctionTransformChanged(matrix))
{
SetTransform(D3DTS_WORLDMATRIX(iMatrix), &GetTransform(matrix));
// clear the change flag
m_DynamicState.m_TransformChanged[matrix] &= ~STATE_CHANGED_FIXED_FUNCTION;
}
}
void CShaderAPIDx8::SetFixedFunctionStateSkinningMatrices()
{
if (IsX360())
return;
for (int i = 1; i < g_pHardwareConfig->MaxBlendMatrices(); i++)
{
UpdateFixedFunctionMatrix(i);
}
}
//-----------------------------------------------------------------------------
// Commits transforms for the fixed function pipeline that can happen on a per pass basis
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitPerPassFixedFunctionTransforms()
{
if (IsX360())
return;
// Update projection
if (FixedFunctionTransformChanged(MATERIAL_PROJECTION))
{
D3DTRANSFORMSTATETYPE matrix = D3DTS_PROJECTION;
SetTransform(matrix, &GetProjectionMatrix());
// clear the change flag
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] &= ~STATE_CHANGED_FIXED_FUNCTION;
}
}
//-----------------------------------------------------------------------------
// Commits transforms for the fixed function pipeline
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitFixedFunctionTransforms()
{
if (IsX360())
return;
// Update view + projection
int i;
for (i = MATERIAL_VIEW; i <= MATERIAL_PROJECTION; ++i)
{
if (FixedFunctionTransformChanged(i))
{
D3DTRANSFORMSTATETYPE matrix = (i == MATERIAL_VIEW) ? D3DTS_VIEW : D3DTS_PROJECTION;
if (i == MATERIAL_PROJECTION)
{
SetTransform(matrix, &GetProjectionMatrix());
}
else
{
SetTransform(matrix, &GetTransform(i));
}
// clear the change flag
m_DynamicState.m_TransformChanged[i] &= ~STATE_CHANGED_FIXED_FUNCTION;
}
}
UpdateFixedFunctionMatrix(0);
}
void CShaderAPIDx8::SetSkinningMatrices()
{
LOCK_SHADERAPI();
Assert(m_pMaterial);
if (m_DynamicState.m_NumBones == 0)
{
// ###OSX### punting on OSX for now
#if defined( DX_TO_GL_ABSTRACTION ) && !defined( OSX)
Dx9Device()->SetMaxUsedVertexShaderConstantsHint(VERTEX_SHADER_BONE_TRANSFORM(0) + 3);
#endif
return;
}
uint nMaxVertexConstantIndex = 0;
if (IsX360() || UsesVertexShader(m_pMaterial->GetVertexFormat()))
{
SetVertexShaderStateSkinningMatrices();
}
else if (IsPC())
{
#if defined( DX_TO_GL_ABSTRACTION ) && !defined( OSX)
Assert(0);
#else
SetFixedFunctionStateSkinningMatrices();
#endif
}
else
{
Assert(0);
}
}
//-----------------------------------------------------------------------------
// Commits vertex shader lighting
//-----------------------------------------------------------------------------
inline bool CShaderAPIDx8::VertexShaderLightingChanged(int i)
{
return (m_DynamicState.m_LightChanged[i] & STATE_CHANGED_VERTEX_SHADER) != 0;
}
inline bool CShaderAPIDx8::VertexShaderLightingEnableChanged(int i)
{
return (m_DynamicState.m_LightEnableChanged[i] & STATE_CHANGED_VERTEX_SHADER) != 0;
}
inline bool CShaderAPIDx8::FixedFunctionLightingChanged(int i)
{
return (m_DynamicState.m_LightChanged[i] & STATE_CHANGED_FIXED_FUNCTION) != 0;
}
inline bool CShaderAPIDx8::FixedFunctionLightingEnableChanged(int i)
{
return (m_DynamicState.m_LightEnableChanged[i] & STATE_CHANGED_FIXED_FUNCTION) != 0;
}
//-----------------------------------------------------------------------------
// Computes the light type
//-----------------------------------------------------------------------------
VertexShaderLightTypes_t CShaderAPIDx8::ComputeLightType(int i) const
{
if (!m_DynamicState.m_LightEnable[i])
return LIGHT_NONE;
switch (m_DynamicState.m_Lights[i].Type)
{
case D3DLIGHT_POINT:
return LIGHT_POINT;
case D3DLIGHT_DIRECTIONAL:
return LIGHT_DIRECTIONAL;
case D3DLIGHT_SPOT:
return LIGHT_SPOT;
}
Assert(0);
return LIGHT_NONE;
}
//-----------------------------------------------------------------------------
// Sort the lights by type
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SortLights(int* index)
{
m_DynamicState.m_NumLights = 0;
for (int i = 0; i < MAX_NUM_LIGHTS; ++i)
{
VertexShaderLightTypes_t type = ComputeLightType(i); // returns LIGHT_NONE if the light is disabled
int j = m_DynamicState.m_NumLights;
if (type != LIGHT_NONE)
{
while (--j >= 0)
{
if (m_DynamicState.m_LightType[j] <= type)
break;
// shift...
m_DynamicState.m_LightType[j + 1] = m_DynamicState.m_LightType[j];
index[j + 1] = index[j];
}
++j;
m_DynamicState.m_LightType[j] = type;
index[j] = i;
++m_DynamicState.m_NumLights;
}
}
}
//-----------------------------------------------------------------------------
// Vertex Shader lighting
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitVertexShaderLighting()
{
// If nothing changed, then don't bother. Otherwise, reload...
int i;
for (i = 0; i < MAX_NUM_LIGHTS; ++i)
{
if (VertexShaderLightingChanged(i) || VertexShaderLightingEnableChanged(i))
break;
}
// Yeah baby
if (i == MAX_NUM_LIGHTS)
return;
// First, gotta sort the lights by their type
int lightIndex[MAX_NUM_LIGHTS];
memset(lightIndex, 0, sizeof(lightIndex));
SortLights(lightIndex);
// Clear the lighting enable flags
for (i = 0; i < MAX_NUM_LIGHTS; ++i)
{
m_DynamicState.m_LightEnableChanged[i] &= ~STATE_CHANGED_VERTEX_SHADER;
m_DynamicState.m_LightChanged[i] &= ~STATE_CHANGED_VERTEX_SHADER;
}
bool bAtLeastDX90 = g_pHardwareConfig->GetDXSupportLevel() >= 90;
// Set the lighting state
for (i = 0; i < m_DynamicState.m_NumLights; ++i)
{
D3DLIGHT& light = m_DynamicState.m_Lights[lightIndex[i]];
Vector4D lightState[5];
// The first one is the light color (and light type code on DX9)
float w = (light.Type == D3DLIGHT_DIRECTIONAL) && bAtLeastDX90 ? 1.0f : 0.0f;
lightState[0].Init(light.Diffuse.r, light.Diffuse.g, light.Diffuse.b, w);
// The next constant holds the light direction (and light type code on DX9)
w = (light.Type == D3DLIGHT_SPOT) && bAtLeastDX90 ? 1.0f : 0.0f;
lightState[1].Init(light.Direction.x, light.Direction.y, light.Direction.z, w);
// The next constant holds the light position
lightState[2].Init(light.Position.x, light.Position.y, light.Position.z, 1.0f);
// The next constant holds exponent, stopdot, stopdot2, 1 / (stopdot - stopdot2)
if (light.Type == D3DLIGHT_SPOT)
{
float stopdot = cos(light.Theta * 0.5f);
float stopdot2 = cos(light.Phi * 0.5f);
float oodot = (stopdot > stopdot2) ? 1.0f / (stopdot - stopdot2) : 0.0f;
lightState[3].Init(light.Falloff, stopdot, stopdot2, oodot);
}
else
{
lightState[3].Init(0, 1, 1, 1);
}
// The last constant holds attenuation0, attenuation1, attenuation2
lightState[4].Init(light.Attenuation0, light.Attenuation1, light.Attenuation2, 0.0f);
// Set the state
SetVertexShaderConstant(VERTEX_SHADER_LIGHTS + i * 5, lightState[0].Base(), 5);
}
if (g_pHardwareConfig->NumIntegerVertexShaderConstants() > 0 && g_pHardwareConfig->NumBooleanVertexShaderConstants() > 0)
{
// Vertex Shader loop counter for number of lights (Only the .x component is used by our shaders)
// .x is the iteration count, .y is the initial value and .z is the increment step
int nLoopControl[4] = { m_DynamicState.m_NumLights, 0, 1, 0 };
SetIntegerVertexShaderConstant(0, nLoopControl, 1);
// Enable lights using vertex shader static flow control
int nLightEnable[VERTEX_SHADER_LIGHT_ENABLE_BOOL_CONST_COUNT] = { 0, 0, 0, 0 };
for (i = 0; i < m_DynamicState.m_NumLights; ++i)
{
nLightEnable[i] = 1;
}
SetBooleanVertexShaderConstant(VERTEX_SHADER_LIGHT_ENABLE_BOOL_CONST, nLightEnable, VERTEX_SHADER_LIGHT_ENABLE_BOOL_CONST_COUNT);
}
}
//-----------------------------------------------------------------------------
// Set the pixel shader constants for lights
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitPixelShaderLighting(int pshReg)
{
#ifndef NDEBUG
char const* materialName = m_pMaterial->GetName();
#endif
// First, gotta sort the lights by their type
int lightIndex[MAX_NUM_LIGHTS];
SortLights(lightIndex);
// Offset to create a point light from directional
const float fFarAway = 10000.0f;
// Total pixel shader lighting state for four lights
Vector4D lightState[6];
for (int i = 0; i < 6; i++)
lightState[i].Init();
int nNumLights = m_DynamicState.m_NumLights;
if (nNumLights > 0)
{
D3DLIGHT* light = &m_DynamicState.m_Lights[lightIndex[0]];
lightState[0].Init(light->Diffuse.r, light->Diffuse.g, light->Diffuse.b, 0.0f);
if (light->Type == D3DLIGHT_DIRECTIONAL)
{
Vector vDir(light->Direction.x, light->Direction.y, light->Direction.z);
Vector vPos = m_DynamicState.m_vLightingOrigin - vDir * fFarAway;
lightState[1].Init(vPos.x, vPos.y, vPos.z, 0.0f);
}
else
{
lightState[1].Init(light->Position.x, light->Position.y, light->Position.z, 0.0f);
}
if (nNumLights > 1) // At least two lights
{
light = &m_DynamicState.m_Lights[lightIndex[1]];
lightState[2].Init(light->Diffuse.r, light->Diffuse.g, light->Diffuse.b, 0.0f);
if (light->Type == D3DLIGHT_DIRECTIONAL)
{
Vector vDir(light->Direction.x, light->Direction.y, light->Direction.z);
Vector vPos = m_DynamicState.m_vLightingOrigin - vDir * fFarAway;
lightState[3].Init(vPos.x, vPos.y, vPos.z, 0.0f);
}
else
{
lightState[3].Init(light->Position.x, light->Position.y, light->Position.z, 0.0f);
}
if (nNumLights > 2) // At least three lights
{
light = &m_DynamicState.m_Lights[lightIndex[2]];
lightState[4].Init(light->Diffuse.r, light->Diffuse.g, light->Diffuse.b, 0.0f);
if (light->Type == D3DLIGHT_DIRECTIONAL)
{
Vector vDir(light->Direction.x, light->Direction.y, light->Direction.z);
Vector vPos = m_DynamicState.m_vLightingOrigin - vDir * fFarAway;
lightState[5].Init(vPos.x, vPos.y, vPos.z, 0.0f);
}
else
{
lightState[5].Init(light->Position.x, light->Position.y, light->Position.z, 0.0f);
}
if (nNumLights > 3) // At least four lights (our current max)
{
light = &m_DynamicState.m_Lights[lightIndex[3]]; // Spread 4th light's constants across w components
lightState[0].w = light->Diffuse.r;
lightState[1].w = light->Diffuse.g;
lightState[2].w = light->Diffuse.b;
if (light->Type == D3DLIGHT_DIRECTIONAL)
{
Vector vDir(light->Direction.x, light->Direction.y, light->Direction.z);
Vector vPos = m_DynamicState.m_vLightingOrigin - vDir * fFarAway;
lightState[3].w = vPos.x;
lightState[4].w = vPos.y;
lightState[5].w = vPos.z;
}
else
{
lightState[3].w = light->Position.x;
lightState[4].w = light->Position.y;
lightState[5].w = light->Position.z;
}
}
}
}
}
SetPixelShaderConstant(pshReg, lightState[0].Base(), 6);
}
//-----------------------------------------------------------------------------
// Fixed function lighting
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitFixedFunctionLighting()
{
if (IsX360())
{
return;
}
// Commit each light
for (int i = 0; i < g_pHardwareConfig->MaxNumLights(); ++i)
{
// Change light enable
if (FixedFunctionLightingEnableChanged(i))
{
LightEnable(i, m_DynamicState.m_LightEnable[i]);
// Clear change flag
m_DynamicState.m_LightEnableChanged[i] &= ~STATE_CHANGED_FIXED_FUNCTION;
}
// Change lighting state...
if (m_DynamicState.m_LightEnable[i])
{
if (FixedFunctionLightingChanged(i))
{
// Store off the "correct" falloff...
D3DLIGHT& light = m_DynamicState.m_Lights[i];
float falloff = light.Falloff;
SetLight(i, &light);
// Clear change flag
m_DynamicState.m_LightChanged[i] &= ~STATE_CHANGED_FIXED_FUNCTION;
// restore the correct falloff
light.Falloff = falloff;
}
}
}
}
//-----------------------------------------------------------------------------
// Commits user clip planes
//-----------------------------------------------------------------------------
D3DXMATRIX& CShaderAPIDx8::GetUserClipTransform()
{
if (!m_DynamicState.m_bUserClipTransformOverride)
return GetTransform(MATERIAL_VIEW);
return m_DynamicState.m_UserClipTransform;
}
//-----------------------------------------------------------------------------
// Commits user clip planes
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitUserClipPlanes(bool bUsingFixedFunction)
{
// We need to transform the clip planes, specified in world space,
// to be in projection space.. To transform the plane, we must transform
// the intercept and then transform the normal.
if (bUsingFixedFunction != m_DynamicState.m_UserClipLastUpdatedUsingFixedFunction)
{
//fixed function clip planes are in world space, vertex shader clip planes are in clip space, so we need to update every clip plane whenever there's a flip
m_DynamicState.m_UserClipPlaneChanged = (1 << g_pHardwareConfig->MaxUserClipPlanes()) - 1;
m_DynamicState.m_UserClipLastUpdatedUsingFixedFunction = bUsingFixedFunction;
}
D3DXMATRIX worldToProjectionInvTrans;
#ifndef _DEBUG
if (m_DynamicState.m_UserClipPlaneChanged & m_DynamicState.m_UserClipPlaneEnabled & ((1 << g_pHardwareConfig->MaxUserClipPlanes()) - 1))
#endif
{
//we're going to need the transformation matrix at least once this call
if (bUsingFixedFunction)
{
if (m_DynamicState.m_bUserClipTransformOverride)
{
//D3DXMatrixIdentity( &worldToProjectionInvTrans ); //TODO: Test user clip transforms with this
//Since GetUserClipTransform() returns the view matrix if a user supplied transform doesn't exist, the general solution to this should be to transform the user transform by the inverse view matrix
//Since we don't know if the user clip is invertable, we'll premultiply by inverse view and cross our fingers that it's right more often than wrong
D3DXMATRIX viewInverse = GetTransform(MATERIAL_VIEW);
D3DXMatrixInverse(&viewInverse, NULL, &viewInverse);
worldToProjectionInvTrans = viewInverse * GetUserClipTransform(); //taking a cue from the multiplication below, multiplication goes left into right
D3DXMatrixInverse(&worldToProjectionInvTrans, NULL, &worldToProjectionInvTrans);
D3DXMatrixTranspose(&worldToProjectionInvTrans, &worldToProjectionInvTrans);
}
else
{
D3DXMatrixIdentity(&worldToProjectionInvTrans);
}
}
else
{
worldToProjectionInvTrans = GetUserClipTransform() * GetTransform(MATERIAL_PROJECTION);
D3DXMatrixInverse(&worldToProjectionInvTrans, NULL, &worldToProjectionInvTrans);
D3DXMatrixTranspose(&worldToProjectionInvTrans, &worldToProjectionInvTrans);
}
}
for (int i = 0; i < g_pHardwareConfig->MaxUserClipPlanes(); ++i)
{
// Don't bother with the plane if it's not enabled
if ((m_DynamicState.m_UserClipPlaneEnabled & (1 << i)) == 0)
continue;
// Don't bother if it didn't change...
if ((m_DynamicState.m_UserClipPlaneChanged & (1 << i)) == 0)
{
#ifdef _DEBUG
//verify that the plane has not actually changed
D3DXPLANE clipPlaneProj;
D3DXPlaneTransform(&clipPlaneProj, &m_DynamicState.m_UserClipPlaneWorld[i], &worldToProjectionInvTrans);
Assert(clipPlaneProj == m_DynamicState.m_UserClipPlaneProj[i]);
#endif
continue;
}
m_DynamicState.m_UserClipPlaneChanged &= ~(1 << i);
D3DXPLANE clipPlaneProj;
D3DXPlaneTransform(&clipPlaneProj, &m_DynamicState.m_UserClipPlaneWorld[i], &worldToProjectionInvTrans);
if (clipPlaneProj != m_DynamicState.m_UserClipPlaneProj[i])
{
Dx9Device()->SetClipPlane(i, (float*)clipPlaneProj);
m_DynamicState.m_UserClipPlaneProj[i] = clipPlaneProj;
}
}
}
//-----------------------------------------------------------------------------
// Need to handle fog mode on a per-pass basis
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitPerPassFogMode(bool bUsingVertexAndPixelShaders)
{
if (IsX360())
{
// FF fog not applicable on 360
return;
}
D3DFOGMODE dxFogMode = D3DFOG_NONE;
if (m_DynamicState.m_FogEnable)
{
dxFogMode = bUsingVertexAndPixelShaders ? D3DFOG_NONE : D3DFOG_LINEAR;
}
// Set fog mode if it's different than before.
if (m_DynamicState.m_FogMode != dxFogMode)
{
SetRenderStateConstMacro(this, D3DRS_FOGVERTEXMODE, dxFogMode);
m_DynamicState.m_FogMode = dxFogMode;
}
}
//-----------------------------------------------------------------------------
// Handle Xbox GPU/DX API fixups necessary before actual draw.
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitPerPassXboxFixups()
{
#if defined( _X360 )
// send updated shader constants to gpu
WriteShaderConstantsToGPU();
// sRGB write state may have changed after RT set, have to re-set correct RT
SetRenderTargetInternalXbox(m_hCachedRenderTarget);
#endif
}
//-----------------------------------------------------------------------------
// These states can change between each pass
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitPerPassStateChanges(StateSnapshot_t id)
{
if (IsX360() || UsesVertexAndPixelShaders(id))
{
CommitPerPassVertexShaderTransforms();
CommitPerPassFogMode(true);
CommitPerPassXboxFixups();
CallCommitFuncs(COMMIT_PER_PASS, false);
}
else if (IsPC())
{
CommitPerPassFixedFunctionTransforms();
CommitPerPassFogMode(false);
CallCommitFuncs(COMMIT_PER_PASS, true);
}
else
{
Assert(0);
}
}
//-----------------------------------------------------------------------------
// Commits transforms and lighting
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CommitStateChanges()
{
VPROF("CShaderAPIDx8::CommitStateChanges");
CommitFastClipPlane();
bool bUsingFixedFunction = !IsX360() && m_pMaterial && !UsesVertexShader(m_pMaterial->GetVertexFormat());
// xboxissue - cannot support ff pipeline
Assert(IsPC() || (IsX360() && !bUsingFixedFunction));
if (IsX360() || !bUsingFixedFunction)
{
CommitVertexShaderTransforms();
if (m_pMaterial && m_pMaterial->IsVertexLit())
{
CommitVertexShaderLighting();
}
}
else if (IsPC())
{
CommitFixedFunctionTransforms();
if (m_pMaterial && (m_pMaterial->IsVertexLit() || m_pMaterial->NeedsFixedFunctionFlashlight()))
{
CommitFixedFunctionLighting();
}
}
else
{
Assert(0);
}
if (m_DynamicState.m_UserClipPlaneEnabled)
{
CommitUserClipPlanes(bUsingFixedFunction);
}
CallCommitFuncs(COMMIT_PER_DRAW, bUsingFixedFunction);
}
//-----------------------------------------------------------------------------
// Commits viewports
//-----------------------------------------------------------------------------
static void CommitSetViewports(IDirect3DDevice9* pDevice, const DynamicState_t& desiredState, DynamicState_t& currentState, bool bForce)
{
bool bChanged = bForce || memcmp(&desiredState.m_Viewport, &currentState.m_Viewport, sizeof(D3DVIEWPORT9));
// The width + height can be zero at startup sometimes.
if (bChanged && (desiredState.m_Viewport.Width != 0) && (desiredState.m_Viewport.Height != 0))
{
if (ReverseDepthOnX360()) //reverse depth on 360 for better perf through hierarchical z
{
D3DVIEWPORT9 reverseDepthViewport;
reverseDepthViewport = desiredState.m_Viewport;
reverseDepthViewport.MinZ = 1.0f - desiredState.m_Viewport.MinZ;
reverseDepthViewport.MaxZ = 1.0f - desiredState.m_Viewport.MaxZ;
Dx9Device()->SetViewport(&reverseDepthViewport);
}
else
{
Dx9Device()->SetViewport(&desiredState.m_Viewport);
}
memcpy(&currentState.m_Viewport, &desiredState.m_Viewport, sizeof(D3DVIEWPORT9));
}
}
void CShaderAPIDx8::SetViewports(int nCount, const ShaderViewport_t* pViewports)
{
Assert(nCount == 1 && pViewports[0].m_nVersion == SHADER_VIEWPORT_VERSION);
if (nCount != 1)
return;
LOCK_SHADERAPI();
D3DVIEWPORT9 viewport;
viewport.X = pViewports[0].m_nTopLeftX;
viewport.Y = pViewports[0].m_nTopLeftY;
viewport.Width = pViewports[0].m_nWidth;
viewport.Height = pViewports[0].m_nHeight;
viewport.MinZ = pViewports[0].m_flMinZ;
viewport.MaxZ = pViewports[0].m_flMaxZ;
// Clamp the viewport to the current render target...
if (!m_UsingTextureRenderTarget)
{
// Clamp to both the back buffer and the window, if it is resizing
int nMaxWidth = 0, nMaxHeight = 0;
GetBackBufferDimensions(nMaxWidth, nMaxHeight);
if (IsPC() && m_IsResizing)
{
RECT viewRect;
#if !defined( DX_TO_GL_ABSTRACTION )
GetClientRect((HWND)m_ViewHWnd, &viewRect);
#else
toglGetClientRect((VD3DHWND)m_ViewHWnd, &viewRect);
#endif
m_nWindowWidth = viewRect.right - viewRect.left;
m_nWindowHeight = viewRect.bottom - viewRect.top;
nMaxWidth = min(m_nWindowWidth, nMaxWidth);
nMaxHeight = min(m_nWindowHeight, nMaxHeight);
}
// Dimensions can freak out on app exit, so at least make sure the viewport is positive
if ((viewport.Width > (unsigned int)nMaxWidth) && (nMaxWidth > 0))
{
viewport.Width = nMaxWidth;
}
// Dimensions can freak out on app exit, so at least make sure the viewport is positive
if ((viewport.Height > (unsigned int)nMaxHeight) && (nMaxHeight > 0))
{
viewport.Height = nMaxHeight;
}
}
else
{
if (viewport.Width > (unsigned int)m_ViewportMaxWidth)
{
viewport.Width = m_ViewportMaxWidth;
}
if (viewport.Height > (unsigned int)m_ViewportMaxHeight)
{
viewport.Height = m_ViewportMaxHeight;
}
}
// FIXME: Once we extract buffered primitives out, we can directly fill in desired state
// and avoid the memcmp and copy
if (memcmp(&m_DesiredState.m_Viewport, &viewport, sizeof(D3DVIEWPORT9)))
{
if (!IsDeactivated())
{
// State changed... need to flush the dynamic buffer
FlushBufferedPrimitives();
}
memcpy(&m_DesiredState.m_Viewport, &viewport, sizeof(D3DVIEWPORT9));
}
ADD_COMMIT_FUNC(COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitSetViewports);
}
//-----------------------------------------------------------------------------
// Gets the current viewport size
//-----------------------------------------------------------------------------
int CShaderAPIDx8::GetViewports(ShaderViewport_t* pViewports, int nMax) const
{
if (!pViewports || nMax == 0)
return 1;
LOCK_SHADERAPI();
pViewports[0].m_nTopLeftX = m_DesiredState.m_Viewport.X;
pViewports[0].m_nTopLeftY = m_DesiredState.m_Viewport.Y;
pViewports[0].m_nWidth = m_DesiredState.m_Viewport.Width;
pViewports[0].m_nHeight = m_DesiredState.m_Viewport.Height;
pViewports[0].m_flMinZ = m_DesiredState.m_Viewport.MinZ;
pViewports[0].m_flMaxZ = m_DesiredState.m_Viewport.MaxZ;
return 1;
}
//-----------------------------------------------------------------------------
// Flushes buffered primitives
//-----------------------------------------------------------------------------
void CShaderAPIDx8::FlushBufferedPrimitives()
{
if (ShaderUtil())
{
if (!ShaderUtil()->OnFlushBufferedPrimitives())
{
return;
}
}
FlushBufferedPrimitivesInternal();
}
void CShaderAPIDx8::FlushBufferedPrimitivesInternal()
{
LOCK_SHADERAPI();
// This shouldn't happen in the inner rendering loop!
Assert(m_pRenderMesh == 0);
// NOTE: We've gotta store off the matrix mode because
// it'll get reset by the default state application caused by the flush
int tempStack = m_CurrStack;
D3DTRANSFORMSTATETYPE tempMatrixMode = m_MatrixMode;
MeshMgr()->Flush();
m_CurrStack = tempStack;
m_MatrixMode = tempMatrixMode;
}
//-----------------------------------------------------------------------------
// Flush the hardware
//-----------------------------------------------------------------------------
void CShaderAPIDx8::FlushHardware()
{
LOCK_SHADERAPI();
FlushBufferedPrimitives();
Dx9Device()->EndScene();
DiscardVertexBuffers();
Dx9Device()->BeginScene();
ForceHardwareSync();
}
//-----------------------------------------------------------------------------
// Deal with device lost (alt-tab)
//-----------------------------------------------------------------------------
void CShaderAPIDx8::HandleDeviceLost()
{
if (IsX360())
{
return;
}
LOCK_SHADERAPI();
if (!IsActive())
return;
// need to flush the dynamic buffer
FlushBufferedPrimitives();
if (!IsDeactivated())
{
Dx9Device()->EndScene();
}
CheckDeviceLost(m_bOtherAppInitializing);
if (!IsDeactivated())
{
Dx9Device()->BeginScene();
}
}
//-----------------------------------------------------------------------------
// Buffer clear color
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ClearColor3ub(unsigned char r, unsigned char g, unsigned char b)
{
LOCK_SHADERAPI();
float a = 255;//(r * 0.30f + g * 0.59f + b * 0.11f) / MAX_HDR_OVERBRIGHT;
// GR - need to force alpha to black for HDR
m_DynamicState.m_ClearColor = D3DCOLOR_ARGB((unsigned char)a, r, g, b);
}
void CShaderAPIDx8::ClearColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
LOCK_SHADERAPI();
m_DynamicState.m_ClearColor = D3DCOLOR_ARGB(a, r, g, b);
}
// Converts the clear color to be appropriate for HDR
D3DCOLOR CShaderAPIDx8::GetActualClearColor(D3DCOLOR clearColor)
{
bool bConvert = !IsX360() && m_TransitionTable.CurrentState().m_bLinearColorSpaceFrameBufferEnable;
#if defined( _X360 )
// The PC disables SRGBWrite when clearing so that the clear color won't get gamma converted
// The 360 cannot disable that state, and thus compensates for the sRGB conversion
// the desired result is the clear color written to the RT as-is
if (clearColor & D3DCOLOR_ARGB(0, 255, 255, 255))
{
IDirect3DSurface* pRTSurface = NULL;
Dx9Device()->GetRenderTarget(0, &pRTSurface);
if (pRTSurface)
{
D3DSURFACE_DESC desc;
HRESULT hr = pRTSurface->GetDesc(&desc);
if (!FAILED(hr) && IS_D3DFORMAT_SRGB(desc.Format))
{
bConvert = true;
}
pRTSurface->Release();
}
}
#endif
if (bConvert)
{
// HDRFIXME: need to make sure this works this way.
// HDRFIXME: Is there a helper function that'll do this easier?
// convert clearColor from gamma to linear since our frame buffer is linear.
Vector vecGammaColor;
vecGammaColor.x = (1.0f / 255.0f) * ((clearColor >> 16) & 0xff);
vecGammaColor.y = (1.0f / 255.0f) * ((clearColor >> 8) & 0xff);
vecGammaColor.z = (1.0f / 255.0f) * (clearColor & 0xff);
Vector vecLinearColor;
vecLinearColor.x = GammaToLinear(vecGammaColor.x);
vecLinearColor.y = GammaToLinear(vecGammaColor.y);
vecLinearColor.z = GammaToLinear(vecGammaColor.z);
clearColor &= D3DCOLOR_RGBA(0, 0, 0, 255);
clearColor |= D3DCOLOR_COLORVALUE(vecLinearColor.x, vecLinearColor.y, vecLinearColor.z, 0.0f);
}
return clearColor;
}
//-----------------------------------------------------------------------------
// Clear buffers while obeying stencil
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ClearBuffersObeyStencil(bool bClearColor, bool bClearDepth)
{
//copy the clear color bool into the clear alpha bool
ClearBuffersObeyStencilEx(bClearColor, bClearColor, bClearDepth);
}
void CShaderAPIDx8::ClearBuffersObeyStencilEx(bool bClearColor, bool bClearAlpha, bool bClearDepth)
{
LOCK_SHADERAPI();
if (!bClearColor && !bClearAlpha && !bClearDepth)
return;
FlushBufferedPrimitives();
// Before clearing can happen, user clip planes must be disabled
SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
D3DCOLOR clearColor = GetActualClearColor(m_DynamicState.m_ClearColor);
unsigned char r, g, b, a;
b = clearColor & 0xFF;
g = (clearColor >> 8) & 0xFF;
r = (clearColor >> 16) & 0xFF;
a = (clearColor >> 24) & 0xFF;
ShaderUtil()->DrawClearBufferQuad(r, g, b, a, bClearColor, bClearAlpha, bClearDepth);
// Reset user clip plane state
FlushBufferedPrimitives();
SetRenderState(D3DRS_CLIPPLANEENABLE, m_DynamicState.m_UserClipPlaneEnabled);
}
//-------------------------------------------------------------------------
//Perform stencil operations to every pixel on the screen
//-------------------------------------------------------------------------
void CShaderAPIDx8::PerformFullScreenStencilOperation(void)
{
LOCK_SHADERAPI();
FlushBufferedPrimitives();
// We'll be drawing a large quad in altered worldspace, user clip planes must be disabled
SetRenderStateConstMacro(this, D3DRS_CLIPPLANEENABLE, 0);
ShaderUtil()->DrawClearBufferQuad(0, 0, 0, 0, false, false, false);
// Reset user clip plane state
FlushBufferedPrimitives();
SetRenderStateConstMacro(this, D3DRS_CLIPPLANEENABLE, m_DynamicState.m_UserClipPlaneEnabled);
}
//-----------------------------------------------------------------------------
// Buffer clear
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ClearBuffers(bool bClearColor, bool bClearDepth, bool bClearStencil, int renderTargetWidth, int renderTargetHeight)
{
LOCK_SHADERAPI();
if (ShaderUtil()->GetConfig().m_bSuppressRendering)
return;
if (IsDeactivated())
return;
// State changed... need to flush the dynamic buffer
FlushBufferedPrimitives();
CallCommitFuncs(COMMIT_PER_DRAW, true);
float depth = (ShaderUtil()->GetConfig().bReverseDepth ^ ReverseDepthOnX360()) ? 0.0f : 1.0f;
DWORD mask = 0;
if (bClearColor)
{
mask |= D3DCLEAR_TARGET;
}
if (bClearDepth)
{
mask |= D3DCLEAR_ZBUFFER;
}
if (bClearStencil && m_bUsingStencil)
{
mask |= D3DCLEAR_STENCIL;
}
// Only clear the current view... right!??!
D3DRECT clear;
clear.x1 = m_DesiredState.m_Viewport.X;
clear.y1 = m_DesiredState.m_Viewport.Y;
clear.x2 = clear.x1 + m_DesiredState.m_Viewport.Width;
clear.y2 = clear.y1 + m_DesiredState.m_Viewport.Height;
// SRGBWrite is disabled when clearing so that the clear color won't get gamma converted
bool bSRGBWriteEnable = false;
if (!IsX360() && bClearColor && m_TransitionTable.CurrentShadowState())
{
bSRGBWriteEnable = m_TransitionTable.CurrentShadowState()->m_SRGBWriteEnable;
}
#if !defined( _X360 )
if (bSRGBWriteEnable)
{
// This path used to be !IsPosix(), but this makes no sense and causes the clear color to differ in D3D9 vs. GL.
Dx9Device()->SetRenderState(D3DRS_SRGBWRITEENABLE, 0);
}
#endif
D3DCOLOR clearColor = GetActualClearColor(m_DynamicState.m_ClearColor);
if (mask != 0)
{
bool bRenderTargetMatchesViewport =
(renderTargetWidth == -1 && renderTargetHeight == -1) ||
(m_DesiredState.m_Viewport.Width == -1 && m_DesiredState.m_Viewport.Height == -1) ||
(renderTargetWidth == (int)m_DesiredState.m_Viewport.Width &&
renderTargetHeight == (int)m_DesiredState.m_Viewport.Height);
if (bRenderTargetMatchesViewport)
{
RECORD_COMMAND(DX8_CLEAR, 6);
RECORD_INT(0);
RECORD_STRUCT(&clear, sizeof(clear));
RECORD_INT(mask);
RECORD_INT(clearColor);
RECORD_FLOAT(depth);
RECORD_INT(0);
Dx9Device()->Clear(0, NULL, mask, clearColor, depth, 0L);
}
else
{
RECORD_COMMAND(DX8_CLEAR, 6);
RECORD_INT(0);
RECORD_STRUCT(&clear, sizeof(clear));
RECORD_INT(mask);
RECORD_INT(clearColor);
RECORD_FLOAT(depth);
RECORD_INT(0);
Dx9Device()->Clear(1, &clear, mask, clearColor, depth, 0L);
}
}
// Restore state
if (bSRGBWriteEnable)
{
// sRGBWriteEnable shouldn't be true if we have no shadow state. . . Assert just in case.
Assert(m_TransitionTable.CurrentShadowState());
m_TransitionTable.ApplySRGBWriteEnable(*m_TransitionTable.CurrentShadowState());
}
}
//-----------------------------------------------------------------------------
// Bind
//-----------------------------------------------------------------------------
void CShaderAPIDx8::BindVertexShader(VertexShaderHandle_t hVertexShader)
{
ShaderManager()->BindVertexShader(hVertexShader);
}
void CShaderAPIDx8::BindGeometryShader(GeometryShaderHandle_t hGeometryShader)
{
Assert(hGeometryShader == GEOMETRY_SHADER_HANDLE_INVALID);
}
void CShaderAPIDx8::BindPixelShader(PixelShaderHandle_t hPixelShader)
{
ShaderManager()->BindPixelShader(hPixelShader);
}
//-----------------------------------------------------------------------------
// Returns a copy of the front buffer
//-----------------------------------------------------------------------------
IDirect3DSurface* CShaderAPIDx8::GetFrontBufferImage(ImageFormat& format)
{
#if !defined( _X360 )
// need to flush the dynamic buffer and make sure the entire image is there
FlushBufferedPrimitives();
int w, h;
GetBackBufferDimensions(w, h);
HRESULT hr;
IDirect3DSurface* pFullScreenSurfaceBits = 0;
hr = Dx9Device()->CreateOffscreenPlainSurface(w, h,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pFullScreenSurfaceBits, NULL);
if (FAILED(hr))
return 0;
hr = Dx9Device()->GetFrontBufferData(0, pFullScreenSurfaceBits);
if (FAILED(hr))
return 0;
int windowWidth, windowHeight;
GetWindowSize(windowWidth, windowHeight);
IDirect3DSurface* pSurfaceBits = 0;
hr = Dx9Device()->CreateOffscreenPlainSurface(windowWidth, windowHeight,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurfaceBits, NULL);
Assert(hr == D3D_OK);
POINT pnt;
pnt.x = pnt.y = 0;
#ifdef _WIN32
BOOL result = ClientToScreen((HWND)m_hWnd, &pnt);
#else
BOOL result = ClientToScreen((VD3DHWND)m_hWnd, &pnt);
#endif
Assert(result);
RECT srcRect;
srcRect.left = pnt.x;
srcRect.top = pnt.y;
srcRect.right = pnt.x + windowWidth;
srcRect.bottom = pnt.y + windowHeight;
POINT dstPnt;
dstPnt.x = dstPnt.y = 0;
D3DLOCKED_RECT lockedSrcRect;
hr = pFullScreenSurfaceBits->LockRect(&lockedSrcRect, &srcRect, D3DLOCK_READONLY);
Assert(hr == D3D_OK);
D3DLOCKED_RECT lockedDstRect;
hr = pSurfaceBits->LockRect(&lockedDstRect, NULL, 0);
Assert(hr == D3D_OK);
int i;
for (i = 0; i < windowHeight; i++)
{
memcpy((unsigned char*)lockedDstRect.pBits + (i * lockedDstRect.Pitch),
(unsigned char*)lockedSrcRect.pBits + (i * lockedSrcRect.Pitch),
windowWidth * 4); // hack . . what if this is a different format?
}
hr = pSurfaceBits->UnlockRect();
Assert(hr == D3D_OK);
hr = pFullScreenSurfaceBits->UnlockRect();
Assert(hr == D3D_OK);
pFullScreenSurfaceBits->Release();
format = ImageLoader::D3DFormatToImageFormat(D3DFMT_A8R8G8B8);
return pSurfaceBits;
#else
Assert(0);
return NULL;
#endif
}
//-----------------------------------------------------------------------------
// Lets the shader know about the full-screen texture so it can
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetFullScreenTextureHandle(ShaderAPITextureHandle_t h)
{
LOCK_SHADERAPI();
m_hFullScreenTexture = h;
}
//-----------------------------------------------------------------------------
// Lets the shader know about the full-screen texture so it can
//-----------------------------------------------------------------------------
void CShaderAPIDx8::SetLinearToGammaConversionTextures(ShaderAPITextureHandle_t hSRGBWriteEnabledTexture, ShaderAPITextureHandle_t hIdentityTexture)
{
LOCK_SHADERAPI();
m_hLinearToGammaTableTexture = hSRGBWriteEnabledTexture;
m_hLinearToGammaTableIdentityTexture = hIdentityTexture;
}
//-----------------------------------------------------------------------------
// Returns a copy of the back buffer
//-----------------------------------------------------------------------------
IDirect3DSurface* CShaderAPIDx8::GetBackBufferImageHDR(Rect_t* pSrcRect, Rect_t* pDstRect, ImageFormat& format)
{
#if !defined( _X360 )
HRESULT hr;
IDirect3DSurface* pSurfaceBits = 0;
IDirect3DSurface* pTmpSurface = NULL;
// Get the back buffer
IDirect3DSurface* pBackBuffer;
hr = Dx9Device()->GetRenderTarget(0, &pBackBuffer);
if (FAILED(hr))
return 0;
// Find about its size and format
D3DSURFACE_DESC desc;
D3DTEXTUREFILTERTYPE filter;
hr = pBackBuffer->GetDesc(&desc);
if (FAILED(hr))
goto CleanUp;
filter = ((pDstRect->width != pSrcRect->width) || (pDstRect->height != pSrcRect->height)) ? D3DTEXF_LINEAR : D3DTEXF_NONE;
if ((pDstRect->x + pDstRect->width <= SMALL_BACK_BUFFER_SURFACE_WIDTH) &&
(pDstRect->y + pDstRect->height <= SMALL_BACK_BUFFER_SURFACE_HEIGHT))
{
if (!m_pSmallBackBufferFP16TempSurface)
{
hr = Dx9Device()->CreateRenderTarget(
SMALL_BACK_BUFFER_SURFACE_WIDTH, SMALL_BACK_BUFFER_SURFACE_HEIGHT,
desc.Format, D3DMULTISAMPLE_NONE, 0, TRUE, &m_pSmallBackBufferFP16TempSurface,
NULL);
}
pTmpSurface = m_pSmallBackBufferFP16TempSurface;
#if POSIX
pTmpSurface->AddRef(0, "CShaderAPIDx8::GetBackBufferImageHDR public addref");
#else
pTmpSurface->AddRef();
#endif
desc.Width = SMALL_BACK_BUFFER_SURFACE_WIDTH;
desc.Height = SMALL_BACK_BUFFER_SURFACE_HEIGHT;
RECT srcRect, destRect;
RectToRECT(pSrcRect, srcRect);
RectToRECT(pDstRect, destRect);
hr = Dx9Device()->StretchRect(pBackBuffer, &srcRect, pTmpSurface, &destRect, filter);
if (FAILED(hr))
goto CleanUp;
}
else
{
// Normally we would only have to create a separate render target here and StretchBlt to it first
// if AA was enabled, but certain machines/drivers get reboots if we do GetRenderTargetData
// straight off the backbuffer.
hr = Dx9Device()->CreateRenderTarget(desc.Width, desc.Height, desc.Format,
D3DMULTISAMPLE_NONE, 0, TRUE, &pTmpSurface, NULL);
if (FAILED(hr))
goto CleanUp;
hr = Dx9Device()->StretchRect(pBackBuffer, NULL, pTmpSurface, NULL, filter);
if (FAILED(hr))
goto CleanUp;
}
// Create a buffer the same size and format
hr = Dx9Device()->CreateOffscreenPlainSurface(desc.Width, desc.Height,
desc.Format, D3DPOOL_SYSTEMMEM, &pSurfaceBits, NULL);
if (FAILED(hr))
goto CleanUp;
// Blit from the back buffer to our scratch buffer
hr = Dx9Device()->GetRenderTargetData(pTmpSurface ? pTmpSurface : pBackBuffer, pSurfaceBits);
if (FAILED(hr))
goto CleanUp2;
format = ImageLoader::D3DFormatToImageFormat(desc.Format);
if (pTmpSurface)
{
pTmpSurface->Release();
}
pBackBuffer->Release();
return pSurfaceBits;
CleanUp2:
pSurfaceBits->Release();
CleanUp:
if (pTmpSurface)
{
pTmpSurface->Release();
}
pBackBuffer->Release();
#else
Assert(0);
#endif
return 0;
}
//-----------------------------------------------------------------------------
// Returns a copy of the back buffer
//-----------------------------------------------------------------------------
IDirect3DSurface* CShaderAPIDx8::GetBackBufferImage(Rect_t* pSrcRect, Rect_t* pDstRect, ImageFormat& format)
{
#if !defined( _X360 )
if (!m_pBackBufferSurface || (m_hFullScreenTexture == INVALID_SHADERAPI_TEXTURE_HANDLE))
return NULL;
HRESULT hr;
D3DSURFACE_DESC desc;
FlushBufferedPrimitives();
// Get the current render target
IDirect3DSurface* pRenderTarget;
hr = Dx9Device()->GetRenderTarget(0, &pRenderTarget);
if (FAILED(hr))
return 0;
// Find about its size and format
hr = pRenderTarget->GetDesc(&desc);
if (desc.Format == D3DFMT_A16B16G16R16F || desc.Format == D3DFMT_A32B32G32R32F)
return GetBackBufferImageHDR(pSrcRect, pDstRect, format);
IDirect3DSurface* pSurfaceBits = NULL;
IDirect3DSurface* pTmpSurface = NULL;
int nRenderTargetRefCount;
nRenderTargetRefCount = 0;
if ((desc.MultiSampleType == D3DMULTISAMPLE_NONE) && (pRenderTarget != m_pBackBufferSurface) &&
(pSrcRect->width == pDstRect->width) && (pSrcRect->height == pDstRect->height))
{
// Don't bother to blit through the full-screen texture if we don't
// have to stretch, we're not coming from the backbuffer, and we don't have to do AA resolve
pTmpSurface = pRenderTarget;
#if POSIX
pTmpSurface->AddRef(0, "CShaderAPIDx8::GetBackBufferImage public addref");
#else
pTmpSurface->AddRef();
#endif
}
else
{
Texture_t* pTex = &GetTexture(m_hFullScreenTexture);
IDirect3DTexture* pFullScreenTexture = (IDirect3DTexture*)pTex->GetTexture();
D3DTEXTUREFILTERTYPE filter = ((pDstRect->width != pSrcRect->width) || (pDstRect->height != pSrcRect->height)) ? D3DTEXF_LINEAR : D3DTEXF_NONE;
hr = pFullScreenTexture->GetSurfaceLevel(0, &pTmpSurface);
if (FAILED(hr))
goto CleanUp;
if (pTmpSurface == pRenderTarget)
{
Warning("Can't blit from full-sized offscreen buffer!\n");
goto CleanUp;
}
RECT srcRect, destRect;
srcRect.left = pSrcRect->x; srcRect.right = pSrcRect->x + pSrcRect->width;
srcRect.top = pSrcRect->y; srcRect.bottom = pSrcRect->y + pSrcRect->height;
srcRect.left = clamp(srcRect.left, 0, (int)desc.Width);
srcRect.right = clamp(srcRect.right, 0, (int)desc.Width);
srcRect.top = clamp(srcRect.top, 0, (int)desc.Height);
srcRect.bottom = clamp(srcRect.bottom, 0, (int)desc.Height);
destRect.left = pDstRect->x; destRect.right = pDstRect->x + pDstRect->width;
destRect.top = pDstRect->y; destRect.bottom = pDstRect->y + pDstRect->height;
destRect.left = clamp(destRect.left, 0, (int)desc.Width);
destRect.right = clamp(destRect.right, 0, (int)desc.Width);
destRect.top = clamp(destRect.top, 0, (int)desc.Height);
destRect.bottom = clamp(destRect.bottom, 0, (int)desc.Height);
hr = Dx9Device()->StretchRect(pRenderTarget, &srcRect, pTmpSurface, &destRect, filter);
if (FAILED(hr))
{
AssertOnce("Error resizing pixels!\n");
goto CleanUp;
}
}
D3DSURFACE_DESC tmpDesc;
hr = pTmpSurface->GetDesc(&tmpDesc);
Assert(!FAILED(hr));
// Create a buffer the same size and format
hr = Dx9Device()->CreateOffscreenPlainSurface(tmpDesc.Width, tmpDesc.Height,
desc.Format, D3DPOOL_SYSTEMMEM, &pSurfaceBits, NULL);
if (FAILED(hr))
{
AssertOnce("Error creating offscreen surface!\n");
goto CleanUp;
}
// Blit from the back buffer to our scratch buffer
hr = Dx9Device()->GetRenderTargetData(pTmpSurface, pSurfaceBits);
if (FAILED(hr))
{
AssertOnce("Error copying bits into the offscreen surface!\n");
goto CleanUp;
}
format = ImageLoader::D3DFormatToImageFormat(desc.Format);
pTmpSurface->Release();
#ifdef _DEBUG
nRenderTargetRefCount =
#endif
pRenderTarget->Release();
AssertOnce(nRenderTargetRefCount == 1);
return pSurfaceBits;
CleanUp:
if (pSurfaceBits)
{
pSurfaceBits->Release();
}
if (pTmpSurface)
{
pTmpSurface->Release();
}
#else
Assert(0);
#endif
return 0;
}
//-----------------------------------------------------------------------------
// Copy bits from a host-memory surface
//-----------------------------------------------------------------------------
void CShaderAPIDx8::CopyBitsFromHostSurface(IDirect3DSurface* pSurfaceBits,
const Rect_t& dstRect, unsigned char* pData, ImageFormat srcFormat, ImageFormat dstFormat, int nDstStride)
{
// Copy out the bits...
RECT rect;
rect.left = dstRect.x;
rect.right = dstRect.x + dstRect.width;
rect.top = dstRect.y;
rect.bottom = dstRect.y + dstRect.height;
D3DLOCKED_RECT lockedRect;
HRESULT hr;
int flags = D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK;
tmZone(TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__);
hr = pSurfaceBits->LockRect(&lockedRect, &rect, flags);
if (!FAILED(hr))
{
unsigned char* pImage = (unsigned char*)lockedRect.pBits;
ShaderUtil()->ConvertImageFormat((unsigned char*)pImage, srcFormat,
pData, dstFormat, dstRect.width, dstRect.height, lockedRect.Pitch, nDstStride);
hr = pSurfaceBits->UnlockRect();
}
}
//-----------------------------------------------------------------------------
// Reads from the current read buffer + stretches
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ReadPixels(Rect_t* pSrcRect, Rect_t* pDstRect, unsigned char* pData, ImageFormat dstFormat, int nDstStride)
{
LOCK_SHADERAPI();
Assert(pDstRect);
if (IsPC() || !IsX360())
{
Rect_t srcRect;
if (!pSrcRect)
{
srcRect.x = srcRect.y = 0;
srcRect.width = m_nWindowWidth;
srcRect.height = m_nWindowHeight;
pSrcRect = &srcRect;
}
ImageFormat format;
IDirect3DSurface* pSurfaceBits = GetBackBufferImage(pSrcRect, pDstRect, format);
if (pSurfaceBits)
{
CopyBitsFromHostSurface(pSurfaceBits, *pDstRect, pData, format, dstFormat, nDstStride);
// Release the temporary surface
pSurfaceBits->Release();
}
}
else
{
#if defined( _X360 )
// 360 requires material system to handle due to RT complexities
ShaderUtil()->ReadBackBuffer(pSrcRect, pDstRect, pData, dstFormat, nDstStride);
#endif
}
}
//-----------------------------------------------------------------------------
// Reads from the current read buffer
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ReadPixels(int x, int y, int width, int height, unsigned char* pData, ImageFormat dstFormat)
{
Rect_t rect;
rect.x = x;
rect.y = y;
rect.width = width;
rect.height = height;
if (IsPC() || !IsX360())
{
ImageFormat format;
IDirect3DSurface* pSurfaceBits = GetBackBufferImage(&rect, &rect, format);
if (pSurfaceBits)
{
CopyBitsFromHostSurface(pSurfaceBits, rect, pData, format, dstFormat, 0);
// Release the temporary surface
pSurfaceBits->Release();
}
}
else
{
#if defined( _X360 )
// 360 requires material system to handle due to RT complexities
ShaderUtil()->ReadBackBuffer(&rect, &rect, pData, dstFormat, 0);
#endif
}
}
//-----------------------------------------------------------------------------
// Binds a particular material to render with
//-----------------------------------------------------------------------------
void CShaderAPIDx8::Bind(IMaterial* pMaterial)
{
LOCK_SHADERAPI();
IMaterialInternal* pMatInt = static_cast<IMaterialInternal*>(pMaterial);
bool bMaterialChanged;
if (m_pMaterial && pMatInt && m_pMaterial->InMaterialPage() && pMatInt->InMaterialPage())
{
bMaterialChanged = (m_pMaterial->GetMaterialPage() != pMatInt->GetMaterialPage());
}
else
{
bMaterialChanged = (m_pMaterial != pMatInt) || (m_pMaterial && m_pMaterial->InMaterialPage()) || (pMatInt && pMatInt->InMaterialPage());
}
if (bMaterialChanged)
{
FlushBufferedPrimitives();
#ifdef RECORDING
RECORD_DEBUG_STRING((char*)pMaterial->GetName());
IShader* pShader = pMatInt->GetShader();
if (pShader && pShader->GetName())
{
RECORD_DEBUG_STRING(pShader->GetName());
}
else
{
RECORD_DEBUG_STRING("<NULL SHADER>");
}
#endif
m_pMaterial = pMatInt;
#if ( defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) )
PIXifyName(s_pPIXMaterialName, sizeof(s_pPIXMaterialName), m_pMaterial->GetName());
#endif
}
}
// Get the currently bound material
IMaterialInternal* CShaderAPIDx8::GetBoundMaterial()
{
return m_pMaterial;
}
//-----------------------------------------------------------------------------
// Binds a standard texture
//-----------------------------------------------------------------------------
void CShaderAPIDx8::BindStandardTexture(Sampler_t sampler, StandardTextureId_t id)
{
if (m_StdTextureHandles[id] != INVALID_SHADERAPI_TEXTURE_HANDLE)
{
BindTexture(sampler, m_StdTextureHandles[id]);
}
else
{
ShaderUtil()->BindStandardTexture(sampler, id);
}
}
void CShaderAPIDx8::BindStandardVertexTexture(VertexTextureSampler_t sampler, StandardTextureId_t id)
{
ShaderUtil()->BindStandardVertexTexture(sampler, id);
}
void CShaderAPIDx8::GetStandardTextureDimensions(int* pWidth, int* pHeight, StandardTextureId_t id)
{
ShaderUtil()->GetStandardTextureDimensions(pWidth, pHeight, id);
}
//-----------------------------------------------------------------------------
// Gets the lightmap dimensions
//-----------------------------------------------------------------------------
void CShaderAPIDx8::GetLightmapDimensions(int* w, int* h)
{
ShaderUtil()->GetLightmapDimensions(w, h);
}
//-----------------------------------------------------------------------------
// Selection mode methods
//-----------------------------------------------------------------------------
int CShaderAPIDx8::SelectionMode(bool selectionMode)
{
LOCK_SHADERAPI();
int numHits = m_NumHits;
if (m_InSelectionMode)
{
WriteHitRecord();
}
m_InSelectionMode = selectionMode;
m_pCurrSelectionRecord = m_pSelectionBuffer;
m_NumHits = 0;
return numHits;
}
bool CShaderAPIDx8::IsInSelectionMode() const
{
return m_InSelectionMode;
}
void CShaderAPIDx8::SelectionBuffer(unsigned int* pBuffer, int size)
{
LOCK_SHADERAPI();
Assert(!m_InSelectionMode);
Assert(pBuffer && size);
m_pSelectionBufferEnd = pBuffer + size;
m_pSelectionBuffer = pBuffer;
m_pCurrSelectionRecord = pBuffer;
}
void CShaderAPIDx8::ClearSelectionNames()
{
LOCK_SHADERAPI();
if (m_InSelectionMode)
{
WriteHitRecord();
}
m_SelectionNames.Clear();
}
void CShaderAPIDx8::LoadSelectionName(int name)
{
LOCK_SHADERAPI();
if (m_InSelectionMode)
{
WriteHitRecord();
Assert(m_SelectionNames.Count() > 0);
m_SelectionNames.Top() = name;
}
}
void CShaderAPIDx8::PushSelectionName(int name)
{
LOCK_SHADERAPI();
if (m_InSelectionMode)
{
WriteHitRecord();
m_SelectionNames.Push(name);
}
}
void CShaderAPIDx8::PopSelectionName()
{
LOCK_SHADERAPI();
if (m_InSelectionMode)
{
WriteHitRecord();
m_SelectionNames.Pop();
}
}
void CShaderAPIDx8::WriteHitRecord()
{
FlushBufferedPrimitives();
if (m_SelectionNames.Count() && (m_SelectionMinZ != FLT_MAX))
{
Assert(m_pCurrSelectionRecord + m_SelectionNames.Count() + 3 < m_pSelectionBufferEnd);
*m_pCurrSelectionRecord++ = m_SelectionNames.Count();
// NOTE: because of rounding, "(uint32)(float)UINT32_MAX" yields zero(!), hence the use of doubles.
// [ ALSO: As of Nov 2011, VS2010 exhibits a debug build code-gen bug if we cast the result to int32 instead of uint32 ]
*m_pCurrSelectionRecord++ = (uint32)(0.5 + m_SelectionMinZ * (double)((uint32)~0));
*m_pCurrSelectionRecord++ = (uint32)(0.5 + m_SelectionMaxZ * (double)((uint32)~0));
for (int i = 0; i < m_SelectionNames.Count(); ++i)
{
*m_pCurrSelectionRecord++ = m_SelectionNames[i];
}
++m_NumHits;
}
m_SelectionMinZ = FLT_MAX;
m_SelectionMaxZ = FLT_MIN;
}
// We hit somefin in selection mode
void CShaderAPIDx8::RegisterSelectionHit(float minz, float maxz)
{
if (minz < 0)
minz = 0;
if (maxz > 1)
maxz = 1;
if (m_SelectionMinZ > minz)
m_SelectionMinZ = minz;
if (m_SelectionMaxZ < maxz)
m_SelectionMaxZ = maxz;
}
int CShaderAPIDx8::GetCurrentNumBones(void) const
{
return m_DynamicState.m_NumBones;
}
bool CShaderAPIDx8::IsHWMorphingEnabled() const
{
return m_DynamicState.m_bHWMorphingEnabled;
}
//-----------------------------------------------------------------------------
// Inserts the lighting block into the code
//-----------------------------------------------------------------------------
// If you change the number of lighting combinations, change this enum
enum
{
DX8_LIGHTING_COMBINATION_COUNT = 22,
DX9_LIGHTING_COMBINATION_COUNT = 35
};
#define MAX_LIGHTS 4
// NOTE: These should match g_lightType* in vsh_prep.pl!
static int g_DX8LightCombinations[][4] =
{
// static ambient local1 local2
// This is a special case for no lighting at all.
{ LIGHT_NONE, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE },
// This is a special case so that we don't have to do the ambient cube
// when we only have static lighting
{ LIGHT_STATIC, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_NONE },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_NONE },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_SPOT },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_POINT, },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_DIRECTIONAL, },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_POINT, },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_DIRECTIONAL, },
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_NONE },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_NONE },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_SPOT },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_POINT, },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_DIRECTIONAL, },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_POINT, },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_DIRECTIONAL, },
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, }
};
// NOTE: These should match g_lightType* in vsh_prep.pl!
// They also correspond to the parallel g_LocalLightTypeXArray[] arrays in common_vs_fxc.h
static int g_DX9LightCombinations[][MAX_LIGHTS] =
{
// local0 local1 local2 local3
{ LIGHT_NONE, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE }, // Zero lights [ Combo 0]
{ LIGHT_SPOT, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE }, // One light [ Combo 1]
{ LIGHT_POINT, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_NONE, LIGHT_NONE }, // Two lights [ Combo 4]
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_POINT, LIGHT_POINT, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_NONE }, // Three lights [ Combo 10]
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_POINT, LIGHT_POINT, LIGHT_POINT, LIGHT_NONE },
{ LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT }, // Four lights [ Combo 20]
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_DIRECTIONAL },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT, LIGHT_DIRECTIONAL },
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT, LIGHT_POINT },
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL },
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
{ LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
{ LIGHT_POINT, LIGHT_POINT, LIGHT_POINT, LIGHT_POINT },
{ LIGHT_POINT, LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL },
{ LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
{ LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
{ LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL }
};
// This is just for getting light combos for DX8
// For DX9, use GetDX9LightState()
// It is up to the shader cpp files to use the right method
int CShaderAPIDx8::GetCurrentLightCombo(void) const
{
Assert(g_pHardwareConfig->Caps().m_nDXSupportLevel <= 81);
Assert(m_DynamicState.m_NumLights <= 2);
COMPILE_TIME_ASSERT(DX8_LIGHTING_COMBINATION_COUNT ==
sizeof(g_DX8LightCombinations) / sizeof(g_DX8LightCombinations[0]));
// hack . . do this a cheaper way.
bool bUseAmbientCube;
if (m_DynamicState.m_AmbientLightCube[0][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[0][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[0][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[1][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[1][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[1][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[2][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[2][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[2][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[3][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[3][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[3][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[4][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[4][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[4][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[5][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[5][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[5][2] == 0.0f)
{
bUseAmbientCube = false;
}
else
{
bUseAmbientCube = true;
}
Assert(m_pRenderMesh);
const VertexShaderLightTypes_t* pLightType = m_DynamicState.m_LightType;
if (m_DynamicState.m_NumLights == 0 && !bUseAmbientCube)
{
if (m_pRenderMesh->HasColorMesh())
return 1; // special case for static lighting only
else
return 0; // special case for no lighting at all.
}
int i;
// hack - skip the first two for now since we don't know if the ambient cube is needed or not.
for (i = 2; i < DX9_LIGHTING_COMBINATION_COUNT; ++i)
{
int j;
for (j = 0; j < m_DynamicState.m_NumLights; ++j)
{
if (pLightType[j] != g_DX8LightCombinations[i][j + 2])
break;
}
if (j == m_DynamicState.m_NumLights)
{
while (j < 2)
{
if (g_DX8LightCombinations[i][j + 2] != LIGHT_NONE)
break;
++j;
}
if (j == 2)
{
if (m_pRenderMesh->HasColorMesh())
{
return i + 10;
}
else
{
return i;
}
}
}
}
// should never get here!
Assert(0);
return 0;
}
void CShaderAPIDx8::GetDX9LightState(LightState_t* state) const
{
// hack . . do this a cheaper way.
if (m_DynamicState.m_AmbientLightCube[0][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[0][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[0][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[1][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[1][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[1][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[2][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[2][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[2][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[3][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[3][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[3][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[4][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[4][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[4][2] == 0.0f &&
m_DynamicState.m_AmbientLightCube[5][0] == 0.0f &&
m_DynamicState.m_AmbientLightCube[5][1] == 0.0f &&
m_DynamicState.m_AmbientLightCube[5][2] == 0.0f)
{
state->m_bAmbientLight = false;
}
else
{
state->m_bAmbientLight = true;
}
Assert(m_pRenderMesh);
Assert(m_DynamicState.m_NumLights <= 4);
if (g_pHardwareConfig->SupportsPixelShaders_2_b())
{
Assert(m_DynamicState.m_NumLights <= MAX_LIGHTS); // 2b hardware gets four lights
}
else
{
Assert(m_DynamicState.m_NumLights <= (MAX_LIGHTS - 2)); // 2.0 hardware gets two less
}
#ifdef OSX
state->m_nNumLights = MIN(MAX_NUM_LIGHTS, m_DynamicState.m_NumLights);
#else
state->m_nNumLights = m_DynamicState.m_NumLights;
#endif
state->m_nNumLights = m_DynamicState.m_NumLights;
state->m_bStaticLightVertex = m_pRenderMesh->HasColorMesh();
state->m_bStaticLightTexel = false; // For now
}
MaterialFogMode_t CShaderAPIDx8::GetCurrentFogType(void) const
{
return m_DynamicState.m_SceneFog;
}
void CShaderAPIDx8::RecordString(const char* pStr)
{
RECORD_STRING(pStr);
}
void CShaderAPIDx8::EvictManagedResourcesInternal()
{
if (IsX360())
return;
if (!ThreadOwnsDevice() || !ThreadInMainThread())
{
ShaderUtil()->OnThreadEvent(SHADER_THREAD_EVICT_RESOURCES);
return;
}
if (mat_debugalttab.GetBool())
{
Warning("mat_debugalttab: CShaderAPIDx8::EvictManagedResourcesInternal\n");
}
#if !defined( _X360 )
if (Dx9Device())
{
Dx9Device()->EvictManagedResources();
}
#endif
}
void CShaderAPIDx8::EvictManagedResources(void)
{
if (IsX360())
{
return;
}
LOCK_SHADERAPI();
Assert(ThreadOwnsDevice());
// Tell other material system applications to release resources
SendIPCMessage(EVICT_MESSAGE);
EvictManagedResourcesInternal();
}
bool CShaderAPIDx8::IsDebugTextureListFresh(int numFramesAllowed /* = 1 */)
{
return (m_nDebugDataExportFrame <= m_CurrentFrame) && (m_nDebugDataExportFrame >= m_CurrentFrame - numFramesAllowed);
}
bool CShaderAPIDx8::SetDebugTextureRendering(bool bEnable)
{
bool bVal = m_bDebugTexturesRendering;
m_bDebugTexturesRendering = bEnable;
return bVal;
}
void CShaderAPIDx8::EnableDebugTextureList(bool bEnable)
{
m_bEnableDebugTextureList = bEnable;
}
void CShaderAPIDx8::EnableGetAllTextures(bool bEnable)
{
m_bDebugGetAllTextures = bEnable;
}
KeyValues* CShaderAPIDx8::GetDebugTextureList()
{
return m_pDebugTextureList;
}
int CShaderAPIDx8::GetTextureMemoryUsed(TextureMemoryType eTextureMemory)
{
switch (eTextureMemory)
{
case MEMORY_BOUND_LAST_FRAME:
return m_nTextureMemoryUsedLastFrame;
case MEMORY_TOTAL_LOADED:
return m_nTextureMemoryUsedTotal;
case MEMORY_ESTIMATE_PICMIP_1:
return m_nTextureMemoryUsedPicMip1;
case MEMORY_ESTIMATE_PICMIP_2:
return m_nTextureMemoryUsedPicMip2;
default:
return 0;
}
}
// Allocate and delete query objects.
ShaderAPIOcclusionQuery_t CShaderAPIDx8::CreateOcclusionQueryObject(void)
{
// don't allow this on <80 because it falls back to wireframe in that case
if (m_DeviceSupportsCreateQuery == 0 || g_pHardwareConfig->Caps().m_nDXSupportLevel < 80)
return INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE;
// While we're deactivated, m_OcclusionQueryObjects just holds NULL pointers.
// Create a dummy one here and let ReacquireResources create the actual D3D object.
if (IsDeactivated())
return INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE;
IDirect3DQuery9* pQuery = NULL;
HRESULT hr = Dx9Device()->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pQuery);
return (hr == D3D_OK) ? (ShaderAPIOcclusionQuery_t)pQuery : INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE;
}
void CShaderAPIDx8::DestroyOcclusionQueryObject(ShaderAPIOcclusionQuery_t handle)
{
IDirect3DQuery9* pQuery = (IDirect3DQuery9*)handle;
int nRetVal = pQuery->Release();
Assert(nRetVal == 0);
}
// Bracket drawing with begin and end so that we can get counts next frame.
void CShaderAPIDx8::BeginOcclusionQueryDrawing(ShaderAPIOcclusionQuery_t handle)
{
IDirect3DQuery9* pQuery = (IDirect3DQuery9*)handle;
HRESULT hResult = pQuery->Issue(D3DISSUE_BEGIN);
Assert(hResult == D3D_OK);
}
void CShaderAPIDx8::EndOcclusionQueryDrawing(ShaderAPIOcclusionQuery_t handle)
{
IDirect3DQuery9* pQuery = (IDirect3DQuery9*)handle;
HRESULT hResult = pQuery->Issue(D3DISSUE_END);
Assert(hResult == D3D_OK);
}
// Get the number of pixels rendered between begin and end on an earlier frame.
// Calling this in the same frame is a huge perf hit!
int CShaderAPIDx8::OcclusionQuery_GetNumPixelsRendered(ShaderAPIOcclusionQuery_t handle, bool bFlush)
{
LOCK_SHADERAPI();
IDirect3DQuery9* pQuery = (IDirect3DQuery9*)handle;
tmZone(TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__);
DWORD nPixels;
HRESULT hResult = pQuery->GetData(&nPixels, sizeof(nPixels), bFlush ? D3DGETDATA_FLUSH : 0);
// This means that the query will not finish and resulted in an error, game should use
// the previous query's results and not reissue the query
if ((hResult == D3DERR_DEVICELOST) || (hResult == D3DERR_NOTAVAILABLE))
return OCCLUSION_QUERY_RESULT_ERROR;
// This means the query isn't finished yet, game will have to use the previous query's
// results and not reissue the query; wait for query to finish later.
if (hResult == S_FALSE)
return OCCLUSION_QUERY_RESULT_PENDING;
// NOTE: This appears to work around a driver bug for ATI on Vista
if (nPixels & 0x80000000)
{
nPixels = 0;
}
return nPixels;
}
void CShaderAPIDx8::SetPixelShaderFogParams(int reg, ShaderFogMode_t fogMode)
{
m_DelayedShaderConstants.iPixelShaderFogParams = reg; //save it off in case the ShaderFogMode_t disables fog. We only find out later.
float fogParams[4];
if ((GetPixelFogMode() != MATERIAL_FOG_NONE) && (fogMode != SHADER_FOGMODE_DISABLED))
{
float ooFogRange = 1.0f;
float fStart = m_VertexShaderFogParams[0];
float fEnd = m_VertexShaderFogParams[1];
// Check for divide by zero
if (fEnd != fStart)
{
ooFogRange = 1.0f / (fEnd - fStart);
}
fogParams[0] = fStart * ooFogRange; // fogStart / ( fogEnd - fogStart )
fogParams[1] = m_DynamicState.m_FogZ; // water height
fogParams[2] = clamp(m_flFogMaxDensity, 0.0f, 1.0f); // Max fog density
fogParams[3] = ooFogRange; // 1 / ( fogEnd - fogStart );
if (GetPixelFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z)
{
// terms are unused for height fog, forcing 1.0 allows unified PS math
fogParams[0] = 0.0f;
fogParams[2] = 1.0f;
}
}
else
{
//emulating MATERIAL_FOG_NONE by setting the parameters so that CalcRangeFog() always returns 0. Gets rid of a dynamic combo across the ps2x set.
fogParams[0] = 0.0f;
fogParams[1] = m_DynamicState.m_FogZ; // water height
fogParams[2] = 1.0f; // Max fog density
fogParams[3] = 0.0f;
}
// cFogEndOverFogRange, cFogOne, unused, cOOFogRange
SetPixelShaderConstant(reg, fogParams, 1);
}
void CShaderAPIDx8::SetPixelShaderFogParams(int reg)
{
SetPixelShaderFogParams(reg, m_TransitionTable.CurrentShadowState()->m_FogMode);
}
void CShaderAPIDx8::SetFlashlightState(const FlashlightState_t& state, const VMatrix& worldToTexture)
{
LOCK_SHADERAPI();
SetFlashlightStateEx(state, worldToTexture, NULL);
}
void CShaderAPIDx8::SetFlashlightStateEx(const FlashlightState_t& state, const VMatrix& worldToTexture, ITexture* pFlashlightDepthTexture)
{
LOCK_SHADERAPI();
// fixme: do a test here.
FlushBufferedPrimitives();
m_FlashlightState = state;
m_FlashlightWorldToTexture = worldToTexture;
m_pFlashlightDepthTexture = pFlashlightDepthTexture;
if (!g_pHardwareConfig->SupportsPixelShaders_2_b())
{
m_FlashlightState.m_bEnableShadows = false;
m_pFlashlightDepthTexture = NULL;
}
}
const FlashlightState_t& CShaderAPIDx8::GetFlashlightState(VMatrix& worldToTexture) const
{
worldToTexture = m_FlashlightWorldToTexture;
return m_FlashlightState;
}
const FlashlightState_t& CShaderAPIDx8::GetFlashlightStateEx(VMatrix& worldToTexture, ITexture** ppFlashlightDepthTexture) const
{
worldToTexture = m_FlashlightWorldToTexture;
*ppFlashlightDepthTexture = m_pFlashlightDepthTexture;
return m_FlashlightState;
}
bool CShaderAPIDx8::SupportsMSAAMode(int nMSAAMode)
{
if (IsX360())
{
return false;
}
return (D3D_OK == D3D()->CheckDeviceMultiSampleType(m_DisplayAdapter, m_DeviceType,
m_PresentParameters.BackBufferFormat,
m_PresentParameters.Windowed,
ComputeMultisampleType(nMSAAMode), NULL));
}
bool CShaderAPIDx8::SupportsCSAAMode(int nNumSamples, int nQualityLevel)
{
#ifdef DX_TO_GL_ABSTRACTION
// GL_NV_framebuffer_multisample_coverage
return false;
#endif
// Only nVidia does this kind of AA
if (g_pHardwareConfig->Caps().m_VendorID != VENDORID_NVIDIA)
return false;
DWORD dwQualityLevels = 0;
HRESULT hr = D3D()->CheckDeviceMultiSampleType(m_DisplayAdapter, m_DeviceType,
m_PresentParameters.BackBufferFormat,
m_PresentParameters.Windowed,
ComputeMultisampleType(nNumSamples), &dwQualityLevels);
return ((D3D_OK == hr) && ((int)dwQualityLevels >= nQualityLevel));
}
bool CShaderAPIDx8::SupportsShadowDepthTextures(void)
{
return g_pHardwareConfig->Caps().m_bSupportsShadowDepthTextures;
}
bool CShaderAPIDx8::SupportsNormalMapCompression(void) const
{
return g_pHardwareConfig->Caps().m_bSupportsNormalMapCompression;
}
bool CShaderAPIDx8::SupportsBorderColor(void) const
{
return g_pHardwareConfig->Caps().m_bSupportsBorderColor;
}
bool CShaderAPIDx8::SupportsFetch4(void)
{
return IsPC() && g_pHardwareConfig->Caps().m_bSupportsFetch4;
}
ImageFormat CShaderAPIDx8::GetShadowDepthTextureFormat(void)
{
return g_pHardwareConfig->Caps().m_ShadowDepthTextureFormat;
}
ImageFormat CShaderAPIDx8::GetNullTextureFormat(void)
{
return g_pHardwareConfig->Caps().m_NullTextureFormat;
}
void CShaderAPIDx8::SetShadowDepthBiasFactors(float fShadowSlopeScaleDepthBias, float fShadowDepthBias)
{
m_fShadowSlopeScaleDepthBias = fShadowSlopeScaleDepthBias;
m_fShadowDepthBias = fShadowDepthBias;
}
void CShaderAPIDx8::ClearVertexAndPixelShaderRefCounts()
{
LOCK_SHADERAPI();
ShaderManager()->ClearVertexAndPixelShaderRefCounts();
}
void CShaderAPIDx8::PurgeUnusedVertexAndPixelShaders()
{
LOCK_SHADERAPI();
ShaderManager()->PurgeUnusedVertexAndPixelShaders();
}
bool CShaderAPIDx8::UsingSoftwareVertexProcessing() const
{
return g_pHardwareConfig->Caps().m_bSoftwareVertexProcessing;
}
ITexture* CShaderAPIDx8::GetRenderTargetEx(int nRenderTargetID)
{
return ShaderUtil()->GetRenderTargetEx(nRenderTargetID);
}
float CShaderAPIDx8::GetLightMapScaleFactor(void) const
{
switch (HardwareConfig()->GetHDRType())
{
case HDR_TYPE_FLOAT:
return 1.0;
break;
case HDR_TYPE_INTEGER:
return 16.0;
case HDR_TYPE_NONE:
default:
return GammaToLinearFullRange(2.0); // light map scale
}
}
void CShaderAPIDx8::SetToneMappingScaleLinear(const Vector& scale)
{
if (g_pHardwareConfig->SupportsPixelShaders_2_0())
{
// Flush buffered primitives before changing the tone map scalar!
FlushBufferedPrimitives();
Vector scale_to_use = scale;
m_ToneMappingScale.AsVector3D() = scale_to_use;
bool mode_uses_srgb = false;
switch (HardwareConfig()->GetHDRType())
{
case HDR_TYPE_NONE:
m_ToneMappingScale.x = 1.0; // output scale
m_ToneMappingScale.z = 1.0; // reflection map scale
break;
case HDR_TYPE_FLOAT:
m_ToneMappingScale.x = scale_to_use.x; // output scale
m_ToneMappingScale.z = 1.0; // reflection map scale
break;
case HDR_TYPE_INTEGER:
mode_uses_srgb = true;
m_ToneMappingScale.x = scale_to_use.x; // output scale
m_ToneMappingScale.z = 16.0; // reflection map scale
break;
}
m_ToneMappingScale.y = GetLightMapScaleFactor(); // light map scale
// w component gets gamma scale
m_ToneMappingScale.w = LinearToGammaFullRange(m_ToneMappingScale.x);
SetPixelShaderConstant(TONE_MAPPING_SCALE_PSH_CONSTANT, m_ToneMappingScale.Base());
// We have to change the fog color since we tone map directly in the shaders in integer HDR mode.
if (HardwareConfig()->GetHDRType() == HDR_TYPE_INTEGER && m_TransitionTable.CurrentShadowState())
{
// Get the shadow state in sync since it depends on SetToneMappingScaleLinear.
ApplyFogMode(m_TransitionTable.CurrentShadowState()->m_FogMode, mode_uses_srgb, m_TransitionTable.CurrentShadowState()->m_bDisableFogGammaCorrection);
}
}
}
const Vector& CShaderAPIDx8::GetToneMappingScaleLinear(void) const
{
return m_ToneMappingScale.AsVector3D();
}
void CShaderAPIDx8::EnableLinearColorSpaceFrameBuffer(bool bEnable)
{
LOCK_SHADERAPI();
m_TransitionTable.EnableLinearColorSpaceFrameBuffer(bEnable);
}
void CShaderAPIDx8::SetPSNearAndFarZ(int pshReg)
{
VMatrix m;
GetMatrix(MATERIAL_PROJECTION, m.m[0]);
// m[2][2] = F/(N-F) (flip sign if RH)
// m[3][2] = NF/(N-F)
float vNearFar[4];
float N = m[3][2] / m[2][2];
float F = (m[3][2] * N) / (N + m[3][2]);
vNearFar[0] = N;
vNearFar[1] = F;
SetPixelShaderConstant(pshReg, vNearFar, 1);
}
void CShaderAPIDx8::SetFloatRenderingParameter(int parm_number, float value)
{
LOCK_SHADERAPI();
if (parm_number < ARRAYSIZE(FloatRenderingParameters))
FloatRenderingParameters[parm_number] = value;
}
void CShaderAPIDx8::SetIntRenderingParameter(int parm_number, int value)
{
LOCK_SHADERAPI();
if (parm_number < ARRAYSIZE(IntRenderingParameters))
IntRenderingParameters[parm_number] = value;
}
void CShaderAPIDx8::SetVectorRenderingParameter(int parm_number, Vector const& value)
{
LOCK_SHADERAPI();
if (parm_number < ARRAYSIZE(VectorRenderingParameters))
VectorRenderingParameters[parm_number] = value;
}
float CShaderAPIDx8::GetFloatRenderingParameter(int parm_number) const
{
LOCK_SHADERAPI();
if (parm_number < ARRAYSIZE(FloatRenderingParameters))
return FloatRenderingParameters[parm_number];
else
return 0.0;
}
int CShaderAPIDx8::GetIntRenderingParameter(int parm_number) const
{
LOCK_SHADERAPI();
if (parm_number < ARRAYSIZE(IntRenderingParameters))
return IntRenderingParameters[parm_number];
else
return 0;
}
Vector CShaderAPIDx8::GetVectorRenderingParameter(int parm_number) const
{
LOCK_SHADERAPI();
if (parm_number < ARRAYSIZE(VectorRenderingParameters))
return VectorRenderingParameters[parm_number];
else
return Vector(0, 0, 0);
}
// stencil entry points
void CShaderAPIDx8::SetStencilEnable(bool onoff)
{
LOCK_SHADERAPI();
SetRenderState(D3DRS_STENCILENABLE, onoff ? TRUE : FALSE, true);
}
void CShaderAPIDx8::SetStencilFailOperation(StencilOperation_t op)
{
LOCK_SHADERAPI();
SetRenderState(D3DRS_STENCILFAIL, op, true);
}
void CShaderAPIDx8::SetStencilZFailOperation(StencilOperation_t op)
{
LOCK_SHADERAPI();
SetRenderState(D3DRS_STENCILZFAIL, op, true);
}
void CShaderAPIDx8::SetStencilPassOperation(StencilOperation_t op)
{
LOCK_SHADERAPI();
SetRenderState(D3DRS_STENCILPASS, op, true);
}
void CShaderAPIDx8::SetStencilCompareFunction(StencilComparisonFunction_t cmpfn)
{
LOCK_SHADERAPI();
SetRenderState(D3DRS_STENCILFUNC, cmpfn, true);
}
void CShaderAPIDx8::SetStencilReferenceValue(int ref)
{
LOCK_SHADERAPI();
SetRenderState(D3DRS_STENCILREF, ref, true);
}
void CShaderAPIDx8::SetStencilTestMask(uint32 msk)
{
LOCK_SHADERAPI();
SetRenderState(D3DRS_STENCILMASK, msk, true);
}
void CShaderAPIDx8::SetStencilWriteMask(uint32 msk)
{
LOCK_SHADERAPI();
SetRenderState(D3DRS_STENCILWRITEMASK, msk, true);
}
void CShaderAPIDx8::ClearStencilBufferRectangle(
int xmin, int ymin, int xmax, int ymax, int value)
{
LOCK_SHADERAPI();
D3DRECT clear;
clear.x1 = xmin;
clear.y1 = ymin;
clear.x2 = xmax;
clear.y2 = ymax;
Dx9Device()->Clear(
1, &clear, D3DCLEAR_STENCIL, 0, 0, value);
}
int CShaderAPIDx8::CompareSnapshots(StateSnapshot_t snapshot0, StateSnapshot_t snapshot1)
{
LOCK_SHADERAPI();
const ShadowState_t& shadow0 = m_TransitionTable.GetSnapshot(snapshot0);
const ShadowState_t& shadow1 = m_TransitionTable.GetSnapshot(snapshot1);
const ShadowShaderState_t& shader0 = m_TransitionTable.GetSnapshotShader(snapshot0);
const ShadowShaderState_t& shader1 = m_TransitionTable.GetSnapshotShader(snapshot1);
int dVertex = shader0.m_VertexShader - shader1.m_VertexShader;
if (dVertex)
return dVertex;
int dVCombo = shader0.m_nStaticVshIndex - shader1.m_nStaticVshIndex;
if (dVCombo)
return dVCombo;
int dPixel = shader0.m_PixelShader - shader1.m_PixelShader;
if (dPixel)
return dPixel;
int dPCombo = shader0.m_nStaticPshIndex - shader1.m_nStaticPshIndex;
if (dPCombo)
return dPCombo;
return snapshot0 - snapshot1;
}
//-----------------------------------------------------------------------------
// X360 TTF support requires XUI state manipulation of d3d.
// Font support lives inside the shaderapi in order to maintain privacy of d3d.
//-----------------------------------------------------------------------------
#if defined( _X360 )
HXUIFONT CShaderAPIDx8::OpenTrueTypeFont(const char* pFontname, int tall, int style)
{
LOCK_SHADERAPI();
struct fontTable_t
{
const char* pFontName;
const char* pPath;
};
// explicit mapping now required, dvd searching to expensive
static fontTable_t fontToFilename[] =
{
{"tf2", "tf/resource/tf2.ttf"},
{"tf2 build", "tf/resource/tf2build.ttf"},
{"tf2 professor", "tf/resource/tf2professor.ttf"},
{"tf2 secondary", "tf/resource/tf2secondary.ttf"},
{"team fortress", "tf/resource/tf.ttf"},
{"tfd", "tf/resource/tfd.ttf"},
{"tflogo", "tf/resource/tflogo.ttf"},
{"hl2ep2", "ep2/resource/hl2ep2.ttf"},
{"hl2ep1", "episodic/resource/hl2ep1.ttf"},
{"halflife2", "hl2/resource/halflife2.ttf"},
{"hl2cross", "hl2/resource/HL2Crosshairs.ttf"},
{"courier new", "platform/vgui/fonts/cour.ttf"},
{"times new roman", "platform/vgui/fonts/times.ttf"},
{"trebuchet ms", "platform/vgui/fonts/trebuc.ttf"},
{"verdana", "platform/vgui/fonts/verdana.ttf"},
{"tahoma", "platform/vgui/fonts/tahoma.ttf"},
};
// remap typeface to diskname
const char* pDiskname = NULL;
for (int i = 0; i < ARRAYSIZE(fontToFilename); i++)
{
if (!V_stricmp(pFontname, fontToFilename[i].pFontName))
{
pDiskname = fontToFilename[i].pPath;
break;
}
}
if (!pDiskname)
{
// not found
DevMsg("True Type Font: '%s' unknown.\n", pFontname);
return NULL;
}
// font will be registered using typeface name
wchar_t wchFontname[MAX_PATH];
Q_UTF8ToUnicode(pFontname, wchFontname, sizeof(wchFontname));
// find font in registered typefaces
TypefaceDescriptor* pDescriptors = NULL;
DWORD numTypeFaces = 0;
HRESULT hr = XuiEnumerateTypefaces(&pDescriptors, &numTypeFaces);
if (FAILED(hr))
{
return NULL;
}
bool bRegistered = false;
for (DWORD i = 0; i < numTypeFaces; i++)
{
if (!V_wcscmp(pDescriptors->szTypeface, wchFontname))
{
bRegistered = true;
break;
}
}
XuiDestroyTypefaceList(pDescriptors, numTypeFaces);
if (!bRegistered)
{
// unregistered type face, register type face and retry
// only file based resource locators work
char filename[MAX_PATH];
V_snprintf(filename, sizeof(filename), "file://d:/%s", pDiskname);
Q_FixSlashes(filename, '/');
wchar_t wchFilename[MAX_PATH];
Q_UTF8ToUnicode(filename, wchFilename, sizeof(wchFilename));
TypefaceDescriptor desc;
desc.fBaselineAdjust = 0;
desc.szFallbackTypeface = NULL;
desc.szLocator = wchFilename;
desc.szReserved1 = 0;
desc.szTypeface = wchFontname;
hr = XuiRegisterTypeface(&desc, FALSE);
if (FAILED(hr))
{
return NULL;
}
}
// empirically derived factor to achieve desired cell height
float pointSize = tall * 0.59f;
HXUIFONT hFont = NULL;
hr = XuiCreateFont(wchFontname, pointSize, style, 0, &hFont);
if (FAILED(hr))
{
return NULL;
}
return hFont;
}
#endif
//-----------------------------------------------------------------------------
// Release TTF
//-----------------------------------------------------------------------------
#if defined( _X360 )
void CShaderAPIDx8::CloseTrueTypeFont(HXUIFONT hFont)
{
if (!hFont)
return;
LOCK_SHADERAPI();
XuiReleaseFont(hFont);
}
#endif
//-----------------------------------------------------------------------------
// Get the TTF Metrics
//-----------------------------------------------------------------------------
#if defined( _X360 )
bool CShaderAPIDx8::GetTrueTypeFontMetrics(HXUIFONT hFont, XUIFontMetrics* pFontMetrics, XUICharMetrics charMetrics[256])
{
if (!hFont)
return false;
LOCK_SHADERAPI();
V_memset(charMetrics, 0, 256 * sizeof(XUICharMetrics));
HRESULT hr = XuiGetFontMetrics(hFont, pFontMetrics);
if (!FAILED(hr))
{
// X360 issue: max character width may be too small.
// Run through each character and fixup
for (int i = 1; i < 256; i++)
{
wchar_t wch = i;
hr = XuiGetCharMetrics(hFont, wch, &charMetrics[i]);
if (!FAILED(hr))
{
float maxWidth = charMetrics[i].fMaxX;
if (charMetrics[i].fMinX < 0)
{
maxWidth = charMetrics[i].fMaxX - charMetrics[i].fMinX;
}
if (maxWidth > pFontMetrics->fMaxWidth)
{
pFontMetrics->fMaxWidth = maxWidth;
}
if (charMetrics[i].fAdvance > pFontMetrics->fMaxWidth)
{
pFontMetrics->fMaxWidth = charMetrics[i].fAdvance;
}
}
}
// fonts are getting cut off, MaxHeight seems to be misreported smaller
// take MaxHeight to be the larger of its reported value or (ascent + descent)
float maxHeight = 0;
if (pFontMetrics->fMaxDescent <= 0)
{
// descent is negative for below baseline
maxHeight = pFontMetrics->fMaxAscent - pFontMetrics->fMaxDescent;
}
if (maxHeight > pFontMetrics->fMaxHeight)
{
pFontMetrics->fMaxHeight = maxHeight;
}
}
return (!FAILED(hr));
}
#endif
//-----------------------------------------------------------------------------
// Gets the glyph bits in rgba order. This function PURPOSELY hijacks D3D
// because XUI is involved. It is called at a very specific place in the VGUI
// render frame where its deleterious affects are going to be harmless.
//-----------------------------------------------------------------------------
#if defined( _X360 )
bool CShaderAPIDx8::GetTrueTypeGlyphs(HXUIFONT hFont, int numChars, wchar_t* pWch, int* pOffsetX, int* pOffsetY, int* pWidth, int* pHeight, unsigned char* pRGBA, int* pRGBAOffset)
{
if (!hFont)
return false;
// Ensure this doesn't talk to D3D at the same time as the loading bar
AUTO_LOCK_FM(m_nonInteractiveModeMutex);
LOCK_SHADERAPI();
bool bSuccess = false;
IDirect3DSurface* pRTSurface = NULL;
IDirect3DSurface* pSavedSurface = NULL;
IDirect3DSurface* pSavedDepthSurface = NULL;
IDirect3DTexture* pTexture = NULL;
D3DVIEWPORT9 savedViewport;
D3DXMATRIX matView;
D3DXMATRIX matXForm;
D3DLOCKED_RECT lockedRect;
// have to reset to default state to rasterize glyph correctly
// state will get re-established during next mesh draw
ResetRenderState(false);
Dx9Device()->SetRenderState(D3DRS_ZENABLE, FALSE);
Dx9Device()->GetRenderTarget(0, &pSavedSurface);
Dx9Device()->GetDepthStencilSurface(&pSavedDepthSurface);
Dx9Device()->GetViewport(&savedViewport);
// Figure out the size of surface/texture we need to allocate
int rtWidth = 0;
int rtHeight = 0;
for (int i = 0; i < numChars; i++)
{
rtWidth += pWidth[i];
rtHeight = max(rtHeight, pHeight[i]);
}
// per resolve() restrictions
rtWidth = AlignValue(rtWidth, 32);
rtHeight = AlignValue(rtHeight, 32);
// create a render target to capture the glyph render
pRTSurface = g_TextureHeap.AllocRenderTargetSurface(rtWidth, rtHeight, D3DFMT_A8R8G8B8);
if (!pRTSurface)
goto cleanUp;
Dx9Device()->SetRenderTarget(0, pRTSurface);
// Disable depth here otherwise you get a colour/depth multisample mismatch error (in 480p)
Dx9Device()->SetDepthStencilSurface(NULL);
Dx9Device()->Clear(0, NULL, D3DCLEAR_TARGET, 0x00000000, (ReverseDepthOnX360() ? 0.0 : 1.0f), 0);
// create texture to get glyph render from EDRAM
HRESULT hr = Dx9Device()->CreateTexture(rtWidth, rtHeight, 1, 0, D3DFMT_A8R8G8B8, 0, &pTexture, NULL);
if (FAILED(hr))
goto cleanUp;
bool bPreviousOwnState = OwnGPUResources(false);
XuiRenderBegin(m_hDC, 0x00000000);
D3DXMatrixIdentity(&matView);
XuiRenderSetViewTransform(m_hDC, &matView);
XuiRenderSetTransform(m_hDC, &matView);
// rasterize the glyph
XuiSelectFont(m_hDC, hFont);
XuiSetColorFactor(m_hDC, 0xFFFFFFFF);
// Draw the characters, stepping across the texture
int xCursor = 0;
for (int i = 0; i < numChars; i++)
{
// FIXME: the drawRect params don't make much sense (should use "(xCursor+pWidth[i]), pHeight[i]", but then some characters disappear!)
XUIRect drawRect = XUIRect(xCursor + pOffsetX[i], pOffsetY[i], rtWidth, rtHeight);
wchar_t text[2] = { pWch[i], 0 };
XuiDrawText(m_hDC, text, XUI_FONT_STYLE_NORMAL | XUI_FONT_STYLE_SINGLE_LINE | XUI_FONT_STYLE_NO_WORDWRAP, 0, &drawRect);
xCursor += pWidth[i];
}
XuiRenderEnd(m_hDC);
OwnGPUResources(bPreviousOwnState);
// transfer from edram to system
hr = Dx9Device()->Resolve(0, NULL, pTexture, NULL, 0, 0, NULL, 0, 0, NULL);
if (FAILED(hr))
goto cleanUp;
hr = pTexture->LockRect(0, &lockedRect, NULL, 0);
if (FAILED(hr))
goto cleanUp;
// transfer to linear format, one character at a time
xCursor = 0;
for (int i = 0; i < numChars; i++)
{
int destPitch = pWidth[i] * 4;
unsigned char* pLinear = pRGBA + pRGBAOffset[i];
RECT copyRect = { xCursor, 0, xCursor + pWidth[i], pHeight[i] };
xCursor += pWidth[i];
XGUntileSurface(pLinear, destPitch, NULL, lockedRect.pBits, rtWidth, rtHeight, &copyRect, 4);
// convert argb to rgba
float r, g, b, a;
for (int y = 0; y < pHeight[i]; y++)
{
unsigned char* pSrc = (unsigned char*)pLinear + y * destPitch;
for (int x = 0; x < pWidth[i]; x++)
{
// undo pre-multiplied alpha since glyph bits will be sourced as a rgba texture
if (!pSrc[0])
a = 1;
else
a = (float)pSrc[0] * 1.0f / 255.0f;
r = ((float)pSrc[1] * 1.0f / 255.0f) / a * 255.0f;
if (r > 255)
r = 255;
g = ((float)pSrc[2] * 1.0f / 255.0f) / a * 255.0f;
if (g > 255)
g = 255;
b = ((float)pSrc[3] * 1.0f / 255.0f) / a * 255.0f;
if (b > 255)
b = 255;
pSrc[3] = pSrc[0];
pSrc[2] = b;
pSrc[1] = g;
pSrc[0] = r;
pSrc += 4;
}
}
}
pTexture->UnlockRect(0);
bSuccess = true;
cleanUp:
if (pRTSurface)
{
Dx9Device()->SetRenderTarget(0, pSavedSurface);
Dx9Device()->SetDepthStencilSurface(pSavedDepthSurface);
Dx9Device()->SetViewport(&savedViewport);
pRTSurface->Release();
}
if (pTexture)
pTexture->Release();
if (pSavedSurface)
pSavedSurface->Release();
// XUI changed renderstates behind our back, so we need to reset to defaults again to get back in synch:
ResetRenderState(false);
return bSuccess;
}
#endif
//-----------------------------------------------------------------------------
// Create a 360 Render Target Surface
//-----------------------------------------------------------------------------
#if defined( _X360 )
ShaderAPITextureHandle_t CShaderAPIDx8::CreateRenderTargetSurface(int width, int height, ImageFormat format, const char* pDebugName, const char* pTextureGroupName)
{
LOCK_SHADERAPI();
ShaderAPITextureHandle_t textureHandle = CreateTextureHandle();
Texture_t* pTexture = &GetTexture(textureHandle);
pTexture->m_Flags = (Texture_t::IS_ALLOCATED | Texture_t::IS_RENDER_TARGET_SURFACE);
pTexture->m_DebugName = pDebugName;
pTexture->m_Width = width;
pTexture->m_Height = height;
pTexture->m_Depth = 1;
pTexture->m_NumCopies = 1;
pTexture->m_CurrentCopy = 0;
ImageFormat dstImageFormat = FindNearestSupportedFormat(format, false, true, false);
D3DFORMAT actualFormat = ImageLoader::ImageFormatToD3DFormat(dstImageFormat);
pTexture->GetRenderTargetSurface(false) = g_TextureHeap.AllocRenderTargetSurface(width, height, actualFormat);
pTexture->GetRenderTargetSurface(true) = g_TextureHeap.AllocRenderTargetSurface(width, height, (D3DFORMAT)MAKESRGBFMT(actualFormat));
pTexture->SetImageFormat(dstImageFormat);
pTexture->m_UTexWrap = D3DTADDRESS_CLAMP;
pTexture->m_VTexWrap = D3DTADDRESS_CLAMP;
pTexture->m_WTexWrap = D3DTADDRESS_CLAMP;
pTexture->m_MagFilter = D3DTEXF_LINEAR;
pTexture->m_NumLevels = 1;
pTexture->m_MipFilter = D3DTEXF_NONE;
pTexture->m_MinFilter = D3DTEXF_LINEAR;
pTexture->m_SwitchNeeded = false;
ComputeStatsInfo(textureHandle, false, false);
SetupTextureGroup(textureHandle, pTextureGroupName);
return textureHandle;
}
#endif
//-----------------------------------------------------------------------------
// Shader constants are batched and written to gpu once prior to draw.
//-----------------------------------------------------------------------------
void CShaderAPIDx8::WriteShaderConstantsToGPU()
{
#if defined( _X360 )
// vector vertex constants can just blast their set range
if (m_MaxVectorVertexShaderConstant)
{
if (m_bGPUOwned)
{
// faster path, write directly into GPU command buffer, bypassing shadow state
// can only set what is actually owned
Assert(m_MaxVectorVertexShaderConstant <= VERTEX_SHADER_MODEL + 3 * NUM_MODEL_TRANSFORMS);
int numVectors = AlignValue(m_MaxVectorVertexShaderConstant, 4);
BYTE* pCommandBufferData;
Dx9Device()->GpuBeginVertexShaderConstantF4(0, (D3DVECTOR4**)&pCommandBufferData, numVectors);
memcpy(pCommandBufferData, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), numVectors * (sizeof(float) * 4));
Dx9Device()->GpuEndVertexShaderConstantF4();
}
else
{
Dx9Device()->SetVertexShaderConstantF(0, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), m_MaxVectorVertexShaderConstant);
}
memcpy(m_DynamicState.m_pVectorVertexShaderConstant[0].Base(), m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), m_MaxVectorVertexShaderConstant * 4 * sizeof(float));
m_MaxVectorVertexShaderConstant = 0;
}
if (m_MaxVectorPixelShaderConstant)
{
if (m_bGPUOwned)
{
// faster path, write directly into GPU command buffer, bypassing shadow state
// can only set what is actually owned
Assert(m_MaxVectorPixelShaderConstant <= 32);
int numVectors = AlignValue(m_MaxVectorPixelShaderConstant, 4);
BYTE* pCommandBufferData;
Dx9Device()->GpuBeginPixelShaderConstantF4(0, (D3DVECTOR4**)&pCommandBufferData, numVectors);
memcpy(pCommandBufferData, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), numVectors * (sizeof(float) * 4));
Dx9Device()->GpuEndPixelShaderConstantF4();
}
else
{
Dx9Device()->SetPixelShaderConstantF(0, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), m_MaxVectorPixelShaderConstant);
}
memcpy(m_DynamicState.m_pVectorPixelShaderConstant[0].Base(), m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), m_MaxVectorPixelShaderConstant * 4 * sizeof(float));
m_MaxVectorPixelShaderConstant = 0;
}
// boolean and integer constants can just blast their set range
// these are currently extremely small in number, if this changes they may benefit from a fast path pattern
if (m_MaxBooleanVertexShaderConstant)
{
Dx9Device()->SetVertexShaderConstantB(0, m_DesiredState.m_pBooleanVertexShaderConstant, m_MaxBooleanVertexShaderConstant);
memcpy(m_DynamicState.m_pBooleanVertexShaderConstant, m_DesiredState.m_pBooleanVertexShaderConstant, m_MaxBooleanVertexShaderConstant * sizeof(BOOL));
m_MaxBooleanVertexShaderConstant = 0;
}
if (m_MaxIntegerVertexShaderConstant)
{
Dx9Device()->SetVertexShaderConstantI(0, (int*)m_DesiredState.m_pIntegerVertexShaderConstant, m_MaxIntegerVertexShaderConstant);
memcpy(m_DynamicState.m_pIntegerVertexShaderConstant[0].Base(), m_DesiredState.m_pIntegerVertexShaderConstant[0].Base(), m_MaxIntegerVertexShaderConstant * sizeof(IntVector4D));
m_MaxIntegerVertexShaderConstant = 0;
}
if (m_MaxBooleanPixelShaderConstant)
{
Dx9Device()->SetPixelShaderConstantB(0, m_DesiredState.m_pBooleanPixelShaderConstant, m_MaxBooleanPixelShaderConstant);
memcpy(m_DynamicState.m_pBooleanPixelShaderConstant, m_DesiredState.m_pBooleanPixelShaderConstant, m_MaxBooleanPixelShaderConstant * sizeof(BOOL));
m_MaxBooleanPixelShaderConstant = 0;
}
// integer pixel constants are not used, so not supporting
#if 0
if (m_MaxIntegerPixelShaderConstant)
{
Dx9Device()->SetPixelShaderConstantI(0, (int*)m_DesiredState.m_pIntegerPixelShaderConstant, m_MaxIntegerPixelShaderConstant);
memcpy(m_DynamicState.m_pIntegerPixelShaderConstant[0].Base(), m_DesiredState.m_pIntegerPixelShaderConstant[0].Base(), m_MaxIntegerPixelShaderConstant * sizeof(IntVector4D));
m_MaxIntegerPixelShaderConstant = 0;
}
#endif
#endif
}
//-----------------------------------------------------------------------------
// The application is about to perform a hard reboot, but wants to hide the screen flash
// by persisting the front buffer across a reboot boundary. The persisted frame buffer
// can be detected and restored.
//-----------------------------------------------------------------------------
#if defined( _X360 )
void CShaderAPIDx8::PersistDisplay()
{
if (m_PresentParameters.FrontBufferFormat != D3DFMT_LE_X8R8G8B8)
{
// The format must be what PersistDisplay() expects, otherwise D3DRIP.
// If this hits due to sRGB bit set that confuses PersistDisplay(),
// the fix may be to slam the presentation parameters to the expected format,
// do a ResetDevice(), and then PersistDisplay().
Assert(0);
return;
}
IDirect3DTexture* pTexture;
HRESULT hr = Dx9Device()->GetFrontBuffer(&pTexture);
if (!FAILED(hr))
{
OwnGPUResources(false);
Dx9Device()->PersistDisplay(pTexture, NULL);
pTexture->Release();
}
}
#endif
#if defined( _X360 )
bool CShaderAPIDx8::PostQueuedTexture(const void* pData, int nDataSize, ShaderAPITextureHandle_t* pHandles, int numHandles, int nWidth, int nHeight, int nDepth, int numMips, int* pRefCount)
{
CUtlBuffer vtfBuffer;
IVTFTexture* pVTFTexture = NULL;
bool bOK = false;
if (!pData || !nDataSize)
{
// invalid
goto cleanUp;
}
// get a unique vtf and mount texture
// vtf can expect non-volatile buffer data to be stable through vtf lifetime
// this prevents redundant copious amounts of image memory transfers
pVTFTexture = CreateVTFTexture();
vtfBuffer.SetExternalBuffer((void*)pData, nDataSize, nDataSize);
if (!pVTFTexture->UnserializeFromBuffer(vtfBuffer, false, false, false, 0))
{
goto cleanUp;
}
// provided vtf buffer is all mips, determine top mip due to possible picmip
int iTopMip = 0;
int mipWidth, mipHeight, mipDepth;
do
{
pVTFTexture->ComputeMipLevelDimensions(iTopMip, &mipWidth, &mipHeight, &mipDepth);
if (nWidth == mipWidth && nHeight == mipHeight && nDepth == mipDepth)
{
break;
}
iTopMip++;
} while (mipWidth != 1 || mipHeight != 1 || mipDepth != 1);
// create and blit
for (int iFrame = 0; iFrame < numHandles; iFrame++)
{
ShaderAPITextureHandle_t hTexture = pHandles[iFrame];
Texture_t* pTexture = &GetTexture(hTexture);
int nFaceCount = (pTexture->m_CreationFlags & TEXTURE_CREATE_CUBEMAP) ? CUBEMAP_FACE_COUNT - 1 : 1;
IDirect3DBaseTexture* pD3DTexture;
if (pTexture->m_CreationFlags & TEXTURE_CREATE_NOD3DMEMORY)
{
pD3DTexture = pTexture->GetTexture();
if (!g_TextureHeap.AllocD3DMemory(pD3DTexture))
{
goto cleanUp;
}
}
else
{
pD3DTexture = pTexture->GetTexture();
}
// blit the hi-res texture bits into d3d memory
for (int iFace = 0; iFace < nFaceCount; ++iFace)
{
for (int iMip = 0; iMip < numMips; ++iMip)
{
pVTFTexture->ComputeMipLevelDimensions(iTopMip + iMip, &mipWidth, &mipHeight, &mipDepth);
unsigned char* pSourceBits = pVTFTexture->ImageData(iFrame, iFace, iTopMip + iMip, 0, 0, 0);
TextureLoadInfo_t info;
info.m_TextureHandle = hTexture;
info.m_pTexture = pD3DTexture;
info.m_nLevel = iMip;
info.m_nCopy = 0;
info.m_CubeFaceID = (D3DCUBEMAP_FACES)iFace;
info.m_nWidth = mipWidth;
info.m_nHeight = mipHeight;
info.m_nZOffset = 0;
info.m_SrcFormat = pVTFTexture->Format();
info.m_pSrcData = pSourceBits;
info.m_bSrcIsTiled = pVTFTexture->IsPreTiled();
info.m_bCanConvertFormat = (pTexture->m_Flags & Texture_t::CAN_CONVERT_FORMAT) != 0;
LoadTexture(info);
}
}
pTexture->m_Flags |= Texture_t::IS_FINALIZED;
(*pRefCount)--;
}
// success
bOK = true;
cleanUp:
if (pVTFTexture)
{
DestroyVTFTexture(pVTFTexture);
}
if (!bOK)
{
// undo artificial lock
(*pRefCount) -= numHandles;
}
return bOK;
}
#endif
#if defined( _X360 )
void* CShaderAPIDx8::GetD3DDevice()
{
return Dx9Device();
}
#endif
#if defined( _X360 )
static void r_enable_gpr_allocations_callback(IConVar* var, const char* pOldValue, float flOldValue)
{
if (((ConVar*)var)->GetBool() == false)
{
//reset back the default 64/64 allocation before we stop updating
if (Dx9Device() != NULL)
{
Dx9Device()->SetShaderGPRAllocation(0, 0, 0);
}
}
}
ConVar r_enable_gpr_allocations("r_enable_gpr_allocations", "1", 0, "Enable usage of IDirect3DDevice9::SetShaderGPRAllocation()", r_enable_gpr_allocations_callback);
static void CommitShaderGPRs(IDirect3DDevice9* pDevice, const DynamicState_t& desiredState, DynamicState_t& currentState, bool bForce)
{
if (desiredState.m_iVertexShaderGPRAllocation != currentState.m_iVertexShaderGPRAllocation)
{
pDevice->SetShaderGPRAllocation(0, desiredState.m_iVertexShaderGPRAllocation, 128 - desiredState.m_iVertexShaderGPRAllocation);
currentState.m_iVertexShaderGPRAllocation = desiredState.m_iVertexShaderGPRAllocation;
}
}
void CShaderAPIDx8::PushVertexShaderGPRAllocation(int iVertexShaderCount)
{
Assert((iVertexShaderCount >= 16) && (iVertexShaderCount <= 112));
m_VertexShaderGPRAllocationStack.Push(iVertexShaderCount);
if (r_enable_gpr_allocations.GetBool())
{
if (m_DynamicState.m_iVertexShaderGPRAllocation != iVertexShaderCount)
{
m_DesiredState.m_iVertexShaderGPRAllocation = iVertexShaderCount;
ADD_COMMIT_FUNC(COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitShaderGPRs);
}
}
}
void CShaderAPIDx8::PopVertexShaderGPRAllocation(void)
{
m_VertexShaderGPRAllocationStack.Pop();
if (r_enable_gpr_allocations.GetBool())
{
int iVertexShaderCount;
if (m_VertexShaderGPRAllocationStack.Count())
iVertexShaderCount = m_VertexShaderGPRAllocationStack.Top();
else
iVertexShaderCount = 64;
if (m_DynamicState.m_iVertexShaderGPRAllocation != iVertexShaderCount)
{
m_DesiredState.m_iVertexShaderGPRAllocation = iVertexShaderCount;
ADD_COMMIT_FUNC(COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitShaderGPRs);
}
}
}
void CShaderAPIDx8::EnableVSync_360(bool bEnable)
{
if (bEnable)
{
Dx9Device()->SetRenderState(D3DRS_PRESENTIMMEDIATETHRESHOLD, 0); //only swap on vertical blanks
}
else
{
Dx9Device()->SetRenderState(D3DRS_PRESENTIMMEDIATETHRESHOLD, 100); //allow a swap at any point in the DAC scan
}
}
#endif
// ------------ New Vertex/Index Buffer interface ----------------------------
void CShaderAPIDx8::BindVertexBuffer(int streamID, IVertexBuffer* pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions)
{
LOCK_SHADERAPI();
MeshMgr()->BindVertexBuffer(streamID, pVertexBuffer, nOffsetInBytes, nFirstVertex, nVertexCount, fmt, nRepetitions);
}
void CShaderAPIDx8::BindIndexBuffer(IIndexBuffer* pIndexBuffer, int nOffsetInBytes)
{
LOCK_SHADERAPI();
MeshMgr()->BindIndexBuffer(pIndexBuffer, nOffsetInBytes);
}
void CShaderAPIDx8::Draw(MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount)
{
LOCK_SHADERAPI();
MeshMgr()->Draw(primitiveType, nFirstIndex, nIndexCount);
}
// ------------ End ----------------------------
float CShaderAPIDx8::GammaToLinear_HardwareSpecific(float fGamma) const
{
if (IsPC())
{
return SrgbGammaToLinear(fGamma);
}
else if (IsX360())
{
return SrgbGammaToLinear(fGamma);
}
else
{
// Unknown console
return pow(fGamma, 2.2f); // Use a gamma 2.2 curve
}
}
float CShaderAPIDx8::LinearToGamma_HardwareSpecific(float fLinear) const
{
if (IsPC())
{
return SrgbLinearToGamma(fLinear);
}
else if (IsX360())
{
return SrgbLinearToGamma(fLinear);
}
else
{
// Unknown console
return pow(fLinear, (1.0f / 2.2f)); // Use a gamma 2.2 curve
}
}
bool CShaderAPIDx8::ShouldWriteDepthToDestAlpha(void) const
{
return IsPC() && g_pHardwareConfig->SupportsPixelShaders_2_b() &&
(m_SceneFogMode != MATERIAL_FOG_LINEAR_BELOW_FOG_Z) &&
(GetIntRenderingParameter(INT_RENDERPARM_WRITE_DEPTH_TO_DESTALPHA) != 0);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CShaderAPIDx8::AcquireThreadOwnership()
{
SetCurrentThreadAsOwner();
#if (defined( _X360 ) || defined( DX_TO_GL_ABSTRACTION ))
Dx9Device()->AcquireThreadOwnership();
#endif
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CShaderAPIDx8::ReleaseThreadOwnership()
{
RemoveThreadOwner();
#if (defined( _X360 ) || defined( DX_TO_GL_ABSTRACTION ))
Dx9Device()->ReleaseThreadOwnership();
#endif
}
//-----------------------------------------------------------------------------
// Actual low level setting of the color RT. All Xbox RT funnels here
// to track the actual RT state. Returns true if the RT gets set, otherwise false.
//-----------------------------------------------------------------------------
bool CShaderAPIDx8::SetRenderTargetInternalXbox(ShaderAPITextureHandle_t hRenderTargetTexture, bool bForce)
{
// valid for 360 only
if (IsPC())
{
Assert(0);
return false;
}
if (hRenderTargetTexture == INVALID_SHADERAPI_TEXTURE_HANDLE)
{
// could be a reset, force to back buffer
hRenderTargetTexture = SHADER_RENDERTARGET_BACKBUFFER;
}
if (m_hCachedRenderTarget == INVALID_SHADERAPI_TEXTURE_HANDLE)
{
// let the set go through to establish the initial state
bForce = true;
}
if (!bForce && (hRenderTargetTexture == m_hCachedRenderTarget && m_DynamicState.m_bSRGBWritesEnabled == m_bUsingSRGBRenderTarget))
{
// current RT matches expected state, leave state intact
return false;
}
// track the updated state
m_bUsingSRGBRenderTarget = m_DynamicState.m_bSRGBWritesEnabled;
m_hCachedRenderTarget = hRenderTargetTexture;
IDirect3DSurface* pSurface;
if (m_hCachedRenderTarget == SHADER_RENDERTARGET_BACKBUFFER)
{
if (!m_bUsingSRGBRenderTarget)
{
pSurface = m_pBackBufferSurface;
}
else
{
pSurface = m_pBackBufferSurfaceSRGB;
}
}
else
{
AssertValidTextureHandle(m_hCachedRenderTarget);
Texture_t* pTexture = &GetTexture(m_hCachedRenderTarget);
pSurface = pTexture->GetRenderTargetSurface(m_bUsingSRGBRenderTarget);
}
// the 360 does a wierd reset of some states on a SetRenderTarget()
// the viewport is a clobbered state, it may not be changed by later callers, so it MUST be put back as expected
// the other clobbered states are waiting to be discovered ... sigh
#if defined( _X360 )
D3DVIEWPORT9 viewport;
Dx9Device()->GetViewport(&viewport);
Dx9Device()->SetRenderTarget(0, pSurface);
Dx9Device()->SetViewport(&viewport);
#endif
return true;
}
//-----------------------------------------------------------------------------
// debug logging
//-----------------------------------------------------------------------------
void CShaderAPIDx8::PrintfVA(char* fmt, va_list vargs)
{
#ifdef DX_TO_GL_ABSTRACTION
#if GLMDEBUG
GLMPrintfVA(fmt, vargs);
#endif
#else
AssertOnce(!"Impl me");
#endif
}
void CShaderAPIDx8::Printf(const char* fmt, ...)
{
#ifdef DX_TO_GL_ABSTRACTION
#if GLMDEBUG
va_list vargs;
va_start(vargs, fmt);
GLMPrintfVA(fmt, vargs);
va_end(vargs);
#endif
#else
AssertOnce(!"Impl me");
#endif
}
float CShaderAPIDx8::Knob(char* knobname, float* setvalue)
{
#ifdef DX_TO_GL_ABSTRACTION
#if GLMDEBUG
return GLMKnob(knobname, setvalue);
#else
return 0.0f;
#endif
#else
return 0.0f;
#endif
}
#if defined( _X360 )
extern ConVar r_blocking_spew_threshold;
void D3DBlockingSpewCallback(DWORD Flags, D3DBLOCKTYPE BlockType, float ClockTime, DWORD ThreadTime)
{
if (ClockTime >= r_blocking_spew_threshold.GetFloat())
{
const char* pBlockType = "";
switch (BlockType)
{
case D3DBLOCKTYPE_NONE:
pBlockType = "D3DBLOCKTYPE_NONE";
break;
case D3DBLOCKTYPE_PRIMARY_OVERRUN:
pBlockType = "D3DBLOCKTYPE_PRIMARY_OVERRUN";
break;
case D3DBLOCKTYPE_SECONDARY_OVERRUN:
pBlockType = "D3DBLOCKTYPE_SECONDARY_OVERRUN";
break;
case D3DBLOCKTYPE_SWAP_THROTTLE:
pBlockType = "D3DBLOCKTYPE_SWAP_THROTTLE";
break;
case D3DBLOCKTYPE_BLOCK_UNTIL_IDLE:
pBlockType = "D3DBLOCKTYPE_BLOCK_UNTIL_IDLE";
break;
case D3DBLOCKTYPE_BLOCK_UNTIL_NOT_BUSY:
pBlockType = "D3DBLOCKTYPE_BLOCK_UNTIL_NOT_BUSY";
break;
case D3DBLOCKTYPE_BLOCK_ON_FENCE:
pBlockType = "D3DBLOCKTYPE_BLOCK_ON_FENCE";
break;
case D3DBLOCKTYPE_VERTEX_SHADER_RELEASE:
pBlockType = "D3DBLOCKTYPE_VERTEX_SHADER_RELEASE";
break;
case D3DBLOCKTYPE_PIXEL_SHADER_RELEASE:
pBlockType = "D3DBLOCKTYPE_PIXEL_SHADER_RELEASE";
break;
case D3DBLOCKTYPE_VERTEX_BUFFER_RELEASE:
pBlockType = "D3DBLOCKTYPE_VERTEX_BUFFER_RELEASE";
break;
case D3DBLOCKTYPE_VERTEX_BUFFER_LOCK:
pBlockType = "D3DBLOCKTYPE_VERTEX_BUFFER_LOCK";
break;
case D3DBLOCKTYPE_INDEX_BUFFER_RELEASE:
pBlockType = "D3DBLOCKTYPE_INDEX_BUFFER_RELEASE";
break;
case D3DBLOCKTYPE_INDEX_BUFFER_LOCK:
pBlockType = "D3DBLOCKTYPE_INDEX_BUFFER_LOCK";
break;
case D3DBLOCKTYPE_TEXTURE_RELEASE:
pBlockType = "D3DBLOCKTYPE_TEXTURE_RELEASE";
break;
case D3DBLOCKTYPE_TEXTURE_LOCK:
pBlockType = "D3DBLOCKTYPE_TEXTURE_LOCK";
break;
case D3DBLOCKTYPE_COMMAND_BUFFER_RELEASE:
pBlockType = "D3DBLOCKTYPE_COMMAND_BUFFER_RELEASE";
break;
case D3DBLOCKTYPE_COMMAND_BUFFER_LOCK:
pBlockType = "D3DBLOCKTYPE_COMMAND_BUFFER_LOCK";
break;
case D3DBLOCKTYPE_CONSTANT_BUFFER_RELEASE:
pBlockType = "D3DBLOCKTYPE_CONSTANT_BUFFER_RELEASE";
break;
case D3DBLOCKTYPE_CONSTANT_BUFFER_LOCK:
pBlockType = "D3DBLOCKTYPE_CONSTANT_BUFFER_LOCK";
break;
NO_DEFAULT;
};
Warning("D3D Block: %s for %.2f ms\n", pBlockType, ClockTime);
}
}
static void r_blocking_spew_threshold_callback(IConVar* var, const char* pOldValue, float flOldValue)
{
if (Dx9Device() != NULL)
{
if (((ConVar*)var)->GetFloat() >= 0.0f)
{
Dx9Device()->SetBlockCallback(0, D3DBlockingSpewCallback);
}
else
{
Dx9Device()->SetBlockCallback(0, NULL);
}
}
}
ConVar r_blocking_spew_threshold("r_blocking_spew_threshold", "-1", 0, "Enable spew of Direct3D Blocks. Specify the minimum blocking time in milliseconds before spewing a warning.", r_blocking_spew_threshold_callback);
#endif