forked from BilalY/Rasagar
1343 lines
67 KiB
C#
1343 lines
67 KiB
C#
using System;
|
|
using UnityEngine.Assertions;
|
|
using UnityEngine.Experimental.Rendering;
|
|
using UnityEngine.Rendering.RenderGraphModule;
|
|
|
|
namespace UnityEngine.Rendering
|
|
{
|
|
/// <summary>
|
|
/// Interface to the Spatial-Temporal Post-Processing Upscaler (STP).
|
|
/// This class allows users to configure and execute STP via render graph.
|
|
/// </summary>
|
|
public static class STP
|
|
{
|
|
/// <summary>
|
|
/// Returns true if STP is supported on the current device. Otherwise, false.
|
|
/// STP requires compute shaders
|
|
/// </summary>
|
|
/// <returns>True if supported</returns>
|
|
public static bool IsSupported()
|
|
{
|
|
bool isSupported = true;
|
|
|
|
// STP uses compute shaders as part of its implementation
|
|
isSupported &= SystemInfo.supportsComputeShaders;
|
|
|
|
// GLES has stricter rules than GL when it comes to image store format declarations and matching them with underlying image types.
|
|
// STP's implementation uses several image formats that don't translate accurately from HLSL to GLSL which means a format mismatch will occur.
|
|
// Image format mismatches result in undefined behavior on writes, so we disable STP support for GLES in order to avoid problems.
|
|
// In most cases, hardware that meets the requirements for STP should be capable of running Vulkan anyways.
|
|
isSupported &= (SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES3);
|
|
|
|
return isSupported;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper function that calculates the STP-specific jitter pattern associated with the provided frame index.
|
|
/// </summary>
|
|
/// <param name="frameIndex">Index of the current frame</param>
|
|
/// <returns>Jitter pattern for the provided frame index</returns>
|
|
public static Vector2 Jit16(int frameIndex)
|
|
{
|
|
Vector2 result;
|
|
result.x = HaltonSequence.Get(frameIndex, 2) - 0.5f;
|
|
result.y = HaltonSequence.Get(frameIndex, 3) - 0.5f;
|
|
|
|
return result;
|
|
}
|
|
|
|
// We use a constant to define the debug view arrays to guarantee that they're exactly the same length at compile time
|
|
const int kNumDebugViews = 6;
|
|
|
|
// We define a fixed array of GUIContent values here which map to supported debug views within the STP shader code
|
|
static readonly GUIContent[] s_DebugViewDescriptions = new GUIContent[kNumDebugViews]
|
|
{
|
|
new GUIContent("Clipped Input Color", "Shows input color clipped to {0 to 1}"),
|
|
new GUIContent("Log Input Depth", "Shows input depth in log scale"),
|
|
new GUIContent("Reversible Tonemapped Input Color", "Shows input color after conversion to reversible tonemaped space"),
|
|
new GUIContent("Shaped Absolute Input Motion", "Visualizes input motion vectors"),
|
|
new GUIContent("Motion Reprojection {R=Prior G=This Sqrt Luma Feedback Diff, B=Offscreen}", "Visualizes reprojected frame difference"),
|
|
new GUIContent("Sensitivity {G=No motion match, R=Responsive, B=Luma}", "Visualize pixel sensitivities"),
|
|
};
|
|
|
|
// Unfortunately we must maintain a sequence of index values that map to the supported debug view indices
|
|
// if we want to be able to display the debug views as an enum field without allocating any garbage.
|
|
static readonly int[] s_DebugViewIndices = new int[kNumDebugViews]
|
|
{
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4,
|
|
5,
|
|
};
|
|
|
|
/// <summary>
|
|
/// Array of debug view descriptions expected to be used in the rendering debugger UI
|
|
/// </summary>
|
|
public static GUIContent[] debugViewDescriptions { get { return s_DebugViewDescriptions; } }
|
|
|
|
/// <summary>
|
|
/// Array of debug view indices expected to be used in the rendering debugger UI
|
|
/// </summary>
|
|
public static int[] debugViewIndices { get { return s_DebugViewIndices; } }
|
|
|
|
/// <summary>
|
|
/// STP configuration data that varies per rendered view
|
|
/// </summary>
|
|
public struct PerViewConfig
|
|
{
|
|
/// <summary>
|
|
/// Non-Jittered projection matrix for the current frame
|
|
/// Used by the static geometry reprojection feature
|
|
/// </summary>
|
|
public Matrix4x4 currentProj;
|
|
|
|
/// <summary>
|
|
/// Non-Jittered projection matrix for the previous frame
|
|
/// Used by the static geometry reprojection feature
|
|
/// </summary>
|
|
public Matrix4x4 lastProj;
|
|
|
|
/// <summary>
|
|
/// Non-Jittered projection matrix for the frame before the previous frame
|
|
/// Used by the static geometry reprojection feature
|
|
/// </summary>
|
|
public Matrix4x4 lastLastProj;
|
|
|
|
/// <summary>
|
|
/// View matrix for the current frame
|
|
/// Used by the static geometry reprojection feature
|
|
/// </summary>
|
|
public Matrix4x4 currentView;
|
|
|
|
/// <summary>
|
|
/// View matrix for the previous frame
|
|
/// Used by the static geometry reprojection feature
|
|
/// </summary>
|
|
public Matrix4x4 lastView;
|
|
|
|
/// <summary>
|
|
/// View matrix for the frame before the previous frame
|
|
/// Used by the static geometry reprojection feature
|
|
/// </summary>
|
|
public Matrix4x4 lastLastView;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Maximum amount of supported per-view configurations
|
|
/// </summary>
|
|
const int kMaxPerViewConfigs = 2;
|
|
|
|
/// <summary>
|
|
/// Static allocation of per-view configurations
|
|
/// </summary>
|
|
static PerViewConfig[] s_PerViewConfigs = new PerViewConfig[kMaxPerViewConfigs];
|
|
|
|
/// <summary>
|
|
/// Static allocation of per-view configurations
|
|
/// Users are expected to populate this during STP configuration and then assign it to the relevant
|
|
/// configuration structure field(s) to avoid unnecessary allocations.
|
|
/// </summary>
|
|
public static PerViewConfig[] perViewConfigs
|
|
{
|
|
get { return s_PerViewConfigs; }
|
|
set { s_PerViewConfigs = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Top-level configuration structure required for STP execution
|
|
/// </summary>
|
|
public struct Config
|
|
{
|
|
/// <summary>
|
|
/// Blue noise texture used in various parts of the upscaling logic
|
|
/// </summary>
|
|
public Texture2D noiseTexture;
|
|
|
|
/// <summary>
|
|
/// Input color texture to be upscaled
|
|
/// </summary>
|
|
public TextureHandle inputColor;
|
|
|
|
/// <summary>
|
|
/// Input depth texture which will be analyzed during upscaling
|
|
/// </summary>
|
|
public TextureHandle inputDepth;
|
|
|
|
/// <summary>
|
|
/// Input motion vector texture which is used to reproject information across frames
|
|
/// </summary>
|
|
public TextureHandle inputMotion;
|
|
|
|
/// <summary>
|
|
/// [Optional] Input stencil texture which is used to identify pixels that need special treatment such as particles or in-game screens
|
|
/// </summary>
|
|
public TextureHandle inputStencil;
|
|
|
|
/// <summary>
|
|
/// [Optional] Output debug view texture which STP can be configured to render debug visualizations into
|
|
/// </summary>
|
|
public TextureHandle debugView;
|
|
|
|
/// <summary>
|
|
/// Output color texture which will receive the final upscaled color result
|
|
/// </summary>
|
|
public TextureHandle destination;
|
|
|
|
/// <summary>
|
|
/// Input history context to use when executing STP
|
|
/// </summary>
|
|
public HistoryContext historyContext;
|
|
|
|
/// <summary>
|
|
/// Set to true if hardware dynamic resolution scaling is currently active
|
|
/// </summary>
|
|
public bool enableHwDrs;
|
|
|
|
/// <summary>
|
|
/// Set to true if the rendering environment is using 2d array textures (usually due to XR)
|
|
/// </summary>
|
|
public bool enableTexArray;
|
|
|
|
/// <summary>
|
|
/// Set to true to enable the motion scaling feature which attempts to compensate for variable frame timing when working with motion vectors
|
|
/// </summary>
|
|
public bool enableMotionScaling;
|
|
|
|
/// <summary>
|
|
/// Distance to the camera's near plane
|
|
/// Used to encode depth values
|
|
/// </summary>
|
|
public float nearPlane;
|
|
|
|
/// <summary>
|
|
/// Distance to the camera's far plane
|
|
/// Used to encode depth values
|
|
/// </summary>
|
|
public float farPlane;
|
|
|
|
/// <summary>
|
|
/// Index of the current frame
|
|
/// Used to calculate jitter pattern
|
|
/// </summary>
|
|
public int frameIndex;
|
|
|
|
/// <summary>
|
|
/// True if the current frame has valid history information
|
|
/// Used to prevent STP from producing invalid data
|
|
/// </summary>
|
|
public bool hasValidHistory;
|
|
|
|
/// <summary>
|
|
/// A mask value applied that determines which stencil bit is associated with the responsive feature
|
|
/// Used to prevent STP from producing incorrect values on transparent pixels
|
|
/// Set to 0 if no stencil data is present
|
|
/// </summary>
|
|
public int stencilMask;
|
|
|
|
/// <summary>
|
|
/// An index value that indicates which debug visualization to render in the debug view
|
|
/// This value is only used when a valid debug view handle is provided
|
|
/// </summary>
|
|
public int debugViewIndex;
|
|
|
|
/// <summary>
|
|
/// Delta frame time for the current frame
|
|
/// Used to compensate for inconsistent frame timings when working with motion vectors
|
|
/// </summary>
|
|
public float deltaTime;
|
|
|
|
/// <summary>
|
|
/// Delta frame time for the previous frame
|
|
/// Used to compensate for inconsistent frame timings when working with motion vectors
|
|
/// </summary>
|
|
public float lastDeltaTime;
|
|
|
|
/// <summary>
|
|
/// Size of the current viewport in pixels
|
|
/// Used to calculate image coordinate scaling factors
|
|
/// </summary>
|
|
public Vector2Int currentImageSize;
|
|
|
|
/// <summary>
|
|
/// Size of the previous viewport in pixels
|
|
/// Used to calculate image coordinate scaling factors
|
|
/// </summary>
|
|
public Vector2Int priorImageSize;
|
|
|
|
/// <summary>
|
|
/// Size of the upscaled output image in pixels
|
|
/// Used to calculate image coordinate scaling factors
|
|
/// </summary>
|
|
public Vector2Int outputImageSize;
|
|
|
|
/// <summary>
|
|
/// Number of active views in the perViewConfigs array
|
|
/// </summary>
|
|
public int numActiveViews;
|
|
|
|
/// <summary>
|
|
/// Configuration parameters that are unique per rendered view
|
|
/// </summary>
|
|
public PerViewConfig[] perViewConfigs;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enumeration of unique types of history textures
|
|
/// </summary>
|
|
internal enum HistoryTextureType
|
|
{
|
|
DepthMotion,
|
|
Luma,
|
|
Convergence,
|
|
Feedback,
|
|
|
|
Count
|
|
}
|
|
|
|
/// <summary>
|
|
/// Number of unique types of history textures used by STP
|
|
/// </summary>
|
|
const int kNumHistoryTextureTypes = (int)HistoryTextureType.Count;
|
|
|
|
/// <summary>
|
|
/// Describes the information needed to update the history context
|
|
/// </summary>
|
|
public struct HistoryUpdateInfo
|
|
{
|
|
/// <summary>
|
|
/// Size of the target image before upscaling is applied
|
|
/// </summary>
|
|
public Vector2Int preUpscaleSize;
|
|
|
|
/// <summary>
|
|
/// Size of the target image after upscaling is applied
|
|
/// </summary>
|
|
public Vector2Int postUpscaleSize;
|
|
|
|
/// <summary>
|
|
/// True if hardware dynamic resolution scaling is active
|
|
/// </summary>
|
|
public bool useHwDrs;
|
|
|
|
/// <summary>
|
|
/// True if texture arrays are being used in the current rendering environment
|
|
/// </summary>
|
|
public bool useTexArray;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Computes a hash value that changes whenever the history context needs to be re-created
|
|
/// </summary>
|
|
/// <param name="hashParams">parameters used to calculate the history hash</param>
|
|
/// <returns>A hash value that changes whenever the history context needs to be re-created</returns>
|
|
static Hash128 ComputeHistoryHash(ref HistoryUpdateInfo info)
|
|
{
|
|
Hash128 hash = new Hash128();
|
|
|
|
hash.Append(ref info.useHwDrs);
|
|
hash.Append(ref info.useTexArray);
|
|
hash.Append(ref info.postUpscaleSize);
|
|
|
|
// The pre-upscale size only affects the history texture logic when hardware dynamic resolution scaling is disabled.
|
|
if (!info.useHwDrs)
|
|
{
|
|
hash.Append(ref info.preUpscaleSize);
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates the correct size for the STP low-frequency convergence texture
|
|
/// </summary>
|
|
/// <param name="historyTextureSize">size of the render-size history textures used by STP</param>
|
|
/// <returns>size of the convergence texture</returns>
|
|
static Vector2Int CalculateConvergenceTextureSize(Vector2Int historyTextureSize)
|
|
{
|
|
// The convergence texture is a 4x4 reduction of data computed at render size, but we must always make sure the size is rounded up.
|
|
return new Vector2Int(CoreUtils.DivRoundUp(historyTextureSize.x, 4), CoreUtils.DivRoundUp(historyTextureSize.y, 4));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opaque history information required by STP's implementation
|
|
/// Users are expected to create their own persistent history context, update it once per frame, and provide it to
|
|
/// STP's execution logic through the configuration structure.
|
|
/// </summary>
|
|
public sealed class HistoryContext : IDisposable
|
|
{
|
|
/// <summary>
|
|
/// Array of history textures used by STP
|
|
/// The array is subdivided into two sets. Each set represents the history textures for a single frame.
|
|
/// </summary>
|
|
RTHandle[] m_textures = new RTHandle[kNumHistoryTextureTypes * 2];
|
|
|
|
/// <summary>
|
|
/// Hash value that changes whenever the history context needs to be re-created
|
|
/// </summary>
|
|
Hash128 m_hash = Hash128.Compute(0);
|
|
|
|
/// <summary>
|
|
/// Updated the state of the history context based on the provided information
|
|
/// This may result in re-allocation of resources internally if state has changed and is now incompatible or if this is the first update
|
|
/// </summary>
|
|
/// <param name="info">information required to update the history context</param>
|
|
/// <returns>True if the internal history data within the context is valid after the update operation</returns>
|
|
public bool Update(ref HistoryUpdateInfo info)
|
|
{
|
|
bool hasValidHistory = true;
|
|
|
|
var hash = ComputeHistoryHash(ref info);
|
|
|
|
if (hash != m_hash)
|
|
{
|
|
hasValidHistory = false;
|
|
|
|
Dispose();
|
|
|
|
m_hash = hash;
|
|
|
|
// Allocate two new sets of history textures for STP based on the current settings
|
|
|
|
Vector2Int historyTextureSize = info.useHwDrs ? info.postUpscaleSize : info.preUpscaleSize;
|
|
TextureDimension texDimension = info.useTexArray ? TextureDimension.Tex2DArray : TextureDimension.Tex2D;
|
|
|
|
int width = 0;
|
|
int height = 0;
|
|
GraphicsFormat format = GraphicsFormat.None;
|
|
bool useDynamicScaleExplicit = false;
|
|
string name = "";
|
|
|
|
for (int historyTypeIndex = 0; historyTypeIndex < kNumHistoryTextureTypes; ++historyTypeIndex)
|
|
{
|
|
switch ((HistoryTextureType)historyTypeIndex)
|
|
{
|
|
case HistoryTextureType.DepthMotion:
|
|
{
|
|
width = historyTextureSize.x;
|
|
height = historyTextureSize.y;
|
|
format = GraphicsFormat.R32_UInt;
|
|
useDynamicScaleExplicit = info.useHwDrs;
|
|
name = "STP Depth & Motion";
|
|
break;
|
|
}
|
|
case HistoryTextureType.Luma:
|
|
{
|
|
width = historyTextureSize.x;
|
|
height = historyTextureSize.y;
|
|
format = GraphicsFormat.R8G8_UNorm;
|
|
useDynamicScaleExplicit = info.useHwDrs;
|
|
name = "STP Luma";
|
|
break;
|
|
}
|
|
case HistoryTextureType.Convergence:
|
|
{
|
|
Vector2Int convergenceSize = CalculateConvergenceTextureSize(historyTextureSize);
|
|
|
|
width = convergenceSize.x;
|
|
height = convergenceSize.y;
|
|
format = GraphicsFormat.R8_UNorm;
|
|
useDynamicScaleExplicit = info.useHwDrs;
|
|
name = "STP Convergence";
|
|
break;
|
|
}
|
|
case HistoryTextureType.Feedback:
|
|
{
|
|
width = info.postUpscaleSize.x;
|
|
height = info.postUpscaleSize.y;
|
|
format = GraphicsFormat.A2B10G10R10_UNormPack32;
|
|
useDynamicScaleExplicit = false;
|
|
name = "STP Feedback";
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// Invalid history texture type
|
|
Debug.Assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int frameIndex = 0; frameIndex < 2; ++frameIndex)
|
|
{
|
|
int offset = (frameIndex * kNumHistoryTextureTypes) + historyTypeIndex;
|
|
|
|
m_textures[offset] = RTHandles.Alloc(
|
|
width, height, TextureXR.slices, DepthBits.None, format, dimension: texDimension, enableRandomWrite: true,
|
|
name: name, useDynamicScaleExplicit: useDynamicScaleExplicit
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hasValidHistory;
|
|
}
|
|
|
|
internal RTHandle GetCurrentHistoryTexture(HistoryTextureType historyType, int frameIndex)
|
|
{
|
|
return m_textures[((frameIndex & 1) * (int)HistoryTextureType.Count) + (int)historyType];
|
|
}
|
|
|
|
internal RTHandle GetPreviousHistoryTexture(HistoryTextureType historyType, int frameIndex)
|
|
{
|
|
return m_textures[(((frameIndex & 1) ^ 1) * (int)HistoryTextureType.Count) + (int)historyType];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Releases the internal resources held within the history context
|
|
/// Typically things like texture allocations
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
for (int texIndex = 0; texIndex < m_textures.Length; ++texIndex)
|
|
{
|
|
if (m_textures[texIndex] != null)
|
|
{
|
|
m_textures[texIndex].Release();
|
|
m_textures[texIndex] = null;
|
|
}
|
|
}
|
|
|
|
m_hash = Hash128.Compute(0);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a motion scaling ratio based on the difference in delta times across frames
|
|
/// </summary>
|
|
/// <param name="deltaTime">Time elapsed from the last frame to the current frame in seconds</param>
|
|
/// <param name="lastDeltaTime">Time elapsed from the frame before the last frame to the last frame in seconds</param>
|
|
/// <returns>Motion scale factor for the current frame</returns>
|
|
static float CalculateMotionScale(float deltaTime, float lastDeltaTime)
|
|
{
|
|
float motionScale = 1.0f;
|
|
|
|
float currentDeltaTime = deltaTime;
|
|
float previousDeltaTime = lastDeltaTime;
|
|
if (!Mathf.Approximately(previousDeltaTime, 0.0f))
|
|
{
|
|
motionScale = currentDeltaTime / previousDeltaTime;
|
|
}
|
|
|
|
return motionScale;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a matrix with the translation component removed
|
|
/// This function is intended to be used with view matrices
|
|
/// </summary>
|
|
/// <param name="input">input view matrix</param>
|
|
/// <returns>a matrix with the translation component removed</returns>
|
|
static Matrix4x4 ExtractRotation(Matrix4x4 input)
|
|
{
|
|
Matrix4x4 output = input;
|
|
|
|
output[0, 3] = 0.0f;
|
|
output[1, 3] = 0.0f;
|
|
output[2, 3] = 0.0f;
|
|
output[3, 3] = 1.0f;
|
|
|
|
return output;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper function that converts the provided Vector2 into a packed integer with two FP16 values
|
|
/// </summary>
|
|
/// <param name="value">input Vector2 value to be packed</param>
|
|
/// <returns>an integer that contains the two vector components packed together as FP16 values</returns>
|
|
static int PackVector2ToInt(Vector2 value)
|
|
{
|
|
uint xAsHalf = Mathf.FloatToHalf(value.x);
|
|
uint yAsHalf = Mathf.FloatToHalf(value.y);
|
|
return (int)(xAsHalf | (yAsHalf << 16));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Number of constants that contain per-view information for the setup pass
|
|
/// NOTE: The name here is important as it's directly translated into HLSL
|
|
/// </summary>
|
|
[GenerateHLSL(PackingRules.Exact)]
|
|
enum StpSetupPerViewConstants
|
|
{
|
|
Count = 8
|
|
};
|
|
|
|
/// <summary>
|
|
/// Total number of constants used for per-view data in STP
|
|
/// </summary>
|
|
const int kTotalSetupViewConstantsCount = kMaxPerViewConfigs * ((int)StpSetupPerViewConstants.Count);
|
|
|
|
/// <summary>
|
|
/// Constant buffer layout used by STP
|
|
/// NOTE: The name here is important as it's directly translated into HLSL
|
|
/// </summary>
|
|
[GenerateHLSL(needAccessors = false, generateCBuffer = true)]
|
|
unsafe struct StpConstantBufferData
|
|
{
|
|
public Vector4 _StpCommonConstant;
|
|
|
|
public Vector4 _StpSetupConstants0;
|
|
public Vector4 _StpSetupConstants1;
|
|
public Vector4 _StpSetupConstants2;
|
|
public Vector4 _StpSetupConstants3;
|
|
public Vector4 _StpSetupConstants4;
|
|
public Vector4 _StpSetupConstants5;
|
|
|
|
[HLSLArray(kTotalSetupViewConstantsCount, typeof(Vector4))]
|
|
public fixed float _StpSetupPerViewConstants[kTotalSetupViewConstantsCount * 4];
|
|
|
|
public Vector4 _StpDilConstants0;
|
|
|
|
public Vector4 _StpTaaConstants0;
|
|
public Vector4 _StpTaaConstants1;
|
|
public Vector4 _StpTaaConstants2;
|
|
public Vector4 _StpTaaConstants3;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Produces constant buffer data in the format required by STP
|
|
/// </summary>
|
|
/// <param name="config">STP's configuration data</param>
|
|
/// <param name="constants">constant buffer data structure required by STP</param>
|
|
static void PopulateConstantData(ref Config config, ref StpConstantBufferData constants)
|
|
{
|
|
Assert.IsTrue(Mathf.IsPowerOfTwo(config.noiseTexture.width));
|
|
|
|
//
|
|
// Common
|
|
//
|
|
|
|
// [DebugViewIndex | StencilMask | HasValidHistory | (Width - 1)]
|
|
int packedBlueNoiseWidthMinusOne = (config.noiseTexture.width - 1) & 0xFF;
|
|
int packedHasValidHistory = (config.hasValidHistory ? 1 : 0) << 8;
|
|
int packedStencilMask = (config.stencilMask & 0xFF) << 16;
|
|
int packedDebugViewIndex = (config.debugViewIndex & 0xFF) << 24;
|
|
|
|
int constant0 = packedStencilMask | packedHasValidHistory | packedBlueNoiseWidthMinusOne | packedDebugViewIndex;
|
|
|
|
// Compute values used for linear depth conversion
|
|
// These values are normally in the _ZBufferParams constant, but we re-compute them here since this constant is defined differently across SRPs
|
|
float zBufferParamZ = (config.farPlane - config.nearPlane) / (config.nearPlane * config.farPlane);
|
|
float zBufferParamW = 1.0f / config.farPlane;
|
|
|
|
constants._StpCommonConstant = new Vector4(BitConverter.Int32BitsToSingle(constant0), zBufferParamZ, zBufferParamW, 0.0f);
|
|
|
|
//
|
|
// NOTE: The logic below is effectively a C# port of the HLSL constant setup logic found in Stp.hlsl
|
|
// The C# code attempts to be as close as possible to the HLSL in order to simplify maintenance.
|
|
//
|
|
|
|
//
|
|
// Setup
|
|
//
|
|
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// StpF2 kRcpC := 1.0 / size of current input image in pixels.
|
|
constants._StpSetupConstants0.x = (1.0f / config.currentImageSize.x);
|
|
constants._StpSetupConstants0.y = (1.0f / config.currentImageSize.y);
|
|
// StpF2 kHalfRcpC := 0.5 / size of current input image in pixels.
|
|
constants._StpSetupConstants0.z = (0.5f / config.currentImageSize.x);
|
|
constants._StpSetupConstants0.w = (0.5f / config.currentImageSize.y);
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// Grab jitter for current and prior frames.
|
|
Vector2 jitP = Jit16(config.frameIndex - 1);
|
|
Vector2 jitC = Jit16(config.frameIndex);
|
|
// StpF2 kJitCRcpCUnjitPRcpP := Map current into prior frame.
|
|
constants._StpSetupConstants1.x = (jitC.x / config.currentImageSize.x - jitP.x / config.priorImageSize.x);
|
|
constants._StpSetupConstants1.y = (jitC.y / config.currentImageSize.y - jitP.y / config.priorImageSize.y);
|
|
// StpF2 kJitCRcpC := Take {0 to 1} position in current image, and map back to {0 to 1} position in feedback (removes jitter).
|
|
constants._StpSetupConstants1.z = jitC.x / config.currentImageSize.x;
|
|
constants._StpSetupConstants1.w = jitC.y / config.currentImageSize.y;
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// StpF2 kF := size of feedback (aka output) in pixels.
|
|
constants._StpSetupConstants2.x = config.outputImageSize.x;
|
|
constants._StpSetupConstants2.y = config.outputImageSize.y;
|
|
// StpF2 kDepth := Copied logic from StpZCon().
|
|
float k0 = (1.0f / config.nearPlane);
|
|
float k1 = (1.0f / Mathf.Log(k0 * config.farPlane, 2.0f));
|
|
constants._StpSetupConstants2.z = k0;
|
|
constants._StpSetupConstants2.w = k1;
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// StpF4 kOS := Scale and bias to check for out of bounds (and kill feedback).
|
|
// Scaled and biased output needs to {-1 out of bounds, >-1 in bounds, <1 in bounds, 1 out of bounds}.
|
|
Vector2 s;
|
|
// Undo 'pM' scaling, and multiply by 2 (as this needs to be -1 to 1 at edge of acceptable reprojection).
|
|
s.x = 2.0f;
|
|
s.y = 2.0f;
|
|
// Scaling to push outside safe reprojection over 1.
|
|
s.x *= (config.priorImageSize.x / (config.priorImageSize.x + 4.0f));
|
|
s.y *= (config.priorImageSize.y / (config.priorImageSize.y + 4.0f));
|
|
constants._StpSetupConstants3.x = s[0];
|
|
constants._StpSetupConstants3.y = s[1];
|
|
// Factor out subtracting off the mid point scaled by the multiply term.
|
|
constants._StpSetupConstants3.z = (-0.5f * s[0]);
|
|
constants._StpSetupConstants3.w = (-0.5f * s[1]);
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// StpF2 kUnDepth := Copied logic from StpZUnCon().
|
|
constants._StpSetupConstants4.x = Mathf.Log(config.farPlane / config.nearPlane, 2.0f);
|
|
constants._StpSetupConstants4.y = config.nearPlane;
|
|
// kMotionMatch
|
|
constants._StpSetupConstants4.z = config.enableMotionScaling ? CalculateMotionScale(config.deltaTime, config.lastDeltaTime) : 1.0f;
|
|
// Unused for now.
|
|
constants._StpSetupConstants4.w = 0.0f;
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// StpF2 kC := Size of current input image in pixels.
|
|
constants._StpSetupConstants5.x = config.currentImageSize.x;
|
|
constants._StpSetupConstants5.y = config.currentImageSize.y;
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// StpF2 kFS := scale factor used to convert from feedback uv space to reduction uv space
|
|
constants._StpSetupConstants5.z = config.outputImageSize.x / (Mathf.Ceil(config.outputImageSize.x / 4.0f) * 4.0f);
|
|
constants._StpSetupConstants5.w = config.outputImageSize.y / (Mathf.Ceil(config.outputImageSize.y / 4.0f) * 4.0f);
|
|
|
|
// Per View
|
|
for (uint viewIndex = 0; viewIndex < config.numActiveViews; ++viewIndex)
|
|
{
|
|
uint baseViewDataOffset = viewIndex * ((int)StpSetupPerViewConstants.Count) * 4;
|
|
var perViewConfig = config.perViewConfigs[viewIndex];
|
|
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// See header docs in "STATIC GEOMETRY MOTION FORWARD PROJECTION".
|
|
Vector4 prjPriABEF;
|
|
prjPriABEF.x = perViewConfig.lastProj[0, 0];
|
|
|
|
// NOTE: Unity flips the Y axis inside the projection matrix. STP requires a non-flipped Y axis, so we undo the flip here with abs
|
|
prjPriABEF.y = Mathf.Abs(perViewConfig.lastProj[1, 1]);
|
|
|
|
// TODO: We need to understand why we need to negate these values for the inverse projection in order to get correct results.
|
|
prjPriABEF.z = -perViewConfig.lastProj[0, 2];
|
|
prjPriABEF.w = -perViewConfig.lastProj[1, 2];
|
|
|
|
Vector4 prjPriCDGH;
|
|
prjPriCDGH.x = perViewConfig.lastProj[2, 2];
|
|
prjPriCDGH.y = perViewConfig.lastProj[2, 3];
|
|
prjPriCDGH.z = perViewConfig.lastProj[3, 2];
|
|
prjPriCDGH.w = perViewConfig.lastProj[3, 3];
|
|
|
|
Vector4 prjCurABEF;
|
|
prjCurABEF.x = perViewConfig.currentProj[0, 0];
|
|
|
|
// NOTE: Unity flips the Y axis inside the projection matrix. STP requires a non-flipped Y axis, so we undo the flip here with abs
|
|
prjCurABEF.y = Mathf.Abs(perViewConfig.currentProj[1, 1]);
|
|
|
|
prjCurABEF.z = perViewConfig.currentProj[0, 2];
|
|
prjCurABEF.w = perViewConfig.currentProj[1, 2];
|
|
|
|
Vector4 prjCurCDGH;
|
|
prjCurCDGH.x = perViewConfig.currentProj[2, 2];
|
|
prjCurCDGH.y = perViewConfig.currentProj[2, 3];
|
|
prjCurCDGH.z = perViewConfig.currentProj[3, 2];
|
|
prjCurCDGH.w = perViewConfig.currentProj[3, 3];
|
|
|
|
Matrix4x4 forwardTransform = ExtractRotation(perViewConfig.currentView) *
|
|
Matrix4x4.Translate(-perViewConfig.currentView.GetColumn(3)) *
|
|
Matrix4x4.Translate(perViewConfig.lastView.GetColumn(3)) *
|
|
ExtractRotation(perViewConfig.lastView).transpose;
|
|
|
|
Vector4 forIJKL = forwardTransform.GetRow(0);
|
|
Vector4 forMNOP = forwardTransform.GetRow(1);
|
|
Vector4 forQRST = forwardTransform.GetRow(2);
|
|
|
|
Vector4 prjPrvABEF;
|
|
prjPrvABEF.x = perViewConfig.lastLastProj[0, 0];
|
|
|
|
// NOTE: Unity flips the Y axis inside the projection matrix. STP requires a non-flipped Y axis, so we undo the flip here with abs
|
|
prjPrvABEF.y = Mathf.Abs(perViewConfig.lastLastProj[1, 1]);
|
|
|
|
prjPrvABEF.z = perViewConfig.lastLastProj[0, 2];
|
|
prjPrvABEF.w = perViewConfig.lastLastProj[1, 2];
|
|
|
|
Vector4 prjPrvCDGH;
|
|
prjPrvCDGH.x = perViewConfig.lastLastProj[2, 2];
|
|
prjPrvCDGH.y = perViewConfig.lastLastProj[2, 3];
|
|
prjPrvCDGH.z = perViewConfig.lastLastProj[3, 2];
|
|
prjPrvCDGH.w = perViewConfig.lastLastProj[3, 3];
|
|
|
|
Matrix4x4 backwardTransform = ExtractRotation(perViewConfig.lastLastView) *
|
|
Matrix4x4.Translate(-perViewConfig.lastLastView.GetColumn(3)) *
|
|
Matrix4x4.Translate(perViewConfig.lastView.GetColumn(3)) *
|
|
ExtractRotation(perViewConfig.lastView).transpose;
|
|
|
|
Vector4 bckIJKL = backwardTransform.GetRow(0);
|
|
Vector4 bckMNOP = backwardTransform.GetRow(1);
|
|
Vector4 bckQRST = backwardTransform.GetRow(2);
|
|
|
|
unsafe
|
|
{
|
|
// Forwards
|
|
|
|
// k0123
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 0] = prjPriCDGH.z / prjPriABEF.x;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 1] = prjPriCDGH.w / prjPriABEF.x;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 2] = prjPriABEF.z / prjPriABEF.x;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 3] = prjPriCDGH.z / prjPriABEF.y;
|
|
// k4567
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 4] = prjPriCDGH.w / prjPriABEF.y;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 5] = prjPriABEF.w / prjPriABEF.y;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 6] = forIJKL.x * prjCurABEF.x + forQRST.x * prjCurABEF.z;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 7] = forIJKL.y * prjCurABEF.x + forQRST.y * prjCurABEF.z;
|
|
// k89AB
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 8] = forIJKL.z * prjCurABEF.x + forQRST.z * prjCurABEF.z;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 9] = forIJKL.w * prjCurABEF.x + forQRST.w * prjCurABEF.z;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 10] = forMNOP.x * prjCurABEF.y + forQRST.x * prjCurABEF.w;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 11] = forMNOP.y * prjCurABEF.y + forQRST.y * prjCurABEF.w;
|
|
// kCDEF
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 12] = forMNOP.z * prjCurABEF.y + forQRST.z * prjCurABEF.w;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 13] = forMNOP.w * prjCurABEF.y + forQRST.w * prjCurABEF.w;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 14] = forQRST.x * prjCurCDGH.z;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 15] = forQRST.y * prjCurCDGH.z;
|
|
// kGHIJ
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 16] = forQRST.z * prjCurCDGH.z;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 17] = forQRST.w * prjCurCDGH.z + prjCurCDGH.w;
|
|
|
|
// Backwards
|
|
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 18] = bckIJKL.x * prjPrvABEF.x + bckQRST.x * prjPrvABEF.z;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 19] = bckIJKL.y * prjPrvABEF.x + bckQRST.y * prjPrvABEF.z;
|
|
// kKLMN
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 20] = bckIJKL.z * prjPrvABEF.x + bckQRST.z * prjPrvABEF.z;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 21] = bckIJKL.w * prjPrvABEF.x + bckQRST.w * prjPrvABEF.z;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 22] = bckMNOP.x * prjPrvABEF.y + bckQRST.x * prjPrvABEF.w;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 23] = bckMNOP.y * prjPrvABEF.y + bckQRST.y * prjPrvABEF.w;
|
|
// kOPQR
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 24] = bckMNOP.z * prjPrvABEF.y + bckQRST.z * prjPrvABEF.w;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 25] = bckMNOP.w * prjPrvABEF.y + bckQRST.w * prjPrvABEF.w;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 26] = bckQRST.x * prjPrvCDGH.z;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 27] = bckQRST.y * prjPrvCDGH.z;
|
|
// kST
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 28] = bckQRST.z * prjPrvCDGH.z;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 29] = bckQRST.w * prjPrvCDGH.z + prjPrvCDGH.w;
|
|
// Unused
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 30] = 0.0f;
|
|
constants._StpSetupPerViewConstants[baseViewDataOffset + 31] = 0.0f;
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
//
|
|
// Dilation
|
|
//
|
|
// StpF2 kRcpR := 4/size of current input image in pixels.
|
|
constants._StpDilConstants0.x = 4.0f / config.currentImageSize.x;
|
|
constants._StpDilConstants0.y = 4.0f / config.currentImageSize.y;
|
|
// StpU2 kR := size/4 of the current input image in pixels.
|
|
// Used for pass merging (DIL and SAA), since convergence is 1/16 area of input, must check position.
|
|
constants._StpDilConstants0.z = BitConverter.Int32BitsToSingle(config.currentImageSize.x >> 2);
|
|
constants._StpDilConstants0.w = BitConverter.Int32BitsToSingle(config.currentImageSize.y >> 2);
|
|
|
|
//
|
|
// TAA
|
|
//
|
|
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// Conversion from integer pix position to center pix float pixel position in image for current input.
|
|
// xy := multiply term (M) --- Scale by 1/imgF to get to {0 to 1}.
|
|
// zw := addition term (A) --- Add 0.5*M to get to center of pixel, then subtract jitC to undo jitter.
|
|
// StpF2 kCRcpF.
|
|
constants._StpTaaConstants0.x = (((float)config.currentImageSize.x) / config.outputImageSize.x);
|
|
constants._StpTaaConstants0.y = (((float)config.currentImageSize.y) / config.outputImageSize.y);
|
|
// StpF2 kHalfCRcpFUnjitC.
|
|
constants._StpTaaConstants0.z = (0.5f * config.currentImageSize.x / config.outputImageSize.x - jitC.x);
|
|
constants._StpTaaConstants0.w = (0.5f * config.currentImageSize.y / config.outputImageSize.y - jitC.y);
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// StpF2 kRcpC := 1/size of current input image in pixels.
|
|
constants._StpTaaConstants1.x = (1.0f / config.currentImageSize.x);
|
|
constants._StpTaaConstants1.y = (1.0f / config.currentImageSize.y);
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// StpF2 kRcpF := 1/size of feedback image (aka output) in pixels.
|
|
constants._StpTaaConstants1.z = (1.0f / config.outputImageSize.x);
|
|
constants._StpTaaConstants1.w = (1.0f / config.outputImageSize.y);
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// StpF2 kHalfRcpF := 0.5/size of feedback image (aka output) in pixels.
|
|
constants._StpTaaConstants2.x = (0.5f / config.outputImageSize.x);
|
|
constants._StpTaaConstants2.y = (0.5f / config.outputImageSize.y);
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// Conversion from a {0 to 1} position in current input to feedback.
|
|
// StpH3 kJitCRcpC0 := jitC / image image size in pixels + {-0.5/size, +0.5/size} of current input image in pixels.
|
|
constants._StpTaaConstants2.z = jitC.x / config.currentImageSize.x - 0.5f / config.currentImageSize.x;
|
|
constants._StpTaaConstants2.w = jitC.y / config.currentImageSize.y + 0.5f / config.currentImageSize.y;
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// StpF2 kHalfRcpC := 0.5/size of current input image in pixels.
|
|
constants._StpTaaConstants3.x = 0.5f / config.currentImageSize.x;
|
|
constants._StpTaaConstants3.y = 0.5f / config.currentImageSize.y;
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// StpF2 kF := size of feedback image in pixels.
|
|
constants._StpTaaConstants3.z = config.outputImageSize.x;
|
|
constants._StpTaaConstants3.w = config.outputImageSize.y;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shader resource ids used to communicate with the STP shader implementation
|
|
/// </summary>
|
|
static class ShaderResources
|
|
{
|
|
public static readonly int _StpConstantBufferData = Shader.PropertyToID("StpConstantBufferData");
|
|
public static readonly int _StpBlueNoiseIn = Shader.PropertyToID("_StpBlueNoiseIn");
|
|
public static readonly int _StpDebugOut = Shader.PropertyToID("_StpDebugOut");
|
|
public static readonly int _StpInputColor = Shader.PropertyToID("_StpInputColor");
|
|
public static readonly int _StpInputDepth = Shader.PropertyToID("_StpInputDepth");
|
|
public static readonly int _StpInputMotion = Shader.PropertyToID("_StpInputMotion");
|
|
public static readonly int _StpInputStencil = Shader.PropertyToID("_StpInputStencil");
|
|
public static readonly int _StpIntermediateColor = Shader.PropertyToID("_StpIntermediateColor");
|
|
public static readonly int _StpIntermediateConvergence = Shader.PropertyToID("_StpIntermediateConvergence");
|
|
public static readonly int _StpIntermediateWeights = Shader.PropertyToID("_StpIntermediateWeights");
|
|
public static readonly int _StpPriorLuma = Shader.PropertyToID("_StpPriorLuma");
|
|
public static readonly int _StpLuma = Shader.PropertyToID("_StpLuma");
|
|
public static readonly int _StpPriorDepthMotion = Shader.PropertyToID("_StpPriorDepthMotion");
|
|
public static readonly int _StpDepthMotion = Shader.PropertyToID("_StpDepthMotion");
|
|
public static readonly int _StpPriorFeedback = Shader.PropertyToID("_StpPriorFeedback");
|
|
public static readonly int _StpFeedback = Shader.PropertyToID("_StpFeedback");
|
|
public static readonly int _StpPriorConvergence = Shader.PropertyToID("_StpPriorConvergence");
|
|
public static readonly int _StpConvergence = Shader.PropertyToID("_StpConvergence");
|
|
public static readonly int _StpOutput = Shader.PropertyToID("_StpOutput");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shader keyword strings used to configure the STP shader implementation
|
|
/// </summary>
|
|
static class ShaderKeywords
|
|
{
|
|
public static readonly string EnableDebugMode = "ENABLE_DEBUG_MODE";
|
|
public static readonly string EnableLargeKernel = "ENABLE_LARGE_KERNEL";
|
|
public static readonly string EnableStencilResponsive = "ENABLE_STENCIL_RESPONSIVE";
|
|
public static readonly string DisableTexture2DXArray = "DISABLE_TEXTURE2D_X_ARRAY";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the compute shaders used during STP's passes
|
|
/// </summary>
|
|
[Serializable]
|
|
[SupportedOnRenderPipeline]
|
|
[Categorization.CategoryInfo(Name = "R: STP", Order = 1000)]
|
|
[Categorization.ElementInfo(Order = 0), HideInInspector]
|
|
class RuntimeResources : IRenderPipelineResources
|
|
{
|
|
public int version => 0;
|
|
|
|
bool IRenderPipelineGraphicsSettings.isAvailableInPlayerBuild => true;
|
|
|
|
[SerializeField, ResourcePath("Runtime/STP/StpSetup.compute")]
|
|
private ComputeShader m_setupCS;
|
|
|
|
public ComputeShader setupCS
|
|
{
|
|
get => m_setupCS;
|
|
set => this.SetValueAndNotify(ref m_setupCS, value);
|
|
}
|
|
|
|
[SerializeField, ResourcePath("Runtime/STP/StpPreTaa.compute")]
|
|
private ComputeShader m_preTaaCS;
|
|
|
|
public ComputeShader preTaaCS
|
|
{
|
|
get => m_preTaaCS;
|
|
set => this.SetValueAndNotify(ref m_preTaaCS, value);
|
|
}
|
|
|
|
[SerializeField, ResourcePath("Runtime/STP/StpTaa.compute")]
|
|
private ComputeShader m_taaCS;
|
|
|
|
public ComputeShader taaCS
|
|
{
|
|
get => m_taaCS;
|
|
set => this.SetValueAndNotify(ref m_taaCS, value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Profiling identifiers associated with STP's passes
|
|
/// </summary>
|
|
enum ProfileId
|
|
{
|
|
StpSetup,
|
|
StpPreTaa,
|
|
StpTaa
|
|
}
|
|
|
|
/// <summary>
|
|
/// Integer value used to identify when STP is running on a Qualcomm GPU
|
|
/// </summary>
|
|
static readonly int kQualcommVendorId = 0x5143;
|
|
|
|
/// <summary>
|
|
/// Information required for STP's setup pass
|
|
/// </summary>
|
|
class SetupData
|
|
{
|
|
public ComputeShader cs;
|
|
public int kernelIndex;
|
|
public int viewCount;
|
|
public Vector2Int dispatchSize;
|
|
|
|
public StpConstantBufferData constantBufferData;
|
|
|
|
// Common
|
|
public TextureHandle noiseTexture;
|
|
public TextureHandle debugView;
|
|
|
|
// Inputs
|
|
public TextureHandle inputColor;
|
|
public TextureHandle inputDepth;
|
|
public TextureHandle inputMotion;
|
|
public TextureHandle inputStencil;
|
|
|
|
// Intermediates
|
|
public TextureHandle intermediateColor;
|
|
public TextureHandle intermediateConvergence;
|
|
|
|
// History
|
|
public TextureHandle priorDepthMotion;
|
|
public TextureHandle depthMotion;
|
|
public TextureHandle priorLuma;
|
|
public TextureHandle luma;
|
|
public TextureHandle priorFeedback;
|
|
public TextureHandle priorConvergence;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Information required for STP's Pre-TAA pass
|
|
/// </summary>
|
|
class PreTaaData
|
|
{
|
|
public ComputeShader cs;
|
|
public int kernelIndex;
|
|
public int viewCount;
|
|
public Vector2Int dispatchSize;
|
|
|
|
// Common
|
|
public TextureHandle noiseTexture;
|
|
public TextureHandle debugView;
|
|
|
|
// Inputs
|
|
public TextureHandle intermediateConvergence;
|
|
|
|
// Intermediates
|
|
public TextureHandle intermediateWeights;
|
|
|
|
// History
|
|
public TextureHandle luma;
|
|
public TextureHandle convergence;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Information required for STP's TAA pass
|
|
/// </summary>
|
|
class TaaData
|
|
{
|
|
public ComputeShader cs;
|
|
public int kernelIndex;
|
|
public int viewCount;
|
|
public Vector2Int dispatchSize;
|
|
|
|
// Common
|
|
public TextureHandle noiseTexture;
|
|
public TextureHandle debugView;
|
|
|
|
// Inputs
|
|
public TextureHandle intermediateColor;
|
|
public TextureHandle intermediateWeights;
|
|
|
|
// History
|
|
public TextureHandle priorFeedback;
|
|
public TextureHandle depthMotion;
|
|
public TextureHandle convergence;
|
|
|
|
// Outputs
|
|
public TextureHandle feedback;
|
|
public TextureHandle output;
|
|
}
|
|
|
|
// Internal helper function used to streamline usage of the render graph API
|
|
static TextureHandle UseTexture(IBaseRenderGraphBuilder builder, TextureHandle texture, AccessFlags flags = AccessFlags.Read)
|
|
{
|
|
builder.UseTexture(texture, flags);
|
|
return texture;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes the STP technique using the provided configuration in the target render graph
|
|
/// </summary>
|
|
/// <param name="renderGraph">render graph to execute STP within</param>
|
|
/// <param name="config">configuration parameters for STP</param>
|
|
/// <returns>Texture handle that contains the upscaled color output</returns>
|
|
public static TextureHandle Execute(RenderGraph renderGraph, ref Config config)
|
|
{
|
|
var runtimeResources = GraphicsSettings.GetRenderPipelineSettings<RuntimeResources>();
|
|
|
|
// Temporarily wrap the noise texture in an RTHandle so it can be imported into render graph
|
|
var noiseTexture = config.noiseTexture;
|
|
RTHandleStaticHelpers.SetRTHandleStaticWrapper(noiseTexture);
|
|
var noiseTextureRtHandle = RTHandleStaticHelpers.s_RTHandleWrapper;
|
|
|
|
RenderTargetInfo noiseTextureInfo;
|
|
noiseTextureInfo.width = noiseTexture.width;
|
|
noiseTextureInfo.height = noiseTexture.height;
|
|
noiseTextureInfo.volumeDepth = 1;
|
|
noiseTextureInfo.msaaSamples = 1;
|
|
noiseTextureInfo.format = noiseTexture.graphicsFormat;
|
|
noiseTextureInfo.bindMS = false;
|
|
|
|
TextureHandle noiseTextureHandle = renderGraph.ImportTexture(noiseTextureRtHandle, noiseTextureInfo);
|
|
|
|
var priorDepthMotion = config.historyContext.GetPreviousHistoryTexture(HistoryTextureType.DepthMotion, config.frameIndex);
|
|
var priorLuma = config.historyContext.GetPreviousHistoryTexture(HistoryTextureType.Luma, config.frameIndex);
|
|
var priorConvergence = config.historyContext.GetPreviousHistoryTexture(HistoryTextureType.Convergence, config.frameIndex);
|
|
var priorFeedback = config.historyContext.GetPreviousHistoryTexture(HistoryTextureType.Feedback, config.frameIndex);
|
|
|
|
var depthMotion = config.historyContext.GetCurrentHistoryTexture(HistoryTextureType.DepthMotion, config.frameIndex);
|
|
var luma = config.historyContext.GetCurrentHistoryTexture(HistoryTextureType.Luma, config.frameIndex);
|
|
var convergence = config.historyContext.GetCurrentHistoryTexture(HistoryTextureType.Convergence, config.frameIndex);
|
|
var feedback = config.historyContext.GetCurrentHistoryTexture(HistoryTextureType.Feedback, config.frameIndex);
|
|
|
|
// Resize the current render-size history textures if hardware dynamic scaling is enabled
|
|
if (config.enableHwDrs)
|
|
{
|
|
depthMotion.rt.ApplyDynamicScale();
|
|
luma.rt.ApplyDynamicScale();
|
|
convergence.rt.ApplyDynamicScale();
|
|
}
|
|
|
|
Vector2Int intermediateSize = config.enableHwDrs ? config.outputImageSize : config.currentImageSize;
|
|
|
|
// Enable the large 128 wide kernel whenever STP runs on Qualcomm GPUs.
|
|
// These GPUs require larger compute work groups in order to reach maximum FP16 ALU efficiency
|
|
bool enableLargeKernel = SystemInfo.graphicsDeviceVendorID == kQualcommVendorId;
|
|
|
|
Vector2Int kernelSize = new Vector2Int(8, enableLargeKernel ? 16 : 8);
|
|
|
|
SetupData setupData;
|
|
|
|
using (var builder = renderGraph.AddComputePass<SetupData>("STP Setup", out var passData, ProfilingSampler.Get(ProfileId.StpSetup)))
|
|
{
|
|
passData.cs = runtimeResources.setupCS;
|
|
passData.cs.shaderKeywords = null;
|
|
|
|
if (enableLargeKernel)
|
|
passData.cs.EnableKeyword(ShaderKeywords.EnableLargeKernel);
|
|
|
|
if (!config.enableTexArray)
|
|
passData.cs.EnableKeyword(ShaderKeywords.DisableTexture2DXArray);
|
|
|
|
// Populate the constant buffer data structure in the render graph pass data
|
|
// This data will be uploaded to the GPU when the node executes later in the frame
|
|
PopulateConstantData(ref config, ref passData.constantBufferData);
|
|
|
|
passData.noiseTexture = UseTexture(builder, noiseTextureHandle);
|
|
|
|
if (config.debugView.IsValid())
|
|
{
|
|
passData.cs.EnableKeyword(ShaderKeywords.EnableDebugMode);
|
|
passData.debugView = UseTexture(builder, config.debugView, AccessFlags.WriteAll);
|
|
}
|
|
|
|
passData.kernelIndex = passData.cs.FindKernel("StpSetup");
|
|
passData.viewCount = config.numActiveViews;
|
|
passData.dispatchSize = new Vector2Int(
|
|
CoreUtils.DivRoundUp(config.currentImageSize.x, kernelSize.x),
|
|
CoreUtils.DivRoundUp(config.currentImageSize.y, kernelSize.y)
|
|
);
|
|
|
|
passData.inputColor = UseTexture(builder, config.inputColor);
|
|
passData.inputDepth = UseTexture(builder, config.inputDepth);
|
|
passData.inputMotion = UseTexture(builder, config.inputMotion);
|
|
|
|
if (config.inputStencil.IsValid())
|
|
{
|
|
passData.cs.EnableKeyword(ShaderKeywords.EnableStencilResponsive);
|
|
passData.inputStencil = UseTexture(builder, config.inputStencil);
|
|
}
|
|
|
|
passData.intermediateColor = UseTexture(builder, renderGraph.CreateTexture(new TextureDesc(intermediateSize.x, intermediateSize.y, config.enableHwDrs, config.enableTexArray)
|
|
{
|
|
name = "STP Intermediate Color",
|
|
colorFormat = GraphicsFormat.A2B10G10R10_UNormPack32,
|
|
enableRandomWrite = true
|
|
}), AccessFlags.WriteAll);
|
|
|
|
Vector2Int convergenceSize = CalculateConvergenceTextureSize(intermediateSize);
|
|
passData.intermediateConvergence = UseTexture(builder, renderGraph.CreateTexture(new TextureDesc(convergenceSize.x, convergenceSize.y, config.enableHwDrs, config.enableTexArray)
|
|
{
|
|
name = "STP Intermediate Convergence",
|
|
colorFormat = GraphicsFormat.R8_UNorm,
|
|
enableRandomWrite = true
|
|
}), AccessFlags.WriteAll);
|
|
|
|
passData.priorDepthMotion = UseTexture(builder, renderGraph.ImportTexture(priorDepthMotion));
|
|
passData.depthMotion = UseTexture(builder, renderGraph.ImportTexture(depthMotion), AccessFlags.WriteAll);
|
|
passData.priorLuma = UseTexture(builder, renderGraph.ImportTexture(priorLuma));
|
|
passData.luma = UseTexture(builder, renderGraph.ImportTexture(luma), AccessFlags.WriteAll);
|
|
|
|
passData.priorFeedback = UseTexture(builder, renderGraph.ImportTexture(priorFeedback));
|
|
passData.priorConvergence = UseTexture(builder, renderGraph.ImportTexture(priorConvergence));
|
|
|
|
builder.SetRenderFunc(
|
|
(SetupData data, ComputeGraphContext ctx) =>
|
|
{
|
|
// Update the constant buffer data on the GPU
|
|
// TODO: Fix usage of m_WrappedCommandBuffer here once NRP support is added to ConstantBuffer.cs
|
|
ConstantBuffer.UpdateData(ctx.cmd.m_WrappedCommandBuffer, data.constantBufferData);
|
|
|
|
ConstantBuffer.Set<StpConstantBufferData>(data.cs, ShaderResources._StpConstantBufferData);
|
|
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpBlueNoiseIn, data.noiseTexture);
|
|
|
|
if (data.debugView.IsValid())
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpDebugOut, data.debugView);
|
|
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpInputColor, data.inputColor);
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpInputDepth, data.inputDepth);
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpInputMotion, data.inputMotion);
|
|
|
|
if (data.inputStencil.IsValid())
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpInputStencil, data.inputStencil, 0, RenderTextureSubElement.Stencil);
|
|
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpIntermediateColor, data.intermediateColor);
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpIntermediateConvergence, data.intermediateConvergence);
|
|
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpPriorDepthMotion, data.priorDepthMotion);
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpDepthMotion, data.depthMotion);
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpPriorLuma, data.priorLuma);
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpLuma, data.luma);
|
|
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpPriorFeedback, data.priorFeedback);
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpPriorConvergence, data.priorConvergence);
|
|
|
|
ctx.cmd.DispatchCompute(data.cs, data.kernelIndex, data.dispatchSize.x, data.dispatchSize.y, data.viewCount);
|
|
});
|
|
|
|
setupData = passData;
|
|
}
|
|
|
|
PreTaaData preTaaData;
|
|
|
|
using (var builder = renderGraph.AddComputePass<PreTaaData>("STP Pre-TAA", out var passData, ProfilingSampler.Get(ProfileId.StpPreTaa)))
|
|
{
|
|
passData.cs = runtimeResources.preTaaCS;
|
|
passData.cs.shaderKeywords = null;
|
|
|
|
if (enableLargeKernel)
|
|
passData.cs.EnableKeyword(ShaderKeywords.EnableLargeKernel);
|
|
|
|
if (!config.enableTexArray)
|
|
passData.cs.EnableKeyword(ShaderKeywords.DisableTexture2DXArray);
|
|
|
|
passData.noiseTexture = UseTexture(builder, noiseTextureHandle);
|
|
|
|
if (config.debugView.IsValid())
|
|
{
|
|
passData.cs.EnableKeyword(ShaderKeywords.EnableDebugMode);
|
|
passData.debugView = UseTexture(builder, config.debugView, AccessFlags.ReadWrite);
|
|
}
|
|
|
|
passData.kernelIndex = passData.cs.FindKernel("StpPreTaa");
|
|
passData.viewCount = config.numActiveViews;
|
|
passData.dispatchSize = new Vector2Int(
|
|
CoreUtils.DivRoundUp(config.currentImageSize.x, kernelSize.x),
|
|
CoreUtils.DivRoundUp(config.currentImageSize.y, kernelSize.y)
|
|
);
|
|
|
|
passData.intermediateConvergence = UseTexture(builder, setupData.intermediateConvergence);
|
|
|
|
passData.intermediateWeights = UseTexture(builder, renderGraph.CreateTexture(new TextureDesc(intermediateSize.x, intermediateSize.y, config.enableHwDrs, config.enableTexArray)
|
|
{
|
|
name = "STP Intermediate Weights",
|
|
colorFormat = GraphicsFormat.R8_UNorm,
|
|
enableRandomWrite = true
|
|
}), AccessFlags.WriteAll);
|
|
|
|
passData.luma = UseTexture(builder, renderGraph.ImportTexture(luma));
|
|
passData.convergence = UseTexture(builder, renderGraph.ImportTexture(convergence), AccessFlags.WriteAll);
|
|
|
|
builder.SetRenderFunc(
|
|
(PreTaaData data, ComputeGraphContext ctx) =>
|
|
{
|
|
ConstantBuffer.Set<StpConstantBufferData>(data.cs, ShaderResources._StpConstantBufferData);
|
|
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpBlueNoiseIn, data.noiseTexture);
|
|
|
|
if (data.debugView.IsValid())
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpDebugOut, data.debugView);
|
|
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpIntermediateConvergence, data.intermediateConvergence);
|
|
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpIntermediateWeights, data.intermediateWeights);
|
|
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpLuma, data.luma);
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpConvergence, data.convergence);
|
|
|
|
ctx.cmd.DispatchCompute(data.cs, data.kernelIndex, data.dispatchSize.x, data.dispatchSize.y, data.viewCount);
|
|
});
|
|
|
|
preTaaData = passData;
|
|
}
|
|
|
|
TaaData taaData;
|
|
|
|
using (var builder = renderGraph.AddComputePass<TaaData>("STP TAA", out var passData, ProfilingSampler.Get(ProfileId.StpTaa)))
|
|
{
|
|
passData.cs = runtimeResources.taaCS;
|
|
passData.cs.shaderKeywords = null;
|
|
|
|
if (enableLargeKernel)
|
|
passData.cs.EnableKeyword(ShaderKeywords.EnableLargeKernel);
|
|
|
|
if (!config.enableTexArray)
|
|
passData.cs.EnableKeyword(ShaderKeywords.DisableTexture2DXArray);
|
|
|
|
passData.noiseTexture = UseTexture(builder, noiseTextureHandle);
|
|
|
|
if (config.debugView.IsValid())
|
|
{
|
|
passData.cs.EnableKeyword(ShaderKeywords.EnableDebugMode);
|
|
passData.debugView = UseTexture(builder, config.debugView, AccessFlags.ReadWrite);
|
|
}
|
|
|
|
passData.kernelIndex = passData.cs.FindKernel("StpTaa");
|
|
passData.viewCount = config.numActiveViews;
|
|
passData.dispatchSize = new Vector2Int(
|
|
CoreUtils.DivRoundUp(config.outputImageSize.x, kernelSize.x),
|
|
CoreUtils.DivRoundUp(config.outputImageSize.y, kernelSize.y)
|
|
);
|
|
|
|
passData.intermediateColor = UseTexture(builder, setupData.intermediateColor);
|
|
passData.intermediateWeights = UseTexture(builder, preTaaData.intermediateWeights);
|
|
|
|
passData.priorFeedback = UseTexture(builder, renderGraph.ImportTexture(priorFeedback));
|
|
passData.depthMotion = UseTexture(builder, renderGraph.ImportTexture(depthMotion));
|
|
passData.convergence = UseTexture(builder, renderGraph.ImportTexture(convergence));
|
|
|
|
passData.feedback = UseTexture(builder, renderGraph.ImportTexture(feedback), AccessFlags.WriteAll);
|
|
|
|
passData.output = UseTexture(builder, config.destination, AccessFlags.WriteAll);
|
|
|
|
builder.SetRenderFunc(
|
|
(TaaData data, ComputeGraphContext ctx) =>
|
|
{
|
|
ConstantBuffer.Set<StpConstantBufferData>(data.cs, ShaderResources._StpConstantBufferData);
|
|
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpBlueNoiseIn, data.noiseTexture);
|
|
|
|
if (data.debugView.IsValid())
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpDebugOut, data.debugView);
|
|
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpIntermediateColor, data.intermediateColor);
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpIntermediateWeights, data.intermediateWeights);
|
|
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpPriorFeedback, data.priorFeedback);
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpDepthMotion, data.depthMotion);
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpConvergence, data.convergence);
|
|
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpFeedback, data.feedback);
|
|
ctx.cmd.SetComputeTextureParam(data.cs, data.kernelIndex, ShaderResources._StpOutput, data.output);
|
|
|
|
ctx.cmd.DispatchCompute(data.cs, data.kernelIndex, data.dispatchSize.x, data.dispatchSize.y, data.viewCount);
|
|
});
|
|
|
|
taaData = passData;
|
|
}
|
|
|
|
return taaData.output;
|
|
}
|
|
}
|
|
}
|