2024-09-20 20:30:10 +02:00

315 lines
12 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;
}
sealed class MultiShaderTarget : UnityEditor.ShaderGraph.Target
{
public MultiShaderTarget()
{
displayName = "MultiShader Test Target";
isHidden = false;
}
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(string additionaShaderID)
{
SubShaderDescriptor result = new SubShaderDescriptor()
{
pipelineTag = "TestPipeline",
renderType = "Opaque",
renderQueue = "Geometry",
generatesPreview = true,
passes = new PassCollection(),
additionalShaderID = additionaShaderID
};
result.passes.Add(BuildPass());
return result;
}
public override void Setup(ref TargetSetupContext context)
{
var ss = BuildSubShader(null); // primary shader
var ss2 = BuildSubShader("{Name}-second");
context.AddSubShader(ss);
context.AddSubShader(ss2);
}
public override bool IsActive() => true;
public override bool WorksWithSRP(UnityEngine.Rendering.RenderPipelineAsset scriptableRenderPipeline)
{
return true;
}
public override void GetPropertiesGUI(ref TargetPropertyGUIContext context, System.Action onChange, System.Action<System.String> registerUndo)
{
}
}
[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);
}
}
}