Torque3D/Engine/source/gfx/D3D9/gfxD3D9Device.cpp
2016-03-20 21:52:11 +10:00

1166 lines
36 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"
#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;
// 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 - This function is totally not needed on PC because there
// is fixed-function support in D3D9
//------------------------------------------------------------------------------
inline void GFXD3D9Device::setupGenericShaders( GenericShaderType type /* = GSColor */ )
{
#ifdef WANT_TO_SIMULATE_UI_ON_360
if( mGenericShader[GSColor] == NULL )
{
mGenericShader[GSColor] = createShader( "shaders/common/genericColorV.hlsl",
"shaders/common/genericColorP.hlsl",
2.f );
mGenericShader[GSModColorTexture] = createShader( "shaders/common/genericModColorTextureV.hlsl",
"shaders/common/genericModColorTextureP.hlsl",
2.f );
mGenericShader[GSAddColorTexture] = createShader( "shaders/common/genericAddColorTextureV.hlsl",
"shaders/common/genericAddColorTextureP.hlsl",
2.f );
}
mGenericShader[type]->process();
MatrixF world, view, proj;
mWorldMatrix[mWorldStackSize].transposeTo( world );
mViewMatrix.transposeTo( view );
mProjectionMatrix.transposeTo( proj );
mTempMatrix = world * view * proj;
setVertexShaderConstF( VC_WORLD_PROJ, (F32 *)&mTempMatrix, 4 );
#else
disableShaders();
#endif
}
//-----------------------------------------------------------------------------
/// 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;
}
// 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,
d3dBuffer && stream != 0 ? d3dBuffer->mVolatileStart * d3dBuffer->mVertexSize : 0,
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;
D3DVERTEXELEMENT9 *vd = new D3DVERTEXELEMENT9[ elemCount + 1 ];
for ( U32 i=0; i < elemCount; i++ )
{
const GFXVertexElement &element = vertexFormat->getElement( i );
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
{
// 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[elemCount] = 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;
}