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

148 lines
5.9 KiB
C++
Raw Normal View History

Implemented a Nanite visualization overview, with a data-driven tile layout. This is similar to the gbuffer overview, except using a custom global shader instead of the post process material shader approach. Additionally, I managed to do this with just a single Nanite cull/raster pass. We already had a rasterizer permutation for retail vs. any visualization enabled, but it was a single ordinal mode. The rasterizer would do a 64b max and a 32b add. Was worried I needed arrays, multi-pass, strided output buffer, etc.. but then realized max is only used by raster mode, and add is only used by overdraw mode. I changed it all to a bitmask, run the rasterizer with that, and then multi-pass all the modes in screen space off the buffer. Renamed Material Depth -> Material ID, and Hit Proxy Depth -> Hit Proxy ID Moved Material ID into the Standard commands grouping, as it can be useful for content creators. Made the editor hide all the Advanced commands by default, can make them available in the UX with r.Nanite.Visualize.Advanced now. Command line has full access to all of them, regardless of this setting. Renamed r.Nanite.DebugSceneComposite to r.Nanite.Visualize.Composite, and changed the behavior for -1=default, 0=off, 1=on Renamed r.Nanite.DebugSobelFilter to r.Nanite.Visualize.EdgeDetect and defaulted it on for content creator benefit. Removed int4 VisualizeConfig member from Rasterizer UB, and added 32bit visualization mode mask instead. Removed 32bit RasterStateReverseCull member from Rasterizer UB, and folded it into pre-existing 32bit RenderFlags Renamed Nanite::FDebugVisualizeCS -> Nanite::FNaniteVisualizeCS Renamed DebugVisualize.usf -> Visualize.usf Made all visualizations composite against scene depth by default, except the overdraw mode. Changed overdraw view mode to use ColorMapInferno from ColorMap.ush #rb brian.karis #fyi michal.valient, rune.stubbe [CL 15828833 by graham wihlidal in ue5-main branch]
2021-03-25 15:02:12 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
#include "PostProcess/PostProcessVisualizeNanite.h"
#include "NaniteVisualizationData.h"
#include "UnrealEngine.h"
class FVisualizeNanitePS : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FVisualizeNanitePS);
SHADER_USE_PARAMETER_STRUCT(FVisualizeNanitePS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Output)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, InputTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, InputSampler)
SHADER_PARAMETER(FLinearColor, SelectionColor)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::ES3_1);
}
};
IMPLEMENT_GLOBAL_SHADER(FVisualizeNanitePS, "/Engine/Private/PostProcessVisualizeBuffer.usf", "MainPS", SF_Pixel);
void AddVisualizeNanitePass(FRDGBuilder& GraphBuilder, const FViewInfo& View, FScreenPassTexture Output, const Nanite::FRasterResults& RasterResults)
{
const FNaniteVisualizationData& VisualizationData = GetNaniteVisualizationData();
if (VisualizationData.IsActive())
{
// Any individual mode
if (VisualizationData.GetActiveModeID() > 0)
{
if (ensure(RasterResults.Visualizations.Num() == 1))
{
const Nanite::FVisualizeResult& Visualization = RasterResults.Visualizations[0];
if (View.ViewRect != View.UnscaledViewRect)
{
FCopyRectPS::FParameters* Parameters = GraphBuilder.AllocParameters<FCopyRectPS::FParameters>();
Parameters->InputTexture = Visualization.ModeOutput;
Parameters->InputSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
Parameters->RenderTargets[0] = FRenderTargetBinding(Output.Texture, ERenderTargetLoadAction::ENoAction);
const FScreenPassTextureViewport InputViewport(Visualization.ModeOutput->Desc.Extent);
const FScreenPassTextureViewport OutputViewport(Output);
TShaderMapRef<FCopyRectPS> PixelShader(View.ShaderMap);
// Use separate input and output viewports w/ bilinear sampling to properly support dynamic resolution scaling
AddDrawScreenPass(GraphBuilder, RDG_EVENT_NAME("DrawTexture"), View, OutputViewport, InputViewport, PixelShader, Parameters, EScreenPassDrawFlags::None);
}
else
{
// Can use faster 1:1 blit when view sizes match
AddDrawTexturePass(
GraphBuilder,
View,
Visualization.ModeOutput,
Output.Texture,
View.ViewRect.Min,
View.ViewRect.Min,
View.ViewRect.Size()
);
}
Implemented a Nanite visualization overview, with a data-driven tile layout. This is similar to the gbuffer overview, except using a custom global shader instead of the post process material shader approach. Additionally, I managed to do this with just a single Nanite cull/raster pass. We already had a rasterizer permutation for retail vs. any visualization enabled, but it was a single ordinal mode. The rasterizer would do a 64b max and a 32b add. Was worried I needed arrays, multi-pass, strided output buffer, etc.. but then realized max is only used by raster mode, and add is only used by overdraw mode. I changed it all to a bitmask, run the rasterizer with that, and then multi-pass all the modes in screen space off the buffer. Renamed Material Depth -> Material ID, and Hit Proxy Depth -> Hit Proxy ID Moved Material ID into the Standard commands grouping, as it can be useful for content creators. Made the editor hide all the Advanced commands by default, can make them available in the UX with r.Nanite.Visualize.Advanced now. Command line has full access to all of them, regardless of this setting. Renamed r.Nanite.DebugSceneComposite to r.Nanite.Visualize.Composite, and changed the behavior for -1=default, 0=off, 1=on Renamed r.Nanite.DebugSobelFilter to r.Nanite.Visualize.EdgeDetect and defaulted it on for content creator benefit. Removed int4 VisualizeConfig member from Rasterizer UB, and added 32bit visualization mode mask instead. Removed 32bit RasterStateReverseCull member from Rasterizer UB, and folded it into pre-existing 32bit RenderFlags Renamed Nanite::FDebugVisualizeCS -> Nanite::FNaniteVisualizeCS Renamed DebugVisualize.usf -> Visualize.usf Made all visualizations composite against scene depth by default, except the overdraw mode. Changed overdraw view mode to use ColorMapInferno from ColorMap.ush #rb brian.karis #fyi michal.valient, rune.stubbe [CL 15828833 by graham wihlidal in ue5-main branch]
2021-03-25 15:02:12 -04:00
}
}
// Overview mode
else if (VisualizationData.GetActiveModeID() == 0)
{
struct FTileLabel
{
FString Label;
FIntPoint Location;
};
TArray<FTileLabel> TileLabels;
TileLabels.Reserve(RasterResults.Visualizations.Num());
// Use the unscaled view so that dynamic resolution scaling doesn't scale the Nanite visualization tile(s).
const FIntRect& UnscaledViewRect = View.UnscaledViewRect;
const int32 MaxTilesX = 4;
const int32 MaxTilesY = 4;
const int32 TileWidth = UnscaledViewRect.Width() / MaxTilesX;
const int32 TileHeight = UnscaledViewRect.Height() / MaxTilesY;
Implemented a Nanite visualization overview, with a data-driven tile layout. This is similar to the gbuffer overview, except using a custom global shader instead of the post process material shader approach. Additionally, I managed to do this with just a single Nanite cull/raster pass. We already had a rasterizer permutation for retail vs. any visualization enabled, but it was a single ordinal mode. The rasterizer would do a 64b max and a 32b add. Was worried I needed arrays, multi-pass, strided output buffer, etc.. but then realized max is only used by raster mode, and add is only used by overdraw mode. I changed it all to a bitmask, run the rasterizer with that, and then multi-pass all the modes in screen space off the buffer. Renamed Material Depth -> Material ID, and Hit Proxy Depth -> Hit Proxy ID Moved Material ID into the Standard commands grouping, as it can be useful for content creators. Made the editor hide all the Advanced commands by default, can make them available in the UX with r.Nanite.Visualize.Advanced now. Command line has full access to all of them, regardless of this setting. Renamed r.Nanite.DebugSceneComposite to r.Nanite.Visualize.Composite, and changed the behavior for -1=default, 0=off, 1=on Renamed r.Nanite.DebugSobelFilter to r.Nanite.Visualize.EdgeDetect and defaulted it on for content creator benefit. Removed int4 VisualizeConfig member from Rasterizer UB, and added 32bit visualization mode mask instead. Removed 32bit RasterStateReverseCull member from Rasterizer UB, and folded it into pre-existing 32bit RenderFlags Renamed Nanite::FDebugVisualizeCS -> Nanite::FNaniteVisualizeCS Renamed DebugVisualize.usf -> Visualize.usf Made all visualizations composite against scene depth by default, except the overdraw mode. Changed overdraw view mode to use ColorMapInferno from ColorMap.ush #rb brian.karis #fyi michal.valient, rune.stubbe [CL 15828833 by graham wihlidal in ue5-main branch]
2021-03-25 15:02:12 -04:00
FRHISamplerState* BilinearClampSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
FScreenPassRenderTarget OutputTarget(Output.Texture, UnscaledViewRect, ERenderTargetLoadAction::ELoad);
Implemented a Nanite visualization overview, with a data-driven tile layout. This is similar to the gbuffer overview, except using a custom global shader instead of the post process material shader approach. Additionally, I managed to do this with just a single Nanite cull/raster pass. We already had a rasterizer permutation for retail vs. any visualization enabled, but it was a single ordinal mode. The rasterizer would do a 64b max and a 32b add. Was worried I needed arrays, multi-pass, strided output buffer, etc.. but then realized max is only used by raster mode, and add is only used by overdraw mode. I changed it all to a bitmask, run the rasterizer with that, and then multi-pass all the modes in screen space off the buffer. Renamed Material Depth -> Material ID, and Hit Proxy Depth -> Hit Proxy ID Moved Material ID into the Standard commands grouping, as it can be useful for content creators. Made the editor hide all the Advanced commands by default, can make them available in the UX with r.Nanite.Visualize.Advanced now. Command line has full access to all of them, regardless of this setting. Renamed r.Nanite.DebugSceneComposite to r.Nanite.Visualize.Composite, and changed the behavior for -1=default, 0=off, 1=on Renamed r.Nanite.DebugSobelFilter to r.Nanite.Visualize.EdgeDetect and defaulted it on for content creator benefit. Removed int4 VisualizeConfig member from Rasterizer UB, and added 32bit visualization mode mask instead. Removed 32bit RasterStateReverseCull member from Rasterizer UB, and folded it into pre-existing 32bit RenderFlags Renamed Nanite::FDebugVisualizeCS -> Nanite::FNaniteVisualizeCS Renamed DebugVisualize.usf -> Visualize.usf Made all visualizations composite against scene depth by default, except the overdraw mode. Changed overdraw view mode to use ColorMapInferno from ColorMap.ush #rb brian.karis #fyi michal.valient, rune.stubbe [CL 15828833 by graham wihlidal in ue5-main branch]
2021-03-25 15:02:12 -04:00
for (int32 TileIndex = 0; TileIndex < RasterResults.Visualizations.Num(); ++TileIndex)
{
const Nanite::FVisualizeResult& Visualization = RasterResults.Visualizations[TileIndex];
// The list can contain invalid entries to keep the indices static.
if (Visualization.bSkippedTile)
{
continue;
}
const int32 TileX = TileIndex % MaxTilesX;
const int32 TileY = TileIndex / MaxTilesX;
FScreenPassTextureViewport OutputViewport(OutputTarget);
OutputViewport.Rect.Min = FIntPoint(TileX * TileWidth, TileY * TileHeight);
OutputViewport.Rect.Max = OutputViewport.Rect.Min + FIntPoint(TileWidth, TileHeight);
const FLinearColor SelectionColor = FLinearColor::Transparent;
FVisualizeNanitePS::FParameters* PassParameters = GraphBuilder.AllocParameters<FVisualizeNanitePS::FParameters>();
PassParameters->Output = GetScreenPassTextureViewportParameters(OutputViewport);
PassParameters->RenderTargets[0] = OutputTarget.GetRenderTargetBinding();
PassParameters->InputTexture = Visualization.ModeOutput;
PassParameters->InputSampler = BilinearClampSampler;
PassParameters->SelectionColor = SelectionColor;
const FScreenPassTextureViewport InputViewport(Visualization.ModeOutput);
TShaderMapRef<FScreenPassVS> VertexShader(View.ShaderMap);
TShaderMapRef<FVisualizeNanitePS> PixelShader(View.ShaderMap);
FRHIBlendState* BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha>::GetRHI();
const FText DisplayText = VisualizationData.GetModeDisplayName(Visualization.ModeName);
const FString& DisplayName = DisplayText.ToString();
AddDrawScreenPass(GraphBuilder, RDG_EVENT_NAME("Tile: %s", *DisplayName), View, OutputViewport, InputViewport, VertexShader, PixelShader, BlendState, PassParameters);
FTileLabel TileLabel;
TileLabel.Label = DisplayName;
TileLabel.Location.X = 8 + TileX * TileWidth;
TileLabel.Location.Y = (TileY + 1) * TileHeight - 19;
TileLabels.Add(TileLabel);
}
AddDrawCanvasPass(GraphBuilder, RDG_EVENT_NAME("Labels"), View, OutputTarget, [LocalTileLabels = MoveTemp(TileLabels)](FCanvas& Canvas)
{
const FLinearColor LabelColor(1, 1, 0);
for (const FTileLabel& TileLabel : LocalTileLabels)
{
Canvas.DrawShadowedString(TileLabel.Location.X, TileLabel.Location.Y, *TileLabel.Label, GetStatsFont(), LabelColor);
}
});
}
}
}