summaryrefslogtreecommitdiff
path: root/Runtime/GfxDevice/d3d/GfxDeviceD3D9.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/GfxDevice/d3d/GfxDeviceD3D9.cpp')
-rw-r--r--Runtime/GfxDevice/d3d/GfxDeviceD3D9.cpp3009
1 files changed, 3009 insertions, 0 deletions
diff --git a/Runtime/GfxDevice/d3d/GfxDeviceD3D9.cpp b/Runtime/GfxDevice/d3d/GfxDeviceD3D9.cpp
new file mode 100644
index 0000000..77fe956
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/GfxDeviceD3D9.cpp
@@ -0,0 +1,3009 @@
+#include "UnityPrefix.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "GfxDeviceD3D9.h"
+#include "D3D9Context.h"
+#include "Runtime/Math/FloatConversion.h"
+#include "D3D9VBO.h"
+#include "CombinerD3D.h"
+#include "External/shaderlab/Library/program.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "External/shaderlab/Library/pass.h"
+#include "Runtime/GfxDevice/BuiltinShaderParams.h"
+#include "Runtime/GfxDevice/GpuProgramParamsApply.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "PlatformDependent/Win/SmartComPointer.h"
+#include "PlatformDependent/Win/WinUnicode.h"
+#include "Runtime/Allocator/LinearAllocator.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Misc/Plugins.h"
+#include "D3D9Utils.h"
+#include "D3D9Window.h"
+#include "RenderTextureD3D.h"
+#include "GpuProgramsD3D.h"
+#include "TimerQueryD3D9.h"
+#include "GfxDeviceD3D9.h"
+
+
+// --------------------------------------------------------------------------
+
+bool IsActiveRenderTargetWithColorD3D9();
+
+typedef std::list<IDirect3DQuery9*> D3D9QueryList;
+static D3D9QueryList s_EventQueries;
+
+static void PushEventQuery (int maxBuffer);
+static void CleanupEventQueries ();
+
+
+
+static const D3DBLEND kBlendModeD3D9[] = {
+ D3DBLEND_ZERO, D3DBLEND_ONE, D3DBLEND_DESTCOLOR, D3DBLEND_SRCCOLOR, D3DBLEND_INVDESTCOLOR, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCCOLOR,
+ D3DBLEND_DESTALPHA, D3DBLEND_INVDESTALPHA, D3DBLEND_SRCALPHASAT, D3DBLEND_INVSRCALPHA,
+};
+
+static const D3DBLENDOP kBlendOpD3D9[] = {
+ D3DBLENDOP_ADD, D3DBLENDOP_SUBTRACT, D3DBLENDOP_REVSUBTRACT, D3DBLENDOP_MIN, D3DBLENDOP_MAX,
+};
+
+static const D3DCMPFUNC kCmpFuncD3D9[] = {
+ D3DCMP_ALWAYS, D3DCMP_NEVER, D3DCMP_LESS, D3DCMP_EQUAL, D3DCMP_LESSEQUAL, D3DCMP_GREATER, D3DCMP_NOTEQUAL, D3DCMP_GREATEREQUAL, D3DCMP_ALWAYS
+};
+
+static const D3DSTENCILOP kStencilOpD3D9[] = {
+ D3DSTENCILOP_KEEP, D3DSTENCILOP_ZERO, D3DSTENCILOP_REPLACE, D3DSTENCILOP_INCRSAT,
+ D3DSTENCILOP_DECRSAT, D3DSTENCILOP_INVERT, D3DSTENCILOP_INCR, D3DSTENCILOP_DECR
+};
+
+static D3DCULL kCullModeD3D9[] = {
+ D3DCULL_NONE, D3DCULL_CW, D3DCULL_CCW
+};
+
+// --------------------------------------------------------------------------
+
+
+static inline D3DCOLOR ColorToD3D( const float color[4] )
+{
+ return D3DCOLOR_RGBA( NormalizedToByte(color[0]), NormalizedToByte(color[1]), NormalizedToByte(color[2]), NormalizedToByte(color[3]) );
+}
+
+
+
+// --------------------------------------------------------------------------
+
+enum {
+ kNeedsSoftwareVPVertexShader = (1<<0),
+ kNeedsSoftwareVPTexGen = (1<<1),
+};
+
+class GfxDeviceD3D9;
+
+static void ApplyBackfaceMode( DeviceStateD3D& state );
+static void ApplyStencilFuncAndOp( DeviceStateD3D& state );
+
+
+
+
+
+void DeviceStateD3D::Invalidate( GfxDeviceD3D9& device )
+{
+ int i;
+
+ depthFunc = kFuncUnknown;
+ depthWrite = -1;
+
+ blending = -1; // unknown
+ srcBlend = destBlend = srcBlendAlpha = destBlendAlpha = -1; // won't match any D3D mode
+ blendOp = blendOpAlpha = -1; // won't match any D3D mode
+ alphaFunc = kFuncUnknown;
+ alphaValue = -1.0f;
+
+ culling = kCullUnknown;
+ d3dculling = D3DCULL_FORCE_DWORD;
+ scissor = -1;
+
+ offsetFactor = offsetUnits = -1000.0f;
+ for( i = 0; i < kShaderTypeCount; ++i )
+ {
+ activeGpuProgramParams[i] = NULL;
+ activeGpuProgram[i] = NULL;
+ activeShader[i] = NULL;
+ }
+ fixedFunctionPS = 0;
+
+ colorWriteMask = -1; // TBD ?
+ m_StencilRef = -1;
+
+ for (i = 0; i < ARRAY_SIZE(texturesPS); ++i)
+ texturesPS[i].Invalidate();
+ for (i = 0; i < ARRAY_SIZE(texturesVS); ++i)
+ texturesVS[i].Invalidate();
+
+ m_SoftwareVP = false;
+ m_NeedsSofwareVPFlags = 0;
+
+ IDirect3DDevice9* dev = GetD3DDeviceNoAssert();
+ if( dev && !m_DeviceLost )
+ {
+ D3D9_CALL(dev->SetVertexShader( NULL ));
+ D3D9_CALL(dev->SetPixelShader( NULL ));
+
+ ApplyBackfaceMode( *this );
+
+ if( g_D3DUsesMixedVP )
+ D3D9_CALL(dev->SetSoftwareVertexProcessing( FALSE ));
+
+ // misc. state
+ D3D9_CALL(dev->SetRenderState( D3DRS_LOCALVIEWER, TRUE ));
+
+ #if UNITY_EDITOR
+ D3D9_CALL(dev->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ));
+ #endif
+ }
+}
+
+
+void UpdateChannelBindingsD3D( const ChannelAssigns& channels )
+{
+ // Texture coordinate index bindings
+ GfxDeviceD3D9& device = (GfxDeviceD3D9&)GetRealGfxDevice();
+ if( device.IsShaderActive(kShaderVertex) )
+ return;
+ DeviceStateD3D& state = device.GetState();
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ const int maxTexCoords = gGraphicsCaps.maxTexCoords; // fetch here once
+
+ VertexPipeConfig& config = device.GetVertexPipeConfig();
+ UInt32 textureSources = config.textureSources;
+ for( int i = 0; i < maxTexCoords; ++i )
+ {
+ UInt32 source = (textureSources >> (i*3)) & 0x7;
+ if( source > kTexSourceUV1 )
+ continue;
+ ShaderChannel texCoordChannel = channels.GetSourceForTarget( (VertexComponent)(kVertexCompTexCoord0 + i) );
+ if( texCoordChannel == kShaderChannelTexCoord0 )
+ textureSources = textureSources & ~(7<<i*3) | (kTexSourceUV0<<i*3);
+ else if( texCoordChannel == kShaderChannelTexCoord1 )
+ textureSources = textureSources & ~(7<<i*3) | (kTexSourceUV1<<i*3);
+ else if( texCoordChannel != kShaderChannelNone ) {
+ AssertString( "Bad texcoord index" );
+ }
+ }
+ config.textureSources = textureSources;
+
+ config.hasVertexColor = (channels.GetTargetMap() & (1<<kVertexCompColor)) ? 1 : 0;
+}
+
+
+struct SetValuesFunctorD3D9
+{
+ SetValuesFunctorD3D9(GfxDevice& device, VertexShaderConstantCache& vs, PixelShaderConstantCache& ps) : m_Device(device), vscache(vs), pscache(ps) { }
+ GfxDevice& m_Device;
+ VertexShaderConstantCache& vscache;
+ PixelShaderConstantCache& pscache;
+ void SetVectorVal (ShaderType shaderType, ShaderParamType type, int index, const float* ptr, int cols, const GpuProgramParameters& params, int cbIndex)
+ {
+ if (shaderType == kShaderVertex)
+ vscache.SetValues(index, ptr, 1);
+ else
+ pscache.SetValues(index, ptr, 1);
+ }
+ void SetMatrixVal (ShaderType shaderType, int index, const Matrix4x4f* ptr, int rows, const GpuProgramParameters& params, int cbIndex)
+ {
+ DebugAssert(rows == 4);
+ Matrix4x4f mat;
+ TransposeMatrix4x4 (ptr, &mat);
+ if (shaderType == kShaderVertex)
+ vscache.SetValues(index, mat.GetPtr(), 4);
+ else
+ pscache.SetValues(index, mat.GetPtr(), 4);
+ }
+ void SetTextureVal (ShaderType shaderType, int index, int samplerIndex, TextureDimension dim, TextureID texID)
+ {
+ m_Device.SetTexture (shaderType, index, samplerIndex, texID, dim, std::numeric_limits<float>::infinity());
+ }
+};
+
+
+// Compute/Update any deferred state before each draw call
+void GfxDeviceD3D9::BeforeDrawCall( bool immediateMode )
+{
+ VertexShaderConstantCache& vscache = GetVertexShaderConstantCache();
+ PixelShaderConstantCache& pscache = GetPixelShaderConstantCache();
+ DeviceStateD3D& state = m_State;
+ IDirect3DDevice9* dev = GetD3DDevice();
+ bool usesVertexShader = (state.activeShader[kShaderVertex] != NULL);
+
+ //@TODO: remove TESTING CODE
+ static bool oldTnL = false;
+ if( oldTnL != (!immediateMode) )
+ {
+ m_VertexPrevious.config.Reset ();
+ m_VertexPrevious.ambient.set(-1,-1,-1,-1);
+ oldTnL = !immediateMode;
+ }
+
+ m_TransformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+
+ // Deferred setup of fixed function stuff
+ if (!immediateMode)
+ SetupVertexShaderD3D9( dev, m_TransformState, m_BuiltinParamValues, m_VertexConfig, m_VertexData, m_VertexPrevious, vscache, usesVertexShader, immediateMode );
+ else
+ SetupFixedFunctionD3D9( dev, m_TransformState, m_BuiltinParamValues, m_VertexConfig, m_VertexData, m_VertexPrevious, usesVertexShader, immediateMode );
+
+
+ // update GL equivalents of built-in shader state
+
+ const BuiltinShaderParamIndices& paramsVS = *m_BuiltinParamIndices[kShaderVertex];
+ const BuiltinShaderParamIndices& paramsPS = *m_BuiltinParamIndices[kShaderFragment];
+ int gpuIndexVS, gpuIndexPS;
+
+#define SET_BUILTIN_MATRIX_BEGIN(idx) \
+ gpuIndexVS = paramsVS.mat[idx].gpuIndex; gpuIndexPS = paramsPS.mat[idx].gpuIndex; if (gpuIndexVS >= 0 || gpuIndexPS >= 0)
+
+#define SET_BUILTIN_MATRIX_END(name) \
+ if (gpuIndexVS >= 0) vscache.SetValues(gpuIndexVS, name.GetPtr(), 4); \
+ if (gpuIndexPS >= 0) pscache.SetValues(gpuIndexPS, name.GetPtr(), 4)
+
+ // MVP matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatMVP)
+ {
+ Matrix4x4f matMul;
+ MultiplyMatrices4x4 (&m_BuiltinParamValues.GetMatrixParam(kShaderMatProj), &m_TransformState.worldViewMatrix, &matMul);
+ Matrix4x4f mat;
+ TransposeMatrix4x4 (&matMul, &mat);
+ SET_BUILTIN_MATRIX_END(mat);
+ }
+ // MV matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatMV)
+ {
+ Matrix4x4f mat;
+ TransposeMatrix4x4 (&m_TransformState.worldViewMatrix, &mat);
+ SET_BUILTIN_MATRIX_END(mat);
+ }
+ // Transpose MV matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatTransMV)
+ {
+ const Matrix4x4f& mat = m_TransformState.worldViewMatrix;
+ SET_BUILTIN_MATRIX_END(mat);
+ }
+ // Inverse transpose of MV matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatInvTransMV)
+ {
+ Matrix4x4f mat;
+ Matrix4x4f::Invert_Full (m_TransformState.worldViewMatrix, mat);
+ if (m_VertexData.normalization == kNormalizationScale)
+ {
+ // Inverse transpose of modelview should be scaled by uniform
+ // normal scale (this will match state.matrix.invtrans.modelview
+ // and gl_NormalMatrix in OpenGL)
+ float scale = Magnitude (m_TransformState.worldMatrix.GetAxisX());
+ mat.Get (0, 0) *= scale;
+ mat.Get (1, 0) *= scale;
+ mat.Get (2, 0) *= scale;
+ mat.Get (0, 1) *= scale;
+ mat.Get (1, 1) *= scale;
+ mat.Get (2, 1) *= scale;
+ mat.Get (0, 2) *= scale;
+ mat.Get (1, 2) *= scale;
+ mat.Get (2, 2) *= scale;
+ }
+ SET_BUILTIN_MATRIX_END(mat);
+ }
+ // M matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatM)
+ {
+ Matrix4x4f mat;
+ TransposeMatrix4x4 (&m_TransformState.worldMatrix, &mat);
+ SET_BUILTIN_MATRIX_END(mat);
+ }
+ // Inverse M matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatInvM)
+ {
+ Matrix4x4f mat = m_TransformState.worldMatrix;
+ if (m_VertexData.normalization == kNormalizationScale)
+ {
+ // Kill scale in the world matrix before inverse
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ mat.Get (0, 0) *= invScale;
+ mat.Get (1, 0) *= invScale;
+ mat.Get (2, 0) *= invScale;
+ mat.Get (0, 1) *= invScale;
+ mat.Get (1, 1) *= invScale;
+ mat.Get (2, 1) *= invScale;
+ mat.Get (0, 2) *= invScale;
+ mat.Get (1, 2) *= invScale;
+ mat.Get (2, 2) *= invScale;
+ }
+ Matrix4x4f inverseMat;
+ Matrix4x4f::Invert_General3D (mat, inverseMat);
+ TransposeMatrix4x4 (&inverseMat, &mat);
+ SET_BUILTIN_MATRIX_END(mat);
+ }
+
+ // Set instance vector parameters
+ for (int i = 0; i < kShaderInstanceVecCount; ++i)
+ {
+ gpuIndexVS = paramsVS.vec[i].gpuIndex;
+ if (gpuIndexVS >= 0)
+ vscache.SetValues(gpuIndexVS, m_BuiltinParamValues.GetInstanceVectorParam((ShaderBuiltinInstanceVectorParam)i).GetPtr(), 1);
+ gpuIndexPS = paramsPS.vec[i].gpuIndex;
+ if (gpuIndexPS >= 0)
+ pscache.SetValues(gpuIndexPS, m_BuiltinParamValues.GetInstanceVectorParam((ShaderBuiltinInstanceVectorParam)i).GetPtr(), 1);
+ }
+
+ // Texture matrices for vertex shader
+ for( int i = 0; i < 8; ++i )
+ {
+ if( paramsVS.mat[kShaderInstanceMatTexture0 + i].gpuIndex >= 0 )
+ {
+ Matrix4x4f mat;
+ TransposeMatrix4x4 (&m_TransformState.texMatrices[i], &mat);
+ const int index = paramsVS.mat[kShaderInstanceMatTexture0 + i].gpuIndex;
+ vscache.SetValues( index, mat.GetPtr(), 4 );
+ }
+ }
+
+ // Software VP flags
+ if( g_D3DUsesMixedVP )
+ {
+ if( state.m_NeedsSofwareVPFlags )
+ {
+ if( state.m_SoftwareVP == false )
+ {
+ D3D9_CALL(dev->SetSoftwareVertexProcessing( TRUE ));
+ state.m_SoftwareVP = true;
+ }
+ }
+ else
+ {
+ if( state.m_SoftwareVP == true )
+ {
+ D3D9_CALL(dev->SetSoftwareVertexProcessing( FALSE ));
+ state.m_SoftwareVP = false;
+ }
+ }
+ }
+
+ SetValuesFunctorD3D9 setValuesFunc(*this, vscache, pscache);
+ ApplyMaterialPropertyBlockValues(m_MaterialProperties, m_State.activeGpuProgram ,m_State.activeGpuProgramParams, setValuesFunc);
+
+ vscache.CommitVertexConstants();
+ pscache.CommitPixelConstants();
+}
+
+
+DeviceBlendState* GfxDeviceD3D9::CreateBlendState(const GfxBlendState& state)
+{
+ std::pair<CachedBlendStates::iterator, bool> result = m_CachedBlendStates.insert(std::make_pair(state, DeviceBlendStateD3D9()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceBlendStateD3D9& d3dstate = result.first->second;
+ memcpy(&d3dstate.sourceState, &state, sizeof(GfxBlendState));
+ DWORD d3dmask = 0;
+ const UInt8 mask = state.renderTargetWriteMask;
+ if( mask & kColorWriteR ) d3dmask |= D3DCOLORWRITEENABLE_RED;
+ if( mask & kColorWriteG ) d3dmask |= D3DCOLORWRITEENABLE_GREEN;
+ if( mask & kColorWriteB ) d3dmask |= D3DCOLORWRITEENABLE_BLUE;
+ if( mask & kColorWriteA ) d3dmask |= D3DCOLORWRITEENABLE_ALPHA;
+ d3dstate.renderTargetWriteMask = d3dmask;
+
+ DebugAssertIf(kFuncUnknown==state.alphaTest);
+ d3dstate.alphaFunc = kCmpFuncD3D9[state.alphaTest];
+ return &result.first->second;
+}
+
+
+DeviceDepthState* GfxDeviceD3D9::CreateDepthState(const GfxDepthState& state)
+{
+ std::pair<CachedDepthStates::iterator, bool> result = m_CachedDepthStates.insert(std::make_pair(state, DeviceDepthStateD3D9()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceDepthStateD3D9& d3dstate = result.first->second;
+ memcpy(&d3dstate.sourceState, &state, sizeof(GfxDepthState));
+ d3dstate.depthFunc = kCmpFuncD3D9[state.depthFunc];
+ return &result.first->second;
+}
+
+DeviceStencilState* GfxDeviceD3D9::CreateStencilState(const GfxStencilState& state)
+{
+ std::pair<CachedStencilStates::iterator, bool> result = m_CachedStencilStates.insert(std::make_pair(state, DeviceStencilStateD3D9()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceStencilStateD3D9& st = result.first->second;
+ memcpy(&st.sourceState, &state, sizeof(state));
+ st.stencilFuncFront = kCmpFuncD3D9[state.stencilFuncFront];
+ st.stencilFailOpFront = kStencilOpD3D9[state.stencilFailOpFront];
+ st.depthFailOpFront = kStencilOpD3D9[state.stencilZFailOpFront];
+ st.depthPassOpFront = kStencilOpD3D9[state.stencilPassOpFront];
+ st.stencilFuncBack = kCmpFuncD3D9[state.stencilFuncBack];
+ st.stencilFailOpBack = kStencilOpD3D9[state.stencilFailOpBack];
+ st.depthFailOpBack = kStencilOpD3D9[state.stencilZFailOpBack];
+ st.depthPassOpBack = kStencilOpD3D9[state.stencilPassOpBack];
+ return &result.first->second;
+}
+
+
+
+DeviceRasterState* GfxDeviceD3D9::CreateRasterState(const GfxRasterState& state)
+{
+ std::pair<CachedRasterStates::iterator, bool> result = m_CachedRasterStates.insert(std::make_pair(state, DeviceRasterState()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceRasterState& d3dstate = result.first->second;
+ memcpy(&d3dstate.sourceState, &state, sizeof(DeviceRasterState));
+
+ return &result.first->second;
+}
+
+
+void GfxDeviceD3D9::SetBlendState(const DeviceBlendState* state, float alphaRef)
+{
+ DeviceBlendStateD3D9* devstate = (DeviceBlendStateD3D9*)state;
+
+ if (m_CurrBlendState == devstate && alphaRef == m_State.alphaValue)
+ return;
+
+ m_CurrBlendState = devstate;
+ if (!m_CurrBlendState)
+ return;
+
+ UInt32 colMask = devstate->renderTargetWriteMask;
+ if (!IsActiveRenderTargetWithColorD3D9())
+ colMask = 0;
+
+ if(colMask != m_State.colorWriteMask)
+ {
+ IDirect3DDevice9* dev = GetD3DDeviceNoAssert();
+ D3D9_CALL(dev->SetRenderState(D3DRS_COLORWRITEENABLE, colMask));
+ m_State.colorWriteMask = colMask;
+ }
+
+ const GfxBlendState& desc = state->sourceState;
+ const CompareFunction mode = state->sourceState.alphaTest;
+ const D3DBLEND d3dsrc = kBlendModeD3D9[desc.srcBlend];
+ const D3DBLEND d3ddst = kBlendModeD3D9[desc.dstBlend];
+ const D3DBLEND d3dsrca = kBlendModeD3D9[desc.srcBlendAlpha];
+ const D3DBLEND d3ddsta = kBlendModeD3D9[desc.dstBlendAlpha];
+ const D3DBLENDOP d3dop = kBlendOpD3D9[desc.blendOp];
+ const D3DBLENDOP d3dopa = kBlendOpD3D9[desc.blendOpAlpha];
+
+ const bool blendDisabled = (d3dsrc == D3DBLEND_ONE && d3ddst == D3DBLEND_ZERO && d3dsrca == D3DBLEND_ONE && d3ddsta == D3DBLEND_ZERO);
+
+ IDirect3DDevice9* dev = GetD3DDevice();
+ if(blendDisabled)
+ {
+ if( m_State.blending != 0 )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE ));
+ m_State.blending = 0;
+ }
+ }
+ else
+ {
+ if( d3dsrc != m_State.srcBlend || d3ddst != m_State.destBlend )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_SRCBLEND, d3dsrc ));
+ D3D9_CALL(dev->SetRenderState( D3DRS_DESTBLEND, d3ddst ));
+ m_State.srcBlend = d3dsrc;
+ m_State.destBlend = d3ddst;
+ }
+
+ if (d3dop != m_State.blendOp)
+ {
+ bool supports = true;
+ if( (d3dop == D3DBLENDOP_SUBTRACT || d3dop == D3DBLENDOP_REVSUBTRACT) && !gGraphicsCaps.hasBlendSub )
+ supports = false;
+ if( (d3dop == D3DBLENDOP_MIN || d3dop == D3DBLENDOP_MAX) && !gGraphicsCaps.hasBlendMinMax )
+ supports = false;
+
+ if(supports)
+ {
+ D3D9_CALL(dev->SetRenderState(D3DRS_BLENDOP, d3dop));
+ m_State.blendOp = d3dop;
+ }
+ }
+ if (gGraphicsCaps.hasSeparateAlphaBlend)
+ {
+ if( d3dsrca != m_State.srcBlendAlpha || d3ddsta != m_State.destBlendAlpha || d3dopa != m_State.blendOpAlpha )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_SEPARATEALPHABLENDENABLE, d3dsrc != d3dsrca || d3ddst != d3ddsta || d3dopa != d3dop));
+ D3D9_CALL(dev->SetRenderState( D3DRS_SRCBLENDALPHA, d3dsrca ));
+ D3D9_CALL(dev->SetRenderState( D3DRS_DESTBLENDALPHA, d3ddsta ));
+ m_State.srcBlendAlpha = d3dsrca;
+ m_State.destBlendAlpha = d3ddsta;
+
+ bool supports = true;
+ if( (d3dopa == D3DBLENDOP_SUBTRACT || d3dopa == D3DBLENDOP_REVSUBTRACT) && !gGraphicsCaps.hasBlendSub )
+ supports = false;
+ if( (d3dopa == D3DBLENDOP_MIN || d3dopa == D3DBLENDOP_MAX) && !gGraphicsCaps.hasBlendMinMax )
+ supports = false;
+
+ if (supports)
+ {
+ D3D9_CALL(dev->SetRenderState(D3DRS_BLENDOPALPHA, d3dopa));
+ m_State.blendOpAlpha = d3dopa;
+ }
+ }
+ }
+ if( m_State.blending != 1 )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ));
+ m_State.blending = 1;
+ }
+ }
+
+ DebugAssertIf(mode==kFuncUnknown);
+#if UNITY_EDITOR // gles2.0 doesn't have FF alpha testing(only discard/clip on shader side), so disable on editor while emulating
+ bool skipAlphaTestFF = (gGraphicsCaps.IsEmulatingGLES20() && IsShaderActive(kShaderFragment));
+ // possible that vertex shader will be used with FF "frag shader" (like Transparent/vertexlit.shader),
+ // which will change alphatesting. So later on when real frag shaders come, we need to force disable alpha
+ // testing or enjoy nasty artefacts (like active alpha testing messing up the whole scene).
+ if ( skipAlphaTestFF && m_State.alphaFunc!=kFuncDisabled )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE ));
+ m_State.alphaFunc = kFuncDisabled;
+ }
+
+ if ( !skipAlphaTestFF )
+ {
+#endif
+ if( mode != m_State.alphaFunc || alphaRef != m_State.alphaValue )
+ {
+ if( mode != kFuncDisabled )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE ));
+ D3D9_CALL(dev->SetRenderState( D3DRS_ALPHAFUNC, kCmpFuncD3D9[mode] ));
+ D3D9_CALL(dev->SetRenderState( D3DRS_ALPHAREF, alphaRef * 255.0f ));
+ }
+ else
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE ));
+ }
+
+ m_State.alphaFunc = mode;
+ m_State.alphaValue = alphaRef;
+ }
+#if UNITY_EDITOR
+ }
+#endif
+ // TODO: ATI/NVIDIA hacks
+}
+
+
+void GfxDeviceD3D9::SetRasterState(const DeviceRasterState* state)
+{
+ DeviceRasterState* devstate = (DeviceRasterState*)state;
+ if(!devstate)
+ {
+ m_CurrRasterState = NULL;
+ return;
+ }
+
+ m_CurrRasterState = devstate;
+
+ IDirect3DDevice9* dev = GetD3DDeviceNoAssert();
+ CullMode cull = devstate->sourceState.cullMode;
+ D3DCULL d3dcull = kCullModeD3D9[cull];
+ if( d3dcull != m_State.d3dculling )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_CULLMODE, d3dcull ));
+ m_State.culling = cull;
+ m_State.d3dculling = d3dcull;
+ }
+
+ float zFactor = devstate->sourceState.slopeScaledDepthBias;
+ float zUnits = devstate->sourceState.depthBias;
+ if( zFactor != m_State.offsetFactor || zUnits != m_State.offsetUnits )
+ {
+ m_State.offsetFactor = zFactor;
+ m_State.offsetUnits = zUnits;
+
+ // In D3D9 the values are in floating point, with 1 meaning "full depth range".
+ // In theory the offset should depend on depth buffer bit count, and on 24 bit depth buffer a value close to 4.8e-7 should be used
+ // (see Lengyel's GDC2007 "projection matrix tricks").
+ // However, it looks like even on 16 bit depth buffer, a value as-if-24-bit should be used (tested on Radeon HD 3850, GeForce 8600, Intel 945).
+ const double kOneBit = 4.8e-7;
+
+ // It looks like generally we need twice the one bit (PolygonOff2 unit test, on Radeon 3850 and GeForce 8600).
+ // To be somewhat more safer, we make it trhee times the one bit. Still looks quite okay.
+ const float kBiasMultiplier = 3.0 * kOneBit;
+
+ if( gGraphicsCaps.d3d.d3dcaps.RasterCaps & D3DPRASTERCAPS_DEPTHBIAS )
+ {
+ zUnits *= kBiasMultiplier;
+ D3D9_CALL(dev->SetRenderState( D3DRS_DEPTHBIAS, *(DWORD*)&zUnits ));
+ }
+ if( gGraphicsCaps.d3d.d3dcaps.RasterCaps & D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_SLOPESCALEDEPTHBIAS, *(DWORD*)&zFactor ));
+ }
+ }
+}
+
+
+void GfxDeviceD3D9::SetDepthState(const DeviceDepthState* state)
+{
+ IDirect3DDevice9* dev = GetD3DDeviceNoAssert();
+ DeviceDepthStateD3D9* devstate = (DeviceDepthStateD3D9*)state;
+ if (m_CurrDepthState == devstate)
+ return;
+
+ m_CurrDepthState = devstate;
+
+ if (!m_CurrDepthState)
+ return;
+
+ if( devstate->sourceState.depthFunc != m_State.depthFunc )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_ZFUNC, devstate->depthFunc ));
+ m_State.depthFunc = devstate->sourceState.depthFunc;
+ }
+
+ int d3dDepthWriteMode = devstate->sourceState.depthWrite ? TRUE : FALSE;
+ if( d3dDepthWriteMode != m_State.depthWrite )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_ZWRITEENABLE, d3dDepthWriteMode ));
+ m_State.depthWrite = d3dDepthWriteMode;
+ }
+}
+
+void GfxDeviceD3D9::SetStencilState(const DeviceStencilState* state, int stencilRef)
+{
+ if (m_CurrStencilState == state && m_State.m_StencilRef == stencilRef)
+ return;
+ const DeviceStencilStateD3D9* st = static_cast<const DeviceStencilStateD3D9*>(state);
+ m_CurrStencilState = st;
+ if (!m_CurrStencilState)
+ return;
+
+ IDirect3DDevice9* dev = GetD3DDevice();
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILENABLE, st->sourceState.stencilEnable));
+ D3D9_CALL (dev->SetRenderState (D3DRS_TWOSIDEDSTENCILMODE, TRUE));
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILMASK, st->sourceState.readMask));
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILWRITEMASK, st->sourceState.writeMask));
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILREF, stencilRef));
+
+ m_State.stencilFunc[0] = st->stencilFuncFront;
+ m_State.stencilFailOp[0] = st->stencilFailOpFront;
+ m_State.depthFailOp[0] = st->depthFailOpFront;
+ m_State.depthPassOp[0] = st->depthPassOpFront;
+ m_State.stencilFunc[1] = st->stencilFuncBack;
+ m_State.stencilFailOp[1] = st->stencilFailOpBack;
+ m_State.depthFailOp[1] = st->depthFailOpBack;
+ m_State.depthPassOp[1] = st->depthPassOpBack;
+ ApplyStencilFuncAndOp(m_State);
+
+ m_State.m_StencilRef = stencilRef;
+}
+
+static void ApplyStencilFuncAndOp (DeviceStateD3D& state)
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ // Normally [0] is front and [1] back stencil state, but when rendering
+ // upside-down, the winding order flips, so flip the state as well.
+ const int cw = state.invertProjMatrix ? 1 : 0;
+ const int ccw = (cw + 1)%2;
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILFUNC, state.stencilFunc[cw]));
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILFAIL, state.stencilFailOp[cw]));
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILZFAIL, state.depthFailOp[cw]));
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILPASS, state.depthPassOp[cw]));
+ D3D9_CALL (dev->SetRenderState (D3DRS_CCW_STENCILFUNC, state.stencilFunc[ccw]));
+ D3D9_CALL (dev->SetRenderState (D3DRS_CCW_STENCILFAIL, state.stencilFailOp[ccw]));
+ D3D9_CALL (dev->SetRenderState (D3DRS_CCW_STENCILZFAIL, state.depthFailOp[ccw]));
+ D3D9_CALL (dev->SetRenderState (D3DRS_CCW_STENCILPASS, state.depthPassOp[ccw]));
+}
+
+void GfxDeviceD3D9::SetSRGBWrite (bool enable)
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ D3D9_CALL (dev->SetRenderState (D3DRS_SRGBWRITEENABLE, enable));
+}
+
+bool GfxDeviceD3D9::GetSRGBWrite ()
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ DWORD v;
+ D3D9_CALL (dev->GetRenderState (D3DRS_SRGBWRITEENABLE, &v));
+ return (v==TRUE);
+}
+
+GfxThreadableDevice* CreateD3D9GfxDevice(bool forceREF)
+{
+ if( !InitializeD3D(forceREF ? D3DDEVTYPE_REF : D3DDEVTYPE_HAL) )
+ return NULL;
+
+ #if UNITY_EDITOR
+ if (!CreateHiddenWindowD3D())
+ return NULL;
+ #endif
+
+ gGraphicsCaps.InitD3D9();
+
+ GfxDeviceD3D9* device = UNITY_NEW_AS_ROOT(GfxDeviceD3D9(), kMemGfxDevice, "D3D9GfxDevice", "");
+
+#if UNITY_EDITOR
+ EditorInitializeD3D(device);
+#else
+ ScreenManagerWin& screenMgr = GetScreenManager();
+ HWND window = screenMgr.GetWindow();
+ int width = screenMgr.GetWidth();
+ int height = screenMgr.GetHeight();
+ int dummy;
+ if (!InitializeOrResetD3DDevice(device, window, width, height, 0, false, 0, 0, dummy, dummy, dummy, dummy))
+ {
+ UNITY_DELETE(device, kMemGfxDevice);
+ device = NULL;
+ }
+#endif
+
+ return device;
+}
+
+GfxDeviceD3D9& GetD3D9GfxDevice()
+{
+ GfxDevice& device = GetRealGfxDevice();
+ Assert( device.GetRenderer() == kGfxRendererD3D9 );
+ return static_cast<GfxDeviceD3D9&>(device);
+}
+
+bool IsD3D9DeviceLost()
+{
+ GfxDeviceD3D9& device = static_cast<GfxDeviceD3D9&>( GetRealGfxDevice() );
+ AssertIf( device.GetRenderer() != kGfxRendererD3D9 );
+ return device.GetState().m_DeviceLost;
+}
+
+void SetD3D9DeviceLost( bool lost )
+{
+ GfxDeviceD3D9& device = static_cast<GfxDeviceD3D9&>( GetRealGfxDevice() );
+ AssertIf( device.GetRenderer() != kGfxRendererD3D9 );
+ device.GetState().m_DeviceLost = lost;
+}
+
+
+GfxDeviceD3D9::GfxDeviceD3D9()
+{
+ m_State.m_DeviceLost = false;
+ m_DynamicVBO = NULL;
+
+ m_State.appBackfaceMode = false;
+ m_State.userBackfaceMode = false;
+ m_State.invertProjMatrix = false;
+ m_State.wireframe = false;
+
+ InvalidateState();
+ ResetFrameStats();
+
+ m_Renderer = kGfxRendererD3D9;
+ m_UsesOpenGLTextureCoords = false;
+ m_UsesHalfTexelOffset = true;
+ m_IsThreadable = true;
+
+ m_MaxBufferedFrames = 1; // -1 means no limiting, default is 1
+
+ m_State.viewport[0] = m_State.viewport[1] = m_State.viewport[2] = m_State.viewport[3] = 0;
+ m_State.scissorRect[0] = m_State.scissorRect[1] = m_State.scissorRect[2] = m_State.scissorRect[3] = 0;
+
+ m_CurrBlendState = 0;
+ m_CurrDepthState = 0;
+ m_CurrStencilState = 0;
+ m_CurrRasterState = 0;
+ m_CurrTargetWidth = 0;
+ m_CurrTargetHeight = 0;
+ m_CurrWindowWidth = 0;
+ m_CurrWindowHeight = 0;
+
+ m_AllWhiteVertexStream = NULL;
+
+ extern RenderSurfaceBase* DummyColorBackBuferD3D9();
+ SetBackBufferColorSurface(DummyColorBackBuferD3D9());
+
+ extern RenderSurfaceBase* DummyDepthBackBuferD3D9();
+ SetBackBufferDepthSurface(DummyDepthBackBuferD3D9());
+}
+
+GfxDeviceD3D9::~GfxDeviceD3D9()
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_WORKER
+ PluginsSetGraphicsDevice (GetD3DDevice(), kGfxRendererD3D9, kGfxDeviceEventShutdown);
+#endif
+
+ D3D9VBO::CleanupSharedIndexBuffer();
+
+ CleanupEventQueries ();
+#if ENABLE_PROFILER
+ m_TimerQueriesD3D9.ReleaseAllQueries();
+#endif
+
+ if( m_DynamicVBO )
+ delete m_DynamicVBO;
+
+ SAFE_RELEASE(m_AllWhiteVertexStream);
+ SAFE_RELEASE(m_Imm.m_ImmVertexDecl);
+ m_VertexDecls.Clear();
+ TextureCombinersD3D::CleanupCombinerCache();
+ CleanupVertexShadersD3D9 ();
+ DestroyD3DDevice();
+
+ #if UNITY_EDITOR
+ DestroyHiddenWindowD3D();
+ #endif
+
+ CleanupD3D();
+}
+
+void GfxDeviceD3D9::InvalidateState()
+{
+ IDirect3DDevice9* dev = GetD3DDeviceNoAssert();
+ if( m_State.m_DeviceLost )
+ dev = NULL;
+
+ ResetVertexPipeStateD3D9 (dev, m_TransformState, m_BuiltinParamValues, m_VertexConfig, m_VertexData, m_VertexPrevious);
+ m_FogParams.Invalidate();
+ m_State.Invalidate(*this);
+ m_Imm.Invalidate();
+ m_VSConstantCache.Invalidate();
+ m_PSConstantCache.Invalidate();
+
+ m_CurrBlendState = NULL;
+ m_CurrDepthState = NULL;
+ m_CurrStencilState = NULL;
+ m_CurrRasterState = NULL;
+}
+
+
+void GfxDeviceD3D9::Clear(UInt32 clearFlags, const float color[4], float depth, int stencil)
+{
+ if( !g_D3DHasDepthStencil )
+ clearFlags &= ~kGfxClearDepthStencil;
+ if (!IsActiveRenderTargetWithColorD3D9())
+ clearFlags &= ~kGfxClearColor;
+
+ DWORD flags = 0;
+ if (clearFlags & kGfxClearColor) flags |= D3DCLEAR_TARGET;
+ if (clearFlags & kGfxClearDepth) flags |= D3DCLEAR_ZBUFFER;
+ if (clearFlags & kGfxClearStencil && GetStencilBitsFromD3DFormat (g_D3DDepthStencilFormat) > 0) {
+ flags |= D3DCLEAR_STENCIL;
+ }
+ GetD3DDevice()->Clear (0, NULL, flags, ColorToD3D(color), depth, stencil);
+}
+
+
+static void ApplyBackfaceMode( DeviceStateD3D& state )
+{
+ if( (state.appBackfaceMode == state.userBackfaceMode) == state.invertProjMatrix )
+ {
+ kCullModeD3D9[kCullFront] = D3DCULL_CCW;
+ kCullModeD3D9[kCullBack] = D3DCULL_CW;
+ }
+ else
+ {
+ kCullModeD3D9[kCullFront] = D3DCULL_CW;
+ kCullModeD3D9[kCullBack] = D3DCULL_CCW;
+ }
+
+ if( state.culling != kCullUnknown )
+ {
+ IDirect3DDevice9* dev = GetD3DDevice();
+ D3DCULL d3dcull = kCullModeD3D9[state.culling];
+ if( d3dcull != state.d3dculling )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_CULLMODE, d3dcull ));
+ state.d3dculling = d3dcull;
+ }
+ }
+}
+
+void GfxDeviceD3D9::SetUserBackfaceMode( bool enable )
+{
+ if( m_State.userBackfaceMode == enable )
+ return;
+ m_State.userBackfaceMode = enable;
+ ApplyBackfaceMode( m_State );
+}
+
+
+void GfxDeviceD3D9::SetWireframe( bool wire )
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ D3D9_CALL(dev->SetRenderState( D3DRS_FILLMODE, wire ? D3DFILL_WIREFRAME : D3DFILL_SOLID ));
+ m_State.wireframe = wire;
+}
+
+bool GfxDeviceD3D9::GetWireframe() const
+{
+ return m_State.wireframe;
+}
+
+
+
+// Even with programmable shaders, some things need fixed function D3DTS_PROJECTION to be set up;
+// most notably fixed function fog (shader model 2.0).
+static void SetFFProjectionMatrixD3D9 (const Matrix4x4f& m)
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ Matrix4x4f projFlip;
+ projFlip.m_Data[ 0] = m.m_Data[ 0];
+ projFlip.m_Data[ 1] = m.m_Data[ 1];
+ projFlip.m_Data[ 2] = m.m_Data[ 2];
+ projFlip.m_Data[ 3] = m.m_Data[ 3];
+ projFlip.m_Data[ 4] = m.m_Data[ 4];
+ projFlip.m_Data[ 5] = m.m_Data[ 5];
+ projFlip.m_Data[ 6] = m.m_Data[ 6];
+ projFlip.m_Data[ 7] = m.m_Data[ 7];
+ projFlip.m_Data[ 8] = -m.m_Data[ 8];
+ projFlip.m_Data[ 9] = -m.m_Data[ 9];
+ projFlip.m_Data[10] = -m.m_Data[10];
+ projFlip.m_Data[11] = -m.m_Data[11];
+ projFlip.m_Data[12] = m.m_Data[12];
+ projFlip.m_Data[13] = m.m_Data[13];
+ projFlip.m_Data[14] = m.m_Data[14];
+ projFlip.m_Data[15] = m.m_Data[15];
+ D3D9_CALL(dev->SetTransform (D3DTS_PROJECTION, (const D3DMATRIX*)projFlip.GetPtr()));
+}
+
+
+void GfxDeviceD3D9::SetInvertProjectionMatrix( bool enable )
+{
+ if( m_State.invertProjMatrix == enable )
+ return;
+
+ m_State.invertProjMatrix = enable;
+ ApplyBackfaceMode( m_State );
+ ApplyStencilFuncAndOp( m_State );
+
+ // When setting up "invert" flag, invert the matrix as well.
+ Matrix4x4f& m = m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj);
+ m.Get(1,1) = -m.Get(1,1);
+ m.Get(1,3) = -m.Get(1,3);
+ m_TransformState.dirtyFlags |= TransformState::kProjDirty;
+ SetFFProjectionMatrixD3D9 (m);
+}
+
+bool GfxDeviceD3D9::GetInvertProjectionMatrix() const
+{
+ return m_State.invertProjMatrix;
+}
+
+void GfxDeviceD3D9::SetWorldMatrix( const float matrix[16] )
+{
+ CopyMatrix (matrix, m_TransformState.worldMatrix.GetPtr());
+ m_TransformState.dirtyFlags |= TransformState::kWorldDirty;
+}
+
+void GfxDeviceD3D9::SetViewMatrix( const float matrix[16] )
+{
+ m_TransformState.SetViewMatrix (matrix, m_BuiltinParamValues);
+}
+
+void GfxDeviceD3D9::SetProjectionMatrix(const Matrix4x4f& matrix)
+{
+ Matrix4x4f& m = m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj);
+ CopyMatrix (matrix.GetPtr(), m.GetPtr());
+ CopyMatrix (matrix.GetPtr(), m_TransformState.projectionMatrixOriginal.GetPtr());
+
+ CalculateDeviceProjectionMatrix (m, m_UsesOpenGLTextureCoords, m_State.invertProjMatrix);
+ SetFFProjectionMatrixD3D9 (m);
+
+ m_TransformState.dirtyFlags |= TransformState::kProjDirty;
+}
+
+
+void GfxDeviceD3D9::GetMatrix(float outMatrix[16]) const
+{
+ m_TransformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+ CopyMatrix (m_TransformState.worldViewMatrix.GetPtr(), outMatrix);
+}
+
+const float* GfxDeviceD3D9::GetWorldMatrix() const
+{
+ return m_TransformState.worldMatrix.GetPtr();
+}
+
+const float* GfxDeviceD3D9::GetViewMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatView).GetPtr();
+}
+
+const float* GfxDeviceD3D9::GetProjectionMatrix() const
+{
+ return m_TransformState.projectionMatrixOriginal.GetPtr();
+}
+
+const float* GfxDeviceD3D9::GetDeviceProjectionMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatProj).GetPtr();
+}
+
+void GfxDeviceD3D9::SetNormalizationBackface( NormalizationMode mode, bool backface )
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ if( mode != m_VertexData.normalization )
+ {
+ m_VertexData.normalization = mode;
+ m_VertexConfig.hasNormalization = (mode == kNormalizationFull);
+ }
+ if( m_State.appBackfaceMode != backface )
+ {
+ m_State.appBackfaceMode = backface;
+ ApplyBackfaceMode( m_State );
+ }
+}
+
+void GfxDeviceD3D9::SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial )
+{
+ m_VertexConfig.hasLighting = on ? 1 : 0;
+ m_VertexConfig.hasSpecular = separateSpecular ? 1 : 0;
+ DebugAssertIf(colorMaterial==kColorMatUnknown);
+ m_VertexConfig.colorMaterial = colorMaterial;
+}
+
+void GfxDeviceD3D9::SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess )
+{
+ D3DMATERIAL9& mat = m_VertexData.material;
+ mat.Ambient = *(D3DCOLORVALUE*)ambient;
+ mat.Diffuse = *(D3DCOLORVALUE*)diffuse;
+ mat.Specular = *(D3DCOLORVALUE*)specular;
+ mat.Emissive = *(D3DCOLORVALUE*)emissive;
+ mat.Power = std::max<float>( std::min<float>(shininess,1.0f), 0.0f) * 128.0f;
+}
+
+
+void GfxDeviceD3D9::SetColor( const float color[4] )
+{
+ // If we have pixel shader set up, do nothing; fixed function
+ // constant color can't be possibly used there
+ if (m_State.activeShader[kShaderFragment] != 0) // inlined IsShaderActive(kShaderFragment)
+ return;
+
+ // There's no really good place to make a glColor equivalent, put it into
+ // TFACTOR... Additionally put that into c4 register for ps_1_1 combiner emulation
+ IDirect3DDevice9* dev = GetD3DDevice();
+ D3D9_CALL(dev->SetRenderState( D3DRS_TEXTUREFACTOR, ColorToD3D(color) ));
+ m_PSConstantCache.SetValues( kMaxD3DTextureStagesForPS, color, 1 );
+}
+
+
+void GfxDeviceD3D9::SetViewport( int x, int y, int width, int height )
+{
+ m_State.viewport[0] = x;
+ m_State.viewport[1] = y;
+ m_State.viewport[2] = width;
+ m_State.viewport[3] = height;
+
+ IDirect3DDevice9* dev = GetD3DDeviceNoAssert();
+ if( !dev ) // happens on startup, when deleting all render textures
+ return;
+ D3DVIEWPORT9 view;
+ view.X = x;
+ view.Y = y;
+ view.Width = width;
+ view.Height = height;
+ view.MinZ = 0.0f;
+ view.MaxZ = 1.0f;
+ dev->SetViewport( &view );
+}
+
+void GfxDeviceD3D9::GetViewport( int* port ) const
+{
+ port[0] = m_State.viewport[0];
+ port[1] = m_State.viewport[1];
+ port[2] = m_State.viewport[2];
+ port[3] = m_State.viewport[3];
+}
+
+
+void GfxDeviceD3D9::SetScissorRect( int x, int y, int width, int height )
+{
+ if (m_State.scissor != 1)
+ {
+ if (gGraphicsCaps.d3d.d3dcaps.RasterCaps & D3DPRASTERCAPS_SCISSORTEST )
+ {
+ GetD3DDevice()->SetRenderState( D3DRS_SCISSORTESTENABLE, TRUE );
+ }
+ m_State.scissor = 1;
+ }
+
+
+ m_State.scissorRect[0] = x;
+ m_State.scissorRect[1] = y;
+ m_State.scissorRect[2] = width;
+ m_State.scissorRect[3] = height;
+
+ RECT rc;
+ rc.left = x;
+ rc.top = y;
+ rc.right = x + width;
+ rc.bottom = y + height;
+ GetD3DDevice()->SetScissorRect( &rc );
+
+}
+void GfxDeviceD3D9::DisableScissor()
+{
+ if (m_State.scissor != 0)
+ {
+ if( gGraphicsCaps.d3d.d3dcaps.RasterCaps & D3DPRASTERCAPS_SCISSORTEST )
+ {
+ GetD3DDevice()->SetRenderState( D3DRS_SCISSORTESTENABLE, FALSE);
+ }
+ m_State.scissor = 0;
+ }
+}
+bool GfxDeviceD3D9::IsScissorEnabled() const
+{
+ return m_State.scissor == 1;
+}
+
+void GfxDeviceD3D9::GetScissorRect( int scissor[4] ) const
+{
+ scissor[0] = m_State.scissorRect[0];
+ scissor[1] = m_State.scissorRect[1];
+ scissor[2] = m_State.scissorRect[2];
+ scissor[3] = m_State.scissorRect[3];
+}
+
+bool GfxDeviceD3D9::IsCombineModeSupported( unsigned int combiner )
+{
+ return true;
+}
+
+TextureCombinersHandle GfxDeviceD3D9::CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular )
+{
+ TextureCombinersD3D* implD3D = TextureCombinersD3D::Create( count, texEnvs, props, hasVertexColorOrLighting, usesAddSpecular );
+ return TextureCombinersHandle( implD3D );
+}
+
+void GfxDeviceD3D9::DeleteTextureCombiners( TextureCombinersHandle& textureCombiners )
+{
+ TextureCombinersD3D* implD3D = OBJECT_FROM_HANDLE(textureCombiners, TextureCombinersD3D);
+ delete implD3D;
+ textureCombiners.Reset();
+}
+
+void GfxDeviceD3D9::SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors )
+{
+ TextureCombinersD3D* implD3D = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersD3D);
+ AssertIf( !implD3D );
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ AssertIf (IsShaderActive( kShaderFragment ));
+
+ const int maxTexUnits = gGraphicsCaps.maxTexUnits; // fetch here once
+
+ // set textures
+ int i = 0;
+ for( ; i < maxTexUnits && i < implD3D->envCount; ++i )
+ {
+ ApplyTexEnvData (i, i, texEnvData[i]);
+ }
+
+ // clear unused textures
+ for (; i < maxTexUnits; ++i)
+ {
+ if (i < kMaxSupportedTextureCoords)
+ m_VertexConfig.ClearTextureUnit(i);
+
+ TextureUnitStateD3D& currTex = m_State.texturesPS[i];
+ if (currTex.texID.m_ID != 0)
+ {
+ D3D9_CALL(dev->SetTexture( GetD3D9SamplerIndex(kShaderFragment,i), NULL ));
+ currTex.texID.m_ID = 0;
+ }
+ }
+
+ // setup texture stages
+ if( implD3D->pixelShader )
+ {
+ for( i = 0; i < implD3D->stageCount; ++i )
+ {
+ const ShaderLab::TextureBinding& binding = implD3D->texEnvs[i];
+ const Vector4f& texcolorVal = texColors[i];
+ m_PSConstantCache.SetValues( i, texcolorVal.GetPtr(), 1 );
+ }
+ if( m_State.fixedFunctionPS != implD3D->uniqueID )
+ {
+ D3D9_CALL(dev->SetPixelShader( implD3D->pixelShader ));
+ m_State.fixedFunctionPS = implD3D->uniqueID;
+ }
+ }
+ else
+ {
+ if( implD3D->textureFactorIndex != -1 )
+ {
+ const Vector4f& color = texColors[implD3D->textureFactorIndex];
+ D3D9_CALL(dev->SetRenderState( D3DRS_TEXTUREFACTOR, ColorToD3D( color.GetPtr() ) ));
+ }
+ for( i = 0; i < implD3D->stageCount; ++i )
+ {
+ // TODO: cache!
+ const D3DTextureStage& stage = implD3D->stages[i];
+ AssertIf( stage.colorOp == D3DTOP_DISABLE || stage.alphaOp == D3DTOP_DISABLE );
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_COLOROP, stage.colorOp ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_COLORARG1, stage.colorArgs[0] ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_COLORARG2, stage.colorArgs[1] ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_COLORARG0, stage.colorArgs[2] ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_ALPHAOP, stage.alphaOp ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_ALPHAARG1, stage.alphaArgs[0] ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_ALPHAARG2, stage.alphaArgs[1] ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_ALPHAARG0, stage.alphaArgs[2] ));
+ }
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_COLOROP, D3DTOP_DISABLE ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_ALPHAOP, D3DTOP_DISABLE ));
+ D3D9_CALL(dev->SetPixelShader( NULL ));
+ m_State.fixedFunctionPS = 0;
+ }
+}
+
+
+void GfxDeviceD3D9::SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props )
+{
+ TextureCombinersD3D* implD3D = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersD3D);
+ AssertIf( !implD3D );
+
+ int count = std::min(implD3D->envCount, gGraphicsCaps.maxTexUnits);
+
+ // Fill in arrays
+ TexEnvData* texEnvData;
+ ALLOC_TEMP (texEnvData, TexEnvData, count);
+ for( int i = 0; i < count; ++i )
+ {
+ ShaderLab::TexEnv *te = ShaderLab::GetTexEnvForBinding( implD3D->texEnvs[i], props );
+ Assert( te != NULL );
+ te->PrepareData (implD3D->texEnvs[i].m_TextureName.index, implD3D->texEnvs[i].m_MatrixName, props, &texEnvData[i]);
+ }
+
+ Vector4f* texColors;
+ ALLOC_TEMP (texColors, Vector4f, implD3D->envCount);
+ for( int i = 0; i < implD3D->envCount; ++i )
+ {
+ const ShaderLab::TextureBinding& binding = implD3D->texEnvs[i];
+ texColors[i] = binding.GetTexColor().Get (props);
+ }
+ GfxDeviceD3D9::SetTextureCombinersThreadable(textureCombiners, texEnvData, texColors);
+}
+
+
+void GfxDeviceD3D9::SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias)
+{
+ DebugAssertIf( dim < kTexDim2D || dim > kTexDimCUBE );
+ DebugAssertIf (unit < 0 || unit >= kMaxSupportedTextureUnits);
+
+ if (unit < kMaxSupportedTextureCoords)
+ m_VertexConfig.SetTextureUnit(unit);
+
+ TextureUnitStateD3D* currTex = NULL;
+ if (shaderType == kShaderFragment)
+ currTex = &m_State.texturesPS[unit];
+ else if (shaderType == kShaderVertex)
+ currTex = &m_State.texturesVS[unit];
+ else
+ {
+ AssertString ("Unsupported shader type for SetTexture");
+ return;
+ }
+
+ if (texture != currTex->texID)
+ {
+ if (m_Textures.SetTexture (shaderType, unit, texture))
+ currTex->texID = texture;
+ }
+ m_Stats.AddUsedTexture(texture);
+ if (gGraphicsCaps.hasMipLevelBias && bias != currTex->bias && shaderType == kShaderFragment)
+ {
+ D3D9_CALL(GetD3DDevice()->SetSamplerState( unit, D3DSAMP_MIPMAPLODBIAS, *(DWORD*)&bias ));
+ currTex->bias = bias;
+ }
+}
+
+
+
+void GfxDeviceD3D9::SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16] )
+{
+ Assert (unit >= 0 && unit < kMaxSupportedTextureCoords);
+
+ m_State.m_NeedsSofwareVPFlags &= ~kNeedsSoftwareVPTexGen;
+
+ // -------- texture matrix
+
+ float* mat = m_TransformState.texMatrices[unit].GetPtr();
+ CopyMatrix( matrix, mat );
+
+ // In OpenGL all texture reads are projective, and matrices are always 4x4, and z/w defaults to 0/1.
+ // In D3D everything is different. So here we try to figure out how many components need to be transformed,
+ // munge the matrix and enable projective texturing if needed.
+
+ TextureMatrixMode matrixMode;
+ int projectedTexture = 0;
+ if( identity )
+ {
+ // matrix guaranteed to be identity: disable transformation
+ matrixMode = kTexMatrixNone;
+ }
+ else if( dim == kTexDimCUBE || dim == kTexDim3D )
+ {
+ // for cube/volume texture: count3
+ matrixMode = kTexMatrix3;
+ }
+ else
+ {
+ // detect projected matrix
+ projectedTexture = (mat[3] != 0.0f || mat[7] != 0.0f || mat[11] != 0.0f || mat[15] != 1.0f) ? 1 : 0;
+ // Cards that do support projected textures or cubemaps seem to want
+ // Count3 flags for object/eyelinear transforms. Cards that don't support
+ // projection nor cubemaps will have to use Count2 - fixes GUI text rendering!
+ bool is3DTexGen = (texGen != kTexGenDisabled && texGen != kTexGenSphereMap);
+
+ if( projectedTexture )
+ {
+ matrixMode = kTexMatrix4;
+ }
+ else if( is3DTexGen )
+ {
+ matrixMode = kTexMatrix3;
+ }
+ else
+ {
+ // regular texture: count2, and move matrix' 4th row into 3rd one
+ matrixMode = kTexMatrix2;
+ mat[ 8] = mat[12];
+ mat[ 9] = mat[13];
+ mat[10] = mat[14];
+ mat[11] = mat[15];
+ }
+ }
+
+ m_VertexConfig.textureMatrixModes = m_VertexConfig.textureMatrixModes & ~(3<<(unit*2)) | (matrixMode<<(unit*2));
+ m_VertexData.projectedTextures = m_VertexData.projectedTextures & ~(1<<unit) | (projectedTexture<<unit);
+
+ // -------- texture coordinate generation
+
+ TextureSourceMode texSource = texGen == kTexGenDisabled ? kTexSourceUV0 : static_cast<TextureSourceMode>(texGen + 1);
+ m_VertexConfig.textureSources = m_VertexConfig.textureSources & ~(7<<(unit*3)) | (texSource<<(unit*3));
+
+ if( texGen == kTexGenSphereMap && !IsShaderActive(kShaderVertex) )
+ {
+ if( g_D3DUsesMixedVP && !(gGraphicsCaps.d3d.d3dcaps.VertexProcessingCaps & D3DVTXPCAPS_TEXGEN_SPHEREMAP) )
+ m_State.m_NeedsSofwareVPFlags |= kNeedsSoftwareVPTexGen;
+ }
+}
+
+void GfxDeviceD3D9::SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace )
+{
+ m_Textures.SetTextureParams( texture, texDim, filter, wrap, anisoLevel, hasMipMap, colorSpace );
+
+ // we'll need to set texture sampler states, so invalidate current texture cache
+ // invalidate texture unit states that used this texture
+ for (int i = 0; i < ARRAY_SIZE(m_State.texturesPS); ++i)
+ {
+ TextureUnitStateD3D& currTex = m_State.texturesPS[i];
+ if( currTex.texID == texture )
+ currTex.Invalidate();
+ }
+ for (int i = 0; i < ARRAY_SIZE(m_State.texturesVS); ++i)
+ {
+ TextureUnitStateD3D& currTex = m_State.texturesVS[i];
+ if (currTex.texID == texture)
+ currTex.Invalidate();
+ }
+}
+
+
+void GfxDeviceD3D9::SetShadersThreadable( GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount])
+{
+ GpuProgram* vertexProgram = programs[kShaderVertex];
+ GpuProgram* fragmentProgram = programs[kShaderFragment];
+
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ // vertex shader
+ if( vertexProgram && vertexProgram->GetImplType() == kShaderImplVertex )
+ {
+ // set the shader
+ bool resetToNoFog = false;
+ IDirect3DVertexShader9* shader = static_cast<D3D9VertexShader&>(*vertexProgram).GetShader(m_FogParams.mode, resetToNoFog);
+ // Note: get pixel shader to match actually used fog mode from VS. If VS was too complex
+ // to patch for fog, for example, then we want PS to not have fog as well.
+ if (resetToNoFog)
+ m_FogParams.mode = kFogDisabled;
+ DebugAssert (shader);
+
+ if( m_State.activeShader[kShaderVertex] != shader )
+ {
+ D3D9_CALL(dev->SetVertexShader( shader ));
+ if (m_State.activeShader[kShaderVertex] == NULL)
+ {
+ for( int i = 0; i < kMaxSupportedTextureCoords; ++i )
+ {
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_TEXCOORDINDEX, i ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTSS_TCI_PASSTHRU ));
+ }
+ }
+
+ m_VertexPrevious.vertexShader = NULL;
+ m_VertexPrevious.ambient.set(-1,-1,-1,-1);
+
+ m_State.activeShader[kShaderVertex] = shader;
+ }
+
+ if( g_D3DUsesMixedVP )
+ m_State.m_NeedsSofwareVPFlags |= kNeedsSoftwareVPVertexShader;
+
+ m_BuiltinParamIndices[kShaderVertex] = &params[kShaderVertex]->GetBuiltinParams();
+ }
+ else
+ {
+ // clear the shader
+ DebugAssertIf( vertexProgram != 0 );
+ if( m_State.activeShader[kShaderVertex] != 0 )
+ {
+ D3D9_CALL(dev->SetVertexShader( NULL ));
+ m_State.activeShader[kShaderVertex] = 0;
+ }
+
+ if( g_D3DUsesMixedVP )
+ m_State.m_NeedsSofwareVPFlags &= ~kNeedsSoftwareVPVertexShader;
+
+ m_BuiltinParamIndices[kShaderVertex] = &m_NullParamIndices;
+ }
+
+ // pixel shader
+ if( fragmentProgram && fragmentProgram->GetImplType() == kShaderImplFragment )
+ {
+ // set the shader
+ IDirect3DPixelShader9* shader = static_cast<D3D9PixelShader&>(*fragmentProgram).GetShader(m_FogParams.mode, *params[kShaderFragment]);
+ DebugAssert (shader);
+
+ if( m_State.activeShader[kShaderFragment] != shader )
+ {
+ D3D9_CALL(dev->SetPixelShader( shader ));
+ m_State.activeShader[kShaderFragment] = shader;
+ m_State.fixedFunctionPS = 0;
+ }
+
+ m_BuiltinParamIndices[kShaderFragment] = &params[kShaderFragment]->GetBuiltinParams();
+ }
+ else
+ {
+ // clear the shader
+ DebugAssertIf( fragmentProgram != 0 );
+ if( m_State.activeShader[kShaderFragment] != 0 )
+ {
+ D3D9_CALL(dev->SetPixelShader( NULL ));
+ m_State.activeShader[kShaderFragment] = 0;
+ m_State.fixedFunctionPS = 0;
+ }
+
+ m_BuiltinParamIndices[kShaderFragment] = &m_NullParamIndices;
+ }
+
+ for (int pt = 0; pt < kShaderTypeCount; ++pt)
+ {
+ if (programs[pt])
+ {
+ m_State.activeGpuProgramParams[pt] = params[pt];
+ m_State.activeGpuProgram[pt] = programs[pt];
+ programs[pt]->ApplyGpuProgram (*params[pt], paramsBuffer[pt]);
+ }
+ else
+ {
+ m_State.activeGpuProgramParams[pt] = NULL;
+ m_State.activeGpuProgram[pt] = NULL;
+ }
+ }
+}
+
+
+bool GfxDeviceD3D9::IsShaderActive( ShaderType type ) const
+{
+ return m_State.activeShader[type] != 0;
+}
+
+void GfxDeviceD3D9::DestroySubProgram( ShaderLab::SubProgram* subprogram )
+{
+ GpuProgram* program = &subprogram->GetGpuProgram();
+ if (program->GetImplType() == kShaderImplVertex)
+ {
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ IUnknown* shader = static_cast<D3D9VertexShader*>(program)->GetShaderAtFogIndex(static_cast<FogMode>(i));
+ if (m_State.activeShader[kShaderVertex] == shader)
+ m_State.activeShader[kShaderVertex] = NULL;
+ }
+ }
+ else if (program->GetImplType() == kShaderImplFragment)
+ {
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ IUnknown* shader = static_cast<D3D9PixelShader*>(program)->GetShaderAtFogIndex(static_cast<FogMode>(i));
+ if (m_State.activeShader[kShaderFragment] == shader)
+ m_State.activeShader[kShaderFragment] = NULL;
+ }
+ }
+ delete subprogram;
+}
+
+void GfxDeviceD3D9::DisableLights( int startLight )
+{
+ m_VertexData.vertexLightCount = startLight;
+
+ const Vector4f black(0.0F, 0.0F, 0.0F, 0.0F);
+ for (int i = startLight; i < gGraphicsCaps.maxLights; ++i)
+ {
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Diffuse + i), black);
+ }
+}
+
+void GfxDeviceD3D9::SetLight( int light, const GfxVertexLight& data)
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ DebugAssert(light >= 0 && light < kMaxSupportedVertexLights);
+
+ DebugAssertIf( (data.position.w == 0.0f) != (data.type == kLightDirectional) ); // directional lights should have 0 in position.w
+ DebugAssertIf( (data.spotAngle != -1.0f) != (data.type == kLightSpot) ); // non-spot lights should have -1 in spot angle
+
+ GfxVertexLight& dest = m_VertexData.lights[light];
+ dest = data;
+
+ const Matrix4x4f& viewMat = m_BuiltinParamValues.GetMatrixParam(kShaderMatView);
+
+ if (data.type == kLightDirectional)
+ {
+ dest.position.Set(0.0f,0.0f,0.0f,0.0f);
+ Vector3f v = viewMat.MultiplyVector3((const Vector3f&)data.position);
+ dest.spotDirection.Set( v.x, v.y, v.z, 0.0f );
+ }
+ else
+ {
+ Vector3f v = viewMat.MultiplyPoint3((const Vector3f&)data.position);
+ dest.position.Set( v.x, v.y, v.z, 1.0f );
+ Vector3f d = viewMat.MultiplyVector3((const Vector3f&)data.spotDirection);
+ dest.spotDirection.Set( d.x, d.y, d.z, 0.0f );
+ }
+
+ SetupVertexLightParams (light, data);
+}
+
+void GfxDeviceD3D9::SetAmbient( const float ambient[4] )
+{
+ if( m_VertexData.ambient != ambient )
+ {
+ m_VertexData.ambient.set( ambient );
+ m_VertexData.ambientClamped.set( clamp01(ambient[0]), clamp01(ambient[1]), clamp01(ambient[2]), clamp01(ambient[3]) );
+ m_BuiltinParamValues.SetVectorParam(kShaderVecLightModelAmbient, Vector4f(ambient));
+ }
+}
+
+
+static D3DFOGMODE s_D3DFogModes[kFogModeCount] = { D3DFOG_NONE, D3DFOG_LINEAR, D3DFOG_EXP, D3DFOG_EXP2 };
+
+void GfxDeviceD3D9::EnableFog(const GfxFogParams& fog)
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ DebugAssertIf( fog.mode <= kFogDisabled );
+ if( m_FogParams.mode != fog.mode )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_FOGTABLEMODE, s_D3DFogModes[fog.mode] )); // TODO: or maybe vertex fog?
+ D3D9_CALL(dev->SetRenderState( D3DRS_FOGENABLE, TRUE ));
+ m_FogParams.mode = fog.mode;
+ }
+ if( m_FogParams.start != fog.start )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_FOGSTART, *(DWORD*)&fog.start ));
+ m_FogParams.start = fog.start;
+ }
+ if( m_FogParams.end != fog.end )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_FOGEND, *(DWORD*)&fog.end ));
+ m_FogParams.end = fog.end;
+ }
+ if( m_FogParams.density != fog.density )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_FOGDENSITY, *(DWORD*)&fog.density ));
+ m_FogParams.density = fog.density;
+ }
+ if( m_FogParams.color != fog.color )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_FOGCOLOR, ColorToD3D(fog.color.GetPtr()) ));
+ m_FogParams.color = fog.color;
+ }
+}
+
+void GfxDeviceD3D9::DisableFog()
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ if( m_FogParams.mode != kFogDisabled )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_FOGENABLE, FALSE ));
+ m_FogParams.mode = kFogDisabled;
+ }
+}
+
+VBO* GfxDeviceD3D9::CreateVBO()
+{
+ VBO* vbo = new D3D9VBO();
+ OnCreateVBO(vbo);
+ return vbo;
+}
+
+void GfxDeviceD3D9::DeleteVBO( VBO* vbo )
+{
+ OnDeleteVBO(vbo);
+ delete vbo;
+}
+
+DynamicVBO& GfxDeviceD3D9::GetDynamicVBO()
+{
+ if( !m_DynamicVBO ) {
+ m_DynamicVBO = new DynamicD3D9VBO( 1024 * 1024, 65536 ); // initial 1 MiB VB, 64 KiB IB
+ }
+ return *m_DynamicVBO;
+}
+
+IDirect3DVertexBuffer9* GfxDeviceD3D9::GetAllWhiteVertexStream()
+{
+ if( !m_AllWhiteVertexStream )
+ {
+ int maxVerts = 0x10000;
+ int size = maxVerts * sizeof(D3DCOLOR);
+ HRESULT hr = GetD3DDevice()->CreateVertexBuffer( size, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &m_AllWhiteVertexStream, NULL );
+ if( !SUCCEEDED(hr) )
+ return NULL;
+ void* buffer;
+ hr = m_AllWhiteVertexStream->Lock( 0 , 0, &buffer, 0 );
+ if( !SUCCEEDED(hr) )
+ {
+ SAFE_RELEASE( m_AllWhiteVertexStream );
+ return NULL;
+ }
+ D3DCOLOR* dest = (D3DCOLOR*)buffer;
+ for( int i = 0; i < maxVerts; i++ )
+ dest[i] = D3DCOLOR_ARGB(255, 255, 255, 255);
+ m_AllWhiteVertexStream->Unlock();
+ }
+ return m_AllWhiteVertexStream;
+}
+
+void GfxDeviceD3D9::ResetDynamicResources()
+{
+ delete m_DynamicVBO;
+ m_DynamicVBO = NULL;
+
+ CleanupEventQueries ();
+ ResetDynamicVBs ();
+
+ #if ENABLE_PROFILER
+ m_TimerQueriesD3D9.ReleaseAllQueries();
+ #endif
+
+ D3D9VBO::CleanupSharedIndexBuffer();
+}
+
+
+void ResetDynamicResourcesD3D9()
+{
+ AutoGfxDeviceAcquireThreadOwnership autoOwner;
+ GetD3D9GfxDevice().ResetDynamicResources();
+}
+
+IDirect3DVertexDeclaration9* GetD3DVertexDeclaration( UInt32 shaderChannelsMap )
+{
+ ChannelInfoArray channels;
+ int offset = 0;
+ for (int i = 0; i < kShaderChannelCount; i++)
+ {
+ ChannelInfo& info = channels[i];
+ if (shaderChannelsMap & (1 << i))
+ {
+ info.stream = 0;
+ info.offset = offset;
+ info.format = VBO::GetDefaultChannelFormat( i );
+ info.dimension = VBO::GetDefaultChannelDimension( i );
+ offset += VBO::GetDefaultChannelByteSize( i );
+}
+ else
+ info.Reset();
+ }
+ return GetD3D9GfxDevice().GetVertexDecls().GetVertexDecl( channels );
+}
+
+VertexShaderConstantCache& GetD3D9VertexShaderConstantCache()
+{
+ return GetD3D9GfxDevice().GetVertexShaderConstantCache();
+}
+
+PixelShaderConstantCache& GetD3D9PixelShaderConstantCache()
+{
+ return GetD3D9GfxDevice().GetPixelShaderConstantCache();
+}
+
+
+// ---------- render textures
+
+RenderSurfaceHandle GfxDeviceD3D9::CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags)
+{
+ return CreateRenderColorSurfaceD3D9 (textureID, width, height, samples, dim, createFlags, format, m_Textures);
+}
+RenderSurfaceHandle GfxDeviceD3D9::CreateRenderDepthSurface(TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags)
+{
+ return CreateRenderDepthSurfaceD3D9 (textureID, width, height, samples, depthFormat, createFlags, m_Textures);
+}
+void GfxDeviceD3D9::DestroyRenderSurface(RenderSurfaceHandle& rs)
+{
+ DestroyRenderSurfaceD3D9( rs, m_Textures );
+}
+void GfxDeviceD3D9::SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face)
+{
+ bool isBackBuffer;
+ m_CurrTargetWidth = m_CurrWindowWidth;
+ m_CurrTargetHeight = m_CurrWindowHeight;
+ if (SetRenderTargetD3D9 (count, colorHandles, depthHandle, mipLevel, face, m_CurrTargetWidth, m_CurrTargetHeight, isBackBuffer))
+ {
+ // changing render target might mean different color clear flags; so reset current state
+ m_CurrBlendState = NULL;
+ }
+}
+void GfxDeviceD3D9::ResolveDepthIntoTexture (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle)
+{
+ Assert (gGraphicsCaps.d3d.hasDepthResolveRESZ);
+
+ RenderSurfaceD3D9* depthSurf = reinterpret_cast<RenderSurfaceD3D9*>(depthHandle.object);
+
+ IDirect3DDevice9* dev = GetD3DDevice();
+ // Important: change point size render state to something else than RESZ
+ // before the dummy draw call; otherwise RESZ state set will be filtered out
+ // by non-PURE D3D device.
+ dev->SetRenderState (D3DRS_POINTSIZE, 0);
+
+ // Bind destination as texture
+ SetTexture (kShaderFragment, 0, 0, depthSurf->textureID, kTexDim2D, 0.0f);
+
+ // Dummy draw call
+ float dummy[3] = {0,0,0};
+ dev->DrawPrimitiveUP (D3DPT_POINTLIST, 1, dummy, 12);
+
+ // RESZ to trigger depth buffer copy
+ dev->SetRenderState (D3DRS_POINTSIZE, 0x7fa05000);
+}
+
+
+void GfxDeviceD3D9::ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle)
+{
+ Assert (srcHandle.IsValid());
+ Assert (dstHandle.IsValid());
+ RenderColorSurfaceD3D9* src = reinterpret_cast<RenderColorSurfaceD3D9*>(srcHandle.object);
+ RenderColorSurfaceD3D9* dst = reinterpret_cast<RenderColorSurfaceD3D9*>(dstHandle.object);
+ if (!src->colorSurface || !dst->colorSurface)
+ {
+ WarningString("RenderTexture: Resolving non-color surfaces.");
+ return;
+ }
+ if (!src->m_Surface || !dst->m_Surface)
+ {
+ WarningString("RenderTexture: Resolving NULL surfaces.");
+ return;
+ }
+ if (src->dim != dst->dim)
+ {
+ WarningString("RenderTexture: Resolving surfaces of different types.");
+ return;
+ }
+ if (src->format != dst->format)
+ {
+ WarningString("RenderTexture: Resolving surfaces of different formats.");
+ return;
+ }
+ if (src->width != dst->width || src->height != dst->height)
+ {
+ WarningString("RenderTexture: Resolving surfaces of different sizes.");
+ return;
+ }
+
+ IDirect3DDevice9* dev = GetD3DDevice();
+ dev->StretchRect (src->m_Surface, NULL, dst->m_Surface, NULL, D3DTEXF_NONE);
+}
+
+RenderSurfaceHandle GfxDeviceD3D9::GetActiveRenderColorSurface (int index)
+{
+ return GetActiveRenderColorSurfaceD3D9(index);
+}
+RenderSurfaceHandle GfxDeviceD3D9::GetActiveRenderDepthSurface()
+{
+ return GetActiveRenderDepthSurfaceD3D9();
+}
+void GfxDeviceD3D9::SetSurfaceFlags (RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags)
+{
+}
+
+
+// ---------- uploading textures
+
+void GfxDeviceD3D9::UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace )
+{
+ m_Textures.UploadTexture2D( texture, dimension, srcData, width, height, format, mipCount, uploadFlags, skipMipLevels, usageMode, colorSpace );
+}
+void GfxDeviceD3D9::UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace )
+{
+ m_Textures.UploadTextureSubData2D( texture, srcData, mipLevel, x, y, width, height, format, colorSpace );
+}
+void GfxDeviceD3D9::UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace )
+{
+ m_Textures.UploadTextureCube( texture, srcData, faceDataSize, size, format, mipCount, uploadFlags, colorSpace );
+}
+void GfxDeviceD3D9::UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags )
+{
+ m_Textures.UploadTexture3D( texture, srcData, width, height, depth, format, mipCount, uploadFlags );
+}
+
+void GfxDeviceD3D9::DeleteTexture( TextureID texture )
+{
+ m_Textures.DeleteTexture( texture );
+
+ // invalidate texture unit states that used this texture
+ for (int i = 0; i < ARRAY_SIZE(m_State.texturesPS); ++i)
+ {
+ TextureUnitStateD3D& currTex = m_State.texturesPS[i];
+ if( currTex.texID == texture )
+ currTex.Invalidate();
+ }
+ for (int i = 0; i < ARRAY_SIZE(m_State.texturesVS); ++i)
+ {
+ TextureUnitStateD3D& currTex = m_State.texturesVS[i];
+ if (currTex.texID == texture)
+ currTex.Invalidate();
+ }
+}
+
+void UnbindTextureD3D9( TextureID texture )
+{
+ GfxDeviceD3D9& device = static_cast<GfxDeviceD3D9&>( GetRealGfxDevice() );
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ // invalidate texture unit states that used this texture
+ for (int i = 0; i < ARRAY_SIZE(device.GetState().texturesPS); ++i)
+ {
+ TextureUnitStateD3D& currTex = device.GetState().texturesPS[i];
+ if( currTex.texID == texture )
+ {
+ D3D9_CALL(dev->SetTexture(GetD3D9SamplerIndex(kShaderFragment,i), NULL));
+ currTex.Invalidate();
+ }
+ }
+ for (int i = 0; i < ARRAY_SIZE(device.GetState().texturesVS); ++i)
+ {
+ TextureUnitStateD3D& currTex = device.GetState().texturesVS[i];
+ if (currTex.texID == texture)
+ {
+ D3D9_CALL(dev->SetTexture(GetD3D9SamplerIndex(kShaderVertex,i), NULL));
+ currTex.Invalidate();
+ }
+ }
+}
+
+
+// ---------- context
+
+GfxDevice::PresentMode GfxDeviceD3D9::GetPresentMode()
+{
+ return kPresentBeforeUpdate;
+}
+
+void GfxDeviceD3D9::BeginFrame()
+{
+ if( m_State.m_DeviceLost )
+ return;
+
+ // begin scene
+ Assert( !m_InsideFrame );
+ GetD3DDevice()->BeginScene();
+ m_InsideFrame = true;
+
+}
+
+void GfxDeviceD3D9::EndFrame()
+{
+ // Check if we're inside scene in case BeginFrame() failed
+ if( !m_InsideFrame )
+ return;
+
+ GetD3DDevice()->EndScene();
+ m_InsideFrame = false;
+}
+
+bool GfxDeviceD3D9::IsValidState()
+{
+ return !m_State.m_DeviceLost;
+}
+
+bool GfxDeviceD3D9::HandleInvalidState()
+{
+#if ENABLE_MULTITHREADED_CODE
+ // Reset render textures owned by the main thread
+ if (Thread::CurrentThreadIsMainThread())
+ CommonReloadResources(kReleaseRenderTextures);
+#endif
+
+ ResetDynamicResourcesD3D9();
+
+ bool success = HandleD3DDeviceLost();
+
+#if ENABLE_PROFILER
+ if (success)
+ m_TimerQueriesD3D9.RecreateAllQueries();
+#endif
+
+ InvalidateState();
+ return success;
+}
+
+static void CleanupEventQueries ()
+{
+ D3D9QueryList::iterator itEnd = s_EventQueries.end();
+ for (D3D9QueryList::iterator it = s_EventQueries.begin(); it != itEnd; ++it)
+ {
+ IDirect3DQuery9* query = *it;
+ if (query != NULL)
+ {
+ query->Release();
+ }
+ }
+ s_EventQueries.clear();
+}
+
+static void PopEventQuery ()
+{
+ AssertIf (s_EventQueries.empty());
+
+ IDirect3DQuery9* query = s_EventQueries.front();
+ AssertIf (query == NULL);
+
+ while (S_FALSE == query->GetData (NULL, 0, D3DGETDATA_FLUSH))
+ {
+ Sleep (1);
+ }
+ query->Release();
+
+ s_EventQueries.pop_front();
+}
+
+void GfxDeviceD3D9::PushEventQuery ()
+{
+ if (m_MaxBufferedFrames < 0)
+ return;
+
+ IDirect3DQuery9* query = NULL;
+ HRESULT hr = GetD3DDevice()->CreateQuery (D3DQUERYTYPE_EVENT, &query);
+ if (query != NULL)
+ {
+ if (SUCCEEDED(query->Issue(D3DISSUE_END)))
+ s_EventQueries.push_back (query);
+ else
+ query->Release();
+ }
+
+ // don't exceed maximum lag... instead we'll deterministically block here until the GPU has done enough work
+ while (!s_EventQueries.empty() && s_EventQueries.size() > m_MaxBufferedFrames)
+ {
+ PopEventQuery();
+ }
+}
+
+void GfxDeviceD3D9::PresentFrame()
+{
+ if( m_State.m_DeviceLost )
+ return;
+
+ HRESULT hr = GetD3DDevice()->Present( NULL, NULL, NULL, NULL );
+ PushEventQuery();
+ // When D3DERR_DRIVERINTERNALERROR is returned from Present(),
+ // the application can do one of the following, try recovering just as
+ // from the lost device.
+ if( hr == D3DERR_DEVICELOST || hr == D3DERR_DRIVERINTERNALERROR )
+ {
+ m_State.m_DeviceLost = true;
+ }
+}
+
+void GfxDeviceD3D9::FinishRendering()
+{
+ // not needed on D3D
+}
+
+
+
+// ---------- immediate mode rendering
+
+// we break very large immediate mode submissions into multiple batches internally
+const int kMaxImmediateVerticesPerDraw = 8192;
+
+
+ImmediateModeD3D::ImmediateModeD3D()
+: m_ImmVertexDecl(NULL)
+{
+ m_QuadsIB = new UInt16[kMaxImmediateVerticesPerDraw*6];
+ UInt32 baseIndex = 0;
+ UInt16* ibPtr = m_QuadsIB;
+ for( int i = 0; i < kMaxImmediateVerticesPerDraw; ++i )
+ {
+ ibPtr[0] = baseIndex + 1;
+ ibPtr[1] = baseIndex + 2;
+ ibPtr[2] = baseIndex;
+ ibPtr[3] = baseIndex + 2;
+ ibPtr[4] = baseIndex + 3;
+ ibPtr[5] = baseIndex;
+ baseIndex += 4;
+ ibPtr += 6;
+ }
+}
+
+ImmediateModeD3D::~ImmediateModeD3D()
+{
+ delete[] m_QuadsIB;
+}
+
+
+void ImmediateModeD3D::Invalidate()
+{
+ m_Vertices.clear();
+ memset( &m_Current, 0, sizeof(m_Current) );
+}
+
+void GfxDeviceD3D9::ImmediateVertex( float x, float y, float z )
+{
+ // If the current batch is becoming too large, internally end it and begin it again.
+ size_t currentSize = m_Imm.m_Vertices.size();
+ if( currentSize >= kMaxImmediateVerticesPerDraw - 4 )
+ {
+ GfxPrimitiveType mode = m_Imm.m_Mode;
+ // For triangles, break batch when multiple of 3's is reached.
+ if( mode == kPrimitiveTriangles && currentSize % 3 == 0 )
+ {
+ ImmediateEnd();
+ ImmediateBegin( mode );
+ }
+ // For other primitives, break on multiple of 4's.
+ // NOTE: This won't quite work for triangle strips, but we'll just pretend
+ // that will never happen.
+ else if( mode != kPrimitiveTriangles && currentSize % 4 == 0 )
+ {
+ ImmediateEnd();
+ ImmediateBegin( mode );
+ }
+ }
+ D3DVECTOR& vert = m_Imm.m_Current.vertex;
+ vert.x = x;
+ vert.y = y;
+ vert.z = z;
+ m_Imm.m_Vertices.push_back( m_Imm.m_Current );
+}
+
+void GfxDeviceD3D9::ImmediateNormal( float x, float y, float z )
+{
+ m_Imm.m_Current.normal.x = x;
+ m_Imm.m_Current.normal.y = y;
+ m_Imm.m_Current.normal.z = z;
+}
+
+void GfxDeviceD3D9::ImmediateColor( float r, float g, float b, float a )
+{
+ float color[4] = { r, g, b, a };
+ m_Imm.m_Current.color = ColorToD3D( color );
+}
+
+void GfxDeviceD3D9::ImmediateTexCoordAll( float x, float y, float z )
+{
+ for( int i = 0; i < 8; ++i )
+ {
+ D3DVECTOR& uv = m_Imm.m_Current.texCoords[i];
+ uv.x = x;
+ uv.y = y;
+ uv.z = z;
+ }
+}
+
+void GfxDeviceD3D9::ImmediateTexCoord( int unit, float x, float y, float z )
+{
+ if( unit < 0 || unit >= 8 )
+ {
+ ErrorString( "Invalid unit for texcoord" );
+ return;
+ }
+ D3DVECTOR& uv = m_Imm.m_Current.texCoords[unit];
+ uv.x = x;
+ uv.y = y;
+ uv.z = z;
+}
+
+void GfxDeviceD3D9::ImmediateBegin( GfxPrimitiveType type )
+{
+ m_Imm.m_Mode = type;
+ m_Imm.m_Vertices.clear();
+}
+
+void GfxDeviceD3D9::ImmediateEnd()
+{
+ if( m_Imm.m_Vertices.empty() )
+ return;
+
+ // lazily create vertex declaration
+ IDirect3DDevice9* dev = GetD3DDevice();
+ HRESULT hr = S_OK;
+ if( !m_Imm.m_ImmVertexDecl )
+ {
+ static const D3DVERTEXELEMENT9 elements[] = {
+ // stream, offset, data type, processing, semantics, index
+ { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, // position
+ { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 }, // normal
+ { 0, 24, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 }, // color
+ { 0, 28, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, // UVs
+ { 0, 40, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
+ { 0, 52, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 2 },
+ { 0, 64, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 3 },
+ { 0, 76, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 4 },
+ { 0, 88, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 5 },
+ { 0, 100, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 6 },
+ { 0, 112, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 7 },
+ D3DDECL_END()
+ };
+ hr = dev->CreateVertexDeclaration( elements, &m_Imm.m_ImmVertexDecl );
+ if( FAILED(hr) ) {
+ // TODO: error
+ }
+ }
+
+ // draw
+ D3D9_CALL(dev->SetVertexDeclaration( m_Imm.m_ImmVertexDecl ));
+
+ BeforeDrawCall( true );
+
+ int vertexCount = m_Imm.m_Vertices.size();
+ const ImmediateVertexD3D* vb = &m_Imm.m_Vertices[0];
+ switch( m_Imm.m_Mode )
+ {
+ case kPrimitiveTriangles:
+ hr = D3D9_CALL_HR(dev->DrawPrimitiveUP( D3DPT_TRIANGLELIST, vertexCount / 3, vb, sizeof(ImmediateVertexD3D) ));
+ m_Stats.AddDrawCall( vertexCount / 3, vertexCount );
+ break;
+ case kPrimitiveTriangleStripDeprecated:
+ hr = D3D9_CALL_HR(dev->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, vertexCount - 2, vb, sizeof(ImmediateVertexD3D) ));
+ m_Stats.AddDrawCall( vertexCount - 2, vertexCount );
+ break;
+ case kPrimitiveQuads:
+ hr = D3D9_CALL_HR(dev->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST, 0, vertexCount, vertexCount / 4 * 2, m_Imm.m_QuadsIB, D3DFMT_INDEX16, vb, sizeof(ImmediateVertexD3D) ));
+ m_Stats.AddDrawCall( vertexCount / 4 * 2, vertexCount );
+ break;
+ case kPrimitiveLines:
+ hr = D3D9_CALL_HR(dev->DrawPrimitiveUP( D3DPT_LINELIST, vertexCount / 2, vb, sizeof(ImmediateVertexD3D) ));
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ default:
+ AssertString("ImmediateEnd: unknown draw mode");
+ }
+ AssertIf( FAILED(hr) );
+ // TODO: stats
+
+ // clear vertices
+ m_Imm.m_Vertices.clear();
+}
+
+
+
+bool GfxDeviceD3D9::CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 )
+{
+ HRESULT hr;
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ SurfacePointer renderTarget;
+ hr = dev->GetRenderTarget( 0, &renderTarget );
+ if( !renderTarget || FAILED(hr) )
+ return false;
+
+ D3DSURFACE_DESC rtDesc;
+ renderTarget->GetDesc( &rtDesc );
+
+ SurfacePointer resolvedSurface;
+ if( rtDesc.MultiSampleType != D3DMULTISAMPLE_NONE )
+ {
+ hr = dev->CreateRenderTarget( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &resolvedSurface, NULL );
+ if( FAILED(hr) )
+ return false;
+ hr = dev->StretchRect( renderTarget, NULL, resolvedSurface, NULL, D3DTEXF_NONE );
+ if( FAILED(hr) )
+ return false;
+ renderTarget = resolvedSurface;
+ }
+
+ SurfacePointer offscreenSurface;
+ hr = dev->CreateOffscreenPlainSurface( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DPOOL_SYSTEMMEM, &offscreenSurface, NULL );
+ if( FAILED(hr) )
+ return false;
+
+ hr = dev->GetRenderTargetData( renderTarget, offscreenSurface );
+ bool ok = SUCCEEDED(hr);
+ if( ok )
+ {
+ rgba32 += (height-1) * width * sizeof(UInt32);
+ if( rtDesc.Format == D3DFMT_A8R8G8B8 || rtDesc.Format == D3DFMT_X8R8G8B8 )
+ {
+ // Backbuffer is 32 bit
+ D3DLOCKED_RECT lr;
+ RECT rect;
+ rect.left = left;
+ rect.right = left + width;
+ rect.top = rtDesc.Height - bottom - height;
+ rect.bottom = rtDesc.Height - bottom;
+ hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
+ if( SUCCEEDED(hr) )
+ {
+ const UInt8* src = (const UInt8*)lr.pBits;
+ for( int y = 0; y < height; ++y )
+ {
+ const UInt32* srcPtr = (const UInt32*)src;
+ UInt32* dstPtr = (UInt32*)rgba32;
+ for( int x = 0; x < width; ++x )
+ {
+ UInt32 argbCol = *srcPtr;
+ UInt32 abgrCol = (argbCol&0xFF00FF00) | ((argbCol&0x00FF0000)>>16) | ((argbCol&0x000000FF)<<16);
+ *dstPtr = abgrCol;
+ ++srcPtr;
+ ++dstPtr;
+ }
+ rgba32 -= width * sizeof(UInt32);
+ src += lr.Pitch;
+ }
+ }
+ else
+ {
+ ok = false;
+ }
+ offscreenSurface->UnlockRect();
+ }
+ else if( rtDesc.Format == D3DFMT_R5G6B5 )
+ {
+ // Backbuffer is 16 bit 565
+ D3DLOCKED_RECT lr;
+ RECT rect;
+ rect.left = left;
+ rect.right = left + width;
+ rect.top = rtDesc.Height - bottom - height;
+ rect.bottom = rtDesc.Height - bottom;
+ hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
+ if( SUCCEEDED(hr) )
+ {
+ const UInt8* src = (const UInt8*)lr.pBits;
+ for( int y = 0; y < height; ++y )
+ {
+ const UInt16* srcPtr = (const UInt16*)src;
+ UInt32* dstPtr = (UInt32*)rgba32;
+ for( int x = 0; x < width; ++x )
+ {
+ UInt16 rgbCol = *srcPtr;
+ UInt32 abgrCol = 0xFF000000 | ((rgbCol&0xF800)>>8) | ((rgbCol&0x07E0)<<5) | ((rgbCol&0x001F)<<19);
+ *dstPtr = abgrCol;
+ ++srcPtr;
+ ++dstPtr;
+ }
+ rgba32 -= width * sizeof(UInt32);
+ src += lr.Pitch;
+ }
+ }
+ else
+ {
+ ok = false;
+ }
+ offscreenSurface->UnlockRect();
+ }
+ else if( rtDesc.Format == D3DFMT_X1R5G5B5 || rtDesc.Format == D3DFMT_A1R5G5B5 )
+ {
+ // Backbuffer is 15 bit 555
+ D3DLOCKED_RECT lr;
+ RECT rect;
+ rect.left = left;
+ rect.right = left + width;
+ rect.top = rtDesc.Height - bottom - height;
+ rect.bottom = rtDesc.Height - bottom;
+ hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
+ if( SUCCEEDED(hr) )
+ {
+ const UInt8* src = (const UInt8*)lr.pBits;
+ for( int y = 0; y < height; ++y )
+ {
+ const UInt16* srcPtr = (const UInt16*)src;
+ UInt32* dstPtr = (UInt32*)rgba32;
+ for( int x = 0; x < width; ++x )
+ {
+ UInt16 rgbCol = *srcPtr;
+ UInt32 abgrCol = ((rgbCol&0x8000)<<16) | ((rgbCol&0x7C00)>>7) | ((rgbCol&0x03E0)<<6) | ((rgbCol&0x001F)<<19);
+ *dstPtr = abgrCol;
+ ++srcPtr;
+ ++dstPtr;
+ }
+ rgba32 -= width * sizeof(UInt32);
+ src += lr.Pitch;
+ }
+ }
+ else
+ {
+ ok = false;
+ }
+ offscreenSurface->UnlockRect();
+ }
+ else
+ {
+ // TODO: handle more conversions!
+ ok = false;
+ }
+ }
+
+ return ok;
+}
+
+
+
+bool GfxDeviceD3D9::ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY )
+{
+ // TODO: make it work in all different situations
+
+ AssertIf( image.GetFormat() != kTexFormatARGB32 && image.GetFormat() != kTexFormatRGB24 );
+
+ HRESULT hr;
+ IDirect3DDevice9* dev = GetD3DDevice();
+ SurfacePointer renderTarget;
+ hr = dev->GetRenderTarget( 0, &renderTarget );
+ if( !renderTarget || FAILED(hr) )
+ return false;
+
+ D3DSURFACE_DESC rtDesc;
+ renderTarget->GetDesc( &rtDesc );
+
+ SurfacePointer resolvedSurface;
+ if( rtDesc.MultiSampleType != D3DMULTISAMPLE_NONE )
+ {
+ hr = dev->CreateRenderTarget( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &resolvedSurface, NULL );
+ if( FAILED(hr) )
+ return false;
+ hr = dev->StretchRect( renderTarget, NULL, resolvedSurface, NULL, D3DTEXF_NONE );
+ if( FAILED(hr) )
+ return false;
+ renderTarget = resolvedSurface;
+ }
+
+ SurfacePointer offscreenSurface;
+ hr = dev->CreateOffscreenPlainSurface( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DPOOL_SYSTEMMEM, &offscreenSurface, NULL );
+ if( FAILED(hr) )
+ return false;
+ if (width <= 0 || left < 0 || left + width > rtDesc.Width)
+ {
+ ErrorString("Trying to read pixel out of bounds");
+ return false;
+ }
+ if (height <= 0 || bottom < 0 || bottom + height > rtDesc.Height)
+ {
+ ErrorString("Trying to read pixel out of bounds");
+ return false;
+ }
+
+ hr = dev->GetRenderTargetData( renderTarget, offscreenSurface );
+ bool ok = SUCCEEDED(hr);
+ if( ok )
+ {
+ if( rtDesc.Format == D3DFMT_A8R8G8B8 || rtDesc.Format == D3DFMT_X8R8G8B8 )
+ {
+ // Render target is 32 bit
+ D3DLOCKED_RECT lr;
+ RECT rect;
+ rect.left = left;
+ rect.right = left + width;
+ rect.top = rtDesc.Height - bottom - height;
+ rect.bottom = rtDesc.Height - bottom;
+ hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
+ if( SUCCEEDED(hr) )
+ {
+ const UInt8* src = (const UInt8*)lr.pBits;
+ if( image.GetFormat() == kTexFormatARGB32 )
+ {
+ for( int y = height-1; y >= 0; --y )
+ {
+ const UInt32* srcPtr = (const UInt32*)src;
+ UInt32* dstPtr = (UInt32*)(image.GetRowPtr(destY+y) + destX * 4);
+ for( int x = 0; x < width; ++x )
+ {
+ UInt32 argbCol = *srcPtr;
+ UInt32 bgraCol = ((argbCol&0xFF000000)>>24) | ((argbCol&0x00FF0000)>>8) | ((argbCol&0x0000FF00)<<8) | ((argbCol&0x000000FF)<<24);
+ *dstPtr = bgraCol;
+ ++srcPtr;
+ ++dstPtr;
+ }
+ src += lr.Pitch;
+ }
+ }
+ else if( image.GetFormat() == kTexFormatRGB24 )
+ {
+ for( int y = height-1; y >= 0; --y )
+ {
+ const UInt32* srcPtr = (const UInt32*)src;
+ UInt8* dstPtr = image.GetRowPtr(destY+y) + destX * 3;
+ for( int x = 0; x < width; ++x )
+ {
+ UInt32 argbCol = *srcPtr;
+ dstPtr[0] = (argbCol & 0x00FF0000) >> 16;
+ dstPtr[1] = (argbCol & 0x0000FF00) >> 8;
+ dstPtr[2] = (argbCol & 0x000000FF);
+ ++srcPtr;
+ dstPtr += 3;
+ }
+ src += lr.Pitch;
+ }
+ }
+ else
+ {
+ AssertString( "Invalid image format" );
+ }
+ }
+ else
+ {
+ ok = false;
+ }
+ offscreenSurface->UnlockRect();
+ }
+ else if( rtDesc.Format == D3DFMT_R5G6B5 )
+ {
+ // Render target is 16 bit 565
+ D3DLOCKED_RECT lr;
+ RECT rect;
+ rect.left = left;
+ rect.right = left + width;
+ rect.top = rtDesc.Height - bottom - height;
+ rect.bottom = rtDesc.Height - bottom;
+ hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
+ if( SUCCEEDED(hr) )
+ {
+ const UInt8* src = (const UInt8*)lr.pBits;
+ if( image.GetFormat() == kTexFormatARGB32 )
+ {
+ for( int y = height-1; y >= 0; --y )
+ {
+ const UInt16* srcPtr = (const UInt16*)src;
+ UInt32* dstPtr = (UInt32*)(image.GetRowPtr(destY+y) + destX * 4);
+ for( int x = 0; x < width; ++x )
+ {
+ UInt16 argbCol = *srcPtr;
+ UInt32 bgraCol = 0x000000FF | (argbCol&0xF800) | ((argbCol&0x07E0)<<13) | ((argbCol&0x001F)<<27);
+ *dstPtr = bgraCol;
+ ++srcPtr;
+ ++dstPtr;
+ }
+ src += lr.Pitch;
+ }
+ }
+ else if( image.GetFormat() == kTexFormatRGB24 )
+ {
+ for( int y = height-1; y >= 0; --y )
+ {
+ const UInt16* srcPtr = (const UInt16*)src;
+ UInt8* dstPtr = image.GetRowPtr(destY+y) + destX * 3;
+ for( int x = 0; x < width; ++x )
+ {
+ UInt16 argbCol = *srcPtr;
+ dstPtr[0] = (argbCol & 0xF800) >> 8;
+ dstPtr[1] = (argbCol & 0x07E0) >> 3;
+ dstPtr[2] = (argbCol & 0x001F) << 3;
+ ++srcPtr;
+ dstPtr += 3;
+ }
+ src += lr.Pitch;
+ }
+ }
+ else
+ {
+ AssertString( "Invalid image format" );
+ }
+ }
+ else
+ {
+ ok = false;
+ }
+ offscreenSurface->UnlockRect();
+ }
+ else if( rtDesc.Format == D3DFMT_A1R5G5B5 || rtDesc.Format == D3DFMT_X1R5G5B5 )
+ {
+ // Render target is 15 bit 555
+ D3DLOCKED_RECT lr;
+ RECT rect;
+ rect.left = left;
+ rect.right = left + width;
+ rect.top = rtDesc.Height - bottom - height;
+ rect.bottom = rtDesc.Height - bottom;
+ hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
+ if( SUCCEEDED(hr) )
+ {
+ const UInt8* src = (const UInt8*)lr.pBits;
+ if( image.GetFormat() == kTexFormatARGB32 )
+ {
+ for( int y = height-1; y >= 0; --y )
+ {
+ const UInt16* srcPtr = (const UInt16*)src;
+ UInt32* dstPtr = (UInt32*)(image.GetRowPtr(destY+y) + destX * 4);
+ for( int x = 0; x < width; ++x )
+ {
+ UInt16 argbCol = *srcPtr;
+ UInt32 bgraCol = ((argbCol&0x8000)>>8) | ((argbCol&0x7C00)<<1) | ((argbCol&0x03E0)<<14) | ((argbCol&0x001F)<<27);
+ *dstPtr = bgraCol;
+ ++srcPtr;
+ ++dstPtr;
+ }
+ src += lr.Pitch;
+ }
+ }
+ else if( image.GetFormat() == kTexFormatRGB24 )
+ {
+ for( int y = height-1; y >= 0; --y )
+ {
+ const UInt16* srcPtr = (const UInt16*)src;
+ UInt8* dstPtr = image.GetRowPtr(destY+y) + destX * 3;
+ for( int x = 0; x < width; ++x )
+ {
+ UInt16 argbCol = *srcPtr;
+ dstPtr[0] = (argbCol & 0x7C00) >> 7;
+ dstPtr[1] = (argbCol & 0x03E0) >> 2;
+ dstPtr[2] = (argbCol & 0x001F) << 3;
+ ++srcPtr;
+ dstPtr += 3;
+ }
+ src += lr.Pitch;
+ }
+ }
+ else
+ {
+ AssertString( "Invalid image format" );
+ }
+ }
+ else
+ {
+ ok = false;
+ }
+ offscreenSurface->UnlockRect();
+ }
+ else
+ {
+ // TODO: handle more conversions!
+ ok = false;
+ }
+ }
+
+ return ok;
+}
+
+void GfxDeviceD3D9::GrabIntoRenderTexture(RenderSurfaceHandle rtHandle, RenderSurfaceHandle rd, int x, int y, int width, int height )
+{
+ if( !rtHandle.IsValid() )
+ return;
+
+ RenderColorSurfaceD3D9* renderTexture = reinterpret_cast<RenderColorSurfaceD3D9*>( rtHandle.object );
+
+ HRESULT hr;
+ IDirect3DDevice9* dev = GetD3DDevice();
+ SurfacePointer currentRenderTarget;
+ hr = dev->GetRenderTarget( 0, &currentRenderTarget );
+ if( !currentRenderTarget || FAILED(hr) )
+ return;
+
+ D3DSURFACE_DESC rtDesc;
+ currentRenderTarget->GetDesc( &rtDesc );
+
+ IDirect3DTexture9* texturePointer = static_cast<IDirect3DTexture9*>(m_Textures.GetTexture (renderTexture->textureID));
+ if( !texturePointer )
+ return;
+
+ SurfacePointer textureSurface;
+ hr = texturePointer->GetSurfaceLevel( 0, &textureSurface );
+ if( !textureSurface || FAILED(hr) )
+ return;
+
+ RECT rc;
+ rc.left = x;
+ rc.top = rtDesc.Height - (y + height);
+ rc.right = x + width;
+ rc.bottom = rtDesc.Height - (y);
+ hr = dev->StretchRect( currentRenderTarget, &rc, textureSurface, NULL, D3DTEXF_NONE );
+}
+
+
+void* GfxDeviceD3D9::GetNativeGfxDevice()
+{
+ return GetD3DDevice();
+}
+
+void* GfxDeviceD3D9::GetNativeTexturePointer(TextureID id)
+{
+ return m_Textures.GetTexture (id);
+}
+
+intptr_t GfxDeviceD3D9::CreateExternalTextureFromNative(intptr_t nativeTex)
+{
+ return m_Textures.RegisterNativeTexture((IDirect3DBaseTexture9*)nativeTex);
+}
+
+void GfxDeviceD3D9::UpdateExternalTextureFromNative(TextureID tex, intptr_t nativeTex)
+{
+ m_Textures.UpdateNativeTexture(tex, (IDirect3DBaseTexture9*)nativeTex);
+}
+
+
+#if ENABLE_PROFILER
+
+void GfxDeviceD3D9::BeginProfileEvent (const char* name)
+{
+ if (g_D3D9BeginEventFunc)
+ {
+ wchar_t wideName[100];
+ UTF8ToWide (name, wideName, 100);
+ g_D3D9BeginEventFunc (0, wideName);
+ }
+}
+
+void GfxDeviceD3D9::EndProfileEvent ()
+{
+ if (g_D3D9EndEventFunc)
+ {
+ g_D3D9EndEventFunc ();
+ }
+}
+
+GfxTimerQuery* GfxDeviceD3D9::CreateTimerQuery()
+{
+ Assert(gGraphicsCaps.hasTimerQuery);
+ return m_TimerQueriesD3D9.CreateTimerQuery();
+}
+
+void GfxDeviceD3D9::DeleteTimerQuery(GfxTimerQuery* query)
+{
+ delete query;
+}
+
+void GfxDeviceD3D9::BeginTimerQueries()
+{
+ if(!gGraphicsCaps.hasTimerQuery)
+ return;
+
+ m_TimerQueriesD3D9.BeginTimerQueries();
+}
+
+void GfxDeviceD3D9::EndTimerQueries()
+{
+ if(!gGraphicsCaps.hasTimerQuery)
+ return;
+
+ m_TimerQueriesD3D9.EndTimerQueries();
+}
+
+/*
+SInt32 GfxDeviceD3D9::GetTimerQueryIdentifier()
+{
+ if(!gGraphicsCaps.hasTimerQuery)
+ return -1;
+ // Allocate more queries
+ if(m_QueryCount[m_CurrentQueryBuffer] >= m_GPUQueries[m_CurrentQueryBuffer].size())
+ {
+ int count = std::max (m_QueryCount[m_CurrentQueryBuffer], 100);
+ IDirect3DQuery9* d3dQuery;
+ for( int i = 0; i < count; i++)
+ {
+ GetD3DDevice()->CreateQuery(D3DQUERYTYPE_TIMESTAMP, &d3dQuery);
+ // initialze more Query objects
+ m_GPUQueries[m_CurrentQueryBuffer].push_back(d3dQuery);
+ }
+ }
+ int index = m_QueryCount[m_CurrentQueryBuffer]++;
+ IDirect3DQuery9* currentQuery = m_GPUQueries[m_CurrentQueryBuffer][index];
+ currentQuery ->Issue(D3DISSUE_END);
+ return index;
+}
+
+ProfileTimeFormat GfxDeviceD3D9::GetTimerQueryData(SInt32 identifier, bool wait)
+{
+ if(!gGraphicsCaps.hasTimerQuery)
+ return 0;
+
+ if(m_GPUQueries[m_CurrentQueryBuffer].size()<=identifier)
+ return 0;
+
+ UINT64 time;
+ while (S_OK != m_GPUQueries[m_CurrentQueryBuffer][identifier]->GetData(&time, sizeof(time), D3DGETDATA_FLUSH)) {}
+ return (double)time * m_TimeMultiplier;
+}
+
+void GfxDeviceD3D9::CleanupTimerQueries ()
+{
+ if(!gGraphicsCaps.hasTimerQuery)
+ return;
+
+ for(int buffer = 0; buffer < 2; buffer++)
+ {
+ for(int i = 0; i < m_GPUQueries[buffer].size(); i++)
+ m_GPUQueries[buffer][i]->Release();
+ m_GPUQueries[buffer].clear();
+ if(m_FrequencyQuery[buffer])
+ m_FrequencyQuery[buffer]->Release();
+ m_FrequencyQuery[buffer] = NULL;
+ m_QueryCount[buffer] = 0;
+ }
+}
+*/
+
+#endif // ENABLE_PROFILER
+
+
+// -------- editor only functions
+
+#if UNITY_EDITOR
+void GfxDeviceD3D9::SetAntiAliasFlag( bool aa )
+{
+ #pragma message("! implement SetAntiAliasFlag")
+}
+
+
+void GfxDeviceD3D9::DrawUserPrimitives( GfxPrimitiveType type, int vertexCount, UInt32 vertexChannels, const void* data, int stride )
+{
+ if( vertexCount == 0 )
+ return;
+
+ AssertIf(vertexCount > 60000); // TODO: handle this by multi-batching
+
+ AssertIf( !data || vertexCount < 0 || vertexChannels == 0 );
+
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ IDirect3DVertexDeclaration9* vertexDecl = GetD3DVertexDeclaration( vertexChannels );
+
+ ChannelAssigns channels;
+ for( int i = 0; i < kShaderChannelCount; ++i )
+ {
+ if( !( vertexChannels & (1<<i) ) )
+ continue;
+ VertexComponent destComponent = kSuitableVertexComponentForChannel[i];
+ channels.Bind( (ShaderChannel)i, destComponent );
+ }
+ D3D9_CALL(dev->SetVertexDeclaration( vertexDecl ));
+ UpdateChannelBindingsD3D( channels );
+ BeforeDrawCall(false);
+
+ HRESULT hr;
+ switch( type ) {
+ case kPrimitiveTriangles:
+ hr = D3D9_CALL_HR(dev->DrawPrimitiveUP( D3DPT_TRIANGLELIST, vertexCount/3, data, stride ));
+ m_Stats.AddDrawCall( vertexCount / 3, vertexCount );
+ break;
+ case kPrimitiveQuads:
+ while (vertexCount > 0)
+ {
+ int vcount = std::min(vertexCount,kMaxImmediateVerticesPerDraw);
+ hr = D3D9_CALL_HR(dev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, vcount, vcount / 4 * 2, m_Imm.m_QuadsIB, D3DFMT_INDEX16, data, stride));
+ m_Stats.AddDrawCall(vcount / 4 * 2, vcount);
+ data = (const UInt8*)data + vcount * stride;
+ vertexCount -= vcount;
+ }
+ break;
+ case kPrimitiveLines:
+ hr = D3D9_CALL_HR(dev->DrawPrimitiveUP( D3DPT_LINELIST, vertexCount/2, data, stride ));
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ case kPrimitiveLineStrip:
+ hr = D3D9_CALL_HR(dev->DrawPrimitiveUP( D3DPT_LINESTRIP, vertexCount-1, data, stride ));
+ m_Stats.AddDrawCall( vertexCount-1, vertexCount );
+ break;
+ default:
+ ErrorString("Primitive type not supported");
+ return;
+ }
+ Assert(SUCCEEDED(hr));
+}
+
+int GfxDeviceD3D9::GetCurrentTargetAA() const
+{
+ return GetCurrentD3DFSAALevel();
+}
+
+GfxDeviceWindow* GfxDeviceD3D9::CreateGfxWindow( HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias )
+{
+ return new D3D9Window( GetD3DDevice(), window, width, height, depthFormat, antiAlias);
+}
+
+#endif
+
+int GfxDeviceD3D9::GetCurrentTargetWidth() const
+{
+ return m_CurrTargetWidth;
+}
+
+int GfxDeviceD3D9::GetCurrentTargetHeight() const
+{
+ return m_CurrTargetHeight;
+}
+
+void GfxDeviceD3D9::SetCurrentTargetSize(int width, int height)
+{
+ m_CurrTargetWidth = width;
+ m_CurrTargetHeight = height;
+}
+
+void GfxDeviceD3D9::SetCurrentWindowSize(int width, int height)
+{
+ m_CurrWindowWidth = m_CurrTargetWidth = width;
+ m_CurrWindowHeight = m_CurrTargetHeight = height;
+}
+
+
+#if UNITY_EDITOR
+
+static IDirect3DTexture9* FindD3D9TextureByID (TextureID tid)
+{
+ GfxDevice& device = GetRealGfxDevice();
+ if (device.GetRenderer() != kGfxRendererD3D9)
+ return NULL;
+ GfxDeviceD3D9& dev = static_cast<GfxDeviceD3D9&>(device);
+ IDirect3DBaseTexture9* basetex = dev.GetTextures().GetTexture (tid);
+ if (!basetex)
+ return NULL;
+ if (basetex->GetType() != D3DRTYPE_TEXTURE)
+ return NULL;
+ return static_cast<IDirect3DTexture9*>(basetex);
+}
+
+// In the editor, for drawing directly into HDC of D3D texture.
+// Functions not defined in any header; declare prototypes manually:
+// HDC AcquireHDCForTextureD3D9 (TextureID tid, int& outWidth, int& outHeight);
+// void ReleaseHDCForTextureD3D9 (TextureID tid, HDC dc);
+// AcquireHDCForTextureD3D9 _can_ return NULL if it can't get to DC (not D3D9, no
+// texture, wrong texture format, ...).
+
+HDC AcquireHDCForTextureD3D9 (TextureID tid, int& outWidth, int& outHeight)
+{
+ IDirect3DTexture9* tex = FindD3D9TextureByID (tid);
+ if (!tex)
+ return NULL;
+ SurfacePointer surface;
+ if (FAILED(tex->GetSurfaceLevel(0,&surface)))
+ return NULL;
+ D3DSURFACE_DESC desc;
+ if (FAILED(surface->GetDesc (&desc)))
+ return NULL;
+ outWidth = desc.Width;
+ outHeight = desc.Height;
+ HDC dc = NULL;
+ if (FAILED(surface->GetDC(&dc)))
+ return NULL;
+ return dc;
+}
+
+void ReleaseHDCForTextureD3D9 (TextureID tid, HDC dc)
+{
+ IDirect3DTexture9* tex = FindD3D9TextureByID (tid);
+ if (!tex)
+ return;
+ SurfacePointer surface;
+ if (FAILED(tex->GetSurfaceLevel(0,&surface)))
+ return;
+ surface->ReleaseDC (dc);
+}
+
+#endif
+
+
+// ----------------------------------------------------------------------
+// verification of state
+
+#if GFX_DEVICE_VERIFY_ENABLE
+
+#include "Runtime/Utilities/Utility.h"
+
+void VerifyStateF(D3DRENDERSTATETYPE rs, float val, const char *str);
+#define VERIFYF(s,t) VerifyState (s, t, #s " (" #t ")")
+void VerifyStateI(D3DRENDERSTATETYPE rs, int val, const char *str);
+#define VERIFYI(s,t) VerifyStateI (s, t, #s " (" #t ")")
+void VerifyEnabled(D3DRENDERSTATETYPE rs, bool val, const char *str);
+#define VERIFYENAB(s,t) VerifyEnabled ( s, t, #s " (" #t ")")
+
+static void VERIFY_PRINT( const char* format, ... )
+{
+ ErrorString( VFormat( format, va_list(&format + 1) ) );
+}
+
+const float kVerifyDelta = 0.0001f;
+
+void VerifyStateF(D3DRENDERSTATETYPE rs, float val, const char *str)
+{
+ float temp = 0;
+ GetD3DDevice()->GetRenderState(rs,(DWORD*)&temp);
+ if( !CompareApproximately(temp,val,kVerifyDelta) ) {
+ VERIFY_PRINT ("%s differs from cache (%f != %f)\n", str, val, temp);
+ }
+}
+
+void VerifyStateI(D3DRENDERSTATETYPE rs, int val, const char *str)
+{
+ int temp;
+ GetD3DDevice()->GetRenderState(rs,(DWORD*)&temp);
+ if (temp != val) {
+ VERIFY_PRINT ("%s differs from cache (%i != %i)\n", str, val, temp);
+ }
+}
+
+void VerifyEnabled(D3DRENDERSTATETYPE rs, bool val, const char *str)
+{
+ DWORD v;
+ GetD3DDevice()->GetRenderState(rs,&v);
+ bool temp = v==TRUE ? true : false;
+ if (temp != val) {
+ VERIFY_PRINT ("%s differs from cache (%d != %d)\n", str, val, temp);
+ }
+}
+
+void GfxDeviceD3D9::VerifyState()
+{
+ // check if current state blocks match internal state
+ if (m_CurrBlendState != NULL) {
+ if (m_State.blending == 0) {
+ Assert (D3DBLEND_ONE == kBlendModeD3D9[m_CurrBlendState->sourceState.srcBlend]);
+ Assert (D3DBLEND_ZERO == kBlendModeD3D9[m_CurrBlendState->sourceState.dstBlend]);
+ } else {
+ Assert (m_State.srcBlend == kBlendModeD3D9[m_CurrBlendState->sourceState.srcBlend]);
+ Assert (m_State.destBlend == kBlendModeD3D9[m_CurrBlendState->sourceState.dstBlend]);
+ }
+ #if !UNITY_EDITOR // Editor does some funkiness when emulating alpha test, see SetBlendState
+ Assert (kCmpFuncD3D9[m_State.alphaFunc] == m_CurrBlendState->alphaFunc);
+ #endif
+ }
+
+ m_State.Verify();
+}
+
+
+
+void DeviceStateD3D::Verify()
+{
+ #ifdef DUMMY_D3D9_CALLS
+ return;
+ #endif
+ if( !GetD3DDevice() ) {
+ ErrorString("Verify: no D3D device");
+ return;
+ }
+
+ if( depthFunc != kFuncUnknown ) {
+ VERIFYI( D3DRS_ZFUNC, kCmpFuncD3D9[depthFunc] );
+ }
+ if( depthWrite != -1 ) {
+ VERIFYI( D3DRS_ZWRITEENABLE, (depthWrite ? TRUE : FALSE) );
+ }
+ if( blending != -1 ) {
+ VERIFYENAB( D3DRS_ALPHABLENDENABLE, blending != 0 );
+ if( blending ) {
+ VERIFYI( D3DRS_SRCBLEND, srcBlend );
+ VERIFYI( D3DRS_DESTBLEND, destBlend );
+ }
+ }
+
+ if( alphaFunc != kFuncUnknown ) {
+ VERIFYENAB( D3DRS_ALPHATESTENABLE, alphaFunc != kFuncDisabled );
+ if( alphaFunc != kFuncDisabled ) {
+ VERIFYI( D3DRS_ALPHAFUNC, kCmpFuncD3D9[alphaFunc] );
+ if( alphaValue != -1 )
+ VERIFYI( D3DRS_ALPHAREF, alphaValue*255.0f );
+ }
+ }
+}
+
+#endif // GFX_DEVICE_VERIFY_ENABLE
+