Torque3D/Engine/source/gfx/D3D9/gfxD3D9Device.cpp
Areloch 1ed8b05169 Initial implementation of the new Base Game Template and some starting modules.
This makes some tweaks to the engine to support this, specifically, it tweaks the hardcoded shaderpaths to defer to a pref variable, so none of the shader paths are hardcoded.

Also tweaks how post effects read in texture files, removing a bizzare filepath interpretation choice, where if the file path didn't start with "/" it forcefully appended the script's file path. This made it impossible to have images not in the same dir as the script file defining the post effect.

This was changed and the existing template's post effects tweaked for now to just add "./" to those few paths impacted, as well as the perf vars to support the non-hardcoded shader paths in the engine.
2017-02-24 02:40:56 -06:00

1214 lines
39 KiB
C++

//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "gfx/D3D9/gfxD3D9Device.h"
#include "console/console.h"
#include "core/stream/fileStream.h"
#include "core/strings/unicode.h"
#include "gfx/D3D9/gfxD3D9CardProfiler.h"
#include "gfx/D3D9/gfxD3D9VertexBuffer.h"
#include "gfx/D3D9/screenShotD3D9.h"
#include "gfx/D3D9/gfxD3D9EnumTranslate.h"
#include "gfx/D3D9/gfxD3D9QueryFence.h"
#include "gfx/D3D9/gfxD3D9OcclusionQuery.h"
#include "gfx/D3D9/gfxD3D9Shader.h"
#include "windowManager/platformWindow.h"
#include "materials/shaderData.h"
#ifndef TORQUE_OS_XENON
# include "windowManager/win32/win32Window.h"
#endif
D3DXFNTable GFXD3D9Device::smD3DX;
GFXD3D9Device::GFXD3D9Device( LPDIRECT3D9 d3d, U32 index )
{
mDeviceSwizzle32 = &Swizzles::bgra;
GFXVertexColor::setSwizzle( mDeviceSwizzle32 );
mDeviceSwizzle24 = &Swizzles::bgr;
mD3D = d3d;
mAdapterIndex = index;
mD3DDevice = NULL;
mVolatileVB = NULL;
mCurrentPB = NULL;
mDynamicPB = NULL;
mLastVertShader = NULL;
mLastPixShader = NULL;
mCanCurrentlyRender = false;
mTextureManager = NULL;
mCurrentStateBlock = NULL;
mResourceListHead = NULL;
#ifdef TORQUE_DEBUG
mVBListHead = NULL;
mNumAllocatedVertexBuffers = 0;
#endif
mPixVersion = 0.0;
mNumSamplers = 0;
mNumRenderTargets = 0;
mCardProfiler = NULL;
mDeviceDepthStencil = NULL;
mDeviceBackbuffer = NULL;
mDeviceColor = NULL;
mCreateFenceType = -1; // Unknown, test on first allocate
mCurrentConstBuffer = NULL;
mOcclusionQuerySupported = false;
for (U32 i = 0; i < GS_COUNT; ++i)
mModelViewProjSC[i] = NULL;
// Set up the Enum translation tables
GFXD3D9EnumTranslate::init();
#if !defined(TORQUE_OS_XENON)
mD3DEx = NULL;
mD3DDeviceEx = NULL;
#endif
}
//-----------------------------------------------------------------------------
GFXD3D9Device::~GFXD3D9Device()
{
// Release our refcount on the current stateblock object
mCurrentStateBlock = NULL;
releaseDefaultPoolResources();
// Free the vertex declarations.
VertexDeclMap::Iterator iter = mVertexDecls.begin();
for ( ; iter != mVertexDecls.end(); iter++ )
delete iter->value;
// Check up on things
Con::printf("Cur. D3DDevice ref count=%d", mD3DDevice->AddRef() - 1);
mD3DDevice->Release();
// Forcibly clean up the pools
mVolatileVBList.setSize(0);
mDynamicPB = NULL;
// And release our D3D resources.
SAFE_RELEASE( mDeviceDepthStencil );
SAFE_RELEASE( mDeviceBackbuffer )
SAFE_RELEASE( mDeviceColor );
SAFE_RELEASE( mD3D );
SAFE_RELEASE( mD3DDevice );
#ifdef TORQUE_DEBUG
logVertexBuffers();
#endif
if( mCardProfiler )
{
delete mCardProfiler;
mCardProfiler = NULL;
}
if( gScreenShot )
{
delete gScreenShot;
gScreenShot = NULL;
}
}
//------------------------------------------------------------------------------
// setupGenericShaders
//------------------------------------------------------------------------------
inline void GFXD3D9Device::setupGenericShaders( GenericShaderType type /* = GSColor */ )
{
AssertFatal(type != GSTargetRestore, ""); //not used
if (mGenericShader[GSColor] == NULL)
{
ShaderData *shaderData;
shaderData = new ShaderData();
shaderData->setField("DXVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/colorV.hlsl"));
shaderData->setField("DXPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/colorP.hlsl"));
shaderData->setField("pixVersion", "3.0");
shaderData->registerObject();
mGenericShader[GSColor] = shaderData->getShader();
mGenericShaderBuffer[GSColor] = mGenericShader[GSColor]->allocConstBuffer();
mModelViewProjSC[GSColor] = mGenericShader[GSColor]->getShaderConstHandle("$modelView");
Sim::getRootGroup()->addObject(shaderData);
shaderData = new ShaderData();
shaderData->setField("DXVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/modColorTextureV.hlsl"));
shaderData->setField("DXPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/modColorTextureP.hlsl"));
shaderData->setSamplerName("$diffuseMap", 0);
shaderData->setField("pixVersion", "3.0");
shaderData->registerObject();
mGenericShader[GSModColorTexture] = shaderData->getShader();
mGenericShaderBuffer[GSModColorTexture] = mGenericShader[GSModColorTexture]->allocConstBuffer();
mModelViewProjSC[GSModColorTexture] = mGenericShader[GSModColorTexture]->getShaderConstHandle("$modelView");
Sim::getRootGroup()->addObject(shaderData);
shaderData = new ShaderData();
shaderData->setField("DXVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/addColorTextureV.hlsl"));
shaderData->setField("DXPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/addColorTextureP.hlsl"));
shaderData->setSamplerName("$diffuseMap", 0);
shaderData->setField("pixVersion", "3.0");
shaderData->registerObject();
mGenericShader[GSAddColorTexture] = shaderData->getShader();
mGenericShaderBuffer[GSAddColorTexture] = mGenericShader[GSAddColorTexture]->allocConstBuffer();
mModelViewProjSC[GSAddColorTexture] = mGenericShader[GSAddColorTexture]->getShaderConstHandle("$modelView");
Sim::getRootGroup()->addObject(shaderData);
shaderData = new ShaderData();
shaderData->setField("DXVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/textureV.hlsl"));
shaderData->setField("DXPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/textureP.hlsl"));
shaderData->setSamplerName("$diffuseMap", 0);
shaderData->setField("pixVersion", "3.0");
shaderData->registerObject();
mGenericShader[GSTexture] = shaderData->getShader();
mGenericShaderBuffer[GSTexture] = mGenericShader[GSTexture]->allocConstBuffer();
mModelViewProjSC[GSTexture] = mGenericShader[GSTexture]->getShaderConstHandle("$modelView");
Sim::getRootGroup()->addObject(shaderData);
//Force an update
mViewportDirty = true;
}
MatrixF tempMatrix = mProjectionMatrix * mViewMatrix * mWorldMatrix[mWorldStackSize];
mGenericShaderBuffer[type]->setSafe(mModelViewProjSC[type], tempMatrix);
setShader(mGenericShader[type]);
setShaderConstBuffer(mGenericShaderBuffer[type]);
}
//-----------------------------------------------------------------------------
/// Creates a state block object based on the desc passed in. This object
/// represents an immutable state.
GFXStateBlockRef GFXD3D9Device::createStateBlockInternal(const GFXStateBlockDesc& desc)
{
return GFXStateBlockRef(new GFXD3D9StateBlock(desc, mD3DDevice));
}
/// Activates a stateblock
void GFXD3D9Device::setStateBlockInternal(GFXStateBlock* block, bool force)
{
AssertFatal(dynamic_cast<GFXD3D9StateBlock*>(block), "Incorrect stateblock type for this device!");
GFXD3D9StateBlock* d3dBlock = static_cast<GFXD3D9StateBlock*>(block);
GFXD3D9StateBlock* d3dCurrent = static_cast<GFXD3D9StateBlock*>(mCurrentStateBlock.getPointer());
if (force)
d3dCurrent = NULL;
d3dBlock->activate(d3dCurrent);
}
/// Called by base GFXDevice to actually set a const buffer
void GFXD3D9Device::setShaderConstBufferInternal(GFXShaderConstBuffer* buffer)
{
if (buffer)
{
PROFILE_SCOPE(GFXD3D9Device_setShaderConstBufferInternal);
AssertFatal(dynamic_cast<GFXD3D9ShaderConstBuffer*>(buffer), "Incorrect shader const buffer type for this device!");
GFXD3D9ShaderConstBuffer* d3dBuffer = static_cast<GFXD3D9ShaderConstBuffer*>(buffer);
d3dBuffer->activate(mCurrentConstBuffer);
mCurrentConstBuffer = d3dBuffer;
} else {
mCurrentConstBuffer = NULL;
}
}
//-----------------------------------------------------------------------------
void GFXD3D9Device::clear( U32 flags, ColorI color, F32 z, U32 stencil )
{
// Make sure we have flushed our render target state.
_updateRenderTargets();
// Kind of a bummer we have to do this, there should be a better way made
DWORD realflags = 0;
if( flags & GFXClearTarget )
realflags |= D3DCLEAR_TARGET;
if( flags & GFXClearZBuffer )
realflags |= D3DCLEAR_ZBUFFER;
if( flags & GFXClearStencil )
realflags |= D3DCLEAR_STENCIL;
mD3DDevice->Clear( 0, NULL, realflags,
D3DCOLOR_ARGB( color.alpha, color.red, color.green, color.blue ),
z, stencil );
}
//-----------------------------------------------------------------------------
bool GFXD3D9Device::beginSceneInternal()
{
HRESULT hr = mD3DDevice->BeginScene();
D3D9Assert(hr, "GFXD3D9Device::beginSceneInternal - failed to BeginScene");
mCanCurrentlyRender = SUCCEEDED(hr);
return mCanCurrentlyRender;
}
//-----------------------------------------------------------------------------
void GFXD3D9Device::endSceneInternal()
{
mD3DDevice->EndScene();
mCanCurrentlyRender = false;
}
void GFXD3D9Device::_updateRenderTargets()
{
if ( mRTDirty || ( mCurrentRT && mCurrentRT->isPendingState() ) )
{
if ( mRTDeactivate )
{
mRTDeactivate->deactivate();
mRTDeactivate = NULL;
}
// NOTE: The render target changes are not really accurate
// as the GFXTextureTarget supports MRT internally. So when
// we activate a GFXTarget it could result in multiple calls
// to SetRenderTarget on the actual device.
mDeviceStatistics.mRenderTargetChanges++;
mCurrentRT->activate();
mRTDirty = false;
}
if ( mViewportDirty )
{
D3DVIEWPORT9 viewport;
viewport.X = mViewport.point.x;
viewport.Y = mViewport.point.y;
viewport.Width = mViewport.extent.x;
viewport.Height = mViewport.extent.y;
viewport.MinZ = 0.0;
viewport.MaxZ = 1.0;
D3D9Assert( mD3DDevice->SetViewport( &viewport ),
"GFXD3D9Device::_updateRenderTargets() - Error setting viewport!" );
mViewportDirty = false;
}
}
#ifdef TORQUE_DEBUG
void GFXD3D9Device::logVertexBuffers()
{
// NOTE: This function should be called on the destructor of this class and ONLY then
// otherwise it'll produce the wrong output
if( mNumAllocatedVertexBuffers == 0 )
return;
FileStream fs;
fs.open( "vertexbuffer.log", Torque::FS::File::Write );
char buff[256];
fs.writeLine( (U8 *)avar("-- Vertex buffer memory leak report -- time = %d", Platform::getRealMilliseconds()) );
dSprintf( (char *)&buff, sizeof( buff ), "%d un-freed vertex buffers", mNumAllocatedVertexBuffers );
fs.writeLine( (U8 *)buff );
GFXD3D9VertexBuffer *walk = mVBListHead;
while( walk != NULL )
{
dSprintf( (char *)&buff, sizeof( buff ), "[Name: %s] Size: %d", walk->name, walk->mNumVerts );
fs.writeLine( (U8 *)buff );
walk = walk->next;
}
fs.writeLine( (U8 *)"-- End report --" );
fs.close();
}
//-----------------------------------------------------------------------------
void GFXD3D9Device::addVertexBuffer( GFXD3D9VertexBuffer *buffer )
{
mNumAllocatedVertexBuffers++;
if( mVBListHead == NULL )
{
mVBListHead = buffer;
}
else
{
GFXD3D9VertexBuffer *walk = mVBListHead;
while( walk->next != NULL )
{
walk = walk->next;
}
walk->next = buffer;
}
buffer->next = NULL;
}
//-----------------------------------------------------------------------------
void GFXD3D9Device::removeVertexBuffer( GFXD3D9VertexBuffer *buffer )
{
mNumAllocatedVertexBuffers--;
// Quick check to see if this is head of list
if( mVBListHead == buffer )
{
mVBListHead = mVBListHead->next;
return;
}
GFXD3D9VertexBuffer *walk = mVBListHead;
while( walk->next != NULL )
{
if( walk->next == buffer )
{
walk->next = walk->next->next;
return;
}
walk = walk->next;
}
AssertFatal( false, "Vertex buffer not found in list." );
}
#endif
//-----------------------------------------------------------------------------
void GFXD3D9Device::releaseDefaultPoolResources()
{
// Release all the dynamic vertex buffer arrays
// Forcibly clean up the pools
for( U32 i=0; i<mVolatileVBList.size(); i++ )
{
// Con::printf("Trying to release volatile vb with COM refcount of %d and internal refcount of %d", mVolatileVBList[i]->vb->AddRef() - 1, mVolatileVBList[i]->mRefCount);
// mVolatileVBList[i]->vb->Release();
mVolatileVBList[i]->vb->Release();
mVolatileVBList[i]->vb = NULL;
mVolatileVBList[i] = NULL;
}
mVolatileVBList.setSize(0);
// We gotta clear the current const buffer else the next
// activate may erroneously think the device is still holding
// this state and fail to set it.
mCurrentConstBuffer = NULL;
// Set current VB to NULL and set state dirty
for ( U32 i=0; i < VERTEX_STREAM_COUNT; i++ )
{
mCurrentVertexBuffer[i] = NULL;
mVertexBufferDirty[i] = true;
mVertexBufferFrequency[i] = 0;
mVertexBufferFrequencyDirty[i] = true;
}
// Release dynamic index buffer
if( mDynamicPB != NULL )
{
SAFE_RELEASE( mDynamicPB->ib );
}
// Set current PB/IB to NULL and set state dirty
mCurrentPrimitiveBuffer = NULL;
mCurrentPB = NULL;
mPrimitiveBufferDirty = true;
// Zombify texture manager (for D3D this only modifies default pool textures)
if( mTextureManager )
mTextureManager->zombify();
// Kill off other potentially dangling references...
SAFE_RELEASE( mDeviceDepthStencil );
SAFE_RELEASE( mDeviceBackbuffer );
mD3DDevice->SetDepthStencilSurface(NULL);
// Set global dirty state so the IB/PB and VB get reset
mStateDirty = true;
// Walk the resource list and zombify everything.
GFXResource *walk = mResourceListHead;
while(walk)
{
walk->zombify();
walk = walk->getNextResource();
}
}
//-----------------------------------------------------------------------------
void GFXD3D9Device::reacquireDefaultPoolResources()
{
// Now do the dynamic index buffers
if( mDynamicPB == NULL )
mDynamicPB = new GFXD3D9PrimitiveBuffer(this, 0, 0, GFXBufferTypeDynamic);
D3D9Assert( mD3DDevice->CreateIndexBuffer( sizeof( U16 ) * MAX_DYNAMIC_INDICES,
#ifdef TORQUE_OS_XENON
D3DUSAGE_WRITEONLY,
#else
D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC,
#endif
GFXD3D9IndexFormat[GFXIndexFormat16], D3DPOOL_DEFAULT, &mDynamicPB->ib, NULL ), "Failed to allocate dynamic IB" );
// Grab the depth-stencil...
SAFE_RELEASE(mDeviceDepthStencil);
D3D9Assert(mD3DDevice->GetDepthStencilSurface(&mDeviceDepthStencil),
"GFXD3D9Device::reacquireDefaultPoolResources - couldn't grab reference to device's depth-stencil surface.");
SAFE_RELEASE(mDeviceBackbuffer);
mD3DDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &mDeviceBackbuffer );
// Store for query later.
mD3DDevice->GetDisplayMode( 0, &mDisplayMode );
// Walk the resource list and zombify everything.
GFXResource *walk = mResourceListHead;
while(walk)
{
walk->resurrect();
walk = walk->getNextResource();
}
if(mTextureManager)
mTextureManager->resurrect();
}
GFXD3D9VertexBuffer* GFXD3D9Device::findVBPool( const GFXVertexFormat *vertexFormat, U32 vertsNeeded )
{
PROFILE_SCOPE( GFXD3D9Device_findVBPool );
// Verts needed is ignored on the base device, 360 is different
for( U32 i=0; i<mVolatileVBList.size(); i++ )
if( mVolatileVBList[i]->mVertexFormat.isEqual( *vertexFormat ) )
return mVolatileVBList[i];
return NULL;
}
GFXD3D9VertexBuffer * GFXD3D9Device::createVBPool( const GFXVertexFormat *vertexFormat, U32 vertSize )
{
PROFILE_SCOPE( GFXD3D9Device_createVBPool );
// this is a bit funky, but it will avoid problems with (lack of) copy constructors
// with a push_back() situation
mVolatileVBList.increment();
StrongRefPtr<GFXD3D9VertexBuffer> newBuff;
mVolatileVBList.last() = new GFXD3D9VertexBuffer();
newBuff = mVolatileVBList.last();
newBuff->mNumVerts = 0;
newBuff->mBufferType = GFXBufferTypeVolatile;
newBuff->mVertexFormat.copy( *vertexFormat );
newBuff->mVertexSize = vertSize;
newBuff->mDevice = this;
// Requesting it will allocate it.
vertexFormat->getDecl();
// Con::printf("Created buff with type %x", vertFlags);
D3D9Assert( mD3DDevice->CreateVertexBuffer( vertSize * MAX_DYNAMIC_VERTS,
#ifdef TORQUE_OS_XENON
D3DUSAGE_WRITEONLY,
#else
D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC,
#endif
0,
D3DPOOL_DEFAULT,
&newBuff->vb,
NULL ),
"Failed to allocate dynamic VB" );
return newBuff;
}
//-----------------------------------------------------------------------------
void GFXD3D9Device::setClipRect( const RectI &inRect )
{
// We transform the incoming rect by the view
// matrix first, so that it can be used to pan
// and scale the clip rect.
//
// This is currently used to take tiled screenshots.
Point3F pos( inRect.point.x, inRect.point.y, 0.0f );
Point3F extent( inRect.extent.x, inRect.extent.y, 0.0f );
getViewMatrix().mulP( pos );
getViewMatrix().mulV( extent );
RectI rect( pos.x, pos.y, extent.x, extent.y );
// Clip the rect against the renderable size.
Point2I size = mCurrentRT->getSize();
RectI maxRect(Point2I(0,0), size);
rect.intersect(maxRect);
mClipRect = rect;
F32 l = F32( mClipRect.point.x );
F32 r = F32( mClipRect.point.x + mClipRect.extent.x );
F32 b = F32( mClipRect.point.y + mClipRect.extent.y );
F32 t = F32( mClipRect.point.y );
// Set up projection matrix,
static Point4F pt;
pt.set(2.0f / (r - l), 0.0f, 0.0f, 0.0f);
mTempMatrix.setColumn(0, pt);
pt.set(0.0f, 2.0f/(t - b), 0.0f, 0.0f);
mTempMatrix.setColumn(1, pt);
pt.set(0.0f, 0.0f, 1.0f, 0.0f);
mTempMatrix.setColumn(2, pt);
pt.set((l+r)/(l-r), (t+b)/(b-t), 1.0f, 1.0f);
mTempMatrix.setColumn(3, pt);
setProjectionMatrix( mTempMatrix );
// Set up world/view matrix
mTempMatrix.identity();
setWorldMatrix( mTempMatrix );
setViewport( mClipRect );
}
void GFXD3D9Device::setVertexStream( U32 stream, GFXVertexBuffer *buffer )
{
GFXD3D9VertexBuffer *d3dBuffer = static_cast<GFXD3D9VertexBuffer*>( buffer );
if ( stream == 0 )
{
// Set the volatile buffer which is used to
// offset the start index when doing draw calls.
if ( d3dBuffer && d3dBuffer->mVolatileStart > 0 )
mVolatileVB = d3dBuffer;
else
mVolatileVB = NULL;
}
U32 offset = d3dBuffer && stream != 0 ? d3dBuffer->mVolatileStart * d3dBuffer->mVertexSize : 0;
// NOTE: We do not use the stream offset here for stream 0
// as that feature is *supposedly* not as well supported as
// using the start index in drawPrimitive.
//
// If we can verify that this is not the case then we should
// start using this method exclusively for all streams.
D3D9Assert( mD3DDevice->SetStreamSource( stream,
d3dBuffer ? d3dBuffer->vb : NULL,
offset,
d3dBuffer ? d3dBuffer->mVertexSize : 0 ),
"GFXD3D9Device::setVertexStream - Failed to set stream source." );
}
void GFXD3D9Device::setVertexStreamFrequency( U32 stream, U32 frequency )
{
if ( frequency == 0 )
frequency = 1;
else
{
if ( stream == 0 )
frequency = D3DSTREAMSOURCE_INDEXEDDATA | frequency;
else
frequency = D3DSTREAMSOURCE_INSTANCEDATA | frequency;
}
D3D9Assert( mD3DDevice->SetStreamSourceFreq( stream, frequency ),
"GFXD3D9Device::setVertexStreamFrequency - Failed to set stream frequency." );
}
void GFXD3D9Device::_setPrimitiveBuffer( GFXPrimitiveBuffer *buffer )
{
mCurrentPB = static_cast<GFXD3D9PrimitiveBuffer *>( buffer );
D3D9Assert( mD3DDevice->SetIndices( mCurrentPB->ib ), "Failed to set indices" );
}
void GFXD3D9Device::drawPrimitive( GFXPrimitiveType primType, U32 vertexStart, U32 primitiveCount )
{
// This is done to avoid the function call overhead if possible
if( mStateDirty )
updateStates();
if (mCurrentShaderConstBuffer)
setShaderConstBufferInternal(mCurrentShaderConstBuffer);
if ( mVolatileVB )
vertexStart += mVolatileVB->mVolatileStart;
D3D9Assert( mD3DDevice->DrawPrimitive( GFXD3D9PrimType[primType], vertexStart, primitiveCount ), "Failed to draw primitives" );
mDeviceStatistics.mDrawCalls++;
if ( mVertexBufferFrequency[0] > 1 )
mDeviceStatistics.mPolyCount += primitiveCount * mVertexBufferFrequency[0];
else
mDeviceStatistics.mPolyCount += primitiveCount;
}
//-----------------------------------------------------------------------------
void GFXD3D9Device::drawIndexedPrimitive( GFXPrimitiveType primType,
U32 startVertex,
U32 minIndex,
U32 numVerts,
U32 startIndex,
U32 primitiveCount )
{
// This is done to avoid the function call overhead if possible
if( mStateDirty )
updateStates();
if (mCurrentShaderConstBuffer)
setShaderConstBufferInternal(mCurrentShaderConstBuffer);
AssertFatal( mCurrentPB != NULL, "Trying to call drawIndexedPrimitive with no current index buffer, call setIndexBuffer()" );
if ( mVolatileVB )
startVertex += mVolatileVB->mVolatileStart;
D3D9Assert( mD3DDevice->DrawIndexedPrimitive( GFXD3D9PrimType[primType],
startVertex,
/* mCurrentPB->mVolatileStart + */ minIndex,
numVerts,
mCurrentPB->mVolatileStart + startIndex,
primitiveCount ), "Failed to draw indexed primitive" );
mDeviceStatistics.mDrawCalls++;
if ( mVertexBufferFrequency[0] > 1 )
mDeviceStatistics.mPolyCount += primitiveCount * mVertexBufferFrequency[0];
else
mDeviceStatistics.mPolyCount += primitiveCount;
}
GFXShader* GFXD3D9Device::createShader()
{
GFXD3D9Shader* shader = new GFXD3D9Shader();
shader->registerResourceWithDevice( this );
return shader;
}
void GFXD3D9Device::disableShaders(bool force)
{
setShader( NULL, force );
setShaderConstBuffer( NULL );
}
//-----------------------------------------------------------------------------
// Set shader - this function exists to make sure this is done in one place,
// and to make sure redundant shader states are not being
// sent to the card.
//-----------------------------------------------------------------------------
void GFXD3D9Device::setShader( GFXShader *shader, bool force )
{
GFXD3D9Shader *d3dShader = static_cast<GFXD3D9Shader*>( shader );
IDirect3DPixelShader9 *pixShader = ( d3dShader != NULL ? d3dShader->mPixShader : NULL );
IDirect3DVertexShader9 *vertShader = ( d3dShader ? d3dShader->mVertShader : NULL );
if( pixShader != mLastPixShader || force )
{
mD3DDevice->SetPixelShader( pixShader );
mLastPixShader = pixShader;
}
if( vertShader != mLastVertShader || force )
{
mD3DDevice->SetVertexShader( vertShader );
mLastVertShader = vertShader;
}
}
//-----------------------------------------------------------------------------
// allocPrimitiveBuffer
//-----------------------------------------------------------------------------
GFXPrimitiveBuffer * GFXD3D9Device::allocPrimitiveBuffer( U32 numIndices,
U32 numPrimitives,
GFXBufferType bufferType,
void* data )
{
// Allocate a buffer to return
GFXD3D9PrimitiveBuffer * res = new GFXD3D9PrimitiveBuffer(this, numIndices, numPrimitives, bufferType);
// Determine usage flags
U32 usage = 0;
D3DPOOL pool = D3DPOOL_DEFAULT;
// Assumptions:
// - static buffers are write rarely, use many
// - dynamic buffers are write many, use many
// - volatile buffers are write once, use once
// You may never read from a buffer.
switch(bufferType)
{
case GFXBufferTypeImmutable:
case GFXBufferTypeStatic:
pool = isD3D9Ex() ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
break;
case GFXBufferTypeDynamic:
case GFXBufferTypeVolatile:
#ifndef TORQUE_OS_XENON
usage |= D3DUSAGE_DYNAMIC;
#endif
break;
}
// Register resource
res->registerResourceWithDevice(this);
// We never allow reading from a primitive buffer.
usage |= D3DUSAGE_WRITEONLY;
// Create d3d index buffer
if(bufferType == GFXBufferTypeVolatile)
{
// Get it from the pool if it's a volatile...
AssertFatal( numIndices < MAX_DYNAMIC_INDICES, "Cannot allocate that many indices in a volatile buffer, increase MAX_DYNAMIC_INDICES." );
res->ib = mDynamicPB->ib;
// mDynamicPB->ib->AddRef();
res->mVolatileBuffer = mDynamicPB;
}
else
{
// Otherwise, get it as a seperate buffer...
D3D9Assert(mD3DDevice->CreateIndexBuffer( sizeof(U16) * numIndices , usage, GFXD3D9IndexFormat[GFXIndexFormat16], pool, &res->ib, 0),
"Failed to allocate an index buffer.");
}
if(data)
{
void* dest;
res->lock(0, numIndices, &dest);
dMemcpy(dest, data, sizeof(U16) * numIndices);
res->unlock();
}
return res;
}
//-----------------------------------------------------------------------------
// allocVertexBuffer
//-----------------------------------------------------------------------------
GFXVertexBuffer * GFXD3D9Device::allocVertexBuffer( U32 numVerts,
const GFXVertexFormat *vertexFormat,
U32 vertSize,
GFXBufferType bufferType,
void* data)
{
PROFILE_SCOPE( GFXD3D9Device_allocVertexBuffer );
GFXD3D9VertexBuffer *res = new GFXD3D9VertexBuffer( this,
numVerts,
vertexFormat,
vertSize,
bufferType );
// Determine usage flags
U32 usage = 0;
D3DPOOL pool = D3DPOOL_DEFAULT;
res->mNumVerts = 0;
// Assumptions:
// - static buffers are write rarely, use many
// - dynamic buffers are write many, use many
// - volatile buffers are write once, use once
// You may never read from a buffer.
switch(bufferType)
{
case GFXBufferTypeImmutable:
case GFXBufferTypeStatic:
pool = isD3D9Ex() ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
break;
case GFXBufferTypeDynamic:
case GFXBufferTypeVolatile:
pool = D3DPOOL_DEFAULT;
usage |= D3DUSAGE_WRITEONLY;
#ifndef TORQUE_OS_XENON
usage |= D3DUSAGE_DYNAMIC;
#endif
break;
}
res->registerResourceWithDevice(this);
// Create vertex buffer
if( bufferType == GFXBufferTypeVolatile )
{
// NOTE: Volatile VBs are pooled and will be allocated at lock time.
AssertFatal( numVerts <= MAX_DYNAMIC_VERTS,
"GFXD3D9Device::allocVertexBuffer - Volatile vertex buffer is too big... see MAX_DYNAMIC_VERTS!" );
}
else
{
// Requesting it will allocate it.
vertexFormat->getDecl();
// Get a new buffer...
D3D9Assert( mD3DDevice->CreateVertexBuffer( vertSize * numVerts, usage, 0, pool, &res->vb, NULL ),
"Failed to allocate VB" );
}
res->mNumVerts = numVerts;
if(data)
{
void* dest;
res->lock(0, numVerts, &dest);
dMemcpy(dest, data, vertSize * numVerts);
res->unlock();
}
return res;
}
//-----------------------------------------------------------------------------
// deallocate vertex buffer
//-----------------------------------------------------------------------------
void GFXD3D9Device::deallocVertexBuffer( GFXD3D9VertexBuffer *vertBuff )
{
SAFE_RELEASE(vertBuff->vb);
}
GFXVertexDecl* GFXD3D9Device::allocVertexDecl( const GFXVertexFormat *vertexFormat )
{
PROFILE_SCOPE( GFXD3D9Device_allocVertexDecl );
// First check the map... you shouldn't allocate VBs very often
// if you want performance. The map lookup should never become
// a performance bottleneck.
D3D9VertexDecl *decl = mVertexDecls[vertexFormat->getDescription()];
if ( decl )
return decl;
// Setup the declaration struct.
U32 elemCount = vertexFormat->getElementCount();
U32 offsets[4] = { 0 };
U32 stream;
S32 i = 0;
S32 elemIdx = 0;
D3DVERTEXELEMENT9 *vd = new D3DVERTEXELEMENT9[ elemCount + 1 ];
for ( i=0; elemIdx < elemCount; i++, elemIdx++ )
{
const GFXVertexElement &element = vertexFormat->getElement( elemIdx );
stream = element.getStreamIndex();
vd[i].Stream = stream;
vd[i].Offset = offsets[stream];
vd[i].Type = GFXD3D9DeclType[element.getType()];
vd[i].Method = D3DDECLMETHOD_DEFAULT;
// We force the usage index of 0 for everything but
// texture coords for now... this may change later.
vd[i].UsageIndex = 0;
if ( element.isSemantic( GFXSemantic::POSITION ) )
vd[i].Usage = D3DDECLUSAGE_POSITION;
else if ( element.isSemantic( GFXSemantic::NORMAL ) )
vd[i].Usage = D3DDECLUSAGE_NORMAL;
else if ( element.isSemantic( GFXSemantic::COLOR ) )
vd[i].Usage = D3DDECLUSAGE_COLOR;
else if ( element.isSemantic( GFXSemantic::TANGENT ) )
vd[i].Usage = D3DDECLUSAGE_TANGENT;
else if ( element.isSemantic( GFXSemantic::BINORMAL ) )
vd[i].Usage = D3DDECLUSAGE_BINORMAL;
else if ( element.isSemantic( GFXSemantic::BLENDINDICES ) )
{
vd[i].Usage = D3DDECLUSAGE_BLENDINDICES;
vd[i].UsageIndex = element.getSemanticIndex();
}
else if ( element.isSemantic( GFXSemantic::BLENDWEIGHT ) )
{
vd[i].Usage = D3DDECLUSAGE_BLENDWEIGHT;
vd[i].UsageIndex = element.getSemanticIndex();
}
else if ( element.isSemantic( GFXSemantic::PADDING ) )
i--;
else
{
// Anything that falls thru to here will be a texture coord.
vd[i].Usage = D3DDECLUSAGE_TEXCOORD;
vd[i].UsageIndex = element.getSemanticIndex();
}
offsets[stream] += element.getSizeInBytes();
}
D3DVERTEXELEMENT9 declEnd = D3DDECL_END();
vd[i] = declEnd;
decl = new D3D9VertexDecl();
D3D9Assert( mD3DDevice->CreateVertexDeclaration( vd, &decl->decl ),
"GFXD3D9Device::allocVertexDecl - Failed to create vertex declaration!" );
delete [] vd;
// Store it in the cache.
mVertexDecls[vertexFormat->getDescription()] = decl;
return decl;
}
void GFXD3D9Device::setVertexDecl( const GFXVertexDecl *decl )
{
IDirect3DVertexDeclaration9 *dx9Decl = NULL;
if ( decl )
dx9Decl = static_cast<const D3D9VertexDecl*>( decl )->decl;
D3D9Assert( mD3DDevice->SetVertexDeclaration( dx9Decl ), "GFXD3D9Device::setVertexDecl - Failed to set vertex declaration." );
}
//-----------------------------------------------------------------------------
// This function should ONLY be called from GFXDevice::updateStates() !!!
//-----------------------------------------------------------------------------
void GFXD3D9Device::setTextureInternal( U32 textureUnit, const GFXTextureObject *texture)
{
if( texture == NULL )
{
D3D9Assert(mD3DDevice->SetTexture( textureUnit, NULL ), "Failed to set texture to null!");
return;
}
GFXD3D9TextureObject *tex = (GFXD3D9TextureObject *) texture;
D3D9Assert(mD3DDevice->SetTexture( textureUnit, tex->getTex()), "Failed to set texture to valid value!");
}
//-----------------------------------------------------------------------------
// This function should ONLY be called from GFXDevice::updateStates() !!!
//-----------------------------------------------------------------------------
void GFXD3D9Device::setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable)
{
#ifndef TORQUE_OS_XENON
if(!lightEnable)
{
mD3DDevice->LightEnable(lightStage, false);
return;
}
D3DLIGHT9 d3dLight;
switch (light.mType)
{
case GFXLightInfo::Ambient:
AssertFatal(false, "Instead of setting an ambient light you should set the global ambient color.");
return;
case GFXLightInfo::Vector:
d3dLight.Type = D3DLIGHT_DIRECTIONAL;
break;
case GFXLightInfo::Point:
d3dLight.Type = D3DLIGHT_POINT;
break;
case GFXLightInfo::Spot:
d3dLight.Type = D3DLIGHT_SPOT;
break;
default :
AssertFatal(false, "Unknown light type!");
};
dMemcpy(&d3dLight.Diffuse, &light.mColor, sizeof(light.mColor));
dMemcpy(&d3dLight.Ambient, &light.mAmbient, sizeof(light.mAmbient));
dMemcpy(&d3dLight.Specular, &light.mColor, sizeof(light.mColor));
dMemcpy(&d3dLight.Position, &light.mPos, sizeof(light.mPos));
dMemcpy(&d3dLight.Direction, &light.mDirection, sizeof(light.mDirection));
d3dLight.Range = light.mRadius;
d3dLight.Falloff = 1.0;
d3dLight.Attenuation0 = 1.0f;
d3dLight.Attenuation1 = 0.1f;
d3dLight.Attenuation2 = 0.0f;
d3dLight.Theta = light.mInnerConeAngle;
d3dLight.Phi = light.mOuterConeAngle;
mD3DDevice->SetLight(lightStage, &d3dLight);
mD3DDevice->LightEnable(lightStage, true);
#endif
}
void GFXD3D9Device::setLightMaterialInternal(const GFXLightMaterial mat)
{
#ifndef TORQUE_OS_XENON
D3DMATERIAL9 d3dmat;
dMemset(&d3dmat, 0, sizeof(D3DMATERIAL9));
D3DCOLORVALUE col;
col.r = mat.ambient.red;
col.g = mat.ambient.green;
col.b = mat.ambient.blue;
col.a = mat.ambient.alpha;
d3dmat.Ambient = col;
col.r = mat.diffuse.red;
col.g = mat.diffuse.green;
col.b = mat.diffuse.blue;
col.a = mat.diffuse.alpha;
d3dmat.Diffuse = col;
col.r = mat.specular.red;
col.g = mat.specular.green;
col.b = mat.specular.blue;
col.a = mat.specular.alpha;
d3dmat.Specular = col;
col.r = mat.emissive.red;
col.g = mat.emissive.green;
col.b = mat.emissive.blue;
col.a = mat.emissive.alpha;
d3dmat.Emissive = col;
d3dmat.Power = mat.shininess;
mD3DDevice->SetMaterial(&d3dmat);
#endif
}
void GFXD3D9Device::setGlobalAmbientInternal(ColorF color)
{
#ifndef TORQUE_OS_XENON
mD3DDevice->SetRenderState(D3DRS_AMBIENT,
D3DCOLOR_COLORVALUE(color.red, color.green, color.blue, color.alpha));
#endif
}
//------------------------------------------------------------------------------
// Check for texture mis-match between GFX internal state and what is on the card
// This function is expensive because of the readbacks from DX, and additionally
// won't work unless it's a non-pure device.
//
// This function can crash or give false positives when the game
// is shutting down or returning to the main menu as some of the textures
// present in the mCurrentTexture array will have been freed.
//
// This function is best used as a quick check for mismatched state when it is
// suspected.
//------------------------------------------------------------------------------
void GFXD3D9Device::doParanoidStateCheck()
{
#ifdef TORQUE_DEBUG
// Read back all states and make sure they match what we think they should be.
// For now just do texture binds.
for(U32 i = 0; i < getNumSamplers(); i++)
{
IDirect3DBaseTexture9 *b=NULL;
getDevice()->GetTexture(i, &b);
if ((mCurrentTexture[i].isNull()) && (mCurrentCubemap[i].isNull()))
{
AssertFatal(b == NULL, "GFXD3D9Device::doParanoidStateCheck - got non-null texture in expected NULL slot!");
getDevice()->SetTexture(i, NULL);
}
else
{
AssertFatal(mCurrentTexture[i] || mCurrentCubemap[i], "GFXD3D9Device::doParanoidStateCheck - got null texture in expected non-null slot!");
if (mCurrentCubemap[i])
{
IDirect3DCubeTexture9 *cur= static_cast<GFXD3D9Cubemap*>(mCurrentCubemap[i].getPointer())->mCubeTex;
AssertFatal(cur == b, "GFXD3D9Device::doParanoidStateCheck - mismatched cubemap!");
}
else
{
IDirect3DBaseTexture9 *cur= static_cast<GFXD3D9TextureObject*>(mCurrentTexture[i].getPointer())->getTex();
AssertFatal(cur == b, "GFXD3D9Device::doParanoidStateCheck - mismatched 2d texture!");
}
}
SAFE_RELEASE(b);
}
#endif
}
GFXFence *GFXD3D9Device::createFence()
{
// Figure out what fence type we should be making if we don't know
if( mCreateFenceType == -1 )
{
IDirect3DQuery9 *testQuery = NULL;
mCreateFenceType = ( mD3DDevice->CreateQuery( D3DQUERYTYPE_EVENT, &testQuery ) == D3DERR_NOTAVAILABLE );
SAFE_RELEASE( testQuery );
}
// Cool, use queries
if( !mCreateFenceType )
{
GFXFence* fence = new GFXD3D9QueryFence( this );
fence->registerResourceWithDevice(this);
return fence;
}
// CodeReview: At some point I would like a specialized D3D9 implementation of
// the method used by the general fence, only without the overhead incurred
// by using the GFX constructs. Primarily the lock() method on texture handles
// will do a data copy, and this method doesn't require a copy, just a lock
// [5/10/2007 Pat]
GFXFence* fence = new GFXGeneralFence( this );
fence->registerResourceWithDevice(this);
return fence;
}
GFXOcclusionQuery* GFXD3D9Device::createOcclusionQuery()
{
GFXOcclusionQuery *query;
if (mOcclusionQuerySupported)
query = new GFXD3D9OcclusionQuery( this );
else
return NULL;
query->registerResourceWithDevice(this);
return query;
}
GFXCubemap * GFXD3D9Device::createCubemap()
{
GFXD3D9Cubemap* cube = new GFXD3D9Cubemap();
cube->registerResourceWithDevice(this);
return cube;
}