UiRenderer.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  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 "LyShine_precompiled.h"
  9. #include "UiRenderer.h"
  10. #include <Atom/RPI.Public/Image/ImageSystemInterface.h>
  11. #include <Atom/RPI.Public/DynamicDraw/DynamicDrawInterface.h>
  12. #include <Atom/RPI.Public/RPISystemInterface.h>
  13. #include <Atom/RPI.Public/RPIUtils.h>
  14. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  15. #include <Atom/Bootstrap/DefaultWindowBus.h>
  16. #include <Atom/RPI.Public/ViewportContextBus.h>
  17. #include <Atom/RPI.Public/RenderPipeline.h>
  18. #include <AzCore/Math/MatrixUtils.h>
  19. #include <AzCore/Debug/Trace.h>
  20. #include <LyShine/Draw2d.h>
  21. ////////////////////////////////////////////////////////////////////////////////////////////////////
  22. // PUBLIC MEMBER FUNCTIONS
  23. ////////////////////////////////////////////////////////////////////////////////////////////////////
  24. ////////////////////////////////////////////////////////////////////////////////////////////////////
  25. UiRenderer::UiRenderer(AZ::RPI::ViewportContextPtr viewportContext)
  26. : m_viewportContext(viewportContext)
  27. {
  28. // Use bootstrap scene event to indicate when the RPI has fully
  29. // initialized with all assets loaded and is ready to be used
  30. AZ::Render::Bootstrap::NotificationBus::Handler::BusConnect();
  31. }
  32. ////////////////////////////////////////////////////////////////////////////////////////////////////
  33. UiRenderer::~UiRenderer()
  34. {
  35. AZ::Render::Bootstrap::NotificationBus::Handler::BusDisconnect();
  36. if (m_viewportContext)
  37. {
  38. AZ::RPI::RPISystemInterface::Get()->UnregisterScene(m_viewportContext->GetRenderScene());
  39. }
  40. m_dynamicDraw = nullptr;
  41. }
  42. bool UiRenderer::IsReady()
  43. {
  44. return m_isRPIReady;
  45. }
  46. void UiRenderer::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapScene)
  47. {
  48. // At this point the RPI is ready for use
  49. // Load the UI shader
  50. const char* uiShaderFilepath = "Shaders/LyShineUI.azshader";
  51. AZ::Data::Instance<AZ::RPI::Shader> uiShader = AZ::RPI::LoadShader(uiShaderFilepath);
  52. // Create scene to be used by the dynamic draw context
  53. AZ::RPI::ScenePtr scene;
  54. if (m_viewportContext)
  55. {
  56. // Create a new scene based on the user specified viewport context
  57. scene = CreateScene(m_viewportContext);
  58. }
  59. else
  60. {
  61. // No viewport context specified, use default scene
  62. scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene();
  63. }
  64. // Create a dynamic draw context for UI Canvas drawing for the scene
  65. CreateDynamicDrawContext(scene, uiShader);
  66. // Cache shader data such as input indices for later use
  67. CacheShaderData(m_dynamicDraw);
  68. m_isRPIReady = true;
  69. }
  70. AZ::RPI::ScenePtr UiRenderer::CreateScene(AZStd::shared_ptr<AZ::RPI::ViewportContext> viewportContext)
  71. {
  72. // Create a scene with the necessary feature processors
  73. AZ::RPI::SceneDescriptor sceneDesc;
  74. AZ::RPI::ScenePtr atomScene = AZ::RPI::Scene::CreateScene(sceneDesc);
  75. atomScene->EnableAllFeatureProcessors(); // LYSHINE_ATOM_TODO - have a UI pipeline and enable only needed fps
  76. // Assign the new scene to the specified viewport context
  77. viewportContext->SetRenderScene(atomScene);
  78. // Create a render pipeline and add it to the scene
  79. AZStd::string pipelineAssetPath = "passes/MainRenderPipeline.azasset"; // LYSHINE_ATOM_TODO - make and use a UI pipeline
  80. AZ::Data::Asset<AZ::RPI::AnyAsset> pipelineAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::AnyAsset>(pipelineAssetPath.c_str(), AZ::RPI::AssetUtils::TraceLevel::Error);
  81. AZStd::shared_ptr<AZ::RPI::WindowContext> windowContext = viewportContext->GetWindowContext();
  82. auto renderPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineAsset, *windowContext.get());
  83. pipelineAsset.Release();
  84. atomScene->AddRenderPipeline(renderPipeline);
  85. atomScene->Activate();
  86. // Register the scene
  87. AZ::RPI::RPISystemInterface::Get()->RegisterScene(atomScene);
  88. return atomScene;
  89. }
  90. void UiRenderer::CreateDynamicDrawContext(AZ::RPI::ScenePtr scene, AZ::Data::Instance<AZ::RPI::Shader> uiShader)
  91. {
  92. m_dynamicDraw = AZ::RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext();
  93. // Initialize the dynamic draw context
  94. m_dynamicDraw->InitShader(uiShader);
  95. m_dynamicDraw->InitVertexFormat(
  96. { { "POSITION", AZ::RHI::Format::R32G32_FLOAT },
  97. { "COLOR", AZ::RHI::Format::B8G8R8A8_UNORM },
  98. { "TEXCOORD", AZ::RHI::Format::R32G32_FLOAT },
  99. { "BLENDINDICES", AZ::RHI::Format::R16G16_UINT } }
  100. );
  101. m_dynamicDraw->AddDrawStateOptions(AZ::RPI::DynamicDrawContext::DrawStateOptions::StencilState
  102. | AZ::RPI::DynamicDrawContext::DrawStateOptions::BlendMode);
  103. m_dynamicDraw->SetOutputScope(scene.get());
  104. m_dynamicDraw->EndInit();
  105. }
  106. AZStd::shared_ptr<AZ::RPI::ViewportContext> UiRenderer::GetViewportContext()
  107. {
  108. if (!m_viewportContext)
  109. {
  110. // Return the default viewport context
  111. auto viewContextManager = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
  112. auto defaultViewportContext = viewContextManager->GetViewportContextByName(viewContextManager->GetDefaultViewportContextName());
  113. return defaultViewportContext;
  114. }
  115. // Return the user specified viewport context
  116. return m_viewportContext;
  117. }
  118. void UiRenderer::CacheShaderData(const AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext>& dynamicDraw)
  119. {
  120. // Cache draw srg input indices
  121. static const char textureIndexName[] = "m_texture";
  122. static const char worldToProjIndexName[] = "m_worldToProj";
  123. static const char isClampIndexName[] = "m_isClamp";
  124. AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> drawSrg = dynamicDraw->NewDrawSrg();
  125. const AZ::RHI::ShaderResourceGroupLayout* layout = drawSrg->GetLayout();
  126. m_uiShaderData.m_imageInputIndex = layout->FindShaderInputImageIndex(AZ::Name(textureIndexName));
  127. AZ_Error(LogName, m_uiShaderData.m_imageInputIndex.IsValid(), "Failed to find shader input constant %s.",
  128. textureIndexName);
  129. m_uiShaderData.m_viewProjInputIndex = layout->FindShaderInputConstantIndex(AZ::Name(worldToProjIndexName));
  130. AZ_Error(LogName, m_uiShaderData.m_viewProjInputIndex.IsValid(), "Failed to find shader input constant %s.",
  131. worldToProjIndexName);
  132. m_uiShaderData.m_isClampInputIndex = layout->FindShaderInputConstantIndex(AZ::Name(isClampIndexName));
  133. AZ_Error(LogName, m_uiShaderData.m_isClampInputIndex.IsValid(), "Failed to find shader input constant %s.",
  134. isClampIndexName);
  135. // Cache shader variants that will be used
  136. // LYSHINE_ATOM_TODO - more variants will be used in future phase (masks/render target support)
  137. AZ::RPI::ShaderOptionList shaderOptionsDefault;
  138. shaderOptionsDefault.push_back(AZ::RPI::ShaderOption(AZ::Name("o_preMultiplyAlpha"), AZ::Name("false")));
  139. shaderOptionsDefault.push_back(AZ::RPI::ShaderOption(AZ::Name("o_alphaTest"), AZ::Name("false")));
  140. shaderOptionsDefault.push_back(AZ::RPI::ShaderOption(AZ::Name("o_srgbWrite"), AZ::Name("true")));
  141. shaderOptionsDefault.push_back(AZ::RPI::ShaderOption(AZ::Name("o_modulate"), AZ::Name("Modulate::None")));
  142. m_uiShaderData.m_shaderVariantDefault = dynamicDraw->UseShaderVariant(shaderOptionsDefault);
  143. AZ::RPI::ShaderOptionList shaderOptionsAlphaTest;
  144. shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_preMultiplyAlpha"), AZ::Name("false")));
  145. shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_alphaTest"), AZ::Name("true")));
  146. shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_srgbWrite"), AZ::Name("true")));
  147. shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_modulate"), AZ::Name("Modulate::None")));
  148. m_uiShaderData.m_shaderVariantAlphaTest = dynamicDraw->UseShaderVariant(shaderOptionsAlphaTest);
  149. }
  150. ////////////////////////////////////////////////////////////////////////////////////////////////////
  151. void UiRenderer::BeginUiFrameRender()
  152. {
  153. #ifndef _RELEASE
  154. if (m_debugTextureDataRecordLevel > 0)
  155. {
  156. m_texturesUsedInFrame.clear();
  157. }
  158. #endif
  159. // Various platform drivers expect all texture slots used in the shader to be bound
  160. BindNullTexture();
  161. }
  162. ////////////////////////////////////////////////////////////////////////////////////////////////////
  163. void UiRenderer::EndUiFrameRender()
  164. {
  165. // We never want to leave a texture bound that could get unloaded before the next render
  166. // So bind the global white texture for all the texture units we use.
  167. BindNullTexture();
  168. }
  169. ////////////////////////////////////////////////////////////////////////////////////////////////////
  170. void UiRenderer::BeginCanvasRender()
  171. {
  172. m_stencilRef = 0;
  173. // Set base state
  174. m_baseState.ResetToDefault();
  175. }
  176. ////////////////////////////////////////////////////////////////////////////////////////////////////
  177. void UiRenderer::EndCanvasRender()
  178. {
  179. }
  180. ////////////////////////////////////////////////////////////////////////////////////////////////////
  181. AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> UiRenderer::GetDynamicDrawContext()
  182. {
  183. return m_dynamicDraw;
  184. }
  185. ////////////////////////////////////////////////////////////////////////////////////////////////////
  186. const UiRenderer::UiShaderData& UiRenderer::GetUiShaderData()
  187. {
  188. return m_uiShaderData;
  189. }
  190. ////////////////////////////////////////////////////////////////////////////////////////////////////
  191. AZ::Matrix4x4 UiRenderer::GetModelViewProjectionMatrix()
  192. {
  193. auto viewportContext = GetViewportContext();
  194. auto windowContext = viewportContext->GetWindowContext();
  195. const AZ::RHI::Viewport& viewport = windowContext->GetViewport();
  196. const float viewX = viewport.m_minX;
  197. const float viewY = viewport.m_minY;
  198. const float viewWidth = viewport.m_maxX - viewport.m_minX;
  199. const float viewHeight = viewport.m_maxY - viewport.m_minY;
  200. const float zf = viewport.m_minZ;
  201. const float zn = viewport.m_maxZ;
  202. AZ::Matrix4x4 modelViewProjMat;
  203. AZ::MakeOrthographicMatrixRH(modelViewProjMat, viewX, viewX + viewWidth, viewY + viewHeight, viewY, zn, zf);
  204. return modelViewProjMat;
  205. }
  206. ////////////////////////////////////////////////////////////////////////////////////////////////////
  207. AZ::Vector2 UiRenderer::GetViewportSize()
  208. {
  209. auto viewportContext = GetViewportContext();
  210. auto windowContext = viewportContext->GetWindowContext();
  211. const AZ::RHI::Viewport& viewport = windowContext->GetViewport();
  212. const float viewX = viewport.m_minX;
  213. const float viewY = viewport.m_minY;
  214. const float viewWidth = viewport.m_maxX - viewport.m_minX;
  215. const float viewHeight = viewport.m_maxY - viewport.m_minY;
  216. return AZ::Vector2(viewWidth, viewHeight);
  217. }
  218. ////////////////////////////////////////////////////////////////////////////////////////////////////
  219. UiRenderer::BaseState UiRenderer::GetBaseState()
  220. {
  221. return m_baseState;
  222. }
  223. ////////////////////////////////////////////////////////////////////////////////////////////////////
  224. void UiRenderer::SetBaseState(BaseState state)
  225. {
  226. m_baseState = state;
  227. }
  228. ////////////////////////////////////////////////////////////////////////////////////////////////////
  229. AZ::RPI::ShaderVariantId UiRenderer::GetCurrentShaderVariant()
  230. {
  231. AZ::RPI::ShaderVariantId variantId = m_uiShaderData.m_shaderVariantDefault;
  232. if (m_baseState.m_useAlphaTest)
  233. {
  234. variantId = m_uiShaderData.m_shaderVariantAlphaTest;
  235. }
  236. return variantId;
  237. }
  238. ////////////////////////////////////////////////////////////////////////////////////////////////////
  239. uint32 UiRenderer::GetStencilRef()
  240. {
  241. return m_stencilRef;
  242. }
  243. ////////////////////////////////////////////////////////////////////////////////////////////////////
  244. void UiRenderer::SetStencilRef(uint32 stencilRef)
  245. {
  246. m_stencilRef = stencilRef;
  247. }
  248. ////////////////////////////////////////////////////////////////////////////////////////////////////
  249. void UiRenderer::IncrementStencilRef()
  250. {
  251. ++m_stencilRef;
  252. }
  253. ////////////////////////////////////////////////////////////////////////////////////////////////////
  254. void UiRenderer::DecrementStencilRef()
  255. {
  256. --m_stencilRef;
  257. }
  258. #ifdef LYSHINE_ATOM_TODO
  259. ////////////////////////////////////////////////////////////////////////////////////////////////////
  260. void UiRenderer::SetTexture(ITexture* texture, int texUnit, bool clamp)
  261. {
  262. if (!texture)
  263. {
  264. texture = m_renderer->GetWhiteTexture();
  265. }
  266. else
  267. {
  268. texture->SetClamp(clamp);
  269. }
  270. m_renderer->SetTexture(texture->GetTextureID(), texUnit);
  271. #ifndef _RELEASE
  272. if (m_debugTextureDataRecordLevel > 0)
  273. {
  274. m_texturesUsedInFrame.insert(texture);
  275. }
  276. #endif
  277. }
  278. #endif
  279. ////////////////////////////////////////////////////////////////////////////////////////////////////
  280. void UiRenderer::BindNullTexture()
  281. {
  282. #ifdef LYSHINE_ATOM_TODO
  283. // Bind the global white texture for all the texture units we use
  284. const int MaxTextures = 16;
  285. int whiteTexId = m_renderer->GetWhiteTextureId();
  286. for (int texUnit = 0; texUnit < MaxTextures; ++texUnit)
  287. {
  288. m_renderer->SetTexture(whiteTexId, texUnit);
  289. }
  290. #endif
  291. }
  292. #ifndef _RELEASE
  293. ////////////////////////////////////////////////////////////////////////////////////////////////////
  294. void UiRenderer::DebugSetRecordingOptionForTextureData(int recordingOption)
  295. {
  296. m_debugTextureDataRecordLevel = recordingOption;
  297. }
  298. ////////////////////////////////////////////////////////////////////////////////////////////////////
  299. void UiRenderer::DebugDisplayTextureData(int recordingOption)
  300. {
  301. if (recordingOption > 0)
  302. {
  303. #ifdef LYSHINE_ATOM_TODO // Convert debug to use Atom images
  304. // compute the total area of all the textures, also create a vector that we can sort by area
  305. AZStd::vector<ITexture*> textures;
  306. int totalArea = 0;
  307. int totalDataSize = 0;
  308. for (ITexture* texture : m_texturesUsedInFrame)
  309. {
  310. int area = texture->GetWidth() * texture->GetHeight();
  311. int dataSize = texture->GetDataSize();
  312. totalArea += area;
  313. totalDataSize += dataSize;
  314. textures.push_back(texture);
  315. }
  316. // sort the vector by data size
  317. std::sort( textures.begin( ), textures.end( ), [ ]( const ITexture* lhs, const ITexture* rhs )
  318. {
  319. return lhs->GetDataSize() > rhs->GetDataSize();
  320. });
  321. CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d();
  322. // setup to render lines of text for the debug display
  323. float xOffset = 20.0f;
  324. float yOffset = 20.0f;
  325. auto blackTexture = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::Black);
  326. float textOpacity = 1.0f;
  327. float backgroundRectOpacity = 0.75f;
  328. const float lineSpacing = 20.0f;
  329. const AZ::Vector3 white(1,1,1);
  330. const AZ::Vector3 red(1,0.3f,0.3f);
  331. const AZ::Vector3 blue(0.3f,0.3f,1);
  332. int xDim, yDim;
  333. if (totalArea > 2048 * 2048)
  334. {
  335. xDim = 4096;
  336. yDim = totalArea / 4096;
  337. }
  338. else
  339. {
  340. xDim = 2048;
  341. yDim = totalArea / 2048;
  342. }
  343. float totalDataSizeMB = static_cast<float>(totalDataSize) / (1024.0f * 1024.0f);
  344. // local function to write a line of text (with a background rect) and increment Y offset
  345. AZStd::function<void(const char*, const AZ::Vector3&)> WriteLine = [&](const char* buffer, const AZ::Vector3& color)
  346. {
  347. IDraw2d::TextOptions textOptions = draw2d->GetDefaultTextOptions();
  348. textOptions.color = color;
  349. AZ::Vector2 textSize = draw2d->GetTextSize(buffer, 16, &textOptions);
  350. AZ::Vector2 rectTopLeft = AZ::Vector2(xOffset - 2, yOffset);
  351. AZ::Vector2 rectSize = AZ::Vector2(textSize.GetX() + 4, lineSpacing);
  352. draw2d->DrawImage(blackTexture, rectTopLeft, rectSize, backgroundRectOpacity);
  353. draw2d->DrawText(buffer, AZ::Vector2(xOffset, yOffset), 16, textOpacity, &textOptions);
  354. yOffset += lineSpacing;
  355. };
  356. int numTexturesUsedInFrame = m_texturesUsedInFrame.size();
  357. char buffer[200];
  358. sprintf_s(buffer, "There are %d unique UI textures rendered in this frame, the total texture area is %d (%d x %d), total data size is %d (%.2f MB)",
  359. numTexturesUsedInFrame, totalArea, xDim, yDim, totalDataSize, totalDataSizeMB);
  360. WriteLine(buffer, white);
  361. sprintf_s(buffer, "Dimensions Data Size Format Texture name");
  362. WriteLine(buffer, blue);
  363. for (ITexture* texture : textures)
  364. {
  365. sprintf_s(buffer, "%4d x %4d, %9d %8s %s",
  366. texture->GetWidth(), texture->GetHeight(), texture->GetDataSize(), texture->GetFormatName(), texture->GetName());
  367. WriteLine(buffer, white);
  368. }
  369. #endif
  370. }
  371. }
  372. #endif