RenderGraph.cpp 68 KB

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