Torque3D/Engine/source/renderInstance/renderFormatChanger.cpp
2018-11-21 15:53:02 +10:00

384 lines
13 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/renderFormatChanger.h"
#include "console/consoleTypes.h"
#include "gfx/gfxStringEnumTranslate.h"
#include "gfx/gfxTextureManager.h"
#include "gfx/gfxDebugEvent.h"
#include "postFx/postEffect.h"
#include "postFx/postEffectManager.h"
extern ColorI gCanvasClearColor;
IMPLEMENT_CONOBJECT(RenderFormatToken);
ConsoleDocClass( RenderFormatToken,
"@brief Used to change the render target format when rendering in AL.\n\n"
"RenderFormatToken is an implementation which changes the format of the "
"back buffer and/or the depth buffer.\n\n"
"The RenderPassStateBin manager changes the rendering state associated with "
"this token. In stock Torque 3D, a single example exists in the "
"way of AL_FormatToken (found in renderManager.cs). In that script file, all the "
"render managers are intialized, and a single RenderFormatToken is used. This "
"implementation basically exists to ensure Advanced Lighting works with MSAA.\n\n"
"The actions for this token toggle the format of the back/depth buffers "
"and it lets you specify a custom shader to \"copy\" the data so it can "
"be reformatted or altered. This is done through the variables copyEffect and "
"resolveEffect (which are post processes just like fog or glow)\n\n"
"@tsexample\n"
"// This token, and the associated render managers, ensure "
"that driver MSAA does not get used for Advanced Lighting renders.\n"
"// The 'AL_FormatResolve' PostEffect copies the result to the backbuffer.\n"
"new RenderFormatToken(AL_FormatToken)\n"
"{\n"
" enabled = \"false\";\n\n"
" format = \"GFXFormatR8G8B8A8\";\n"
" depthFormat = \"GFXFormatD24S8\";\n"
" aaLevel = 0; // -1 = match backbuffer\n\n"
" // The contents of the back buffer before this format token is executed\n"
" // is provided in $inTex\n"
" copyEffect = \"AL_FormatCopy\";\n\n"
" // The contents of the render target created by this format token is\n"
" // provided in $inTex\n"
" resolveEffect = \"AL_FormatCopy\";\n"
"};\n"
"@endtsexample\n\n"
"@see RenderPassToken\n\n"
"@see RenderPassStateBin\n"
"@see game/core/scripts/client/renderManager.cs\n"
"@ingroup GFX\n"
);
RenderFormatToken::RenderFormatToken()
: Parent(),
mFCState(FTSDisabled),
mColorFormat(GFXFormat_COUNT),
mDepthFormat(GFXFormat_COUNT),
mTargetUpdatePending(true),
mTargetChainIdx(0),
mTargetSize(Point2I::Zero),
mTargetAALevel(GFXTextureManager::AA_MATCH_BACKBUFFER),
mCopyPostEffect(NULL),
mResolvePostEffect(NULL)
{
GFXDevice::getDeviceEventSignal().notify(this, &RenderFormatToken::_handleGFXEvent);
GFXTextureManager::addEventDelegate(this, &RenderFormatToken::_onTextureEvent);
}
RenderFormatToken::~RenderFormatToken()
{
GFXTextureManager::removeEventDelegate(this, &RenderFormatToken::_onTextureEvent);
GFXDevice::getDeviceEventSignal().remove(this, &RenderFormatToken::_handleGFXEvent);
_teardownTargets();
}
void RenderFormatToken::process(SceneRenderState *state, RenderPassStateBin *callingBin)
{
switch(mFCState)
{
case FTSWaiting:
{
GFXDEBUGEVENT_SCOPE_EX(RFT_Waiting, ColorI::BLUE, avar("[%s Activate] (%s)", getName(), GFXStringTextureFormat[mColorFormat]));
mFCState = FTSActive;
mTarget.setViewport( GFX->getViewport() );
// Update targets
_updateTargets();
// If we have a copy PostEffect then get the active backbuffer copy
// now before we swap the render targets.
GFXTexHandle curBackBuffer;
if(mCopyPostEffect.isValid())
curBackBuffer = PFXMGR->getBackBufferTex();
// Push target
GFX->pushActiveRenderTarget();
GFX->setActiveRenderTarget(mTargetChain[mTargetChainIdx]);
// Set viewport
GFX->setViewport( mTarget.getViewport() );
// Clear
GFX->clear(GFXClearTarget | GFXClearZBuffer | GFXClearStencil, gCanvasClearColor, 1.0f, 0);
// Set active z target on render pass
if(mTargetDepthStencilTexture[mTargetChainIdx].isValid())
{
if(callingBin->getRenderPass()->getDepthTargetTexture() != GFXTextureTarget::sDefaultDepthStencil)
mStoredPassZTarget = callingBin->getRenderPass()->getDepthTargetTexture();
else
mStoredPassZTarget = NULL;
callingBin->getRenderPass()->setDepthTargetTexture(mTargetDepthStencilTexture[mTargetChainIdx]);
}
// Run the PostEffect which copies data into the new target.
if ( mCopyPostEffect.isValid() )
mCopyPostEffect->process( state, curBackBuffer, &mTarget.getViewport() );
}
break;
case FTSActive:
{
GFXDEBUGEVENT_SCOPE_EX(RFT_Active, ColorI::BLUE, avar("[%s Deactivate]", getName()));
mFCState = FTSComplete;
// Pop target
AssertFatal(GFX->getActiveRenderTarget() == mTargetChain[mTargetChainIdx], "Render target stack went wrong somewhere");
mTargetChain[mTargetChainIdx]->resolve();
GFX->popActiveRenderTarget();
mTarget.setTexture( mTargetColorTexture[mTargetChainIdx] );
// This is the GFX viewport when we were first processed.
GFX->setViewport( mTarget.getViewport() );
// Restore active z-target
if(mTargetDepthStencilTexture[mTargetChainIdx].isValid())
{
callingBin->getRenderPass()->setDepthTargetTexture(mStoredPassZTarget.getPointer());
mStoredPassZTarget = NULL;
}
// Run the PostEffect which copies data to the backbuffer
if(mResolvePostEffect.isValid())
{
// Need to create a texhandle here, since inOutTex gets assigned during process()
GFXTexHandle inOutTex = mTargetColorTexture[mTargetChainIdx];
mResolvePostEffect->process( state, inOutTex, &mTarget.getViewport() );
}
}
break;
case FTSComplete:
AssertFatal(false, "process() called on a RenderFormatToken which was already complete.");
// fall through
case FTSDisabled:
break;
}
}
void RenderFormatToken::reset()
{
AssertFatal(mFCState != FTSActive, "RenderFormatToken still active during reset()!");
if(mFCState != FTSDisabled)
mFCState = FTSWaiting;
}
void RenderFormatToken::_updateTargets()
{
if ( GFX->getActiveRenderTarget() == NULL )
return;
const Point2I &rtSize = GFX->getActiveRenderTarget()->getSize();
if ( rtSize.x <= mTargetSize.x &&
rtSize.y <= mTargetSize.y &&
!mTargetUpdatePending )
return;
mTargetSize = rtSize;
mTargetUpdatePending = false;
mTargetChainIdx = 0;
for( U32 i = 0; i < TargetChainLength; i++ )
{
if( !mTargetChain[i] )
mTargetChain[i] = GFX->allocRenderToTextureTarget();
// Update color target
if(mColorFormat != GFXFormat_COUNT)
{
// try reuse of old color texture
if( !mTargetColorTexture[i] || mTargetColorTexture[i].getFormat() != mColorFormat
|| mTargetColorTexture[i].getWidthHeight() != rtSize)
{
mTargetColorTexture[i].set( rtSize.x, rtSize.y, mColorFormat,
&GFXRenderTargetSRGBProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ),
1, mTargetAALevel );
mTargetChain[i]->attachTexture( GFXTextureTarget::Color0, mTargetColorTexture[i] );
}
}
// Update depth target
if(mDepthFormat != GFXFormat_COUNT)
{
// try reuse of old depth texture
if( !mTargetDepthStencilTexture[i] || mTargetDepthStencilTexture[i].getFormat() != mColorFormat
|| mTargetDepthStencilTexture[i].getWidthHeight() != rtSize)
{
mTargetDepthStencilTexture[i].set( rtSize.x, rtSize.y, mDepthFormat,
&GFXZTargetProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ),
1, mTargetAALevel );
mTargetChain[i]->attachTexture( GFXTextureTarget::DepthStencil, mTargetDepthStencilTexture[i] );
}
}
}
//set the texture for now as the first color target texture
mTarget.setTexture(mTargetColorTexture[0]);
}
void RenderFormatToken::_teardownTargets()
{
mTarget.release();
for(S32 i = 0; i < TargetChainLength; i++)
{
mTargetColorTexture[i] = NULL;
mTargetDepthStencilTexture[i] = NULL;
mTargetChain[i] = NULL;
}
}
bool RenderFormatToken::_setFmt( void *object, const char *index, const char *data )
{
// Flag update pending
reinterpret_cast<RenderFormatToken *>( object )->mTargetUpdatePending = true;
// Allow console system to assign value
return true;
}
const char* RenderFormatToken::_getCopyPostEffect( void* object, const char* data )
{
RenderFormatToken* token = reinterpret_cast< RenderFormatToken* >( object );
if( token->mCopyPostEffect.isValid() )
return token->mCopyPostEffect->getIdString();
return "0";
}
const char* RenderFormatToken::_getResolvePostEffect( void* object, const char* data )
{
RenderFormatToken* token = reinterpret_cast< RenderFormatToken* >( object );
if( token->mResolvePostEffect.isValid() )
return token->mResolvePostEffect->getIdString();
return "0";
}
bool RenderFormatToken::_setCopyPostEffect( void* object, const char* index, const char* data )
{
RenderFormatToken* token = reinterpret_cast< RenderFormatToken* >( object );
PostEffect* effect;
Sim::findObject( data, effect );
token->mCopyPostEffect = effect;
return false;
}
bool RenderFormatToken::_setResolvePostEffect( void* object, const char* index, const char* data )
{
RenderFormatToken* token = reinterpret_cast< RenderFormatToken* >( object );
PostEffect* effect;
Sim::findObject( data, effect );
token->mResolvePostEffect = effect;
return false;
}
void RenderFormatToken::enable( bool enabled /*= true*/ )
{
AssertFatal(mFCState != FTSActive, "RenderFormatToken is active, cannot change state now!");
if(enabled)
mFCState = FTSWaiting;
else
mFCState = FTSDisabled;
}
bool RenderFormatToken::isEnabled() const
{
return (mFCState != FTSDisabled);
}
void RenderFormatToken::initPersistFields()
{
addProtectedField("format", TypeGFXFormat, Offset(mColorFormat, RenderFormatToken), &_setFmt, &defaultProtectedGetFn,
"Sets the color buffer format for this token.");
addProtectedField("depthFormat", TypeGFXFormat, Offset(mDepthFormat, RenderFormatToken), &_setFmt, &defaultProtectedGetFn,
"Sets the depth/stencil buffer format for this token.");
addProtectedField("copyEffect", TYPEID<PostEffect>(), Offset(mCopyPostEffect, RenderFormatToken),
&_setCopyPostEffect, &_getCopyPostEffect,
"This PostEffect will be run when the render target is changed to the format specified "
"by this token. It is used to copy/format data into the token rendertarget");
addProtectedField("resolveEffect", TYPEID<PostEffect>(), Offset(mResolvePostEffect, RenderFormatToken),
&_setResolvePostEffect, &_getResolvePostEffect,
"This PostEffect will be run when the render target is changed back to the format "
"active prior to this token. It is used to copy/format data from the token rendertarget to the backbuffer.");
addField("aaLevel", TypeS32, Offset(mTargetAALevel, RenderFormatToken),
"Anti-ailiasing level for the this token. 0 disables, -1 uses adapter default.");
Parent::initPersistFields();
}
bool RenderFormatToken::_handleGFXEvent(GFXDevice::GFXDeviceEventType event_)
{
if ( event_ == GFXDevice::deStartOfFrame )
{
mTargetChainIdx++;
if ( mTargetChainIdx >= TargetChainLength )
mTargetChainIdx = 0;
}
return true;
}
void RenderFormatToken::_onTextureEvent( GFXTexCallbackCode code )
{
if(code == GFXZombify)
{
_teardownTargets();
mTargetUpdatePending = true;
}
}
bool RenderFormatToken::onAdd()
{
if(!Parent::onAdd())
return false;
mTarget.registerWithName( getName() );
mTarget.setSamplerState( GFXSamplerStateDesc::getClampPoint() );
return true;
}
void RenderFormatToken::onRemove()
{
mTarget.unregister();
mTarget.release();
Parent::onRemove();
}