// 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 ::GetRHI(); } else if (InColorMask == EColorWriteMask::CW_RGB) { // Copy only RGB channels from source to dest return TStaticBlendState ::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::GetRHI() // Use the 'Point' sampler if the input and output texture sizes are equal : TStaticSamplerState::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(ESourceEncoding::Gamma); OutParameters.DisplayGamma.X = SrcGamma / DestGamma; OutPermutationVector.Set(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(ESourceEncoding::Gamma); OutParameters.DisplayGamma.X = SrcGamma; OutPermutationVector.Set(true); break; case EDisplayClusterColorEncoding::sRGB: // sRGB -> Linear OutParameters.Encodings.X = static_cast(ESourceEncoding::sRGB); OutPermutationVector.Set(true); break; case EDisplayClusterColorEncoding::MediaPQ: // MediaPQ -> Linear OutParameters.Encodings.X = static_cast(ESourceEncoding::MediaPQ); OutPermutationVector.Set(true); break; default: break; } // Convert linear to the output encoding switch (Output.ColorEncoding.Encoding) { case EDisplayClusterColorEncoding::Gamma: // Linear -> Gamma OutParameters.Encodings.Y = static_cast(ESourceEncoding::Gamma); OutParameters.DisplayGamma.Y = 1.0f / DestGamma; OutPermutationVector.Set(true); break; case EDisplayClusterColorEncoding::sRGB: // Linear -> sRGB OutParameters.Encodings.Y = static_cast(ESourceEncoding::sRGB); OutPermutationVector.Set(true); break; case EDisplayClusterColorEncoding::MediaPQ: // Linear -> MediaPQ OutParameters.Encodings.Y = static_cast(ESourceEncoding::MediaPQ); OutPermutationVector.Set(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(EColorPremultiply::Alpha); OutPermutationVector.Set(true); break; case EDisplayClusterColorPremultiply::InvertPremultiply: OutParameters.ColorPremultiply.X = static_cast(EColorPremultiply::InvertedAlpha); OutPermutationVector.Set(true); break; default: break; } switch (Output.ColorEncoding.Premultiply) { case EDisplayClusterColorPremultiply::Premultiply: OutParameters.ColorPremultiply.Y = static_cast(EColorPremultiply::Alpha); OutPermutationVector.Set(true); break; case EDisplayClusterColorPremultiply::InvertPremultiply: OutParameters.ColorPremultiply.Y = static_cast(EColorPremultiply::InvertedAlpha); OutPermutationVector.Set(true); break; default: break; } } // Override alpha switch(Settings.OverrideAlpha) { case EDisplayClusterShaderTextureUtilsOverrideAlpha::Invert_Alpha: OutParameters.Encodings.Z = static_cast(EOverrideAlpha::Invert); OutPermutationVector.Set(true); break; case EDisplayClusterShaderTextureUtilsOverrideAlpha::Set_Alpha_One: OutParameters.Encodings.Z = static_cast(EOverrideAlpha::One); OutPermutationVector.Set(true); break; case EDisplayClusterShaderTextureUtilsOverrideAlpha::Set_Alpha_Zero: OutParameters.Encodings.Z = static_cast(EOverrideAlpha::Zero); OutPermutationVector.Set(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 VertexShader(ShaderMap); TShaderMapRef 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::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(); 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(); 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; }