You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
346 lines
16 KiB
346 lines
16 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEditor.Graphing;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering.HighDefinition;
|
|
using UnityEditor.ShaderGraph;
|
|
using UnityEditor.VFX;
|
|
|
|
using static UnityEngine.Rendering.HighDefinition.HDMaterial;
|
|
|
|
namespace UnityEditor.Rendering.HighDefinition.ShaderGraph
|
|
{
|
|
abstract class HDSubTarget : SubTarget<HDTarget>, IHasMetadata,
|
|
IRequiresData<SystemData>, IVersionable<ShaderGraphVersion>, IRequireVFXContext
|
|
{
|
|
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;
|
|
}
|
|
|
|
// VFX Properties
|
|
protected VFXContext m_ContextVFX = null;
|
|
protected VFXTaskCompiledData m_TaskDataVFX;
|
|
protected bool TargetsVFX() => m_ContextVFX != null;
|
|
protected bool TargetVFXSupportsRaytracing() => TargetsVFX() && ((VFXAbstractParticleOutput)m_ContextVFX).isRayTraced;
|
|
|
|
protected virtual int ComputeMaterialNeedsUpdateHash() => 0;
|
|
|
|
public override bool IsActive() => true;
|
|
|
|
internal GUID subTargetGuid { get { return subTargetAssetGuid; } }
|
|
protected abstract ShaderID shaderID { get; }
|
|
|
|
protected abstract string customInspector { get; }
|
|
internal abstract MaterialResetter setupMaterialKeywordsAndPassFunc { get; }
|
|
|
|
protected abstract GUID subTargetAssetGuid { get; }
|
|
protected abstract string renderType { get; }
|
|
protected abstract string renderQueue { get; }
|
|
protected abstract string disableBatchingTag { 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;
|
|
protected virtual string[] sharedTemplatePath => new string[]{
|
|
$"{HDUtils.GetHDRenderPipelinePath()}Editor/Material/ShaderGraph/Templates/",
|
|
$"{HDUtils.GetVFXPath()}/Editor/ShaderGraph/Templates"
|
|
};
|
|
protected virtual bool supportGlobalMipBias => true;
|
|
|
|
public virtual string identifier => GetType().Name;
|
|
|
|
public virtual ScriptableObject GetMetadataObject(GraphDataReadOnly graph)
|
|
{
|
|
var hdMetadata = ScriptableObject.CreateInstance<HDMetadata>();
|
|
hdMetadata.shaderID = shaderID;
|
|
hdMetadata.subTargetGuid = subTargetGuid;
|
|
hdMetadata.migrateFromOldCrossPipelineSG = m_MigrateFromOldCrossPipelineSG;
|
|
hdMetadata.hdSubTargetVersion = systemData.version;
|
|
hdMetadata.hasVertexModificationInMotionVector = systemData.customVelocity || systemData.tessellation || graph.AnyVertexAnimationActive();
|
|
hdMetadata.isVFXCompatible = graph.IsVFXCompatible();
|
|
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)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this method to handle subtarget specific migration (ie including private versioning)
|
|
/// in inherited subtargets
|
|
/// </summary>
|
|
internal virtual void Migrate()
|
|
{
|
|
if (migrationSteps.Migrate(this))
|
|
OnBeforeSerialize();
|
|
}
|
|
|
|
static readonly GUID kSourceCodeGuid = new GUID("c09e6e9062cbd5a48900c48a0c2ed1c2"); // HDSubTarget.cs
|
|
|
|
public override void Setup(ref TargetSetupContext context)
|
|
{
|
|
Migrate();
|
|
|
|
systemData.materialNeedsUpdateHash = ComputeMaterialNeedsUpdateHash();
|
|
context.AddAssetDependency(kSourceCodeGuid, AssetCollection.Flags.SourceDependency);
|
|
context.AddAssetDependency(subTargetAssetGuid, AssetCollection.Flags.SourceDependency);
|
|
var inspector = customInspector;
|
|
if (!context.HasCustomEditorForRenderPipeline(typeof(HDRenderPipelineAsset)))
|
|
context.AddCustomEditorForRenderPipeline(inspector, typeof(HDRenderPipelineAsset));
|
|
|
|
foreach (var subShader in EnumerateSubShaders())
|
|
{
|
|
// patch render type and render queue from pass declaration:
|
|
var patchedSubShader = subShader;
|
|
patchedSubShader.renderType = renderType;
|
|
patchedSubShader.renderQueue = renderQueue;
|
|
patchedSubShader.disableBatchingTag = disableBatchingTag;
|
|
context.AddSubShader(patchedSubShader);
|
|
}
|
|
|
|
foreach (var kernel in EnumerateKernels())
|
|
{
|
|
context.AddKernel(kernel);
|
|
}
|
|
}
|
|
|
|
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;
|
|
if (passDescriptor.passTemplatePath?.Length == 0 || passDescriptor.passTemplatePath == null)
|
|
passDescriptor.passTemplatePath = templatePath;
|
|
passDescriptor.sharedTemplateDirectories = sharedTemplatePath.Concat(templateMaterialDirectories).ToArray();
|
|
|
|
// 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();
|
|
|
|
// Replace include placeholders if necessary:
|
|
foreach (var include in passDescriptor.includes)
|
|
{
|
|
var path = include.path;
|
|
|
|
if (path == CoreIncludes.kPassPlaceholder)
|
|
path = subShaderInclude;
|
|
if (path == CoreIncludes.kPostDecalsPlaceholder)
|
|
path = postDecalsInclude;
|
|
if (path == CoreIncludes.kRaytracingPlaceholder)
|
|
path = raytracingInclude;
|
|
if (path == CoreIncludes.kPathtracingPlaceholder)
|
|
path = pathtracingInclude;
|
|
|
|
if (!String.IsNullOrEmpty(path))
|
|
finalIncludes.Add(path, include.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 various collections, most are init in HDShaderPasses.cs
|
|
passDescriptor.keywords = passDescriptor.keywords == null ? new KeywordCollection() : new KeywordCollection { passDescriptor.keywords }; // Duplicate keywords to avoid side effects (static list modification)
|
|
|
|
passDescriptor.fieldDependencies = passDescriptor.fieldDependencies == null ? new DependencyCollection() : new DependencyCollection { passDescriptor.fieldDependencies }; // Duplicate fieldDependencies to avoid side effects (static list modification)
|
|
passDescriptor.fieldDependencies.Add(CoreFieldDependencies.Default);
|
|
|
|
if (systemData.debugSymbols && Unsupported.IsDeveloperMode())
|
|
{
|
|
passDescriptor.pragmas = new PragmaCollection
|
|
{
|
|
passDescriptor.pragmas,
|
|
Pragma.DebugSymbols
|
|
};
|
|
}
|
|
|
|
if (supportGlobalMipBias)
|
|
{
|
|
if (passDescriptor.defines == null)
|
|
passDescriptor.defines = new();
|
|
passDescriptor.defines.Add(CoreDefines.SupportGlobalMipBias);
|
|
}
|
|
|
|
CollectPassKeywords(ref passDescriptor);
|
|
|
|
finalPasses.Add(passDescriptor, passes[i].fieldConditions);
|
|
}
|
|
|
|
subShaderDescriptor.passes = finalPasses;
|
|
|
|
if (TargetsVFX())
|
|
subShaderDescriptor = VFXSubTarget.PostProcessSubShader(subShaderDescriptor, m_ContextVFX, m_TaskDataVFX);
|
|
|
|
return subShaderDescriptor;
|
|
}
|
|
|
|
protected KernelDescriptor PostProcessKernel(KernelDescriptor kernel)
|
|
{
|
|
// Process the kernel's pass basis.
|
|
var passDescriptor = kernel.passDescriptorReference;
|
|
passDescriptor.passTemplatePath = templatePath;
|
|
passDescriptor.sharedTemplateDirectories = sharedTemplatePath.Concat(templateMaterialDirectories).ToArray();
|
|
|
|
// 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();
|
|
|
|
// Replace include placeholders if necessary:
|
|
foreach (var include in passDescriptor.includes)
|
|
{
|
|
var path = include.path;
|
|
|
|
if (path == CoreIncludes.kPassPlaceholder)
|
|
path = subShaderInclude;
|
|
if (path == CoreIncludes.kPostDecalsPlaceholder)
|
|
path = postDecalsInclude;
|
|
if (path == CoreIncludes.kRaytracingPlaceholder)
|
|
path = raytracingInclude;
|
|
if (path == CoreIncludes.kPathtracingPlaceholder)
|
|
path = pathtracingInclude;
|
|
|
|
if (!String.IsNullOrEmpty(path))
|
|
finalIncludes.Add(path, include.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 various collections, most are init in HDShaderPasses.cs
|
|
passDescriptor.keywords = passDescriptor.keywords == null ? new KeywordCollection() : new KeywordCollection { passDescriptor.keywords }; // Duplicate keywords to avoid side effects (static list modification)
|
|
|
|
passDescriptor.fieldDependencies = passDescriptor.fieldDependencies == null ? new DependencyCollection() : new DependencyCollection { passDescriptor.fieldDependencies }; // Duplicate fieldDependencies to avoid side effects (static list modification)
|
|
passDescriptor.fieldDependencies.Add(CoreFieldDependencies.Default);
|
|
|
|
// Overwrite the pass pragmas with just the kernel pragma for now.
|
|
passDescriptor.pragmas = new PragmaCollection { Pragma.Kernel(kernel.name) };
|
|
|
|
if (supportGlobalMipBias)
|
|
{
|
|
if (passDescriptor.defines == null)
|
|
passDescriptor.defines = new();
|
|
passDescriptor.defines.Add(CoreDefines.SupportGlobalMipBias);
|
|
}
|
|
|
|
CollectPassKeywords(ref passDescriptor);
|
|
|
|
kernel.passDescriptorReference = passDescriptor;
|
|
kernel.sharedTemplateDirectories = sharedTemplatePath.Concat(templateMaterialDirectories).ToArray();
|
|
|
|
return kernel;
|
|
}
|
|
|
|
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);
|
|
|
|
// VFX Setup
|
|
if (TargetsVFX())
|
|
VFXSubTarget.GetFields(ref context, m_ContextVFX);
|
|
}
|
|
|
|
protected abstract IEnumerable<SubShaderDescriptor> EnumerateSubShaders();
|
|
|
|
protected abstract IEnumerable<KernelDescriptor> EnumerateKernels();
|
|
|
|
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 virtual 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 };
|
|
}
|
|
}
|
|
|
|
public void ConfigureContextData(VFXContext context, VFXTaskCompiledData data)
|
|
{
|
|
m_ContextVFX = context;
|
|
m_TaskDataVFX = data;
|
|
}
|
|
}
|
|
}
|