658 lines
32 KiB
C#
658 lines
32 KiB
C#
|
using System;
|
||
|
using System.Globalization;
|
||
|
using NUnit.Framework;
|
||
|
using UnityEngine;
|
||
|
using UnityEditor.Graphing;
|
||
|
using UnityEditor.ShaderGraph;
|
||
|
using UnityEditor.ShaderGraph.Internal;
|
||
|
|
||
|
namespace UnityEditor.ShaderGraph.UnitTests
|
||
|
{
|
||
|
[TestFixture]
|
||
|
class ShaderGeneratorTests
|
||
|
{
|
||
|
[OneTimeSetUp]
|
||
|
public void RunBeforeAnyTests()
|
||
|
{
|
||
|
Debug.unityLogger.logHandler = new ConsoleLogHandler();
|
||
|
}
|
||
|
|
||
|
class TestNode : AbstractMaterialNode
|
||
|
{
|
||
|
public const int V1Out = 0;
|
||
|
public const int V2Out = 1;
|
||
|
public const int V3Out = 2;
|
||
|
public const int V4Out = 3;
|
||
|
|
||
|
public TestNode()
|
||
|
{
|
||
|
AddSlot(new Vector1MaterialSlot(V1Out, "V1Out", "V1Out", SlotType.Output, 0));
|
||
|
AddSlot(new Vector2MaterialSlot(V2Out, "V2Out", "V2Out", SlotType.Output, Vector4.zero));
|
||
|
AddSlot(new Vector3MaterialSlot(V3Out, "V3Out", "V3Out", SlotType.Output, Vector4.zero));
|
||
|
AddSlot(new Vector4MaterialSlot(V4Out, "V4Out", "V4Out", SlotType.Output, Vector4.zero));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput1To1Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V1Out, ConcreteSlotValueType.Vector1);
|
||
|
Assert.AreEqual(string.Format("{0}", node.GetVariableNameForSlot(TestNode.V1Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput1To2Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V1Out, ConcreteSlotValueType.Vector2);
|
||
|
Assert.AreEqual(string.Format("({0}.xx)", node.GetVariableNameForSlot(TestNode.V1Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput1To3Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V1Out, ConcreteSlotValueType.Vector3);
|
||
|
Assert.AreEqual(string.Format("({0}.xxx)", node.GetVariableNameForSlot(TestNode.V1Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput1To4Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V1Out, ConcreteSlotValueType.Vector4);
|
||
|
Assert.AreEqual(string.Format("({0}.xxxx)", node.GetVariableNameForSlot(TestNode.V1Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput2To1Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V2Out, ConcreteSlotValueType.Vector1);
|
||
|
Assert.AreEqual(string.Format("({0}).x", node.GetVariableNameForSlot(TestNode.V2Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput2To2Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V2Out, ConcreteSlotValueType.Vector2);
|
||
|
Assert.AreEqual(string.Format("{0}", node.GetVariableNameForSlot(TestNode.V2Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput2To3Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V2Out, ConcreteSlotValueType.Vector3);
|
||
|
Assert.AreEqual(string.Format("($precision3({0}, 0.0))", node.GetVariableNameForSlot(TestNode.V2Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput2To4Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V2Out, ConcreteSlotValueType.Vector4);
|
||
|
Assert.AreEqual(string.Format("($precision4({0}, 0.0, 1.0))", node.GetVariableNameForSlot(TestNode.V2Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput3To1Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V3Out, ConcreteSlotValueType.Vector1);
|
||
|
Assert.AreEqual(string.Format("({0}).x", node.GetVariableNameForSlot(TestNode.V3Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput3To2Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V3Out, ConcreteSlotValueType.Vector2);
|
||
|
Assert.AreEqual(string.Format("({0}.xy)", node.GetVariableNameForSlot(TestNode.V3Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput3To3Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V3Out, ConcreteSlotValueType.Vector3);
|
||
|
Assert.AreEqual(string.Format("{0}", node.GetVariableNameForSlot(TestNode.V3Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput3To4Fails()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V3Out, ConcreteSlotValueType.Vector4);
|
||
|
Assert.AreEqual(string.Format("($precision4({0}, 1.0))", node.GetVariableNameForSlot(TestNode.V3Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput4To1Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V4Out, ConcreteSlotValueType.Vector1);
|
||
|
Assert.AreEqual(string.Format("({0}).x", node.GetVariableNameForSlot(TestNode.V4Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput4To2Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V4Out, ConcreteSlotValueType.Vector2);
|
||
|
Assert.AreEqual(string.Format("({0}.xy)", node.GetVariableNameForSlot(TestNode.V4Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput4To3Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V4Out, ConcreteSlotValueType.Vector3);
|
||
|
Assert.AreEqual(string.Format("({0}.xyz)", node.GetVariableNameForSlot(TestNode.V4Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput4To4Works()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutput(node, TestNode.V4Out, ConcreteSlotValueType.Vector4);
|
||
|
Assert.AreEqual(string.Format("{0}", node.GetVariableNameForSlot(TestNode.V4Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput1To4PreviewWorks()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var result = GenerationUtils.AdaptNodeOutputForPreview(node, TestNode.V1Out);
|
||
|
Assert.AreEqual(string.Format("half4({0}, {0}, {0}, 1.0)", node.GetVariableNameForSlot(TestNode.V1Out)), result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput2To4PreviewWorks()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var expected = string.Format("half4({0}.x, {0}.y, 0.0, 1.0)", node.GetVariableNameForSlot(TestNode.V2Out));
|
||
|
var result = GenerationUtils.AdaptNodeOutputForPreview(node, TestNode.V2Out);
|
||
|
Assert.AreEqual(expected, result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput3To4PreviewWorks()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var expected = string.Format("half4({0}.x, {0}.y, {0}.z, 1.0)", node.GetVariableNameForSlot(TestNode.V3Out));
|
||
|
var result = GenerationUtils.AdaptNodeOutputForPreview(node, TestNode.V3Out);
|
||
|
Assert.AreEqual(expected, result);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AdaptNodeOutput4To4PreviewWorks()
|
||
|
{
|
||
|
var node = new TestNode();
|
||
|
var expected = string.Format("half4({0}.x, {0}.y, {0}.z, 1.0)", node.GetVariableNameForSlot(TestNode.V4Out));
|
||
|
var result = GenerationUtils.AdaptNodeOutputForPreview(node, TestNode.V4Out);
|
||
|
Assert.AreEqual(expected, result);
|
||
|
}
|
||
|
|
||
|
public struct PackingTestCase
|
||
|
{
|
||
|
internal string name;
|
||
|
internal StructDescriptor inputStruct;
|
||
|
internal StructDescriptor expectedOutputStruct;
|
||
|
internal string expectedInterpolatorFunction;
|
||
|
public override string ToString()
|
||
|
{
|
||
|
return name;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static readonly PackingTestCase[] s_PackingTestCase = new[]
|
||
|
{
|
||
|
new PackingTestCase()
|
||
|
{
|
||
|
name = "Simple_With_Semantic",
|
||
|
inputStruct = new StructDescriptor()
|
||
|
{
|
||
|
name = "Input",
|
||
|
packFields = false,
|
||
|
fields = new []
|
||
|
{
|
||
|
new FieldDescriptor(tag: "Original", name: "position", define: string.Empty, type: ShaderValueType.Float4, semantic: "SV_POSITION", interpolation: "SV_POSITION_QUALIFIERS"),
|
||
|
new FieldDescriptor(tag: "Original", name: "normalWS", define: string.Empty, type: ShaderValueType.Float3, semantic: "NORMAL_WS", interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloatA", define: string.Empty, type: ShaderValueType.Float, semantic: string.Empty, interpolation: string.Empty),
|
||
|
}
|
||
|
},
|
||
|
|
||
|
expectedOutputStruct = new StructDescriptor()
|
||
|
{
|
||
|
name = "PackedInput",
|
||
|
packFields = true,
|
||
|
fields = new []
|
||
|
{
|
||
|
new FieldDescriptor(tag: "Original", name: "position", define: string.Empty, type: ShaderValueType.Float4, semantic: "SV_POSITION", interpolation: "SV_POSITION_QUALIFIERS"),
|
||
|
new FieldDescriptor(tag: "Original", name: "normalWS", define: string.Empty, type: ShaderValueType.Float3, semantic: "NORMAL_WS", interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "PackedInput", name: "myFloatA", define: string.Empty, type: "float1", semantic: "INTERP0", interpolation: string.Empty),
|
||
|
}
|
||
|
},
|
||
|
expectedInterpolatorFunction = @"
|
||
|
PackedInput PackInput (Input input)
|
||
|
{
|
||
|
PackedInput output;
|
||
|
ZERO_INITIALIZE(PackedInput, output);
|
||
|
output.position = input.position;
|
||
|
output.normalWS = input.normalWS;
|
||
|
output.myFloatA.x = input.myFloatA;
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
Input UnpackInput (PackedInput input)
|
||
|
{
|
||
|
Input output;
|
||
|
output.position = input.position;
|
||
|
output.normalWS = input.normalWS;
|
||
|
output.myFloatA = input.myFloatA.x;
|
||
|
return output;
|
||
|
}"
|
||
|
},
|
||
|
|
||
|
new PackingTestCase()
|
||
|
{
|
||
|
name = "Equivalent_Packing_F3F3F1F1_A",
|
||
|
inputStruct = new StructDescriptor()
|
||
|
{
|
||
|
name = "Input",
|
||
|
packFields = false,
|
||
|
fields = new []
|
||
|
{
|
||
|
new FieldDescriptor(tag: "Original", name: "myVector3_A", define: string.Empty, type: ShaderValueType.Float3, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myVector3_B", define: string.Empty, type: ShaderValueType.Float3, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloat_A", define: string.Empty, type: ShaderValueType.Float, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloat_B", define: string.Empty, type: ShaderValueType.Float, semantic: string.Empty, interpolation: string.Empty),
|
||
|
}
|
||
|
},
|
||
|
|
||
|
expectedOutputStruct = new StructDescriptor()
|
||
|
{
|
||
|
name = "PackedInput",
|
||
|
packFields = true,
|
||
|
fields = new []
|
||
|
{
|
||
|
new FieldDescriptor(tag: "PackedInput", name: "packed_myVector3_A_myFloat_A", define: string.Empty, type: "float4", semantic: "INTERP0", interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "PackedInput", name: "packed_myVector3_B_myFloat_B", define: string.Empty, type: "float4", semantic: "INTERP1", interpolation: string.Empty),
|
||
|
}
|
||
|
},
|
||
|
expectedInterpolatorFunction = @"
|
||
|
PackedInput PackInput (Input input)
|
||
|
{
|
||
|
PackedInput output;
|
||
|
ZERO_INITIALIZE(PackedInput, output);
|
||
|
output.packed_myVector3_A_myFloat_A.xyz = input.myVector3_A;
|
||
|
output.packed_myVector3_A_myFloat_A.w = input.myFloat_A;
|
||
|
output.packed_myVector3_B_myFloat_B.xyz = input.myVector3_B;
|
||
|
output.packed_myVector3_B_myFloat_B.w = input.myFloat_B;
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
Input UnpackInput (PackedInput input)
|
||
|
{
|
||
|
Input output;
|
||
|
output.myVector3_A = input.packed_myVector3_A_myFloat_A.xyz;
|
||
|
output.myFloat_A = input.packed_myVector3_A_myFloat_A.w;
|
||
|
output.myVector3_B = input.packed_myVector3_B_myFloat_B.xyz;
|
||
|
output.myFloat_B = input.packed_myVector3_B_myFloat_B.w;
|
||
|
return output;
|
||
|
}"
|
||
|
},
|
||
|
|
||
|
new PackingTestCase()
|
||
|
{
|
||
|
name = "Equivalent_Packing_F3F3F1F1_B",
|
||
|
inputStruct = new StructDescriptor()
|
||
|
{
|
||
|
name = "Input",
|
||
|
packFields = false,
|
||
|
fields = new []
|
||
|
{
|
||
|
//This test insure the order of input packing doesn't fail the following packing
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloat_A", define: string.Empty, type: ShaderValueType.Float, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloat_B", define: string.Empty, type: ShaderValueType.Float, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myVector3_A", define: string.Empty, type: ShaderValueType.Float3, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myVector3_B", define: string.Empty, type: ShaderValueType.Float3, semantic: string.Empty, interpolation: string.Empty),
|
||
|
}
|
||
|
},
|
||
|
|
||
|
expectedOutputStruct = new StructDescriptor()
|
||
|
{
|
||
|
name = "PackedInput",
|
||
|
packFields = true,
|
||
|
fields = new []
|
||
|
{
|
||
|
new FieldDescriptor(tag: "PackedInput", name: "packed_myVector3_A_myFloat_A", define: string.Empty, type: "float4", semantic: "INTERP0", interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "PackedInput", name: "packed_myVector3_B_myFloat_B", define: string.Empty, type: "float4", semantic: "INTERP1", interpolation: string.Empty),
|
||
|
}
|
||
|
},
|
||
|
expectedInterpolatorFunction = @"
|
||
|
PackedInput PackInput (Input input)
|
||
|
{
|
||
|
PackedInput output;
|
||
|
ZERO_INITIALIZE(PackedInput, output);
|
||
|
output.packed_myVector3_A_myFloat_A.xyz = input.myVector3_A;
|
||
|
output.packed_myVector3_A_myFloat_A.w = input.myFloat_A;
|
||
|
output.packed_myVector3_B_myFloat_B.xyz = input.myVector3_B;
|
||
|
output.packed_myVector3_B_myFloat_B.w = input.myFloat_B;
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
Input UnpackInput (PackedInput input)
|
||
|
{
|
||
|
Input output;
|
||
|
output.myVector3_A = input.packed_myVector3_A_myFloat_A.xyz;
|
||
|
output.myFloat_A = input.packed_myVector3_A_myFloat_A.w;
|
||
|
output.myVector3_B = input.packed_myVector3_B_myFloat_B.xyz;
|
||
|
output.myFloat_B = input.packed_myVector3_B_myFloat_B.w;
|
||
|
return output;
|
||
|
}"
|
||
|
},
|
||
|
|
||
|
new PackingTestCase()
|
||
|
{
|
||
|
name = "Packing_F3F3F2",
|
||
|
inputStruct = new StructDescriptor()
|
||
|
{
|
||
|
name = "Input",
|
||
|
packFields = false,
|
||
|
fields = new []
|
||
|
{
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloat_A", define: string.Empty, type: ShaderValueType.Float3, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloat_B", define: string.Empty, type: ShaderValueType.Float3, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myVector2", define: string.Empty, type: ShaderValueType.Float2, semantic: string.Empty, interpolation: string.Empty),
|
||
|
}
|
||
|
},
|
||
|
|
||
|
expectedOutputStruct = new StructDescriptor()
|
||
|
{
|
||
|
name = "PackedInput",
|
||
|
packFields = true,
|
||
|
fields = new []
|
||
|
{
|
||
|
new FieldDescriptor(tag: "PackedInput", name: "packed_myFloat_A_myVector2x", define: string.Empty, type: "float4", semantic: "INTERP0", interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "PackedInput", name: "packed_myFloat_B_myVector2y", define: string.Empty, type: "float4", semantic: "INTERP1", interpolation: string.Empty),
|
||
|
}
|
||
|
},
|
||
|
expectedInterpolatorFunction = @"
|
||
|
PackedInput PackInput (Input input)
|
||
|
{
|
||
|
PackedInput output;
|
||
|
ZERO_INITIALIZE(PackedInput, output);
|
||
|
output.packed_myFloat_A_myVector2x.xyz = input.myFloat_A;
|
||
|
output.packed_myFloat_A_myVector2x.w = input.myVector2.x;
|
||
|
output.packed_myFloat_B_myVector2y.xyz = input.myFloat_B;
|
||
|
output.packed_myFloat_B_myVector2y.w = input.myVector2.y;
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
Input UnpackInput (PackedInput input)
|
||
|
{
|
||
|
Input output;
|
||
|
output.myFloat_A = input.packed_myFloat_A_myVector2x.xyz;
|
||
|
output.myVector2.x = input.packed_myFloat_A_myVector2x.w;
|
||
|
output.myFloat_B = input.packed_myFloat_B_myVector2y.xyz;
|
||
|
output.myVector2.y = input.packed_myFloat_B_myVector2y.w;
|
||
|
return output;
|
||
|
}
|
||
|
"
|
||
|
},
|
||
|
|
||
|
new PackingTestCase()
|
||
|
{
|
||
|
name = "Packing_F3F3F3F3",
|
||
|
inputStruct = new StructDescriptor()
|
||
|
{
|
||
|
name = "Input",
|
||
|
packFields = false,
|
||
|
fields = new []
|
||
|
{
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloat3_A", define: string.Empty, type: ShaderValueType.Float3, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloat3_B", define: string.Empty, type: ShaderValueType.Float3, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloat3_C", define: string.Empty, type: ShaderValueType.Float3, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloat3_D", define: string.Empty, type: ShaderValueType.Float3, semantic: string.Empty, interpolation: string.Empty),
|
||
|
}
|
||
|
},
|
||
|
|
||
|
expectedOutputStruct = new StructDescriptor()
|
||
|
{
|
||
|
name = "PackedInput",
|
||
|
packFields = true,
|
||
|
fields = new []
|
||
|
{
|
||
|
new FieldDescriptor(tag: "PackedInput", name: "packed_myFloat3_A_myFloat3_Dx", define: string.Empty, type: "float4", semantic: "INTERP0", interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "PackedInput", name: "packed_myFloat3_B_myFloat3_Dy", define: string.Empty, type: "float4", semantic: "INTERP1", interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "PackedInput", name: "packed_myFloat3_C_myFloat3_Dz", define: string.Empty, type: "float4", semantic: "INTERP2", interpolation: string.Empty),
|
||
|
}
|
||
|
},
|
||
|
expectedInterpolatorFunction = @"
|
||
|
PackedInput PackInput (Input input)
|
||
|
{
|
||
|
PackedInput output;
|
||
|
ZERO_INITIALIZE(PackedInput, output);
|
||
|
output.packed_myFloat3_A_myFloat3_Dx.xyz = input.myFloat3_A;
|
||
|
output.packed_myFloat3_A_myFloat3_Dx.w = input.myFloat3_D.x;
|
||
|
output.packed_myFloat3_B_myFloat3_Dy.xyz = input.myFloat3_B;
|
||
|
output.packed_myFloat3_B_myFloat3_Dy.w = input.myFloat3_D.y;
|
||
|
output.packed_myFloat3_C_myFloat3_Dz.xyz = input.myFloat3_C;
|
||
|
output.packed_myFloat3_C_myFloat3_Dz.w = input.myFloat3_D.z;
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
Input UnpackInput (PackedInput input)
|
||
|
{
|
||
|
Input output;
|
||
|
output.myFloat3_A = input.packed_myFloat3_A_myFloat3_Dx.xyz;
|
||
|
output.myFloat3_D.x = input.packed_myFloat3_A_myFloat3_Dx.w;
|
||
|
output.myFloat3_B = input.packed_myFloat3_B_myFloat3_Dy.xyz;
|
||
|
output.myFloat3_D.y = input.packed_myFloat3_B_myFloat3_Dy.w;
|
||
|
output.myFloat3_C = input.packed_myFloat3_C_myFloat3_Dz.xyz;
|
||
|
output.myFloat3_D.z = input.packed_myFloat3_C_myFloat3_Dz.w;
|
||
|
return output;
|
||
|
}
|
||
|
"
|
||
|
},
|
||
|
|
||
|
new PackingTestCase()
|
||
|
{
|
||
|
name = "Typical_Use_Case",
|
||
|
inputStruct = new StructDescriptor()
|
||
|
{
|
||
|
name = "Input",
|
||
|
packFields = false,
|
||
|
fields = new []
|
||
|
{
|
||
|
new FieldDescriptor(tag: "Original", name: "position", define: string.Empty, type: ShaderValueType.Float4, semantic: "SV_POSITION", interpolation: "SV_POSITION_QUALIFIERS"),
|
||
|
new FieldDescriptor(tag: "Original", name: "myIntA", define: string.Empty, type: ShaderValueType.Integer, semantic: string.Empty, interpolation: "nointerpolation"),
|
||
|
new FieldDescriptor(tag: "Original", name: "myIntB", define: string.Empty, type: ShaderValueType.Integer, semantic: string.Empty, interpolation: "nointerpolation"),
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloatA", define: string.Empty, type: ShaderValueType.Float, semantic: string.Empty, interpolation: "nointerpolation"),
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloatB", define: string.Empty, type: ShaderValueType.Float2, semantic: string.Empty, interpolation: "nointerpolation"),
|
||
|
new FieldDescriptor(tag: "Original", name: "myInterFloatA", define: string.Empty, type: ShaderValueType.Float, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myInterFloatB", define: string.Empty, type: ShaderValueType.Float2, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "instanceID", define: string.Empty, type: ShaderValueType.Uint, semantic: "CUSTOM_INSTANCE_ID", preprocessor: "UNITY_ANY_INSTANCING_ENABLED")
|
||
|
}
|
||
|
},
|
||
|
expectedOutputStruct = new StructDescriptor()
|
||
|
{
|
||
|
name = "PackedInput",
|
||
|
packFields = true,
|
||
|
fields = new[]
|
||
|
{
|
||
|
new FieldDescriptor(tag: "Original", name: "position", define: string.Empty, type: ShaderValueType.Float4, semantic: "SV_POSITION", interpolation: "SV_POSITION_QUALIFIERS"),
|
||
|
new FieldDescriptor(tag: "PackedInput", name: "myIntA", define: string.Empty, type: ShaderValueType.Integer, semantic: "INTERP0", interpolation: "nointerpolation"),
|
||
|
new FieldDescriptor(tag: "PackedInput", name: "myIntB", define: string.Empty, type: ShaderValueType.Integer, semantic: "INTERP1", interpolation: "nointerpolation"),
|
||
|
new FieldDescriptor(tag: "PackedInput", name: "packed_myFloatB_myFloatA", define: string.Empty, type: "float3", semantic: "INTERP2", interpolation: "nointerpolation"),
|
||
|
new FieldDescriptor(tag: "PackedInput", name: "packed_myInterFloatB_myInterFloatA", define: string.Empty, type: "float3", semantic: "INTERP3", interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "instanceID", define: string.Empty, type: ShaderValueType.Uint, semantic: "CUSTOM_INSTANCE_ID", preprocessor: "UNITY_ANY_INSTANCING_ENABLED")
|
||
|
}
|
||
|
},
|
||
|
|
||
|
expectedInterpolatorFunction = @"
|
||
|
PackedInput PackInput (Input input)
|
||
|
{
|
||
|
PackedInput output;
|
||
|
ZERO_INITIALIZE(PackedInput, output);
|
||
|
output.position = input.position;
|
||
|
output.myIntA = input.myIntA;
|
||
|
output.myIntB = input.myIntB;
|
||
|
output.packed_myFloatB_myFloatA.xy = input.myFloatB;
|
||
|
output.packed_myFloatB_myFloatA.z = input.myFloatA;
|
||
|
output.packed_myInterFloatB_myInterFloatA.xy = input.myInterFloatB;
|
||
|
output.packed_myInterFloatB_myInterFloatA.z = input.myInterFloatA;
|
||
|
#if UNITY_ANY_INSTANCING_ENABLED
|
||
|
output.instanceID = input.instanceID;
|
||
|
#endif
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
Input UnpackInput (PackedInput input)
|
||
|
{
|
||
|
Input output;
|
||
|
output.position = input.position;
|
||
|
output.myIntA = input.myIntA;
|
||
|
output.myIntB = input.myIntB;
|
||
|
output.myFloatB = input.packed_myFloatB_myFloatA.xy;
|
||
|
output.myFloatA = input.packed_myFloatB_myFloatA.z;
|
||
|
output.myInterFloatB = input.packed_myInterFloatB_myInterFloatA.xy;
|
||
|
output.myInterFloatA = input.packed_myInterFloatB_myInterFloatA.z;
|
||
|
#if UNITY_ANY_INSTANCING_ENABLED
|
||
|
output.instanceID = input.instanceID;
|
||
|
#endif
|
||
|
return output;
|
||
|
}"
|
||
|
}
|
||
|
};
|
||
|
|
||
|
[Test]
|
||
|
public void GenerationUtils_GeneratePackedStruct([ValueSource(nameof(s_PackingTestCase))] PackingTestCase testCase)
|
||
|
{
|
||
|
var activeFields = new ActiveFields();
|
||
|
foreach (var field in testCase.inputStruct.fields)
|
||
|
activeFields.all.AddAll(field);
|
||
|
|
||
|
GenerationUtils.GeneratePackedStruct(testCase.inputStruct, activeFields, out var packedStruct);
|
||
|
|
||
|
var expected = testCase.expectedOutputStruct;
|
||
|
Assert.AreEqual(expected.name, packedStruct.name);
|
||
|
Assert.AreEqual(expected.packFields, packedStruct.packFields);
|
||
|
Assert.AreEqual(expected.fields.Length, packedStruct.fields.Length);
|
||
|
for (int i = 0; i < expected.fields.Length; i++)
|
||
|
{
|
||
|
var currentField = packedStruct.fields[i];
|
||
|
var expectedField = expected.fields[i];
|
||
|
Assert.AreEqual(expectedField.tag, currentField.tag);
|
||
|
Assert.AreEqual(expectedField.name, currentField.name);
|
||
|
Assert.AreEqual(expectedField.define, currentField.define);
|
||
|
Assert.AreEqual(expectedField.interpolation, currentField.interpolation);
|
||
|
Assert.AreEqual(expectedField.type, currentField.type);
|
||
|
Assert.AreEqual(expectedField.vectorCount, currentField.vectorCount);
|
||
|
Assert.AreEqual(expectedField.semantic, currentField.semantic);
|
||
|
Assert.AreEqual(expectedField.preprocessor, currentField.preprocessor);
|
||
|
Assert.AreEqual(expectedField.subscriptOptions, currentField.subscriptOptions);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void GenerationUtils_GenerateInterpolatorFunctions([ValueSource(nameof(s_PackingTestCase))] PackingTestCase packingTestCase)
|
||
|
{
|
||
|
var activeFields = new ActiveFields();
|
||
|
foreach (var field in packingTestCase.inputStruct.fields)
|
||
|
activeFields.all.AddAll(field);
|
||
|
|
||
|
GenerationUtils.GenerateInterpolatorFunctions(packingTestCase.inputStruct, activeFields.baseInstance, true, out var shaderFunction);
|
||
|
|
||
|
var result = shaderFunction.ToString();
|
||
|
int length = Math.Max(packingTestCase.expectedInterpolatorFunction.Length, result.Length);
|
||
|
var compare = String.Compare(packingTestCase.expectedInterpolatorFunction, 0, result, 0, length, CultureInfo.InvariantCulture, CompareOptions.IgnoreSymbols);
|
||
|
Assert.AreEqual(0, compare, "Unexpected generated function:\n" + result);
|
||
|
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void GenerationUtils_ActivationFields()
|
||
|
{
|
||
|
var inputStruct = new StructDescriptor()
|
||
|
{
|
||
|
name = "Input",
|
||
|
packFields = false,
|
||
|
fields = new[]
|
||
|
{
|
||
|
//This test insure the order of input packing doesn't fail the following packing
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloat_Cond_0", define: string.Empty, type: ShaderValueType.Float, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloat_Cond_1", define: string.Empty, type: ShaderValueType.Float, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloat_Cond_2", define: string.Empty, type: ShaderValueType.Float, semantic: string.Empty, interpolation: string.Empty),
|
||
|
new FieldDescriptor(tag: "Original", name: "myFloat_Cond_ALL", define: string.Empty, type: ShaderValueType.Float, semantic: string.Empty, interpolation: string.Empty),
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var activeFields = new ActiveFields();
|
||
|
foreach (var field in inputStruct.fields)
|
||
|
{
|
||
|
var index = field.name.EndsWith("0") ? 0 : field.name.EndsWith("1") ? 1 : field.name.EndsWith("2") ? 2 : -1;
|
||
|
if (index != -1)
|
||
|
{
|
||
|
activeFields[index].Add(field);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
activeFields.all.AddAll(field);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GenerationUtils.GenerateInterpolatorFunctions(inputStruct, activeFields.baseInstance, true, out var shaderFunctionBuilder);
|
||
|
GenerationUtils.GeneratePackedStruct(inputStruct, activeFields, out var packedStruct);
|
||
|
GenerationUtils.GenerateShaderStruct(packedStruct, activeFields, true, out var structDeclarationBuilder);
|
||
|
|
||
|
var expectedShaderFunction = @"
|
||
|
PackedInput PackInput (Input input)
|
||
|
{
|
||
|
PackedInput output;
|
||
|
ZERO_INITIALIZE(PackedInput, output);
|
||
|
output.packed_myFloat_Cond_0_myFloat_Cond_1_myFloat_Cond_2_myFloat_Cond_ALL.x = input.myFloat_Cond_0;
|
||
|
output.packed_myFloat_Cond_0_myFloat_Cond_1_myFloat_Cond_2_myFloat_Cond_ALL.y = input.myFloat_Cond_1;
|
||
|
output.packed_myFloat_Cond_0_myFloat_Cond_1_myFloat_Cond_2_myFloat_Cond_ALL.z = input.myFloat_Cond_2;
|
||
|
output.packed_myFloat_Cond_0_myFloat_Cond_1_myFloat_Cond_2_myFloat_Cond_ALL.w = input.myFloat_Cond_ALL;
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
Input UnpackInput (PackedInput input)
|
||
|
{
|
||
|
Input output;
|
||
|
output.myFloat_Cond_0 = input.packed_myFloat_Cond_0_myFloat_Cond_1_myFloat_Cond_2_myFloat_Cond_ALL.x;
|
||
|
output.myFloat_Cond_1 = input.packed_myFloat_Cond_0_myFloat_Cond_1_myFloat_Cond_2_myFloat_Cond_ALL.y;
|
||
|
output.myFloat_Cond_2 = input.packed_myFloat_Cond_0_myFloat_Cond_1_myFloat_Cond_2_myFloat_Cond_ALL.z;
|
||
|
output.myFloat_Cond_ALL = input.packed_myFloat_Cond_0_myFloat_Cond_1_myFloat_Cond_2_myFloat_Cond_ALL.w;
|
||
|
return output;
|
||
|
}";
|
||
|
|
||
|
var expectedStructDeclaration = @"
|
||
|
struct PackedInput
|
||
|
{
|
||
|
#if defined(KEYWORD_PERMUTATION_0) || defined(KEYWORD_PERMUTATION_1) || defined(KEYWORD_PERMUTATION_2)
|
||
|
float4 packed_myFloat_Cond_0_myFloat_Cond_1_myFloat_Cond_2_myFloat_Cond_ALL : INTERP0;
|
||
|
#endif
|
||
|
};";
|
||
|
{
|
||
|
var shaderFunction = shaderFunctionBuilder.ToString();
|
||
|
var length = Math.Max(expectedShaderFunction.Length, shaderFunction.Length);
|
||
|
var compare = String.Compare(expectedShaderFunction, 0, shaderFunction, 0, length, CultureInfo.InvariantCulture, CompareOptions.IgnoreSymbols);
|
||
|
Assert.AreEqual(0, compare, "Unexpected generated function:\n" + shaderFunction);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var structDeclaration = structDeclarationBuilder.ToString();
|
||
|
var length = Math.Max(expectedStructDeclaration.Length, structDeclaration.Length);
|
||
|
var compare = String.Compare(expectedStructDeclaration, 0, expectedStructDeclaration, 0, length, CultureInfo.InvariantCulture, CompareOptions.IgnoreSymbols);
|
||
|
Assert.AreEqual(0, compare, "Unexpected generated function:\n" + structDeclaration);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|