Files
UnrealEngine/Engine/Plugins/Runtime/nDisplay/Source/DisplayClusterShaders/Private/Shaders/DisplayClusterShadersCopyTexture.cpp
2025-05-18 13:04:45 +08:00

512 lines
18 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Shaders/DisplayClusterShadersCopyTexture.h"
#include "DisplayClusterShadersLog.h"
#include "GlobalShader.h"
#include "ShaderParameters.h"
#include "ShaderParameterUtils.h"
#include "TextureResource.h"
#include "RHIStaticStates.h"
#include "RenderResource.h"
#include "RenderingThread.h"
#include "CommonRenderResources.h"
#include "PixelShaderUtils.h"
#include "ClearQuad.h"
#include "GlobalShader.h"
#include "ShaderParameters.h"
#include "ShaderParameterUtils.h"
#include "ScreenRendering.h"
#include "RenderGraphUtils.h"
#include "RenderGraphResources.h"
#include "PostProcess/DrawRectangle.h"
#include "PostProcess/PostProcessMaterialInputs.h"
#include "SceneView.h"
#include "ScreenPass.h"
#include "Engine/TextureRenderTarget.h"
namespace UE::DisplayClusterShaders::Private
{
/** Returns the RHI blend state for the requested DC blend mode. */
FRHIBlendState* GetBlendStateRHI(const EColorWriteMask InColorMask)
{
if (InColorMask == EColorWriteMask::CW_ALPHA)
{
// Copy alpha channel from source to dest
return TStaticBlendState <CW_ALPHA, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero>::GetRHI();
}
else if (InColorMask == EColorWriteMask::CW_RGB)
{
// Copy only RGB channels from source to dest
return TStaticBlendState <CW_RGB, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero>::GetRHI();
}
return TStaticBlendState<>::GetRHI();
}
/** Enumerations that cover definitions in the shader. */
enum class ESourceEncoding : uint32
{
Linear = 0, // #define ESourceEncoding_Linear 0
Gamma = 1, // #define ESourceEncoding_Gamma 1
sRGB = 2, // #define ESourceEncoding_sRGB 2
MediaPQ = 3, // #define ESourceEncoding_MediaPQ 3
};
enum class EColorPremultiply : uint32
{
None = 0, // #define EColorPremultiply_None 0
Alpha = 1, // #define EColorPremultiply_Alpha 1
InvertedAlpha = 2, // #define EColorPremultiply_InvertedAlpha 2
};
/** Enumerations that cover definitions in the shader. */
enum class EOverrideAlpha : uint32
{
None = 0, // #define EOverrideAlpha_None 0
Invert = 1, // #define EOverrideAlpha_Invert 1
One = 2, // #define EOverrideAlpha_One 2
Zero = 3, // #define EOverrideAlpha_Zero 3
};
namespace FColorEncodingCopyRectPSPermutation
{
// Shared permutation for picp warp
class FPermutationEncodeInput : SHADER_PERMUTATION_BOOL("ENCODE_INPUT");
class FPermutationEncodeOutput : SHADER_PERMUTATION_BOOL("ENCODE_OUTPUT");
class FPermutationOverrideAlpha : SHADER_PERMUTATION_BOOL("OVERRIDE_ALPHA");
class FPermutationColorPremultiply : SHADER_PERMUTATION_BOOL("COLOR_PREMULTIPLY");
using FCommonPSDomain = TShaderPermutationDomain<
FPermutationEncodeInput,
FPermutationEncodeOutput,
FPermutationOverrideAlpha,
FPermutationColorPremultiply
>;
bool ShouldCompileCommonPSPermutation(const FCommonPSDomain& PermutationVector)
{
return true;
}
};
/** RDG copy texture parameters. */
BEGIN_SHADER_PARAMETER_STRUCT(FDisplayClusterCopyTextureParameters, )
RDG_TEXTURE_ACCESS(Input, ERHIAccess::CopySrc)
RDG_TEXTURE_ACCESS(Output, ERHIAccess::CopyDest)
END_SHADER_PARAMETER_STRUCT()
/**
* Pixel shaders parameters for 'FColorEncodingCopyRectPS'
*/
BEGIN_SHADER_PARAMETER_STRUCT(FColorEncodingCopyRectPSParameters, )
SHADER_PARAMETER_TEXTURE(Texture2D, InputTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, InputTextureSampler)
SHADER_PARAMETER(FUint32Vector, ColorPremultiply)
SHADER_PARAMETER(FUint32Vector, Encodings)
SHADER_PARAMETER(FVector3f, DisplayGamma)
END_SHADER_PARAMETER_STRUCT()
/**
* A pixel shader for TextureShare resource resampling
*/
class FColorEncodingCopyRectPS : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FColorEncodingCopyRectPS);
SHADER_USE_PARAMETER_STRUCT(FColorEncodingCopyRectPS, FGlobalShader);
using FPermutationDomain = FColorEncodingCopyRectPSPermutation::FCommonPSDomain;
using FParameters = FColorEncodingCopyRectPSParameters;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return FColorEncodingCopyRectPSPermutation::ShouldCompileCommonPSPermutation(FPermutationDomain(Parameters.PermutationId));
}
/** Initialize shader parameters. */
static bool InitializeShaderParameters(
const FDisplayClusterShadersTextureViewportContext& Input,
const FDisplayClusterShadersTextureViewportContext& Output,
const FDisplayClusterShadersTextureUtilsSettings& Settings,
FParameters& OutParameters,
FColorEncodingCopyRectPSPermutation::FCommonPSDomain& OutPermutationVector)
{
OutParameters.InputTextureSampler = (Input.Rect.Size() == Output.Rect.Size())
? TStaticSamplerState<SF_Point>::GetRHI() // Use the 'Point' sampler if the input and output texture sizes are equal
: TStaticSamplerState<SF_Bilinear>::GetRHI();
// Setup color encoding.
// The default `0` is ESceneColorSourceEncoding::Linear.
// Also linear encoding is used for HoldoutComposite input
OutParameters.Encodings = FUint32Vector::ZeroValue;
OutParameters.DisplayGamma = FVector3f(1.f, 1.f, 1.f);
const bool UseGammaEncoding = (Settings.ColorMask & EColorWriteMask::CW_RGB)
&& !Input.ColorEncoding.IsEqualsGammaEncoding(Output.ColorEncoding);
// Only apply color transform if input and output encodings are not equal and color channels are used.
if (UseGammaEncoding)
{
// Gets source and dest gamma values:
const float DefaultDisplayGamma = UTextureRenderTarget::GetDefaultDisplayGamma();
const float SrcGamma = (Input.ColorEncoding.GammaValue > 0) ? Input.ColorEncoding.GammaValue : DefaultDisplayGamma;
const float DestGamma = (Output.ColorEncoding.GammaValue > 0) ? Output.ColorEncoding.GammaValue : DefaultDisplayGamma;
if (Input.ColorEncoding.Encoding == Output.ColorEncoding.Encoding
&& Input.ColorEncoding.Encoding == EDisplayClusterColorEncoding::Gamma)
{
// Convert Gamma->Gamma in one pow()
OutParameters.Encodings.X = static_cast<uint32>(ESourceEncoding::Gamma);
OutParameters.DisplayGamma.X = SrcGamma / DestGamma;
OutPermutationVector.Set<FColorEncodingCopyRectPSPermutation::FPermutationEncodeInput>(true);
}
else // Transformation via linear color space:
{
// Convert input encoding to the linear
switch (Input.ColorEncoding.Encoding)
{
case EDisplayClusterColorEncoding::Gamma: // Gamma -> Linear
OutParameters.Encodings.X = static_cast<uint32>(ESourceEncoding::Gamma);
OutParameters.DisplayGamma.X = SrcGamma;
OutPermutationVector.Set<FColorEncodingCopyRectPSPermutation::FPermutationEncodeInput>(true);
break;
case EDisplayClusterColorEncoding::sRGB: // sRGB -> Linear
OutParameters.Encodings.X = static_cast<uint32>(ESourceEncoding::sRGB);
OutPermutationVector.Set<FColorEncodingCopyRectPSPermutation::FPermutationEncodeInput>(true);
break;
case EDisplayClusterColorEncoding::MediaPQ: // MediaPQ -> Linear
OutParameters.Encodings.X = static_cast<uint32>(ESourceEncoding::MediaPQ);
OutPermutationVector.Set<FColorEncodingCopyRectPSPermutation::FPermutationEncodeInput>(true);
break;
default:
break;
}
// Convert linear to the output encoding
switch (Output.ColorEncoding.Encoding)
{
case EDisplayClusterColorEncoding::Gamma: // Linear -> Gamma
OutParameters.Encodings.Y = static_cast<uint32>(ESourceEncoding::Gamma);
OutParameters.DisplayGamma.Y = 1.0f / DestGamma;
OutPermutationVector.Set<FColorEncodingCopyRectPSPermutation::FPermutationEncodeOutput>(true);
break;
case EDisplayClusterColorEncoding::sRGB: // Linear -> sRGB
OutParameters.Encodings.Y = static_cast<uint32>(ESourceEncoding::sRGB);
OutPermutationVector.Set<FColorEncodingCopyRectPSPermutation::FPermutationEncodeOutput>(true);
break;
case EDisplayClusterColorEncoding::MediaPQ: // Linear -> MediaPQ
OutParameters.Encodings.Y = static_cast<uint32>(ESourceEncoding::MediaPQ);
OutPermutationVector.Set<FColorEncodingCopyRectPSPermutation::FPermutationEncodeOutput>(true);
break;
default:
break;
}
}
}
// Setup Color Premultiply
OutParameters.ColorPremultiply = FUint32Vector::ZeroValue;
if (Input.ColorEncoding.Premultiply != Output.ColorEncoding.Premultiply
|| (UseGammaEncoding && Input.ColorEncoding.Premultiply != EDisplayClusterColorPremultiply::None))
{
switch (Input.ColorEncoding.Premultiply)
{
case EDisplayClusterColorPremultiply::Premultiply:
OutParameters.ColorPremultiply.X = static_cast<uint32>(EColorPremultiply::Alpha);
OutPermutationVector.Set<FColorEncodingCopyRectPSPermutation::FPermutationColorPremultiply>(true);
break;
case EDisplayClusterColorPremultiply::InvertPremultiply:
OutParameters.ColorPremultiply.X = static_cast<uint32>(EColorPremultiply::InvertedAlpha);
OutPermutationVector.Set<FColorEncodingCopyRectPSPermutation::FPermutationColorPremultiply>(true);
break;
default:
break;
}
switch (Output.ColorEncoding.Premultiply)
{
case EDisplayClusterColorPremultiply::Premultiply:
OutParameters.ColorPremultiply.Y = static_cast<uint32>(EColorPremultiply::Alpha);
OutPermutationVector.Set<FColorEncodingCopyRectPSPermutation::FPermutationColorPremultiply>(true);
break;
case EDisplayClusterColorPremultiply::InvertPremultiply:
OutParameters.ColorPremultiply.Y = static_cast<uint32>(EColorPremultiply::InvertedAlpha);
OutPermutationVector.Set<FColorEncodingCopyRectPSPermutation::FPermutationColorPremultiply>(true);
break;
default:
break;
}
}
// Override alpha
switch(Settings.OverrideAlpha)
{
case EDisplayClusterShaderTextureUtilsOverrideAlpha::Invert_Alpha:
OutParameters.Encodings.Z = static_cast<uint32>(EOverrideAlpha::Invert);
OutPermutationVector.Set<FColorEncodingCopyRectPSPermutation::FPermutationOverrideAlpha>(true);
break;
case EDisplayClusterShaderTextureUtilsOverrideAlpha::Set_Alpha_One:
OutParameters.Encodings.Z = static_cast<uint32>(EOverrideAlpha::One);
OutPermutationVector.Set<FColorEncodingCopyRectPSPermutation::FPermutationOverrideAlpha>(true);
break;
case EDisplayClusterShaderTextureUtilsOverrideAlpha::Set_Alpha_Zero:
OutParameters.Encodings.Z = static_cast<uint32>(EOverrideAlpha::Zero);
OutPermutationVector.Set<FColorEncodingCopyRectPSPermutation::FPermutationOverrideAlpha>(true);
break;
}
// Check the permutation vectors. This should prevent a crash when no shader permutation is found.
if (!FColorEncodingCopyRectPSPermutation::ShouldCompileCommonPSPermutation(OutPermutationVector))
{
UE_LOG(LogDisplayClusterShaders, Warning, TEXT("Invalid permutation vector %d for shader `FColorEncodingCopyRectPS`"), OutPermutationVector.ToDimensionValueId());
return false;
}
return true;
}
/** Render input to output using this shader. */
static bool RenderPass(
FRHICommandListImmediate& RHICmdList,
FRHITexture* SrcTexture,
FRHITexture* DestTexture,
const FDisplayClusterShadersTextureViewportContext& InputContext,
const FDisplayClusterShadersTextureViewportContext& OutputContext,
const FDisplayClusterShadersTextureUtilsSettings& Settings)
{
if (!SrcTexture || !DestTexture)
{
return false;
}
// Initialize shader parameters and permutation vectors
FParameters PixelShaderParameters;
FColorEncodingCopyRectPSPermutation::FCommonPSDomain PermutationVector;
if (!InitializeShaderParameters(InputContext, OutputContext, Settings, PixelShaderParameters, PermutationVector))
{
return false;
}
PixelShaderParameters.InputTexture = SrcTexture;
FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel);
TShaderMapRef<FScreenVS> VertexShader(ShaderMap);
TShaderMapRef<FColorEncodingCopyRectPS> PixelShader(ShaderMap, PermutationVector);
if (!VertexShader.IsValid() || !PixelShader.IsValid())
{
// Always check if shaders are available on the current platform and hardware
return false;
}
const FIntPoint& SrcTextureSize = SrcTexture->GetDesc().Extent;
const FIntPoint& DestTextureSize = DestTexture->GetDesc().Extent;
const FIntRect& SrcRect = InputContext.Rect;
const FIntRect& DestRect = OutputContext.Rect;
RHICmdList.Transition(FRHITransitionInfo(SrcTexture, ERHIAccess::Unknown, ERHIAccess::SRVMask));
RHICmdList.Transition(FRHITransitionInfo(DestTexture, ERHIAccess::Unknown, ERHIAccess::RTV));
FRHIRenderPassInfo RPInfo(DestTexture, ERenderTargetActions::Load_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("nDisplay.Shaders.ColorEncodingCopyRect"));
{
RHICmdList.SetViewport(0.0f, 0.0f, 0.0f, DestTextureSize.X, DestTextureSize.Y, 1.0f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.BlendState = GetBlendStateRHI(Settings.ColorMask);
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), PixelShaderParameters);
UE::Renderer::PostProcess::DrawRectangle(
RHICmdList, VertexShader,
DestRect.Min.X, DestRect.Min.Y,
DestRect.Size().X, DestRect.Size().Y,
SrcRect.Min.X, SrcRect.Min.Y,
SrcRect.Size().X, SrcRect.Size().Y,
DestTextureSize, SrcTextureSize
);
}
RHICmdList.EndRenderPass();
RHICmdList.Transition(FRHITransitionInfo(DestTexture, ERHIAccess::Unknown, ERHIAccess::SRVMask));
return true;
}
};
IMPLEMENT_GLOBAL_SHADER(FColorEncodingCopyRectPS, "/Plugin/nDisplay/Private/ResourceUtils.usf", "Main", SF_Pixel);
/** Return CopyTextureInfo structure. */
static inline FRHICopyTextureInfo GetRHICopyTextureInfo(
const FDisplayClusterShadersTextureViewport& Input,
const FDisplayClusterShadersTextureViewport& Output,
const FDisplayClusterShadersTextureUtilsSettings& InSettings)
{
FRHICopyTextureInfo OutCopyInfo;
OutCopyInfo.SourceSliceIndex = InSettings.SourceSliceIndex;
OutCopyInfo.DestSliceIndex = InSettings.DestSliceIndex;
OutCopyInfo.SourcePosition = FIntVector(Input.Rect.Min.X, Input.Rect.Min.Y, 0);
OutCopyInfo.DestPosition = FIntVector(Output.Rect.Min.X, Output.Rect.Min.Y, 0);
OutCopyInfo.Size = FIntVector(Output.Rect.Width(), Output.Rect.Height(), 0);
return OutCopyInfo;
}
};
bool FDisplayClusterShadersCopyTexture::ColorEncodingCopyRect_RenderThread(
FRHICommandListImmediate& RHICmdList,
const FDisplayClusterShadersTextureViewportContext& Input,
const FDisplayClusterShadersTextureViewportContext& Output,
const FDisplayClusterShadersTextureUtilsSettings& Settings)
{
using namespace UE::DisplayClusterShaders::Private;
return FColorEncodingCopyRectPS::RenderPass(RHICmdList, Input.TextureRHI, Output.TextureRHI, Input, Output, Settings);
}
/**
* Copy RDG textures via shader.
*/
bool FDisplayClusterShadersCopyTexture::AddPassColorEncodingCopyRect_RenderThread(
FRDGBuilder& GraphBuilder,
const FDisplayClusterShadersTextureViewportContext& Input,
const FDisplayClusterShadersTextureViewportContext& Output,
const FDisplayClusterShadersTextureUtilsSettings& Settings)
{
using namespace UE::DisplayClusterShaders::Private;
if (!Input.TextureRDG || !Output.TextureRDG)
{
return false;
}
// Initialize render pass parameters.
FDisplayClusterCopyTextureParameters* Parameters = GraphBuilder.AllocParameters<FDisplayClusterCopyTextureParameters>();
Parameters->Input = Input.TextureRDG;
Parameters->Output = Output.TextureRDG;
if (Input.bExternalTextureRDG)
{
GraphBuilder.SetTextureAccessFinal(Input.TextureRDG, ERHIAccess::SRVGraphics);
}
if (Output.bExternalTextureRDG)
{
GraphBuilder.SetTextureAccessFinal(Output.TextureRDG, ERHIAccess::RTV);
}
GraphBuilder.AddPass(
RDG_EVENT_NAME("nDisplayShaders.ResampleTexture"),
Parameters,
ERDGPassFlags::Copy | ERDGPassFlags::NeverCull,
[Parameters, Input, Output, Settings](FRHICommandListImmediate& RHICmdList)
{
FColorEncodingCopyRectPS::RenderPass(
RHICmdList,
Parameters->Input->GetRHI(),
Parameters->Output->GetRHI(),
Input, Output, Settings);
});
return true;
}
/**
* Copy RDG textures without shader.
*/
bool FDisplayClusterShadersCopyTexture::AddPassTransitionAndCopyTexture_RenderThread(
FRDGBuilder& GraphBuilder,
const FDisplayClusterShadersTextureViewport& Input,
const FDisplayClusterShadersTextureViewport& Output,
const FDisplayClusterShadersTextureUtilsSettings& Settings)
{
using namespace UE::DisplayClusterShaders::Private;
if (!Input.TextureRDG || !Output.TextureRDG)
{
return false;
}
if (Input.Rect.Size() != Output.Rect.Size())
{
return false;
}
// Initialize render pass parameters.
FDisplayClusterCopyTextureParameters* Parameters = GraphBuilder.AllocParameters<FDisplayClusterCopyTextureParameters>();
Parameters->Input = Input.TextureRDG;
Parameters->Output = Output.TextureRDG;
GraphBuilder.AddPass(
RDG_EVENT_NAME("nDisplayShaders.CopyTexture"),
Parameters,
ERDGPassFlags::Copy | ERDGPassFlags::NeverCull,
[ Input, Output, Settings](FRHICommandListImmediate& RHICmdList)
{
FRHITexture* Src = Input.TextureRDG->GetRHI();
FRHITexture* Dest = Output.TextureRDG->GetRHI();
if (Src && Dest)
{
TransitionAndCopyTexture(RHICmdList, Src, Dest, GetRHICopyTextureInfo(Input, Output, Settings));
}
});
return true;
}
bool FDisplayClusterShadersCopyTexture::TransitionAndCopyTexture_RenderThread(
FRHICommandListImmediate& RHICmdList,
const FDisplayClusterShadersTextureViewport& Input,
const FDisplayClusterShadersTextureViewport& Output,
const FDisplayClusterShadersTextureUtilsSettings& InSettings)
{
using namespace UE::DisplayClusterShaders::Private;
if (!Input.TextureRHI || !Output.TextureRHI)
{
return false;
}
if (Input.Rect.Size() != Output.Rect.Size())
{
return false;
}
TransitionAndCopyTexture(RHICmdList,
Input.TextureRHI,
Output.TextureRHI,
GetRHICopyTextureInfo(Input, Output, InSettings));
return true;
}