Reenable support for UI Elements that use Render Targets (#2352)

* Re-add support for UI Elements that use Render Targets
* Move LyShine pass request from Atom's MainPipeline.pass to project's
* Make all dynamic draw contexts in LyShine draw to pass directly without the need of draw list tags
* Remove local RPI changes that are no longer needed
* Prevent crash if LyShine gem is enabled but its custom pass hasn't been added to the main render pipeline
* Revert to default UI pass if the LyShine pass has not been added to project's main render pipeline

Signed-off-by: abrmich <abrmich@amazon.com>
This commit is contained in:
michabr
2021-08-03 10:14:09 -07:00
committed by GitHub
parent 03722789cf
commit 3a689aa319
37 changed files with 1951 additions and 492 deletions
+48 -75
View File
@@ -6,6 +6,7 @@
*
*/
#include "UiFaderComponent.h"
#include "RenderGraph.h"
#include <LyShine/Draw2d.h>
#include <AzCore/Math/Crc.h>
@@ -14,6 +15,9 @@
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <Atom/RPI.Public/Image/AttachmentImage.h>
#include <AtomCore/Instance/Instance.h>
#include <LyShine/Bus/UiElementBus.h>
#include <LyShine/Bus/UiRenderBus.h>
#include <LyShine/Bus/UiCanvasBus.h>
@@ -22,6 +26,7 @@
#include <ITimer.h>
#include "UiSerialize.h"
#include "RenderToTextureBus.h"
// BehaviorContext UiFaderNotificationBus forwarder
class BehaviorUiFaderNotificationBusHandler
@@ -120,7 +125,7 @@ void UiFaderComponent::Render(LyShine::IRenderGraph* renderGraph, UiElementInter
AZ::Vector2 renderTargetSize = pixelAlignedBottomRight - pixelAlignedTopLeft;
bool needsResize = static_cast<int>(renderTargetSize.GetX()) != m_renderTargetWidth || static_cast<int>(renderTargetSize.GetY()) != m_renderTargetHeight;
if (m_renderTargetHandle == -1 || needsResize)
if (m_attachmentImageId.IsEmpty() || needsResize)
{
// We delay first creation of the render target until render time since size is not known in Activate
// We also call this if the size has changed
@@ -128,7 +133,7 @@ void UiFaderComponent::Render(LyShine::IRenderGraph* renderGraph, UiElementInter
}
// if the render target failed to be created (zero size for example) we don't render the element at all
if (m_renderTargetHandle == -1)
if (m_attachmentImageId.IsEmpty())
{
return;
}
@@ -139,7 +144,7 @@ void UiFaderComponent::Render(LyShine::IRenderGraph* renderGraph, UiElementInter
else
{
// destroy previous render target, if exists
if (m_renderTargetHandle != -1)
if (!m_attachmentImageId.IsEmpty())
{
DestroyRenderTarget();
}
@@ -452,54 +457,22 @@ void UiFaderComponent::CreateOrResizeRenderTarget(const AZ::Vector2& pixelAligne
m_viewportTopLeft = pixelAlignedTopLeft;
m_viewportSize = renderTargetSize;
#ifdef LYSHINE_ATOM_TODO // [LYN-3359] Support RTT using Atom
// Check if the render target already exists
if (m_renderTargetHandle != -1)
{
// Render target exists, resize it to the given size
if (!gEnv->pRenderer->ResizeRenderTarget(m_renderTargetHandle, static_cast<int>(renderTargetSize.GetX()), static_cast<int>(renderTargetSize.GetY())))
{
AZ_Warning("UI", false, "Failed to resize render target for UiFaderComponent");
DestroyRenderTarget();
}
}
else
{
// Create a render target that this element and its children will be rendered to.
m_renderTargetHandle = gEnv->pRenderer->CreateRenderTarget(m_renderTargetName.c_str(),
static_cast<int>(renderTargetSize.GetX()), static_cast<int>(renderTargetSize.GetY()), Clr_Transparent, eTF_R8G8B8A8);
// LYSHINE_ATOM_TODO: optimize by reusing/resizing targets
DestroyRenderTarget();
if (m_renderTargetHandle == -1)
{
AZ_Warning("UI", false, "Failed to create render target for UiFaderComponent");
}
}
// if depth surface already exists then destroy it
if (m_renderTargetDepthSurface)
// Create a render target that this element and its children will be rendered to
AZ::EntityId canvasEntityId;
EBUS_EVENT_ID_RESULT(canvasEntityId, GetEntityId(), UiElementBus, GetCanvasEntityId);
AZ::RHI::Size imageSize(renderTargetSize.GetX(), renderTargetSize.GetY(), 1);
EBUS_EVENT_ID_RESULT(m_attachmentImageId, canvasEntityId, LyShine::RenderToTextureRequestBus, UseRenderTarget, AZ::Name(m_renderTargetName.c_str()), imageSize);
if (m_attachmentImageId.IsEmpty())
{
gEnv->pRenderer->DestroyDepthSurface(m_renderTargetDepthSurface);
m_renderTargetDepthSurface = nullptr;
AZ_Warning("UI", false, "Failed to create render target for UiFaderComponent");
}
if (m_renderTargetHandle != -1)
{
// Also create a depth surface to render the canvas to, we need depth for masking
// since that uses the stencil buffer. We support any combination of nesting faders and masks
m_renderTargetDepthSurface = gEnv->pRenderer->CreateDepthSurface(
static_cast<int>(renderTargetSize.GetX()), static_cast<int>(renderTargetSize.GetY()));
if (!m_renderTargetDepthSurface)
{
AZ_Warning("UI", false, "Failed to create depth surface for UiFaderComponent");
DestroyRenderTarget();
}
}
#endif
// at this point either all render targets and depth surfaces are created or none are.
// If all succeeded then update the render target size
if (m_renderTargetHandle != -1)
if (!m_attachmentImageId.IsEmpty())
{
m_renderTargetWidth = static_cast<int>(renderTargetSize.GetX());
m_renderTargetHeight = static_cast<int>(renderTargetSize.GetY());
@@ -511,16 +484,12 @@ void UiFaderComponent::CreateOrResizeRenderTarget(const AZ::Vector2& pixelAligne
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiFaderComponent::DestroyRenderTarget()
{
if (m_renderTargetHandle != -1)
if (!m_attachmentImageId.IsEmpty())
{
gEnv->pRenderer->DestroyRenderTarget(m_renderTargetHandle);
m_renderTargetHandle = -1;
}
if (m_renderTargetDepthSurface)
{
gEnv->pRenderer->DestroyDepthSurface(m_renderTargetDepthSurface);
m_renderTargetDepthSurface = nullptr;
AZ::EntityId canvasEntityId;
EBUS_EVENT_ID_RESULT(canvasEntityId, GetEntityId(), UiElementBus, GetCanvasEntityId);
EBUS_EVENT_ID(canvasEntityId, LyShine::RenderToTextureRequestBus, ReleaseRenderTarget, m_attachmentImageId);
m_attachmentImageId = AZ::RHI::AttachmentId{};
}
}
@@ -594,14 +563,20 @@ void UiFaderComponent::RenderStandardFader(LyShine::IRenderGraph* renderGraph, U
void UiFaderComponent::RenderRttFader(LyShine::IRenderGraph* renderGraph, UiElementInterface* elementInterface,
UiRenderInterface* renderInterface, int numChildren, bool isInGame)
{
// Get the render target
AZ::Data::Instance<AZ::RPI::AttachmentImage> attachmentImage;
AZ::EntityId canvasEntityId;
EBUS_EVENT_ID_RESULT(canvasEntityId, GetEntityId(), UiElementBus, GetCanvasEntityId);
EBUS_EVENT_ID_RESULT(attachmentImage, canvasEntityId, LyShine::RenderToTextureRequestBus, GetRenderTarget, m_attachmentImageId);
// Render the element and its children to a render target
{
// we always clear to transparent black - the accumulation of alpha in the render target requires it
AZ::Color clearColor(0.0f, 0.0f, 0.0f, 0.0f);
// Start building the render to texture node in the render graph
renderGraph->BeginRenderToTexture(m_renderTargetHandle, m_renderTargetDepthSurface,
m_viewportTopLeft, m_viewportSize, clearColor);
LyShine::RenderGraph* lyRenderGraph = dynamic_cast<LyShine::RenderGraph*>(renderGraph);
lyRenderGraph->BeginRenderToTexture(attachmentImage, m_viewportTopLeft, m_viewportSize, clearColor);
// We don't want this fader or parent faders to affect what is rendered to the render target since we will
// apply those fades when we render from the render target.
@@ -624,14 +599,13 @@ void UiFaderComponent::RenderRttFader(LyShine::IRenderGraph* renderGraph, UiElem
float desiredAlpha = renderGraph->GetAlphaFade() * m_fade;
uint8 desiredPackedAlpha = static_cast<uint8>(desiredAlpha * 255.0f);
UCol desiredPackedColor;
// This is a special case. We have an input texture that already has premultiplied alpha.
// So we tell the shader not to premultiply the output colors and we premultiply the alpha
// into the vertex colors so that they are premultiplied too.
desiredPackedColor.r = desiredPackedColor.g = desiredPackedColor.b = desiredPackedColor.a = desiredPackedAlpha;
if (m_cachedPrimitive.m_vertices[0].color.dcolor != desiredPackedColor.dcolor)
// If the fade value has changed we need to update the alpha values in the vertex colors but we do
// not want to touch or recompute the RGB values
if (m_cachedPrimitive.m_vertices[0].color.a != desiredPackedAlpha)
{
// go through the cached vertices and update the color values
// go through all the cached vertices and update the alpha values
UCol desiredPackedColor = m_cachedPrimitive.m_vertices[0].color;
desiredPackedColor.a = desiredPackedAlpha;
for (int i = 0; i < m_cachedPrimitive.m_numVertices; ++i)
{
m_cachedPrimitive.m_vertices[i].color = desiredPackedColor;
@@ -639,21 +613,20 @@ void UiFaderComponent::RenderRttFader(LyShine::IRenderGraph* renderGraph, UiElem
}
}
#ifdef LYSHINE_ATOM_TODO // [LYN-3359] Support RTT using Atom
// Add a primitive to render a quad using the render target we have created
{
// Set the texture and other render state required
ITexture* texture = gEnv->pRenderer->EF_GetTextureByID(m_renderTargetHandle);
bool isClampTextureMode = true;
bool isTextureSRGB = true;
bool isTexturePremultipliedAlpha = true;
LyShine::BlendMode blendMode = LyShine::BlendMode::Normal;
// add a render node to render from the render target texture to the current target
renderGraph->AddPrimitive(&m_cachedPrimitive, texture,
isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, blendMode);
LyShine::RenderGraph* lyRenderGraph = dynamic_cast<LyShine::RenderGraph*>(renderGraph);
if (lyRenderGraph)
{
// Set the texture and other render state required
AZ::Data::Instance<AZ::RPI::Image> image = attachmentImage;
bool isClampTextureMode = true;
bool isTextureSRGB = true;
bool isTexturePremultipliedAlpha = true;
LyShine::BlendMode blendMode = LyShine::BlendMode::Normal;
lyRenderGraph->AddPrimitiveAtom(&m_cachedPrimitive, image, isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, blendMode);
}
}
#endif
}
}