2021-09-09 20:42:29 -04:00

223 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Rendering.HighDefinition;
using UnityEditor.ShaderGraph;
using UnityEditor.ShaderGraph.Internal;
using UnityEditor.Graphing;
using UnityEditor.ShaderGraph.Legacy;
using UnityEditor.Rendering.HighDefinition.ShaderGraph.Legacy;
using static UnityEngine.Rendering.HighDefinition.HDMaterialProperties;
using static UnityEditor.Rendering.HighDefinition.HDShaderUtils;
namespace UnityEditor.Rendering.HighDefinition.ShaderGraph
{
abstract class HDSubTarget : SubTarget<HDTarget>, IHasMetadata,
IRequiresData<SystemData>, IVersionable<ShaderGraphVersion>
{
SystemData m_SystemData;
protected bool m_MigrateFromOldCrossPipelineSG; // Use only for the migration to shader stack architecture
protected bool m_MigrateFromOldSG; // Use only for the migration from early shader stack architecture to recent one
// Interface Properties
SystemData IRequiresData<SystemData>.data
{
get => m_SystemData;
set => m_SystemData = value;
}
// Public properties
public SystemData systemData
{
get => m_SystemData;
set => m_SystemData = value;
}
protected virtual int ComputeMaterialNeedsUpdateHash() => 0;
public override bool IsActive() => true;
protected abstract ShaderID shaderID { get; }
protected abstract string customInspector { get; }
protected abstract GUID subTargetAssetGuid { get; }
protected abstract string renderType { get; }
protected abstract string renderQueue { get; }
protected abstract string templatePath { get; }
protected abstract string[] templateMaterialDirectories { get; }
protected abstract FieldDescriptor subShaderField { get; }
protected abstract string subShaderInclude { get; }
protected virtual string postDecalsInclude => null;
protected virtual string raytracingInclude => null;
protected virtual string pathtracingInclude => null;
protected virtual bool supportPathtracing => false;
protected virtual bool supportRaytracing => false;
public virtual string identifier => GetType().Name;
public virtual ScriptableObject GetMetadataObject()
{
var hdMetadata = ScriptableObject.CreateInstance<HDMetadata>();
hdMetadata.shaderID = shaderID;
hdMetadata.migrateFromOldCrossPipelineSG = m_MigrateFromOldCrossPipelineSG;
return hdMetadata;
}
ShaderGraphVersion IVersionable<ShaderGraphVersion>.version
{
get => systemData.version;
set => systemData.version = value;
}
// Generate migration description steps to migrate HD shader targets
internal static MigrationDescription<ShaderGraphVersion, HDSubTarget> migrationSteps => MigrationDescription.New(
Enum.GetValues(typeof(ShaderGraphVersion)).Cast<ShaderGraphVersion>().Select(
version => MigrationStep.New(version, (HDSubTarget t) => t.MigrateTo(version))
).ToArray()
);
/// <summary>
/// Override this method to handle migration in inherited subtargets
/// </summary>
/// <param name="version">The current version of the migration</param>
internal virtual void MigrateTo(ShaderGraphVersion version)
{
}
static readonly GUID kSourceCodeGuid = new GUID("c09e6e9062cbd5a48900c48a0c2ed1c2"); // HDSubTarget.cs
public override void Setup(ref TargetSetupContext context)
{
context.AddAssetDependency(kSourceCodeGuid, AssetCollection.Flags.SourceDependency);
context.AddAssetDependency(subTargetAssetGuid, AssetCollection.Flags.SourceDependency);
if (!context.HasCustomEditorForRenderPipeline(typeof(HDRenderPipelineAsset)))
context.AddCustomEditorForRenderPipeline(customInspector, typeof(HDRenderPipelineAsset));
if (migrationSteps.Migrate(this))
OnBeforeSerialize();
// Migration hack to have the case where SG doesn't have version yet but is already upgraded to the stack system
if (!systemData.firstTimeMigrationExecuted)
{
// Force the initial migration step
MigrateTo(ShaderGraphVersion.FirstTimeMigration);
systemData.firstTimeMigrationExecuted = true;
OnBeforeSerialize();
systemData.materialNeedsUpdateHash = ComputeMaterialNeedsUpdateHash();
}
foreach (var subShader in EnumerateSubShaders())
{
// patch render type and render queue from pass declaration:
var patchedSubShader = subShader;
patchedSubShader.renderType = renderType;
patchedSubShader.renderQueue = renderQueue;
context.AddSubShader(patchedSubShader);
}
}
protected SubShaderDescriptor PostProcessSubShader(SubShaderDescriptor subShaderDescriptor)
{
if (String.IsNullOrEmpty(subShaderDescriptor.pipelineTag))
subShaderDescriptor.pipelineTag = HDRenderPipeline.k_ShaderTagName;
var passes = subShaderDescriptor.passes.ToArray();
PassCollection finalPasses = new PassCollection();
for (int i = 0; i < passes.Length; i++)
{
var passDescriptor = passes[i].descriptor;
passDescriptor.passTemplatePath = templatePath;
passDescriptor.sharedTemplateDirectories = templateMaterialDirectories;
// Add the subShader to enable fields that depends on it
var originalRequireFields = passDescriptor.requiredFields;
// Duplicate require fields to avoid unwanted shared list modification
passDescriptor.requiredFields = new FieldCollection();
if (originalRequireFields != null)
foreach (var field in originalRequireFields)
passDescriptor.requiredFields.Add(field.field);
passDescriptor.requiredFields.Add(subShaderField);
IncludeCollection finalIncludes = new IncludeCollection();
var includeList = passDescriptor.includes.Select(include => include.descriptor).ToList();
// Replace include placeholders if necessary:
foreach (var include in passDescriptor.includes)
{
if (include.descriptor.value == CoreIncludes.kPassPlaceholder)
include.descriptor.value = subShaderInclude;
if (include.descriptor.value == CoreIncludes.kPostDecalsPlaceholder)
include.descriptor.value = postDecalsInclude;
if (include.descriptor.value == CoreIncludes.kRaytracingPlaceholder)
include.descriptor.value = raytracingInclude;
if (include.descriptor.value == CoreIncludes.kPathtracingPlaceholder)
include.descriptor.value = pathtracingInclude;
if (!String.IsNullOrEmpty(include.descriptor.value))
finalIncludes.Add(include.descriptor.value, include.descriptor.location, include.fieldConditions);
}
passDescriptor.includes = finalIncludes;
// Replace valid pixel blocks by automatic thing so we don't have to write them
var tmpCtx = new TargetActiveBlockContext(new List<BlockFieldDescriptor>(), passDescriptor);
GetActiveBlocks(ref tmpCtx);
if (passDescriptor.validPixelBlocks == null)
passDescriptor.validPixelBlocks = tmpCtx.activeBlocks.Where(b => b.shaderStage == ShaderStage.Fragment).ToArray();
if (passDescriptor.validVertexBlocks == null)
passDescriptor.validVertexBlocks = tmpCtx.activeBlocks.Where(b => b.shaderStage == ShaderStage.Vertex).ToArray();
// Add keywords from subshaders:
passDescriptor.keywords = passDescriptor.keywords == null ? new KeywordCollection() : new KeywordCollection { passDescriptor.keywords }; // Duplicate keywords to avoid side effects (static list modification)
passDescriptor.defines = passDescriptor.defines == null ? new DefineCollection() : new DefineCollection { passDescriptor.defines }; // Duplicate defines to avoid side effects (static list modification)
CollectPassKeywords(ref passDescriptor);
// Set default values for HDRP "surface" passes:
if (passDescriptor.structs == null)
passDescriptor.structs = CoreStructCollections.Default;
if (passDescriptor.fieldDependencies == null)
passDescriptor.fieldDependencies = CoreFieldDependencies.Default;
finalPasses.Add(passDescriptor, passes[i].fieldConditions);
}
subShaderDescriptor.passes = finalPasses;
return subShaderDescriptor;
}
protected virtual void CollectPassKeywords(ref PassDescriptor pass) {}
public override void GetFields(ref TargetFieldContext context)
{
// Common properties between all HD master nodes
// Dots
context.AddField(HDFields.DotsInstancing, systemData.dotsInstancing);
}
protected abstract IEnumerable<SubShaderDescriptor> EnumerateSubShaders();
public override void GetPropertiesGUI(ref TargetPropertyGUIContext context, Action onChange, Action<String> registerUndo)
{
var gui = new SubTargetPropertiesGUI(context, onChange, registerUndo, systemData, null, null);
AddInspectorPropertyBlocks(gui);
context.Add(gui);
}
protected abstract void AddInspectorPropertyBlocks(SubTargetPropertiesGUI blockList);
public override object saveContext
{
get
{
int hash = ComputeMaterialNeedsUpdateHash();
bool needsUpdate = hash != systemData.materialNeedsUpdateHash;
if (needsUpdate)
systemData.materialNeedsUpdateHash = hash;
return new HDSaveContext { updateMaterials = needsUpdate };
}
}
}
}