mirror of
https://github.com/celisej567/cool-source-archive.git
synced 2026-09-21 20:10:15 +03:00
14351 lines
436 KiB
C++
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(¤tState.m_pVectorVertexShaderConstant[nFirstCommit],
|
|
&desiredState.m_pVectorVertexShaderConstant[nFirstCommit], nCommitCount * 4 * sizeof(float));
|
|
}
|
|
|
|
// start of new range
|
|
nFirstCommit = nVar + 1;
|
|
nCommitCount = 0;
|
|
}
|
|
else
|
|
{
|
|
++nCommitCount;
|
|
}
|
|
}
|
|
|
|
if (nCommitCount != 0)
|
|
{
|
|
// flush range
|
|
pDevice->SetVertexShaderConstantF(nFirstCommit, desiredState.m_pVectorVertexShaderConstant[nFirstCommit].Base(), nCommitCount);
|
|
|
|
memcpy(¤tState.m_pVectorVertexShaderConstant[nFirstCommit],
|
|
&desiredState.m_pVectorVertexShaderConstant[nFirstCommit], nCommitCount * 4 * sizeof(float));
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the current buffered state... (debug only)
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::GetBufferedState(BufferedState_t& state)
|
|
{
|
|
memcpy(&state.m_Transform[0], &GetTransform(MATERIAL_MODEL), sizeof(D3DXMATRIX));
|
|
memcpy(&state.m_Transform[1], &GetTransform(MATERIAL_VIEW), sizeof(D3DXMATRIX));
|
|
memcpy(&state.m_Transform[2], &GetTransform(MATERIAL_PROJECTION), sizeof(D3DXMATRIX));
|
|
memcpy(&state.m_Viewport, &m_DynamicState.m_Viewport, sizeof(state.m_Viewport));
|
|
state.m_PixelShader = ShaderManager()->GetCurrentPixelShader();
|
|
state.m_VertexShader = ShaderManager()->GetCurrentVertexShader();
|
|
for (int i = 0; i < g_pHardwareConfig->GetSamplerCount(); ++i)
|
|
{
|
|
state.m_BoundTexture[i] = m_DynamicState.m_SamplerState[i].m_BoundTexture;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// constant color methods
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CShaderAPIDx8::Color3f(float r, float g, float b)
|
|
{
|
|
unsigned int color = D3DCOLOR_ARGB(255, (int)(r * 255),
|
|
(int)(g * 255), (int)(b * 255));
|
|
if (color != m_DynamicState.m_ConstantColor)
|
|
{
|
|
m_DynamicState.m_ConstantColor = color;
|
|
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::Color4f(float r, float g, float b, float a)
|
|
{
|
|
unsigned int color = D3DCOLOR_ARGB((int)(a * 255), (int)(r * 255),
|
|
(int)(g * 255), (int)(b * 255));
|
|
if (color != m_DynamicState.m_ConstantColor)
|
|
{
|
|
m_DynamicState.m_ConstantColor = color;
|
|
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::Color3fv(float const* c)
|
|
{
|
|
Assert(c);
|
|
unsigned int color = D3DCOLOR_ARGB(255, (int)(c[0] * 255),
|
|
(int)(c[1] * 255), (int)(c[2] * 255));
|
|
if (color != m_DynamicState.m_ConstantColor)
|
|
{
|
|
m_DynamicState.m_ConstantColor = color;
|
|
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::Color4fv(float const* c)
|
|
{
|
|
Assert(c);
|
|
unsigned int color = D3DCOLOR_ARGB((int)(c[3] * 255), (int)(c[0] * 255),
|
|
(int)(c[1] * 255), (int)(c[2] * 255));
|
|
if (color != m_DynamicState.m_ConstantColor)
|
|
{
|
|
m_DynamicState.m_ConstantColor = color;
|
|
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::Color3ub(unsigned char r, unsigned char g, unsigned char b)
|
|
{
|
|
unsigned int color = D3DCOLOR_ARGB(255, r, g, b);
|
|
if (color != m_DynamicState.m_ConstantColor)
|
|
{
|
|
m_DynamicState.m_ConstantColor = color;
|
|
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::Color3ubv(unsigned char const* pColor)
|
|
{
|
|
Assert(pColor);
|
|
unsigned int color = D3DCOLOR_ARGB(255, pColor[0], pColor[1], pColor[2]);
|
|
if (color != m_DynamicState.m_ConstantColor)
|
|
{
|
|
m_DynamicState.m_ConstantColor = color;
|
|
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::Color4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
|
|
{
|
|
unsigned int color = D3DCOLOR_ARGB(a, r, g, b);
|
|
if (color != m_DynamicState.m_ConstantColor)
|
|
{
|
|
m_DynamicState.m_ConstantColor = color;
|
|
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::Color4ubv(unsigned char const* pColor)
|
|
{
|
|
Assert(pColor);
|
|
unsigned int color = D3DCOLOR_ARGB(pColor[3], pColor[0], pColor[1], pColor[2]);
|
|
if (color != m_DynamicState.m_ConstantColor)
|
|
{
|
|
m_DynamicState.m_ConstantColor = color;
|
|
SetSupportedRenderState(D3DRS_TEXTUREFACTOR, color);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The shade mode
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::ShadeMode(ShaderShadeMode_t mode)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
D3DSHADEMODE shadeMode = (mode == SHADER_FLAT) ? D3DSHADE_FLAT : D3DSHADE_GOURAUD;
|
|
if (m_DynamicState.m_ShadeMode != shadeMode)
|
|
{
|
|
m_DynamicState.m_ShadeMode = shadeMode;
|
|
SetRenderStateConstMacro(this, D3DRS_SHADEMODE, shadeMode);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Buffering 2 frames ahead
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::EnableBuffer2FramesAhead(bool bEnable)
|
|
{
|
|
#ifdef _X360
|
|
m_bBuffer2FramesAhead = bEnable;
|
|
if (bEnable != m_DynamicState.m_bBuffer2Frames)
|
|
{
|
|
SetRenderState(D3DRS_BUFFER2FRAMES, bEnable);
|
|
m_DynamicState.m_bBuffer2Frames = bEnable;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CShaderAPIDx8::SetDepthFeatheringPixelShaderConstant(int iConstant, float fDepthBlendScale)
|
|
{
|
|
float fConstantValues[4];
|
|
|
|
if (IsX360())
|
|
{
|
|
const D3DMATRIX& projMatrix = GetProjectionMatrix();
|
|
|
|
fConstantValues[0] = 50.0f / fDepthBlendScale;
|
|
fConstantValues[1] = 1.0f / projMatrix.m[2][2];
|
|
fConstantValues[2] = 1.0f / projMatrix.m[3][2];
|
|
fConstantValues[3] = projMatrix.m[2][2];
|
|
|
|
/*
|
|
D3DXMATRIX invProjMatrix;
|
|
D3DXMatrixInverse( &invProjMatrix, NULL, (D3DXMATRIX *)&projMatrix );
|
|
fConstantValues[1] = invProjMatrix.m[3][2];
|
|
fConstantValues[2] = invProjMatrix.m[3][3];
|
|
fConstantValues[3] = invProjMatrix.m[2][2];
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
fConstantValues[0] = m_DynamicState.m_DestAlphaDepthRange / fDepthBlendScale;
|
|
fConstantValues[1] = fConstantValues[2] = fConstantValues[3] = 0.0f; //empty
|
|
}
|
|
|
|
SetPixelShaderConstant(iConstant, fConstantValues);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cull mode..
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::SetCullModeState(bool bEnable, D3DCULL nDesiredCullMode)
|
|
{
|
|
D3DCULL nCullMode = bEnable ? nDesiredCullMode : D3DCULL_NONE;
|
|
if (nCullMode != m_DynamicState.m_CullMode)
|
|
{
|
|
SetRenderStateConstMacro(this, D3DRS_CULLMODE, nCullMode);
|
|
m_DynamicState.m_CullMode = nCullMode;
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::ApplyCullEnable(bool bEnable)
|
|
{
|
|
m_DynamicState.m_bCullEnabled = bEnable;
|
|
SetCullModeState(m_DynamicState.m_bCullEnabled, m_DynamicState.m_DesiredCullMode);
|
|
}
|
|
|
|
void CShaderAPIDx8::CullMode(MaterialCullMode_t nCullMode)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
D3DCULL nNewCullMode;
|
|
switch (nCullMode)
|
|
{
|
|
case MATERIAL_CULLMODE_CCW:
|
|
// Culls backfacing polys (normal)
|
|
nNewCullMode = D3DCULL_CCW;
|
|
break;
|
|
|
|
case MATERIAL_CULLMODE_CW:
|
|
// Culls frontfacing polys
|
|
nNewCullMode = D3DCULL_CW;
|
|
break;
|
|
|
|
default:
|
|
Warning("CullMode: invalid cullMode\n");
|
|
return;
|
|
}
|
|
|
|
if (m_DynamicState.m_DesiredCullMode != nNewCullMode)
|
|
{
|
|
FlushBufferedPrimitives();
|
|
m_DynamicState.m_DesiredCullMode = nNewCullMode;
|
|
SetCullModeState(m_DynamicState.m_bCullEnabled, m_DynamicState.m_DesiredCullMode);
|
|
}
|
|
}
|
|
|
|
static ConVar mat_alphacoverage("mat_alphacoverage", "1");
|
|
void CShaderAPIDx8::ApplyAlphaToCoverage(bool bEnable)
|
|
{
|
|
if (mat_alphacoverage.GetBool())
|
|
{
|
|
if (bEnable)
|
|
EnableAlphaToCoverage();
|
|
else
|
|
DisableAlphaToCoverage();
|
|
}
|
|
else
|
|
{
|
|
DisableAlphaToCoverage();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the current cull mode of the current material (for selection mode only)
|
|
//-----------------------------------------------------------------------------
|
|
D3DCULL CShaderAPIDx8::GetCullMode() const
|
|
{
|
|
Assert(m_pMaterial);
|
|
if (m_pMaterial->GetMaterialVarFlag(MATERIAL_VAR_NOCULL))
|
|
return D3DCULL_NONE;
|
|
return m_DynamicState.m_DesiredCullMode;
|
|
}
|
|
|
|
void CShaderAPIDx8::SetRasterState(const ShaderRasterState_t& state)
|
|
{
|
|
// FIXME: Implement!
|
|
}
|
|
|
|
|
|
void CShaderAPIDx8::ForceDepthFuncEquals(bool bEnable)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (!g_pShaderDeviceDx8->IsDeactivated())
|
|
{
|
|
m_TransitionTable.ForceDepthFuncEquals(bEnable);
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::OverrideDepthEnable(bool bEnable, bool bDepthEnable)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (!g_pShaderDeviceDx8->IsDeactivated())
|
|
{
|
|
m_TransitionTable.OverrideDepthEnable(bEnable, bDepthEnable);
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::OverrideAlphaWriteEnable(bool bOverrideEnable, bool bAlphaWriteEnable)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (!g_pShaderDeviceDx8->IsDeactivated())
|
|
{
|
|
m_TransitionTable.OverrideAlphaWriteEnable(bOverrideEnable, bAlphaWriteEnable);
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::OverrideColorWriteEnable(bool bOverrideEnable, bool bColorWriteEnable)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (!g_pShaderDeviceDx8->IsDeactivated())
|
|
{
|
|
m_TransitionTable.OverrideColorWriteEnable(bOverrideEnable, bColorWriteEnable);
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::UpdateFastClipUserClipPlane(void)
|
|
{
|
|
float plane[4];
|
|
switch (m_DynamicState.m_HeightClipMode)
|
|
{
|
|
case MATERIAL_HEIGHTCLIPMODE_DISABLE:
|
|
EnableFastClip(false);
|
|
break;
|
|
case MATERIAL_HEIGHTCLIPMODE_RENDER_ABOVE_HEIGHT:
|
|
plane[0] = 0.0f;
|
|
plane[1] = 0.0f;
|
|
plane[2] = 1.0f;
|
|
plane[3] = m_DynamicState.m_HeightClipZ;
|
|
EnableFastClip(true);
|
|
SetFastClipPlane(plane);
|
|
break;
|
|
case MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT:
|
|
plane[0] = 0.0f;
|
|
plane[1] = 0.0f;
|
|
plane[2] = -1.0f;
|
|
plane[3] = -m_DynamicState.m_HeightClipZ;
|
|
EnableFastClip(true);
|
|
SetFastClipPlane(plane);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::SetHeightClipZ(float z)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (z != m_DynamicState.m_HeightClipZ)
|
|
{
|
|
FlushBufferedPrimitives();
|
|
m_DynamicState.m_HeightClipZ = z;
|
|
UpdateVertexShaderFogParams();
|
|
UpdateFastClipUserClipPlane();
|
|
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |=
|
|
STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION;
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::SetHeightClipMode(MaterialHeightClipMode_t heightClipMode)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (heightClipMode != m_DynamicState.m_HeightClipMode)
|
|
{
|
|
FlushBufferedPrimitives();
|
|
m_DynamicState.m_HeightClipMode = heightClipMode;
|
|
UpdateVertexShaderFogParams();
|
|
UpdateFastClipUserClipPlane();
|
|
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |=
|
|
STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION;
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::SetClipPlane(int index, const float* pPlane)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
Assert(index < g_pHardwareConfig->MaxUserClipPlanes() && index >= 0);
|
|
|
|
// NOTE: The plane here is specified in *world space*
|
|
// NOTE: This is done because they assume Ax+By+Cz+Dw = 0 (where w = 1 in real space)
|
|
// while we use Ax+By+Cz=D
|
|
D3DXPLANE plane;
|
|
plane.a = pPlane[0];
|
|
plane.b = pPlane[1];
|
|
plane.c = pPlane[2];
|
|
plane.d = -pPlane[3];
|
|
|
|
if (plane != m_DynamicState.m_UserClipPlaneWorld[index])
|
|
{
|
|
FlushBufferedPrimitives();
|
|
|
|
m_DynamicState.m_UserClipPlaneChanged |= (1 << index);
|
|
m_DynamicState.m_UserClipPlaneWorld[index] = plane;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Converts a D3DXMatrix to a VMatrix and back
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::D3DXMatrixToVMatrix(const D3DXMATRIX& in, VMatrix& out)
|
|
{
|
|
MatrixTranspose(*(const VMatrix*)&in, out);
|
|
}
|
|
|
|
void CShaderAPIDx8::VMatrixToD3DXMatrix(const VMatrix& in, D3DXMATRIX& out)
|
|
{
|
|
MatrixTranspose(in, *(VMatrix*)&out);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Mark all user clip planes as being dirty
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::MarkAllUserClipPlanesDirty()
|
|
{
|
|
m_DynamicState.m_UserClipPlaneChanged |= (1 << g_pHardwareConfig->MaxUserClipPlanes()) - 1;
|
|
m_DynamicState.m_bFastClipPlaneChanged = true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// User clip plane override
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::EnableUserClipTransformOverride(bool bEnable)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (m_DynamicState.m_bUserClipTransformOverride != bEnable)
|
|
{
|
|
FlushBufferedPrimitives();
|
|
m_DynamicState.m_bUserClipTransformOverride = bEnable;
|
|
MarkAllUserClipPlanesDirty();
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Specify user clip transform
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::UserClipTransform(const VMatrix& worldToProjection)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
D3DXMATRIX dxWorldToProjection;
|
|
VMatrixToD3DXMatrix(worldToProjection, dxWorldToProjection);
|
|
|
|
if (m_DynamicState.m_UserClipTransform != dxWorldToProjection)
|
|
{
|
|
m_DynamicState.m_UserClipTransform = dxWorldToProjection;
|
|
if (m_DynamicState.m_bUserClipTransformOverride)
|
|
{
|
|
FlushBufferedPrimitives();
|
|
MarkAllUserClipPlanesDirty();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Enables a user clip plane
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::EnableClipPlane(int index, bool bEnable)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
Assert(index < g_pHardwareConfig->MaxUserClipPlanes() && index >= 0);
|
|
if ((m_DynamicState.m_UserClipPlaneEnabled & (1 << index) ? true : false) != bEnable)
|
|
{
|
|
FlushBufferedPrimitives();
|
|
if (bEnable)
|
|
{
|
|
m_DynamicState.m_UserClipPlaneEnabled |= (1 << index);
|
|
}
|
|
else
|
|
{
|
|
m_DynamicState.m_UserClipPlaneEnabled &= ~(1 << index);
|
|
}
|
|
SetRenderStateConstMacro(this, D3DRS_CLIPPLANEENABLE, m_DynamicState.m_UserClipPlaneEnabled);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Recomputes the fast-clip plane matrices based on the current fast-clip plane
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::CommitFastClipPlane()
|
|
{
|
|
// Don't bother recomputing if unchanged or disabled
|
|
if (!m_DynamicState.m_bFastClipPlaneChanged || !m_DynamicState.m_FastClipEnabled)
|
|
return;
|
|
|
|
m_DynamicState.m_bFastClipPlaneChanged = false;
|
|
|
|
D3DXMatrixIdentity(&m_CachedFastClipProjectionMatrix);
|
|
|
|
// Compute worldToProjection - need inv. transpose for transforming plane.
|
|
D3DXMATRIX viewToProjInvTrans, viewToProjInv, viewToProj = GetTransform(MATERIAL_PROJECTION);
|
|
viewToProj._43 *= 0.5f; // pull in zNear because the shear in effect
|
|
// moves it out: clipping artifacts when looking down at water
|
|
// could occur if this multiply is not done
|
|
|
|
D3DXMATRIX worldToViewInvTrans, worldToViewInv, worldToView = GetUserClipTransform();
|
|
|
|
D3DXMatrixInverse(&worldToViewInv, NULL, &worldToView);
|
|
D3DXMatrixTranspose(&worldToViewInvTrans, &worldToViewInv);
|
|
|
|
D3DXMatrixInverse(&viewToProjInv, NULL, &viewToProj);
|
|
D3DXMatrixTranspose(&viewToProjInvTrans, &viewToProjInv);
|
|
|
|
D3DXPLANE plane;
|
|
D3DXPlaneNormalize(&plane, &m_DynamicState.m_FastClipPlane);
|
|
D3DXVECTOR4 clipPlane(plane.a, plane.b, plane.c, plane.d);
|
|
|
|
// transform clip plane into view space
|
|
D3DXVec4Transform(&clipPlane, &clipPlane, &worldToViewInvTrans);
|
|
|
|
// transform clip plane into projection space
|
|
D3DXVec4Transform(&clipPlane, &clipPlane, &viewToProjInvTrans);
|
|
|
|
#define ALLOW_FOR_FASTCLIPDUMPS 0
|
|
|
|
#if (ALLOW_FOR_FASTCLIPDUMPS == 1)
|
|
static ConVar shader_dumpfastclipprojectioncoords("shader_dumpfastclipprojectioncoords", "0", 0, "dump fast clip projected matrix");
|
|
if (shader_dumpfastclipprojectioncoords.GetBool())
|
|
DevMsg("Fast clip plane projected coordinates: %f %f %f %f", clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w);
|
|
#endif
|
|
|
|
if ((clipPlane.z * clipPlane.w) <= -0.4f) // a plane with (z*w) > -0.4 at this point is behind the camera and will cause graphical glitches. Toss it. (0.4 found through experimentation)
|
|
{
|
|
#if (ALLOW_FOR_FASTCLIPDUMPS == 1)
|
|
if (shader_dumpfastclipprojectioncoords.GetBool())
|
|
DevMsg(" %f %f %f %f\n", clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w);
|
|
#endif
|
|
|
|
D3DXVec4Normalize(&clipPlane, &clipPlane);
|
|
|
|
//if ((fabs(clipPlane.z) > 0.01) && (fabs(clipPlane.w) > 0.01f))
|
|
{
|
|
// put projection space clip plane in Z column
|
|
m_CachedFastClipProjectionMatrix._13 = clipPlane.x;
|
|
m_CachedFastClipProjectionMatrix._23 = clipPlane.y;
|
|
m_CachedFastClipProjectionMatrix._33 = clipPlane.z;
|
|
m_CachedFastClipProjectionMatrix._43 = clipPlane.w;
|
|
}
|
|
}
|
|
#if (ALLOW_FOR_FASTCLIPDUMPS == 1)
|
|
else
|
|
{
|
|
if (shader_dumpfastclipprojectioncoords.GetBool())
|
|
DevMsg("\n"); //finish off the line above
|
|
}
|
|
#endif
|
|
|
|
m_CachedFastClipProjectionMatrix = viewToProj * m_CachedFastClipProjectionMatrix;
|
|
|
|
// Update the cached polyoffset matrix (with clip) too:
|
|
ComputePolyOffsetMatrix(m_CachedFastClipProjectionMatrix, m_CachedFastClipPolyOffsetProjectionMatrix);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the fast-clip plane; but doesn't update the matrices
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::SetFastClipPlane(const float* pPlane)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
D3DXPLANE plane;
|
|
plane.a = pPlane[0];
|
|
plane.b = pPlane[1];
|
|
plane.c = pPlane[2];
|
|
plane.d = -pPlane[3];
|
|
if (plane != m_DynamicState.m_FastClipPlane)
|
|
{
|
|
FlushBufferedPrimitives();
|
|
UpdateVertexShaderFogParams();
|
|
|
|
m_DynamicState.m_FastClipPlane = plane;
|
|
|
|
// Mark a dirty bit so when it comes time to commit view + projection transforms,
|
|
// we also update the fast clip matrices
|
|
m_DynamicState.m_bFastClipPlaneChanged = true;
|
|
|
|
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |=
|
|
STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Enables/disables fast-clip mode
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::EnableFastClip(bool bEnable)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (m_DynamicState.m_FastClipEnabled != bEnable)
|
|
{
|
|
FlushBufferedPrimitives();
|
|
UpdateVertexShaderFogParams();
|
|
|
|
m_DynamicState.m_FastClipEnabled = bEnable;
|
|
|
|
m_DynamicState.m_TransformChanged[MATERIAL_PROJECTION] |=
|
|
STATE_CHANGED_VERTEX_SHADER | STATE_CHANGED_FIXED_FUNCTION;
|
|
}
|
|
}
|
|
|
|
/*
|
|
// -----------------------------------------------------------------------------
|
|
// SetInvariantClipVolume - This routine takes six planes as input and sets the
|
|
// appropriate Direct3D user clip plane state
|
|
// What we mean by "invariant clipping" here is that certain devices implement
|
|
// user clip planes at the raster level, which means that multi-pass rendering
|
|
// where one pass is unclipped (such as base geometry) and another pass *IS*
|
|
// clipped (such as flashlight geometry), there is no z-fighting since the
|
|
// clipping is implemented at the raster level in an "invariant" way
|
|
// -----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::SetInvariantClipVolume( Frustum_t *pFrustumPlanes )
|
|
{
|
|
// Only do this on modern nVidia hardware, which does invariant clipping
|
|
if ( m_VendorID == VENDORID_NVIDIA )
|
|
{
|
|
if ( pFrustumPlanes )
|
|
{
|
|
// if ()
|
|
// {
|
|
//
|
|
// }
|
|
|
|
for (int i=0; i<6; i++)
|
|
{
|
|
const cplane_t *pPlane = pFrustumPlanes->GetPlane(i);
|
|
|
|
SetClipPlane( i, (float *) &pPlane->normal );
|
|
EnableClipPlane( i, true );
|
|
|
|
// FRUSTUM_RIGHT = 0,
|
|
// FRUSTUM_LEFT = 1,
|
|
// FRUSTUM_TOP = 2,
|
|
// FRUSTUM_BOTTOM = 3,
|
|
// FRUSTUM_NEARZ = 4,
|
|
// FRUSTUM_FARZ = 5,
|
|
|
|
}
|
|
}
|
|
else // NULL disables the invariant clip volume...
|
|
{
|
|
for (int i=0; i<6; i++)
|
|
{
|
|
EnableClipPlane( i, false );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Vertex blending
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::SetVertexBlendState(int numBones)
|
|
{
|
|
if (numBones < 0)
|
|
{
|
|
numBones = m_DynamicState.m_NumBones;
|
|
}
|
|
|
|
// For fixed-function, the number of weights is actually one less than
|
|
// the number of bones
|
|
if (numBones > 0)
|
|
--numBones;
|
|
|
|
bool normalizeNormals = true;
|
|
D3DVERTEXBLENDFLAGS vertexBlend;
|
|
switch (numBones)
|
|
{
|
|
case 0:
|
|
vertexBlend = D3DVBF_DISABLE;
|
|
normalizeNormals = false;
|
|
break;
|
|
|
|
case 1:
|
|
vertexBlend = D3DVBF_1WEIGHTS;
|
|
break;
|
|
|
|
case 2:
|
|
vertexBlend = D3DVBF_2WEIGHTS;
|
|
break;
|
|
|
|
case 3:
|
|
vertexBlend = D3DVBF_3WEIGHTS;
|
|
break;
|
|
|
|
default:
|
|
vertexBlend = D3DVBF_DISABLE;
|
|
Assert(0);
|
|
break;
|
|
}
|
|
|
|
if (m_DynamicState.m_VertexBlend != vertexBlend)
|
|
{
|
|
m_DynamicState.m_VertexBlend = vertexBlend;
|
|
SetSupportedRenderState(D3DRS_VERTEXBLEND, vertexBlend);
|
|
}
|
|
|
|
// Activate normalize normals when skinning is on
|
|
if (m_DynamicState.m_NormalizeNormals != normalizeNormals)
|
|
{
|
|
m_DynamicState.m_NormalizeNormals = normalizeNormals;
|
|
SetSupportedRenderState(D3DRS_NORMALIZENORMALS, normalizeNormals);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Vertex blending
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::SetNumBoneWeights(int numBones)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (m_DynamicState.m_NumBones != numBones)
|
|
{
|
|
FlushBufferedPrimitives();
|
|
m_DynamicState.m_NumBones = numBones;
|
|
|
|
if (m_TransitionTable.CurrentShadowState())
|
|
{
|
|
SetVertexBlendState(m_TransitionTable.CurrentShadowState()->m_VertexBlendEnable ? -1 : 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::EnableHWMorphing(bool bEnable)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (m_DynamicState.m_bHWMorphingEnabled != bEnable)
|
|
{
|
|
FlushBufferedPrimitives();
|
|
m_DynamicState.m_bHWMorphingEnabled = bEnable;
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::EnabledSRGBWrite(bool bEnabled)
|
|
{
|
|
m_DynamicState.m_bSRGBWritesEnabled = bEnabled;
|
|
|
|
if (g_pHardwareConfig->SupportsPixelShaders_2_b())
|
|
{
|
|
UpdatePixelFogColorConstant();
|
|
|
|
if (bEnabled && g_pHardwareConfig->NeedsShaderSRGBConversion())
|
|
BindTexture(SHADER_SAMPLER15, m_hLinearToGammaTableTexture);
|
|
else
|
|
BindTexture(SHADER_SAMPLER15, m_hLinearToGammaTableIdentityTexture);
|
|
}
|
|
}
|
|
|
|
#if defined( _X360 )
|
|
void CShaderAPIDx8::ApplySRGBReadState(int iTextureStage, bool bSRGBReadEnabled)
|
|
{
|
|
Sampler_t sampler = (Sampler_t)iTextureStage;
|
|
SamplerState_t& samplerState = SamplerState(sampler);
|
|
samplerState.m_SRGBReadEnable = bSRGBReadEnabled;
|
|
|
|
if ((samplerState.m_BoundTexture == INVALID_SHADERAPI_TEXTURE_HANDLE) || !samplerState.m_TextureEnable)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IDirect3DBaseTexture* pBindTexture = CShaderAPIDx8::GetD3DTexture(samplerState.m_BoundTexture);
|
|
if (!pBindTexture)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DWORD linearFormatBackup = pBindTexture->Format.dword[0]; //if we convert to srgb format, we need the original format for reverting. We only need the first DWORD of GPUTEXTURE_FETCH_CONSTANT.
|
|
if (bSRGBReadEnabled)
|
|
{
|
|
pBindTexture->Format.SignX = pBindTexture->Format.SignY = pBindTexture->Format.SignZ = 3; //convert to srgb format for the bind. This effectively emulates the old srgb read sampler state
|
|
}
|
|
|
|
Dx9Device()->SetTexture(sampler, pBindTexture);
|
|
|
|
// copy back the format in case we changed it
|
|
pBindTexture->Format.dword[0] = linearFormatBackup;
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Fog methods...
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::UpdatePixelFogColorConstant(void)
|
|
{
|
|
Assert(HardwareConfig()->SupportsPixelShaders_2_b());
|
|
float fogColor[4];
|
|
|
|
switch (GetPixelFogMode())
|
|
{
|
|
case MATERIAL_FOG_NONE:
|
|
{
|
|
for (int i = 0; i != 3; ++i)
|
|
fogColor[i] = 0.0f;
|
|
}
|
|
break;
|
|
|
|
case MATERIAL_FOG_LINEAR:
|
|
{
|
|
//setup the fog for mixing linear fog in the pixel shader so that it emulates ff range fog
|
|
for (int i = 0; i != 3; ++i)
|
|
fogColor[i] = m_DynamicState.m_PixelFogColor[i];
|
|
|
|
if (m_DynamicState.m_bSRGBWritesEnabled)
|
|
{
|
|
//since the fog color will assuredly get converted from linear to gamma, we should probably convert it from gamma to linear
|
|
for (int i = 0; i != 3; ++i)
|
|
fogColor[i] = GammaToLinear_HardwareSpecific(fogColor[i]);
|
|
}
|
|
|
|
if ((!m_DynamicState.m_bFogGammaCorrectionDisabled) && (g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER))
|
|
{
|
|
for (int i = 0; i != 3; ++i)
|
|
fogColor[i] *= m_ToneMappingScale.x; // Linear
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MATERIAL_FOG_LINEAR_BELOW_FOG_Z:
|
|
{
|
|
//water fog has been around a while and has never tonemap scaled, and has always been in linear space
|
|
if (g_pHardwareConfig->NeedsShaderSRGBConversion())
|
|
{
|
|
//srgb in ps2b uses the 2.2 curve
|
|
for (int i = 0; i != 3; ++i)
|
|
fogColor[i] = pow(m_DynamicState.m_PixelFogColor[i], 2.2f);
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i != 3; ++i)
|
|
fogColor[i] = GammaToLinear_HardwareSpecific(m_DynamicState.m_PixelFogColor[i]); //this is how water fog color has always been setup in the past
|
|
}
|
|
|
|
if ((!m_DynamicState.m_bFogGammaCorrectionDisabled) && (g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER))
|
|
{
|
|
for (int i = 0; i != 3; ++i)
|
|
fogColor[i] *= m_ToneMappingScale.x; // Linear
|
|
}
|
|
}
|
|
break;
|
|
|
|
NO_DEFAULT;
|
|
};
|
|
|
|
fogColor[3] = 1.0f / m_DynamicState.m_DestAlphaDepthRange;
|
|
|
|
SetPixelShaderConstant(LINEAR_FOG_COLOR, fogColor);
|
|
}
|
|
|
|
|
|
void CShaderAPIDx8::ApplyFogMode(ShaderFogMode_t fogMode, bool bSRGBWritesEnabled, bool bDisableFogGammaCorrection)
|
|
{
|
|
HDRType_t hdrType = g_pHardwareConfig->GetHDRType();
|
|
|
|
if (fogMode == SHADER_FOGMODE_DISABLED)
|
|
{
|
|
if (hdrType != HDR_TYPE_FLOAT)
|
|
{
|
|
FogMode(MATERIAL_FOG_NONE);
|
|
}
|
|
|
|
if (m_DelayedShaderConstants.iPixelShaderFogParams != -1)
|
|
SetPixelShaderFogParams(m_DelayedShaderConstants.iPixelShaderFogParams, fogMode);
|
|
|
|
return;
|
|
}
|
|
|
|
bool bShouldGammaCorrect = true; // By default, we'll gamma correct.
|
|
unsigned char r = 0, g = 0, b = 0; // Black fog
|
|
|
|
|
|
if (hdrType != HDR_TYPE_FLOAT)
|
|
{
|
|
FogMode(m_SceneFogMode);
|
|
}
|
|
|
|
if (m_DelayedShaderConstants.iPixelShaderFogParams != -1)
|
|
SetPixelShaderFogParams(m_DelayedShaderConstants.iPixelShaderFogParams, fogMode);
|
|
|
|
switch (fogMode)
|
|
{
|
|
case SHADER_FOGMODE_BLACK: // Additive decals
|
|
bShouldGammaCorrect = false;
|
|
break;
|
|
case SHADER_FOGMODE_OO_OVERBRIGHT:
|
|
case SHADER_FOGMODE_GREY: // Mod2x decals
|
|
r = g = b = 128;
|
|
break;
|
|
case SHADER_FOGMODE_WHITE: // Multiplicative decals
|
|
r = g = b = 255;
|
|
bShouldGammaCorrect = false;
|
|
break;
|
|
case SHADER_FOGMODE_FOGCOLOR:
|
|
GetSceneFogColor(&r, &g, &b); // Scene fog color
|
|
break;
|
|
NO_DEFAULT
|
|
}
|
|
|
|
bShouldGammaCorrect &= !bDisableFogGammaCorrection;
|
|
m_DynamicState.m_bFogGammaCorrectionDisabled = !bShouldGammaCorrect;
|
|
|
|
D3DCOLOR color;
|
|
if (bShouldGammaCorrect)
|
|
{
|
|
color = ComputeGammaCorrectedFogColor(r, g, b, bSRGBWritesEnabled);
|
|
}
|
|
else
|
|
{
|
|
color = D3DCOLOR_ARGB(255, r, g, b);
|
|
}
|
|
|
|
|
|
const float fColorScale = 1.0f / 255.0f;
|
|
m_DynamicState.m_PixelFogColor[0] = (float)(r)*fColorScale;
|
|
m_DynamicState.m_PixelFogColor[1] = (float)(g)*fColorScale;
|
|
m_DynamicState.m_PixelFogColor[2] = (float)(b)*fColorScale;
|
|
|
|
if (g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_b())
|
|
{
|
|
UpdatePixelFogColorConstant();
|
|
}
|
|
|
|
if (color != m_DynamicState.m_FogColor)
|
|
{
|
|
if (hdrType != HDR_TYPE_FLOAT)
|
|
{
|
|
m_DynamicState.m_FogColor = color;
|
|
SetRenderStateConstMacro(this, D3DRS_FOGCOLOR, m_DynamicState.m_FogColor);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::SceneFogMode(MaterialFogMode_t fogMode)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (m_SceneFogMode != fogMode)
|
|
{
|
|
FlushBufferedPrimitives();
|
|
m_SceneFogMode = fogMode;
|
|
|
|
if (m_TransitionTable.CurrentShadowState())
|
|
{
|
|
// Get the shadow state in sync since it depends on SceneFogMode.
|
|
ApplyFogMode(m_TransitionTable.CurrentShadowState()->m_FogMode, m_TransitionTable.CurrentShadowState()->m_SRGBWriteEnable, m_TransitionTable.CurrentShadowState()->m_bDisableFogGammaCorrection);
|
|
}
|
|
}
|
|
}
|
|
|
|
MaterialFogMode_t CShaderAPIDx8::GetSceneFogMode()
|
|
{
|
|
return m_SceneFogMode;
|
|
}
|
|
|
|
MaterialFogMode_t CShaderAPIDx8::GetPixelFogMode()
|
|
{
|
|
if (ShouldUsePixelFogForMode(m_SceneFogMode))
|
|
return m_SceneFogMode;
|
|
else
|
|
return MATERIAL_FOG_NONE;
|
|
}
|
|
|
|
int CShaderAPIDx8::GetPixelFogCombo(void)
|
|
{
|
|
if ((m_SceneFogMode != MATERIAL_FOG_NONE) && ShouldUsePixelFogForMode(m_SceneFogMode))
|
|
return m_SceneFogMode - 1; //PIXELFOGTYPE dynamic combos are shifted down one. MATERIAL_FOG_NONE is simulated by range fog with rigged parameters. Gets rid of a dynamic combo across the ps2x set
|
|
else
|
|
return MATERIAL_FOG_NONE;
|
|
}
|
|
|
|
|
|
static ConVar r_pixelfog("r_pixelfog", "1");
|
|
|
|
bool CShaderAPIDx8::ShouldUsePixelFogForMode(MaterialFogMode_t fogMode)
|
|
{
|
|
if (fogMode == MATERIAL_FOG_NONE)
|
|
return false;
|
|
|
|
if (IsX360() || IsPosix()) // Always use pixel fog on X360 and Posix
|
|
return true;
|
|
|
|
if (g_pHardwareConfig->Caps().m_nDXSupportLevel < 90) //pixel fog not available until at least ps2.0
|
|
return false;
|
|
|
|
switch (m_SceneFogMode)
|
|
{
|
|
case MATERIAL_FOG_LINEAR:
|
|
return (g_pHardwareConfig->SupportsPixelShaders_2_b() && //lightmappedgeneric_ps20.fxc can't handle the instruction count
|
|
r_pixelfog.GetBool()); //use pixel fog if preferred
|
|
|
|
case MATERIAL_FOG_LINEAR_BELOW_FOG_Z:
|
|
return true;
|
|
};
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Fog methods...
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::FogMode(MaterialFogMode_t fogMode)
|
|
{
|
|
bool bFogEnable;
|
|
|
|
if (IsX360())
|
|
{
|
|
// FF fog not applicable on 360
|
|
return;
|
|
}
|
|
|
|
m_DynamicState.m_SceneFog = fogMode;
|
|
switch (fogMode)
|
|
{
|
|
default:
|
|
Assert(0);
|
|
// fall through
|
|
|
|
case MATERIAL_FOG_NONE:
|
|
bFogEnable = false;
|
|
break;
|
|
|
|
case MATERIAL_FOG_LINEAR:
|
|
// use vertex fog to achieve linear range fog
|
|
bFogEnable = true;
|
|
break;
|
|
|
|
case MATERIAL_FOG_LINEAR_BELOW_FOG_Z:
|
|
// use pixel fog on 9.0 and greater for height fog
|
|
bFogEnable = g_pHardwareConfig->Caps().m_nDXSupportLevel < 90;
|
|
break;
|
|
}
|
|
|
|
if (ShouldUsePixelFogForMode(fogMode))
|
|
{
|
|
bFogEnable = false; //disable FF fog when doing fog in the pixel shader
|
|
}
|
|
|
|
#if 0
|
|
// HACK - do this to disable fog always
|
|
bFogEnable = false;
|
|
m_DynamicState.m_SceneFog = MATERIAL_FOG_NONE;
|
|
#endif
|
|
|
|
// These two are always set to this, so don't bother setting them.
|
|
// We are always using vertex fog.
|
|
// SetRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE );
|
|
// SetRenderState( D3DRS_RANGEFOGENABLE, false );
|
|
|
|
// Set fog enable if it's different than before.
|
|
if (bFogEnable != m_DynamicState.m_FogEnable)
|
|
{
|
|
SetSupportedRenderState(D3DRS_FOGENABLE, bFogEnable);
|
|
m_DynamicState.m_FogEnable = bFogEnable;
|
|
}
|
|
}
|
|
|
|
|
|
void CShaderAPIDx8::FogStart(float fStart)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (fStart != m_DynamicState.m_FogStart)
|
|
{
|
|
// need to flush the dynamic buffer
|
|
FlushBufferedPrimitives();
|
|
|
|
SetRenderStateConstMacro(this, D3DRS_FOGSTART, *((DWORD*)(&fStart)));
|
|
m_VertexShaderFogParams[0] = fStart;
|
|
UpdateVertexShaderFogParams();
|
|
m_DynamicState.m_FogStart = fStart;
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::FogEnd(float fEnd)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (fEnd != m_DynamicState.m_FogEnd)
|
|
{
|
|
// need to flush the dynamic buffer
|
|
FlushBufferedPrimitives();
|
|
|
|
SetRenderStateConstMacro(this, D3DRS_FOGEND, *((DWORD*)(&fEnd)));
|
|
m_VertexShaderFogParams[1] = fEnd;
|
|
UpdateVertexShaderFogParams();
|
|
m_DynamicState.m_FogEnd = fEnd;
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::SetFogZ(float fogZ)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (fogZ != m_DynamicState.m_FogZ)
|
|
{
|
|
// need to flush the dynamic buffer
|
|
FlushBufferedPrimitives();
|
|
m_DynamicState.m_FogZ = fogZ;
|
|
UpdateVertexShaderFogParams();
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::FogMaxDensity(float flMaxDensity)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (flMaxDensity != m_DynamicState.m_FogMaxDensity)
|
|
{
|
|
// need to flush the dynamic buffer
|
|
FlushBufferedPrimitives();
|
|
|
|
// SetRenderState(D3DRS_FOGDENSITY, *((DWORD*)(&flMaxDensity))); // ??? do I need to to this ???
|
|
m_flFogMaxDensity = flMaxDensity;
|
|
UpdateVertexShaderFogParams();
|
|
m_DynamicState.m_FogMaxDensity = flMaxDensity;
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::GetFogDistances(float* fStart, float* fEnd, float* fFogZ)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (fStart)
|
|
*fStart = m_DynamicState.m_FogStart;
|
|
|
|
if (fEnd)
|
|
*fEnd = m_DynamicState.m_FogEnd;
|
|
|
|
if (fFogZ)
|
|
*fFogZ = m_DynamicState.m_FogZ;
|
|
}
|
|
|
|
void CShaderAPIDx8::SceneFogColor3ub(unsigned char r, unsigned char g, unsigned char b)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (m_SceneFogColor[0] != r || m_SceneFogColor[1] != g || m_SceneFogColor[2] != b)
|
|
{
|
|
FlushBufferedPrimitives();
|
|
m_SceneFogColor[0] = r;
|
|
m_SceneFogColor[1] = g;
|
|
m_SceneFogColor[2] = b;
|
|
|
|
if (m_TransitionTable.CurrentShadowState())
|
|
{
|
|
ApplyFogMode(m_TransitionTable.CurrentShadowState()->m_FogMode, m_TransitionTable.CurrentShadowState()->m_SRGBWriteEnable, m_TransitionTable.CurrentShadowState()->m_bDisableFogGammaCorrection);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::GetSceneFogColor(unsigned char* rgb)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
rgb[0] = m_SceneFogColor[0];
|
|
rgb[1] = m_SceneFogColor[1];
|
|
rgb[2] = m_SceneFogColor[2];
|
|
}
|
|
|
|
void CShaderAPIDx8::GetSceneFogColor(unsigned char* r, unsigned char* g, unsigned char* b)
|
|
{
|
|
*r = m_SceneFogColor[0];
|
|
*g = m_SceneFogColor[1];
|
|
*b = m_SceneFogColor[2];
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gamma correction of fog color, or not...
|
|
//-----------------------------------------------------------------------------
|
|
D3DCOLOR CShaderAPIDx8::ComputeGammaCorrectedFogColor(unsigned char r, unsigned char g, unsigned char b, bool bSRGBWritesEnabled)
|
|
{
|
|
#ifdef _DEBUG
|
|
if (g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT && !bSRGBWritesEnabled)
|
|
{
|
|
// Assert( 0 );
|
|
}
|
|
#endif
|
|
bool bLinearSpace = g_pHardwareConfig->Caps().m_bFogColorAlwaysLinearSpace ||
|
|
(bSRGBWritesEnabled && (g_pHardwareConfig->Caps().m_bFogColorSpecifiedInLinearSpace || g_pHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT));
|
|
|
|
bool bScaleFogByToneMappingScale = g_pHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER;
|
|
|
|
float fr = (r / 255.0f);
|
|
float fg = (g / 255.0f);
|
|
float fb = (b / 255.0f);
|
|
if (bLinearSpace)
|
|
{
|
|
fr = GammaToLinear(fr);
|
|
fg = GammaToLinear(fg);
|
|
fb = GammaToLinear(fb);
|
|
if (bScaleFogByToneMappingScale)
|
|
{
|
|
fr *= m_ToneMappingScale.x; //
|
|
fg *= m_ToneMappingScale.x; // Linear
|
|
fb *= m_ToneMappingScale.x; //
|
|
}
|
|
}
|
|
else if (bScaleFogByToneMappingScale)
|
|
{
|
|
fr *= m_ToneMappingScale.w; //
|
|
fg *= m_ToneMappingScale.w; // Gamma
|
|
fb *= m_ToneMappingScale.w; //
|
|
}
|
|
|
|
fr = min(fr, 1.0f);
|
|
fg = min(fg, 1.0f);
|
|
fb = min(fb, 1.0f);
|
|
r = (int)(fr * 255);
|
|
g = (int)(fg * 255);
|
|
b = (int)(fb * 255);
|
|
return D3DCOLOR_ARGB(255, r, g, b);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Some methods chaining vertex + pixel shaders through to the shader manager
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::SetVertexShaderIndex(int vshIndex)
|
|
{
|
|
ShaderManager()->SetVertexShaderIndex(vshIndex);
|
|
}
|
|
|
|
void CShaderAPIDx8::SetPixelShaderIndex(int pshIndex)
|
|
{
|
|
ShaderManager()->SetPixelShaderIndex(pshIndex);
|
|
}
|
|
|
|
void CShaderAPIDx8::SyncToken(const char* pToken)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
RECORD_COMMAND(DX8_SYNC_TOKEN, 1);
|
|
RECORD_STRING(pToken);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Deals with vertex buffers
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::DestroyVertexBuffers(bool bExitingLevel)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
MeshMgr()->DestroyVertexBuffers();
|
|
// After a map is shut down, we switch to using smaller dynamic VBs
|
|
// (VGUI shouldn't need much), so that we have more memory free during map loading
|
|
m_nDynamicVBSize = bExitingLevel ? DYNAMIC_VERTEX_BUFFER_MEMORY_SMALL : DYNAMIC_VERTEX_BUFFER_MEMORY;
|
|
}
|
|
|
|
int CShaderAPIDx8::GetCurrentDynamicVBSize(void)
|
|
{
|
|
return m_nDynamicVBSize;
|
|
}
|
|
|
|
FORCEINLINE void CShaderAPIDx8::SetVertexShaderConstantInternal(int var, float const* pVec, int numVecs, bool bForce)
|
|
{
|
|
Assert(pVec);
|
|
|
|
// DX8 asm shaders use a constant mapping which has transforms and vertex shader
|
|
// specific constants shifted down by 10 constants (two 5-constant light structures)
|
|
if (IsPC())
|
|
{
|
|
if ((g_pHardwareConfig->Caps().m_nDXSupportLevel < 90) && (var >= VERTEX_SHADER_MODULATION_COLOR))
|
|
{
|
|
var -= 10;
|
|
}
|
|
Assert(var + numVecs <= g_pHardwareConfig->NumVertexShaderConstants());
|
|
|
|
if (!bForce)
|
|
{
|
|
int skip = 0;
|
|
numVecs = AdjustUpdateRange(pVec, &m_DesiredState.m_pVectorVertexShaderConstant[var], numVecs, &skip);
|
|
if (!numVecs)
|
|
return;
|
|
var += skip;
|
|
pVec += skip * 4;
|
|
}
|
|
Dx9Device()->SetVertexShaderConstantF(var, pVec, numVecs);
|
|
memcpy(&m_DynamicState.m_pVectorVertexShaderConstant[var], pVec, numVecs * 4 * sizeof(float));
|
|
}
|
|
else
|
|
{
|
|
Assert(var + numVecs <= g_pHardwareConfig->NumVertexShaderConstants());
|
|
}
|
|
|
|
memcpy(&m_DesiredState.m_pVectorVertexShaderConstant[var], pVec, numVecs * 4 * sizeof(float));
|
|
|
|
if (IsX360())
|
|
{
|
|
m_MaxVectorVertexShaderConstant = max(m_MaxVectorVertexShaderConstant, var + numVecs);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the constant register for vertex and pixel shaders
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::SetVertexShaderConstant(int var, float const* pVec, int numVecs, bool bForce)
|
|
{
|
|
SetVertexShaderConstantInternal(var, pVec, numVecs, bForce);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the boolean registers for vertex shader control flow
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::SetBooleanVertexShaderConstant(int var, int const* pVec, int numBools, bool bForce)
|
|
{
|
|
Assert(pVec);
|
|
Assert(var + numBools <= g_pHardwareConfig->NumBooleanVertexShaderConstants());
|
|
|
|
if (IsPC() && g_pHardwareConfig->GetDXSupportLevel() < 90)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (IsPC() && !bForce && memcmp(pVec, &m_DesiredState.m_pBooleanVertexShaderConstant[var], numBools * sizeof(BOOL)) == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (IsPC())
|
|
{
|
|
Dx9Device()->SetVertexShaderConstantB(var, pVec, numBools);
|
|
memcpy(&m_DynamicState.m_pBooleanVertexShaderConstant[var], pVec, numBools * sizeof(BOOL));
|
|
}
|
|
|
|
memcpy(&m_DesiredState.m_pBooleanVertexShaderConstant[var], pVec, numBools * sizeof(BOOL));
|
|
|
|
if (IsX360() && var + numBools > m_MaxBooleanVertexShaderConstant)
|
|
{
|
|
m_MaxBooleanVertexShaderConstant = var + numBools;
|
|
Assert(m_MaxBooleanVertexShaderConstant <= 16);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the integer registers for vertex shader control flow
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::SetIntegerVertexShaderConstant(int var, int const* pVec, int numIntVecs, bool bForce)
|
|
{
|
|
Assert(pVec);
|
|
Assert(var + numIntVecs <= g_pHardwareConfig->NumIntegerVertexShaderConstants());
|
|
|
|
if (IsPC() && g_pHardwareConfig->GetDXSupportLevel() < 90)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (IsPC() && !bForce && memcmp(pVec, &m_DesiredState.m_pIntegerVertexShaderConstant[var], numIntVecs * sizeof(IntVector4D)) == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (IsPC())
|
|
{
|
|
Dx9Device()->SetVertexShaderConstantI(var, pVec, numIntVecs);
|
|
memcpy(&m_DynamicState.m_pIntegerVertexShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D));
|
|
}
|
|
memcpy(&m_DesiredState.m_pIntegerVertexShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D));
|
|
|
|
if (IsX360() && var + numIntVecs > m_MaxIntegerVertexShaderConstant)
|
|
{
|
|
m_MaxIntegerVertexShaderConstant = var + numIntVecs;
|
|
Assert(m_MaxIntegerVertexShaderConstant <= 16);
|
|
}
|
|
}
|
|
|
|
FORCEINLINE void CShaderAPIDx8::SetPixelShaderConstantInternal(int nStartConst, float const* pValues, int nNumConsts, bool bForce)
|
|
{
|
|
Assert(nStartConst + nNumConsts <= g_pHardwareConfig->NumPixelShaderConstants());
|
|
|
|
if (IsPC())
|
|
{
|
|
if (!bForce)
|
|
{
|
|
int skip = 0;
|
|
nNumConsts = AdjustUpdateRange(pValues, &m_DesiredState.m_pVectorPixelShaderConstant[nStartConst], nNumConsts, &skip);
|
|
if (!nNumConsts)
|
|
return;
|
|
nStartConst += skip;
|
|
pValues += skip * 4;
|
|
}
|
|
|
|
Dx9Device()->SetPixelShaderConstantF(nStartConst, pValues, nNumConsts);
|
|
memcpy(&m_DynamicState.m_pVectorPixelShaderConstant[nStartConst], pValues, nNumConsts * 4 * sizeof(float));
|
|
}
|
|
|
|
memcpy(&m_DesiredState.m_pVectorPixelShaderConstant[nStartConst], pValues, nNumConsts * 4 * sizeof(float));
|
|
|
|
if (IsX360())
|
|
{
|
|
m_MaxVectorPixelShaderConstant = max(m_MaxVectorPixelShaderConstant, nStartConst + nNumConsts);
|
|
Assert(m_MaxVectorPixelShaderConstant <= 32);
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::SetPixelShaderConstant(int var, float const* pVec, int numVecs, bool bForce)
|
|
{
|
|
SetPixelShaderConstantInternal(var, pVec, numVecs, bForce);
|
|
}
|
|
|
|
template<class T> FORCEINLINE T GetData(uint8 const* pData)
|
|
{
|
|
return *(reinterpret_cast<T const*>(pData));
|
|
}
|
|
|
|
|
|
|
|
void CShaderAPIDx8::SetStandardTextureHandle(StandardTextureId_t nId, ShaderAPITextureHandle_t nHandle)
|
|
{
|
|
Assert(nId < ARRAYSIZE(m_StdTextureHandles));
|
|
m_StdTextureHandles[nId] = nHandle;
|
|
}
|
|
|
|
void CShaderAPIDx8::ExecuteCommandBuffer(uint8* pCmdBuf)
|
|
{
|
|
uint8* pReturnStack[20];
|
|
uint8** pSP = &pReturnStack[ARRAYSIZE(pReturnStack)];
|
|
uint8* pLastCmd;
|
|
for (;;)
|
|
{
|
|
uint8* pCmd = pCmdBuf;
|
|
int nCmd = GetData<int>(pCmdBuf);
|
|
switch (nCmd)
|
|
{
|
|
case CBCMD_END:
|
|
{
|
|
if (pSP == &pReturnStack[ARRAYSIZE(pReturnStack)])
|
|
return;
|
|
else
|
|
{
|
|
// pop pc
|
|
pCmdBuf = *(pSP++);
|
|
break;
|
|
}
|
|
}
|
|
|
|
case CBCMD_JUMP:
|
|
pCmdBuf = GetData<uint8*>(pCmdBuf + sizeof(int));
|
|
break;
|
|
|
|
case CBCMD_JSR:
|
|
{
|
|
Assert(pSP > &(pReturnStack[0]));
|
|
// *(--pSP ) = pCmdBuf + sizeof( int ) + sizeof( uint8 *);
|
|
// pCmdBuf = GetData<uint8 *>( pCmdBuf + sizeof( int ) );
|
|
ExecuteCommandBuffer(GetData<uint8*>(pCmdBuf + sizeof(int)));
|
|
pCmdBuf = pCmdBuf + sizeof(int) + sizeof(uint8*);
|
|
break;
|
|
}
|
|
|
|
case CBCMD_SET_PIXEL_SHADER_FLOAT_CONST:
|
|
{
|
|
int nStartConst = GetData<int>(pCmdBuf + sizeof(int));
|
|
int nNumConsts = GetData<int>(pCmdBuf + 2 * sizeof(int));
|
|
float const* pValues = reinterpret_cast<float const*> (pCmdBuf + 3 * sizeof(int));
|
|
pCmdBuf += nNumConsts * 4 * sizeof(float) + 3 * sizeof(int);
|
|
SetPixelShaderConstantInternal(nStartConst, pValues, nNumConsts, false);
|
|
break;
|
|
}
|
|
|
|
case CBCMD_SETPIXELSHADERFOGPARAMS:
|
|
{
|
|
int nReg = GetData<int>(pCmdBuf + sizeof(int));
|
|
pCmdBuf += 2 * sizeof(int);
|
|
SetPixelShaderFogParams(nReg); // !! speed fixme
|
|
break;
|
|
}
|
|
case CBCMD_STORE_EYE_POS_IN_PSCONST:
|
|
{
|
|
int nReg = GetData<int>(pCmdBuf + sizeof(int));
|
|
pCmdBuf += 2 * sizeof(int);
|
|
SetPixelShaderConstantInternal(nReg, m_WorldSpaceCameraPositon.Base(), 1, false);
|
|
break;
|
|
}
|
|
|
|
case CBCMD_COMMITPIXELSHADERLIGHTING:
|
|
{
|
|
int nReg = GetData<int>(pCmdBuf + sizeof(int));
|
|
pCmdBuf += 2 * sizeof(int);
|
|
CommitPixelShaderLighting(nReg);
|
|
break;
|
|
}
|
|
|
|
case CBCMD_SETPIXELSHADERSTATEAMBIENTLIGHTCUBE:
|
|
{
|
|
int nReg = GetData<int>(pCmdBuf + sizeof(int));
|
|
pCmdBuf += 2 * sizeof(int);
|
|
float* pCubeBase = m_DynamicState.m_AmbientLightCube[0].Base();
|
|
SetPixelShaderConstantInternal(nReg, pCubeBase, 6, false);
|
|
break;
|
|
}
|
|
|
|
case CBCMD_SETAMBIENTCUBEDYNAMICSTATEVERTEXSHADER:
|
|
{
|
|
pCmdBuf += sizeof(int);
|
|
SetVertexShaderStateAmbientLightCube();
|
|
break;
|
|
}
|
|
|
|
case CBCMD_SET_DEPTH_FEATHERING_CONST:
|
|
{
|
|
int nConst = GetData<int>(pCmdBuf + sizeof(int));
|
|
float fDepthBlendScale = GetData<float>(pCmdBuf + 2 * sizeof(int));
|
|
pCmdBuf += 2 * sizeof(int) + sizeof(float);
|
|
SetDepthFeatheringPixelShaderConstant(nConst, fDepthBlendScale);
|
|
break;
|
|
}
|
|
|
|
case CBCMD_SET_VERTEX_SHADER_FLOAT_CONST:
|
|
{
|
|
int nStartConst = GetData<int>(pCmdBuf + sizeof(int));
|
|
int nNumConsts = GetData<int>(pCmdBuf + 2 * sizeof(int));
|
|
float const* pValues = reinterpret_cast<float const*> (pCmdBuf + 3 * sizeof(int));
|
|
pCmdBuf += nNumConsts * 4 * sizeof(float) + 3 * sizeof(int);
|
|
SetVertexShaderConstantInternal(nStartConst, pValues, nNumConsts, false);
|
|
break;
|
|
}
|
|
|
|
case CBCMD_BIND_STANDARD_TEXTURE:
|
|
{
|
|
int nSampler = GetData<int>(pCmdBuf + sizeof(int));
|
|
int nTextureID = GetData<int>(pCmdBuf + 2 * sizeof(int));
|
|
pCmdBuf += 3 * sizeof(int);
|
|
if (m_StdTextureHandles[nTextureID] != INVALID_SHADERAPI_TEXTURE_HANDLE)
|
|
{
|
|
BindTexture((Sampler_t)nSampler, m_StdTextureHandles[nTextureID]);
|
|
}
|
|
else
|
|
{
|
|
ShaderUtil()->BindStandardTexture((Sampler_t)nSampler, (StandardTextureId_t)nTextureID);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CBCMD_BIND_SHADERAPI_TEXTURE_HANDLE:
|
|
{
|
|
int nSampler = GetData<int>(pCmdBuf + sizeof(int));
|
|
ShaderAPITextureHandle_t hTexture = GetData<ShaderAPITextureHandle_t>(pCmdBuf + 2 * sizeof(int));
|
|
Assert(hTexture != INVALID_SHADERAPI_TEXTURE_HANDLE);
|
|
pCmdBuf += 2 * sizeof(int) + sizeof(ShaderAPITextureHandle_t);
|
|
BindTexture((Sampler_t)nSampler, hTexture);
|
|
break;
|
|
}
|
|
|
|
case CBCMD_SET_PSHINDEX:
|
|
{
|
|
int nIdx = GetData<int>(pCmdBuf + sizeof(int));
|
|
ShaderManager()->SetPixelShaderIndex(nIdx);
|
|
pCmdBuf += 2 * sizeof(int);
|
|
break;
|
|
}
|
|
|
|
case CBCMD_SET_VSHINDEX:
|
|
{
|
|
int nIdx = GetData<int>(pCmdBuf + sizeof(int));
|
|
ShaderManager()->SetVertexShaderIndex(nIdx);
|
|
pCmdBuf += 2 * sizeof(int);
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
default:
|
|
{
|
|
Assert(0);
|
|
}
|
|
#endif
|
|
|
|
|
|
}
|
|
pLastCmd = pCmd;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the boolean registers for pixel shader control flow
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::SetBooleanPixelShaderConstant(int var, int const* pVec, int numBools, bool bForce)
|
|
{
|
|
Assert(pVec);
|
|
Assert(var + numBools <= g_pHardwareConfig->NumBooleanPixelShaderConstants());
|
|
|
|
if (IsPC() && !bForce && memcmp(pVec, &m_DesiredState.m_pBooleanPixelShaderConstant[var], numBools * sizeof(BOOL)) == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (IsPC())
|
|
{
|
|
Dx9Device()->SetPixelShaderConstantB(var, pVec, numBools);
|
|
memcpy(&m_DynamicState.m_pBooleanPixelShaderConstant[var], pVec, numBools * sizeof(BOOL));
|
|
}
|
|
|
|
memcpy(&m_DesiredState.m_pBooleanPixelShaderConstant[var], pVec, numBools * sizeof(BOOL));
|
|
|
|
if (IsX360() && var + numBools > m_MaxBooleanPixelShaderConstant)
|
|
{
|
|
m_MaxBooleanPixelShaderConstant = var + numBools;
|
|
Assert(m_MaxBooleanPixelShaderConstant <= 16);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the integer registers for pixel shader control flow
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::SetIntegerPixelShaderConstant(int var, int const* pVec, int numIntVecs, bool bForce)
|
|
{
|
|
Assert(pVec);
|
|
Assert(var + numIntVecs <= g_pHardwareConfig->NumIntegerPixelShaderConstants());
|
|
|
|
if (IsPC() && !bForce && memcmp(pVec, &m_DesiredState.m_pIntegerPixelShaderConstant[var], numIntVecs * sizeof(IntVector4D)) == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (IsPC())
|
|
{
|
|
Dx9Device()->SetPixelShaderConstantI(var, pVec, numIntVecs);
|
|
memcpy(&m_DynamicState.m_pIntegerPixelShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D));
|
|
}
|
|
|
|
memcpy(&m_DesiredState.m_pIntegerPixelShaderConstant[var], pVec, numIntVecs * sizeof(IntVector4D));
|
|
|
|
if (IsX360() && var + numIntVecs > m_MaxIntegerPixelShaderConstant)
|
|
{
|
|
m_MaxIntegerPixelShaderConstant = var + numIntVecs;
|
|
Assert(m_MaxBooleanPixelShaderConstant <= 16);
|
|
}
|
|
}
|
|
|
|
|
|
void CShaderAPIDx8::InvalidateDelayedShaderConstants(void)
|
|
{
|
|
m_DelayedShaderConstants.Invalidate();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Methods dealing with texture stage state
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the texture associated with a texture state...
|
|
//-----------------------------------------------------------------------------
|
|
|
|
inline IDirect3DBaseTexture* CShaderAPIDx8::GetD3DTexture(ShaderAPITextureHandle_t hTexture)
|
|
{
|
|
if (hTexture == INVALID_SHADERAPI_TEXTURE_HANDLE)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
AssertValidTextureHandle(hTexture);
|
|
|
|
Texture_t& tex = GetTexture(hTexture);
|
|
if (tex.m_NumCopies == 1)
|
|
{
|
|
return tex.GetTexture();
|
|
}
|
|
else
|
|
{
|
|
return tex.GetTexture(tex.m_CurrentCopy);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Inline methods
|
|
//-----------------------------------------------------------------------------
|
|
inline ShaderAPITextureHandle_t CShaderAPIDx8::GetModifyTextureHandle() const
|
|
{
|
|
return m_ModifyTextureHandle;
|
|
}
|
|
|
|
inline IDirect3DBaseTexture* CShaderAPIDx8::GetModifyTexture()
|
|
{
|
|
return CShaderAPIDx8::GetD3DTexture(m_ModifyTextureHandle);
|
|
}
|
|
|
|
void CShaderAPIDx8::SetModifyTexture(IDirect3DBaseTexture* pTex)
|
|
{
|
|
if (m_ModifyTextureHandle == INVALID_SHADERAPI_TEXTURE_HANDLE)
|
|
return;
|
|
|
|
Texture_t& tex = GetTexture(m_ModifyTextureHandle);
|
|
if (tex.m_NumCopies == 1)
|
|
{
|
|
tex.SetTexture(pTex);
|
|
}
|
|
else
|
|
{
|
|
tex.SetTexture(tex.m_CurrentCopy, pTex);
|
|
}
|
|
}
|
|
|
|
inline ShaderAPITextureHandle_t CShaderAPIDx8::GetBoundTextureBindId(Sampler_t sampler) const
|
|
{
|
|
return SamplerState(sampler).m_BoundTexture;
|
|
}
|
|
|
|
inline bool CShaderAPIDx8::WouldBeOverTextureLimit(ShaderAPITextureHandle_t hTexture)
|
|
{
|
|
if (IsPC())
|
|
{
|
|
if (mat_texture_limit.GetInt() < 0)
|
|
return false;
|
|
|
|
Texture_t& tex = GetTexture(hTexture);
|
|
if (tex.m_LastBoundFrame == m_CurrentFrame)
|
|
return false;
|
|
|
|
return m_nTextureMemoryUsedLastFrame + tex.GetMemUsage() > (mat_texture_limit.GetInt() * 1024);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
#define SETSAMPLESTATEANDMIRROR( sampler, samplerState, state_type, mirror_field, value ) \
|
|
if ( samplerState.mirror_field != value ) \
|
|
{ \
|
|
samplerState.mirror_field = value; \
|
|
::SetSamplerState( g_pD3DDevice, sampler, state_type, value ); \
|
|
}
|
|
|
|
#define SETSAMPLESTATEANDMIRROR_FLOAT( sampler, samplerState, state_type, mirror_field, value ) \
|
|
if ( samplerState.mirror_field != value ) \
|
|
{ \
|
|
samplerState.mirror_field = value; \
|
|
::SetSamplerState( g_pD3DDevice, sampler, state_type, *(DWORD*)&value ); \
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets state on the board related to the texture state
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::SetTextureState(Sampler_t sampler, ShaderAPITextureHandle_t hTexture, bool force)
|
|
{
|
|
// Get the dynamic texture info
|
|
SamplerState_t& samplerState = SamplerState(sampler);
|
|
|
|
// Set the texture state, but only if it changes
|
|
if ((samplerState.m_BoundTexture == hTexture) && !force)
|
|
return;
|
|
|
|
// Disabling texturing
|
|
if (hTexture == INVALID_SHADERAPI_TEXTURE_HANDLE || WouldBeOverTextureLimit(hTexture))
|
|
{
|
|
Dx9Device()->SetTexture(sampler, 0);
|
|
return;
|
|
}
|
|
|
|
samplerState.m_BoundTexture = hTexture;
|
|
|
|
// Don't set this if we're disabled
|
|
if (!samplerState.m_TextureEnable)
|
|
return;
|
|
|
|
RECORD_COMMAND(DX8_SET_TEXTURE, 3);
|
|
RECORD_INT(stage);
|
|
RECORD_INT(hTexture);
|
|
RECORD_INT(GetTexture(hTexture).m_CurrentCopy);
|
|
|
|
IDirect3DBaseTexture* pTexture = CShaderAPIDx8::GetD3DTexture(hTexture);
|
|
|
|
#if defined( _X360 )
|
|
DWORD linearFormatBackup = pTexture->Format.dword[0];
|
|
if (samplerState.m_SRGBReadEnable)
|
|
{
|
|
// convert to srgb format for the bind. This effectively emulates the old srgb read sampler state
|
|
pTexture->Format.SignX =
|
|
pTexture->Format.SignY =
|
|
pTexture->Format.SignZ = 3;
|
|
}
|
|
#endif
|
|
|
|
Dx9Device()->SetTexture(sampler, pTexture);
|
|
|
|
#if defined( _X360 )
|
|
// put the format back in linear space
|
|
pTexture->Format.dword[0] = linearFormatBackup;
|
|
#endif
|
|
|
|
Texture_t& tex = GetTexture(hTexture);
|
|
if (tex.m_LastBoundFrame != m_CurrentFrame)
|
|
{
|
|
tex.m_LastBoundFrame = m_CurrentFrame;
|
|
tex.m_nTimesBoundThisFrame = 0;
|
|
|
|
if (tex.m_pTextureGroupCounterFrame)
|
|
{
|
|
// Update the per-frame texture group counter.
|
|
*tex.m_pTextureGroupCounterFrame += tex.GetMemUsage();
|
|
}
|
|
|
|
// Track memory usage.
|
|
m_nTextureMemoryUsedLastFrame += tex.GetMemUsage();
|
|
}
|
|
|
|
if (!m_bDebugTexturesRendering)
|
|
++tex.m_nTimesBoundThisFrame;
|
|
|
|
tex.m_nTimesBoundMax = MAX(tex.m_nTimesBoundMax, tex.m_nTimesBoundThisFrame);
|
|
|
|
static MaterialSystem_Config_t& materialSystemConfig = ShaderUtil()->GetConfig();
|
|
|
|
D3DTEXTUREFILTERTYPE minFilter = tex.m_MinFilter;
|
|
D3DTEXTUREFILTERTYPE magFilter = tex.m_MagFilter;
|
|
D3DTEXTUREFILTERTYPE mipFilter = tex.m_MipFilter;
|
|
int finestMipmapLevel = tex.m_FinestMipmapLevel;
|
|
float lodBias = tex.m_LodBias;
|
|
|
|
if (materialSystemConfig.bMipMapTextures == 0)
|
|
{
|
|
mipFilter = D3DTEXF_NONE;
|
|
}
|
|
|
|
if (materialSystemConfig.bFilterTextures == 0 && tex.m_NumLevels > 1)
|
|
{
|
|
minFilter = D3DTEXF_NONE;
|
|
magFilter = D3DTEXF_NONE;
|
|
mipFilter = D3DTEXF_POINT;
|
|
}
|
|
|
|
D3DTEXTUREADDRESS uTexWrap = tex.m_UTexWrap;
|
|
D3DTEXTUREADDRESS vTexWrap = tex.m_VTexWrap;
|
|
D3DTEXTUREADDRESS wTexWrap = tex.m_WTexWrap;
|
|
|
|
// For now do this the old way on OSX since the dxabstract layer doesn't support SetSamplerStates
|
|
// ###OSX### punting on OSX for now
|
|
#if DX_TO_GL_ABSTRACTION && !OSX
|
|
if ((samplerState.m_MinFilter != minFilter) || (samplerState.m_MagFilter != magFilter) || (samplerState.m_MipFilter != mipFilter) ||
|
|
(samplerState.m_UTexWrap != uTexWrap) || (samplerState.m_VTexWrap != vTexWrap) || (samplerState.m_WTexWrap != wTexWrap) ||
|
|
(samplerState.m_FinestMipmapLevel != finestMipmapLevel) || (samplerState.m_LodBias != lodBias))
|
|
{
|
|
samplerState.m_UTexWrap = uTexWrap;
|
|
samplerState.m_VTexWrap = vTexWrap;
|
|
samplerState.m_WTexWrap = wTexWrap;
|
|
samplerState.m_MinFilter = minFilter;
|
|
samplerState.m_MagFilter = magFilter;
|
|
samplerState.m_MipFilter = mipFilter;
|
|
samplerState.m_FinestMipmapLevel = finestMipmapLevel;
|
|
samplerState.m_LodBias = lodBias;
|
|
Dx9Device()->SetSamplerStates(sampler, uTexWrap, vTexWrap, wTexWrap, minFilter, magFilter, mipFilter, finestMipmapLevel, lodBias);
|
|
}
|
|
#else
|
|
SETSAMPLESTATEANDMIRROR(sampler, samplerState, D3DSAMP_ADDRESSU, m_UTexWrap, uTexWrap);
|
|
SETSAMPLESTATEANDMIRROR(sampler, samplerState, D3DSAMP_ADDRESSV, m_VTexWrap, vTexWrap);
|
|
SETSAMPLESTATEANDMIRROR(sampler, samplerState, D3DSAMP_ADDRESSW, m_WTexWrap, wTexWrap);
|
|
SETSAMPLESTATEANDMIRROR(sampler, samplerState, D3DSAMP_MINFILTER, m_MinFilter, minFilter);
|
|
SETSAMPLESTATEANDMIRROR(sampler, samplerState, D3DSAMP_MAGFILTER, m_MagFilter, magFilter);
|
|
SETSAMPLESTATEANDMIRROR(sampler, samplerState, D3DSAMP_MIPFILTER, m_MipFilter, mipFilter);
|
|
SETSAMPLESTATEANDMIRROR(sampler, samplerState, D3DSAMP_MAXMIPLEVEL, m_FinestMipmapLevel, finestMipmapLevel);
|
|
SETSAMPLESTATEANDMIRROR_FLOAT(sampler, samplerState, D3DSAMP_MIPMAPLODBIAS, m_LodBias, lodBias);
|
|
|
|
#endif
|
|
}
|
|
|
|
void CShaderAPIDx8::BindTexture(Sampler_t sampler, ShaderAPITextureHandle_t textureHandle)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
SetTextureState(sampler, textureHandle);
|
|
}
|
|
|
|
void CShaderAPIDx8::BindVertexTexture(VertexTextureSampler_t nStage, ShaderAPITextureHandle_t textureHandle)
|
|
{
|
|
Assert(g_pMaterialSystemHardwareConfig->GetVertexTextureCount() != 0);
|
|
LOCK_SHADERAPI();
|
|
ADD_VERTEX_TEXTURE_FUNC(COMMIT_PER_PASS, COMMIT_VERTEX_SHADER,
|
|
CommitVertexTextures, nStage, m_BoundTexture, textureHandle);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Texture allocation/deallocation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Computes stats info for a texture
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::ComputeStatsInfo(ShaderAPITextureHandle_t hTexture, bool isCubeMap, bool isVolumeTexture)
|
|
{
|
|
Texture_t& textureData = GetTexture(hTexture);
|
|
|
|
textureData.m_SizeBytes = 0;
|
|
textureData.m_SizeTexels = 0;
|
|
textureData.m_LastBoundFrame = -1;
|
|
if (IsX360())
|
|
{
|
|
textureData.m_nTimesBoundThisFrame = 0;
|
|
}
|
|
|
|
IDirect3DBaseTexture* pD3DTex = CShaderAPIDx8::GetD3DTexture(hTexture);
|
|
|
|
if (IsPC() || !IsX360())
|
|
{
|
|
if (isCubeMap)
|
|
{
|
|
IDirect3DCubeTexture* pTex = static_cast<IDirect3DCubeTexture*>(pD3DTex);
|
|
if (!pTex)
|
|
{
|
|
Assert(0);
|
|
return;
|
|
}
|
|
|
|
int numLevels = pTex->GetLevelCount();
|
|
for (int i = 0; i < numLevels; ++i)
|
|
{
|
|
D3DSURFACE_DESC desc;
|
|
HRESULT hr = pTex->GetLevelDesc(i, &desc);
|
|
Assert(!FAILED(hr));
|
|
textureData.m_SizeBytes += 6 * ImageLoader::GetMemRequired(desc.Width, desc.Height, 1, textureData.GetImageFormat(), false);
|
|
textureData.m_SizeTexels += 6 * desc.Width * desc.Height;
|
|
}
|
|
}
|
|
else if (isVolumeTexture)
|
|
{
|
|
IDirect3DVolumeTexture9* pTex = static_cast<IDirect3DVolumeTexture9*>(pD3DTex);
|
|
if (!pTex)
|
|
{
|
|
Assert(0);
|
|
return;
|
|
}
|
|
int numLevels = pTex->GetLevelCount();
|
|
for (int i = 0; i < numLevels; ++i)
|
|
{
|
|
D3DVOLUME_DESC desc;
|
|
HRESULT hr = pTex->GetLevelDesc(i, &desc);
|
|
Assert(!FAILED(hr));
|
|
textureData.m_SizeBytes += ImageLoader::GetMemRequired(desc.Width, desc.Height, desc.Depth, textureData.GetImageFormat(), false);
|
|
textureData.m_SizeTexels += desc.Width * desc.Height;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IDirect3DTexture* pTex = static_cast<IDirect3DTexture*>(pD3DTex);
|
|
if (!pTex)
|
|
{
|
|
Assert(0);
|
|
return;
|
|
}
|
|
|
|
int numLevels = pTex->GetLevelCount();
|
|
for (int i = 0; i < numLevels; ++i)
|
|
{
|
|
D3DSURFACE_DESC desc;
|
|
HRESULT hr = pTex->GetLevelDesc(i, &desc);
|
|
Assert(!FAILED(hr));
|
|
textureData.m_SizeBytes += ImageLoader::GetMemRequired(desc.Width, desc.Height, 1, textureData.GetImageFormat(), false);
|
|
textureData.m_SizeTexels += desc.Width * desc.Height;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined( _X360 )
|
|
// 360 uses gpu storage size (which accounts for page alignment bloat), not format size
|
|
textureData.m_SizeBytes = g_TextureHeap.GetSize(pD3DTex);
|
|
#endif
|
|
}
|
|
|
|
static D3DFORMAT ComputeFormat(IDirect3DBaseTexture* pTexture, bool isCubeMap)
|
|
{
|
|
Assert(pTexture);
|
|
D3DSURFACE_DESC desc;
|
|
if (isCubeMap)
|
|
{
|
|
IDirect3DCubeTexture* pTex = static_cast<IDirect3DCubeTexture*>(pTexture);
|
|
HRESULT hr = pTex->GetLevelDesc(0, &desc);
|
|
Assert(!FAILED(hr));
|
|
}
|
|
else
|
|
{
|
|
IDirect3DTexture* pTex = static_cast<IDirect3DTexture*>(pTexture);
|
|
HRESULT hr = pTex->GetLevelDesc(0, &desc);
|
|
Assert(!FAILED(hr));
|
|
}
|
|
return desc.Format;
|
|
}
|
|
|
|
ShaderAPITextureHandle_t CShaderAPIDx8::CreateDepthTexture(
|
|
ImageFormat renderTargetFormat,
|
|
int width,
|
|
int height,
|
|
const char* pDebugName,
|
|
bool bTexture)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
|
|
ShaderAPITextureHandle_t i = CreateTextureHandle();
|
|
Texture_t* pTexture = &GetTexture(i);
|
|
|
|
pTexture->m_Flags = Texture_t::IS_ALLOCATED;
|
|
if (bTexture)
|
|
{
|
|
pTexture->m_Flags |= Texture_t::IS_DEPTH_STENCIL_TEXTURE;
|
|
}
|
|
else
|
|
{
|
|
pTexture->m_Flags |= Texture_t::IS_DEPTH_STENCIL;
|
|
}
|
|
|
|
pTexture->m_DebugName = pDebugName;
|
|
pTexture->m_Width = width;
|
|
pTexture->m_Height = height;
|
|
pTexture->m_Depth = 1; // fake
|
|
pTexture->m_Count = 1; // created single texture
|
|
pTexture->m_CountIndex = 0; // created single texture
|
|
pTexture->m_CreationFlags = 0; // fake
|
|
pTexture->m_NumCopies = 1;
|
|
pTexture->m_CurrentCopy = 0;
|
|
|
|
ImageFormat renderFormat = FindNearestSupportedFormat(renderTargetFormat, false, true, false);
|
|
#if defined( _X360 )
|
|
D3DFORMAT nDepthFormat = ReverseDepthOnX360() ? D3DFMT_D24FS8 : D3DFMT_D24S8;
|
|
#else
|
|
D3DFORMAT nDepthFormat = m_bUsingStencil ? D3DFMT_D24S8 : D3DFMT_D24X8;
|
|
#endif
|
|
D3DFORMAT format = FindNearestSupportedDepthFormat(m_nAdapter, m_AdapterFormat, renderFormat, nDepthFormat);
|
|
D3DMULTISAMPLE_TYPE multisampleType = D3DMULTISAMPLE_NONE;
|
|
|
|
pTexture->m_NumLevels = 1;
|
|
pTexture->m_SizeTexels = width * height;
|
|
pTexture->m_SizeBytes = ImageLoader::GetMemRequired(width, height, 1, renderFormat, false);
|
|
|
|
RECORD_COMMAND(DX8_CREATE_DEPTH_TEXTURE, 5);
|
|
RECORD_INT(i);
|
|
RECORD_INT(width);
|
|
RECORD_INT(height);
|
|
RECORD_INT(format);
|
|
RECORD_INT(multisampleType);
|
|
|
|
HRESULT hr;
|
|
if (!bTexture)
|
|
{
|
|
#if defined( _X360 )
|
|
int backWidth, backHeight;
|
|
ShaderAPI()->GetBackBufferDimensions(backWidth, backHeight);
|
|
D3DFORMAT backBufferFormat = ImageLoader::ImageFormatToD3DFormat(g_pShaderDevice->GetBackBufferFormat());
|
|
// immediately follows back buffer in EDRAM
|
|
D3DSURFACE_PARAMETERS surfParameters;
|
|
surfParameters.Base = 2 * XGSurfaceSize(backWidth, backHeight, backBufferFormat, D3DMULTISAMPLE_NONE);
|
|
surfParameters.ColorExpBias = 0;
|
|
surfParameters.HierarchicalZBase = 0;
|
|
hr = Dx9Device()->CreateDepthStencilSurface(
|
|
width, height, format, multisampleType, 0, TRUE, &pTexture->GetDepthStencilSurface(), &surfParameters);
|
|
#else
|
|
hr = Dx9Device()->CreateDepthStencilSurface(
|
|
width, height, format, multisampleType, 0, TRUE, &pTexture->GetDepthStencilSurface(), NULL);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
IDirect3DTexture9* pTex;
|
|
hr = Dx9Device()->CreateTexture(width, height, 1, D3DUSAGE_DEPTHSTENCIL, format, D3DPOOL_DEFAULT, &pTex, NULL);
|
|
pTexture->SetTexture(pTex);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
switch (hr)
|
|
{
|
|
case D3DERR_INVALIDCALL:
|
|
Warning("ShaderAPIDX8::CreateDepthStencilSurface: D3DERR_INVALIDCALL\n");
|
|
break;
|
|
case D3DERR_OUTOFVIDEOMEMORY:
|
|
Warning("ShaderAPIDX8::CreateDepthStencilSurface: D3DERR_OUTOFVIDEOMEMORY\n");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
Assert(0);
|
|
}
|
|
|
|
#ifdef _XBOX
|
|
D3DSURFACE_DESC desc;
|
|
hr = pTexture->GetDepthStencilSurface()->GetDesc(&desc);
|
|
Assert(!FAILED(hr));
|
|
|
|
pTexture->m_nTimesBoundThisFrame = 0;
|
|
pTexture->m_StaticSizeBytes = desc.Size;
|
|
pTexture->m_DynamicSizeBytes = 0;
|
|
pTexture->m_DebugName = pDebugName;
|
|
pTexture->m_TextureGroupName = TEXTURE_GROUP_RENDER_TARGET;
|
|
pTexture->SetImageFormat(IMAGE_FORMAT_UNKNOWN);
|
|
#endif
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
// FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
// Could keep a free-list for this instead of linearly searching. We
|
|
// don't create textures all the time, so this is probably fine for now.
|
|
// FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
ShaderAPITextureHandle_t CShaderAPIDx8::CreateTextureHandle(void)
|
|
{
|
|
ShaderAPITextureHandle_t handle;
|
|
CreateTextureHandles(&handle, 1);
|
|
return handle;
|
|
}
|
|
|
|
void CShaderAPIDx8::CreateTextureHandles(ShaderAPITextureHandle_t* handles, int count)
|
|
{
|
|
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, ¤tState.m_Viewport, sizeof(D3DVIEWPORT9));
|
|
|
|
// The width + height can be zero at startup sometimes.
|
|
if (bChanged && (desiredState.m_Viewport.Width != 0) && (desiredState.m_Viewport.Height != 0))
|
|
{
|
|
if (ReverseDepthOnX360()) //reverse depth on 360 for better perf through hierarchical z
|
|
{
|
|
D3DVIEWPORT9 reverseDepthViewport;
|
|
reverseDepthViewport = desiredState.m_Viewport;
|
|
reverseDepthViewport.MinZ = 1.0f - desiredState.m_Viewport.MinZ;
|
|
reverseDepthViewport.MaxZ = 1.0f - desiredState.m_Viewport.MaxZ;
|
|
Dx9Device()->SetViewport(&reverseDepthViewport);
|
|
}
|
|
else
|
|
{
|
|
Dx9Device()->SetViewport(&desiredState.m_Viewport);
|
|
}
|
|
memcpy(¤tState.m_Viewport, &desiredState.m_Viewport, sizeof(D3DVIEWPORT9));
|
|
}
|
|
}
|
|
|
|
|
|
void CShaderAPIDx8::SetViewports(int nCount, const ShaderViewport_t* pViewports)
|
|
{
|
|
Assert(nCount == 1 && pViewports[0].m_nVersion == SHADER_VIEWPORT_VERSION);
|
|
if (nCount != 1)
|
|
return;
|
|
|
|
LOCK_SHADERAPI();
|
|
|
|
D3DVIEWPORT9 viewport;
|
|
viewport.X = pViewports[0].m_nTopLeftX;
|
|
viewport.Y = pViewports[0].m_nTopLeftY;
|
|
viewport.Width = pViewports[0].m_nWidth;
|
|
viewport.Height = pViewports[0].m_nHeight;
|
|
viewport.MinZ = pViewports[0].m_flMinZ;
|
|
viewport.MaxZ = pViewports[0].m_flMaxZ;
|
|
|
|
// Clamp the viewport to the current render target...
|
|
if (!m_UsingTextureRenderTarget)
|
|
{
|
|
// Clamp to both the back buffer and the window, if it is resizing
|
|
int nMaxWidth = 0, nMaxHeight = 0;
|
|
GetBackBufferDimensions(nMaxWidth, nMaxHeight);
|
|
if (IsPC() && m_IsResizing)
|
|
{
|
|
RECT viewRect;
|
|
#if !defined( DX_TO_GL_ABSTRACTION )
|
|
GetClientRect((HWND)m_ViewHWnd, &viewRect);
|
|
#else
|
|
toglGetClientRect((VD3DHWND)m_ViewHWnd, &viewRect);
|
|
#endif
|
|
m_nWindowWidth = viewRect.right - viewRect.left;
|
|
m_nWindowHeight = viewRect.bottom - viewRect.top;
|
|
nMaxWidth = min(m_nWindowWidth, nMaxWidth);
|
|
nMaxHeight = min(m_nWindowHeight, nMaxHeight);
|
|
}
|
|
|
|
// Dimensions can freak out on app exit, so at least make sure the viewport is positive
|
|
if ((viewport.Width > (unsigned int)nMaxWidth) && (nMaxWidth > 0))
|
|
{
|
|
viewport.Width = nMaxWidth;
|
|
}
|
|
|
|
// Dimensions can freak out on app exit, so at least make sure the viewport is positive
|
|
if ((viewport.Height > (unsigned int)nMaxHeight) && (nMaxHeight > 0))
|
|
{
|
|
viewport.Height = nMaxHeight;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (viewport.Width > (unsigned int)m_ViewportMaxWidth)
|
|
{
|
|
viewport.Width = m_ViewportMaxWidth;
|
|
}
|
|
if (viewport.Height > (unsigned int)m_ViewportMaxHeight)
|
|
{
|
|
viewport.Height = m_ViewportMaxHeight;
|
|
}
|
|
}
|
|
|
|
// FIXME: Once we extract buffered primitives out, we can directly fill in desired state
|
|
// and avoid the memcmp and copy
|
|
if (memcmp(&m_DesiredState.m_Viewport, &viewport, sizeof(D3DVIEWPORT9)))
|
|
{
|
|
if (!IsDeactivated())
|
|
{
|
|
// State changed... need to flush the dynamic buffer
|
|
FlushBufferedPrimitives();
|
|
}
|
|
|
|
memcpy(&m_DesiredState.m_Viewport, &viewport, sizeof(D3DVIEWPORT9));
|
|
}
|
|
|
|
ADD_COMMIT_FUNC(COMMIT_PER_DRAW, COMMIT_ALWAYS, CommitSetViewports);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the current viewport size
|
|
//-----------------------------------------------------------------------------
|
|
int CShaderAPIDx8::GetViewports(ShaderViewport_t* pViewports, int nMax) const
|
|
{
|
|
if (!pViewports || nMax == 0)
|
|
return 1;
|
|
|
|
LOCK_SHADERAPI();
|
|
|
|
pViewports[0].m_nTopLeftX = m_DesiredState.m_Viewport.X;
|
|
pViewports[0].m_nTopLeftY = m_DesiredState.m_Viewport.Y;
|
|
pViewports[0].m_nWidth = m_DesiredState.m_Viewport.Width;
|
|
pViewports[0].m_nHeight = m_DesiredState.m_Viewport.Height;
|
|
pViewports[0].m_flMinZ = m_DesiredState.m_Viewport.MinZ;
|
|
pViewports[0].m_flMaxZ = m_DesiredState.m_Viewport.MaxZ;
|
|
return 1;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Flushes buffered primitives
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::FlushBufferedPrimitives()
|
|
{
|
|
if (ShaderUtil())
|
|
{
|
|
if (!ShaderUtil()->OnFlushBufferedPrimitives())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
FlushBufferedPrimitivesInternal();
|
|
}
|
|
|
|
void CShaderAPIDx8::FlushBufferedPrimitivesInternal()
|
|
{
|
|
LOCK_SHADERAPI();
|
|
// This shouldn't happen in the inner rendering loop!
|
|
Assert(m_pRenderMesh == 0);
|
|
|
|
// NOTE: We've gotta store off the matrix mode because
|
|
// it'll get reset by the default state application caused by the flush
|
|
int tempStack = m_CurrStack;
|
|
D3DTRANSFORMSTATETYPE tempMatrixMode = m_MatrixMode;
|
|
|
|
MeshMgr()->Flush();
|
|
|
|
m_CurrStack = tempStack;
|
|
m_MatrixMode = tempMatrixMode;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Flush the hardware
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::FlushHardware()
|
|
{
|
|
LOCK_SHADERAPI();
|
|
FlushBufferedPrimitives();
|
|
|
|
Dx9Device()->EndScene();
|
|
|
|
DiscardVertexBuffers();
|
|
|
|
Dx9Device()->BeginScene();
|
|
|
|
ForceHardwareSync();
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Deal with device lost (alt-tab)
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::HandleDeviceLost()
|
|
{
|
|
if (IsX360())
|
|
{
|
|
return;
|
|
}
|
|
|
|
LOCK_SHADERAPI();
|
|
|
|
if (!IsActive())
|
|
return;
|
|
|
|
// need to flush the dynamic buffer
|
|
FlushBufferedPrimitives();
|
|
|
|
if (!IsDeactivated())
|
|
{
|
|
Dx9Device()->EndScene();
|
|
}
|
|
|
|
CheckDeviceLost(m_bOtherAppInitializing);
|
|
|
|
if (!IsDeactivated())
|
|
{
|
|
Dx9Device()->BeginScene();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Buffer clear color
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::ClearColor3ub(unsigned char r, unsigned char g, unsigned char b)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
float a = 255;//(r * 0.30f + g * 0.59f + b * 0.11f) / MAX_HDR_OVERBRIGHT;
|
|
|
|
// GR - need to force alpha to black for HDR
|
|
m_DynamicState.m_ClearColor = D3DCOLOR_ARGB((unsigned char)a, r, g, b);
|
|
}
|
|
|
|
void CShaderAPIDx8::ClearColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
m_DynamicState.m_ClearColor = D3DCOLOR_ARGB(a, r, g, b);
|
|
}
|
|
|
|
// Converts the clear color to be appropriate for HDR
|
|
D3DCOLOR CShaderAPIDx8::GetActualClearColor(D3DCOLOR clearColor)
|
|
{
|
|
bool bConvert = !IsX360() && m_TransitionTable.CurrentState().m_bLinearColorSpaceFrameBufferEnable;
|
|
|
|
#if defined( _X360 )
|
|
// The PC disables SRGBWrite when clearing so that the clear color won't get gamma converted
|
|
// The 360 cannot disable that state, and thus compensates for the sRGB conversion
|
|
// the desired result is the clear color written to the RT as-is
|
|
if (clearColor & D3DCOLOR_ARGB(0, 255, 255, 255))
|
|
{
|
|
IDirect3DSurface* pRTSurface = NULL;
|
|
Dx9Device()->GetRenderTarget(0, &pRTSurface);
|
|
if (pRTSurface)
|
|
{
|
|
D3DSURFACE_DESC desc;
|
|
HRESULT hr = pRTSurface->GetDesc(&desc);
|
|
if (!FAILED(hr) && IS_D3DFORMAT_SRGB(desc.Format))
|
|
{
|
|
bConvert = true;
|
|
}
|
|
pRTSurface->Release();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (bConvert)
|
|
{
|
|
// HDRFIXME: need to make sure this works this way.
|
|
// HDRFIXME: Is there a helper function that'll do this easier?
|
|
// convert clearColor from gamma to linear since our frame buffer is linear.
|
|
Vector vecGammaColor;
|
|
vecGammaColor.x = (1.0f / 255.0f) * ((clearColor >> 16) & 0xff);
|
|
vecGammaColor.y = (1.0f / 255.0f) * ((clearColor >> 8) & 0xff);
|
|
vecGammaColor.z = (1.0f / 255.0f) * (clearColor & 0xff);
|
|
Vector vecLinearColor;
|
|
vecLinearColor.x = GammaToLinear(vecGammaColor.x);
|
|
vecLinearColor.y = GammaToLinear(vecGammaColor.y);
|
|
vecLinearColor.z = GammaToLinear(vecGammaColor.z);
|
|
clearColor &= D3DCOLOR_RGBA(0, 0, 0, 255);
|
|
clearColor |= D3DCOLOR_COLORVALUE(vecLinearColor.x, vecLinearColor.y, vecLinearColor.z, 0.0f);
|
|
}
|
|
|
|
return clearColor;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Clear buffers while obeying stencil
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::ClearBuffersObeyStencil(bool bClearColor, bool bClearDepth)
|
|
{
|
|
//copy the clear color bool into the clear alpha bool
|
|
ClearBuffersObeyStencilEx(bClearColor, bClearColor, bClearDepth);
|
|
}
|
|
|
|
void CShaderAPIDx8::ClearBuffersObeyStencilEx(bool bClearColor, bool bClearAlpha, bool bClearDepth)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
|
|
if (!bClearColor && !bClearAlpha && !bClearDepth)
|
|
return;
|
|
|
|
FlushBufferedPrimitives();
|
|
|
|
// Before clearing can happen, user clip planes must be disabled
|
|
SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
|
|
|
|
D3DCOLOR clearColor = GetActualClearColor(m_DynamicState.m_ClearColor);
|
|
|
|
unsigned char r, g, b, a;
|
|
b = clearColor & 0xFF;
|
|
g = (clearColor >> 8) & 0xFF;
|
|
r = (clearColor >> 16) & 0xFF;
|
|
a = (clearColor >> 24) & 0xFF;
|
|
|
|
ShaderUtil()->DrawClearBufferQuad(r, g, b, a, bClearColor, bClearAlpha, bClearDepth);
|
|
|
|
// Reset user clip plane state
|
|
FlushBufferedPrimitives();
|
|
SetRenderState(D3DRS_CLIPPLANEENABLE, m_DynamicState.m_UserClipPlaneEnabled);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
//Perform stencil operations to every pixel on the screen
|
|
//-------------------------------------------------------------------------
|
|
void CShaderAPIDx8::PerformFullScreenStencilOperation(void)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
|
|
FlushBufferedPrimitives();
|
|
|
|
// We'll be drawing a large quad in altered worldspace, user clip planes must be disabled
|
|
SetRenderStateConstMacro(this, D3DRS_CLIPPLANEENABLE, 0);
|
|
|
|
ShaderUtil()->DrawClearBufferQuad(0, 0, 0, 0, false, false, false);
|
|
|
|
// Reset user clip plane state
|
|
FlushBufferedPrimitives();
|
|
SetRenderStateConstMacro(this, D3DRS_CLIPPLANEENABLE, m_DynamicState.m_UserClipPlaneEnabled);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Buffer clear
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::ClearBuffers(bool bClearColor, bool bClearDepth, bool bClearStencil, int renderTargetWidth, int renderTargetHeight)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (ShaderUtil()->GetConfig().m_bSuppressRendering)
|
|
return;
|
|
|
|
if (IsDeactivated())
|
|
return;
|
|
|
|
// State changed... need to flush the dynamic buffer
|
|
FlushBufferedPrimitives();
|
|
CallCommitFuncs(COMMIT_PER_DRAW, true);
|
|
|
|
float depth = (ShaderUtil()->GetConfig().bReverseDepth ^ ReverseDepthOnX360()) ? 0.0f : 1.0f;
|
|
DWORD mask = 0;
|
|
|
|
if (bClearColor)
|
|
{
|
|
mask |= D3DCLEAR_TARGET;
|
|
}
|
|
|
|
if (bClearDepth)
|
|
{
|
|
mask |= D3DCLEAR_ZBUFFER;
|
|
}
|
|
|
|
if (bClearStencil && m_bUsingStencil)
|
|
{
|
|
mask |= D3DCLEAR_STENCIL;
|
|
}
|
|
|
|
// Only clear the current view... right!??!
|
|
D3DRECT clear;
|
|
clear.x1 = m_DesiredState.m_Viewport.X;
|
|
clear.y1 = m_DesiredState.m_Viewport.Y;
|
|
clear.x2 = clear.x1 + m_DesiredState.m_Viewport.Width;
|
|
clear.y2 = clear.y1 + m_DesiredState.m_Viewport.Height;
|
|
|
|
// SRGBWrite is disabled when clearing so that the clear color won't get gamma converted
|
|
bool bSRGBWriteEnable = false;
|
|
if (!IsX360() && bClearColor && m_TransitionTable.CurrentShadowState())
|
|
{
|
|
bSRGBWriteEnable = m_TransitionTable.CurrentShadowState()->m_SRGBWriteEnable;
|
|
}
|
|
|
|
#if !defined( _X360 )
|
|
if (bSRGBWriteEnable)
|
|
{
|
|
// This path used to be !IsPosix(), but this makes no sense and causes the clear color to differ in D3D9 vs. GL.
|
|
Dx9Device()->SetRenderState(D3DRS_SRGBWRITEENABLE, 0);
|
|
}
|
|
#endif
|
|
|
|
D3DCOLOR clearColor = GetActualClearColor(m_DynamicState.m_ClearColor);
|
|
|
|
if (mask != 0)
|
|
{
|
|
bool bRenderTargetMatchesViewport =
|
|
(renderTargetWidth == -1 && renderTargetHeight == -1) ||
|
|
(m_DesiredState.m_Viewport.Width == -1 && m_DesiredState.m_Viewport.Height == -1) ||
|
|
(renderTargetWidth == (int)m_DesiredState.m_Viewport.Width &&
|
|
renderTargetHeight == (int)m_DesiredState.m_Viewport.Height);
|
|
|
|
if (bRenderTargetMatchesViewport)
|
|
{
|
|
RECORD_COMMAND(DX8_CLEAR, 6);
|
|
RECORD_INT(0);
|
|
RECORD_STRUCT(&clear, sizeof(clear));
|
|
RECORD_INT(mask);
|
|
RECORD_INT(clearColor);
|
|
RECORD_FLOAT(depth);
|
|
RECORD_INT(0);
|
|
|
|
Dx9Device()->Clear(0, NULL, mask, clearColor, depth, 0L);
|
|
}
|
|
else
|
|
{
|
|
RECORD_COMMAND(DX8_CLEAR, 6);
|
|
RECORD_INT(0);
|
|
RECORD_STRUCT(&clear, sizeof(clear));
|
|
RECORD_INT(mask);
|
|
RECORD_INT(clearColor);
|
|
RECORD_FLOAT(depth);
|
|
RECORD_INT(0);
|
|
|
|
Dx9Device()->Clear(1, &clear, mask, clearColor, depth, 0L);
|
|
}
|
|
}
|
|
|
|
// Restore state
|
|
if (bSRGBWriteEnable)
|
|
{
|
|
// sRGBWriteEnable shouldn't be true if we have no shadow state. . . Assert just in case.
|
|
Assert(m_TransitionTable.CurrentShadowState());
|
|
m_TransitionTable.ApplySRGBWriteEnable(*m_TransitionTable.CurrentShadowState());
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Bind
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::BindVertexShader(VertexShaderHandle_t hVertexShader)
|
|
{
|
|
ShaderManager()->BindVertexShader(hVertexShader);
|
|
}
|
|
|
|
void CShaderAPIDx8::BindGeometryShader(GeometryShaderHandle_t hGeometryShader)
|
|
{
|
|
Assert(hGeometryShader == GEOMETRY_SHADER_HANDLE_INVALID);
|
|
}
|
|
|
|
void CShaderAPIDx8::BindPixelShader(PixelShaderHandle_t hPixelShader)
|
|
{
|
|
ShaderManager()->BindPixelShader(hPixelShader);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns a copy of the front buffer
|
|
//-----------------------------------------------------------------------------
|
|
IDirect3DSurface* CShaderAPIDx8::GetFrontBufferImage(ImageFormat& format)
|
|
{
|
|
#if !defined( _X360 )
|
|
// need to flush the dynamic buffer and make sure the entire image is there
|
|
FlushBufferedPrimitives();
|
|
|
|
int w, h;
|
|
GetBackBufferDimensions(w, h);
|
|
|
|
HRESULT hr;
|
|
IDirect3DSurface* pFullScreenSurfaceBits = 0;
|
|
hr = Dx9Device()->CreateOffscreenPlainSurface(w, h,
|
|
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pFullScreenSurfaceBits, NULL);
|
|
if (FAILED(hr))
|
|
return 0;
|
|
|
|
hr = Dx9Device()->GetFrontBufferData(0, pFullScreenSurfaceBits);
|
|
if (FAILED(hr))
|
|
return 0;
|
|
|
|
int windowWidth, windowHeight;
|
|
GetWindowSize(windowWidth, windowHeight);
|
|
|
|
IDirect3DSurface* pSurfaceBits = 0;
|
|
hr = Dx9Device()->CreateOffscreenPlainSurface(windowWidth, windowHeight,
|
|
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurfaceBits, NULL);
|
|
Assert(hr == D3D_OK);
|
|
|
|
POINT pnt;
|
|
pnt.x = pnt.y = 0;
|
|
#ifdef _WIN32
|
|
BOOL result = ClientToScreen((HWND)m_hWnd, &pnt);
|
|
#else
|
|
BOOL result = ClientToScreen((VD3DHWND)m_hWnd, &pnt);
|
|
#endif
|
|
Assert(result);
|
|
|
|
RECT srcRect;
|
|
srcRect.left = pnt.x;
|
|
srcRect.top = pnt.y;
|
|
srcRect.right = pnt.x + windowWidth;
|
|
srcRect.bottom = pnt.y + windowHeight;
|
|
|
|
POINT dstPnt;
|
|
dstPnt.x = dstPnt.y = 0;
|
|
|
|
D3DLOCKED_RECT lockedSrcRect;
|
|
hr = pFullScreenSurfaceBits->LockRect(&lockedSrcRect, &srcRect, D3DLOCK_READONLY);
|
|
Assert(hr == D3D_OK);
|
|
|
|
D3DLOCKED_RECT lockedDstRect;
|
|
hr = pSurfaceBits->LockRect(&lockedDstRect, NULL, 0);
|
|
Assert(hr == D3D_OK);
|
|
|
|
int i;
|
|
for (i = 0; i < windowHeight; i++)
|
|
{
|
|
memcpy((unsigned char*)lockedDstRect.pBits + (i * lockedDstRect.Pitch),
|
|
(unsigned char*)lockedSrcRect.pBits + (i * lockedSrcRect.Pitch),
|
|
windowWidth * 4); // hack . . what if this is a different format?
|
|
}
|
|
hr = pSurfaceBits->UnlockRect();
|
|
Assert(hr == D3D_OK);
|
|
hr = pFullScreenSurfaceBits->UnlockRect();
|
|
Assert(hr == D3D_OK);
|
|
|
|
pFullScreenSurfaceBits->Release();
|
|
|
|
format = ImageLoader::D3DFormatToImageFormat(D3DFMT_A8R8G8B8);
|
|
return pSurfaceBits;
|
|
#else
|
|
Assert(0);
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Lets the shader know about the full-screen texture so it can
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::SetFullScreenTextureHandle(ShaderAPITextureHandle_t h)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
m_hFullScreenTexture = h;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Lets the shader know about the full-screen texture so it can
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::SetLinearToGammaConversionTextures(ShaderAPITextureHandle_t hSRGBWriteEnabledTexture, ShaderAPITextureHandle_t hIdentityTexture)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
|
|
m_hLinearToGammaTableTexture = hSRGBWriteEnabledTexture;
|
|
m_hLinearToGammaTableIdentityTexture = hIdentityTexture;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns a copy of the back buffer
|
|
//-----------------------------------------------------------------------------
|
|
IDirect3DSurface* CShaderAPIDx8::GetBackBufferImageHDR(Rect_t* pSrcRect, Rect_t* pDstRect, ImageFormat& format)
|
|
{
|
|
#if !defined( _X360 )
|
|
HRESULT hr;
|
|
IDirect3DSurface* pSurfaceBits = 0;
|
|
IDirect3DSurface* pTmpSurface = NULL;
|
|
|
|
// Get the back buffer
|
|
IDirect3DSurface* pBackBuffer;
|
|
hr = Dx9Device()->GetRenderTarget(0, &pBackBuffer);
|
|
if (FAILED(hr))
|
|
return 0;
|
|
|
|
// Find about its size and format
|
|
D3DSURFACE_DESC desc;
|
|
D3DTEXTUREFILTERTYPE filter;
|
|
|
|
hr = pBackBuffer->GetDesc(&desc);
|
|
if (FAILED(hr))
|
|
goto CleanUp;
|
|
|
|
filter = ((pDstRect->width != pSrcRect->width) || (pDstRect->height != pSrcRect->height)) ? D3DTEXF_LINEAR : D3DTEXF_NONE;
|
|
|
|
if ((pDstRect->x + pDstRect->width <= SMALL_BACK_BUFFER_SURFACE_WIDTH) &&
|
|
(pDstRect->y + pDstRect->height <= SMALL_BACK_BUFFER_SURFACE_HEIGHT))
|
|
{
|
|
if (!m_pSmallBackBufferFP16TempSurface)
|
|
{
|
|
hr = Dx9Device()->CreateRenderTarget(
|
|
SMALL_BACK_BUFFER_SURFACE_WIDTH, SMALL_BACK_BUFFER_SURFACE_HEIGHT,
|
|
desc.Format, D3DMULTISAMPLE_NONE, 0, TRUE, &m_pSmallBackBufferFP16TempSurface,
|
|
NULL);
|
|
}
|
|
pTmpSurface = m_pSmallBackBufferFP16TempSurface;
|
|
#if POSIX
|
|
pTmpSurface->AddRef(0, "CShaderAPIDx8::GetBackBufferImageHDR public addref");
|
|
#else
|
|
pTmpSurface->AddRef();
|
|
#endif
|
|
|
|
desc.Width = SMALL_BACK_BUFFER_SURFACE_WIDTH;
|
|
desc.Height = SMALL_BACK_BUFFER_SURFACE_HEIGHT;
|
|
|
|
RECT srcRect, destRect;
|
|
RectToRECT(pSrcRect, srcRect);
|
|
RectToRECT(pDstRect, destRect);
|
|
hr = Dx9Device()->StretchRect(pBackBuffer, &srcRect, pTmpSurface, &destRect, filter);
|
|
if (FAILED(hr))
|
|
goto CleanUp;
|
|
}
|
|
else
|
|
{
|
|
// Normally we would only have to create a separate render target here and StretchBlt to it first
|
|
// if AA was enabled, but certain machines/drivers get reboots if we do GetRenderTargetData
|
|
// straight off the backbuffer.
|
|
hr = Dx9Device()->CreateRenderTarget(desc.Width, desc.Height, desc.Format,
|
|
D3DMULTISAMPLE_NONE, 0, TRUE, &pTmpSurface, NULL);
|
|
if (FAILED(hr))
|
|
goto CleanUp;
|
|
|
|
hr = Dx9Device()->StretchRect(pBackBuffer, NULL, pTmpSurface, NULL, filter);
|
|
if (FAILED(hr))
|
|
goto CleanUp;
|
|
}
|
|
|
|
// Create a buffer the same size and format
|
|
hr = Dx9Device()->CreateOffscreenPlainSurface(desc.Width, desc.Height,
|
|
desc.Format, D3DPOOL_SYSTEMMEM, &pSurfaceBits, NULL);
|
|
if (FAILED(hr))
|
|
goto CleanUp;
|
|
|
|
// Blit from the back buffer to our scratch buffer
|
|
hr = Dx9Device()->GetRenderTargetData(pTmpSurface ? pTmpSurface : pBackBuffer, pSurfaceBits);
|
|
if (FAILED(hr))
|
|
goto CleanUp2;
|
|
|
|
format = ImageLoader::D3DFormatToImageFormat(desc.Format);
|
|
if (pTmpSurface)
|
|
{
|
|
pTmpSurface->Release();
|
|
}
|
|
pBackBuffer->Release();
|
|
return pSurfaceBits;
|
|
|
|
CleanUp2:
|
|
pSurfaceBits->Release();
|
|
|
|
CleanUp:
|
|
if (pTmpSurface)
|
|
{
|
|
pTmpSurface->Release();
|
|
}
|
|
|
|
pBackBuffer->Release();
|
|
#else
|
|
Assert(0);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns a copy of the back buffer
|
|
//-----------------------------------------------------------------------------
|
|
IDirect3DSurface* CShaderAPIDx8::GetBackBufferImage(Rect_t* pSrcRect, Rect_t* pDstRect, ImageFormat& format)
|
|
{
|
|
#if !defined( _X360 )
|
|
if (!m_pBackBufferSurface || (m_hFullScreenTexture == INVALID_SHADERAPI_TEXTURE_HANDLE))
|
|
return NULL;
|
|
|
|
HRESULT hr;
|
|
D3DSURFACE_DESC desc;
|
|
|
|
FlushBufferedPrimitives();
|
|
|
|
// Get the current render target
|
|
IDirect3DSurface* pRenderTarget;
|
|
hr = Dx9Device()->GetRenderTarget(0, &pRenderTarget);
|
|
if (FAILED(hr))
|
|
return 0;
|
|
|
|
// Find about its size and format
|
|
hr = pRenderTarget->GetDesc(&desc);
|
|
|
|
if (desc.Format == D3DFMT_A16B16G16R16F || desc.Format == D3DFMT_A32B32G32R32F)
|
|
return GetBackBufferImageHDR(pSrcRect, pDstRect, format);
|
|
|
|
IDirect3DSurface* pSurfaceBits = NULL;
|
|
IDirect3DSurface* pTmpSurface = NULL;
|
|
int nRenderTargetRefCount;
|
|
nRenderTargetRefCount = 0;
|
|
|
|
if ((desc.MultiSampleType == D3DMULTISAMPLE_NONE) && (pRenderTarget != m_pBackBufferSurface) &&
|
|
(pSrcRect->width == pDstRect->width) && (pSrcRect->height == pDstRect->height))
|
|
{
|
|
// Don't bother to blit through the full-screen texture if we don't
|
|
// have to stretch, we're not coming from the backbuffer, and we don't have to do AA resolve
|
|
pTmpSurface = pRenderTarget;
|
|
#if POSIX
|
|
pTmpSurface->AddRef(0, "CShaderAPIDx8::GetBackBufferImage public addref");
|
|
#else
|
|
pTmpSurface->AddRef();
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
Texture_t* pTex = &GetTexture(m_hFullScreenTexture);
|
|
IDirect3DTexture* pFullScreenTexture = (IDirect3DTexture*)pTex->GetTexture();
|
|
|
|
D3DTEXTUREFILTERTYPE filter = ((pDstRect->width != pSrcRect->width) || (pDstRect->height != pSrcRect->height)) ? D3DTEXF_LINEAR : D3DTEXF_NONE;
|
|
|
|
hr = pFullScreenTexture->GetSurfaceLevel(0, &pTmpSurface);
|
|
if (FAILED(hr))
|
|
goto CleanUp;
|
|
|
|
if (pTmpSurface == pRenderTarget)
|
|
{
|
|
Warning("Can't blit from full-sized offscreen buffer!\n");
|
|
goto CleanUp;
|
|
}
|
|
|
|
RECT srcRect, destRect;
|
|
srcRect.left = pSrcRect->x; srcRect.right = pSrcRect->x + pSrcRect->width;
|
|
srcRect.top = pSrcRect->y; srcRect.bottom = pSrcRect->y + pSrcRect->height;
|
|
srcRect.left = clamp(srcRect.left, 0, (int)desc.Width);
|
|
srcRect.right = clamp(srcRect.right, 0, (int)desc.Width);
|
|
srcRect.top = clamp(srcRect.top, 0, (int)desc.Height);
|
|
srcRect.bottom = clamp(srcRect.bottom, 0, (int)desc.Height);
|
|
|
|
destRect.left = pDstRect->x; destRect.right = pDstRect->x + pDstRect->width;
|
|
destRect.top = pDstRect->y; destRect.bottom = pDstRect->y + pDstRect->height;
|
|
destRect.left = clamp(destRect.left, 0, (int)desc.Width);
|
|
destRect.right = clamp(destRect.right, 0, (int)desc.Width);
|
|
destRect.top = clamp(destRect.top, 0, (int)desc.Height);
|
|
destRect.bottom = clamp(destRect.bottom, 0, (int)desc.Height);
|
|
|
|
hr = Dx9Device()->StretchRect(pRenderTarget, &srcRect, pTmpSurface, &destRect, filter);
|
|
if (FAILED(hr))
|
|
{
|
|
AssertOnce("Error resizing pixels!\n");
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
D3DSURFACE_DESC tmpDesc;
|
|
hr = pTmpSurface->GetDesc(&tmpDesc);
|
|
Assert(!FAILED(hr));
|
|
|
|
// Create a buffer the same size and format
|
|
hr = Dx9Device()->CreateOffscreenPlainSurface(tmpDesc.Width, tmpDesc.Height,
|
|
desc.Format, D3DPOOL_SYSTEMMEM, &pSurfaceBits, NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
AssertOnce("Error creating offscreen surface!\n");
|
|
goto CleanUp;
|
|
}
|
|
|
|
// Blit from the back buffer to our scratch buffer
|
|
hr = Dx9Device()->GetRenderTargetData(pTmpSurface, pSurfaceBits);
|
|
if (FAILED(hr))
|
|
{
|
|
AssertOnce("Error copying bits into the offscreen surface!\n");
|
|
goto CleanUp;
|
|
}
|
|
|
|
format = ImageLoader::D3DFormatToImageFormat(desc.Format);
|
|
|
|
pTmpSurface->Release();
|
|
#ifdef _DEBUG
|
|
nRenderTargetRefCount =
|
|
#endif
|
|
pRenderTarget->Release();
|
|
AssertOnce(nRenderTargetRefCount == 1);
|
|
return pSurfaceBits;
|
|
|
|
CleanUp:
|
|
if (pSurfaceBits)
|
|
{
|
|
pSurfaceBits->Release();
|
|
}
|
|
|
|
if (pTmpSurface)
|
|
{
|
|
pTmpSurface->Release();
|
|
}
|
|
#else
|
|
Assert(0);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Copy bits from a host-memory surface
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::CopyBitsFromHostSurface(IDirect3DSurface* pSurfaceBits,
|
|
const Rect_t& dstRect, unsigned char* pData, ImageFormat srcFormat, ImageFormat dstFormat, int nDstStride)
|
|
{
|
|
// Copy out the bits...
|
|
RECT rect;
|
|
rect.left = dstRect.x;
|
|
rect.right = dstRect.x + dstRect.width;
|
|
rect.top = dstRect.y;
|
|
rect.bottom = dstRect.y + dstRect.height;
|
|
|
|
D3DLOCKED_RECT lockedRect;
|
|
HRESULT hr;
|
|
int flags = D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK;
|
|
|
|
tmZone(TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__);
|
|
|
|
hr = pSurfaceBits->LockRect(&lockedRect, &rect, flags);
|
|
if (!FAILED(hr))
|
|
{
|
|
unsigned char* pImage = (unsigned char*)lockedRect.pBits;
|
|
ShaderUtil()->ConvertImageFormat((unsigned char*)pImage, srcFormat,
|
|
pData, dstFormat, dstRect.width, dstRect.height, lockedRect.Pitch, nDstStride);
|
|
|
|
hr = pSurfaceBits->UnlockRect();
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Reads from the current read buffer + stretches
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::ReadPixels(Rect_t* pSrcRect, Rect_t* pDstRect, unsigned char* pData, ImageFormat dstFormat, int nDstStride)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
Assert(pDstRect);
|
|
|
|
if (IsPC() || !IsX360())
|
|
{
|
|
Rect_t srcRect;
|
|
if (!pSrcRect)
|
|
{
|
|
srcRect.x = srcRect.y = 0;
|
|
srcRect.width = m_nWindowWidth;
|
|
srcRect.height = m_nWindowHeight;
|
|
pSrcRect = &srcRect;
|
|
}
|
|
|
|
ImageFormat format;
|
|
IDirect3DSurface* pSurfaceBits = GetBackBufferImage(pSrcRect, pDstRect, format);
|
|
if (pSurfaceBits)
|
|
{
|
|
CopyBitsFromHostSurface(pSurfaceBits, *pDstRect, pData, format, dstFormat, nDstStride);
|
|
|
|
// Release the temporary surface
|
|
pSurfaceBits->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if defined( _X360 )
|
|
// 360 requires material system to handle due to RT complexities
|
|
ShaderUtil()->ReadBackBuffer(pSrcRect, pDstRect, pData, dstFormat, nDstStride);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Reads from the current read buffer
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::ReadPixels(int x, int y, int width, int height, unsigned char* pData, ImageFormat dstFormat)
|
|
{
|
|
Rect_t rect;
|
|
rect.x = x;
|
|
rect.y = y;
|
|
rect.width = width;
|
|
rect.height = height;
|
|
|
|
if (IsPC() || !IsX360())
|
|
{
|
|
ImageFormat format;
|
|
IDirect3DSurface* pSurfaceBits = GetBackBufferImage(&rect, &rect, format);
|
|
if (pSurfaceBits)
|
|
{
|
|
CopyBitsFromHostSurface(pSurfaceBits, rect, pData, format, dstFormat, 0);
|
|
|
|
// Release the temporary surface
|
|
pSurfaceBits->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if defined( _X360 )
|
|
// 360 requires material system to handle due to RT complexities
|
|
ShaderUtil()->ReadBackBuffer(&rect, &rect, pData, dstFormat, 0);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Binds a particular material to render with
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::Bind(IMaterial* pMaterial)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
IMaterialInternal* pMatInt = static_cast<IMaterialInternal*>(pMaterial);
|
|
|
|
bool bMaterialChanged;
|
|
if (m_pMaterial && pMatInt && m_pMaterial->InMaterialPage() && pMatInt->InMaterialPage())
|
|
{
|
|
bMaterialChanged = (m_pMaterial->GetMaterialPage() != pMatInt->GetMaterialPage());
|
|
}
|
|
else
|
|
{
|
|
bMaterialChanged = (m_pMaterial != pMatInt) || (m_pMaterial && m_pMaterial->InMaterialPage()) || (pMatInt && pMatInt->InMaterialPage());
|
|
}
|
|
|
|
if (bMaterialChanged)
|
|
{
|
|
FlushBufferedPrimitives();
|
|
#ifdef RECORDING
|
|
RECORD_DEBUG_STRING((char*)pMaterial->GetName());
|
|
IShader* pShader = pMatInt->GetShader();
|
|
if (pShader && pShader->GetName())
|
|
{
|
|
RECORD_DEBUG_STRING(pShader->GetName());
|
|
}
|
|
else
|
|
{
|
|
RECORD_DEBUG_STRING("<NULL SHADER>");
|
|
}
|
|
#endif
|
|
m_pMaterial = pMatInt;
|
|
|
|
#if ( defined( PIX_INSTRUMENTATION ) || defined( NVPERFHUD ) )
|
|
PIXifyName(s_pPIXMaterialName, sizeof(s_pPIXMaterialName), m_pMaterial->GetName());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Get the currently bound material
|
|
IMaterialInternal* CShaderAPIDx8::GetBoundMaterial()
|
|
{
|
|
return m_pMaterial;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Binds a standard texture
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::BindStandardTexture(Sampler_t sampler, StandardTextureId_t id)
|
|
{
|
|
if (m_StdTextureHandles[id] != INVALID_SHADERAPI_TEXTURE_HANDLE)
|
|
{
|
|
BindTexture(sampler, m_StdTextureHandles[id]);
|
|
}
|
|
else
|
|
{
|
|
ShaderUtil()->BindStandardTexture(sampler, id);
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::BindStandardVertexTexture(VertexTextureSampler_t sampler, StandardTextureId_t id)
|
|
{
|
|
ShaderUtil()->BindStandardVertexTexture(sampler, id);
|
|
}
|
|
|
|
void CShaderAPIDx8::GetStandardTextureDimensions(int* pWidth, int* pHeight, StandardTextureId_t id)
|
|
{
|
|
ShaderUtil()->GetStandardTextureDimensions(pWidth, pHeight, id);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the lightmap dimensions
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::GetLightmapDimensions(int* w, int* h)
|
|
{
|
|
ShaderUtil()->GetLightmapDimensions(w, h);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Selection mode methods
|
|
//-----------------------------------------------------------------------------
|
|
int CShaderAPIDx8::SelectionMode(bool selectionMode)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
int numHits = m_NumHits;
|
|
if (m_InSelectionMode)
|
|
{
|
|
WriteHitRecord();
|
|
}
|
|
m_InSelectionMode = selectionMode;
|
|
m_pCurrSelectionRecord = m_pSelectionBuffer;
|
|
m_NumHits = 0;
|
|
return numHits;
|
|
}
|
|
|
|
bool CShaderAPIDx8::IsInSelectionMode() const
|
|
{
|
|
return m_InSelectionMode;
|
|
}
|
|
|
|
void CShaderAPIDx8::SelectionBuffer(unsigned int* pBuffer, int size)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
Assert(!m_InSelectionMode);
|
|
Assert(pBuffer && size);
|
|
m_pSelectionBufferEnd = pBuffer + size;
|
|
m_pSelectionBuffer = pBuffer;
|
|
m_pCurrSelectionRecord = pBuffer;
|
|
}
|
|
|
|
void CShaderAPIDx8::ClearSelectionNames()
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (m_InSelectionMode)
|
|
{
|
|
WriteHitRecord();
|
|
}
|
|
m_SelectionNames.Clear();
|
|
}
|
|
|
|
void CShaderAPIDx8::LoadSelectionName(int name)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (m_InSelectionMode)
|
|
{
|
|
WriteHitRecord();
|
|
Assert(m_SelectionNames.Count() > 0);
|
|
m_SelectionNames.Top() = name;
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::PushSelectionName(int name)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (m_InSelectionMode)
|
|
{
|
|
WriteHitRecord();
|
|
m_SelectionNames.Push(name);
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::PopSelectionName()
|
|
{
|
|
LOCK_SHADERAPI();
|
|
if (m_InSelectionMode)
|
|
{
|
|
WriteHitRecord();
|
|
m_SelectionNames.Pop();
|
|
}
|
|
}
|
|
|
|
void CShaderAPIDx8::WriteHitRecord()
|
|
{
|
|
FlushBufferedPrimitives();
|
|
|
|
if (m_SelectionNames.Count() && (m_SelectionMinZ != FLT_MAX))
|
|
{
|
|
Assert(m_pCurrSelectionRecord + m_SelectionNames.Count() + 3 < m_pSelectionBufferEnd);
|
|
*m_pCurrSelectionRecord++ = m_SelectionNames.Count();
|
|
// NOTE: because of rounding, "(uint32)(float)UINT32_MAX" yields zero(!), hence the use of doubles.
|
|
// [ ALSO: As of Nov 2011, VS2010 exhibits a debug build code-gen bug if we cast the result to int32 instead of uint32 ]
|
|
*m_pCurrSelectionRecord++ = (uint32)(0.5 + m_SelectionMinZ * (double)((uint32)~0));
|
|
*m_pCurrSelectionRecord++ = (uint32)(0.5 + m_SelectionMaxZ * (double)((uint32)~0));
|
|
for (int i = 0; i < m_SelectionNames.Count(); ++i)
|
|
{
|
|
*m_pCurrSelectionRecord++ = m_SelectionNames[i];
|
|
}
|
|
|
|
++m_NumHits;
|
|
}
|
|
|
|
m_SelectionMinZ = FLT_MAX;
|
|
m_SelectionMaxZ = FLT_MIN;
|
|
}
|
|
|
|
// We hit somefin in selection mode
|
|
void CShaderAPIDx8::RegisterSelectionHit(float minz, float maxz)
|
|
{
|
|
if (minz < 0)
|
|
minz = 0;
|
|
if (maxz > 1)
|
|
maxz = 1;
|
|
if (m_SelectionMinZ > minz)
|
|
m_SelectionMinZ = minz;
|
|
if (m_SelectionMaxZ < maxz)
|
|
m_SelectionMaxZ = maxz;
|
|
}
|
|
|
|
int CShaderAPIDx8::GetCurrentNumBones(void) const
|
|
{
|
|
return m_DynamicState.m_NumBones;
|
|
}
|
|
|
|
bool CShaderAPIDx8::IsHWMorphingEnabled() const
|
|
{
|
|
return m_DynamicState.m_bHWMorphingEnabled;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Inserts the lighting block into the code
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// If you change the number of lighting combinations, change this enum
|
|
enum
|
|
{
|
|
DX8_LIGHTING_COMBINATION_COUNT = 22,
|
|
DX9_LIGHTING_COMBINATION_COUNT = 35
|
|
};
|
|
|
|
#define MAX_LIGHTS 4
|
|
|
|
|
|
// NOTE: These should match g_lightType* in vsh_prep.pl!
|
|
static int g_DX8LightCombinations[][4] =
|
|
{
|
|
// static ambient local1 local2
|
|
|
|
// This is a special case for no lighting at all.
|
|
{ LIGHT_NONE, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE },
|
|
|
|
// This is a special case so that we don't have to do the ambient cube
|
|
// when we only have static lighting
|
|
{ LIGHT_STATIC, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE },
|
|
|
|
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_NONE, LIGHT_NONE },
|
|
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_NONE },
|
|
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_NONE },
|
|
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_NONE },
|
|
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_SPOT },
|
|
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_POINT, },
|
|
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_DIRECTIONAL, },
|
|
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_POINT, },
|
|
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_DIRECTIONAL, },
|
|
{ LIGHT_NONE, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, },
|
|
|
|
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_NONE, LIGHT_NONE },
|
|
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_NONE },
|
|
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_NONE },
|
|
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_NONE },
|
|
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_SPOT },
|
|
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_POINT, },
|
|
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_SPOT, LIGHT_DIRECTIONAL, },
|
|
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_POINT, },
|
|
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_POINT, LIGHT_DIRECTIONAL, },
|
|
{ LIGHT_STATIC, LIGHT_AMBIENTCUBE, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, }
|
|
};
|
|
|
|
// NOTE: These should match g_lightType* in vsh_prep.pl!
|
|
// They also correspond to the parallel g_LocalLightTypeXArray[] arrays in common_vs_fxc.h
|
|
static int g_DX9LightCombinations[][MAX_LIGHTS] =
|
|
{
|
|
// local0 local1 local2 local3
|
|
{ LIGHT_NONE, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE }, // Zero lights [ Combo 0]
|
|
|
|
{ LIGHT_SPOT, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE }, // One light [ Combo 1]
|
|
{ LIGHT_POINT, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE },
|
|
{ LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE, LIGHT_NONE },
|
|
|
|
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_NONE, LIGHT_NONE }, // Two lights [ Combo 4]
|
|
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_NONE, LIGHT_NONE },
|
|
{ LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE },
|
|
{ LIGHT_POINT, LIGHT_POINT, LIGHT_NONE, LIGHT_NONE },
|
|
{ LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE },
|
|
{ LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE, LIGHT_NONE },
|
|
|
|
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_NONE }, // Three lights [ Combo 10]
|
|
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT, LIGHT_NONE },
|
|
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_NONE },
|
|
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT, LIGHT_NONE },
|
|
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_NONE },
|
|
{ LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE },
|
|
{ LIGHT_POINT, LIGHT_POINT, LIGHT_POINT, LIGHT_NONE },
|
|
{ LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_NONE },
|
|
{ LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE },
|
|
{ LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_NONE },
|
|
|
|
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT }, // Four lights [ Combo 20]
|
|
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT },
|
|
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_SPOT, LIGHT_DIRECTIONAL },
|
|
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT },
|
|
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_POINT, LIGHT_DIRECTIONAL },
|
|
{ LIGHT_SPOT, LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
|
|
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT, LIGHT_POINT },
|
|
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL },
|
|
{ LIGHT_SPOT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
|
|
{ LIGHT_SPOT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
|
|
{ LIGHT_POINT, LIGHT_POINT, LIGHT_POINT, LIGHT_POINT },
|
|
{ LIGHT_POINT, LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL },
|
|
{ LIGHT_POINT, LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
|
|
{ LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL },
|
|
{ LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL, LIGHT_DIRECTIONAL }
|
|
};
|
|
|
|
// This is just for getting light combos for DX8
|
|
// For DX9, use GetDX9LightState()
|
|
// It is up to the shader cpp files to use the right method
|
|
int CShaderAPIDx8::GetCurrentLightCombo(void) const
|
|
{
|
|
Assert(g_pHardwareConfig->Caps().m_nDXSupportLevel <= 81);
|
|
|
|
Assert(m_DynamicState.m_NumLights <= 2);
|
|
|
|
COMPILE_TIME_ASSERT(DX8_LIGHTING_COMBINATION_COUNT ==
|
|
sizeof(g_DX8LightCombinations) / sizeof(g_DX8LightCombinations[0]));
|
|
|
|
// hack . . do this a cheaper way.
|
|
bool bUseAmbientCube;
|
|
if (m_DynamicState.m_AmbientLightCube[0][0] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[0][1] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[0][2] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[1][0] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[1][1] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[1][2] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[2][0] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[2][1] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[2][2] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[3][0] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[3][1] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[3][2] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[4][0] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[4][1] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[4][2] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[5][0] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[5][1] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[5][2] == 0.0f)
|
|
{
|
|
bUseAmbientCube = false;
|
|
}
|
|
else
|
|
{
|
|
bUseAmbientCube = true;
|
|
}
|
|
|
|
Assert(m_pRenderMesh);
|
|
|
|
const VertexShaderLightTypes_t* pLightType = m_DynamicState.m_LightType;
|
|
|
|
if (m_DynamicState.m_NumLights == 0 && !bUseAmbientCube)
|
|
{
|
|
if (m_pRenderMesh->HasColorMesh())
|
|
return 1; // special case for static lighting only
|
|
else
|
|
return 0; // special case for no lighting at all.
|
|
}
|
|
|
|
int i;
|
|
// hack - skip the first two for now since we don't know if the ambient cube is needed or not.
|
|
for (i = 2; i < DX9_LIGHTING_COMBINATION_COUNT; ++i)
|
|
{
|
|
int j;
|
|
for (j = 0; j < m_DynamicState.m_NumLights; ++j)
|
|
{
|
|
if (pLightType[j] != g_DX8LightCombinations[i][j + 2])
|
|
break;
|
|
}
|
|
if (j == m_DynamicState.m_NumLights)
|
|
{
|
|
while (j < 2)
|
|
{
|
|
if (g_DX8LightCombinations[i][j + 2] != LIGHT_NONE)
|
|
break;
|
|
++j;
|
|
}
|
|
if (j == 2)
|
|
{
|
|
if (m_pRenderMesh->HasColorMesh())
|
|
{
|
|
return i + 10;
|
|
}
|
|
else
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// should never get here!
|
|
Assert(0);
|
|
return 0;
|
|
}
|
|
|
|
void CShaderAPIDx8::GetDX9LightState(LightState_t* state) const
|
|
{
|
|
// hack . . do this a cheaper way.
|
|
if (m_DynamicState.m_AmbientLightCube[0][0] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[0][1] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[0][2] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[1][0] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[1][1] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[1][2] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[2][0] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[2][1] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[2][2] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[3][0] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[3][1] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[3][2] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[4][0] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[4][1] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[4][2] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[5][0] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[5][1] == 0.0f &&
|
|
m_DynamicState.m_AmbientLightCube[5][2] == 0.0f)
|
|
{
|
|
state->m_bAmbientLight = false;
|
|
}
|
|
else
|
|
{
|
|
state->m_bAmbientLight = true;
|
|
}
|
|
|
|
Assert(m_pRenderMesh);
|
|
Assert(m_DynamicState.m_NumLights <= 4);
|
|
|
|
if (g_pHardwareConfig->SupportsPixelShaders_2_b())
|
|
{
|
|
Assert(m_DynamicState.m_NumLights <= MAX_LIGHTS); // 2b hardware gets four lights
|
|
}
|
|
else
|
|
{
|
|
Assert(m_DynamicState.m_NumLights <= (MAX_LIGHTS - 2)); // 2.0 hardware gets two less
|
|
}
|
|
|
|
#ifdef OSX
|
|
state->m_nNumLights = MIN(MAX_NUM_LIGHTS, m_DynamicState.m_NumLights);
|
|
#else
|
|
state->m_nNumLights = m_DynamicState.m_NumLights;
|
|
#endif
|
|
|
|
state->m_nNumLights = m_DynamicState.m_NumLights;
|
|
state->m_bStaticLightVertex = m_pRenderMesh->HasColorMesh();
|
|
state->m_bStaticLightTexel = false; // For now
|
|
}
|
|
|
|
MaterialFogMode_t CShaderAPIDx8::GetCurrentFogType(void) const
|
|
{
|
|
return m_DynamicState.m_SceneFog;
|
|
}
|
|
|
|
void CShaderAPIDx8::RecordString(const char* pStr)
|
|
{
|
|
RECORD_STRING(pStr);
|
|
}
|
|
|
|
void CShaderAPIDx8::EvictManagedResourcesInternal()
|
|
{
|
|
if (IsX360())
|
|
return;
|
|
|
|
if (!ThreadOwnsDevice() || !ThreadInMainThread())
|
|
{
|
|
ShaderUtil()->OnThreadEvent(SHADER_THREAD_EVICT_RESOURCES);
|
|
return;
|
|
}
|
|
if (mat_debugalttab.GetBool())
|
|
{
|
|
Warning("mat_debugalttab: CShaderAPIDx8::EvictManagedResourcesInternal\n");
|
|
}
|
|
|
|
#if !defined( _X360 )
|
|
if (Dx9Device())
|
|
{
|
|
Dx9Device()->EvictManagedResources();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CShaderAPIDx8::EvictManagedResources(void)
|
|
{
|
|
if (IsX360())
|
|
{
|
|
return;
|
|
}
|
|
|
|
LOCK_SHADERAPI();
|
|
Assert(ThreadOwnsDevice());
|
|
// Tell other material system applications to release resources
|
|
SendIPCMessage(EVICT_MESSAGE);
|
|
EvictManagedResourcesInternal();
|
|
}
|
|
|
|
bool CShaderAPIDx8::IsDebugTextureListFresh(int numFramesAllowed /* = 1 */)
|
|
{
|
|
return (m_nDebugDataExportFrame <= m_CurrentFrame) && (m_nDebugDataExportFrame >= m_CurrentFrame - numFramesAllowed);
|
|
}
|
|
|
|
bool CShaderAPIDx8::SetDebugTextureRendering(bool bEnable)
|
|
{
|
|
bool bVal = m_bDebugTexturesRendering;
|
|
m_bDebugTexturesRendering = bEnable;
|
|
return bVal;
|
|
}
|
|
|
|
void CShaderAPIDx8::EnableDebugTextureList(bool bEnable)
|
|
{
|
|
m_bEnableDebugTextureList = bEnable;
|
|
}
|
|
|
|
void CShaderAPIDx8::EnableGetAllTextures(bool bEnable)
|
|
{
|
|
m_bDebugGetAllTextures = bEnable;
|
|
}
|
|
|
|
KeyValues* CShaderAPIDx8::GetDebugTextureList()
|
|
{
|
|
return m_pDebugTextureList;
|
|
}
|
|
|
|
int CShaderAPIDx8::GetTextureMemoryUsed(TextureMemoryType eTextureMemory)
|
|
{
|
|
switch (eTextureMemory)
|
|
{
|
|
case MEMORY_BOUND_LAST_FRAME:
|
|
return m_nTextureMemoryUsedLastFrame;
|
|
case MEMORY_TOTAL_LOADED:
|
|
return m_nTextureMemoryUsedTotal;
|
|
case MEMORY_ESTIMATE_PICMIP_1:
|
|
return m_nTextureMemoryUsedPicMip1;
|
|
case MEMORY_ESTIMATE_PICMIP_2:
|
|
return m_nTextureMemoryUsedPicMip2;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
// Allocate and delete query objects.
|
|
ShaderAPIOcclusionQuery_t CShaderAPIDx8::CreateOcclusionQueryObject(void)
|
|
{
|
|
// don't allow this on <80 because it falls back to wireframe in that case
|
|
if (m_DeviceSupportsCreateQuery == 0 || g_pHardwareConfig->Caps().m_nDXSupportLevel < 80)
|
|
return INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE;
|
|
|
|
// While we're deactivated, m_OcclusionQueryObjects just holds NULL pointers.
|
|
// Create a dummy one here and let ReacquireResources create the actual D3D object.
|
|
if (IsDeactivated())
|
|
return INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE;
|
|
|
|
IDirect3DQuery9* pQuery = NULL;
|
|
HRESULT hr = Dx9Device()->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pQuery);
|
|
return (hr == D3D_OK) ? (ShaderAPIOcclusionQuery_t)pQuery : INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE;
|
|
}
|
|
|
|
void CShaderAPIDx8::DestroyOcclusionQueryObject(ShaderAPIOcclusionQuery_t handle)
|
|
{
|
|
IDirect3DQuery9* pQuery = (IDirect3DQuery9*)handle;
|
|
|
|
int nRetVal = pQuery->Release();
|
|
Assert(nRetVal == 0);
|
|
}
|
|
|
|
// Bracket drawing with begin and end so that we can get counts next frame.
|
|
void CShaderAPIDx8::BeginOcclusionQueryDrawing(ShaderAPIOcclusionQuery_t handle)
|
|
{
|
|
IDirect3DQuery9* pQuery = (IDirect3DQuery9*)handle;
|
|
|
|
HRESULT hResult = pQuery->Issue(D3DISSUE_BEGIN);
|
|
Assert(hResult == D3D_OK);
|
|
}
|
|
|
|
void CShaderAPIDx8::EndOcclusionQueryDrawing(ShaderAPIOcclusionQuery_t handle)
|
|
{
|
|
IDirect3DQuery9* pQuery = (IDirect3DQuery9*)handle;
|
|
|
|
HRESULT hResult = pQuery->Issue(D3DISSUE_END);
|
|
Assert(hResult == D3D_OK);
|
|
}
|
|
|
|
// Get the number of pixels rendered between begin and end on an earlier frame.
|
|
// Calling this in the same frame is a huge perf hit!
|
|
int CShaderAPIDx8::OcclusionQuery_GetNumPixelsRendered(ShaderAPIOcclusionQuery_t handle, bool bFlush)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
IDirect3DQuery9* pQuery = (IDirect3DQuery9*)handle;
|
|
|
|
tmZone(TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__);
|
|
|
|
DWORD nPixels;
|
|
HRESULT hResult = pQuery->GetData(&nPixels, sizeof(nPixels), bFlush ? D3DGETDATA_FLUSH : 0);
|
|
|
|
// This means that the query will not finish and resulted in an error, game should use
|
|
// the previous query's results and not reissue the query
|
|
if ((hResult == D3DERR_DEVICELOST) || (hResult == D3DERR_NOTAVAILABLE))
|
|
return OCCLUSION_QUERY_RESULT_ERROR;
|
|
|
|
// This means the query isn't finished yet, game will have to use the previous query's
|
|
// results and not reissue the query; wait for query to finish later.
|
|
if (hResult == S_FALSE)
|
|
return OCCLUSION_QUERY_RESULT_PENDING;
|
|
|
|
// NOTE: This appears to work around a driver bug for ATI on Vista
|
|
if (nPixels & 0x80000000)
|
|
{
|
|
nPixels = 0;
|
|
}
|
|
return nPixels;
|
|
}
|
|
|
|
|
|
void CShaderAPIDx8::SetPixelShaderFogParams(int reg, ShaderFogMode_t fogMode)
|
|
{
|
|
m_DelayedShaderConstants.iPixelShaderFogParams = reg; //save it off in case the ShaderFogMode_t disables fog. We only find out later.
|
|
float fogParams[4];
|
|
|
|
if ((GetPixelFogMode() != MATERIAL_FOG_NONE) && (fogMode != SHADER_FOGMODE_DISABLED))
|
|
{
|
|
float ooFogRange = 1.0f;
|
|
|
|
float fStart = m_VertexShaderFogParams[0];
|
|
float fEnd = m_VertexShaderFogParams[1];
|
|
|
|
// Check for divide by zero
|
|
if (fEnd != fStart)
|
|
{
|
|
ooFogRange = 1.0f / (fEnd - fStart);
|
|
}
|
|
|
|
fogParams[0] = fStart * ooFogRange; // fogStart / ( fogEnd - fogStart )
|
|
fogParams[1] = m_DynamicState.m_FogZ; // water height
|
|
fogParams[2] = clamp(m_flFogMaxDensity, 0.0f, 1.0f); // Max fog density
|
|
fogParams[3] = ooFogRange; // 1 / ( fogEnd - fogStart );
|
|
|
|
if (GetPixelFogMode() == MATERIAL_FOG_LINEAR_BELOW_FOG_Z)
|
|
{
|
|
// terms are unused for height fog, forcing 1.0 allows unified PS math
|
|
fogParams[0] = 0.0f;
|
|
fogParams[2] = 1.0f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//emulating MATERIAL_FOG_NONE by setting the parameters so that CalcRangeFog() always returns 0. Gets rid of a dynamic combo across the ps2x set.
|
|
fogParams[0] = 0.0f;
|
|
fogParams[1] = m_DynamicState.m_FogZ; // water height
|
|
fogParams[2] = 1.0f; // Max fog density
|
|
fogParams[3] = 0.0f;
|
|
}
|
|
|
|
// cFogEndOverFogRange, cFogOne, unused, cOOFogRange
|
|
SetPixelShaderConstant(reg, fogParams, 1);
|
|
}
|
|
|
|
void CShaderAPIDx8::SetPixelShaderFogParams(int reg)
|
|
{
|
|
SetPixelShaderFogParams(reg, m_TransitionTable.CurrentShadowState()->m_FogMode);
|
|
}
|
|
|
|
void CShaderAPIDx8::SetFlashlightState(const FlashlightState_t& state, const VMatrix& worldToTexture)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
SetFlashlightStateEx(state, worldToTexture, NULL);
|
|
}
|
|
|
|
void CShaderAPIDx8::SetFlashlightStateEx(const FlashlightState_t& state, const VMatrix& worldToTexture, ITexture* pFlashlightDepthTexture)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
// fixme: do a test here.
|
|
FlushBufferedPrimitives();
|
|
m_FlashlightState = state;
|
|
m_FlashlightWorldToTexture = worldToTexture;
|
|
m_pFlashlightDepthTexture = pFlashlightDepthTexture;
|
|
|
|
if (!g_pHardwareConfig->SupportsPixelShaders_2_b())
|
|
{
|
|
m_FlashlightState.m_bEnableShadows = false;
|
|
m_pFlashlightDepthTexture = NULL;
|
|
}
|
|
}
|
|
|
|
const FlashlightState_t& CShaderAPIDx8::GetFlashlightState(VMatrix& worldToTexture) const
|
|
{
|
|
worldToTexture = m_FlashlightWorldToTexture;
|
|
return m_FlashlightState;
|
|
}
|
|
|
|
const FlashlightState_t& CShaderAPIDx8::GetFlashlightStateEx(VMatrix& worldToTexture, ITexture** ppFlashlightDepthTexture) const
|
|
{
|
|
worldToTexture = m_FlashlightWorldToTexture;
|
|
*ppFlashlightDepthTexture = m_pFlashlightDepthTexture;
|
|
return m_FlashlightState;
|
|
}
|
|
|
|
bool CShaderAPIDx8::SupportsMSAAMode(int nMSAAMode)
|
|
{
|
|
if (IsX360())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return (D3D_OK == D3D()->CheckDeviceMultiSampleType(m_DisplayAdapter, m_DeviceType,
|
|
m_PresentParameters.BackBufferFormat,
|
|
m_PresentParameters.Windowed,
|
|
ComputeMultisampleType(nMSAAMode), NULL));
|
|
}
|
|
|
|
bool CShaderAPIDx8::SupportsCSAAMode(int nNumSamples, int nQualityLevel)
|
|
{
|
|
#ifdef DX_TO_GL_ABSTRACTION
|
|
// GL_NV_framebuffer_multisample_coverage
|
|
return false;
|
|
#endif
|
|
|
|
// Only nVidia does this kind of AA
|
|
if (g_pHardwareConfig->Caps().m_VendorID != VENDORID_NVIDIA)
|
|
return false;
|
|
|
|
DWORD dwQualityLevels = 0;
|
|
HRESULT hr = D3D()->CheckDeviceMultiSampleType(m_DisplayAdapter, m_DeviceType,
|
|
m_PresentParameters.BackBufferFormat,
|
|
m_PresentParameters.Windowed,
|
|
ComputeMultisampleType(nNumSamples), &dwQualityLevels);
|
|
|
|
return ((D3D_OK == hr) && ((int)dwQualityLevels >= nQualityLevel));
|
|
}
|
|
|
|
bool CShaderAPIDx8::SupportsShadowDepthTextures(void)
|
|
{
|
|
return g_pHardwareConfig->Caps().m_bSupportsShadowDepthTextures;
|
|
}
|
|
|
|
bool CShaderAPIDx8::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, ©Rect, 4);
|
|
|
|
// convert argb to rgba
|
|
float r, g, b, a;
|
|
for (int y = 0; y < pHeight[i]; y++)
|
|
{
|
|
unsigned char* pSrc = (unsigned char*)pLinear + y * destPitch;
|
|
for (int x = 0; x < pWidth[i]; x++)
|
|
{
|
|
// undo pre-multiplied alpha since glyph bits will be sourced as a rgba texture
|
|
if (!pSrc[0])
|
|
a = 1;
|
|
else
|
|
a = (float)pSrc[0] * 1.0f / 255.0f;
|
|
|
|
r = ((float)pSrc[1] * 1.0f / 255.0f) / a * 255.0f;
|
|
if (r > 255)
|
|
r = 255;
|
|
|
|
g = ((float)pSrc[2] * 1.0f / 255.0f) / a * 255.0f;
|
|
if (g > 255)
|
|
g = 255;
|
|
|
|
b = ((float)pSrc[3] * 1.0f / 255.0f) / a * 255.0f;
|
|
if (b > 255)
|
|
b = 255;
|
|
|
|
pSrc[3] = pSrc[0];
|
|
pSrc[2] = b;
|
|
pSrc[1] = g;
|
|
pSrc[0] = r;
|
|
|
|
pSrc += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
pTexture->UnlockRect(0);
|
|
|
|
bSuccess = true;
|
|
|
|
cleanUp:
|
|
if (pRTSurface)
|
|
{
|
|
Dx9Device()->SetRenderTarget(0, pSavedSurface);
|
|
Dx9Device()->SetDepthStencilSurface(pSavedDepthSurface);
|
|
Dx9Device()->SetViewport(&savedViewport);
|
|
pRTSurface->Release();
|
|
}
|
|
|
|
if (pTexture)
|
|
pTexture->Release();
|
|
|
|
if (pSavedSurface)
|
|
pSavedSurface->Release();
|
|
|
|
// XUI changed renderstates behind our back, so we need to reset to defaults again to get back in synch:
|
|
ResetRenderState(false);
|
|
|
|
return bSuccess;
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Create a 360 Render Target Surface
|
|
//-----------------------------------------------------------------------------
|
|
#if defined( _X360 )
|
|
ShaderAPITextureHandle_t CShaderAPIDx8::CreateRenderTargetSurface(int width, int height, ImageFormat format, const char* pDebugName, const char* pTextureGroupName)
|
|
{
|
|
LOCK_SHADERAPI();
|
|
ShaderAPITextureHandle_t textureHandle = CreateTextureHandle();
|
|
Texture_t* pTexture = &GetTexture(textureHandle);
|
|
|
|
pTexture->m_Flags = (Texture_t::IS_ALLOCATED | Texture_t::IS_RENDER_TARGET_SURFACE);
|
|
|
|
pTexture->m_DebugName = pDebugName;
|
|
pTexture->m_Width = width;
|
|
pTexture->m_Height = height;
|
|
pTexture->m_Depth = 1;
|
|
pTexture->m_NumCopies = 1;
|
|
pTexture->m_CurrentCopy = 0;
|
|
|
|
ImageFormat dstImageFormat = FindNearestSupportedFormat(format, false, true, false);
|
|
D3DFORMAT actualFormat = ImageLoader::ImageFormatToD3DFormat(dstImageFormat);
|
|
|
|
pTexture->GetRenderTargetSurface(false) = g_TextureHeap.AllocRenderTargetSurface(width, height, actualFormat);
|
|
pTexture->GetRenderTargetSurface(true) = g_TextureHeap.AllocRenderTargetSurface(width, height, (D3DFORMAT)MAKESRGBFMT(actualFormat));
|
|
|
|
pTexture->SetImageFormat(dstImageFormat);
|
|
|
|
pTexture->m_UTexWrap = D3DTADDRESS_CLAMP;
|
|
pTexture->m_VTexWrap = D3DTADDRESS_CLAMP;
|
|
pTexture->m_WTexWrap = D3DTADDRESS_CLAMP;
|
|
pTexture->m_MagFilter = D3DTEXF_LINEAR;
|
|
|
|
pTexture->m_NumLevels = 1;
|
|
pTexture->m_MipFilter = D3DTEXF_NONE;
|
|
pTexture->m_MinFilter = D3DTEXF_LINEAR;
|
|
|
|
pTexture->m_SwitchNeeded = false;
|
|
|
|
ComputeStatsInfo(textureHandle, false, false);
|
|
SetupTextureGroup(textureHandle, pTextureGroupName);
|
|
|
|
return textureHandle;
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Shader constants are batched and written to gpu once prior to draw.
|
|
//-----------------------------------------------------------------------------
|
|
void CShaderAPIDx8::WriteShaderConstantsToGPU()
|
|
{
|
|
#if defined( _X360 )
|
|
// vector vertex constants can just blast their set range
|
|
if (m_MaxVectorVertexShaderConstant)
|
|
{
|
|
if (m_bGPUOwned)
|
|
{
|
|
// faster path, write directly into GPU command buffer, bypassing shadow state
|
|
// can only set what is actually owned
|
|
Assert(m_MaxVectorVertexShaderConstant <= VERTEX_SHADER_MODEL + 3 * NUM_MODEL_TRANSFORMS);
|
|
int numVectors = AlignValue(m_MaxVectorVertexShaderConstant, 4);
|
|
BYTE* pCommandBufferData;
|
|
Dx9Device()->GpuBeginVertexShaderConstantF4(0, (D3DVECTOR4**)&pCommandBufferData, numVectors);
|
|
memcpy(pCommandBufferData, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), numVectors * (sizeof(float) * 4));
|
|
Dx9Device()->GpuEndVertexShaderConstantF4();
|
|
}
|
|
else
|
|
{
|
|
Dx9Device()->SetVertexShaderConstantF(0, m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), m_MaxVectorVertexShaderConstant);
|
|
}
|
|
|
|
memcpy(m_DynamicState.m_pVectorVertexShaderConstant[0].Base(), m_DesiredState.m_pVectorVertexShaderConstant[0].Base(), m_MaxVectorVertexShaderConstant * 4 * sizeof(float));
|
|
m_MaxVectorVertexShaderConstant = 0;
|
|
}
|
|
|
|
if (m_MaxVectorPixelShaderConstant)
|
|
{
|
|
if (m_bGPUOwned)
|
|
{
|
|
// faster path, write directly into GPU command buffer, bypassing shadow state
|
|
// can only set what is actually owned
|
|
Assert(m_MaxVectorPixelShaderConstant <= 32);
|
|
int numVectors = AlignValue(m_MaxVectorPixelShaderConstant, 4);
|
|
BYTE* pCommandBufferData;
|
|
Dx9Device()->GpuBeginPixelShaderConstantF4(0, (D3DVECTOR4**)&pCommandBufferData, numVectors);
|
|
memcpy(pCommandBufferData, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), numVectors * (sizeof(float) * 4));
|
|
Dx9Device()->GpuEndPixelShaderConstantF4();
|
|
}
|
|
else
|
|
{
|
|
Dx9Device()->SetPixelShaderConstantF(0, m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), m_MaxVectorPixelShaderConstant);
|
|
}
|
|
|
|
memcpy(m_DynamicState.m_pVectorPixelShaderConstant[0].Base(), m_DesiredState.m_pVectorPixelShaderConstant[0].Base(), m_MaxVectorPixelShaderConstant * 4 * sizeof(float));
|
|
m_MaxVectorPixelShaderConstant = 0;
|
|
}
|
|
|
|
// boolean and integer constants can just blast their set range
|
|
// these are currently extremely small in number, if this changes they may benefit from a fast path pattern
|
|
if (m_MaxBooleanVertexShaderConstant)
|
|
{
|
|
Dx9Device()->SetVertexShaderConstantB(0, m_DesiredState.m_pBooleanVertexShaderConstant, m_MaxBooleanVertexShaderConstant);
|
|
memcpy(m_DynamicState.m_pBooleanVertexShaderConstant, m_DesiredState.m_pBooleanVertexShaderConstant, m_MaxBooleanVertexShaderConstant * sizeof(BOOL));
|
|
m_MaxBooleanVertexShaderConstant = 0;
|
|
}
|
|
if (m_MaxIntegerVertexShaderConstant)
|
|
{
|
|
Dx9Device()->SetVertexShaderConstantI(0, (int*)m_DesiredState.m_pIntegerVertexShaderConstant, m_MaxIntegerVertexShaderConstant);
|
|
memcpy(m_DynamicState.m_pIntegerVertexShaderConstant[0].Base(), m_DesiredState.m_pIntegerVertexShaderConstant[0].Base(), m_MaxIntegerVertexShaderConstant * sizeof(IntVector4D));
|
|
m_MaxIntegerVertexShaderConstant = 0;
|
|
}
|
|
|
|
if (m_MaxBooleanPixelShaderConstant)
|
|
{
|
|
Dx9Device()->SetPixelShaderConstantB(0, m_DesiredState.m_pBooleanPixelShaderConstant, m_MaxBooleanPixelShaderConstant);
|
|
memcpy(m_DynamicState.m_pBooleanPixelShaderConstant, m_DesiredState.m_pBooleanPixelShaderConstant, m_MaxBooleanPixelShaderConstant * sizeof(BOOL));
|
|
m_MaxBooleanPixelShaderConstant = 0;
|
|
}
|
|
|
|
// integer pixel constants are not used, so not supporting
|
|
#if 0
|
|
if (m_MaxIntegerPixelShaderConstant)
|
|
{
|
|
Dx9Device()->SetPixelShaderConstantI(0, (int*)m_DesiredState.m_pIntegerPixelShaderConstant, m_MaxIntegerPixelShaderConstant);
|
|
memcpy(m_DynamicState.m_pIntegerPixelShaderConstant[0].Base(), m_DesiredState.m_pIntegerPixelShaderConstant[0].Base(), m_MaxIntegerPixelShaderConstant * sizeof(IntVector4D));
|
|
m_MaxIntegerPixelShaderConstant = 0;
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The application is about to perform a hard reboot, but wants to hide the screen flash
|
|
// by persisting the front buffer across a reboot boundary. The persisted frame buffer
|
|
// can be detected and restored.
|
|
//-----------------------------------------------------------------------------
|
|
#if defined( _X360 )
|
|
void CShaderAPIDx8::PersistDisplay()
|
|
{
|
|
if (m_PresentParameters.FrontBufferFormat != D3DFMT_LE_X8R8G8B8)
|
|
{
|
|
// The format must be what PersistDisplay() expects, otherwise D3DRIP.
|
|
// If this hits due to sRGB bit set that confuses PersistDisplay(),
|
|
// the fix may be to slam the presentation parameters to the expected format,
|
|
// do a ResetDevice(), and then PersistDisplay().
|
|
Assert(0);
|
|
return;
|
|
}
|
|
|
|
IDirect3DTexture* pTexture;
|
|
HRESULT hr = Dx9Device()->GetFrontBuffer(&pTexture);
|
|
if (!FAILED(hr))
|
|
{
|
|
OwnGPUResources(false);
|
|
Dx9Device()->PersistDisplay(pTexture, NULL);
|
|
pTexture->Release();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined( _X360 )
|
|
bool CShaderAPIDx8::PostQueuedTexture(const void* pData, int nDataSize, ShaderAPITextureHandle_t* pHandles, int numHandles, int nWidth, int nHeight, int nDepth, int numMips, int* pRefCount)
|
|
{
|
|
CUtlBuffer vtfBuffer;
|
|
IVTFTexture* pVTFTexture = NULL;
|
|
bool bOK = false;
|
|
|
|
if (!pData || !nDataSize)
|
|
{
|
|
// invalid
|
|
goto cleanUp;
|
|
}
|
|
|
|
// get a unique vtf and mount texture
|
|
// vtf can expect non-volatile buffer data to be stable through vtf lifetime
|
|
// this prevents redundant copious amounts of image memory transfers
|
|
pVTFTexture = CreateVTFTexture();
|
|
vtfBuffer.SetExternalBuffer((void*)pData, nDataSize, nDataSize);
|
|
if (!pVTFTexture->UnserializeFromBuffer(vtfBuffer, false, false, false, 0))
|
|
{
|
|
goto cleanUp;
|
|
}
|
|
|
|
// provided vtf buffer is all mips, determine top mip due to possible picmip
|
|
int iTopMip = 0;
|
|
int mipWidth, mipHeight, mipDepth;
|
|
do
|
|
{
|
|
pVTFTexture->ComputeMipLevelDimensions(iTopMip, &mipWidth, &mipHeight, &mipDepth);
|
|
if (nWidth == mipWidth && nHeight == mipHeight && nDepth == mipDepth)
|
|
{
|
|
break;
|
|
}
|
|
iTopMip++;
|
|
} while (mipWidth != 1 || mipHeight != 1 || mipDepth != 1);
|
|
|
|
// create and blit
|
|
for (int iFrame = 0; iFrame < numHandles; iFrame++)
|
|
{
|
|
ShaderAPITextureHandle_t hTexture = pHandles[iFrame];
|
|
Texture_t* pTexture = &GetTexture(hTexture);
|
|
|
|
int nFaceCount = (pTexture->m_CreationFlags & TEXTURE_CREATE_CUBEMAP) ? CUBEMAP_FACE_COUNT - 1 : 1;
|
|
|
|
IDirect3DBaseTexture* pD3DTexture;
|
|
if (pTexture->m_CreationFlags & TEXTURE_CREATE_NOD3DMEMORY)
|
|
{
|
|
pD3DTexture = pTexture->GetTexture();
|
|
if (!g_TextureHeap.AllocD3DMemory(pD3DTexture))
|
|
{
|
|
goto cleanUp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pD3DTexture = pTexture->GetTexture();
|
|
}
|
|
|
|
// blit the hi-res texture bits into d3d memory
|
|
for (int iFace = 0; iFace < nFaceCount; ++iFace)
|
|
{
|
|
for (int iMip = 0; iMip < numMips; ++iMip)
|
|
{
|
|
pVTFTexture->ComputeMipLevelDimensions(iTopMip + iMip, &mipWidth, &mipHeight, &mipDepth);
|
|
unsigned char* pSourceBits = pVTFTexture->ImageData(iFrame, iFace, iTopMip + iMip, 0, 0, 0);
|
|
|
|
TextureLoadInfo_t info;
|
|
info.m_TextureHandle = hTexture;
|
|
info.m_pTexture = pD3DTexture;
|
|
info.m_nLevel = iMip;
|
|
info.m_nCopy = 0;
|
|
info.m_CubeFaceID = (D3DCUBEMAP_FACES)iFace;
|
|
info.m_nWidth = mipWidth;
|
|
info.m_nHeight = mipHeight;
|
|
info.m_nZOffset = 0;
|
|
info.m_SrcFormat = pVTFTexture->Format();
|
|
info.m_pSrcData = pSourceBits;
|
|
info.m_bSrcIsTiled = pVTFTexture->IsPreTiled();
|
|
info.m_bCanConvertFormat = (pTexture->m_Flags & Texture_t::CAN_CONVERT_FORMAT) != 0;
|
|
LoadTexture(info);
|
|
}
|
|
}
|
|
|
|
pTexture->m_Flags |= Texture_t::IS_FINALIZED;
|
|
(*pRefCount)--;
|
|
}
|
|
|
|
// success
|
|
bOK = true;
|
|
|
|
cleanUp:
|
|
if (pVTFTexture)
|
|
{
|
|
DestroyVTFTexture(pVTFTexture);
|
|
}
|
|
|
|
if (!bOK)
|
|
{
|
|
// undo artificial lock
|
|
(*pRefCount) -= numHandles;
|
|
}
|
|
|
|
return bOK;
|
|
}
|
|
#endif
|
|
|
|
#if defined( _X360 )
|
|
void* CShaderAPIDx8::GetD3DDevice()
|
|
{
|
|
return Dx9Device();
|
|
}
|
|
#endif
|
|
|
|
#if defined( _X360 )
|
|
static void r_enable_gpr_allocations_callback(IConVar* var, const char* pOldValue, float flOldValue)
|
|
{
|
|
if (((ConVar*)var)->GetBool() == false)
|
|
{
|
|
//reset back the default 64/64 allocation before we stop updating
|
|
if (Dx9Device() != NULL)
|
|
{
|
|
Dx9Device()->SetShaderGPRAllocation(0, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
ConVar r_enable_gpr_allocations("r_enable_gpr_allocations", "1", 0, "Enable usage of IDirect3DDevice9::SetShaderGPRAllocation()", r_enable_gpr_allocations_callback);
|
|
|
|
static void CommitShaderGPRs(IDirect3DDevice9* pDevice, const DynamicState_t& desiredState, DynamicState_t& 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
|