diff --git a/com.unity.render-pipelines.core/Runtime/Utilities/CoreUtils.cs b/com.unity.render-pipelines.core/Runtime/Utilities/CoreUtils.cs
index 0eebc37b585..9e6665e982b 100644
--- a/com.unity.render-pipelines.core/Runtime/Utilities/CoreUtils.cs
+++ b/com.unity.render-pipelines.core/Runtime/Utilities/CoreUtils.cs
@@ -421,6 +421,24 @@ public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier col
ClearRenderTarget(cmd, clearFlag, clearColor);
}
+ ///
+ /// Set the current render texture.
+ ///
+ /// CommandBuffer used for rendering commands.
+ /// RenderTargetIdentifier of the render texture.
+ /// Color buffer load action.
+ /// Color buffer store action.
+ /// Depth buffer load action.
+ /// Depth buffer store action.
+ /// If not set to ClearFlag.None, specifies how to clear the render target after setup.
+ /// If applicable, color with which to clear the render texture after setup.
+ public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier buffer, RenderBufferLoadAction colorLoadAction, RenderBufferStoreAction colorStoreAction,
+ RenderBufferLoadAction depthLoadAction, RenderBufferStoreAction depthStoreAction, ClearFlag clearFlag, Color clearColor)
+ {
+ cmd.SetRenderTarget(buffer, colorLoadAction, colorStoreAction, depthLoadAction, depthStoreAction);
+ ClearRenderTarget(cmd, clearFlag, clearColor);
+ }
+
///
/// Set the current render texture.
///
diff --git a/com.unity.render-pipelines.universal/CHANGELOG.md b/com.unity.render-pipelines.universal/CHANGELOG.md
index 89c9fdbe898..50642a47bf2 100644
--- a/com.unity.render-pipelines.universal/CHANGELOG.md
+++ b/com.unity.render-pipelines.universal/CHANGELOG.md
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Added support for SSAO in Particle and Unlit shaders.
- Added a SpeedTree 8 Shader Graph but did not set it as the default when importing or upgrading Speed Tree 8 assets. Because URP doesn't yet support per-material culling, this Shader Graph does not yet behave in the same way as the existing handwritten SpeedTree 8 shader for URP.
- Added blending and box projection for reflection probes.
+- Added 'Store Actions' option that enables bandwidth optimizations on mobile GPU architectures.
### Changed
- Moved fog evaluation from vertex shader to pixel shader. This improves rendering of fog for big triangles and fog quality. This can change the look of the fog slightly.
diff --git a/com.unity.render-pipelines.universal/Documentation~/universalrp-asset.md b/com.unity.render-pipelines.universal/Documentation~/universalrp-asset.md
index 986bb2597b4..db80fb7a24d 100644
--- a/com.unity.render-pipelines.universal/Documentation~/universalrp-asset.md
+++ b/com.unity.render-pipelines.universal/Documentation~/universalrp-asset.md
@@ -113,7 +113,7 @@ This section allows you to fine-tune less commonly changed settings, which impac
| __Mixed Lighting__ | Enable [Mixed Lighting](https://docs.unity3d.com/Manual/LightMode-Mixed.html), to tell the pipeline to include mixed lighting shader variants in the build. |
| __Debug Level__ | Set the level of debug information that the render pipeline generates. The values are:
**Disabled**: Debugging is disabled. This is the default.
**Profiling**: Makes the render pipeline provide detailed information tags, which you can see in the FrameDebugger. |
| __Shader Variant Log Level__ | Set the level of information about Shader Stripping and Shader Variants you want to display when Unity finishes a build. Values are:
**Disabled**: Unity doesn’t log anything.
**Only Universal**: Unity logs information for all of the [URP Shaders](shaders-in-universalrp.md).
**All**: Unity logs information for all Shaders in your build.
You can see the information in Console panel when your build has finished. |
-
+| __Store Actions__ | Defines if Unity discards or stores the render targets of the DrawObjects Passes. Selecting the **Store** option significantly increases the memory bandwidth on mobile and tile-based GPUs.
__Auto__: Unity uses the **Discard** option by default, and falls back to the **Store** option if it detects any injected Passes.
__Discard__: Unity discards the render targets of render Passes that are not reused later (lower memory bandwidth).
__Store__: Unity stores all render targets of each Pass (higher memory bandwidth). |
### Adaptive Performance
diff --git a/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAssetEditor.cs b/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAssetEditor.cs
index 64b310bcc6c..7326b69fed5 100644
--- a/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAssetEditor.cs
+++ b/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAssetEditor.cs
@@ -82,6 +82,7 @@ internal class Styles
public static GUIContent supportsLightLayers = EditorGUIUtility.TrTextContent("Light Layers", "When enabled, UniversalRP uses rendering layers instead of culling mask for the purpose of selecting how lights affect groups of geometry. For deferred rendering, an extra render target is allocated.");
public static GUIContent debugLevel = EditorGUIUtility.TrTextContent("Debug Level", "Controls the level of debug information generated by the render pipeline. When Profiling is selected, the pipeline provides detailed profiling tags.");
public static GUIContent shaderVariantLogLevel = EditorGUIUtility.TrTextContent("Shader Variant Log Level", "Controls the level logging in of shader variants information is outputted when a build is performed. Information will appear in the Unity console when the build finishes.");
+ public static GUIContent storeActionsOptimizationText = EditorGUIUtility.TrTextContent("Store Actions", "Sets the store actions policy on tile based GPUs. Affects render targets memory usage and will impact performance.");
// Adaptive performance settings
public static GUIContent useAdaptivePerformance = EditorGUIUtility.TrTextContent("Use adaptive performance", "Allows Adaptive Performance to adjust rendering quality during runtime");
@@ -121,6 +122,7 @@ internal class Styles
SerializedProperty m_RequireOpaqueTextureProp;
SerializedProperty m_OpaqueDownsamplingProp;
SerializedProperty m_SupportsTerrainHolesProp;
+ SerializedProperty m_StoreActionsOptimizationProperty;
SerializedProperty m_HDR;
SerializedProperty m_MSAA;
@@ -245,6 +247,8 @@ void OnEnable()
m_ShaderVariantLogLevel = serializedObject.FindProperty("m_ShaderVariantLogLevel");
+ m_StoreActionsOptimizationProperty = serializedObject.FindProperty("m_StoreActionsOptimization");
+
m_ColorGradingMode = serializedObject.FindProperty("m_ColorGradingMode");
m_ColorGradingLutSize = serializedObject.FindProperty("m_ColorGradingLutSize");
@@ -640,6 +644,7 @@ void DrawAdvancedSettings()
EditorGUILayout.HelpBox(Styles.lightlayersUnsupportedMessage.text + unsupportedGraphicsApisMessage, MessageType.Warning, true);
EditorGUILayout.PropertyField(m_DebugLevelProp, Styles.debugLevel);
EditorGUILayout.PropertyField(m_ShaderVariantLogLevel, Styles.shaderVariantLogLevel);
+ EditorGUILayout.PropertyField(m_StoreActionsOptimizationProperty, Styles.storeActionsOptimizationText);
EditorGUI.indentLevel--;
EditorGUILayout.Space();
EditorGUILayout.Space();
diff --git a/com.unity.render-pipelines.universal/Runtime/Data/UniversalRenderPipelineAsset.cs b/com.unity.render-pipelines.universal/Runtime/Data/UniversalRenderPipelineAsset.cs
index 6f1ed37353a..09d2f7d0083 100644
--- a/com.unity.render-pipelines.universal/Runtime/Data/UniversalRenderPipelineAsset.cs
+++ b/com.unity.render-pipelines.universal/Runtime/Data/UniversalRenderPipelineAsset.cs
@@ -87,6 +87,19 @@ public enum ColorGradingMode
HighDynamicRange
}
+ ///
+ /// Defines if Unity discards or stores the render targets of the DrawObjects Passes. Selecting the Store option significantly increases the memory bandwidth on mobile and tile-based GPUs.
+ ///
+ public enum StoreActionsOptimization
+ {
+ /// Unity uses the Discard option by default, and falls back to the Store option if it detects any injected Passes.
+ Auto,
+ /// Unity discards the render targets of render Passes that are not reused later (lower memory bandwidth).
+ Discard,
+ /// Unity stores all render targets of each Pass (higher memory bandwidth).
+ Store
+ }
+
[ExcludeFromPreset]
public partial class UniversalRenderPipelineAsset : RenderPipelineAsset, ISerializationCallbackReceiver
{
@@ -127,6 +140,7 @@ public partial class UniversalRenderPipelineAsset : RenderPipelineAsset, ISerial
[SerializeField] bool m_RequireOpaqueTexture = false;
[SerializeField] Downsampling m_OpaqueDownsampling = Downsampling._2xBilinear;
[SerializeField] bool m_SupportsTerrainHoles = true;
+ [SerializeField] StoreActionsOptimization m_StoreActionsOptimization = StoreActionsOptimization.Auto;
// Quality settings
[SerializeField] bool m_SupportsHDR = true;
@@ -534,6 +548,16 @@ public bool supportsTerrainHoles
get { return m_SupportsTerrainHoles; }
}
+ ///
+ /// Returns the active store action optimization value.
+ ///
+ /// Returns the active store action optimization value.
+ public StoreActionsOptimization storeActionsOptimization
+ {
+ get { return m_StoreActionsOptimization; }
+ set { m_StoreActionsOptimization = value; }
+ }
+
public bool supportsHDR
{
get { return m_SupportsHDR; }
diff --git a/com.unity.render-pipelines.universal/Runtime/Passes/ScriptableRenderPass.cs b/com.unity.render-pipelines.universal/Runtime/Passes/ScriptableRenderPass.cs
index 58a59ba283c..c0ad2e0b25e 100644
--- a/com.unity.render-pipelines.universal/Runtime/Passes/ScriptableRenderPass.cs
+++ b/com.unity.render-pipelines.universal/Runtime/Passes/ScriptableRenderPass.cs
@@ -151,6 +151,16 @@ public RenderTargetIdentifier depthAttachment
get => m_DepthAttachment;
}
+ public RenderBufferStoreAction[] colorStoreActions
+ {
+ get => m_ColorStoreActions;
+ }
+
+ public RenderBufferStoreAction depthStoreAction
+ {
+ get => m_DepthStoreAction;
+ }
+
///
/// The input requirements for the ScriptableRenderPass, which has been set using ConfigureInput
///
@@ -170,6 +180,9 @@ public Color clearColor
get => m_ClearColor;
}
+ RenderBufferStoreAction[] m_ColorStoreActions = new RenderBufferStoreAction[] { RenderBufferStoreAction.Store };
+ RenderBufferStoreAction m_DepthStoreAction = RenderBufferStoreAction.Store;
+
///
/// A ProfilingSampler for the entire render pass. Used as a profiling name by ScriptableRenderer when executing the pass.
/// Default is Unnamed_ScriptableRenderPass.
@@ -204,6 +217,8 @@ public ScriptableRenderPass()
renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
m_ColorAttachments = new RenderTargetIdentifier[] {BuiltinRenderTextureType.CameraTarget, 0, 0, 0, 0, 0, 0, 0};
m_DepthAttachment = BuiltinRenderTextureType.CameraTarget;
+ m_ColorStoreActions = new RenderBufferStoreAction[] { RenderBufferStoreAction.Store, 0, 0, 0, 0, 0, 0, 0 };
+ m_DepthStoreAction = RenderBufferStoreAction.Store;
m_ClearFlag = ClearFlag.None;
m_ClearColor = Color.black;
overrideCameraTarget = false;
@@ -232,6 +247,25 @@ public void ConfigureInput(ScriptableRenderPassInput passInput)
m_Input = passInput;
}
+ public void ConfigureColorStoreAction(RenderBufferStoreAction storeAction, uint attachmentIndex = 0)
+ {
+ m_ColorStoreActions[attachmentIndex] = storeAction;
+ }
+
+ public void ConfigureColorStoreActions(RenderBufferStoreAction[] storeActions)
+ {
+ int count = Math.Min(storeActions.Length, m_ColorStoreActions.Length);
+ for (uint i = 0; i < count; ++i)
+ {
+ m_ColorStoreActions[i] = storeActions[i];
+ }
+ }
+
+ public void ConfigureDepthStoreAction(RenderBufferStoreAction storeAction)
+ {
+ m_DepthStoreAction = storeAction;
+ }
+
///
/// Configures render targets for this render pass. Call this instead of CommandBuffer.SetRenderTarget.
/// This method should be called inside Configure.
diff --git a/com.unity.render-pipelines.universal/Runtime/ScriptableRenderer.cs b/com.unity.render-pipelines.universal/Runtime/ScriptableRenderer.cs
index 300bc32583b..ba9b24919c8 100644
--- a/com.unity.render-pipelines.universal/Runtime/ScriptableRenderer.cs
+++ b/com.unity.render-pipelines.universal/Runtime/ScriptableRenderer.cs
@@ -408,6 +408,9 @@ static class RenderPassBlock
public static readonly int AfterRendering = 3;
}
+ private StoreActionsOptimization m_StoreActionsOptimizationSetting = StoreActionsOptimization.Auto;
+ private static bool m_UseOptimizedStoreActions = false;
+
const int k_RenderPassBlockCount = 4;
List m_ActiveRenderPassQueue = new List(32);
@@ -429,6 +432,14 @@ static class RenderPassBlock
static RenderTargetIdentifier[] m_ActiveColorAttachments = new RenderTargetIdentifier[] {0, 0, 0, 0, 0, 0, 0, 0 };
static RenderTargetIdentifier m_ActiveDepthAttachment;
+ private static RenderBufferStoreAction[] m_ActiveColorStoreActions = new RenderBufferStoreAction[]
+ {
+ RenderBufferStoreAction.Store, RenderBufferStoreAction.Store, RenderBufferStoreAction.Store, RenderBufferStoreAction.Store,
+ RenderBufferStoreAction.Store, RenderBufferStoreAction.Store, RenderBufferStoreAction.Store, RenderBufferStoreAction.Store
+ };
+
+ private static RenderBufferStoreAction m_ActiveDepthStoreAction = RenderBufferStoreAction.Store;
+
static AttachmentDescriptor[] m_ActiveColorAttachmentDescriptors = new AttachmentDescriptor[]
{
RenderingUtils.emptyAttachment, RenderingUtils.emptyAttachment, RenderingUtils.emptyAttachment,
@@ -486,6 +497,11 @@ public ScriptableRenderer(ScriptableRendererData data)
useRenderPassEnabled = data.useNativeRenderPass;
Clear(CameraRenderType.Base);
m_ActiveRenderPassQueue.Clear();
+
+ if (UniversalRenderPipeline.asset)
+ m_StoreActionsOptimizationSetting = UniversalRenderPipeline.asset.storeActionsOptimization;
+
+ m_UseOptimizedStoreActions = m_StoreActionsOptimizationSetting != StoreActionsOptimization.Store;
}
public void Dispose()
@@ -826,6 +842,10 @@ protected void AddRenderPasses(ref RenderingData renderingData)
if (activeRenderPassQueue[i] == null)
activeRenderPassQueue.RemoveAt(i);
}
+
+ // if any pass was injected, the "automatic" store optimization policy will disable the optimized load actions
+ if (count > 0 && m_StoreActionsOptimizationSetting == StoreActionsOptimization.Auto)
+ m_UseOptimizedStoreActions = false;
}
void ClearRenderingState(CommandBuffer cmd)
@@ -1225,9 +1245,10 @@ void SetRenderPassAttachments(CommandBuffer cmd, ScriptableRenderPass renderPass
else
{
// Only setup render target if current render pass attachments are different from the active ones
- if (passColorAttachment != m_ActiveColorAttachments[0] || passDepthAttachment != m_ActiveDepthAttachment || finalClearFlag != ClearFlag.None)
+ if (passColorAttachment != m_ActiveColorAttachments[0] || passDepthAttachment != m_ActiveDepthAttachment || finalClearFlag != ClearFlag.None ||
+ renderPass.colorStoreActions[0] != m_ActiveColorStoreActions[0] || renderPass.depthStoreAction != m_ActiveDepthStoreAction)
{
- SetRenderTarget(cmd, passColorAttachment, passDepthAttachment, finalClearFlag, finalClearColor);
+ SetRenderTarget(cmd, passColorAttachment, passDepthAttachment, finalClearFlag, finalClearColor, renderPass.colorStoreActions[0], renderPass.depthStoreAction);
#if ENABLE_VR && ENABLE_XR_MODULE
if (cameraData.xr.enabled)
@@ -1275,6 +1296,11 @@ internal static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier c
for (int i = 1; i < m_ActiveColorAttachments.Length; ++i)
m_ActiveColorAttachments[i] = 0;
+ m_ActiveColorStoreActions[0] = RenderBufferStoreAction.Store;
+ m_ActiveDepthStoreAction = RenderBufferStoreAction.Store;
+ for (int i = 1; i < m_ActiveColorStoreActions.Length; ++i)
+ m_ActiveColorStoreActions[i] = RenderBufferStoreAction.Store;
+
m_ActiveDepthAttachment = depthAttachment;
RenderBufferLoadAction colorLoadAction = ((uint)clearFlag & (uint)ClearFlag.Color) != 0 ? RenderBufferLoadAction.DontCare : RenderBufferLoadAction.Load;
@@ -1286,6 +1312,39 @@ internal static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier c
depthAttachment, depthLoadAction, RenderBufferStoreAction.Store, clearFlag, clearColor);
}
+ internal static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier colorAttachment, RenderTargetIdentifier depthAttachment, ClearFlag clearFlag, Color clearColor, RenderBufferStoreAction colorStoreAction, RenderBufferStoreAction depthStoreAction)
+ {
+ m_ActiveColorAttachments[0] = colorAttachment;
+ for (int i = 1; i < m_ActiveColorAttachments.Length; ++i)
+ m_ActiveColorAttachments[i] = 0;
+
+ m_ActiveColorStoreActions[0] = colorStoreAction;
+ m_ActiveDepthStoreAction = depthStoreAction;
+ for (int i = 1; i < m_ActiveColorStoreActions.Length; ++i)
+ m_ActiveColorStoreActions[i] = RenderBufferStoreAction.Store;
+
+ m_ActiveDepthAttachment = depthAttachment;
+
+ RenderBufferLoadAction colorLoadAction = ((uint)clearFlag & (uint)ClearFlag.Color) != 0 ?
+ RenderBufferLoadAction.DontCare : RenderBufferLoadAction.Load;
+
+ RenderBufferLoadAction depthLoadAction = ((uint)clearFlag & (uint)ClearFlag.Depth) != 0 ?
+ RenderBufferLoadAction.DontCare : RenderBufferLoadAction.Load;
+
+ // if we shouldn't use optimized store actions then fall back to the conservative safe (un-optimal!) route and just store everything
+ if (!m_UseOptimizedStoreActions)
+ {
+ if (colorStoreAction != RenderBufferStoreAction.StoreAndResolve)
+ colorStoreAction = RenderBufferStoreAction.Store;
+ if (depthStoreAction != RenderBufferStoreAction.StoreAndResolve)
+ depthStoreAction = RenderBufferStoreAction.Store;
+ }
+
+
+ SetRenderTarget(cmd, colorAttachment, colorLoadAction, colorStoreAction,
+ depthAttachment, depthLoadAction, depthStoreAction, clearFlag, clearColor);
+ }
+
static void SetRenderTarget(CommandBuffer cmd,
RenderTargetIdentifier colorAttachment,
RenderBufferLoadAction colorLoadAction,
@@ -1309,7 +1368,7 @@ static void SetRenderTarget(CommandBuffer cmd,
// XRTODO: Revisit the logic. Why treat CameraTarget depth specially?
if (depthAttachment == BuiltinRenderTextureType.CameraTarget)
{
- SetRenderTarget(cmd, colorAttachment, colorLoadAction, colorStoreAction, clearFlags, clearColor);
+ CoreUtils.SetRenderTarget(cmd, colorAttachment, colorLoadAction, colorStoreAction, depthLoadAction, depthStoreAction, clearFlags, clearColor);
}
else
{
diff --git a/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs b/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs
index 99c36f3040a..338bfd5ee90 100644
--- a/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs
+++ b/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs
@@ -575,7 +575,22 @@ public override void Setup(ScriptableRenderContext context, ref RenderingData re
if (this.actualRenderingMode == RenderingMode.Deferred)
EnqueueDeferred(ref renderingData, requiresDepthPrepass, renderPassInputs.requiresNormalsTexture, mainLightShadows, additionalLightShadows);
else
+ {
+ // Optimized store actions are very important on tile based GPUs and have a great impact on performance.
+ // if MSAA is enabled and any of the following passes need a copy of the color or depth target, make sure the MSAA'd surface is stored
+ // if following passes won't use it then just resolve (the Resolve action will still store the resolved surface, but discard the MSAA'd surface, which is very expensive to store).
+ RenderBufferStoreAction opaquePassColorStoreAction = RenderBufferStoreAction.Store;
+ if (cameraTargetDescriptor.msaaSamples > 1)
+ opaquePassColorStoreAction = copyColorPass ? RenderBufferStoreAction.StoreAndResolve : RenderBufferStoreAction.Store;
+
+ // make sure we store the depth only if following passes need it.
+ RenderBufferStoreAction opaquePassDepthStoreAction = (copyColorPass || requiresDepthCopyPass) ? RenderBufferStoreAction.Store : RenderBufferStoreAction.DontCare;
+
+ m_RenderOpaqueForwardPass.ConfigureColorStoreAction(opaquePassColorStoreAction);
+ m_RenderOpaqueForwardPass.ConfigureDepthStoreAction(opaquePassDepthStoreAction);
+
EnqueuePass(m_RenderOpaqueForwardPass);
+ }
if (camera.clearFlags == CameraClearFlags.Skybox && cameraData.renderType != CameraRenderType.Overlay)
{
@@ -617,6 +632,10 @@ public override void Setup(ScriptableRenderContext context, ref RenderingData re
EnqueuePass(m_TransparentSettingsPass);
}
+ RenderBufferStoreAction transparentPassColorStoreAction = cameraTargetDescriptor.msaaSamples > 1 ? RenderBufferStoreAction.Resolve : RenderBufferStoreAction.Store;
+ RenderBufferStoreAction transparentPassDepthStoreAction = RenderBufferStoreAction.DontCare;
+ m_RenderTransparentForwardPass.ConfigureColorStoreAction(transparentPassColorStoreAction);
+ m_RenderTransparentForwardPass.ConfigureDepthStoreAction(transparentPassDepthStoreAction);
EnqueuePass(m_RenderTransparentForwardPass);
}
EnqueuePass(m_OnRenderObjectCallbackPass);