Files
SampleRenderPasses/Assets/Scripts/RenderPasses/SourceToDestinationBlitRenderFeature.cs
2025-05-18 22:39:39 +03:00

323 lines
13 KiB
C#

#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.XR;
namespace Golems.RenderFeatures
{
/// <summary>
/// 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.
/// </summary>
public abstract class SourceToDestinationBlitRenderFeature : ScriptableRendererFeature
{
/// <summary>
/// The texture source type of the source of the pass.
/// Camera colour or named temporary texture.
/// </summary>
[Header("Source and Destination")]
[SerializeField]
protected RenderFeatures.TextureSourceType m_SourceTextureSourceType = RenderFeatures.TextureSourceType.CameraColour;
/// <summary>
/// If the source is a named texture then this is its name.
/// </summary>
[SerializeField]
private string m_SourceNamedTemporaryName;
protected int m_SourceNamedTemporaryNameHash;
/// <summary>
/// The texture source type of the destination of the pass.
/// Camera colour or named temporary texture.
/// </summary>
[SerializeField]
protected RenderFeatures.TextureSourceType m_DestinationTextureSourceType = RenderFeatures.TextureSourceType.CameraColour;
/// <summary>
/// If the destination is a named texture then this is its name.
/// </summary>
[SerializeField]
private string m_DestinationNamedTemporaryName;
protected int m_DestinationNamedTemporaryNameHash;
/// <summary>
/// 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.
/// </summary>
[SerializeField]
private string m_TempCopyNamedTemporaryName;
protected int m_TempCopyNamedTemporaryNameHash;
/// <summary>
/// When to insert the pass
/// </summary>
[Header("What stage")]
[SerializeField]
protected RenderPassEvent m_WhenToInsert = RenderPassEvent.BeforeRenderingPostProcessing;
/// <summary>
/// Gets the hashes of the named texture names.
///
/// NOTE: this should be called in the Create() of sub-classes.
/// </summary>
protected void DoCreate()
{
SetTextureNameHashes();
}
/// <summary>
/// Determine if the source and destination are the same.
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// Set the texture name hashes from the current name values.
/// </summary>
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
}
/// <summary>
/// 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.
/// </summary>
public abstract class SourceToDestinationBlitRenderPass : ScriptableRenderPass
{
/// <summary>
/// If this pass needs to create a temporary texture (when the source and destination
/// are the same) then is id is used.
/// </summary>
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;
/// <summary>
/// If this is true then Execute() can happen, if false something went wrong in the pass setup
/// so the Execute() should do nothing.
/// </summary>
protected bool m_GoodToExecute;
protected RenderTargetIdentifier m_CameraColourTargetIdentifier;
protected RenderTargetIdentifier m_SourceTargetIdentifier;
protected RenderTargetIdentifier m_DestinationTargetIdentifier;
protected RenderTargetIdentifier m_TempCopyTargetIdentifier;
/// <summary>
/// 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.
/// </summary>
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);
}
}
/// <summary>
/// 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.
/// </summary>
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;
}
}
}