Files
2026-11-01 23:39:55 +02:00

199 lines
8.0 KiB
C#

#if ENABLE_UPSCALER_FRAMEWORK
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
#if UNITY_EDITOR
using UnityEditor;
#endif
#if UNITY_EDITOR
[InitializeOnLoad]
#endif
static class RegisterSTP
{
static RegisterSTP() => UpscalerRegistry.Register<STPIUpscaler, STPOptions>(STPIUpscaler.upscalerName);
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void InitRuntime() => UpscalerRegistry.Register<STPIUpscaler, STPOptions>(STPIUpscaler.upscalerName);
}
/// <summary>
/// Per-camera context for STP upscaling. Wraps the STP.HistoryContext
/// and tracks the display resolution it was created for.
/// </summary>
public class STPUpscalerContext : IUpscalerContext
{
private STP.HistoryContext m_HistoryContext;
/// <inheritdoc/>
public Vector2Int createdForDisplayResolution { get; private set; }
/// <inheritdoc/>
public int lastUsedFrame { get; set; }
public STP.HistoryContext historyContext => m_HistoryContext;
public STPUpscalerContext(Vector2Int displayResolution)
{
m_HistoryContext = new STP.HistoryContext();
createdForDisplayResolution = displayResolution;
}
/// <inheritdoc/>
public bool IsValidForOptions(UpscalerOptions options)
{
// STP options only contain injection point which doesn't affect context validity.
// Resolution validation is handled by the framework via createdForDisplayResolution.
return true;
}
/// <inheritdoc/>
public void Cleanup(CommandBuffer cmd)
{
if (m_HistoryContext != null)
{
m_HistoryContext.Dispose();
m_HistoryContext = null;
}
}
}
public class STPIUpscaler : AbstractUpscaler
{
public static readonly string upscalerName = "Spatial-Temporal Post-Processing";
private const string k_UpscaledColorTargetName = "_UpscaledCameraColor";
public override string name => upscalerName;
public override bool isTemporal => true;
public override bool supportsSharpening => true;
public override IUpscalerContext CreateContext(UpscalerOptions options, Vector2Int displayResolution)
{
return new STPUpscalerContext(displayResolution);
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
UpscalingIO io = frameData.Get<UpscalingIO>();
// Get the per-camera context from UpscalingIO (set by the pipeline)
var upscalerContext = io.context as STPUpscalerContext;
if (upscalerContext == null)
{
Debug.LogWarning("STPIUpscaler: No valid context provided via io.context. Skipping upscaling.");
return;
}
// Create an output texture
TextureHandle outputColor;
{
TextureDesc inputDesc = io.cameraColor.GetDescriptor(renderGraph);
TextureDesc outputDesc = inputDesc;
outputDesc.width = io.postUpscaleResolution.x;
outputDesc.height = io.postUpscaleResolution.y;
// Avoid enabling sRGB because STP works with compute shaders which can't output sRGB automatically.
outputDesc.format = GraphicsFormatUtility.GetLinearFormat(inputDesc.format);
outputDesc.msaaSamples = MSAASamples.None;
outputDesc.useMipMap = false;
outputDesc.autoGenerateMips = false;
outputDesc.useDynamicScale = false;
outputDesc.anisoLevel = 0;
outputDesc.discardBuffer = false;
// STP uses compute shaders so all render textures must enable random writes
outputDesc.enableRandomWrite = true;
outputDesc.name = k_UpscaledColorTargetName;
outputDesc.clearBuffer = false;
outputDesc.filterMode = FilterMode.Bilinear;
outputColor = renderGraph.CreateTexture(outputDesc);
}
// Populate the STP config
STP.Config config = new();
{
// TODO (Apoorva): Set this using UpscalingInputs when we add HDRP support
config.enableTexArray = io.enableTexArray;
config.enableMotionScaling = io.enableMotionScaling;
config.noiseTexture = io.blueNoiseTextureSet[io.frameIndex & (io.blueNoiseTextureSet.Length - 1)];
config.inputColor = io.cameraColor;
config.inputDepth = io.cameraDepth;
config.inputMotion = io.motionVectorColor;
config.currentImageSize = io.preUpscaleResolution;
config.priorImageSize = io.previousPreUpscaleResolution;
config.outputImageSize = io.postUpscaleResolution;
config.deltaTime = io.deltaTime;
config.lastDeltaTime = io.previousDeltaTime;
config.frameIndex = io.frameIndex;
config.nearPlane = io.nearClipPlane;
config.farPlane = io.farClipPlane;
// TODO (Apoorva): Support stencil masking. URP doesn't support this, HDRP does. We should add support for
// both.
config.inputStencil = TextureHandle.nullHandle;
config.stencilMask = 0;
// TODO (Apoorva): Add support for debug views.
config.debugView = TextureHandle.nullHandle;
config.debugViewIndex = 0;
config.destination = outputColor;
// Use the per-camera history context
bool hasValidHistory;
{
STP.HistoryUpdateInfo info;
info.preUpscaleSize = io.preUpscaleResolution;
info.postUpscaleSize = io.postUpscaleResolution;
info.useHwDrs = io.dynamicResolution == DynamicResolutionType.Hardware;
info.useTexArray = io.enableTexArray;
hasValidHistory = upscalerContext.historyContext.Update(ref info);
}
config.historyContext = upscalerContext.historyContext;
config.enableHwDrs = io.dynamicResolution == DynamicResolutionType.Hardware;
config.hasValidHistory = !io.resetHistory && hasValidHistory;
config.numActiveViews = io.numActiveViews;
config.perViewConfigs = STP.perViewConfigs;
Debug.Assert(io.numActiveViews <= STP.perViewConfigs.Length);
for (int viewIndex = 0; viewIndex < io.numActiveViews; ++viewIndex)
{
int targetIndex = viewIndex;
STP.PerViewConfig perViewConfig;
// STP requires non-jittered versions of the current, previous, and "previous previous" projection
// matrix.
perViewConfig.currentProj = io.projectionMatrices[targetIndex];
perViewConfig.lastProj = io.previousProjectionMatrices[targetIndex];
perViewConfig.lastLastProj = io.previousPreviousProjectionMatrices[targetIndex];
perViewConfig.currentView = io.viewMatrices[targetIndex];
perViewConfig.lastView = io.previousViewMatrices[targetIndex];
perViewConfig.lastLastView = io.previousPreviousViewMatrices[targetIndex];
// NOTE: STP assumes the view matrices also contain the world space camera position so we inject the
// camera position directly here.
Vector3 currentPosition = io.worldSpaceCameraPositions[viewIndex];
Vector3 lastPosition = io.previousWorldSpaceCameraPositions[viewIndex];
Vector3 lastLastPosition = io.previousPreviousWorldSpaceCameraPositions[viewIndex];
perViewConfig.currentView.SetColumn(3, new Vector4(-currentPosition.x, -currentPosition.y, -currentPosition.z, 1.0f));
perViewConfig.lastView.SetColumn(3, new Vector4(-lastPosition.x, -lastPosition.y, -lastPosition.z, 1.0f));
perViewConfig.lastLastView.SetColumn(3, new Vector4(-lastLastPosition.x, -lastLastPosition.y, -lastLastPosition.z, 1.0f));
STP.perViewConfigs[viewIndex] = perViewConfig;
}
}
STP.Execute(renderGraph, ref config);
io.cameraColor = outputColor;
}
}
#endif