Torque3D/Engine/source/renderInstance/renderImposterMgr.cpp

360 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()
{
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 *prePassBin, bool startDeferred )
{
PROFILE_SCOPE( RenderImposterMgr_RenderDeferred );
if ( !mElementList.size() || !startDeferred )
return;
GFXDEBUGEVENT_SCOPE( RenderImposterMgr_RenderDeferred, ColorI::RED );
_innerRender( state, prePassBin );
}
void RenderImposterMgr::_innerRender( const SceneRenderState *state, RenderDeferredMgr *prePassBin )
{
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, NULL, NULL);
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, prePassBin ? 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 = prePassBin ? prePassBin->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;
}