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.
156 lines
7.4 KiB
156 lines
7.4 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using UnityEditor.Graphing;
|
|
using UnityEngine;
|
|
|
|
namespace UnityEditor.ShaderGraph
|
|
{
|
|
sealed partial class GraphData : ISerializationCallbackReceiver
|
|
{
|
|
public static class GraphValidation
|
|
{
|
|
static Dictionary<Type, FieldInfo> s_ActiveSubTarget = new();
|
|
static readonly PropertyInfo s_JsonData = typeof(Serialization.JsonData<SubTarget>).GetProperty("value");
|
|
|
|
static SubTarget GetActiveSubTarget(Target target)
|
|
{
|
|
if (target.activeSubTarget != null && target.activeSubTarget.IsActive())
|
|
return target.activeSubTarget;
|
|
|
|
var type = target.GetType();
|
|
if (!s_ActiveSubTarget.TryGetValue(type, out var activeSubTarget))
|
|
{
|
|
activeSubTarget = type.GetField("m_ActiveSubTarget", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
s_ActiveSubTarget.Add(type, activeSubTarget);
|
|
}
|
|
if (activeSubTarget != null)
|
|
{
|
|
var jsonData = activeSubTarget.GetValue(target);
|
|
if (jsonData != null)
|
|
{
|
|
return s_JsonData.GetValue(jsonData) as SubTarget;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static void ValidateNode(AbstractMaterialNode node)
|
|
{
|
|
Type t = node.GetType();
|
|
node.ValidateNode();
|
|
if (!(node is BlockNode))
|
|
{
|
|
bool disallowedByAnyTargets = false;
|
|
bool disallowedByAllTargets = true;
|
|
bool disallowedByAnySubTarget = false;
|
|
IEnumerable<Target> targets = node.owner.activeTargets;
|
|
|
|
foreach (var target in targets)
|
|
{
|
|
var subtarget = GetActiveSubTarget(target);
|
|
if (subtarget != null && (!subtarget.IsNodeAllowedBySubTarget(t) || !node.IsSubTargetCompatible(subtarget.GetType())))
|
|
{
|
|
disallowedByAnySubTarget = true;
|
|
node.isValid = false;
|
|
node.owner.AddValidationError(node.objectId, $"{node.name} Node is not allowed by {subtarget.displayName} implementation", Rendering.ShaderCompilerMessageSeverity.Error);
|
|
}
|
|
//if at least one target doesn't allow a node, it is considered invalid
|
|
else if (!target.IsNodeAllowedByTarget(t))
|
|
{
|
|
disallowedByAnyTargets = true;
|
|
node.isValid = false;
|
|
node.owner.AddValidationError(node.objectId, $"{node.name} Node is not allowed by {target.displayName} implementation", Rendering.ShaderCompilerMessageSeverity.Warning);
|
|
node.owner.m_UnsupportedTargets.Add(target);
|
|
}
|
|
//at least one target does allow node, not going to be explicitly set inactive
|
|
else
|
|
{
|
|
disallowedByAllTargets = false;
|
|
}
|
|
|
|
if (subtarget != null && subtarget.ValidateNodeCompatibility(node, out string warningMessage, out Rendering.ShaderCompilerMessageSeverity severity))
|
|
{
|
|
if (severity == Rendering.ShaderCompilerMessageSeverity.Error)
|
|
{
|
|
disallowedByAnySubTarget = true;
|
|
node.isValid = false;
|
|
}
|
|
node.owner.AddValidationError(node.objectId, warningMessage, severity);
|
|
}
|
|
}
|
|
|
|
// Subgraphs have no allegiance to a Target/SubTarget workflow,
|
|
// but we can infer incompatibilities when SRPFilter and SubTargetFilter are
|
|
// similarly incompatible across nodes in a subgraph.
|
|
// Unfortunately, there isn't a good way to promote these compatibilities to
|
|
// the main graph without quite a bit more work.
|
|
if (node.owner.isSubGraph)
|
|
{
|
|
disallowedByAllTargets = false;
|
|
System.Text.StringBuilder sb = new();
|
|
if (!node.IsCompatibleWithSRPs(srpSet, out var badSrps))
|
|
{
|
|
disallowedByAnyTargets = true;
|
|
sb.Append("Nodes in subgraph have conflicting SRP restrictions; ");
|
|
foreach (var srp in badSrps)
|
|
sb.Append($"{srp.Name}, ");
|
|
sb.Remove(sb.Length - 2, 2);
|
|
sb.AppendLine();
|
|
}
|
|
if (!node.IsCompatibleWithSubTargetFilters(subTargetSet, out var badSubTargets))
|
|
{
|
|
disallowedByAnySubTarget = true;
|
|
sb.Append("Nodes in subgraph have conflicting Material restrictions; ");
|
|
foreach (var subTarget in badSubTargets)
|
|
sb.Append($"{subTarget.Name}, ");
|
|
sb.Remove(sb.Length - 2, 2);
|
|
}
|
|
if (disallowedByAnySubTarget || disallowedByAnyTargets)
|
|
{
|
|
node.isValid = false;
|
|
node.owner.AddValidationError(node.objectId, sb.ToString(), Rendering.ShaderCompilerMessageSeverity.Warning);
|
|
}
|
|
}
|
|
|
|
if (!disallowedByAnyTargets && !disallowedByAnySubTarget)
|
|
{
|
|
node.isValid = true;
|
|
}
|
|
|
|
//Set ActiveState based on if all targets disallow this node
|
|
if (disallowedByAllTargets)
|
|
{
|
|
node.SetOverrideActiveState(AbstractMaterialNode.ActiveState.ExplicitInactive);
|
|
node.owner.AddValidationError(node.objectId, $"{node.name} Node is not allowed by any active targets, and will not be used in generation", Rendering.ShaderCompilerMessageSeverity.Warning);
|
|
}
|
|
else
|
|
{
|
|
node.SetOverrideActiveState(AbstractMaterialNode.ActiveState.Implicit);
|
|
}
|
|
}
|
|
}
|
|
|
|
static HashSet<Type> srpSet;
|
|
static HashSet<Type> subTargetSet;
|
|
public static void ValidateGraph(GraphData graph)
|
|
{
|
|
graph.m_UnsupportedTargets.Clear();
|
|
|
|
srpSet = new();
|
|
subTargetSet = new();
|
|
|
|
if (graph.isSubGraph)
|
|
{
|
|
foreach(var node in graph.GetNodes<AbstractMaterialNode>())
|
|
{
|
|
node.GatherSRPCompatibility(ref srpSet);
|
|
node.GatherSubTargetCompatibility(ref subTargetSet);
|
|
}
|
|
}
|
|
GraphDataUtils.ApplyActionLeafFirst(graph, ValidateNode);
|
|
}
|
|
}
|
|
}
|
|
}
|