diff options
Diffstat (limited to 'Runtime/GfxDevice/d3d/GfxDeviceD3D9.cpp')
-rw-r--r-- | Runtime/GfxDevice/d3d/GfxDeviceD3D9.cpp | 3009 |
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] = ¶ms[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] = ¶ms[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, ¤tRenderTarget ); + 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 + |