Game Development Community

[beta 3] Adding depth and stencil support to PostEffect [code]

by Manoel Neto · in Torque 3D Professional · 03/27/2011 (9:53 am) · 4 replies

This is needed to speedup the morphological antialiasing post effect. It reduces the performance cost from 2ms to 1ms on my HD 4850.

The change bellow adds support to using the screen depthStencil buffer (if the current render target has the same size as the backbuffer) and named depthStencil buffers (in the same way as named targets).

In Engine/source/postFx/postEffect.h:

Add the marked lines:
NamedTexTarget mNamedTarget;
NamedTexTarget mNamedTargetDepthStencil; // <-- ADDED!
String mTargetName;
String mTargetDepthStencilName; // <-- ADDED!
GFXTexHandle mTargetTex;
GFXTexHandle mTargetDepthStencil; // <-- ADDED!
And change this line:
if ( code == GFXZombify && mNamedTarget.isRegistered() )
To:
if ( code == GFXZombify && (mNamedTarget.isRegistered() || mNamedTargetDepthStencil.isRegistered()) )

In Engine/source/postFx/postEffect.cpp, make the marked changes:

addField( "target", TypeRealString, Offset( mTargetName, PostEffect ),
   "String identifier of this effect's target texture.\n"
   "@see PFXTextureIdentifiers" );

addField( "targetDepthStencil", TypeRealString, Offset( mTargetDepthStencilName, PostEffect ), //<-- ADDED
   "String identifier of this effect's target depth/stencil texture.\n" //<-- ADDED
   "@see PFXTextureIdentifiers" ); //<-- ADDED
// Is the target a named target?
   if ( mTargetName.isNotEmpty() && mTargetName[0] == '#' )
   {
      mNamedTarget.registerWithName( mTargetName.substr( 1 ) );
      mNamedTarget.getTextureDelegate().bind( this, &PostEffect::_getTargetTexture );

      //GFXTextureManager::addEventDelegate( this, &PostEffect::_onTextureEvent ); // <--- REMOVED!
   }
   
   // Is the target a named target?
   if ( mTargetDepthStencilName.isNotEmpty() && mTargetDepthStencilName[0] == '#' ) // <--- ADDED!
      mNamedTargetDepthStencil.registerWithName( mTargetDepthStencilName.substr( 1 ) );  // <--- ADDED!

   if (mNamedTarget.isRegistered() || mNamedTargetDepthStencil.isRegistered())  // <--- ADDED!
      GFXTextureManager::addEventDelegate( this, &PostEffect::_onTextureEvent );  // <--- ADDED!

   // Call onAdd in script
   onAdd_callback();

void PostEffect::onRemove()
{
   Parent::onRemove();

   PFXMGR->_removeEffect( this );

   LightManager::smActivateSignal.remove( this, &PostEffect::_onLMActivate );

   mShader = NULL;
   _cleanTargets();

   if ( mNamedTarget.isRegistered() || mNamedTargetDepthStencil.isRegistered() )    // <--- ADDED!
      GFXTextureManager::removeEventDelegate( this, &PostEffect::_onTextureEvent ); // <--- ADDED!

   if ( mNamedTarget.isRegistered() )
   {      
      // GFXTextureManager::removeEventDelegate( this, &PostEffect::_onTextureEvent );  // <--- REMOVED!
      mNamedTarget.unregister();
      mNamedTarget.getTextureDelegate().clear();
   }

   if ( mNamedTargetDepthStencil.isRegistered() ) // <--- ADDED!
      mNamedTargetDepthStencil.unregister();      // <--- ADDED!
}

