Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/PostProcess/VisualizeTemporalUpscaler.cpp

347 lines
14 KiB
C++
Raw Normal View History

// Copyright Epic Games, Inc. All Rights Reserved.
#include "PostProcess/TemporalAA.h"
#include "PostProcess/PostProcessing.h"
#include "ScenePrivate.h"
#include "ScreenPass.h"
#include "PixelShaderUtils.h"
#include "UnrealEngine.h"
#include "PostProcess/PostProcessMotionBlur.h"
#include "PostProcess/PostProcessVisualizeBuffer.h"
#include "PostProcess/VisualizeMotionVectors.h"
#include "VisualizeTexture.h"
FScreenPassTexture AddVisualizeTemporalUpscalerPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FVisualizeTemporalUpscalerInputs& Inputs)
{
check(Inputs.SceneColor.IsValid());
RDG_EVENT_SCOPE(GraphBuilder, "VisualizeTemporalUpscaler %dx%d", Inputs.SceneColor.ViewRect.Width(), Inputs.SceneColor.ViewRect.Height());
const bool bSupportsAlpha = IsPostProcessingWithAlphaChannelSupported();
// Allocate output and copy
FScreenPassRenderTarget Output = Inputs.OverrideOutput;
if (!Output.IsValid())
{
Output = FScreenPassRenderTarget::CreateFromInput(GraphBuilder, Inputs.SceneColor, View.GetOverwriteLoadAction(), TEXT("MotionVectors.Visualize"));
}
// Populate all the tiles
TArray<FVisualizeBufferTile> Tiles;
Tiles.SetNum(16);
if (Inputs.TAAConfig != EMainTAAPassConfig::Disabled)
{
FScreenPassTexture OutputTexture;
if (Inputs.Outputs.FullRes.TextureSRV->Desc.Texture->Desc.IsTextureArray())
{
FRDGTextureDesc Desc = Inputs.Outputs.FullRes.TextureSRV->Desc.Texture->Desc;
Desc.Dimension = ETextureDimension::Texture2D;
Desc.ArraySize = 1;
Desc.NumMips = 1;
FRDGTextureRef NewTexture = GraphBuilder.CreateTexture(Desc, Inputs.Outputs.FullRes.TextureSRV->Desc.Texture->Name);
FRHICopyTextureInfo CopyInfo;
CopyInfo.SourceSliceIndex = Inputs.Outputs.FullRes.TextureSRV->Desc.FirstArraySlice;
AddCopyTexturePass(
GraphBuilder,
Inputs.Outputs.FullRes.TextureSRV->Desc.Texture,
NewTexture,
CopyInfo);
OutputTexture = FScreenPassTexture(NewTexture, Inputs.Outputs.FullRes.ViewRect);
}
else
{
OutputTexture = FScreenPassTexture(Inputs.Outputs.FullRes.TextureSRV->Desc.Texture, Inputs.Outputs.FullRes.ViewRect);
}
auto VisualizeTextureLabel = [](FRDGTextureRef Texture, const TCHAR* Suffix = TEXT(""))
{
return FString::Printf(TEXT("vis %s%s"), Texture->Name, Suffix);
};
auto CropViewRectToCenter = [](FIntRect ViewRect)
{
return FIntRect(ViewRect.Min + ViewRect.Size() / 4, ViewRect.Min + (ViewRect.Size() * 3) / 4);
};
// Depth buffer
{
FVisualizeBufferTile& Tile = Tiles[4 * 0 + 0];
Tile.Input.Texture = FVisualizeTexture::AddVisualizeTexturePass(GraphBuilder, View.ShaderMap, Inputs.Inputs.SceneDepth.Texture);
Tile.Input.ViewRect = CropViewRectToCenter(View.ViewRect);
Tile.Label = VisualizeTextureLabel(Inputs.Inputs.SceneDepth.Texture);
}
// Scene Color
{
FVisualizeBufferTile& Tile = Tiles[4 * 0 + 1];
Tile.Input = FScreenPassTexture(Inputs.Inputs.SceneColor.Texture, CropViewRectToCenter(View.ViewRect));
Tile.Label = VisualizeTextureLabel(Inputs.Inputs.SceneColor.Texture);
}
// Scene Color alpha
if (bSupportsAlpha)
{
FVisualizeBufferTile& Tile = Tiles[4 * 3 + 1];
//Tile.Input = FScreenPassTexture(Inputs.Inputs.SceneColorTexture, CropViewRectToCenter(View.ViewRect));
Tile.Input.Texture = FVisualizeTexture::AddVisualizeTextureAlphaPass(GraphBuilder, View.ShaderMap, Inputs.Inputs.SceneColor.Texture);
Tile.Input.ViewRect = CropViewRectToCenter(View.ViewRect);
Tile.Label = VisualizeTextureLabel(Inputs.Inputs.SceneColor.Texture, TEXT(" A"));
}
// Translucency
if (Inputs.TAAConfig == EMainTAAPassConfig::TSR)
{
FVisualizeBufferTile& Tile = Tiles[4 * 0 + 2];
if (Inputs.Inputs.PostDOFTranslucencyResources.IsValid())
{
Tile.Input.Texture = Inputs.Inputs.PostDOFTranslucencyResources.ColorTexture.Resolve;
Tile.Input.ViewRect = CropViewRectToCenter(Inputs.Inputs.PostDOFTranslucencyResources.ViewRect);
Tile.Label = VisualizeTextureLabel(Inputs.Inputs.PostDOFTranslucencyResources.ColorTexture.Resolve);
}
else
{
Tile.Input = FScreenPassTexture(GSystemTextures.GetBlackDummy(GraphBuilder));
Tile.Label = TEXT("No PostDOF Translucency!");
}
}
// Translucency alpha
if (Inputs.TAAConfig == EMainTAAPassConfig::TSR)
{
FVisualizeBufferTile& Tile = Tiles[4 * 0 + 3];
if (Inputs.Inputs.PostDOFTranslucencyResources.IsValid())
{
Tile.Input.Texture = FVisualizeTexture::AddVisualizeTextureAlphaPass(GraphBuilder, View.ShaderMap, Inputs.Inputs.PostDOFTranslucencyResources.ColorTexture.Resolve);
Tile.Input.ViewRect = CropViewRectToCenter(Inputs.Inputs.PostDOFTranslucencyResources.ViewRect);
Tile.Label = VisualizeTextureLabel(Inputs.Inputs.PostDOFTranslucencyResources.ColorTexture.Resolve, TEXT(" A"));
}
else
{
Tile.Input = FScreenPassTexture(GSystemTextures.GetBlackDummy(GraphBuilder));
Tile.Label = TEXT("No PostDOF Translucency!");
}
}
// Motion blur
{
FMotionBlurInputs PassInputs;
PassInputs.SceneColor = Inputs.Outputs.FullRes;
PassInputs.SceneDepth = Inputs.Inputs.SceneDepth;
PassInputs.SceneVelocity = Inputs.Inputs.SceneVelocity;
FVisualizeBufferTile& Tile = Tiles[4 * 1 + 0];
Tile.Input = FScreenPassTexture(AddVisualizeMotionBlurPass(GraphBuilder, View, PassInputs));
Tile.Input.ViewRect = CropViewRectToCenter(Tile.Input.ViewRect);
Tile.Label = TEXT("show VisualizeMotionBlur");
}
// Reprojection
{
FVisualizeMotionVectorsInputs PassInputs;
PassInputs.SceneColor = Inputs.SceneColor;
PassInputs.SceneDepth = Inputs.Inputs.SceneDepth;
PassInputs.SceneVelocity = Inputs.Inputs.SceneVelocity;
FVisualizeBufferTile& Tile = Tiles[4 * 2 + 0];
Implements UMaterial::bHasPixelAnimation TSR's anti-flickering heuristic (r.TSR.ShadingRejection.Flickering=1) analyses how opaque pixels changes overtime and whenever the period of changes get lower than r.TSR.ShadingRejection.Flickering.Period, the heuristic tries to stabilise the pixel by relaxing the rejections. The problems this creates is that any fast moving texture paning and other pixel animation made in material might looks identically flickering to TSR, because not knowing the motion of the pixels animations. TSR currently not having optical flow yet, the artists needs a way to say that the velocity buffer containing the geometric velocities used for reprojection actually exclude pixel animations. This changes implements UMaterial::bHasPixelAnimation the artists can define on opaque geometry. It end up encoded forcing the primitive to draws velocity so that bit can be encoded in velocity buffer when VELOCITY_ENCODE_DEPTH==1 because there is no room left in stencil or gbuffer, and this has the advantage to works for both forward and deferred shading renderer. This gets passdown to shaders through in the primitive uniform parameters so that nanite global shaders FEmitSceneDepthPS and FDepthExportCS can encode it when writing out velocity. It also allows to not need to compile additional material shader permutation of FVelocityPS for non masked opaque geometry. But this causes the limitation currently that any primitive that have at least one shader with this setting enabled will force the entire primitive's material slots to draw this bit in the velocity. TSR in turn disables it's r.TSR.ShadingRejection.Flickering=1 heuristic on any pixel that have it sets using DecodeHasPixelAnimationFromVelocityTexture(). The flag can be visualized by artists with the VisualizeTemporalUpscaler show flag. #rb jeremy.moore #jira UE-187459 [CL 27385162 by guillaume abadie in ue5-main branch]
2023-08-25 14:12:42 -04:00
Tile.Input = AddVisualizeMotionVectorsPass(GraphBuilder, View, PassInputs, EVisualizeMotionVectors::ReprojectionAlignment);
Tile.Input.ViewRect = CropViewRectToCenter(Tile.Input.ViewRect);
Tile.Label = TEXT("show VisualizeReprojection");
}
// Display TSR's luminance used for stability.
if (Inputs.TAAConfig == EMainTAAPassConfig::TSR)
{
FVisualizeBufferTile& Tile = Tiles[4 * 1 + 3];
if (Inputs.Inputs.FlickeringInputTexture.IsValid())
{
Tile.Input = Inputs.Inputs.FlickeringInputTexture;
Tile.Input.ViewRect = CropViewRectToCenter(View.ViewRect);
Tile.Label = VisualizeTextureLabel(Inputs.Inputs.FlickeringInputTexture.Texture);
}
else
{
Tile.Input = FScreenPassTexture(GSystemTextures.GetBlackDummy(GraphBuilder));
Tile.Label = TEXT("No Moire Luma!");
}
}
Implements UMaterial::bHasPixelAnimation TSR's anti-flickering heuristic (r.TSR.ShadingRejection.Flickering=1) analyses how opaque pixels changes overtime and whenever the period of changes get lower than r.TSR.ShadingRejection.Flickering.Period, the heuristic tries to stabilise the pixel by relaxing the rejections. The problems this creates is that any fast moving texture paning and other pixel animation made in material might looks identically flickering to TSR, because not knowing the motion of the pixels animations. TSR currently not having optical flow yet, the artists needs a way to say that the velocity buffer containing the geometric velocities used for reprojection actually exclude pixel animations. This changes implements UMaterial::bHasPixelAnimation the artists can define on opaque geometry. It end up encoded forcing the primitive to draws velocity so that bit can be encoded in velocity buffer when VELOCITY_ENCODE_DEPTH==1 because there is no room left in stencil or gbuffer, and this has the advantage to works for both forward and deferred shading renderer. This gets passdown to shaders through in the primitive uniform parameters so that nanite global shaders FEmitSceneDepthPS and FDepthExportCS can encode it when writing out velocity. It also allows to not need to compile additional material shader permutation of FVelocityPS for non masked opaque geometry. But this causes the limitation currently that any primitive that have at least one shader with this setting enabled will force the entire primitive's material slots to draw this bit in the velocity. TSR in turn disables it's r.TSR.ShadingRejection.Flickering=1 heuristic on any pixel that have it sets using DecodeHasPixelAnimationFromVelocityTexture(). The flag can be visualized by artists with the VisualizeTemporalUpscaler show flag. #rb jeremy.moore #jira UE-187459 [CL 27385162 by guillaume abadie in ue5-main branch]
2023-08-25 14:12:42 -04:00
// Display UMaterial::bHasPixelAnimation used to disable TSR's anti-flickering heuristic (r.TSR.ShadingRejection.Flickering) on per pixel basis
if (Inputs.TAAConfig == EMainTAAPassConfig::TSR)
{
FVisualizeMotionVectorsInputs PassInputs;
PassInputs.SceneColor = Inputs.SceneColor;
PassInputs.SceneDepth = Inputs.Inputs.SceneDepth;
PassInputs.SceneVelocity = Inputs.Inputs.SceneVelocity;
FVisualizeBufferTile& Tile = Tiles[4 * 2 + 3];
Tile.Input = AddVisualizeMotionVectorsPass(GraphBuilder, View, PassInputs, EVisualizeMotionVectors::HasPixelAnimationFlag);
Tile.Input.ViewRect = CropViewRectToCenter(Tile.Input.ViewRect);
Tile.Label = TEXT("UMaterial::bHasPixelAnimation");
}
// Output
{
FVisualizeBufferTile& Tile = Tiles[4 * 3 + 3];
Tile.Input.Texture = OutputTexture.Texture;
Tile.Input.ViewRect = CropViewRectToCenter(OutputTexture.ViewRect);
Tile.Label = VisualizeTextureLabel(OutputTexture.Texture);
}
// Output alpha
if (bSupportsAlpha)
{
FVisualizeBufferTile& Tile = Tiles[4 * 3 + 2];
Tile.Input.Texture = FVisualizeTexture::AddVisualizeTextureAlphaPass(GraphBuilder, View.ShaderMap, OutputTexture.Texture);
Tile.Input.ViewRect = CropViewRectToCenter(OutputTexture.ViewRect);
Tile.Label = VisualizeTextureLabel(OutputTexture.Texture, TEXT(" A"));
}
// Black bottom left corner
{
FVisualizeBufferTile& Tile = Tiles[4 * 3 + 0];
Tile.Input = FScreenPassTexture(GSystemTextures.GetBlackDummy(GraphBuilder));
Tile.Label = TEXT("Summary");
}
}
// Draws all overview tiles
{
FVisualizeBufferInputs PassInputs;
PassInputs.OverrideOutput = Output;
PassInputs.SceneColor = Inputs.SceneColor;
PassInputs.Tiles = Tiles;
AddVisualizeBufferPass(GraphBuilder, View, PassInputs);
}
// Draw additional text
// Early return if not using a temporal upscaler.
if (Inputs.TAAConfig != EMainTAAPassConfig::Disabled)
{
AddDrawCanvasPass(GraphBuilder, RDG_EVENT_NAME("VisualizeTemporalUpscaler Text"), View, FScreenPassRenderTarget(Output, ERenderTargetLoadAction::ELoad),
[&View, &ViewRect = Output.ViewRect, &Inputs, bSupportsAlpha](FCanvas& Canvas)
{
const float DPIScale = Canvas.GetDPIScale();
Canvas.SetBaseTransform(FMatrix(FScaleMatrix(DPIScale) * Canvas.CalcBaseTransform2D(Canvas.GetViewRect().Width(), Canvas.GetViewRect().Height())));
auto QuickDrawSummary = [&](int32 Location, const FString& Text)
{
FIntPoint LabelLocation(20, 20 + 22 * Location + (ViewRect.Height() * 3) / 4);
Canvas.DrawShadowedString(LabelLocation.X / DPIScale, LabelLocation.Y / DPIScale, *Text, GetStatsFont(), FLinearColor::White);
};
// Display which temporal upscaler is being used.
{
static auto CVarAntiAliasingQuality = IConsoleManager::Get().FindConsoleVariable(TEXT("sg.AntiAliasingQuality"));
check(CVarAntiAliasingQuality);
FString Text;
if (Inputs.TAAConfig == EMainTAAPassConfig::TAA)
{
Text = FString::Printf(TEXT("TemporalUpscaler: TAA (sg.AntiAliasingQuality=%d)"), CVarAntiAliasingQuality->GetInt());
}
else if (Inputs.TAAConfig == EMainTAAPassConfig::TSR)
{
Text = FString::Printf(TEXT("TemporalUpscaler: TSR (sg.AntiAliasingQuality=%d)"), CVarAntiAliasingQuality->GetInt());
}
else
{
Text = FString::Printf(TEXT("ThirdParty TemporalUpscaler: %s"), Inputs.UpscalerUsed->GetDebugName());
}
QuickDrawSummary(/* Location = */ 0, Text);
}
// Display the input/output resolutions
{
QuickDrawSummary(/* Location = */ 1, FString::Printf(TEXT("Input: %dx%d %s"), View.ViewRect.Width(), View.ViewRect.Height(), GPixelFormats[Inputs.Inputs.SceneColor.Texture->Desc.Format].Name));
QuickDrawSummary(/* Location = */ 2, FString::Printf(TEXT("Output: %dx%d %s"), Inputs.Outputs.FullRes.ViewRect.Width(), Inputs.Outputs.FullRes.ViewRect.Height(), GPixelFormats[Inputs.Outputs.FullRes.TextureSRV->Desc.Texture->Desc.Format].Name));
}
// Display the pre-exposure being used
{
static auto CVarPreExposureOverride = IConsoleManager::Get().FindConsoleVariable(TEXT("r.EyeAdaptation.PreExposureOverride"));
check(CVarPreExposureOverride);
FString Text = FString::Printf(TEXT("PreExposure: %f"), View.PreExposure);
if (CVarPreExposureOverride->GetFloat() > 0.0f)
{
Text += TEXT(" (r.EyeAdaptation.PreExposureOverride)");
}
QuickDrawSummary(/* Location = */ 3, Text);
}
// Display alpha support
{
FString Text = bSupportsAlpha ? TEXT("r.PostProcessing.PropagateAlpha=true") : TEXT("r.PostProcessing.PropagateAlpha=false");
if (Inputs.TAAConfig == EMainTAAPassConfig::ThirdParty)
{
Text = TEXT("Unknown");
}
QuickDrawSummary(/* Location = */ 4, TEXT("Support Alpha: ") + Text);
}
// Display memory size of the history.
{
static auto CVarAntiAliasingQuality = IConsoleManager::Get().FindConsoleVariable(TEXT("sg.AntiAliasingQuality"));
check(CVarAntiAliasingQuality);
const uint64 PingPongHistorySizeMultiplier = 2;
uint64 HistorySize = 0;
if (Inputs.TAAConfig == EMainTAAPassConfig::TAA)
{
HistorySize = View.PrevViewInfo.TemporalAAHistory.GetGPUSizeBytes(/* bLogSizes = */ false) * PingPongHistorySizeMultiplier;
}
else if (Inputs.TAAConfig == EMainTAAPassConfig::TSR && View.PrevViewInfo.TSRHistory.IsValid())
{
HistorySize = View.PrevViewInfo.TSRHistory.GetGPUSizeBytes(/* bLogSizes = */ false);
bool bHasTSRHistoryResurrection = View.PrevViewInfo.TSRHistory.MetadataArray->GetDesc().ArraySize > 1;
bool bHasTSRPingPongHistory = !bHasTSRHistoryResurrection;
if (bHasTSRPingPongHistory)
{
HistorySize *= PingPongHistorySizeMultiplier;
}
}
else if (Inputs.TAAConfig == EMainTAAPassConfig::ThirdParty && View.PrevViewInfo.ThirdPartyTemporalUpscalerHistory.IsValid())
{
HistorySize = View.PrevViewInfo.ThirdPartyTemporalUpscalerHistory->GetGPUSizeBytes();
}
QuickDrawSummary(/* Location = */ 5, FString::Printf(TEXT("History VRAM footprint: %.1f MB"), float(HistorySize) / float(1024 * 1024)));
}
// Display if any additional sharpening is happening
{
static auto CVarSharpen = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Tonemapper.Sharpen"));
check(CVarSharpen);
float Sharpen = CVarSharpen->GetFloat();
Sharpen = (Sharpen < 0) ? View.FinalPostProcessSettings.Sharpen : Sharpen;
QuickDrawSummary(/* Location = */ 6, Sharpen > 0 ? FString::Printf(TEXT("Tonemapper Sharpen: %f"), Sharpen) : TEXT("Tonemapper Sharpen: Off"));
}
});
}
else
{
check(Inputs.TAAConfig == EMainTAAPassConfig::Disabled);
AddDrawCanvasPass(GraphBuilder, RDG_EVENT_NAME("VisualizeTemporalUpscaler Text"), View, FScreenPassRenderTarget(Output, ERenderTargetLoadAction::ELoad),
[&ViewRect = Output.ViewRect](FCanvas& Canvas)
{
const float DPIScale = Canvas.GetDPIScale();
Canvas.SetBaseTransform(FMatrix(FScaleMatrix(DPIScale) * Canvas.CalcBaseTransform2D(Canvas.GetViewRect().Width(), Canvas.GetViewRect().Height())));
FIntPoint LabelLocation(60, 60);
Canvas.DrawShadowedString(LabelLocation.X / DPIScale, LabelLocation.Y / DPIScale, TEXT("No temporal upscaler used"), GetStatsFont(), FLinearColor::Red);
});
}
return MoveTemp(Output);
}