mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-19 20:24:49 +00:00
1166 lines
36 KiB
C++
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;
|
|
}
|