renderFormatChanger.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "renderInstance/renderFormatChanger.h"
  24. #include "console/consoleTypes.h"
  25. #include "gfx/gfxStringEnumTranslate.h"
  26. #include "gfx/gfxTextureManager.h"
  27. #include "gfx/gfxDebugEvent.h"
  28. #include "postFx/postEffect.h"
  29. #include "postFx/postEffectManager.h"
  30. extern ColorI gCanvasClearColor;
  31. IMPLEMENT_CONOBJECT(RenderFormatToken);
  32. ConsoleDocClass( RenderFormatToken,
  33. "@brief Used to change the render target format when rendering in AL.\n\n"
  34. "RenderFormatToken is an implementation which changes the format of the "
  35. "back buffer and/or the depth buffer.\n\n"
  36. "The RenderPassStateBin manager changes the rendering state associated with "
  37. "this token. In stock Torque 3D, a single example exists in the "
  38. "way of AL_FormatToken (found in renderManager." TORQUE_SCRIPT_EXTENSION "). In that script file, all the "
  39. "render managers are intialized, and a single RenderFormatToken is used. This "
  40. "implementation basically exists to ensure Advanced Lighting works with MSAA.\n\n"
  41. "The actions for this token toggle the format of the back/depth buffers "
  42. "and it lets you specify a custom shader to \"copy\" the data so it can "
  43. "be reformatted or altered. This is done through the variables copyEffect and "
  44. "resolveEffect (which are post processes just like fog or glow)\n\n"
  45. "@tsexample\n"
  46. "// This token, and the associated render managers, ensure "
  47. "that driver MSAA does not get used for Advanced Lighting renders.\n"
  48. "// The 'AL_FormatResolve' PostEffect copies the result to the backbuffer.\n"
  49. "new RenderFormatToken(AL_FormatToken)\n"
  50. "{\n"
  51. " enabled = \"false\";\n\n"
  52. " format = \"GFXFormatR8G8B8A8\";\n"
  53. " depthFormat = \"GFXFormatD24S8\";\n"
  54. " aaLevel = 0; // -1 = match backbuffer\n\n"
  55. " // The contents of the back buffer before this format token is executed\n"
  56. " // is provided in $inTex\n"
  57. " copyEffect = \"AL_FormatCopy\";\n\n"
  58. " // The contents of the render target created by this format token is\n"
  59. " // provided in $inTex\n"
  60. " resolveEffect = \"AL_FormatCopy\";\n"
  61. "};\n"
  62. "@endtsexample\n\n"
  63. "@see RenderPassToken\n\n"
  64. "@see RenderPassStateBin\n"
  65. "@see game/core/scripts/client/renderManager." TORQUE_SCRIPT_EXTENSION "\n"
  66. "@ingroup GFX\n"
  67. );
  68. RenderFormatToken::RenderFormatToken()
  69. : Parent(),
  70. mFCState(FTSDisabled),
  71. mColorFormat(GFXFormat_COUNT),
  72. mDepthFormat(GFXFormat_COUNT),
  73. mTargetUpdatePending(true),
  74. mTargetChainIdx(0),
  75. mTargetSize(Point2I::Zero),
  76. mTargetAALevel(GFXTextureManager::AA_MATCH_BACKBUFFER),
  77. mCopyPostEffect(NULL),
  78. mResolvePostEffect(NULL)
  79. {
  80. GFXDevice::getDeviceEventSignal().notify(this, &RenderFormatToken::_handleGFXEvent);
  81. GFXTextureManager::addEventDelegate(this, &RenderFormatToken::_onTextureEvent);
  82. }
  83. RenderFormatToken::~RenderFormatToken()
  84. {
  85. GFXTextureManager::removeEventDelegate(this, &RenderFormatToken::_onTextureEvent);
  86. GFXDevice::getDeviceEventSignal().remove(this, &RenderFormatToken::_handleGFXEvent);
  87. _teardownTargets();
  88. }
  89. void RenderFormatToken::process(SceneRenderState *state, RenderPassStateBin *callingBin)
  90. {
  91. switch(mFCState)
  92. {
  93. case FTSWaiting:
  94. {
  95. GFXDEBUGEVENT_SCOPE_EX(RFT_Waiting, ColorI::BLUE, avar("[%s Activate] (%s)", getName(), GFXStringTextureFormat[mColorFormat]));
  96. mFCState = FTSActive;
  97. mTarget.setViewport( GFX->getViewport() );
  98. // Update targets
  99. _updateTargets();
  100. // If we have a copy PostEffect then get the active backbuffer copy
  101. // now before we swap the render targets.
  102. GFXTexHandle curBackBuffer;
  103. if(mCopyPostEffect.isValid())
  104. curBackBuffer = PFXMGR->getBackBufferTex();
  105. // Push target
  106. GFX->pushActiveRenderTarget();
  107. GFX->setActiveRenderTarget(mTargetChain[mTargetChainIdx]);
  108. // Set viewport
  109. GFX->setViewport( mTarget.getViewport() );
  110. // Clear
  111. GFX->clear(GFXClearTarget | GFXClearZBuffer | GFXClearStencil, gCanvasClearColor, 1.0f, 0);
  112. // Set active z target on render pass
  113. if(mTargetDepthStencilTexture[mTargetChainIdx].isValid())
  114. {
  115. if(callingBin->getRenderPass()->getDepthTargetTexture() != GFXTextureTarget::sDefaultDepthStencil)
  116. mStoredPassZTarget = callingBin->getRenderPass()->getDepthTargetTexture();
  117. else
  118. mStoredPassZTarget = NULL;
  119. callingBin->getRenderPass()->setDepthTargetTexture(mTargetDepthStencilTexture[mTargetChainIdx]);
  120. }
  121. // Run the PostEffect which copies data into the new target.
  122. if ( mCopyPostEffect.isValid() )
  123. mCopyPostEffect->process( state, curBackBuffer, &mTarget.getViewport() );
  124. }
  125. break;
  126. case FTSActive:
  127. {
  128. GFXDEBUGEVENT_SCOPE_EX(RFT_Active, ColorI::BLUE, avar("[%s Deactivate]", getName()));
  129. mFCState = FTSComplete;
  130. // Pop target
  131. AssertFatal(GFX->getActiveRenderTarget() == mTargetChain[mTargetChainIdx], "Render target stack went wrong somewhere");
  132. mTargetChain[mTargetChainIdx]->resolve();
  133. GFX->popActiveRenderTarget();
  134. mTarget.setTexture( mTargetColorTexture[mTargetChainIdx] );
  135. // This is the GFX viewport when we were first processed.
  136. GFX->setViewport( mTarget.getViewport() );
  137. // Restore active z-target
  138. if(mTargetDepthStencilTexture[mTargetChainIdx].isValid())
  139. {
  140. callingBin->getRenderPass()->setDepthTargetTexture(mStoredPassZTarget.getPointer());
  141. mStoredPassZTarget = NULL;
  142. }
  143. // Run the PostEffect which copies data to the backbuffer
  144. if(mResolvePostEffect.isValid())
  145. {
  146. // Need to create a texhandle here, since inOutTex gets assigned during process()
  147. GFXTexHandle inOutTex = mTargetColorTexture[mTargetChainIdx];
  148. mResolvePostEffect->process( state, inOutTex, &mTarget.getViewport() );
  149. }
  150. }
  151. break;
  152. case FTSComplete:
  153. AssertFatal(false, "process() called on a RenderFormatToken which was already complete.");
  154. // fall through
  155. case FTSDisabled:
  156. break;
  157. }
  158. }
  159. void RenderFormatToken::reset()
  160. {
  161. AssertFatal(mFCState != FTSActive, "RenderFormatToken still active during reset()!");
  162. if(mFCState != FTSDisabled)
  163. mFCState = FTSWaiting;
  164. }
  165. void RenderFormatToken::_updateTargets()
  166. {
  167. if ( GFX->getActiveRenderTarget() == NULL )
  168. return;
  169. const Point2I &rtSize = GFX->getActiveRenderTarget()->getSize();
  170. if ( rtSize.x <= mTargetSize.x &&
  171. rtSize.y <= mTargetSize.y &&
  172. !mTargetUpdatePending )
  173. return;
  174. mTargetSize = rtSize;
  175. mTargetUpdatePending = false;
  176. mTargetChainIdx = 0;
  177. for( U32 i = 0; i < TargetChainLength; i++ )
  178. {
  179. if( !mTargetChain[i] )
  180. mTargetChain[i] = GFX->allocRenderToTextureTarget();
  181. // Update color target
  182. if(mColorFormat != GFXFormat_COUNT)
  183. {
  184. // try reuse of old color texture
  185. if( !mTargetColorTexture[i] || mTargetColorTexture[i].getFormat() != mColorFormat
  186. || mTargetColorTexture[i].getWidthHeight() != rtSize)
  187. {
  188. mTargetColorTexture[i].set( rtSize.x, rtSize.y, mColorFormat,
  189. &GFXRenderTargetSRGBProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ),
  190. 1, mTargetAALevel );
  191. mTargetChain[i]->attachTexture( GFXTextureTarget::Color0, mTargetColorTexture[i] );
  192. }
  193. }
  194. // Update depth target
  195. if(mDepthFormat != GFXFormat_COUNT)
  196. {
  197. // try reuse of old depth texture
  198. if( !mTargetDepthStencilTexture[i] || mTargetDepthStencilTexture[i].getFormat() != mColorFormat
  199. || mTargetDepthStencilTexture[i].getWidthHeight() != rtSize)
  200. {
  201. mTargetDepthStencilTexture[i].set( rtSize.x, rtSize.y, mDepthFormat,
  202. &GFXZTargetProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ),
  203. 1, mTargetAALevel );
  204. mTargetChain[i]->attachTexture( GFXTextureTarget::DepthStencil, mTargetDepthStencilTexture[i] );
  205. }
  206. }
  207. }
  208. //set the texture for now as the first color target texture
  209. mTarget.setTexture(mTargetColorTexture[0]);
  210. }
  211. void RenderFormatToken::_teardownTargets()
  212. {
  213. mTarget.release();
  214. for(S32 i = 0; i < TargetChainLength; i++)
  215. {
  216. mTargetColorTexture[i] = NULL;
  217. mTargetDepthStencilTexture[i] = NULL;
  218. mTargetChain[i] = NULL;
  219. }
  220. }
  221. bool RenderFormatToken::_setFmt( void *object, const char *index, const char *data )
  222. {
  223. // Flag update pending
  224. reinterpret_cast<RenderFormatToken *>( object )->mTargetUpdatePending = true;
  225. // Allow console system to assign value
  226. return true;
  227. }
  228. const char* RenderFormatToken::_getCopyPostEffect( void* object, const char* data )
  229. {
  230. RenderFormatToken* token = reinterpret_cast< RenderFormatToken* >( object );
  231. if( token->mCopyPostEffect.isValid() )
  232. return token->mCopyPostEffect->getIdString();
  233. return "0";
  234. }
  235. const char* RenderFormatToken::_getResolvePostEffect( void* object, const char* data )
  236. {
  237. RenderFormatToken* token = reinterpret_cast< RenderFormatToken* >( object );
  238. if( token->mResolvePostEffect.isValid() )
  239. return token->mResolvePostEffect->getIdString();
  240. return "0";
  241. }
  242. bool RenderFormatToken::_setCopyPostEffect( void* object, const char* index, const char* data )
  243. {
  244. RenderFormatToken* token = reinterpret_cast< RenderFormatToken* >( object );
  245. PostEffect* effect;
  246. Sim::findObject( data, effect );
  247. token->mCopyPostEffect = effect;
  248. return false;
  249. }
  250. bool RenderFormatToken::_setResolvePostEffect( void* object, const char* index, const char* data )
  251. {
  252. RenderFormatToken* token = reinterpret_cast< RenderFormatToken* >( object );
  253. PostEffect* effect;
  254. Sim::findObject( data, effect );
  255. token->mResolvePostEffect = effect;
  256. return false;
  257. }
  258. void RenderFormatToken::enable( bool enabled /*= true*/ )
  259. {
  260. AssertFatal(mFCState != FTSActive, "RenderFormatToken is active, cannot change state now!");
  261. if(enabled)
  262. mFCState = FTSWaiting;
  263. else
  264. mFCState = FTSDisabled;
  265. }
  266. bool RenderFormatToken::isEnabled() const
  267. {
  268. return (mFCState != FTSDisabled);
  269. }
  270. void RenderFormatToken::initPersistFields()
  271. {
  272. addProtectedField("format", TypeGFXFormat, Offset(mColorFormat, RenderFormatToken), &_setFmt, &defaultProtectedGetFn,
  273. "Sets the color buffer format for this token.");
  274. addProtectedField("depthFormat", TypeGFXFormat, Offset(mDepthFormat, RenderFormatToken), &_setFmt, &defaultProtectedGetFn,
  275. "Sets the depth/stencil buffer format for this token.");
  276. addProtectedField("copyEffect", TYPEID<PostEffect>(), Offset(mCopyPostEffect, RenderFormatToken),
  277. &_setCopyPostEffect, &_getCopyPostEffect,
  278. "This PostEffect will be run when the render target is changed to the format specified "
  279. "by this token. It is used to copy/format data into the token rendertarget");
  280. addProtectedField("resolveEffect", TYPEID<PostEffect>(), Offset(mResolvePostEffect, RenderFormatToken),
  281. &_setResolvePostEffect, &_getResolvePostEffect,
  282. "This PostEffect will be run when the render target is changed back to the format "
  283. "active prior to this token. It is used to copy/format data from the token rendertarget to the backbuffer.");
  284. addField("aaLevel", TypeS32, Offset(mTargetAALevel, RenderFormatToken),
  285. "Anti-ailiasing level for the this token. 0 disables, -1 uses adapter default.");
  286. Parent::initPersistFields();
  287. }
  288. bool RenderFormatToken::_handleGFXEvent(GFXDevice::GFXDeviceEventType event_)
  289. {
  290. if ( event_ == GFXDevice::deStartOfFrame )
  291. {
  292. mTargetChainIdx++;
  293. if ( mTargetChainIdx >= TargetChainLength )
  294. mTargetChainIdx = 0;
  295. }
  296. return true;
  297. }
  298. void RenderFormatToken::_onTextureEvent( GFXTexCallbackCode code )
  299. {
  300. if(code == GFXZombify)
  301. {
  302. _teardownTargets();
  303. mTargetUpdatePending = true;
  304. }
  305. }
  306. bool RenderFormatToken::onAdd()
  307. {
  308. if(!Parent::onAdd())
  309. return false;
  310. mTarget.registerWithName( getName() );
  311. mTarget.setSamplerState( GFXSamplerStateDesc::getClampPoint() );
  312. return true;
  313. }
  314. void RenderFormatToken::onRemove()
  315. {
  316. mTarget.unregister();
  317. mTarget.release();
  318. Parent::onRemove();
  319. }