Net-Game/Library/PackageCache/com.unity.shadergraph@cdef3258c742/Tests/Editor/UnitTests/TargetTests.cs
2025-03-28 08:33:16 -04:00

352 lines
14 KiB
C#

using System.Linq;
using System.IO;
using NUnit.Framework;
using UnityEngine;
using UnityEditor.Graphing;
namespace UnityEditor.ShaderGraph.UnitTests
{
[TestFixture]
class TargetTests
{
[OneTimeSetUp]
public void RunBeforeAnyTests()
{
Debug.unityLogger.logHandler = new ConsoleLogHandler();
}
[Test]
public void CanCreateBlankGraph()
{
GraphData graph = new GraphData();
graph.AddContexts();
Assert.IsNotNull(graph.activeTargets);
Assert.AreEqual(0, graph.activeTargets.Count());
}
public static bool s_ForceVFXFakeTargetVisible = false;
#if !VFX_GRAPH_10_0_0_OR_NEWER
//A barebone VFXTarget for testing coverage.
sealed class VFXTarget : UnityEditor.ShaderGraph.Target
{
public VFXTarget()
{
displayName = "Fake VFX Target"; //Should not be displayed outside the test runner.
isHidden = !s_ForceVFXFakeTargetVisible;
}
public override void GetActiveBlocks(ref UnityEditor.ShaderGraph.TargetActiveBlockContext context)
{
context.AddBlock(ShaderGraph.BlockFields.SurfaceDescription.BaseColor);
context.AddBlock(ShaderGraph.BlockFields.SurfaceDescription.Alpha);
}
public override void GetFields(ref TargetFieldContext context)
{
}
public override void Setup(ref TargetSetupContext context)
{
}
public override bool IsActive() => false;
public override bool WorksWithSRP(UnityEngine.Rendering.RenderPipelineAsset scriptableRenderPipeline)
{
return false;
}
public override void GetPropertiesGUI(ref TargetPropertyGUIContext context, System.Action onChange, System.Action<System.String> registerUndo)
{
}
}
#endif
[Test]
public void CanInitializeOutputTargets()
{
s_ForceVFXFakeTargetVisible = true;
GraphData graph = new GraphData();
graph.AddContexts();
graph.InitializeOutputs(new[] { new VFXTarget() }, null);
Assert.IsNotNull(graph.activeTargets);
Assert.AreEqual(1, graph.activeTargets.Count());
Assert.AreEqual(typeof(VFXTarget), graph.activeTargets.ElementAt(0).GetType());
s_ForceVFXFakeTargetVisible = false;
}
[Test]
public void CanAddTarget()
{
s_ForceVFXFakeTargetVisible = true;
GraphData graph = new GraphData();
graph.AddContexts();
var vfxTarget = graph.allPotentialTargets.FirstOrDefault(x => x is VFXTarget);
graph.SetTargetActive(vfxTarget);
Assert.IsNotNull(graph.activeTargets);
Assert.AreEqual(1, graph.activeTargets.Count());
Assert.AreEqual(vfxTarget, graph.activeTargets.ElementAt(0));
s_ForceVFXFakeTargetVisible = false;
}
[Test]
public void ActiveTargetsArePotentialTargets()
{
s_ForceVFXFakeTargetVisible = true;
GraphData graph = new GraphData();
graph.AddContexts();
var vfxTarget = new VFXTarget();
graph.SetTargetActive(vfxTarget);
Assert.IsTrue(graph.allPotentialTargets.Contains(vfxTarget));
s_ForceVFXFakeTargetVisible = false;
}
[Test]
public void GetTargetIndexWorks()
{
s_ForceVFXFakeTargetVisible = true;
GraphData graph = new GraphData();
graph.AddContexts();
int targetIndex = graph.GetTargetIndexByKnownType(typeof(VFXTarget));
Assert.IsTrue(targetIndex >= 0);
var vfxTarget = new VFXTarget();
graph.SetTargetActive(vfxTarget);
var targetIndex2 = graph.GetTargetIndex(vfxTarget);
Assert.AreEqual(targetIndex, targetIndex2);
var nonActiveVFXTarget = new VFXTarget();
Assert.AreEqual(-1, graph.GetTargetIndex(nonActiveVFXTarget));
s_ForceVFXFakeTargetVisible = false;
}
[Test]
public void CanRemoveTarget()
{
s_ForceVFXFakeTargetVisible = true;
GraphData graph = new GraphData();
graph.AddContexts();
var vfxTarget = new VFXTarget();
graph.InitializeOutputs(new[] { vfxTarget }, null);
graph.SetTargetInactive(vfxTarget);
Assert.IsNotNull(graph.activeTargets);
Assert.AreEqual(0, graph.activeTargets.Count());
s_ForceVFXFakeTargetVisible = false;
}
[Test]
public void CanSetBlockActive()
{
s_ForceVFXFakeTargetVisible = true;
GraphData graph = new GraphData();
graph.AddContexts();
graph.InitializeOutputs(new[] { new VFXTarget() }, new BlockFieldDescriptor[] { BlockFields.SurfaceDescription.BaseColor, BlockFields.SurfaceDescription.NormalTS });
// Block active state should match VFX Target's default GetActiveBlocks
var blocks = graph.GetNodes<BlockNode>().ToList();
Assert.AreEqual(2, blocks.Count);
Assert.AreEqual(BlockFields.SurfaceDescription.BaseColor, blocks[0].descriptor);
Assert.AreEqual(true, blocks[0].isActive);
Assert.AreEqual(BlockFields.SurfaceDescription.NormalTS, blocks[1].descriptor);
Assert.AreEqual(false, blocks[1].isActive);
s_ForceVFXFakeTargetVisible = false;
}
[Test]
public void CanUpdateBlockActiveState()
{
s_ForceVFXFakeTargetVisible = true;
GraphData graph = new GraphData();
graph.AddContexts();
graph.InitializeOutputs(new[] { new VFXTarget() }, new BlockFieldDescriptor[] { BlockFields.SurfaceDescription.BaseColor, BlockFields.SurfaceDescription.NormalTS });
// Remove VFX target
var vfxTarget = graph.allPotentialTargets.FirstOrDefault(x => x is VFXTarget);
graph.SetTargetInactive(vfxTarget);
var activeBlocks = graph.GetActiveBlocksForAllActiveTargets();
graph.UpdateActiveBlocks(activeBlocks);
// All blocks should be inactive as there are no active targets
var blocks = graph.GetNodes<BlockNode>().ToList();
Assert.AreEqual(2, blocks.Count);
Assert.AreEqual(BlockFields.SurfaceDescription.BaseColor, blocks[0].descriptor);
Assert.AreEqual(false, blocks[0].isActive);
Assert.AreEqual(BlockFields.SurfaceDescription.NormalTS, blocks[1].descriptor);
Assert.AreEqual(false, blocks[1].isActive);
s_ForceVFXFakeTargetVisible = false;
}
abstract class TestShaderTarget : UnityEditor.ShaderGraph.Target
{
public override void GetActiveBlocks(ref UnityEditor.ShaderGraph.TargetActiveBlockContext context)
{
context.AddBlock(ShaderGraph.BlockFields.SurfaceDescription.BaseColor);
context.AddBlock(ShaderGraph.BlockFields.SurfaceDescription.Alpha);
}
public override void GetFields(ref TargetFieldContext context)
{
}
public static PassDescriptor BuildPass()
{
PassDescriptor pass = new PassDescriptor()
{
// Definition
displayName = "DepthOnly",
referenceName = "SHADERPASS_DEPTHONLY",
lightMode = "DepthOnly",
useInPreview = true,
// Template
passTemplatePath = "Packages/com.unity.shadergraph/Editor/Generation/Targets/BuiltIn/Editor/ShaderGraph/Templates/ShaderPass.template",
sharedTemplateDirectories = GenerationUtils.GetDefaultSharedTemplateDirectories(),
// Port Mask
validVertexBlocks = new BlockFieldDescriptor[] { BlockFields.VertexDescription.Position, BlockFields.VertexDescription.Normal, BlockFields.VertexDescription.Tangent },
validPixelBlocks = new BlockFieldDescriptor[] { BlockFields.SurfaceDescription.Alpha, BlockFields.SurfaceDescription.AlphaClipThreshold },
// Fields
structs = new StructCollection { { Structs.Attributes }, { Structs.SurfaceDescriptionInputs }, { Structs.VertexDescriptionInputs } },
fieldDependencies = FieldDependencies.Default,
// Conditional State
renderStates = null,
pragmas = new PragmaCollection { { Pragma.Target(ShaderModel.Target30) }, { Pragma.MultiCompileInstancing }, { Pragma.Vertex("vert") }, { Pragma.Fragment("frag") } },
defines = null,
keywords = null,
includes = null,
customInterpolators = null,
};
return pass;
}
public static SubShaderDescriptor BuildSubShader()
{
SubShaderDescriptor result = new SubShaderDescriptor()
{
pipelineTag = "TestPipeline",
renderType = "Opaque",
renderQueue = "Geometry",
generatesPreview = true,
passes = new PassCollection()
};
result.passes.Add(BuildPass());
return result;
}
public override bool IsActive() => true;
public override bool WorksWithSRP(UnityEngine.Rendering.RenderPipelineAsset scriptableRenderPipeline) => true;
public override void GetPropertiesGUI(ref TargetPropertyGUIContext context, System.Action onChange, System.Action<System.String> registerUndo)
{
}
}
sealed class MultiShaderTarget : TestShaderTarget
{
public MultiShaderTarget()
{
displayName = "MultiShader Test Target";
isHidden = false;
}
public override void Setup(ref TargetSetupContext context)
{
context.AddSubShader(BuildSubShader()); // primary shader
var ss2 = BuildSubShader();
ss2.additionalShaderID = "{Name}-second";
context.AddSubShader(ss2);
}
}
[Test]
public void CanBuildMultipleShaders()
{
GraphData graph = new GraphData();
graph.AddContexts();
var multiTarget = new MultiShaderTarget();
graph.SetTargetActive(multiTarget);
Assert.IsTrue(graph.allPotentialTargets.Contains(multiTarget));
graph.OnEnable();
graph.ValidateGraph();
var generator = new Generator(graph, graph.outputNode, GenerationMode.ForReals, "MyTestShader");
var generatedShaders = generator.allGeneratedShaders.ToList();
Assert.AreEqual(2, generatedShaders.Count);
Assert.IsTrue(generatedShaders[0].shaderName == "MyTestShader");
Assert.IsTrue(generatedShaders[1].shaderName == "MyTestShader-second");
Assert.IsTrue(generatedShaders[0].codeString.Contains("Shader \"MyTestShader\""));
Assert.IsTrue(generatedShaders[1].codeString.Contains("Shader \"MyTestShader-second\""));
// save graph to file on disk and import it...
var path = AssetDatabase.GenerateUniqueAssetPath("Assets/multiShaderTest.ShaderGraph");
FileUtilities.WriteShaderGraphToDisk(path, graph);
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate | ImportAssetOptions.DontDownloadFromCacheServer);
// check that we actually have two shader assets in the import result
// (they won't work, but they should exist)
var assets = AssetDatabase.LoadAllAssetsAtPath(path);
int shaderCount = assets.OfType<Shader>().Count();
Assert.AreEqual(2, shaderCount);
AssetDatabase.DeleteAsset(path);
}
sealed class DependencyShaderNameTarget : TestShaderTarget
{
public DependencyShaderNameTarget()
{
displayName = "DependencyShaderName Test Target";
isHidden = false;
}
public override void Setup(ref TargetSetupContext context)
{
var ss = BuildSubShader();
ss.shaderDependencies = new()
{
new ShaderDependency() { dependencyName = "TestDep1", shaderName = "Name-blah" },
new ShaderDependency() { dependencyName = "TestDep2", shaderName = "{Name}-blah" }
};
context.AddSubShader(ss);
}
}
[Test]
public void ShaderNamesAreCorrectReplacedForDependencies()
{
GraphData graph = new GraphData();
graph.AddContexts();
var testTarget = new DependencyShaderNameTarget();
graph.SetTargetActive(testTarget);
graph.OnEnable();
graph.ValidateGraph();
var generator = new Generator(graph, graph.outputNode, GenerationMode.ForReals, "MyTestShader");
var shaderCodeString = generator.generatedShader;
Assert.IsTrue(shaderCodeString.Contains("Dependency \"TestDep1\" = \"Name-blah\""));
Assert.IsTrue(shaderCodeString.Contains("Dependency \"TestDep2\" = \"MyTestShader-blah\""));
}
}
}