void PostEffect::_cleanTargets( bool recurse )
{
   mTargetTex = NULL;
   mTargetDepthStencil = NULL; //<--- ADDED!
   mTarget = NULL;

In PostEffect::_setupTarget(), after this:
else
   mTargetTex = NULL;
Add this:
// Do we have a named depthStencil target?
   if ( mNamedTargetDepthStencil.isRegistered() )
   {
      // Size it relative to the texture of the first stage or
      // if NULL then use the current target.

      Point2I targetSize;

      // If we have an absolute target size then use that.
      if ( !mTargetSize.isZero() )
         targetSize = mTargetSize;

      // Else generate a relative size using the target scale.
      else if ( mActiveTextures[ 0 ] )
      {
         const Point3I &texSize = mActiveTextures[ 0 ]->getSize();

         targetSize.set(   texSize.x * mTargetScale.x,
                           texSize.y * mTargetScale.y );
      }
      else
      {
         GFXTarget *oldTarget = GFX->getActiveRenderTarget();
         const Point2I &oldTargetSize = oldTarget->getSize();

         targetSize.set(   oldTargetSize.x * mTargetScale.x,
                           oldTargetSize.y * mTargetScale.y );
      }

      // Make sure its at least 1x1.
      targetSize.setMax( Point2I::One );
      
      if (  mNamedTargetDepthStencil.isRegistered() ||
            mTargetDepthStencil.getWidthHeight() != targetSize )
      {         
         mTargetDepthStencil.set( targetSize.x, targetSize.y, GFXFormatD24S8,
                     &GFXDefaultZTargetProfile, "PostEffect::_setupTarget" );

         if ( mTargetClear == PFXTargetClear_OnCreate )
            *outClearTarget = true;

         mNamedTargetDepthStencil.setViewport( RectI( 0, 0, targetSize.x, targetSize.y ) );
      }
   }
   else
      mTargetDepthStencil = NULL;

And change:
if ( !mTarget && mTargetTex )
To:
if ( !mTarget && (mTargetTex || mTargetDepthStencil) )

Inside PostEffect::process(), change all instances of:
if ( mTargetTex )
To:
if ( mTargetTex || mTargetDepthStencil )
(There are two of those, make sure to change both).

And finally:
mTarget->attachTexture( GFXTextureTarget::Color0, mTargetTex );
if ( !mTargetDepthStencil && mTargetTex.getWidthHeight() == GFX->getActiveRenderTarget()->getSize() ) // <--- ADDED!     
   mTarget->attachTexture( GFXTextureTarget::DepthStencil, GFXTextureTarget::sDefaultDepthStencil );  // <--- ADDED!
else  // <--- ADDED!
   mTarget->attachTexture( GFXTextureTarget::DepthStencil, mTargetDepthStencil ); // <--- ADDED!
GFX->setActiveRenderTarget( mTarget );

To read/write to a named depthStencil target in your postEffects, just add this to your post effect declarations:
targetDepthStencil = "#myDepth";
And enable stencil in the stateBlock.

#1
03/29/2011 (4:03 pm)
Thanks Manoel! THREED-1531
#2
04/02/2011 (3:06 pm)
The main issue i see with this patch is that it doesn't allow for clearing the named depth/stencil target. In that case you get whatever garbage was in the stencil buffer from the last pass.

Seems to fix it you need several fields. The value to clear stencil and depth to... and weather depth or stencil needs to be cleared at all.

Thoughts?
#3
04/02/2011 (3:58 pm)
Yeah, I just checked the code again and it's not clearing. I saw the GFX->clear() call there and assumed it would clear, but didn't notice it needs the flags (GFXClearStencil | GFXClearTarget | GFXClearZBuffer) to clear everything. But even if it did, it wouldn't do anything when using the default DepthStencil.

I'll make the changes and post them here.
#4
04/02/2011 (4:20 pm)
This should expose the same control over depth and stencil clearing as the PostEffect offers for color:

In postEffect.h, add these to the protected members:

F32 mDepthClearValue;
U32 mStencilClearValue;

PFXTargetClear mDepthClear;
PFXTargetClear mStencilClear;

Change this method:
virtual void _setupTarget( const SceneState *state, bool *outClearTarget );
To this:
virtual void _setupTarget( const SceneState *state, U32 *outClearFlags );

In PostEffect.cpp, add these:

mTargetClearColor( ColorF::BLACK ),
mDepthClearValue(1.0f), //<--- ADDED
mStencilClearValue(0), //<--- ADDED
mDepthClear( PFXTargetClear_None ), //<--- ADDED
mStencilClear( PFXTargetClear_None ), //<--- ADDED
mOneFrameOnly( false ),

addField( "targetClear", TYPEID< PFXTargetClear >(), Offset( mTargetClear, PostEffect ),
   "Describes when the target texture should be cleared." );

addField( "depthClearValue", TypeF32, Offset( mDepthClearValue, PostEffect ), //<--- ADDED
   "Depth value used to clear the target depth buffer before rendering." ); //<--- ADDED

addField( "stencilClearValue", TypeS32, Offset( mStencilClearValue, PostEffect ), //<--- ADDED
   "Stencil value used to clear the target depth buffer before rendering." );   //<--- ADDED

addField( "depthClear", TYPEID< PFXTargetClear >(), Offset( mDepthClear, PostEffect ), //<--- ADDED
   "Describes when the target depth should be cleared." );  //<--- ADDED

addField( "stencilClear", TYPEID< PFXTargetClear >(), Offset( mStencilClear, PostEffect ), //<--- ADDED
   "Describes when the target stencil should be cleared." ); //<--- ADDED

Change:
void PostEffect::_setupTarget( const SceneState *state, bool *outClearTarget )
To:
void PostEffect::_setupTarget( const SceneState *state, U32 *outClearFlags )

Change:
if ( mTargetClear == PFXTargetClear_OnCreate )
   *outClearTarget = true;

mNamedTarget.setViewport( RectI( 0, 0, targetSize.x, targetSize.y ) );
to:
if ( mTargetClear == PFXTargetClear_OnCreate )
   *outClearFlags |= GFXClearTarget;

mNamedTarget.setViewport( RectI( 0, 0, targetSize.x, targetSize.y ) );

Change:
if ( mTargetClear == PFXTargetClear_OnCreate )
   *outClearTarget = true;

mNamedTargetDepthStencil.setViewport( RectI( 0, 0, targetSize.x, targetSize.y ) );

To
if ( mDepthClear == PFXTargetClear_OnCreate )
   *outClearFlags |= GFXClearZBuffer;

if ( mStencilClear == PFXTargetClear_OnCreate )
   *outClearFlags |= GFXClearStencil;

mNamedTargetDepthStencil.setViewport( RectI( 0, 0, targetSize.x, targetSize.y ) );

Change:
if ( mTargetClear == PFXTargetClear_OnDraw )
   *outClearTarget = true;
To:
if ( mTargetClear == PFXTargetClear_OnDraw )
   *outClearFlags |= GFXClearTarget;

if ( mDepthClear == PFXTargetClear_OnDraw )
   *outClearFlags |= GFXClearZBuffer;

if ( mStencilClear == PFXTargetClear_OnDraw )
   *outClearFlags |= GFXClearStencil;

Change:
bool clearTarget = false;
_setupTarget( state, &clearTarget );
To:
U32 clearFlags = false;
_setupTarget( state, &clearFlags );

Change:
if ( clearTarget )
   GFX->clear( GFXClearTarget, mTargetClearColor, 1.f, 0 );
To:
if ( clearFlags )
   GFX->clear( clearFlags, mTargetClearColor, mDepthClearValue, mStencilClearValue );

That should do it.