Search Unity

Enable or disable render features at runtime

Discussion in 'Universal Render Pipeline' started by demonixis, Jul 16, 2020.

  1. demonixis

    demonixis

    Joined:
    Aug 20, 2013
    Posts:
    185
    Hello,
    I would like to enable or disable some render features at runtime, is it possible?

    Thanks.
     
    LagField and Artem-Harmash like this.
  2. LagField

    LagField

    Joined:
    Nov 30, 2015
    Posts:
    4
  3. demonixis

    demonixis

    Joined:
    Aug 20, 2013
    Posts:
    185
    Hello,

    Here is my code that works with URP 8+

    Code (CSharp):
    1. public static class UniversalRenderPipelineAssetExtensions
    2. {
    3.     public static ScriptableRendererFeature DisableRenderFeature<T>(this UniversalRenderPipelineAsset asset) where T : ScriptableRendererFeature
    4.     {
    5.         var type = asset.GetType();
    6.         var propertyInfo = type.GetField("m_RendererDataList", BindingFlags.Instance | BindingFlags.NonPublic);
    7.  
    8.         if (propertyInfo == null)
    9.         {
    10.             return null;
    11.         }
    12.  
    13.         var scriptableRenderData = (ScriptableRendererData[])propertyInfo.GetValue(asset);
    14.  
    15.         if (scriptableRenderData != null && scriptableRenderData.Length > 0)
    16.         {
    17.             foreach (var renderData in scriptableRenderData)
    18.             {
    19.                 foreach (var rendererFeature in renderData.rendererFeatures)
    20.                 {
    21.                     if (rendererFeature is T)
    22.                     {
    23.                         rendererFeature.SetActive(false);
    24.  
    25.                         return rendererFeature;
    26.                     }
    27.                 }
    28.             }
    29.         }
    30.  
    31.         return null;
    32.     }
    33. }
    Somewhere in my code when I want to disable a render feature:

    Code (CSharp):
    1. private void DisableRenderFeature<T>(UniversalRenderPipelineAsset asset) where T : ScriptableRendererFeature
    2. {
    3.         var renderFeature = asset.DisableRenderFeature<T>();
    4.  
    5.         if (renderFeature != null)
    6.         {
    7.             m_DisabledRenderFeatures.Add(renderFeature);
    8.         }
    9.  }
    I use a list of disabled render features to enable them when the application quit, because it's a scriptable object and it's saved automatically. Another thing could be to clone the current renderer but I don't know how to do that.
     
    CainUnity and _geo__ like this.
  4. weiping-toh

    weiping-toh

    Joined:
    Sep 8, 2015
    Posts:
    192
    You could do something similar to the AdditionalCameraData if the switching of features is on a per-camera basis and read off the data from the affected camera as to enqueue the passes for the feature or not.
     
  5. tokar_dev

    tokar_dev

    Joined:
    Feb 1, 2017
    Posts:
    9
    You can also switch to another Forward Renderer

    Code (csharp):
    1. _camData = YourCamera.GetUniversalAdditionalCameraData();
    2. _camData.SetRenderer(index); // switch to renderer without/with render feature.
     
    GGsparta and LagField like this.
  6. Gasimo

    Gasimo

    Joined:
    Mar 3, 2015
    Posts:
    68
    I would assume switching the entire Renderers could result in a bit more significant performance impact than changing a specific field alone, but I might be wrong.
     
    ChiuanWei and tokar_dev like this.
  7. weiping-toh

    weiping-toh

    Joined:
    Sep 8, 2015
    Posts:
    192
    Switching Renderers is cheap on a per-camera basis (it is just switching the index for the renderer list) as the URP render loops on a per-camera basis rather than a per-renderer basis.
    And besides neither the Renderer or the Camera retains information pertaining each other for cleanup code to cleanup during switching.
    The only downfall to use a switching Renderer style is that one is limited to very small number of available renderers to switch (I believe it to be 7 or 8).
     
  8. Wopsie

    Wopsie

    Joined:
    Feb 2, 2015
    Posts:
    2
    ScriptableRendererFeature supports SetActive(). I made a criminally simple script that allows you to enable or disable your defined URP render features at runtime and in the editor:
    RFeatureToggler.PNG
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Rendering.Universal;
    5.  
    6. [System.Serializable]
    7. public struct RenderFeatureToggle
    8. {
    9.     public ScriptableRendererFeature feature;
    10.     public bool isEnabled;
    11. }
    12.  
    13. [ExecuteAlways]
    14. public class RenderFeatureToggler : MonoBehaviour
    15. {
    16.     [SerializeField]
    17.     private List<RenderFeatureToggle> renderFeatures = new List<RenderFeatureToggle>();
    18.     [SerializeField]
    19.     private UniversalRenderPipelineAsset pipelineAsset;
    20.  
    21.     private void Update()
    22.     {
    23.         foreach (RenderFeatureToggle toggleObj in renderFeatures)
    24.         {
    25.             toggleObj.feature.SetActive(toggleObj.isEnabled);
    26.         }
    27.     }
    28. }
    It may be a bit unsafe, I dont know what will happen if you enable or disable a render feature you defined in the list through the editor that is not supported by the currently active RenderPipelineAsset. Also it doesn't save anything to disk with a SO, but thats all additional logic that shouldn't be too much trouble to implement. For me this is working fine in it's current form for now.
     
  9. bwulff

    bwulff

    Joined:
    Jun 20, 2020
    Posts:
    12
    Thank you so much, this works like a charm!
     
  10. 8bitgoose

    8bitgoose

    Joined:
    Dec 28, 2014
    Posts:
    448
    Did this end up throwing an issues with you? I am trying to black out a screen between loads so a VR player doesn't puke.
     
  11. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,336
    A variation of @demonixis approach to make it more versatile. I am using this to access various features. It offers getting RendererFeatures by string too (use with caution). Using strings we can even get to some hidden (internal) feature types like "ScreenSpaceAmbientOcclusion".

    Not sure why they call these the "scriptable" render pipelines when we have to use reflection to get to the stuff we need. In the old legacy built-in renderer it was all freely accessible via post processing profiles *sigh*.

    Usage:
    Code (CSharp):
    1. // Yeah, accessing via strings sucks, but "this is the way" in Unity it seems.
    2. // There is a generic SetRendererFeatureActive<T> too but the ScreenSpaceAmbientOcclusion
    3. // type is declared as "internal" by Unity, thus we can not use it that way.
    4. UniversalRenderPipelineUtils.SetRendererFeatureActive("ScreenSpaceAmbientOcclusion", true);
    5.  
    6. var isActive = UniversalRenderPipelineUtils.IsRendererFeatureActive("ScreenSpaceAmbientOcclusion");

    Code (CSharp):
    1.  
    2. // Thanks to: https://forum.unity.com/threads/enable-or-disable-render-features-at-runtime.932571/
    3.  
    4. using System.Reflection;
    5. using UnityEngine;
    6. using UnityEngine.Rendering;
    7. using UnityEngine.Rendering.Universal;
    8.  
    9. public static class UniversalRenderPipelineUtils
    10. {
    11.     private static FieldInfo RenderDataList_FieldInfo;
    12.  
    13.     static UniversalRenderPipelineUtils()
    14.     {
    15.         try
    16.         {
    17.             var pipelineAssetType = typeof(UniversalRenderPipelineAsset);
    18.             var flags = BindingFlags.Instance | BindingFlags.NonPublic;
    19.  
    20.             RenderDataList_FieldInfo = pipelineAssetType.GetField("m_RendererDataList", flags);
    21.         }
    22.         catch (System.Exception e)
    23.         {
    24.             Debug.LogError("UniversalRenderPipelineUtils reflection cache build failed. Maybe the API has changed? \n" + e.Message);
    25.         }
    26.     }
    27.  
    28.     public static ScriptableRendererData[] GetRendererDataList(UniversalRenderPipelineAsset asset = null)
    29.     {
    30.         try
    31.         {
    32.             if (asset == null)
    33.             {
    34.                 asset = (UniversalRenderPipelineAsset)GraphicsSettings.currentRenderPipeline;
    35.             }
    36.             if (asset == null)
    37.             {
    38. #if UNITY_EDITOR
    39.                 Debug.LogWarning("GetRenderFeature() current renderpipleline is null.");
    40. #endif
    41.                 return null;
    42.             }
    43.  
    44.             if (RenderDataList_FieldInfo == null)
    45.             {
    46. #if UNITY_EDITOR
    47.                 Debug.LogWarning("GetRenderFeature() reflection failed to get m_RendererDataList field.");
    48. #endif
    49.                 return null;
    50.             }
    51.  
    52.             var renderDataList = (ScriptableRendererData[])RenderDataList_FieldInfo.GetValue(asset);
    53.             return renderDataList;
    54.         }
    55.         catch
    56.         {
    57.             // Fail silently if reflection failed.
    58.             return null;
    59.         }
    60.     }
    61.  
    62.     public static T GetRendererFeature<T>(UniversalRenderPipelineAsset asset = null) where T : ScriptableRendererFeature
    63.     {
    64.         var renderDataList = GetRendererDataList();
    65.         if (renderDataList == null || renderDataList.Length == 0)
    66.             return null;
    67.  
    68.         foreach (var renderData in renderDataList)
    69.         {
    70.             foreach (var rendererFeature in renderData.rendererFeatures)
    71.             {
    72.                 if (rendererFeature is T)
    73.                 {
    74.                     return rendererFeature as T;
    75.                 }
    76.             }
    77.         }
    78.  
    79.         return null;
    80.     }
    81.  
    82.     public static ScriptableRendererFeature GetRendererFeature(string typeName, UniversalRenderPipelineAsset asset = null)
    83.     {
    84.         var renderDataList = GetRendererDataList();
    85.         if (renderDataList == null || renderDataList.Length == 0)
    86.             return null;
    87.  
    88.         foreach (var renderData in renderDataList)
    89.         {
    90.             foreach (var rendererFeature in renderData.rendererFeatures)
    91.             {
    92.                 if (rendererFeature == null)
    93.                     continue;
    94.  
    95.                 if (rendererFeature.GetType().Name.Contains(typeName))
    96.                 {
    97.                     return rendererFeature;
    98.                 }
    99.             }
    100.         }
    101.  
    102.         return null;
    103.     }
    104.  
    105.     public static bool IsRendererFeatureActive<T>(UniversalRenderPipelineAsset asset = null, bool defaultValue = false) where T : ScriptableRendererFeature
    106.     {
    107.         var feature = GetRendererFeature<T>(asset);
    108.         if (feature == null)
    109.             return defaultValue;
    110.  
    111.         return feature.isActive;
    112.     }
    113.  
    114.     public static bool IsRendererFeatureActive(string typeName, UniversalRenderPipelineAsset asset = null, bool defaultValue = false)
    115.     {
    116.         var feature = GetRendererFeature(typeName, asset);
    117.         if (feature == null)
    118.             return defaultValue;
    119.  
    120.         return feature.isActive;
    121.     }
    122.  
    123.     public static void SetRendererFeatureActive<T>(bool active, UniversalRenderPipelineAsset asset = null) where T : ScriptableRendererFeature
    124.     {
    125.         var feature = GetRendererFeature<T>(asset);
    126.         if (feature == null)
    127.             return;
    128.  
    129.         feature.SetActive(active);
    130.     }
    131.  
    132.     public static void SetRendererFeatureActive(string typeName, bool active, UniversalRenderPipelineAsset asset = null)
    133.     {
    134.         var feature = GetRendererFeature(typeName, asset);
    135.         if (feature == null)
    136.             return;
    137.  
    138.         feature.SetActive(active);
    139.     }
    140. }
     
    Last edited: Nov 22, 2022
    tw00275, OCASM, Roman-Ilyin and 7 others like this.
  12. hm443

    hm443

    Joined:
    Sep 6, 2022
    Posts:
    3
    Thanks! That saves my life