mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 04:34:48 +00:00
362 lines
12 KiB
C++
362 lines
12 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 "platform/platform.h"
|
|
#include "renderInstance/renderImposterMgr.h"
|
|
|
|
#include "scene/sceneManager.h"
|
|
#include "T3D/gameBase/gameConnection.h"
|
|
#include "materials/shaderData.h"
|
|
#include "lighting/lightManager.h"
|
|
#include "lighting/lightInfo.h"
|
|
#include "scene/sceneRenderState.h"
|
|
#include "gfx/gfxDebugEvent.h"
|
|
#include "renderInstance/renderDeferredMgr.h"
|
|
#include "gfx/gfxTransformSaver.h"
|
|
#include "console/consoleTypes.h"
|
|
#include "gfx/util/screenspace.h"
|
|
#include "math/util/matrixSet.h"
|
|
#include "materials/materialManager.h"
|
|
#include "materials/materialFeatureTypes.h"
|
|
|
|
/*
|
|
GFXImplementVertexFormat( ImposterCorner )
|
|
{
|
|
addElement( "ImposterCorner", GFXDeclType_Float, 4 );
|
|
};
|
|
*/
|
|
|
|
const RenderInstType RenderImposterMgr::RIT_Imposter( "Imposter" );
|
|
const RenderInstType RenderImposterMgr::RIT_ImposterBatch( "ImposterBatch" );
|
|
|
|
|
|
U32 RenderImposterMgr::smRendered = 0.0f;
|
|
U32 RenderImposterMgr::smBatches = 0.0f;
|
|
U32 RenderImposterMgr::smDrawCalls = 0.0f;
|
|
U32 RenderImposterMgr::smPolyCount = 0.0f;
|
|
U32 RenderImposterMgr::smRTChanges = 0.0f;
|
|
|
|
|
|
IMPLEMENT_CONOBJECT(RenderImposterMgr);
|
|
|
|
ConsoleDocClass( RenderImposterMgr,
|
|
"@brief A render bin for batch rendering imposters.\n\n"
|
|
"This render bin gathers imposter render instances and renders them in large "
|
|
"batches.\n\n"
|
|
"You can type 'metrics( imposter )' in the console to see rendering statistics.\n\n"
|
|
"@ingroup RenderBin\n" );
|
|
|
|
|
|
RenderImposterMgr::RenderImposterMgr( F32 renderOrder, F32 processAddOrder )
|
|
: RenderBinManager( RIT_Imposter, renderOrder, processAddOrder )
|
|
{
|
|
notifyType( RIT_ImposterBatch );
|
|
RenderDeferredMgr::getRenderSignal().notify( this, &RenderImposterMgr::_renderDeferred );
|
|
}
|
|
|
|
void RenderImposterMgr::initPersistFields()
|
|
{
|
|
docsURL;
|
|
GFXDevice::getDeviceEventSignal().notify( &RenderImposterMgr::_clearStats );
|
|
|
|
Con::addVariable( "$ImposterStats::rendered", TypeS32, &smRendered, "@internal" );
|
|
Con::addVariable( "$ImposterStats::batches", TypeS32, &smBatches, "@internal" );
|
|
Con::addVariable( "$ImposterStats::drawCalls", TypeS32, &smDrawCalls, "@internal" );
|
|
Con::addVariable( "$ImposterStats::polyCount", TypeS32, &smPolyCount, "@internal" );
|
|
Con::addVariable( "$ImposterStats::rtChanges", TypeS32, &smRTChanges, "@internal" );
|
|
|
|
Parent::initPersistFields();
|
|
}
|
|
|
|
RenderImposterMgr::~RenderImposterMgr()
|
|
{
|
|
RenderDeferredMgr::getRenderSignal().remove( this, &RenderImposterMgr::_renderDeferred );
|
|
|
|
mIB = NULL;
|
|
}
|
|
|
|
void RenderImposterMgr::render( SceneRenderState *state )
|
|
{
|
|
PROFILE_SCOPE( RenderImposterMgr_Render );
|
|
|
|
if ( !mElementList.size() )
|
|
return;
|
|
|
|
GFXDEBUGEVENT_SCOPE( RenderImposterMgr_Render, ColorI::RED );
|
|
|
|
_innerRender( state, NULL );
|
|
}
|
|
|
|
bool RenderImposterMgr::_clearStats( GFXDevice::GFXDeviceEventType type )
|
|
{
|
|
if ( type == GFXDevice::deStartOfFrame )
|
|
{
|
|
smRendered = 0.0f;
|
|
smBatches = 0.0f;
|
|
smDrawCalls = 0.0f;
|
|
smPolyCount = 0.0f;
|
|
smRTChanges = 0.0f;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void RenderImposterMgr::_renderDeferred( const SceneRenderState *state, RenderDeferredMgr *deferredBin, bool startDeferred )
|
|
{
|
|
PROFILE_SCOPE( RenderImposterMgr_RenderDeferred );
|
|
|
|
if ( !mElementList.size() || !startDeferred )
|
|
return;
|
|
|
|
GFXDEBUGEVENT_SCOPE( RenderImposterMgr_RenderDeferred, ColorI::RED );
|
|
|
|
_innerRender( state, deferredBin );
|
|
}
|
|
|
|
void RenderImposterMgr::_innerRender( const SceneRenderState *state, RenderDeferredMgr *deferredBin )
|
|
{
|
|
if (deferredBin == NULL) return;
|
|
PROFILE_SCOPE( RenderImposterMgr_InnerRender );
|
|
|
|
// Capture the GFX stats for this render.
|
|
GFXDeviceStatistics stats;
|
|
stats.start( GFX->getDeviceStatistics() );
|
|
|
|
GFXTransformSaver saver;
|
|
|
|
// Restore transforms
|
|
MatrixSet &matrixSet = getRenderPass()->getMatrixSet();
|
|
matrixSet.restoreSceneViewProjection();
|
|
matrixSet.setWorld( MatrixF::Identity );
|
|
|
|
// Setup the large static index buffer for rendering the imposters.
|
|
if ( !mIB.isValid() )
|
|
{
|
|
// Setup a static index buffer for rendering.
|
|
mIB.set( GFX, smImposterBatchSize * 6, 0, GFXBufferTypeStatic );
|
|
U16 *idxBuff;
|
|
mIB.lock(&idxBuff, NULL, 0, 0);
|
|
for ( U32 i=0; i < smImposterBatchSize; i++ )
|
|
{
|
|
//
|
|
// The vertex pattern in the VB for each
|
|
// imposter is as follows...
|
|
//
|
|
// 0----1
|
|
// |\ |
|
|
// | \ |
|
|
// | \ |
|
|
// | \|
|
|
// 3----2
|
|
//
|
|
// We setup the index order below to ensure
|
|
// sequental, cache friendly, access.
|
|
//
|
|
U32 offset = i * 4;
|
|
idxBuff[i*6+0] = 0 + offset;
|
|
idxBuff[i*6+1] = 1 + offset;
|
|
idxBuff[i*6+2] = 2 + offset;
|
|
idxBuff[i*6+3] = 2 + offset;
|
|
idxBuff[i*6+4] = 3 + offset;
|
|
idxBuff[i*6+5] = 0 + offset;
|
|
}
|
|
mIB.unlock();
|
|
}
|
|
|
|
/*
|
|
if ( !mCornerVB.isValid() )
|
|
{
|
|
// Setup a static vertex buffer for the corner index for each imposter state.
|
|
mCornerVB.set( GFX, smImposterBatchSize * 4, GFXBufferTypeStatic );
|
|
ImposterCorner *corner = mCornerVB.lock( 0 );
|
|
for ( U32 i=0; i < smImposterBatchSize; i++ )
|
|
{
|
|
corner->corner = 0; corner++;
|
|
corner->corner = 1; corner++;
|
|
corner->corner = 2; corner++;
|
|
corner->corner = 3; corner++;
|
|
}
|
|
mCornerVB.unlock();
|
|
}
|
|
*/
|
|
|
|
// Set the buffers here once.
|
|
GFX->setPrimitiveBuffer( mIB );
|
|
|
|
// Batch up the imposters into the buffer. These
|
|
// are already sorted by texture, to minimize switches
|
|
// so just batch them up and render as they come.
|
|
|
|
ImposterState* statePtr = NULL;
|
|
U32 stateCount;
|
|
ImposterBaseRenderInst *ri;
|
|
ImposterRenderInst *imposter;
|
|
ImposterBatchRenderInst *batch;
|
|
const U32 binSize = mElementList.size();
|
|
BaseMatInstance *setupMat, *currMat;
|
|
GFXVertexBufferHandle<ImposterState> vb;
|
|
|
|
// TODO: We could maybe do better with lights when forward
|
|
// rendering the imposters. Just pass a light list with it
|
|
// and do some simple tests to break the batch when the light
|
|
// list changes.
|
|
|
|
SceneData sgData;
|
|
sgData.init( state, deferredBin ? SceneData::DeferredBin : SceneData::RegularBin );
|
|
sgData.lights[0] = LIGHTMGR->getDefaultLight();
|
|
|
|
// TODO: I should rework this loop to generate the VB first then
|
|
// do all the material passes... should be easier to read and faster.
|
|
//
|
|
// Also consider making this store two element lists... one for
|
|
// batches and one for individual imposters.
|
|
//
|
|
|
|
for ( U32 i=0; i < binSize; )
|
|
{
|
|
currMat = static_cast<ImposterBaseRenderInst*>( mElementList[i].inst )->mat;
|
|
setupMat = deferredBin ? deferredBin->getDeferredMaterial( currMat ) : currMat;
|
|
|
|
// TODO: Fix MatInstance to take a const SceneRenderState!
|
|
while ( setupMat->setupPass( (SceneRenderState*)state, sgData ) )
|
|
{
|
|
setupMat->setSceneInfo( (SceneRenderState*)state, sgData );
|
|
setupMat->setTransforms( matrixSet, (SceneRenderState*)state );
|
|
|
|
for ( ; i < binSize; )
|
|
{
|
|
ri = static_cast<ImposterBaseRenderInst*>( mElementList[i].inst );
|
|
|
|
// NOTE: Its safe to compare matinstances here instead of
|
|
// the state hint because imposters all share the same
|
|
// material instances.... if this changes revise.
|
|
if ( ri->mat != currMat )
|
|
break;
|
|
|
|
// Ok if this is a batch then we can just fire off the draw now.
|
|
if ( ri->type == RIT_ImposterBatch )
|
|
{
|
|
batch = static_cast<ImposterBatchRenderInst*>( ri );
|
|
|
|
GFX->setVertexBuffer( batch->vertBuff->getPointer() );
|
|
GFX->drawPrimitive( GFXTriangleList, 0, batch->vertBuff->getPointer()->mNumVerts / 3 );
|
|
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
// This wasn't a batch so build up all the single imposters into
|
|
// a dynamic batch and render it.
|
|
|
|
statePtr = mBuffer;
|
|
stateCount = 0;
|
|
|
|
// Loop for each individual imposter.
|
|
for ( ; i < binSize; i++ )
|
|
{
|
|
if ( mElementList[i].inst->type == RIT_ImposterBatch )
|
|
break;
|
|
|
|
imposter = static_cast<ImposterRenderInst*>( mElementList[i].inst );
|
|
|
|
// Stop the loop if the material changed.
|
|
if ( imposter->mat != currMat )
|
|
break;
|
|
|
|
++smRendered;
|
|
|
|
// If we're out of vb space then draw what we got.
|
|
if ( stateCount + 1 >= smImposterBatchSize )
|
|
{
|
|
smBatches++;
|
|
|
|
vb.set( GFX, stateCount*4, GFXBufferTypeVolatile );
|
|
ImposterState *buf = vb.lock();
|
|
if(buf)
|
|
{
|
|
dMemcpy( buf, mBuffer, stateCount * 4 * sizeof( ImposterState ) );
|
|
vb.unlock();
|
|
}
|
|
|
|
//GFX->setVertexBuffer( mCornerVB, 0, stateCount * 4 );
|
|
GFX->setVertexBuffer( vb );
|
|
///GFX->setVertexFormat( &mImposterVertDecl );
|
|
|
|
GFX->drawIndexedPrimitive( GFXTriangleList, 0, 0, stateCount * 4, 0, stateCount * 2 );
|
|
statePtr = mBuffer;
|
|
stateCount = 0;
|
|
}
|
|
|
|
// Setup the imposter state.
|
|
*statePtr = imposter->state;
|
|
statePtr->corner = 0;
|
|
statePtr++;
|
|
|
|
*statePtr = imposter->state;
|
|
statePtr->corner = 1;
|
|
statePtr++;
|
|
|
|
*statePtr = imposter->state;
|
|
statePtr->corner = 2;
|
|
statePtr++;
|
|
|
|
*statePtr = imposter->state;
|
|
statePtr->corner = 3;
|
|
statePtr++;
|
|
|
|
stateCount++;
|
|
|
|
} // for ( ; i < binSize; i++ )
|
|
|
|
// Any remainder to dump?
|
|
if ( stateCount > 0 )
|
|
{
|
|
smBatches++;
|
|
|
|
vb.set( GFX, stateCount*4, GFXBufferTypeVolatile );
|
|
ImposterState *buf = vb.lock();
|
|
if(buf)
|
|
{
|
|
dMemcpy( buf, mBuffer, stateCount * 4 * sizeof( ImposterState ) );
|
|
vb.unlock();
|
|
}
|
|
|
|
//GFX->setVertexBuffer( mCornerVB, 0, stateCount * 4 );
|
|
GFX->setVertexBuffer( vb );
|
|
///GFX->setVertexFormat( &mImposterVertDecl );
|
|
|
|
GFX->drawIndexedPrimitive( GFXTriangleList, 0, 0, stateCount * 4, 0, stateCount * 2 );
|
|
}
|
|
|
|
} // for( U32 i=0; i < binSize; )
|
|
|
|
} // while ( currMat->setupPass( (SceneRenderState*)state, sgData ) )
|
|
|
|
} // for ( U32 i=0; i < binSize; )
|
|
|
|
// Capture the GFX stats for this render.
|
|
stats.end( GFX->getDeviceStatistics() );
|
|
smDrawCalls += stats.mDrawCalls;
|
|
smPolyCount += stats.mPolyCount;
|
|
smRTChanges += stats.mRenderTargetChanges;
|
|
}
|