3
0

RenderGraph.cpp 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "RenderGraph.h"
  9. #include "UiRenderer.h"
  10. #include <Atom/RPI.Public/Image/ImageSystemInterface.h>
  11. #include <Atom/RHI/RHISystemInterface.h>
  12. #include <AzCore/Math/MatrixUtils.h>
  13. #ifndef _RELEASE
  14. #include <AzCore/Asset/AssetManagerBus.h>
  15. #include <AzCore/std/time.h>
  16. #include <AzFramework/IO/LocalFileIO.h>
  17. #endif
  18. namespace LyShine
  19. {
  20. enum UiColorOp
  21. {
  22. ColorOp_Unused = 0, // reusing shader flag value, FixedPipelineEmu shader uses 0 to mean eCO_NOSET
  23. ColorOp_Normal = 1, // reusing shader flag value, FixedPipelineEmu shader uses 1 to mean eCO_DISABLE
  24. ColorOp_PreMultiplyAlpha = 2 // reusing shader flag value, FixedPipelineEmu shader uses 2 to mean eCO_REPLACE
  25. };
  26. enum UiAlphaOp
  27. {
  28. AlphaOp_Unused = 0, // reusing shader flag value, FixedPipelineEmu shader uses 0 to mean eCO_NOSET
  29. AlphaOp_Normal = 1, // reusing shader flag value, FixedPipelineEmu shader uses 1 to mean eCO_DISABLE
  30. AlphaOp_ModulateAlpha = 2, // reusing shader flag value, FixedPipelineEmu shader uses 2 to mean eCO_REPLACE
  31. AlphaOp_ModulateAlphaAndColor = 3 // reusing shader flag value, FixedPipelineEmu shader uses 3 to mean eCO_DECAL
  32. };
  33. ////////////////////////////////////////////////////////////////////////////////////////////////////
  34. PrimitiveListRenderNode::PrimitiveListRenderNode(const AZ::Data::Instance<AZ::RPI::Image>& texture,
  35. bool isClampTextureMode, bool isTextureSRGB, bool preMultiplyAlpha, const AZ::RHI::TargetBlendState& blendModeState)
  36. : RenderNode(RenderNodeType::PrimitiveList)
  37. , m_numTextures(1)
  38. , m_isTextureSRGB(isTextureSRGB)
  39. , m_preMultiplyAlpha(preMultiplyAlpha)
  40. , m_alphaMaskType(AlphaMaskType::None)
  41. , m_blendModeState(blendModeState)
  42. , m_totalNumVertices(0)
  43. , m_totalNumIndices(0)
  44. {
  45. m_textures[0].m_texture = texture;
  46. m_textures[0].m_isClampTextureMode = isClampTextureMode;
  47. }
  48. ////////////////////////////////////////////////////////////////////////////////////////////////////
  49. PrimitiveListRenderNode::PrimitiveListRenderNode(const AZ::Data::Instance<AZ::RPI::Image>& texture,
  50. const AZ::Data::Instance<AZ::RPI::Image>& maskTexture, bool isClampTextureMode, bool isTextureSRGB,
  51. bool preMultiplyAlpha, AlphaMaskType alphaMaskType, const AZ::RHI::TargetBlendState& blendModeState)
  52. : RenderNode(RenderNodeType::PrimitiveList)
  53. , m_numTextures(2)
  54. , m_isTextureSRGB(isTextureSRGB)
  55. , m_preMultiplyAlpha(preMultiplyAlpha)
  56. , m_alphaMaskType(alphaMaskType)
  57. , m_blendModeState(blendModeState)
  58. , m_totalNumVertices(0)
  59. , m_totalNumIndices(0)
  60. {
  61. m_textures[0].m_texture = texture;
  62. m_textures[0].m_isClampTextureMode = isClampTextureMode;
  63. m_textures[1].m_texture = maskTexture;
  64. m_textures[1].m_isClampTextureMode = isClampTextureMode;
  65. }
  66. ////////////////////////////////////////////////////////////////////////////////////////////////////
  67. PrimitiveListRenderNode::~PrimitiveListRenderNode()
  68. {
  69. m_primitives.clear();
  70. }
  71. ////////////////////////////////////////////////////////////////////////////////////////////////////
  72. void PrimitiveListRenderNode::Render(UiRenderer* uiRenderer
  73. , const AZ::Matrix4x4& modelViewProjMat
  74. , AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw)
  75. {
  76. if (!uiRenderer->IsReady())
  77. {
  78. return;
  79. }
  80. UiRenderer::BaseState curBaseState = uiRenderer->GetBaseState();
  81. UiRenderer::BaseState prevBaseState = curBaseState;
  82. if (m_isTextureSRGB)
  83. {
  84. curBaseState.m_srgbWrite = false;
  85. }
  86. if (m_alphaMaskType == AlphaMaskType::ModulateAlpha)
  87. {
  88. curBaseState.m_modulateAlpha = true;
  89. }
  90. uiRenderer->SetBaseState(curBaseState);
  91. const UiRenderer::UiShaderData& uiShaderData = uiRenderer->GetUiShaderData();
  92. dynamicDraw->SetStencilState(uiRenderer->GetBaseState().m_stencilState);
  93. // The blend factor and op is stored in m_blendModeState when the primitive is added to the graph.
  94. // That is also when the graph determines whether a new primitive list node is needed.
  95. // The rest of the blend properties are assigned during the render calls, so they get merged here
  96. // and all are passed to the dynamic draw context
  97. AZ::RHI::TargetBlendState targetBlendState = m_blendModeState;
  98. targetBlendState.m_enable = uiRenderer->GetBaseState().m_blendStateEnabled;
  99. targetBlendState.m_writeMask = uiRenderer->GetBaseState().m_blendStateWriteMask;
  100. dynamicDraw->SetTarget0BlendState(targetBlendState);
  101. dynamicDraw->SetShaderVariant(uiRenderer->GetCurrentShaderVariant());
  102. // Set up per draw SRG
  103. AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> drawSrg = dynamicDraw->NewDrawSrg();
  104. // Set textures
  105. uint32_t isClampTextureMode = 0;
  106. for (int i = 0; i < m_numTextures; ++i)
  107. {
  108. // Default to white texture
  109. const AZ::Data::Instance<AZ::RPI::Image>& image = m_textures[i].m_texture ? m_textures[i].m_texture
  110. : AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White);
  111. const AZ::RHI::ImageView* imageView = image->GetImageView();
  112. if (imageView)
  113. {
  114. drawSrg->SetImageView(uiShaderData.m_imageInputIndex, imageView, i);
  115. if (m_textures[i].m_isClampTextureMode)
  116. {
  117. isClampTextureMode |= (1 << i);
  118. }
  119. #ifndef _RELEASE
  120. uiRenderer->DebugUseTexture(image);
  121. #endif
  122. }
  123. }
  124. // Set sampler state per texture
  125. drawSrg->SetConstant(uiShaderData.m_isClampInputIndex, isClampTextureMode);
  126. // Set projection matrix
  127. drawSrg->SetConstant(uiShaderData.m_viewProjInputIndex, modelViewProjMat);
  128. drawSrg->Compile();
  129. // Add the indexed primitives to the dynamic draw context for drawing
  130. //
  131. // [LYSHINE_ATOM_TODO][ATOM-15073] Combine into a single DrawIndexed call to take advantage of the draw call
  132. // optimization done by this RenderGraph. This option will be added to DynamicDrawContext. For
  133. // now we could combine the vertices ourselves
  134. for (const LyShine::UiPrimitive& primitive : m_primitives)
  135. {
  136. dynamicDraw->DrawIndexed(primitive.m_vertices, primitive.m_numVertices, primitive.m_indices, primitive.m_numIndices, AZ::RHI::IndexFormat::Uint16, drawSrg);
  137. }
  138. uiRenderer->SetBaseState(prevBaseState);
  139. }
  140. ////////////////////////////////////////////////////////////////////////////////////////////////////
  141. void PrimitiveListRenderNode::AddPrimitive(LyShine::UiPrimitive* primitive)
  142. {
  143. // always clear the next pointer before adding to list
  144. primitive->m_next = nullptr;
  145. m_primitives.push_back(*primitive);
  146. m_totalNumVertices += primitive->m_numVertices;
  147. m_totalNumIndices += primitive->m_numIndices;
  148. }
  149. ////////////////////////////////////////////////////////////////////////////////////////////////////
  150. LyShine::UiPrimitiveList& PrimitiveListRenderNode::GetPrimitives() const
  151. {
  152. return const_cast<LyShine::UiPrimitiveList&>(m_primitives);
  153. }
  154. ////////////////////////////////////////////////////////////////////////////////////////////////////
  155. int PrimitiveListRenderNode::GetOrAddTexture(const AZ::Data::Instance<AZ::RPI::Image>& texture, bool isClampTextureMode)
  156. {
  157. // Check if node is already using this texture
  158. int texUnit = FindTexture(texture, isClampTextureMode);
  159. // render node is not already using this texture, if there is space to add a texture do so
  160. if (texUnit == -1 && m_numTextures < PrimitiveListRenderNode::MaxTextures)
  161. {
  162. texUnit = m_numTextures;
  163. m_textures[texUnit].m_texture = texture;
  164. m_textures[texUnit].m_isClampTextureMode = isClampTextureMode;
  165. m_numTextures++;
  166. }
  167. return texUnit;
  168. }
  169. ////////////////////////////////////////////////////////////////////////////////////////////////////
  170. bool PrimitiveListRenderNode::HasSpaceToAddPrimitive(LyShine::UiPrimitive* primitive) const
  171. {
  172. return primitive->m_numVertices + m_totalNumVertices < std::numeric_limits<uint16>::max();
  173. }
  174. ////////////////////////////////////////////////////////////////////////////////////////////////////
  175. int PrimitiveListRenderNode::FindTexture(const AZ::Data::Instance<AZ::RPI::Image>& texture, bool isClampTextureMode) const
  176. {
  177. for (int i = 0; i < m_numTextures; ++i)
  178. {
  179. if (m_textures[i].m_texture == texture && m_textures[i].m_isClampTextureMode == isClampTextureMode)
  180. {
  181. return i; // texture is already in the list
  182. }
  183. }
  184. return -1;
  185. }
  186. #ifndef _RELEASE
  187. ////////////////////////////////////////////////////////////////////////////////////////////////////
  188. void PrimitiveListRenderNode::ValidateNode()
  189. {
  190. size_t numPrims = m_primitives.size();
  191. size_t primCount = 0;
  192. const LyShine::UiPrimitive* lastPrim = nullptr;
  193. int highestTexUnit = 0;
  194. for (const LyShine::UiPrimitive& primitive : m_primitives)
  195. {
  196. if (primCount > numPrims)
  197. {
  198. AZ_Error("UI", false, "There are more primitives in the m_primitives slist than m_primitives.size (%d)", numPrims)
  199. }
  200. primCount++;
  201. lastPrim = &primitive;
  202. if (primitive.m_vertices[0].texIndex > highestTexUnit)
  203. {
  204. highestTexUnit = primitive.m_vertices[0].texIndex;
  205. }
  206. }
  207. if (m_numTextures != highestTexUnit+1)
  208. {
  209. AZ_Error("UI", false, "m_numTextures (%d) is not highestTexUnit+1 (%d)", m_numTextures, highestTexUnit+1)
  210. }
  211. if (numPrims > 0 && lastPrim != &*m_primitives.last())
  212. {
  213. AZ_Error("UI", false, "lastPrim is not the same as last node")
  214. }
  215. }
  216. #endif
  217. ////////////////////////////////////////////////////////////////////////////////////////////////////
  218. MaskRenderNode::MaskRenderNode(MaskRenderNode* parentMask, bool isMaskingEnabled, bool useAlphaTest, bool drawBehind, bool drawInFront)
  219. : RenderNode(RenderNodeType::Mask)
  220. , m_parentMask(parentMask)
  221. , m_isMaskingEnabled(isMaskingEnabled)
  222. , m_useAlphaTest(useAlphaTest)
  223. , m_drawBehind(drawBehind)
  224. , m_drawInFront(drawInFront)
  225. {
  226. }
  227. ////////////////////////////////////////////////////////////////////////////////////////////////////
  228. MaskRenderNode::~MaskRenderNode()
  229. {
  230. for (RenderNode* renderNode : m_contentRenderNodes)
  231. {
  232. delete renderNode;
  233. }
  234. m_contentRenderNodes.clear();
  235. for (RenderNode* renderNode : m_maskRenderNodes)
  236. {
  237. AZ_Assert(renderNode->GetType() != RenderNodeType::Mask, "There cannot be mask render nodes in the mask visual");
  238. delete renderNode;
  239. }
  240. m_maskRenderNodes.clear();
  241. }
  242. ////////////////////////////////////////////////////////////////////////////////////////////////////
  243. void MaskRenderNode::Render(UiRenderer* uiRenderer
  244. , const AZ::Matrix4x4& modelViewProjMat
  245. , AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw)
  246. {
  247. UiRenderer::BaseState priorBaseState = uiRenderer->GetBaseState();
  248. if (m_isMaskingEnabled || m_drawBehind)
  249. {
  250. SetupBeforeRenderingMask(uiRenderer, dynamicDraw, true, priorBaseState);
  251. for (RenderNode* renderNode : m_maskRenderNodes)
  252. {
  253. renderNode->Render(uiRenderer, modelViewProjMat, dynamicDraw);
  254. }
  255. SetupAfterRenderingMask(uiRenderer, dynamicDraw, true, priorBaseState);
  256. }
  257. for (RenderNode* renderNode : m_contentRenderNodes)
  258. {
  259. renderNode->Render(uiRenderer, modelViewProjMat, dynamicDraw);
  260. }
  261. if (m_isMaskingEnabled || m_drawInFront)
  262. {
  263. SetupBeforeRenderingMask(uiRenderer, dynamicDraw, false, priorBaseState);
  264. for (RenderNode* renderNode : m_maskRenderNodes)
  265. {
  266. renderNode->Render(uiRenderer, modelViewProjMat, dynamicDraw);
  267. }
  268. SetupAfterRenderingMask(uiRenderer, dynamicDraw, false, priorBaseState);
  269. }
  270. }
  271. ////////////////////////////////////////////////////////////////////////////////////////////////////
  272. bool MaskRenderNode::IsMaskRedundant()
  273. {
  274. // if there are no content nodes then there is no point rendering anything for the mask primitives
  275. // unless the mask primitives are non-empty and we are visually drawing the mask primitives in front or
  276. // behind of the children.
  277. if (m_contentRenderNodes.empty() &&
  278. (m_maskRenderNodes.empty() || (!m_drawBehind && !m_drawInFront)))
  279. {
  280. return true;
  281. }
  282. else
  283. {
  284. return false;
  285. }
  286. }
  287. #ifndef _RELEASE
  288. ////////////////////////////////////////////////////////////////////////////////////////////////////
  289. void MaskRenderNode::ValidateNode()
  290. {
  291. for (RenderNode* renderNode : m_maskRenderNodes)
  292. {
  293. renderNode->ValidateNode();
  294. }
  295. for (RenderNode* renderNode : m_contentRenderNodes)
  296. {
  297. renderNode->ValidateNode();
  298. }
  299. }
  300. #endif
  301. ////////////////////////////////////////////////////////////////////////////////////////////////////
  302. void MaskRenderNode::SetupBeforeRenderingMask(UiRenderer* uiRenderer,
  303. AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw,
  304. bool firstPass, UiRenderer::BaseState priorBaseState)
  305. {
  306. UiRenderer::BaseState curBaseState = priorBaseState;
  307. // If using alpha test for drawing the renderable components on this element then we turn on
  308. // alpha test as a pre-render step
  309. curBaseState.m_useAlphaTest = m_useAlphaTest;
  310. // if either of the draw flags are checked then we may want to draw the renderable component(s)
  311. // on this element, otherwise use the color mask to stop them rendering
  312. curBaseState.m_blendStateEnabled = false;
  313. curBaseState.m_blendStateWriteMask = 0x0;
  314. if ((m_drawBehind && firstPass) ||
  315. (m_drawInFront && !firstPass))
  316. {
  317. curBaseState.m_blendStateEnabled = true;
  318. curBaseState.m_blendStateWriteMask = 0xF;
  319. }
  320. if (m_isMaskingEnabled)
  321. {
  322. AZ::RHI::StencilOpState stencilOpState;
  323. stencilOpState.m_func = AZ::RHI::ComparisonFunc::Equal;
  324. // masking is enabled so we want to setup to increment (first pass) or decrement (second pass)
  325. // the stencil buff when rendering the renderable component(s) on this element
  326. if (firstPass)
  327. {
  328. stencilOpState.m_passOp = AZ::RHI::StencilOp::Increment;
  329. }
  330. else
  331. {
  332. stencilOpState.m_passOp = AZ::RHI::StencilOp::Decrement;
  333. }
  334. curBaseState.m_stencilState.m_frontFace = stencilOpState;
  335. curBaseState.m_stencilState.m_backFace = stencilOpState;
  336. // set up for stencil write
  337. dynamicDraw->SetStencilReference(static_cast<uint8_t>(uiRenderer->GetStencilRef()));
  338. curBaseState.m_stencilState.m_enable = true;
  339. curBaseState.m_stencilState.m_writeMask = 0xFF;
  340. }
  341. else
  342. {
  343. // masking is not enabled
  344. curBaseState.m_stencilState.m_enable = false;
  345. }
  346. uiRenderer->SetBaseState(curBaseState);
  347. }
  348. ////////////////////////////////////////////////////////////////////////////////////////////////////
  349. void MaskRenderNode::SetupAfterRenderingMask(UiRenderer* uiRenderer,
  350. AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw,
  351. bool firstPass, UiRenderer::BaseState priorBaseState)
  352. {
  353. if (m_isMaskingEnabled)
  354. {
  355. // Masking is enabled so on the first pass we want to increment the stencil ref stored
  356. // in the UiRenderer and used by all normal rendering, this is so that it matches the
  357. // increments to the stencil buffer that we have just done by rendering the mask.
  358. // On the second pass we want to decrement the stencil ref so it is back to what it
  359. // was before rendering the normal children of this mask element.
  360. if (firstPass)
  361. {
  362. uiRenderer->IncrementStencilRef();
  363. }
  364. else
  365. {
  366. uiRenderer->DecrementStencilRef();
  367. }
  368. dynamicDraw->SetStencilReference(static_cast<uint8_t>(uiRenderer->GetStencilRef()));
  369. if (firstPass)
  370. {
  371. UiRenderer::BaseState curBaseState = priorBaseState;
  372. // turn off stencil write and turn on stencil test
  373. curBaseState.m_stencilState.m_enable = true;
  374. curBaseState.m_stencilState.m_writeMask = 0x00;
  375. AZ::RHI::StencilOpState stencilOpState;
  376. stencilOpState.m_func = AZ::RHI::ComparisonFunc::Equal;
  377. curBaseState.m_stencilState.m_frontFace = stencilOpState;
  378. curBaseState.m_stencilState.m_backFace = stencilOpState;
  379. uiRenderer->SetBaseState(curBaseState);
  380. }
  381. else
  382. {
  383. // second pass, set base state back to what it was before rendering this element
  384. uiRenderer->SetBaseState(priorBaseState);
  385. }
  386. }
  387. else
  388. {
  389. // masking is not enabled
  390. // remove any color mask or alpha test that we set in pre-render
  391. uiRenderer->SetBaseState(priorBaseState);
  392. }
  393. }
  394. ////////////////////////////////////////////////////////////////////////////////////////////////////
  395. RenderTargetRenderNode::RenderTargetRenderNode(
  396. RenderTargetRenderNode* parentRenderTarget,
  397. AZ::Data::Instance<AZ::RPI::AttachmentImage> attachmentImage,
  398. const AZ::Vector2& viewportTopLeft,
  399. const AZ::Vector2& viewportSize,
  400. const AZ::Color& clearColor,
  401. int nestLevel)
  402. : RenderNode(RenderNodeType::RenderTarget)
  403. , m_parentRenderTarget(parentRenderTarget)
  404. , m_attachmentImage(attachmentImage)
  405. , m_viewportX(viewportTopLeft.GetX())
  406. , m_viewportY(viewportTopLeft.GetY())
  407. , m_viewportWidth(viewportSize.GetX())
  408. , m_viewportHeight(viewportSize.GetY())
  409. , m_clearColor(clearColor)
  410. , m_nestLevel(nestLevel)
  411. {
  412. AZ::MakeOrthographicMatrixRH(m_modelViewProjMat,
  413. m_viewportX,
  414. m_viewportX + m_viewportWidth,
  415. m_viewportY + m_viewportHeight,
  416. m_viewportY,
  417. 0.0f,
  418. 1.0f);
  419. }
  420. ////////////////////////////////////////////////////////////////////////////////////////////////////
  421. RenderTargetRenderNode::~RenderTargetRenderNode()
  422. {
  423. for (RenderNode* renderNode : m_childRenderNodes)
  424. {
  425. delete renderNode;
  426. }
  427. m_childRenderNodes.clear();
  428. }
  429. ////////////////////////////////////////////////////////////////////////////////////////////////////
  430. void RenderTargetRenderNode::Render(UiRenderer* uiRenderer
  431. , [[maybe_unused]] const AZ::Matrix4x4& modelViewProjMat
  432. , [[maybe_unused]] AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw)
  433. {
  434. if (!m_attachmentImage)
  435. {
  436. return;
  437. }
  438. ISystem* system = gEnv->pSystem;
  439. if (system && !gEnv->IsDedicated())
  440. {
  441. // Use a dedicated dynamic draw context for rendering to the texture since it can only have one draw list tag
  442. if (!m_dynamicDraw)
  443. {
  444. m_dynamicDraw = uiRenderer->CreateDynamicDrawContextForRTT(GetRenderTargetName());
  445. if (m_dynamicDraw)
  446. {
  447. m_dynamicDraw->SetViewport(AZ::RHI::Viewport(0.0f, m_viewportWidth, 0.0f, m_viewportHeight));
  448. }
  449. }
  450. if (m_dynamicDraw)
  451. {
  452. for (RenderNode* renderNode : m_childRenderNodes)
  453. {
  454. renderNode->Render(uiRenderer, m_modelViewProjMat, m_dynamicDraw);
  455. }
  456. }
  457. else
  458. {
  459. AZ_WarningOnce("UI", false, "Failed to create a Dynamic Draw Context for UI Element's render target. "\
  460. "Please ensure that the custom LyShinePass has been added to the project's main render pipeline.");
  461. }
  462. }
  463. }
  464. ////////////////////////////////////////////////////////////////////////////////////////////////////
  465. const char* RenderTargetRenderNode::GetRenderTargetName() const
  466. {
  467. return m_attachmentImage ? m_attachmentImage->GetRHIImage()->GetName().GetCStr() : "";
  468. }
  469. ////////////////////////////////////////////////////////////////////////////////////////////////////
  470. int RenderTargetRenderNode::GetNestLevel() const
  471. {
  472. return m_nestLevel;
  473. }
  474. ////////////////////////////////////////////////////////////////////////////////////////////////////
  475. const AZ::Data::Instance<AZ::RPI::AttachmentImage> RenderTargetRenderNode::GetRenderTarget() const
  476. {
  477. return m_attachmentImage;
  478. }
  479. #ifndef _RELEASE
  480. ////////////////////////////////////////////////////////////////////////////////////////////////////
  481. void RenderTargetRenderNode::ValidateNode()
  482. {
  483. for (RenderNode* renderNode : m_childRenderNodes)
  484. {
  485. renderNode->ValidateNode();
  486. }
  487. }
  488. #endif
  489. ////////////////////////////////////////////////////////////////////////////////////////////////////
  490. bool RenderTargetRenderNode::CompareNestLevelForSort(RenderTargetRenderNode* a, RenderTargetRenderNode*b)
  491. {
  492. // elements with higher nest levels should be rendered first so they should be considered "less than"
  493. // for the sort
  494. return a->m_nestLevel > b->m_nestLevel;
  495. }
  496. ////////////////////////////////////////////////////////////////////////////////////////////////////
  497. RenderGraph::RenderGraph()
  498. {
  499. // we keep track of the list of render nodes that new nodes should be added to. Initially
  500. // it is the main, top-level list of nodes. If we start defining a mask or render to texture
  501. // then it becomes the node list for that render node.
  502. m_renderNodeListStack.push(&m_renderNodes);
  503. }
  504. ////////////////////////////////////////////////////////////////////////////////////////////////////
  505. RenderGraph::~RenderGraph()
  506. {
  507. ResetGraph();
  508. }
  509. ////////////////////////////////////////////////////////////////////////////////////////////////////
  510. void RenderGraph::ResetGraph()
  511. {
  512. // clear and delete the list of render target nodes
  513. for (RenderNode* renderNode : m_renderTargetRenderNodes)
  514. {
  515. delete renderNode;
  516. }
  517. m_renderTargetRenderNodes.clear();
  518. // clear and delete the list of render nodes
  519. for (RenderNode* renderNode : m_renderNodes)
  520. {
  521. delete renderNode;
  522. }
  523. m_renderNodes.clear();
  524. // clear and delete the dynamic quads
  525. for (DynamicQuad* quad : m_dynamicQuads)
  526. {
  527. delete quad;
  528. }
  529. m_dynamicQuads.clear();
  530. m_currentMask = nullptr;
  531. m_currentRenderTarget = nullptr;
  532. // clear the render node list stack and reset it to be the top level node list
  533. while (!m_renderNodeListStack.empty())
  534. {
  535. m_renderNodeListStack.pop();
  536. }
  537. m_renderNodeListStack.push(&m_renderNodes);
  538. m_isDirty = true;
  539. m_renderToRenderTargetCount = 0;
  540. #ifndef _RELEASE
  541. m_wasBuiltThisFrame = true;
  542. m_timeGraphLastBuiltMs = AZStd::GetTimeUTCMilliSecond();
  543. #endif
  544. }
  545. ////////////////////////////////////////////////////////////////////////////////////////////////////
  546. void RenderGraph::BeginMask(bool isMaskingEnabled, bool useAlphaTest, bool drawBehind, bool drawInFront)
  547. {
  548. // this uses pool allocator
  549. MaskRenderNode* maskRenderNode = new MaskRenderNode(m_currentMask, isMaskingEnabled, useAlphaTest, drawBehind, drawInFront);
  550. m_currentMask = maskRenderNode;
  551. m_renderNodeListStack.push(&maskRenderNode->GetMaskRenderNodeList());
  552. }
  553. ////////////////////////////////////////////////////////////////////////////////////////////////////
  554. void RenderGraph::StartChildrenForMask()
  555. {
  556. AZ_Assert(m_currentMask, "Calling StartChildrenForMask while not defining a mask");
  557. m_renderNodeListStack.pop();
  558. m_renderNodeListStack.push(&m_currentMask->GetContentRenderNodeList());
  559. }
  560. ////////////////////////////////////////////////////////////////////////////////////////////////////
  561. void RenderGraph::EndMask()
  562. {
  563. AZ_Assert(m_currentMask, "Calling EndMask while not defining a mask");
  564. if (m_currentMask)
  565. {
  566. MaskRenderNode* newMaskRenderNode = m_currentMask;
  567. m_currentMask = m_currentMask->GetParentMask();
  568. // pop off the mask's content render node list
  569. m_renderNodeListStack.pop();
  570. if (newMaskRenderNode->IsMaskRedundant())
  571. {
  572. // We don't know the mask is redundant until we have created this node and found that it hasn't got
  573. // child nodes. This is not common but does happen sometimes when all the children are currently disabled.
  574. delete newMaskRenderNode;
  575. }
  576. else
  577. {
  578. m_renderNodeListStack.top()->push_back(newMaskRenderNode);
  579. }
  580. }
  581. }
  582. ////////////////////////////////////////////////////////////////////////////////////////////////////
  583. void RenderGraph::BeginRenderToTexture(AZ::Data::Instance<AZ::RPI::AttachmentImage> attachmentImage,
  584. const AZ::Vector2& viewportTopLeft, const AZ::Vector2& viewportSize, const AZ::Color& clearColor)
  585. {
  586. // this uses pool allocator
  587. RenderTargetRenderNode* renderTargetRenderNode = new RenderTargetRenderNode(
  588. m_currentRenderTarget, attachmentImage,
  589. viewportTopLeft, viewportSize, clearColor, m_renderTargetNestLevel);
  590. m_currentRenderTarget = renderTargetRenderNode;
  591. m_renderNodeListStack.push(&m_currentRenderTarget->GetChildRenderNodeList());
  592. m_renderTargetNestLevel++;
  593. }
  594. ////////////////////////////////////////////////////////////////////////////////////////////////////
  595. void RenderGraph::EndRenderToTexture()
  596. {
  597. AZ_Assert(m_currentRenderTarget, "Calling EndRenderToTexture while not defining a render target node");
  598. if (m_currentRenderTarget)
  599. {
  600. RenderTargetRenderNode* newRenderTargetRenderNode = m_currentRenderTarget;
  601. m_currentRenderTarget = m_currentRenderTarget->GetParentRenderTarget();
  602. // we don't add this node to the normal list of render nodes since it is rendered before
  603. // the main render for the render graph
  604. m_renderTargetRenderNodes.push_back(newRenderTargetRenderNode);
  605. m_renderNodeListStack.pop();
  606. m_renderTargetNestLevel--;
  607. }
  608. }
  609. ////////////////////////////////////////////////////////////////////////////////////////////////////
  610. void RenderGraph::AddPrimitive(LyShine::UiPrimitive* primitive, const AZ::Data::Instance<AZ::RPI::Image>& texture,
  611. bool isClampTextureMode, bool isTextureSRGB, bool isTexturePremultipliedAlpha, BlendMode blendMode)
  612. {
  613. AZStd::vector<RenderNode*>* renderNodeList = m_renderNodeListStack.top();
  614. int texUnit = -1;
  615. if (renderNodeList)
  616. {
  617. // we want to pre-multiply alpha if we are rendering to a render target AND we are not rendering from a render target
  618. bool isPreMultiplyAlpha = m_renderTargetNestLevel > 0 && !isTexturePremultipliedAlpha;
  619. // given the blend mode get the right state, the state depends on whether the shader is outputing premultiplied alpha.
  620. // The shader can be outputing premultiplied alpha EITHER if the input texture is premultiplied alpha OR if the
  621. // shader is doing the premultiply of the output color
  622. bool isShaderOutputPremultAlpha = isPreMultiplyAlpha || isTexturePremultipliedAlpha;
  623. AZ::RHI::TargetBlendState blendModeState = GetBlendModeState(blendMode, isShaderOutputPremultAlpha);
  624. PrimitiveListRenderNode* renderNodeToAddTo = nullptr;
  625. if (!renderNodeList->empty())
  626. {
  627. RenderNode* lastRenderNode = renderNodeList->back();
  628. if (lastRenderNode && lastRenderNode->GetType() == RenderNodeType::PrimitiveList)
  629. {
  630. PrimitiveListRenderNode* primListRenderNode = static_cast<PrimitiveListRenderNode*>(lastRenderNode);
  631. // compare render state
  632. if (primListRenderNode->GetIsTextureSRGB() == isTextureSRGB &&
  633. primListRenderNode->GetBlendModeState() == blendModeState &&
  634. primListRenderNode->GetIsPremultiplyAlpha() == isPreMultiplyAlpha &&
  635. primListRenderNode->GetAlphaMaskType() == AlphaMaskType::None &&
  636. primListRenderNode->HasSpaceToAddPrimitive(primitive))
  637. {
  638. // render state is the same - we can add the primitive to this list if the texture is in
  639. // the list or there is space for another texture
  640. texUnit = primListRenderNode->GetOrAddTexture(texture, isClampTextureMode);
  641. if (texUnit != -1)
  642. {
  643. renderNodeToAddTo = primListRenderNode;
  644. }
  645. }
  646. }
  647. }
  648. if (!renderNodeToAddTo)
  649. {
  650. // We can't add this primitive to the existing render node, we need to create a new render node
  651. // this uses a pool allocator for fast allocation
  652. renderNodeToAddTo = new PrimitiveListRenderNode(texture, isClampTextureMode, isTextureSRGB, isPreMultiplyAlpha, blendModeState);
  653. renderNodeList->push_back(renderNodeToAddTo);
  654. texUnit = 0;
  655. }
  656. // Ensure that the vertices are referencing the right texture unit
  657. // Because primitive verts are only created when a UI component changes, they have a longer
  658. // lifetime than the render graph. So if not much has changed since the render graph was last built
  659. // it is quite likely that the verts are already set to use the correct texture unit.
  660. if (primitive->m_vertices[0].texIndex != texUnit)
  661. {
  662. for (int i = 0; i < primitive->m_numVertices; ++i)
  663. {
  664. primitive->m_vertices[i].texIndex = static_cast<uint8>(texUnit);
  665. }
  666. }
  667. // add this primitive to the render node
  668. renderNodeToAddTo->AddPrimitive(primitive);
  669. }
  670. }
  671. ////////////////////////////////////////////////////////////////////////////////////////////////////
  672. void RenderGraph::AddAlphaMaskPrimitive(LyShine::UiPrimitive* primitive,
  673. AZ::Data::Instance<AZ::RPI::AttachmentImage> contentAttachmentImage,
  674. AZ::Data::Instance<AZ::RPI::AttachmentImage> maskAttachmentImage,
  675. bool isClampTextureMode,
  676. bool isTextureSRGB,
  677. bool isTexturePremultipliedAlpha,
  678. BlendMode blendMode)
  679. {
  680. AZStd::vector<RenderNode*>* renderNodeList = m_renderNodeListStack.top();
  681. int texUnit0 = -1;
  682. int texUnit1 = -1;
  683. if (renderNodeList)
  684. {
  685. // we want to pre-multiply alpha if we are rendering to a render target AND we are not rendering from a render target
  686. bool isPreMultiplyAlpha = m_renderTargetNestLevel > 0 && !isTexturePremultipliedAlpha;
  687. // given the blend mode get the right state, the state depends on whether the shader is outputing premultiplied alpha.
  688. // The shader can be outputing premultiplied alpha EITHER if the input texture is premultiplied alpha OR if the
  689. // shader is doing the premultiply of the output color
  690. bool isShaderOutputPremultAlpha = isPreMultiplyAlpha || isTexturePremultipliedAlpha;
  691. AZ::RHI::TargetBlendState blendModeState = GetBlendModeState(blendMode, isShaderOutputPremultAlpha);
  692. AlphaMaskType alphaMaskType = isShaderOutputPremultAlpha ? AlphaMaskType::ModulateAlphaAndColor : AlphaMaskType::ModulateAlpha;
  693. PrimitiveListRenderNode* renderNodeToAddTo = nullptr;
  694. if (!renderNodeList->empty())
  695. {
  696. RenderNode* lastRenderNode = renderNodeList->back();
  697. if (lastRenderNode && lastRenderNode->GetType() == RenderNodeType::PrimitiveList)
  698. {
  699. PrimitiveListRenderNode* primListRenderNode = static_cast<PrimitiveListRenderNode*>(lastRenderNode);
  700. // compare render state
  701. if (primListRenderNode->GetIsTextureSRGB() == isTextureSRGB &&
  702. primListRenderNode->GetBlendModeState() == blendModeState &&
  703. primListRenderNode->GetIsPremultiplyAlpha() == isPreMultiplyAlpha &&
  704. primListRenderNode->GetAlphaMaskType() == alphaMaskType &&
  705. primListRenderNode->HasSpaceToAddPrimitive(primitive))
  706. {
  707. // render state is the same - we can add the primitive to this list if the texture is in
  708. // the list or there is space for another texture
  709. texUnit0 = primListRenderNode->GetOrAddTexture(contentAttachmentImage, true);
  710. texUnit1 = primListRenderNode->GetOrAddTexture(maskAttachmentImage, true);
  711. if (texUnit0 != -1 && texUnit1 != -1)
  712. {
  713. renderNodeToAddTo = primListRenderNode;
  714. }
  715. }
  716. }
  717. }
  718. if (!renderNodeToAddTo)
  719. {
  720. // We can't add this primitive to the existing render node, we need to create a new render node
  721. // this uses a pool allocator for fast allocation
  722. renderNodeToAddTo = new PrimitiveListRenderNode(contentAttachmentImage, maskAttachmentImage,
  723. isClampTextureMode, isTextureSRGB, isPreMultiplyAlpha, alphaMaskType, blendModeState);
  724. renderNodeList->push_back(renderNodeToAddTo);
  725. texUnit0 = 0;
  726. texUnit1 = 1;
  727. }
  728. // Ensure that the vertices are referencing the right texture unit
  729. // Because primitive verts are only created when a UI component changes, they have a longer
  730. // lifetime than the render graph. So if not much has changed since the render graph was last built
  731. // it is quite likely that the verts are already set to use the correct texture unit.
  732. if (primitive->m_vertices[0].texIndex != texUnit0 || primitive->m_vertices[0].texIndex2 != texUnit1)
  733. {
  734. for (int i = 0; i < primitive->m_numVertices; ++i)
  735. {
  736. primitive->m_vertices[i].texIndex = aznumeric_cast<uint8>(texUnit0);
  737. primitive->m_vertices[i].texIndex2 = aznumeric_cast<uint8>(texUnit1);
  738. }
  739. }
  740. // add this primitive to the render node
  741. renderNodeToAddTo->AddPrimitive(primitive);
  742. }
  743. }
  744. ////////////////////////////////////////////////////////////////////////////////////////////////////
  745. LyShine::UiPrimitive* RenderGraph::GetDynamicQuadPrimitive(const AZ::Vector2* positions, uint32 packedColor)
  746. {
  747. const int numVertsInQuad = 4;
  748. const int numIndicesInQuad = 6;
  749. // points are a clockwise quad
  750. static const Vec2 uvs[numVertsInQuad] = { {0, 0}, {1, 0}, {1, 1}, {0, 1} };
  751. static uint16 indices[numIndicesInQuad] = { 0, 1, 2, 2, 3, 0 };
  752. DynamicQuad* quad = new DynamicQuad;
  753. for (int i = 0; i < numVertsInQuad; ++i)
  754. {
  755. quad->m_quadVerts[i].xy = Vec2(positions[i].GetX(), positions[i].GetY());
  756. quad->m_quadVerts[i].color.dcolor = packedColor;
  757. quad->m_quadVerts[i].st = uvs[i];
  758. quad->m_quadVerts[i].texIndex = 0;
  759. quad->m_quadVerts[i].texHasColorChannel = 1;
  760. quad->m_quadVerts[i].texIndex2 = 0;
  761. quad->m_quadVerts[i].pad = 0;
  762. }
  763. quad->m_primitive.m_vertices = quad->m_quadVerts;
  764. quad->m_primitive.m_numVertices = numVertsInQuad;
  765. quad->m_primitive.m_indices = indices;
  766. quad->m_primitive.m_numIndices = numIndicesInQuad;
  767. m_dynamicQuads.push_back(quad);
  768. return &quad->m_primitive;
  769. }
  770. ////////////////////////////////////////////////////////////////////////////////////////////////////
  771. bool RenderGraph::IsRenderingToMask() const
  772. {
  773. return m_isRenderingToMask;
  774. }
  775. ////////////////////////////////////////////////////////////////////////////////////////////////////
  776. void RenderGraph::SetIsRenderingToMask(bool isRenderingToMask)
  777. {
  778. m_isRenderingToMask = isRenderingToMask;
  779. }
  780. ////////////////////////////////////////////////////////////////////////////////////////////////////
  781. void RenderGraph::PushAlphaFade(float alphaFadeValue)
  782. {
  783. float currentAlphaFade = GetAlphaFade();
  784. m_alphaFadeStack.push(alphaFadeValue * currentAlphaFade);
  785. }
  786. ////////////////////////////////////////////////////////////////////////////////////////////////////
  787. void RenderGraph::PushOverrideAlphaFade(float alphaFadeValue)
  788. {
  789. m_alphaFadeStack.push(alphaFadeValue);
  790. }
  791. ////////////////////////////////////////////////////////////////////////////////////////////////////
  792. void RenderGraph::PopAlphaFade()
  793. {
  794. if (!m_alphaFadeStack.empty())
  795. {
  796. m_alphaFadeStack.pop();
  797. }
  798. }
  799. ////////////////////////////////////////////////////////////////////////////////////////////////////
  800. float RenderGraph::GetAlphaFade() const
  801. {
  802. float alphaFade = 1.0f; // by default nothing is faded
  803. if (!m_alphaFadeStack.empty())
  804. {
  805. alphaFade = m_alphaFadeStack.top();
  806. }
  807. return alphaFade;
  808. }
  809. ////////////////////////////////////////////////////////////////////////////////////////////////////
  810. void RenderGraph::Render(UiRenderer* uiRenderer, [[maybe_unused]] const AZ::Vector2& viewportSize)
  811. {
  812. AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = uiRenderer->GetDynamicDrawContext();
  813. // Reset stencil and blend mode to defaults (disable stencil and enable blend/color write)
  814. dynamicDraw->SetStencilState(uiRenderer->GetBaseState().m_stencilState);
  815. AZ::RHI::TargetBlendState defaultBlendModeState = GetBlendModeState(LyShine::BlendMode::Normal, false);
  816. defaultBlendModeState.m_enable = uiRenderer->GetBaseState().m_blendStateEnabled;
  817. defaultBlendModeState.m_writeMask = uiRenderer->GetBaseState().m_blendStateWriteMask;
  818. dynamicDraw->SetTarget0BlendState(defaultBlendModeState);
  819. // LYSHINE_ATOM_TODO - It is currently necessary to render to the targets twice. Needs investigation
  820. // Note, the rtt pass might not be created when the first time the render is called. So we enable rtt pass in both frames when render the node.
  821. constexpr int timesToRenderToRenderTargets = 2;
  822. if (m_renderToRenderTargetCount < timesToRenderToRenderTargets)
  823. {
  824. SetRttPassesEnabled(uiRenderer, true);
  825. for (RenderNode* renderNode : m_renderTargetRenderNodes)
  826. {
  827. renderNode->Render(uiRenderer, uiRenderer->GetModelViewProjectionMatrix(), dynamicDraw);
  828. }
  829. m_renderToRenderTargetCount++;
  830. }
  831. else if (m_renderToRenderTargetCount < timesToRenderToRenderTargets + 1)
  832. {
  833. // Disable the rtt render passes since they don't need to be rendered to until the graph becomes invalidated again.
  834. // This is also necessary to prevent the render targets' contents getting cleared on load by the pass.
  835. SetRttPassesEnabled(uiRenderer, false);
  836. m_renderToRenderTargetCount++;
  837. }
  838. for (RenderNode* renderNode : m_renderNodes)
  839. {
  840. renderNode->Render(uiRenderer, uiRenderer->GetModelViewProjectionMatrix(), dynamicDraw);
  841. }
  842. }
  843. ////////////////////////////////////////////////////////////////////////////////////////////////////
  844. void RenderGraph::SetDirtyFlag(bool isDirty)
  845. {
  846. if (m_isDirty != isDirty)
  847. {
  848. if (isDirty)
  849. {
  850. // when graph first becomes dirty it must be reset since an element may have been deleted
  851. // and the graph contains pointers to DynUiPrimitives owned by components on elements.
  852. ResetGraph();
  853. }
  854. m_isDirty = isDirty;
  855. }
  856. }
  857. ////////////////////////////////////////////////////////////////////////////////////////////////////
  858. bool RenderGraph::GetDirtyFlag()
  859. {
  860. return m_isDirty;
  861. }
  862. ////////////////////////////////////////////////////////////////////////////////////////////////////
  863. void RenderGraph::FinalizeGraph()
  864. {
  865. // sort the render targets so that more deeply nested ones are rendered first
  866. std::sort(m_renderTargetRenderNodes.begin(), m_renderTargetRenderNodes.end(),
  867. RenderTargetRenderNode::CompareNestLevelForSort);
  868. }
  869. ////////////////////////////////////////////////////////////////////////////////////////////////////
  870. bool RenderGraph::IsEmpty()
  871. {
  872. return m_renderNodes.empty() && m_renderTargetRenderNodes.empty();
  873. }
  874. ////////////////////////////////////////////////////////////////////////////////////////////////////
  875. void RenderGraph::GetRenderTargetsAndDependencies(LyShine::AttachmentImagesAndDependencies& attachmentImagesAndDependencies)
  876. {
  877. for (RenderNode* renderNode : m_renderTargetRenderNodes)
  878. {
  879. const RenderTargetRenderNode* renderTargetRenderNode = static_cast<const RenderTargetRenderNode*>(renderNode);
  880. if (renderTargetRenderNode->GetNestLevel() == 0)
  881. {
  882. LyShine::AttachmentImages attachmentImages;
  883. const AZStd::vector<RenderNode*>& childNodeList = renderTargetRenderNode->GetChildRenderNodeList();
  884. for (auto& childNode : childNodeList)
  885. {
  886. if (childNode->GetType() == RenderNodeType::RenderTarget)
  887. {
  888. const RenderTargetRenderNode* childRenderTargetRenderNode = static_cast<const RenderTargetRenderNode*>(childNode);
  889. attachmentImages.emplace_back(childRenderTargetRenderNode->GetRenderTarget());
  890. }
  891. }
  892. attachmentImagesAndDependencies.emplace_back(AttachmentImageAndDependentsPair(renderTargetRenderNode->GetRenderTarget(), attachmentImages));
  893. }
  894. }
  895. }
  896. #ifndef _RELEASE
  897. ////////////////////////////////////////////////////////////////////////////////////////////////////
  898. void RenderGraph::ValidateGraph()
  899. {
  900. for (RenderNode* renderNode : m_renderNodes)
  901. {
  902. renderNode->ValidateNode();
  903. }
  904. }
  905. ////////////////////////////////////////////////////////////////////////////////////////////////////
  906. void RenderGraph::GetDebugInfoRenderGraph(LyShineDebug::DebugInfoRenderGraph& info) const
  907. {
  908. info.m_numPrimitives = 0;
  909. info.m_numRenderNodes = 0;
  910. info.m_numTriangles = 0;
  911. info.m_numUniqueTextures = 0;
  912. info.m_numMasks = 0;
  913. info.m_numRTs = 0;
  914. info.m_numNodesDueToMask = 0;
  915. info.m_numNodesDueToRT = 0;
  916. info.m_numNodesDueToBlendMode = 0;
  917. info.m_numNodesDueToSrgb = 0;
  918. info.m_numNodesDueToMaxVerts = 0;
  919. info.m_numNodesDueToTextures = 0;
  920. info.m_wasBuiltThisFrame = m_wasBuiltThisFrame;
  921. info.m_timeGraphLastBuiltMs = m_timeGraphLastBuiltMs;
  922. info.m_isReusingRenderTargets = m_renderToRenderTargetCount >= 2 && !m_renderTargetRenderNodes.empty();
  923. m_wasBuiltThisFrame = false;
  924. AZStd::set<AZ::Data::Instance<AZ::RPI::Image>> uniqueTextures;
  925. // If we are rendering to the render targets this frame then record the stats for doing that
  926. if (m_renderToRenderTargetCount < 2)
  927. {
  928. for (RenderNode* renderNode : m_renderTargetRenderNodes)
  929. {
  930. const RenderTargetRenderNode* renderTargetRenderNode = static_cast<const RenderTargetRenderNode*>(renderNode);
  931. if (renderTargetRenderNode->GetChildRenderNodeList().size() > 0)
  932. {
  933. info.m_numNodesDueToRT += 1; // there is an extra draw call because these are inside a render target (so can't be combined with those outside)
  934. }
  935. ++info.m_numRTs;
  936. const AZStd::vector<RenderNode*>& childNodeList = renderTargetRenderNode->GetChildRenderNodeList();
  937. // walk the rendertarget's graph recursively to add up all of the data
  938. GetDebugInfoRenderNodeList(childNodeList, info, uniqueTextures);
  939. }
  940. }
  941. // walk the graph recursively to add up all of the data
  942. GetDebugInfoRenderNodeList(m_renderNodes, info, uniqueTextures);
  943. info.m_numUniqueTextures = static_cast<int>(uniqueTextures.size());
  944. }
  945. ////////////////////////////////////////////////////////////////////////////////////////////////////
  946. void RenderGraph::GetDebugInfoRenderNodeList(
  947. const AZStd::vector<RenderNode*>& renderNodeList,
  948. LyShineDebug::DebugInfoRenderGraph& info,
  949. AZStd::set<AZ::Data::Instance<AZ::RPI::Image>>& uniqueTextures) const
  950. {
  951. const PrimitiveListRenderNode* prevPrimListNode = nullptr;
  952. bool isFirstNode = true;
  953. bool wasLastNodeAMask = false;
  954. for (const RenderNode* renderNode : renderNodeList)
  955. {
  956. ++info.m_numRenderNodes;
  957. if (renderNode->GetType() == RenderNodeType::Mask)
  958. {
  959. const MaskRenderNode* maskRenderNode = static_cast<const MaskRenderNode*>(renderNode);
  960. if (maskRenderNode->GetMaskRenderNodeList().size() > 0)
  961. {
  962. info.m_numNodesDueToMask += 1; // there are always 2 draw calls for a mask so the mask adds one even if it is the first element
  963. }
  964. if (maskRenderNode->GetContentRenderNodeList().size() > 0)
  965. {
  966. info.m_numNodesDueToMask += 1; // there is an extra draw call because these are inside a mask (so can't be combined with those outside)
  967. }
  968. if (!isFirstNode)
  969. {
  970. info.m_numNodesDueToMask += 1; // caused a break from the previous due to a mask
  971. }
  972. wasLastNodeAMask = true;
  973. ++info.m_numMasks;
  974. GetDebugInfoRenderNodeList(maskRenderNode->GetContentRenderNodeList(), info, uniqueTextures);
  975. if (maskRenderNode->GetIsMaskingEnabled())
  976. {
  977. GetDebugInfoRenderNodeList(maskRenderNode->GetMaskRenderNodeList(), info, uniqueTextures);
  978. }
  979. prevPrimListNode = nullptr;
  980. }
  981. else if (renderNode->GetType() == RenderNodeType::PrimitiveList)
  982. {
  983. if (wasLastNodeAMask)
  984. {
  985. info.m_numNodesDueToMask += 1; // this could not be combined with the render nodes before the mask
  986. wasLastNodeAMask = false;
  987. }
  988. const PrimitiveListRenderNode* primListRenderNode = static_cast<const PrimitiveListRenderNode*>(renderNode);
  989. LyShine::UiPrimitiveList& primitives = primListRenderNode->GetPrimitives();
  990. info.m_numPrimitives += static_cast<int>(primitives.size());
  991. {
  992. for (const LyShine::UiPrimitive& primitive : primitives)
  993. {
  994. info.m_numTriangles += primitive.m_numIndices / 3;
  995. }
  996. }
  997. for (int i = 0; i < primListRenderNode->GetNumTextures(); ++i)
  998. {
  999. uniqueTextures.insert(primListRenderNode->GetTexture(i));
  1000. }
  1001. if (prevPrimListNode)
  1002. {
  1003. if (!(prevPrimListNode->GetBlendModeState() == primListRenderNode->GetBlendModeState()))
  1004. {
  1005. ++info.m_numNodesDueToBlendMode;
  1006. }
  1007. else if (prevPrimListNode->GetIsTextureSRGB() != primListRenderNode->GetIsTextureSRGB())
  1008. {
  1009. ++info.m_numNodesDueToSrgb;
  1010. }
  1011. else if (!prevPrimListNode->HasSpaceToAddPrimitive(&primListRenderNode->GetPrimitives().front()))
  1012. {
  1013. ++info.m_numNodesDueToMaxVerts;
  1014. }
  1015. else if (prevPrimListNode->GetNumTextures() == PrimitiveListRenderNode::MaxTextures)
  1016. {
  1017. ++info.m_numNodesDueToTextures;
  1018. }
  1019. }
  1020. prevPrimListNode = primListRenderNode;
  1021. }
  1022. isFirstNode = false;
  1023. }
  1024. }
  1025. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1026. void RenderGraph::DebugReportDrawCalls(AZ::IO::HandleType fileHandle, LyShineDebug::DebugInfoDrawCallReport& reportInfo, void* context) const
  1027. {
  1028. if (m_renderNodes.empty())
  1029. {
  1030. AZStd::string logLine = "Rendergraph is empty\r\n";
  1031. AZ::IO::LocalFileIO::GetInstance()->Write(fileHandle, logLine.c_str(), logLine.size());
  1032. }
  1033. else
  1034. {
  1035. // first list the render nodes for creating render targets
  1036. for (const RenderNode* renderNode : m_renderTargetRenderNodes)
  1037. {
  1038. const RenderTargetRenderNode* renderTargetRenderNode = static_cast<const RenderTargetRenderNode*>(renderNode);
  1039. const char* renderTargetName = renderTargetRenderNode->GetRenderTargetName();
  1040. AZ::Color clearColor = renderTargetRenderNode->GetClearColor();
  1041. AZStd::string logLine = AZStd::string::format("RenderTarget %s (ClearColor=(%f,%f,%f), ClearAlpha=%f, Viewport=(%f,%f,%f,%f)) :\r\n",
  1042. renderTargetName,
  1043. static_cast<float>(clearColor.GetR()), static_cast<float>(clearColor.GetG()), static_cast<float>(clearColor.GetB()), static_cast<float>(clearColor.GetA()),
  1044. renderTargetRenderNode->GetViewportX(),
  1045. renderTargetRenderNode->GetViewportY(),
  1046. renderTargetRenderNode->GetViewportWidth(),
  1047. renderTargetRenderNode->GetViewportHeight());
  1048. AZ::IO::LocalFileIO::GetInstance()->Write(fileHandle, logLine.c_str(), logLine.size());
  1049. const AZStd::vector<RenderNode*>& childNodeList = renderTargetRenderNode->GetChildRenderNodeList();
  1050. AZStd::string indent = " ";
  1051. DebugReportDrawCallsRenderNodeList(childNodeList, fileHandle, reportInfo, context, indent);
  1052. // write blank separator line
  1053. logLine = "\r\n";
  1054. AZ::IO::LocalFileIO::GetInstance()->Write(fileHandle, logLine.c_str(), logLine.size());
  1055. }
  1056. AZStd::string logLine = "Main render target:\r\n";
  1057. AZ::IO::LocalFileIO::GetInstance()->Write(fileHandle, logLine.c_str(), logLine.size());
  1058. // Recursively visit all the render nodes
  1059. AZStd::string indent = " ";
  1060. DebugReportDrawCallsRenderNodeList(m_renderNodes, fileHandle, reportInfo, context, indent);
  1061. }
  1062. }
  1063. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1064. void RenderGraph::DebugReportDrawCallsRenderNodeList(
  1065. const AZStd::vector<RenderNode*>& renderNodeList,
  1066. AZ::IO::HandleType fileHandle,
  1067. LyShineDebug::DebugInfoDrawCallReport& reportInfo,
  1068. void* context,
  1069. const AZStd::string& indent) const
  1070. {
  1071. AZStd::string logLine;
  1072. bool previousNodeAlreadyCounted = false;
  1073. const PrimitiveListRenderNode* prevPrimListNode = nullptr;
  1074. for (const RenderNode* renderNode : renderNodeList)
  1075. {
  1076. if (renderNode->GetType() == RenderNodeType::Mask)
  1077. {
  1078. const MaskRenderNode* maskRenderNode = static_cast<const MaskRenderNode*>(renderNode);
  1079. AZStd::string newIndent = indent + " ";
  1080. logLine = AZStd::string::format("%sMask (MaskEnabled=%d, UseAlphaTest=%d, DrawBehind=%d, DrawInFront=%d) :\r\n",
  1081. indent.c_str(),
  1082. static_cast<int>(maskRenderNode->GetIsMaskingEnabled()),
  1083. static_cast<int>(maskRenderNode->GetUseAlphaTest()),
  1084. static_cast<int>(maskRenderNode->GetDrawBehind()),
  1085. static_cast<int>(maskRenderNode->GetDrawInFront()));
  1086. AZ::IO::LocalFileIO::GetInstance()->Write(fileHandle, logLine.c_str(), logLine.size());
  1087. logLine = AZStd::string::format("%s Mask shape render nodes:\r\n", indent.c_str());
  1088. AZ::IO::LocalFileIO::GetInstance()->Write(fileHandle, logLine.c_str(), logLine.size());
  1089. DebugReportDrawCallsRenderNodeList(maskRenderNode->GetMaskRenderNodeList(), fileHandle, reportInfo, context, newIndent);
  1090. logLine = AZStd::string::format("%s Mask content render nodes:\r\n", indent.c_str());
  1091. AZ::IO::LocalFileIO::GetInstance()->Write(fileHandle, logLine.c_str(), logLine.size());
  1092. DebugReportDrawCallsRenderNodeList(maskRenderNode->GetContentRenderNodeList(), fileHandle, reportInfo, context, newIndent);
  1093. prevPrimListNode = nullptr;
  1094. }
  1095. else if (renderNode->GetType() == RenderNodeType::PrimitiveList)
  1096. {
  1097. const PrimitiveListRenderNode* primListRenderNode = static_cast<const PrimitiveListRenderNode*>(renderNode);
  1098. bool nodeExistsBecauseOfExceedingMaxTextures = false;
  1099. if (prevPrimListNode)
  1100. {
  1101. if (prevPrimListNode->GetBlendModeState() == primListRenderNode->GetBlendModeState() &&
  1102. prevPrimListNode->GetIsTextureSRGB() == primListRenderNode->GetIsTextureSRGB() &&
  1103. prevPrimListNode->HasSpaceToAddPrimitive(&primListRenderNode->GetPrimitives().front()) &&
  1104. prevPrimListNode->GetNumTextures() == PrimitiveListRenderNode::MaxTextures)
  1105. {
  1106. // this node could have been combined with the previous node if less unique textures were used
  1107. // so this is an opportunity for texture atlases to reduce draw calls
  1108. nodeExistsBecauseOfExceedingMaxTextures = true;
  1109. }
  1110. }
  1111. // If this render node was created because the previous render node ran out of textures
  1112. // then we need to record the previous render node's textures as contributing to exceeding
  1113. // the max textures.
  1114. if (nodeExistsBecauseOfExceedingMaxTextures)
  1115. {
  1116. if (!previousNodeAlreadyCounted)
  1117. {
  1118. for (int i = 0; i < prevPrimListNode->GetNumTextures(); ++i)
  1119. {
  1120. AZ::Data::Instance<AZ::RPI::Image> texture = prevPrimListNode->GetTexture(i);
  1121. if (!texture)
  1122. {
  1123. texture = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White);
  1124. }
  1125. bool isClampTextureUsage = prevPrimListNode->GetTextureIsClampMode(i);
  1126. LyShineDebug::DebugInfoTextureUsage* matchingTextureUsage = nullptr;
  1127. // The texture should already be in reportInfo because we have already visited the previous
  1128. // render node.
  1129. for (LyShineDebug::DebugInfoTextureUsage& reportTextureUsage : reportInfo.m_textures)
  1130. {
  1131. if (reportTextureUsage.m_texture == texture &&
  1132. reportTextureUsage.m_isClampTextureUsage == isClampTextureUsage)
  1133. {
  1134. matchingTextureUsage = &reportTextureUsage;
  1135. break;
  1136. }
  1137. }
  1138. if (matchingTextureUsage)
  1139. {
  1140. matchingTextureUsage->m_numDrawCallsWhereExceedingMaxTextures++;
  1141. }
  1142. }
  1143. previousNodeAlreadyCounted = true;
  1144. }
  1145. }
  1146. else
  1147. {
  1148. previousNodeAlreadyCounted = false;
  1149. }
  1150. LyShine::UiPrimitiveList& primitives = primListRenderNode->GetPrimitives();
  1151. int numPrimitives = static_cast<int>(primitives.size());
  1152. int numTriangles = 0;
  1153. for (const LyShine::UiPrimitive& primitive : primitives)
  1154. {
  1155. numTriangles += primitive.m_numIndices / 3;
  1156. }
  1157. // Write heading to logfile for this render node
  1158. AZ::RHI::TargetBlendState blendMode = primListRenderNode->GetBlendModeState();
  1159. logLine = AZStd::string::format("%sPrimitive render node (Blend mode=%s, SRGB=%d). NumPrims=%d, NumTris=%d. Using textures:\r\n",
  1160. indent.c_str(), blendMode.m_enable ? "enabled" : "disabled",
  1161. static_cast<int>(primListRenderNode->GetIsTextureSRGB()),
  1162. numPrimitives, numTriangles);
  1163. AZ::IO::LocalFileIO::GetInstance()->Write(fileHandle, logLine.c_str(), logLine.size());
  1164. for (int i = 0; i < primListRenderNode->GetNumTextures(); ++i)
  1165. {
  1166. AZ::Data::Instance<AZ::RPI::Image> texture = primListRenderNode->GetTexture(i);
  1167. if (!texture)
  1168. {
  1169. texture = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White);
  1170. }
  1171. bool isClampTextureUsage = primListRenderNode->GetTextureIsClampMode(i);
  1172. LyShineDebug::DebugInfoTextureUsage* matchingTextureUsage = nullptr;
  1173. // Write line to logfile for this texture
  1174. AZStd::string textureName;
  1175. AZ::Data::AssetCatalogRequestBus::BroadcastResult(textureName, &AZ::Data::AssetCatalogRequests::GetAssetPathById, texture->GetAssetId());
  1176. logLine = AZStd::string::format("%s %s\r\n", indent.c_str(), textureName.c_str());
  1177. AZ::IO::LocalFileIO::GetInstance()->Write(fileHandle, logLine.c_str(), logLine.size());
  1178. // see if texture is in reportInfo
  1179. for (LyShineDebug::DebugInfoTextureUsage& reportTextureUsage : reportInfo.m_textures)
  1180. {
  1181. if (reportTextureUsage.m_texture == texture &&
  1182. reportTextureUsage.m_isClampTextureUsage == isClampTextureUsage)
  1183. {
  1184. matchingTextureUsage = &reportTextureUsage;
  1185. break;
  1186. }
  1187. }
  1188. if (!matchingTextureUsage)
  1189. {
  1190. // Texture is not already in reportInfo so add it
  1191. LyShineDebug::DebugInfoTextureUsage newTextureUsage;
  1192. newTextureUsage.m_texture = texture;
  1193. newTextureUsage.m_isClampTextureUsage = isClampTextureUsage;
  1194. newTextureUsage.m_numCanvasesUsed = 0;
  1195. newTextureUsage.m_numDrawCallsUsed = 0;
  1196. newTextureUsage.m_numDrawCallsWhereExceedingMaxTextures = 0;
  1197. newTextureUsage.m_lastContextUsed = nullptr;
  1198. reportInfo.m_textures.push_back(newTextureUsage);
  1199. matchingTextureUsage = &reportInfo.m_textures.back();
  1200. }
  1201. matchingTextureUsage->m_numDrawCallsUsed++;
  1202. if (nodeExistsBecauseOfExceedingMaxTextures)
  1203. {
  1204. matchingTextureUsage->m_numDrawCallsWhereExceedingMaxTextures++;
  1205. }
  1206. if (matchingTextureUsage->m_lastContextUsed != context)
  1207. {
  1208. matchingTextureUsage->m_numCanvasesUsed++;
  1209. matchingTextureUsage->m_lastContextUsed = context;
  1210. }
  1211. }
  1212. prevPrimListNode = primListRenderNode;
  1213. }
  1214. }
  1215. }
  1216. #endif
  1217. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1218. AZ::RHI::TargetBlendState RenderGraph::GetBlendModeState(LyShine::BlendMode blendMode, [[maybe_unused]] bool isShaderOutputPremultAlpha) const
  1219. {
  1220. // LYSHINE_ATOM_TODO - remove "premultiplyAlpha" parameter and clean up related comments as I think it's no longer needed
  1221. // Our blend modes are complicated by the fact we want to be able to render to a render target and then
  1222. // render from that render target texture to the back buffer and get the same result as if we rendered
  1223. // directly to the back buffer. This should be true even if the render target texture does not end up
  1224. // fully opaque.
  1225. // If the blend mode is LyShine::BlendMode::Normal and we just use GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA
  1226. // then this doesn't work for render targets that end up with transparency. To make it work the alpha has to be
  1227. // accumulated as we render it into the alpha channel of the render target. If we use:
  1228. // GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA it gets used for both the color blend op and the alpha blend op
  1229. // so we end up with: dstAlpha = srcAlpha * srcAlpha + dstAlpha * (1-srcAlpha).
  1230. // This does not accumulate properly.
  1231. // What we actually want is: dstAlpha = srcAlpha + dstAlpha * (1-srcAlpha)
  1232. // So that would mean for alpha we want GS_BLSRC_ONE | GS_BLDST_ONEMINUSSRCALPHA
  1233. // If the IRenderer::SetState allowed us to set the alpha and color blend op separately that would be pretty simple.
  1234. // However, it does not. So we use a work around. We use a variant of the shader that premultiplies the output
  1235. // color by the output alpha. So using that variant means that:
  1236. // GS_BLSRC_ONE | GS_BLDST_ONEMINUSSRCALPHA
  1237. // will give the same *color* result as GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA
  1238. // while giving us the alpha result that we want.
  1239. //
  1240. // For blend modes other than LyShine::BlendMode::Normal we make similar adjustments. This works well for
  1241. // LyShine::BlendMode::Add. For the other three blend modes we cannot get the same results - but the results
  1242. // for those blend modes have always been inadequate. Until we get full control over the blend ops
  1243. // we won't be able to properly support those blend modes by using blend states. Even then to do them
  1244. // properly might require shader changes also. For the moment using the blend modes Screen, Darken, Lighten
  1245. // is not encouraged, especially when rendering to a render target.
  1246. AZ::RHI::TargetBlendState blendState;
  1247. blendState.m_blendAlphaSource = AZ::RHI::BlendFactor::One;
  1248. blendState.m_blendAlphaDest = AZ::RHI::BlendFactor::AlphaSourceInverse;
  1249. switch (blendMode)
  1250. {
  1251. case LyShine::BlendMode::Normal:
  1252. // This is the default mode that does an alpha blend by interpolating based on src alpha
  1253. blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource;
  1254. blendState.m_blendDest = AZ::RHI::BlendFactor::AlphaSourceInverse;
  1255. break;
  1256. case LyShine::BlendMode::Add:
  1257. // This works well, the amount of the src color added is controlled by src alpha
  1258. blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource;
  1259. blendState.m_blendDest = AZ::RHI::BlendFactor::One;
  1260. break;
  1261. case LyShine::BlendMode::Screen:
  1262. // This is a poor approximation of the PhotoShop Screen mode but trying to take some account of src alpha
  1263. // In Photoshop this would be 1 - ( (1-SrcColor) * (1-DstColor) )
  1264. // So we should use a blend op of multiply but the IRenderer interface doesn't support that. We get some multiply
  1265. // from GS_BLDST_ONEMINUSSRCCOL which multiplies the DstColor by (1-SrcColor)
  1266. blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource;
  1267. blendState.m_blendDest = AZ::RHI::BlendFactor::ColorSourceInverse;
  1268. break;
  1269. case LyShine::BlendMode::Darken:
  1270. // This is a poor approximation of the PhotoShop Darken mode but trying to take some account of src alpha
  1271. // In Photoshop Darken means min(SrcColor, DstColor)
  1272. blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSourceInverse;
  1273. blendState.m_blendDest = AZ::RHI::BlendFactor::One;
  1274. blendState.m_blendOp = AZ::RHI::BlendOp::Minimum;
  1275. break;
  1276. case LyShine::BlendMode::Lighten:
  1277. // This is a pretty good an approximation of the PhotoShop Lighten mode but trying to take some account of src alpha
  1278. // In PhotoShop Lighten means max(SrcColor, DstColor)
  1279. blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource;
  1280. blendState.m_blendDest = AZ::RHI::BlendFactor::One;
  1281. blendState.m_blendOp = AZ::RHI::BlendOp::Maximum;
  1282. break;
  1283. }
  1284. return blendState;
  1285. }
  1286. void RenderGraph::SetRttPassesEnabled(UiRenderer* uiRenderer, bool enabled)
  1287. {
  1288. // Enable or disable the rtt render passes
  1289. AZ::RPI::SceneId sceneId = uiRenderer->GetViewportContext()->GetRenderScene()->GetId();
  1290. for (RenderTargetRenderNode* renderTargetRenderNode : m_renderTargetRenderNodes)
  1291. {
  1292. // Find the rtt pass to disable
  1293. AZ::RPI::RasterPass* rttPass = nullptr;
  1294. LyShinePassRequestBus::EventResult(rttPass, sceneId, &LyShinePassRequestBus::Events::GetRttPass, renderTargetRenderNode->GetRenderTargetName());
  1295. if (rttPass)
  1296. {
  1297. rttPass->SetEnabled(enabled);
  1298. }
  1299. }
  1300. }
  1301. }