#if UNITY_EDITOR using UnityEditor; #endif using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using UnityEngine.XR; namespace Golems.RenderFeatures { /// /// A base class for RenderFeatures that take a source and do some operation on it and put /// the result into some destination. /// This base class provides settings for the source and destination textures via /// RenderFeatures.TextureSourceType, allowing the named temporary texture name to be given /// if need (named textures are provided by TemporaryCameraCopyTextureRenderFeature). /// Can also set a named textures as a temporary copy texture if the source and destination /// are the same. /// public abstract class SourceToDestinationBlitRenderFeature : ScriptableRendererFeature { /// /// The texture source type of the source of the pass. /// Camera colour or named temporary texture. /// [Header("Source and Destination")] [SerializeField] protected RenderFeatures.TextureSourceType m_SourceTextureSourceType = RenderFeatures.TextureSourceType.CameraColour; /// /// If the source is a named texture then this is its name. /// [SerializeField] private string m_SourceNamedTemporaryName; protected int m_SourceNamedTemporaryNameHash; /// /// The texture source type of the destination of the pass. /// Camera colour or named temporary texture. /// [SerializeField] protected RenderFeatures.TextureSourceType m_DestinationTextureSourceType = RenderFeatures.TextureSourceType.CameraColour; /// /// If the destination is a named texture then this is its name. /// [SerializeField] private string m_DestinationNamedTemporaryName; protected int m_DestinationNamedTemporaryNameHash; /// /// If the source and destination are the same then this named texture can be used as /// the temporary copy location. /// If not set then a the pass will create its own temporary texture. /// [SerializeField] private string m_TempCopyNamedTemporaryName; protected int m_TempCopyNamedTemporaryNameHash; /// /// When to insert the pass /// [Header("What stage")] [SerializeField] protected RenderPassEvent m_WhenToInsert = RenderPassEvent.BeforeRenderingPostProcessing; /// /// Gets the hashes of the named texture names. /// /// NOTE: this should be called in the Create() of sub-classes. /// protected void DoCreate() { SetTextureNameHashes(); } /// /// Determine if the source and destination are the same. /// /// protected bool IsSourceAndDestinationSame() { if (m_SourceTextureSourceType == TextureSourceType.CameraColour && m_DestinationTextureSourceType == TextureSourceType.CameraColour) { return true; } if (m_SourceTextureSourceType == TextureSourceType.NameTemporary && m_DestinationTextureSourceType == TextureSourceType.NameTemporary) { // // Both using a named temporary texture, are they the same // return m_SourceNamedTemporaryNameHash == m_DestinationNamedTemporaryNameHash; } return false; } /// /// Set the texture name hashes from the current name values. /// protected void SetTextureNameHashes() { m_SourceNamedTemporaryNameHash = TemporaryCameraCopyTextureRenderFeature.GetNameHash(m_SourceNamedTemporaryName); m_DestinationNamedTemporaryNameHash = TemporaryCameraCopyTextureRenderFeature.GetNameHash(m_DestinationNamedTemporaryName); m_TempCopyNamedTemporaryNameHash = TemporaryCameraCopyTextureRenderFeature.GetNameHash(m_TempCopyNamedTemporaryName); } #if UNITY_EDITOR private bool m_EditorTextureHashesFoldout; protected virtual void EditorOnInspectorGUIAfterBase() { EditorGUILayout.LabelField("Info", EditorStyles.boldLabel); var origIndent = EditorGUI.indentLevel; EditorGUI.indentLevel += 1; m_EditorTextureHashesFoldout = EditorGUILayout.Foldout(m_EditorTextureHashesFoldout, "Texture hashes"); if (m_EditorTextureHashesFoldout) { EditorGUI.BeginDisabledGroup(true); EditorGUI.indentLevel += 1; EditorTextureNameAndHash("Source", m_SourceNamedTemporaryName, m_SourceNamedTemporaryNameHash); EditorTextureNameAndHash("Destination", m_DestinationNamedTemporaryName, m_DestinationNamedTemporaryNameHash); EditorTextureNameAndHash("Source", m_SourceNamedTemporaryName, m_SourceNamedTemporaryNameHash); EditorGUI.EndDisabledGroup(); } EditorGUI.indentLevel = origIndent; EditorGUILayout.LabelField("Controls", EditorStyles.boldLabel); // // NOTE: // This button is not actually needed as the Create() of the render feature is called // if the exposed settings are changed. // if (GUILayout.Button("Update temp texture hashes")) { SetTextureNameHashes(); } } private static void EditorTextureNameAndHash(string label, string namedTemporaryName, int namedTemporaryNameHash) { EditorGUILayout.TextField(label); EditorGUI.indentLevel += 1; if (!string.IsNullOrEmpty(namedTemporaryName)) { EditorGUILayout.IntField(namedTemporaryName, namedTemporaryNameHash); } else { EditorGUILayout.IntField("null name", namedTemporaryNameHash); } EditorGUI.indentLevel -= 1; } [CustomEditor(typeof(SourceToDestinationBlitRenderFeature), true)] private class SourceToDestinationBlitRenderFeatureEditor : Editor { public override void OnInspectorGUI() { base.OnInspectorGUI(); var me = target as SourceToDestinationBlitRenderFeature; if (me != null) { me.EditorOnInspectorGUIAfterBase(); } } } #endif } /// /// The RenderPass for things that take a source, do some operation on it and put the /// result in some destination. /// /// NOTE: /// If the source and destination are different then the execute must at least copy the /// source to the destination (as long as m_GoodToExecute is true) otherwise later /// passes may not see the correct texture contents. /// public abstract class SourceToDestinationBlitRenderPass : ScriptableRenderPass { /// /// If this pass needs to create a temporary texture (when the source and destination /// are the same) then is id is used. /// protected readonly int m_ScreenCopyID = Shader.PropertyToID("_ScreenCopy"); protected RenderFeatures.TextureSourceType m_SourceTextureSourceType; protected int m_SourceNamedTemporaryNameHash; protected RenderFeatures.TextureSourceType m_DestinationTextureSourceType; protected int m_DestinationNamedTemporaryNameHash; protected int m_SameSourceDestNamedTemporaryNameHash; protected bool m_SourceAndDestinationSame; /// /// If this is true then Execute() can happen, if false something went wrong in the pass setup /// so the Execute() should do nothing. /// protected bool m_GoodToExecute; protected RenderTargetIdentifier m_CameraColourTargetIdentifier; protected RenderTargetIdentifier m_SourceTargetIdentifier; protected RenderTargetIdentifier m_DestinationTargetIdentifier; protected RenderTargetIdentifier m_TempCopyTargetIdentifier; /// /// Sets up the named textures and/or the camera colour texture and determines if the /// source and destination are the same. /// /// NOTE: /// Call this from whatever setup type method the sub-class implements. /// protected void DoSetup(RenderPassEvent whenToRender, RenderTargetIdentifier cameraColourTargetIdentifier, RenderFeatures.TextureSourceType sourceSourceType, int tempSourceTextureHash, RenderFeatures.TextureSourceType destinationSourceType, int tempDestinationTextureHash, int sameSourceDestTextureHash) { renderPassEvent = whenToRender; m_CameraColourTargetIdentifier = cameraColourTargetIdentifier; m_SourceTextureSourceType = sourceSourceType; m_SourceNamedTemporaryNameHash = tempSourceTextureHash; m_DestinationTextureSourceType = destinationSourceType; m_DestinationNamedTemporaryNameHash = tempDestinationTextureHash; m_SameSourceDestNamedTemporaryNameHash = sameSourceDestTextureHash; m_SourceAndDestinationSame = false; if (m_SourceTextureSourceType == TextureSourceType.CameraColour && m_DestinationTextureSourceType == TextureSourceType.CameraColour) { m_SourceAndDestinationSame = true; } else if (m_SourceTextureSourceType == TextureSourceType.NameTemporary && m_DestinationTextureSourceType == TextureSourceType.NameTemporary) { // // Both using a named temporary texture, are they the same // m_SourceAndDestinationSame = (m_SourceNamedTemporaryNameHash == m_DestinationNamedTemporaryNameHash); } } /// /// Gets the named textures and works out if all textures are available meaning that /// execution of the pass can go ahead. /// This create the local temporary copy texture if that is required. /// This sets up the RenderTargetIdentifiers the Execute() can then use. /// protected void DoOnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { m_GoodToExecute = false; if (m_SourceTextureSourceType == TextureSourceType.CameraColour) { m_SourceTargetIdentifier = m_CameraColourTargetIdentifier; } else if (!TemporaryCameraCopyTextureRenderFeature.TryGetTempTextureIdentifier(m_SourceNamedTemporaryNameHash, out m_SourceTargetIdentifier)) { return; } if (m_DestinationTextureSourceType == TextureSourceType.CameraColour) { m_DestinationTargetIdentifier = m_CameraColourTargetIdentifier; } else if (!TemporaryCameraCopyTextureRenderFeature.TryGetTempTextureIdentifier(m_DestinationNamedTemporaryNameHash, out m_DestinationTargetIdentifier)) { return; } if (m_SourceAndDestinationSame) { // // need some texture to use as a copy // if (m_SameSourceDestNamedTemporaryNameHash != TemporaryCameraCopyTextureRenderFeature.k_InvalidNameHash) { if (!TemporaryCameraCopyTextureRenderFeature.TryGetTempTextureIdentifier(m_SameSourceDestNamedTemporaryNameHash, out m_TempCopyTargetIdentifier)) { return; } } else { // // Use our own temporary texture // #if ENABLE_VR var renderTextureDescriptor = XRSettings.enabled ? XRSettings.eyeTextureDesc : renderingData.cameraData.cameraTargetDescriptor; #else var renderTextureDescriptor = renderingData.cameraData.cameraTargetDescriptor; #endif cmd.GetTemporaryRT(m_ScreenCopyID, renderTextureDescriptor); m_TempCopyTargetIdentifier = new RenderTargetIdentifier(m_ScreenCopyID); } } m_GoodToExecute = true; } } }