Fix blend mode options for LyShine's image component (#2377)

* Fix blend mode options for LyShine's image component
* Fix Lighten blend mode
* Fix errors from last merge

Signed-off-by: abrmich <abrmich@amazon.com>
monroegm-disable-blank-issue-2
michabr 4 years ago committed by GitHub
parent a54fceccc6
commit f58b51b8ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -38,7 +38,7 @@ namespace LyShine
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
PrimitiveListRenderNode::PrimitiveListRenderNode(const AZ::Data::Instance<AZ::RPI::Image>& texture, PrimitiveListRenderNode::PrimitiveListRenderNode(const AZ::Data::Instance<AZ::RPI::Image>& texture,
bool isClampTextureMode, bool isTextureSRGB, bool preMultiplyAlpha, int blendModeState) bool isClampTextureMode, bool isTextureSRGB, bool preMultiplyAlpha, const AZ::RHI::TargetBlendState& blendModeState)
: RenderNode(RenderNodeType::PrimitiveList) : RenderNode(RenderNodeType::PrimitiveList)
, m_numTextures(1) , m_numTextures(1)
, m_isTextureSRGB(isTextureSRGB) , m_isTextureSRGB(isTextureSRGB)
@ -55,7 +55,7 @@ namespace LyShine
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
PrimitiveListRenderNode::PrimitiveListRenderNode(const AZ::Data::Instance<AZ::RPI::Image>& texture, PrimitiveListRenderNode::PrimitiveListRenderNode(const AZ::Data::Instance<AZ::RPI::Image>& texture,
const AZ::Data::Instance<AZ::RPI::Image>& maskTexture, bool isClampTextureMode, bool isTextureSRGB, const AZ::Data::Instance<AZ::RPI::Image>& maskTexture, bool isClampTextureMode, bool isTextureSRGB,
bool preMultiplyAlpha, AlphaMaskType alphaMaskType, int blendModeState) bool preMultiplyAlpha, AlphaMaskType alphaMaskType, const AZ::RHI::TargetBlendState& blendModeState)
: RenderNode(RenderNodeType::PrimitiveList) : RenderNode(RenderNodeType::PrimitiveList)
, m_numTextures(2) , m_numTextures(2)
, m_isTextureSRGB(isTextureSRGB) , m_isTextureSRGB(isTextureSRGB)
@ -102,9 +102,16 @@ namespace LyShine
const UiRenderer::UiShaderData& uiShaderData = uiRenderer->GetUiShaderData(); const UiRenderer::UiShaderData& uiShaderData = uiRenderer->GetUiShaderData();
// Set render state
dynamicDraw->SetStencilState(uiRenderer->GetBaseState().m_stencilState); dynamicDraw->SetStencilState(uiRenderer->GetBaseState().m_stencilState);
dynamicDraw->SetTarget0BlendState(uiRenderer->GetBaseState().m_blendState);
// The blend factor and op is stored in m_blendModeState when the primitive is added to the graph.
// That is also when the graph determines whether a new primitive list node is needed.
// The rest of the blend properties are assigned during the render calls, so they get merged here
// and all are passed to the dynamic draw context
AZ::RHI::TargetBlendState targetBlendState = m_blendModeState;
targetBlendState.m_enable = uiRenderer->GetBaseState().m_blendStateEnabled;
targetBlendState.m_writeMask = uiRenderer->GetBaseState().m_blendStateWriteMask;
dynamicDraw->SetTarget0BlendState(targetBlendState);
dynamicDraw->SetShaderVariant(uiRenderer->GetCurrentShaderVariant()); dynamicDraw->SetShaderVariant(uiRenderer->GetCurrentShaderVariant());
@ -354,13 +361,13 @@ namespace LyShine
// if either of the draw flags are checked then we may want to draw the renderable component(s) // if either of the draw flags are checked then we may want to draw the renderable component(s)
// on this element, otherwise use the color mask to stop them rendering // on this element, otherwise use the color mask to stop them rendering
curBaseState.m_blendState.m_enable = false; curBaseState.m_blendStateEnabled = false;
curBaseState.m_blendState.m_writeMask = 0x0; curBaseState.m_blendStateWriteMask = 0x0;
if ((m_drawBehind && firstPass) || if ((m_drawBehind && firstPass) ||
(m_drawInFront && !firstPass)) (m_drawInFront && !firstPass))
{ {
curBaseState.m_blendState.m_enable = true; curBaseState.m_blendStateEnabled = true;
curBaseState.m_blendState.m_writeMask = 0xF; curBaseState.m_blendStateWriteMask = 0xF;
} }
if (m_isMaskingEnabled) if (m_isMaskingEnabled)
@ -507,19 +514,10 @@ namespace LyShine
if (m_dynamicDraw) if (m_dynamicDraw)
{ {
UiRenderer::BaseState priorBaseState = uiRenderer->GetBaseState();
UiRenderer::BaseState curBaseState = priorBaseState;
curBaseState.m_blendState.m_blendAlphaSource = AZ::RHI::BlendFactor::One;
curBaseState.m_blendState.m_blendAlphaDest = AZ::RHI::BlendFactor::AlphaSource1Inverse;
uiRenderer->SetBaseState(curBaseState);
for (RenderNode* renderNode : m_childRenderNodes) for (RenderNode* renderNode : m_childRenderNodes)
{ {
renderNode->Render(uiRenderer, m_modelViewProjMat, m_dynamicDraw); renderNode->Render(uiRenderer, m_modelViewProjMat, m_dynamicDraw);
} }
uiRenderer->SetBaseState(priorBaseState);
} }
else else
{ {
@ -722,7 +720,7 @@ namespace LyShine
// The shader can be outputing premultiplied alpha EITHER if the input texture is premultiplied alpha OR if the // The shader can be outputing premultiplied alpha EITHER if the input texture is premultiplied alpha OR if the
// shader is doing the premultiply of the output color // shader is doing the premultiply of the output color
bool isShaderOutputPremultAlpha = isPreMultiplyAlpha || isTexturePremultipliedAlpha; bool isShaderOutputPremultAlpha = isPreMultiplyAlpha || isTexturePremultipliedAlpha;
int blendModeState = GetBlendModeState(blendMode, isShaderOutputPremultAlpha); AZ::RHI::TargetBlendState blendModeState = GetBlendModeState(blendMode, isShaderOutputPremultAlpha);
PrimitiveListRenderNode* renderNodeToAddTo = nullptr; PrimitiveListRenderNode* renderNodeToAddTo = nullptr;
if (!renderNodeList->empty()) if (!renderNodeList->empty())
@ -801,7 +799,7 @@ namespace LyShine
// The shader can be outputing premultiplied alpha EITHER if the input texture is premultiplied alpha OR if the // The shader can be outputing premultiplied alpha EITHER if the input texture is premultiplied alpha OR if the
// shader is doing the premultiply of the output color // shader is doing the premultiply of the output color
bool isShaderOutputPremultAlpha = isPreMultiplyAlpha || isTexturePremultipliedAlpha; bool isShaderOutputPremultAlpha = isPreMultiplyAlpha || isTexturePremultipliedAlpha;
int blendModeState = GetBlendModeState(blendMode, isShaderOutputPremultAlpha); AZ::RHI::TargetBlendState blendModeState = GetBlendModeState(blendMode, isShaderOutputPremultAlpha);
AlphaMaskType alphaMaskType = isShaderOutputPremultAlpha ? AlphaMaskType::ModulateAlphaAndColor : AlphaMaskType::ModulateAlpha; AlphaMaskType alphaMaskType = isShaderOutputPremultAlpha ? AlphaMaskType::ModulateAlphaAndColor : AlphaMaskType::ModulateAlpha;
PrimitiveListRenderNode* renderNodeToAddTo = nullptr; PrimitiveListRenderNode* renderNodeToAddTo = nullptr;
@ -947,9 +945,12 @@ namespace LyShine
{ {
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = uiRenderer->GetDynamicDrawContext(); AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = uiRenderer->GetDynamicDrawContext();
// Disable stencil and enable blend/color write // Reset stencil and blend mode to defaults (disable stencil and enable blend/color write)
dynamicDraw->SetStencilState(uiRenderer->GetBaseState().m_stencilState); dynamicDraw->SetStencilState(uiRenderer->GetBaseState().m_stencilState);
dynamicDraw->SetTarget0BlendState(uiRenderer->GetBaseState().m_blendState); AZ::RHI::TargetBlendState defaultBlendModeState = GetBlendModeState(LyShine::BlendMode::Normal, false);
defaultBlendModeState.m_enable = uiRenderer->GetBaseState().m_blendStateEnabled;
defaultBlendModeState.m_writeMask = uiRenderer->GetBaseState().m_blendStateWriteMask;
dynamicDraw->SetTarget0BlendState(defaultBlendModeState);
// First render the render targets, they are sorted so that more deeply nested ones are rendered first. // First render the render targets, they are sorted so that more deeply nested ones are rendered first.
// They only need to be rendered the first time that a render graph is rendered after it has been built. // They only need to be rendered the first time that a render graph is rendered after it has been built.
@ -1169,7 +1170,7 @@ namespace LyShine
if (prevPrimListNode) if (prevPrimListNode)
{ {
if (prevPrimListNode->GetBlendModeState() != primListRenderNode->GetBlendModeState()) if (!(prevPrimListNode->GetBlendModeState() == primListRenderNode->GetBlendModeState()))
{ {
++info.m_numNodesDueToBlendMode; ++info.m_numNodesDueToBlendMode;
} }
@ -1346,8 +1347,9 @@ namespace LyShine
} }
// Write heading to logfile for this render node // Write heading to logfile for this render node
logLine = AZStd::string::format("%sPrimitive render node (Blend mode=%d, SRGB=%d). NumPrims=%d, NumTris=%d. Using textures:\r\n", AZ::RHI::TargetBlendState blendMode = primListRenderNode->GetBlendModeState();
indent.c_str(), primListRenderNode->GetBlendModeState(), logLine = AZStd::string::format("%sPrimitive render node (Blend mode=%s, SRGB=%d). NumPrims=%d, NumTris=%d. Using textures:\r\n",
indent.c_str(), blendMode.m_enable ? "enabled" : "disabled",
static_cast<int>(primListRenderNode->GetIsTextureSRGB()), static_cast<int>(primListRenderNode->GetIsTextureSRGB()),
numPrimitives, numTriangles); numPrimitives, numTriangles);
AZ::IO::LocalFileIO::GetInstance()->Write(fileHandle, logLine.c_str(), logLine.size()); AZ::IO::LocalFileIO::GetInstance()->Write(fileHandle, logLine.c_str(), logLine.size());
@ -1415,8 +1417,9 @@ namespace LyShine
#endif #endif
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
int RenderGraph::GetBlendModeState(LyShine::BlendMode blendMode, bool isShaderOutputPremultAlpha) const AZ::RHI::TargetBlendState RenderGraph::GetBlendModeState(LyShine::BlendMode blendMode, [[maybe_unused]] bool isShaderOutputPremultAlpha) const
{ {
// LYSHINE_ATOM_TODO - remove "premultiplyAlpha" parameter and clean up related comments as I think it's no longer needed
// Our blend modes are complicated by the fact we want to be able to render to a render target and then // Our blend modes are complicated by the fact we want to be able to render to a render target and then
// render from that render target texture to the back buffer and get the same result as if we rendered // render from that render target texture to the back buffer and get the same result as if we rendered
// directly to the back buffer. This should be true even if the render target texture does not end up // directly to the back buffer. This should be true even if the render target texture does not end up
@ -1443,72 +1446,47 @@ namespace LyShine
// properly might require shader changes also. For the moment using the blend modes Screen, Darken, Lighten // properly might require shader changes also. For the moment using the blend modes Screen, Darken, Lighten
// is not encouraged, especially when rendering to a render target. // is not encouraged, especially when rendering to a render target.
int flags = GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA; // the default AZ::RHI::TargetBlendState blendState;
blendState.m_blendAlphaSource = AZ::RHI::BlendFactor::One;
blendState.m_blendAlphaDest = AZ::RHI::BlendFactor::AlphaSourceInverse;
switch (blendMode) switch (blendMode)
{ {
case LyShine::BlendMode::Normal: case LyShine::BlendMode::Normal:
// This is the default mode that does an alpha blend by interpolating based on src alpha // This is the default mode that does an alpha blend by interpolating based on src alpha
if (isShaderOutputPremultAlpha) blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource;
{ blendState.m_blendDest = AZ::RHI::BlendFactor::AlphaSourceInverse;
flags = GS_BLSRC_ONE | GS_BLDST_ONEMINUSSRCALPHA;
}
else
{
flags = GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA;
}
break; break;
case LyShine::BlendMode::Add: case LyShine::BlendMode::Add:
// This works well, the amount of the src color added is controlled by src alpha // This works well, the amount of the src color added is controlled by src alpha
if (isShaderOutputPremultAlpha) blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource;
{ blendState.m_blendDest = AZ::RHI::BlendFactor::One;
flags = GS_BLSRC_ONE | GS_BLDST_ONE;
}
else
{
flags = GS_BLSRC_SRCALPHA | GS_BLDST_ONE;
}
break; break;
case LyShine::BlendMode::Screen: case LyShine::BlendMode::Screen:
// This is a poor approximation of the PhotoShop Screen mode but trying to take some account of src alpha // This is a poor approximation of the PhotoShop Screen mode but trying to take some account of src alpha
// In Photoshop this would be 1 - ( (1-SrcColor) * (1-DstColor) ) // In Photoshop this would be 1 - ( (1-SrcColor) * (1-DstColor) )
// So we should use a blend op of multiply but the IRenderer interface doesn't support that. We get some multiply // So we should use a blend op of multiply but the IRenderer interface doesn't support that. We get some multiply
// from GS_BLDST_ONEMINUSSRCCOL which multiplies the DstColor by (1-SrcColor) // from GS_BLDST_ONEMINUSSRCCOL which multiplies the DstColor by (1-SrcColor)
if (isShaderOutputPremultAlpha) blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource;
{ blendState.m_blendDest = AZ::RHI::BlendFactor::ColorSourceInverse;
flags = GS_BLSRC_ONE | GS_BLDST_ONEMINUSSRCCOL;
}
else
{
flags = GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCCOL;
}
break; break;
case LyShine::BlendMode::Darken: case LyShine::BlendMode::Darken:
// This is a poor approximation of the PhotoShop Darken mode but trying to take some account of src alpha // This is a poor approximation of the PhotoShop Darken mode but trying to take some account of src alpha
// In Photoshop Darken means min(SrcColor, DstColor) // In Photoshop Darken means min(SrcColor, DstColor)
if (isShaderOutputPremultAlpha) blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSourceInverse;
{ blendState.m_blendDest = AZ::RHI::BlendFactor::One;
flags = GS_BLOP_MIN | GS_BLSRC_ONE | GS_BLDST_ONE | GS_BLALPHA_MAX; blendState.m_blendOp = AZ::RHI::BlendOp::Minimum;
}
else
{
flags = GS_BLOP_MIN | GS_BLSRC_ONEMINUSSRCALPHA | GS_BLDST_ONE;
}
break; break;
case LyShine::BlendMode::Lighten: case LyShine::BlendMode::Lighten:
// This is a pretty good an approximation of the PhotoShop Lighten mode but trying to take some account of src alpha // This is a pretty good an approximation of the PhotoShop Lighten mode but trying to take some account of src alpha
// In PhotoShop Lighten means max(SrcColor, DstColor) // In PhotoShop Lighten means max(SrcColor, DstColor)
if (isShaderOutputPremultAlpha) blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource;
{ blendState.m_blendDest = AZ::RHI::BlendFactor::One;
flags = GS_BLOP_MAX | GS_BLSRC_ONE| GS_BLDST_ONE; blendState.m_blendOp = AZ::RHI::BlendOp::Maximum;
}
else
{
flags = GS_BLOP_MAX | GS_BLSRC_SRCALPHA | GS_BLDST_ONE;
}
break; break;
} }
return flags; return blendState;
} }
void RenderGraph::SetRttPassesEnabled(UiRenderer* uiRenderer, bool enabled) void RenderGraph::SetRttPassesEnabled(UiRenderer* uiRenderer, bool enabled)

@ -71,9 +71,9 @@ namespace LyShine
// We use a pool allocator to keep these allocations fast. // We use a pool allocator to keep these allocations fast.
AZ_CLASS_ALLOCATOR(PrimitiveListRenderNode, AZ::PoolAllocator, 0); AZ_CLASS_ALLOCATOR(PrimitiveListRenderNode, AZ::PoolAllocator, 0);
PrimitiveListRenderNode(const AZ::Data::Instance<AZ::RPI::Image>& texture, bool isClampTextureMode, bool isTextureSRGB, bool preMultiplyAlpha, int blendModeState); PrimitiveListRenderNode(const AZ::Data::Instance<AZ::RPI::Image>& texture, bool isClampTextureMode, bool isTextureSRGB, bool preMultiplyAlpha, const AZ::RHI::TargetBlendState& blendModeState);
PrimitiveListRenderNode(const AZ::Data::Instance<AZ::RPI::Image>& texture, const AZ::Data::Instance<AZ::RPI::Image>& maskTexture, PrimitiveListRenderNode(const AZ::Data::Instance<AZ::RPI::Image>& texture, const AZ::Data::Instance<AZ::RPI::Image>& maskTexture,
bool isClampTextureMode, bool isTextureSRGB, bool preMultiplyAlpha, AlphaMaskType alphaMaskType, int blendModeState); bool isClampTextureMode, bool isTextureSRGB, bool preMultiplyAlpha, AlphaMaskType alphaMaskType, const AZ::RHI::TargetBlendState& blendModeState);
~PrimitiveListRenderNode() override; ~PrimitiveListRenderNode() override;
void Render(UiRenderer* uiRenderer void Render(UiRenderer* uiRenderer
, const AZ::Matrix4x4& modelViewProjMat , const AZ::Matrix4x4& modelViewProjMat
@ -88,7 +88,7 @@ namespace LyShine
bool GetTextureIsClampMode(int texIndex) const { return m_textures[texIndex].m_isClampTextureMode; } bool GetTextureIsClampMode(int texIndex) const { return m_textures[texIndex].m_isClampTextureMode; }
bool GetIsTextureSRGB() const { return m_isTextureSRGB; } bool GetIsTextureSRGB() const { return m_isTextureSRGB; }
int GetBlendModeState() const { return m_blendModeState; } AZ::RHI::TargetBlendState GetBlendModeState() const { return m_blendModeState; }
bool GetIsPremultiplyAlpha() const { return m_preMultiplyAlpha; } bool GetIsPremultiplyAlpha() const { return m_preMultiplyAlpha; }
AlphaMaskType GetAlphaMaskType() const { return m_alphaMaskType; } AlphaMaskType GetAlphaMaskType() const { return m_alphaMaskType; }
@ -118,7 +118,7 @@ namespace LyShine
bool m_isTextureSRGB; bool m_isTextureSRGB;
bool m_preMultiplyAlpha; bool m_preMultiplyAlpha;
AlphaMaskType m_alphaMaskType; AlphaMaskType m_alphaMaskType;
int m_blendModeState; AZ::RHI::TargetBlendState m_blendModeState;
int m_totalNumVertices; int m_totalNumVertices;
int m_totalNumIndices; int m_totalNumIndices;
@ -340,7 +340,7 @@ namespace LyShine
protected: // member functions protected: // member functions
//! Given a blend mode and whether the shader will be outputing premultiplied alpha, return state flags //! Given a blend mode and whether the shader will be outputing premultiplied alpha, return state flags
int GetBlendModeState(LyShine::BlendMode blendMode, bool isShaderOutputPremultAlpha) const; AZ::RHI::TargetBlendState GetBlendModeState(LyShine::BlendMode blendMode, bool isShaderOutputPremultAlpha) const;
void SetRttPassesEnabled(UiRenderer* uiRenderer, bool enabled); void SetRttPassesEnabled(UiRenderer* uiRenderer, bool enabled);

@ -53,14 +53,8 @@ public: // types
void ResetToDefault() void ResetToDefault()
{ {
// Enable blend/color write // Enable blend/color write
m_blendState.m_enable = true; m_blendStateEnabled = true;
m_blendState.m_writeMask = 0xF; m_blendStateWriteMask = 0xF;
m_blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource;
m_blendState.m_blendDest = AZ::RHI::BlendFactor::AlphaSourceInverse;
m_blendState.m_blendOp = AZ::RHI::BlendOp::Add;
m_blendState.m_blendAlphaSource = AZ::RHI::BlendFactor::One;
m_blendState.m_blendAlphaDest = AZ::RHI::BlendFactor::Zero;
m_blendState.m_blendAlphaOp = AZ::RHI::BlendOp::Add;
// Disable stencil // Disable stencil
m_stencilState = AZ::RHI::StencilState(); m_stencilState = AZ::RHI::StencilState();
@ -70,7 +64,8 @@ public: // types
m_modulateAlpha = false; m_modulateAlpha = false;
} }
AZ::RHI::TargetBlendState m_blendState; uint32_t m_blendStateEnabled = true;
uint32_t m_blendStateWriteMask = 0xF;
AZ::RHI::StencilState m_stencilState; AZ::RHI::StencilState m_stencilState;
bool m_useAlphaTest = false; bool m_useAlphaTest = false;
bool m_modulateAlpha = false; bool m_modulateAlpha = false;

Loading…
Cancel
Save