Forráskód Böngészése

Add an explicit "SystemPosition" vertex semantic, and allow multiple fragment shader color outputs.

* Vertex shaders must now emit a field with a SystemPosition semantic
  attribute, explicitly. A PositionSemantic field is no longer implicitly
  considered the system position value.
* A fragment shader may now return a structure with multiple fields.
  This allows multiple color attachments to be written to. This is
  accomplished by attaching the "ColorTargetSemantic" to fields returned
  from a fragment shader function.
  * Fragment shaders can still return a single Vector4, which is
    interpreted as a single color target.
  * Fragment shaders can also return void, as before.
Eric Mellino 8 éve
szülő
commit
fb46362c5d
31 módosított fájl, 182 hozzáadás és 239 törlés
  1. 2 0
      src/ShaderGen.Primitives/SemanticType.cs
  2. 11 1
      src/ShaderGen.Primitives/VertexSemanticAttribute.cs
  3. 1 0
      src/ShaderGen.Tests/ShaderGeneratorTests.cs
  4. 1 1
      src/ShaderGen.Tests/ShaderModelTests.cs
  5. 1 1
      src/ShaderGen.Tests/TestAssets/ComplexExpression.cs
  6. 5 2
      src/ShaderGen.Tests/TestAssets/CustomMethodCalls.cs
  7. 4 2
      src/ShaderGen.Tests/TestAssets/CustomStructResource/CustomStructResource.cs
  8. 1 45
      src/ShaderGen.Tests/TestAssets/MissingInputSemantics.cs
  9. 33 0
      src/ShaderGen.Tests/TestAssets/MultipleColorOutputs.cs
  10. 7 4
      src/ShaderGen.Tests/TestAssets/MultipleResourceSets.cs
  11. 1 1
      src/ShaderGen.Tests/TestAssets/PartialClassShader/PartialVertex1.cs
  12. 4 9
      src/ShaderGen.Tests/TestAssets/PercentOperator.cs
  13. 4 2
      src/ShaderGen.Tests/TestAssets/PointLightInfoStructs.cs
  14. 6 0
      src/ShaderGen.Tests/TestAssets/PositionTexture.cs
  15. 8 2
      src/ShaderGen.Tests/TestAssets/ProcessorTestShaders.cs
  16. 5 2
      src/ShaderGen.Tests/TestAssets/ShaderBuiltinsTestShader.cs
  17. 5 0
      src/ShaderGen.Tests/TestAssets/SimpleVertexStructs.cs
  18. 6 2
      src/ShaderGen.Tests/TestAssets/Swizzles.cs
  19. 1 1
      src/ShaderGen.Tests/TestAssets/TestFragmentShader.cs
  20. 1 45
      src/ShaderGen.Tests/TestAssets/TestVertexShader.cs
  21. 1 1
      src/ShaderGen.Tests/TestAssets/TextureSamplerFragment.cs
  22. 4 2
      src/ShaderGen.Tests/TestAssets/VectorConstructors.cs
  23. 4 2
      src/ShaderGen.Tests/TestAssets/VectorStaticFunctions.cs
  24. 4 2
      src/ShaderGen.Tests/TestAssets/VectorStaticProperties.cs
  25. 1 1
      src/ShaderGen.Tests/TestAssets/VeldridShaders/ForwardMtlCombined.cs
  26. 1 1
      src/ShaderGen.Tests/TestAssets/VeldridShaders/ShadowDepth.cs
  27. 1 1
      src/ShaderGen.Tests/TestAssets/VertexAndFragment.cs
  28. 36 18
      src/ShaderGen/GlslBackendBase.cs
  29. 14 86
      src/ShaderGen/HlslBackend.cs
  30. 0 4
      src/ShaderGen/HlslMethodVisitor.cs
  31. 9 1
      src/ShaderGen/ShaderSyntaxWalker.cs

+ 2 - 0
src/ShaderGen.Primitives/SemanticType.cs

@@ -8,5 +8,7 @@
         TextureCoordinate,
         Color,
         Tangent,
+        SystemPosition,
+        ColorTarget,
     }
 }

+ 11 - 1
src/ShaderGen.Primitives/VertexSemanticAttribute.cs

@@ -12,7 +12,7 @@ namespace ShaderGen
         }
     }
 
-    public class PositionSemanticAttribute: VertexSemanticAttribute
+    public class PositionSemanticAttribute : VertexSemanticAttribute
     {
         public PositionSemanticAttribute() : base(SemanticType.Position) { }
     }
@@ -36,4 +36,14 @@ namespace ShaderGen
     {
         public TangentSemanticAttribute() : base(SemanticType.Tangent) { }
     }
+
+    public class SystemPositionSemanticAttribute : VertexSemanticAttribute
+    {
+        public SystemPositionSemanticAttribute() : base(SemanticType.SystemPosition) { }
+    }
+
+    public class ColorTargetSemanticAttribute : VertexSemanticAttribute
+    {
+        public ColorTargetSemanticAttribute() : base(SemanticType.ColorTarget) { }
+    }
 }

+ 1 - 0
src/ShaderGen.Tests/ShaderGeneratorTests.cs

@@ -28,6 +28,7 @@ namespace ShaderGen.Tests
             yield return new object[] { "TestShaders.VectorStaticProperties.VS", null };
             yield return new object[] { "TestShaders.VectorStaticFunctions.VS", null };
             yield return new object[] { "TestShaders.MultipleResourceSets.VS", null };
+            yield return new object[] { "TestShaders.MultipleColorOutputs.VS", "TestShaders.MultipleColorOutputs.FS" };
         }
 
         [Theory]

+ 1 - 1
src/ShaderGen.Tests/ShaderModelTests.cs

@@ -54,7 +54,7 @@ namespace ShaderGen.Tests
 
             StructureDefinition fsInput = shaderModel.GetStructureDefinition(
                 nameof(TestShaders) + "." + nameof(TestVertexShader) + "+" + nameof(TestVertexShader.VertexOutput));
-            Assert.Equal(SemanticType.Position, fsInput.Fields[0].SemanticType);
+            Assert.Equal(SemanticType.SystemPosition, fsInput.Fields[0].SemanticType);
             Assert.Equal(SemanticType.TextureCoordinate, fsInput.Fields[1].SemanticType);
         }
 

+ 1 - 1
src/ShaderGen.Tests/TestAssets/ComplexExpression.cs

@@ -8,7 +8,7 @@ namespace TestShaders
     {
         public struct FragmentInput
         {
-            [VertexSemantic(SemanticType.Position)]
+            [VertexSemantic(SemanticType.SystemPosition)]
             public Vector4 Position;
             [VertexSemantic(SemanticType.TextureCoordinate)]
             public Vector2 TextureCoordinate;

+ 5 - 2
src/ShaderGen.Tests/TestAssets/CustomMethodCalls.cs

@@ -6,10 +6,13 @@ namespace TestShaders
     public class CustomMethodCalls
     {
         [VertexShader]
-        Position4 VS(Position4 input)
+        SystemPosition4 VS(Position4 input)
         {
             Position4 reversed = Reverse(input);
-            return ShufflePosition4(reversed);
+            Position4 shuffled = ShufflePosition4(reversed);
+            SystemPosition4 output;
+            output.Position = shuffled.Position;
+            return output;
         }
 
         private Position4 Reverse(Position4 vert)

+ 4 - 2
src/ShaderGen.Tests/TestAssets/CustomStructResource/CustomStructResource.cs

@@ -9,14 +9,16 @@ namespace TestShaders
         public Matrix4x4 RegularField;
 
         [VertexShader]
-        public Position4 VS(Position4 input)
+        public SystemPosition4 VS(Position4 input)
         {
             input.Position.X += CustomField.F2_4.X;
             input.Position.Y += CustomField.F1_3;
             input.Position.Z -= CustomField.F2_1.Y;
             input.Position.W -= CustomField.F3_0.Z;
 
-            return input;
+            SystemPosition4 output;
+            output.Position = input.Position;
+            return output;
         }
     }
 }

+ 1 - 45
src/ShaderGen.Tests/TestAssets/MissingInputSemantics.cs

@@ -32,54 +32,10 @@ namespace TestShaders
 
         public struct VertexOutput
         {
-            [VertexSemantic(SemanticType.Position)]
+            [VertexSemantic(SemanticType.SystemPosition)]
             public Vector4 Position;
             [VertexSemantic(SemanticType.TextureCoordinate)]
             public Vector2 TextureCoord;
         }
     }
 }
-
-// Should generate this HLSL:
-
-/*
-
-cbuffer WorldBuffer : register(b0)
-{
-    float4x4 World;
-}
-
-cbuffer ViewMatrixBuffer : register(b1)
-{
-    float4x4 View;
-}
-
-cbuffer ProjectionMatrixBuffer : register(b2)
-{
-    float4x4 Projection;
-}
-
-struct PositionTexture
-{
-    float3 Position : POSITION;
-    float2 TextureCoord : TEXCOORD0
-};
-
-struct PSInput
-{
-    float4 Position : SV_POSITION;
-    float2 TextureCoord: TEXCOORD0;
-};
-
-PSInput VS(VSInput input)
-{
-    PSInput output;
-    float4 worldPosition = mul(view, float4(input.Position, 1));
-    float4 viewPosition = mul(view, worldPosition);
-    output.position = mul(projection, viewPosition);
-    output.worldPosition = worldPosition.xyz;
-
-    return output;
-}
-
-*/

+ 33 - 0
src/ShaderGen.Tests/TestAssets/MultipleColorOutputs.cs

@@ -0,0 +1,33 @@
+using ShaderGen;
+using System.Numerics;
+
+namespace TestShaders
+{
+    public class MultipleColorOutputs
+    {
+        [VertexShader]
+        public SystemPosition4 VS(Position4 input)
+        {
+            SystemPosition4 output;
+            output.Position = input.Position;
+            return output;
+        }
+
+        [FragmentShader]
+        public DualOutput FS(SystemPosition4 input)
+        {
+            DualOutput output;
+            output.FirstOutput = new Vector4(input.Position.X, input.Position.Y, input.Position.Z, 1);
+            output.SecondOutput = new Vector4(input.Position.Z, input.Position.X, input.Position.Y, 1);
+            return output;
+        }
+
+        public struct DualOutput
+        {
+            [ColorTargetSemantic]
+            public Vector4 FirstOutput;
+            [ColorTargetSemantic]
+            public Vector4 SecondOutput;
+        }
+    }
+}

+ 7 - 4
src/ShaderGen.Tests/TestAssets/MultipleResourceSets.cs

@@ -25,16 +25,19 @@ namespace TestShaders
 #pragma warning restore 0649
 
         [VertexShader]
-        public Position4 VS(Position4 input)
+        public SystemPosition4 VS(Position4 input)
         {
-            Position4 output;
+            Vector4 outputPos;
             Matrix4x4 result = NoAttributeMatrix * Matrix0 * Matrix1 * Matrix2 * Matrix3 * Matrix4 * Matrix00;
-            output.Position = Mul(result, input.Position);
+            outputPos = Mul(result, input.Position);
+
+            SystemPosition4 output;
+            output.Position = outputPos;
             return output;
         }
 
         [FragmentShader]
-        public Vector4 FS(Position4 input)
+        public Vector4 FS(SystemPosition4 input)
         {
             return input.Position;
         }

+ 1 - 1
src/ShaderGen.Tests/TestAssets/PartialClassShader/PartialVertex1.cs

@@ -7,7 +7,7 @@ namespace TestShaders
     {
         struct FragmentInput
         {
-            [VertexSemantic(SemanticType.Position)] public Vector4 Position;
+            [VertexSemantic(SemanticType.SystemPosition)] public Vector4 Position;
             [VertexSemantic(SemanticType.Color)] public Vector4 Color;
         }
 

+ 4 - 9
src/ShaderGen.Tests/TestAssets/PercentOperator.cs

@@ -4,19 +4,14 @@ namespace TestShaders
 {
     public class PercentOperator
     {
-        //[VertexShader]
-        //public Position4 PercentVS(Position4 input)
-        //{
-        //    float x = input.Position.X % 10;
-        //    return input;
-        //}
-
         [VertexShader]
-        public Position4 PercentEqualsVS(Position4 input)
+        public SystemPosition4 PercentEqualsVS(Position4 input)
         {
             float x = 5;
             x %= input.Position.Y;
-            return input;
+            SystemPosition4 output;
+            output.Position = input.Position;
+            return output;
         }
     }
 }

+ 4 - 2
src/ShaderGen.Tests/TestAssets/PointLightInfoStructs.cs

@@ -7,9 +7,11 @@ namespace TestShaders
     {
         public PointLightsInfo PointLights;
 
-        [VertexShader] Position4 VS(Position4 input)
+        [VertexShader] SystemPosition4 VS(Position4 input)
         {
-            return input;
+            SystemPosition4 output;
+            output.Position = input.Position;
+            return output;
         }
     }
 

+ 6 - 0
src/ShaderGen.Tests/TestAssets/PositionTexture.cs

@@ -16,4 +16,10 @@ namespace TestShaders
         [PositionSemantic] public Vector4 Position;
         [TextureCoordinateSemantic] public Vector2 TextureCoord;
     }
+
+    public struct SystemPosition4Texture2
+    {
+        [SystemPositionSemantic] public Vector4 Position;
+        [TextureCoordinateSemantic] public Vector2 TextureCoord;
+    }
 }

+ 8 - 2
src/ShaderGen.Tests/TestAssets/ProcessorTestShaders.cs

@@ -19,8 +19,14 @@ namespace TestShaders
 #pragma warning restore 0649
 
         [VertexShader]
-        Position4 VS(Position4 input) { return input; }
+        SystemPosition4 VS(Position4 input)
+        {
+            SystemPosition4 output;
+            output.Position = input.Position;
+            return output;
+        }
+
         [FragmentShader]
-        Vector4 FS(Position4 input) { return input.Position; }
+        Vector4 FS(SystemPosition4 input) { return input.Position; }
     }
 }

+ 5 - 2
src/ShaderGen.Tests/TestAssets/ShaderBuiltinsTestShader.cs

@@ -7,7 +7,7 @@ namespace TestShaders
     public class ShaderBuiltinsTestShader
     {
         [VertexShader]
-        public Position4 VS(Position4 input)
+        public SystemPosition4 VS(Position4 input)
         {
             float f = 0;
             Vector2 v2 = new Vector2(0, 0);
@@ -62,7 +62,10 @@ namespace TestShaders
 
             // ClipToTextureCoordinates
             r2 = ClipToTextureCoordinates(v4);
-            return input;
+
+            SystemPosition4 output;
+            output.Position = input.Position;
+            return output;
         }
     }
 }

+ 5 - 0
src/ShaderGen.Tests/TestAssets/SimpleVertexStructs.cs

@@ -7,4 +7,9 @@ namespace TestShaders
     {
         [PositionSemantic] public Vector4 Position;
     }
+
+    public struct SystemPosition4
+    {
+        [SystemPositionSemantic] public Vector4 Position;
+    }
 }

+ 6 - 2
src/ShaderGen.Tests/TestAssets/Swizzles.cs

@@ -7,12 +7,16 @@ namespace TestShaders
     class Swizzles
     {
         [VertexShader]
-        Position4Texture2 VS(Position4Texture2 input)
+        SystemPosition4Texture2 VS(Position4Texture2 input)
         {
             input.Position = input.Position.WZYX();
             input.Position = input.Position.WWXY();
             input.TextureCoord = input.TextureCoord.YY();
-            return input;
+
+            SystemPosition4Texture2 output;
+            output.Position = input.Position;
+            output.TextureCoord = input.TextureCoord;
+            return output;
         }
     }
 }

+ 1 - 1
src/ShaderGen.Tests/TestAssets/TestFragmentShader.cs

@@ -14,7 +14,7 @@ namespace TestShaders
 
         public struct VertexOutput
         {
-            [VertexSemantic(SemanticType.Position)]
+            [VertexSemantic(SemanticType.SystemPosition)]
             public Vector4 Position;
             [VertexSemantic(SemanticType.Color)]
             public Vector4 Color;

+ 1 - 45
src/ShaderGen.Tests/TestAssets/TestVertexShader.cs

@@ -23,54 +23,10 @@ namespace TestShaders
 
         public struct VertexOutput
         {
-            [VertexSemantic(SemanticType.Position)]
+            [VertexSemantic(SemanticType.SystemPosition)]
             public Vector4 Position;
             [VertexSemantic(SemanticType.TextureCoordinate)]
             public Vector2 TextureCoord;
         }
     }
 }
-
-// Should generate this HLSL:
-
-/*
-
-cbuffer WorldBuffer : register(b0)
-{
-    float4x4 World;
-}
-
-cbuffer ViewMatrixBuffer : register(b1)
-{
-    float4x4 View;
-}
-
-cbuffer ProjectionMatrixBuffer : register(b2)
-{
-    float4x4 Projection;
-}
-
-struct PositionTexture
-{
-    float3 Position : POSITION;
-    float2 TextureCoord : TEXCOORD0
-};
-
-struct PSInput
-{
-    float4 Position : SV_POSITION;
-    float2 TextureCoord: TEXCOORD0;
-};
-
-PSInput VS(VSInput input)
-{
-    PSInput output;
-    float4 worldPosition = mul(view, float4(input.Position, 1));
-    float4 viewPosition = mul(view, worldPosition);
-    output.position = mul(projection, viewPosition);
-    output.worldPosition = worldPosition.xyz;
-
-    return output;
-}
-
-*/

+ 1 - 1
src/ShaderGen.Tests/TestAssets/TextureSamplerFragment.cs

@@ -8,7 +8,7 @@ namespace TestShaders
     {
         public struct FragmentInput
         {
-            [VertexSemantic(SemanticType.Position)]
+            [VertexSemantic(SemanticType.SystemPosition)]
             public Vector4 Position;
             [VertexSemantic(SemanticType.TextureCoordinate)]
             public Vector2 TextureCoordinate;

+ 4 - 2
src/ShaderGen.Tests/TestAssets/VectorConstructors.cs

@@ -6,7 +6,7 @@ namespace TestShaders
     public class VectorConstructors
     {
         [VertexShader]
-        Position4 VS(Position4 input)
+        SystemPosition4 VS(Position4 input)
         {
             Vector2 v2 = new Vector2();
             v2 = new Vector2(1);
@@ -23,7 +23,9 @@ namespace TestShaders
             v4 = new Vector4(v2, 3, 4);
             v4 = new Vector4(1, 2, 3, 4);
 
-            return input;
+            SystemPosition4 output;
+            output.Position = input.Position;
+            return output;
         }
     }
 }

+ 4 - 2
src/ShaderGen.Tests/TestAssets/VectorStaticFunctions.cs

@@ -6,7 +6,7 @@ namespace TestShaders
     public class VectorStaticFunctions
     {
         [VertexShader]
-        Position4 VS(Position4 input)
+        SystemPosition4 VS(Position4 input)
         {
             Vector2 v2 = new Vector2(1, 2);
             Vector2 r2 = Vector2.Abs(v2);
@@ -77,7 +77,9 @@ namespace TestShaders
             r = v4.Length();
             r = v4.LengthSquared();
 
-            return input;
+            SystemPosition4 output;
+            output.Position = input.Position;
+            return output;
         }
     }
 }

+ 4 - 2
src/ShaderGen.Tests/TestAssets/VectorStaticProperties.cs

@@ -6,7 +6,7 @@ namespace TestShaders
     public class VectorStaticProperties
     {
         [VertexShader]
-        Position4 VS(Position4 input)
+        SystemPosition4 VS(Position4 input)
         {
             Vector2 v2 = Vector2.Zero;
             v2 = Vector2.One;
@@ -26,7 +26,9 @@ namespace TestShaders
             v4 = Vector4.UnitZ;
             v4 = Vector4.UnitW;
 
-            return input;
+            SystemPosition4 output;
+            output.Position = input.Position;
+            return output;
         }
     }
 }

+ 1 - 1
src/ShaderGen.Tests/TestAssets/VeldridShaders/ForwardMtlCombined.cs

@@ -68,7 +68,7 @@ namespace TestShaders.VeldridShaders
 
         public struct PixelInput
         {
-            [VertexSemantic(SemanticType.Position)] public Vector4 Position;
+            [VertexSemantic(SemanticType.SystemPosition)] public Vector4 Position;
             [VertexSemantic(SemanticType.Position)] public Vector3 Position_WorldSpace;
             [VertexSemantic(SemanticType.TextureCoordinate)] public Vector4 LightPosition;
             [VertexSemantic(SemanticType.Normal)] public Vector3 Normal;

+ 1 - 1
src/ShaderGen.Tests/TestAssets/VeldridShaders/ShadowDepth.cs

@@ -30,7 +30,7 @@ namespace TestShaders.VeldridShaders
 
         public struct FragmentInput
         {
-            [PositionSemantic]
+            [SystemPositionSemantic]
             public Vector4 Position;
         }
     }

+ 1 - 1
src/ShaderGen.Tests/TestAssets/VertexAndFragment.cs

@@ -27,7 +27,7 @@ namespace TestShaders
 
         public struct FragmentInput
         {
-            [VertexSemantic(SemanticType.Position)]
+            [VertexSemantic(SemanticType.SystemPosition)]
             public Vector4 Position;
         }
     }

+ 36 - 18
src/ShaderGen/GlslBackendBase.cs

@@ -117,9 +117,11 @@ namespace ShaderGen
         {
             ParameterDefinition input = entryFunction.Parameters[0];
             StructureDefinition inputType = GetRequiredStructureType(setName, input.Type);
-            StructureDefinition outputType = entryFunction.Type == ShaderFunctionType.VertexEntryPoint
-                ? GetRequiredStructureType(setName, entryFunction.ReturnType)
-                : null; // Hacky but meh
+            StructureDefinition outputType =
+                entryFunction.ReturnType.Name != "System.Numerics.Vector4"
+                && entryFunction.ReturnType.Name != "System.Void"
+                    ? GetRequiredStructureType(setName, entryFunction.ReturnType)
+                    : null;
 
             // Declare "in" variables
             int inVarIndex = 0;
@@ -128,7 +130,7 @@ namespace ShaderGen
             {
                 if (entryFunction.Type == ShaderFunctionType.FragmentEntryPoint
                     && fragCoordName == null
-                    && field.SemanticType == SemanticType.Position)
+                    && field.SemanticType == SemanticType.SystemPosition)
                 {
                     fragCoordName = field.Name;
                 }
@@ -150,13 +152,11 @@ namespace ShaderGen
             // Declare "out" variables
             if (entryFunction.Type == ShaderFunctionType.VertexEntryPoint)
             {
-                bool skippedFirstPositionSemantic = false;
                 int outVarIndex = 0;
                 foreach (FieldDefinition field in outputType.Fields)
                 {
-                    if (field.SemanticType == SemanticType.Position && !skippedFirstPositionSemantic)
+                    if (field.SemanticType == SemanticType.SystemPosition)
                     {
-                        skippedFirstPositionSemantic = true;
                         continue;
                     }
                     else
@@ -175,15 +175,23 @@ namespace ShaderGen
             else
             {
                 Debug.Assert(entryFunction.Type == ShaderFunctionType.FragmentEntryPoint);
-                if (mappedReturnType != "vec4" && mappedReturnType != "void")
-                {
-                    throw new ShaderGenerationException("Fragment shader must return a System.Numerics.Vector4 value, or no value.");
-                }
 
                 if (mappedReturnType == "vec4")
                 {
                     WriteInOutVariable(sb, false, false, "vec4", "_outputColor_", 0);
                 }
+                else if (mappedReturnType != "void")
+                {
+                    // Composite struct -- declare an out variable for each.
+                    int colorTargetIndex = 0;
+                    foreach (FieldDefinition field in outputType.Fields)
+                    {
+                        Debug.Assert(field.SemanticType == SemanticType.ColorTarget);
+                        Debug.Assert(field.Type.Name == "System.Numerics.Vector4");
+                        int index = colorTargetIndex++;
+                        sb.AppendLine($"    layout(location = {index}) out vec4 _outputColor_{index};");
+                    }
+                }
             }
 
             sb.AppendLine();
@@ -205,7 +213,7 @@ namespace ShaderGen
                 }
                 else
                 {
-                    if (field.SemanticType == SemanticType.Position && !foundSystemPosition)
+                    if (field.SemanticType == SemanticType.SystemPosition && !foundSystemPosition)
                     {
                         Debug.Assert(field.Name == fragCoordName);
                         foundSystemPosition = true;
@@ -232,12 +240,12 @@ namespace ShaderGen
             if (entryFunction.Type == ShaderFunctionType.VertexEntryPoint)
             {
                 inoutIndex = 0;
-                FieldDefinition positionSemanticField = null;
+                FieldDefinition systemPositionField = null;
                 foreach (FieldDefinition field in outputType.Fields)
                 {
-                    if (positionSemanticField == null && field.SemanticType == SemanticType.Position)
+                    if (systemPositionField == null && field.SemanticType == SemanticType.SystemPosition)
                     {
-                        positionSemanticField = field;
+                        systemPositionField = field;
                     }
                     else
                     {
@@ -245,13 +253,13 @@ namespace ShaderGen
                     }
                 }
 
-                if (positionSemanticField == null)
+                if (systemPositionField == null)
                 {
                     // TODO: Should be caught earlier.
-                    throw new ShaderGenerationException("At least one vertex output must have a position semantic.");
+                    throw new ShaderGenerationException("Vertex functions must output a SystemPosition semantic.");
                 }
 
-                sb.AppendLine($"    gl_Position = {CorrectIdentifier("output")}.{CorrectIdentifier(positionSemanticField.Name)};");
+                sb.AppendLine($"    gl_Position = {CorrectIdentifier("output")}.{CorrectIdentifier(systemPositionField.Name)};");
                 EmitGlPositionCorrection(sb);
             }
             else
@@ -261,6 +269,16 @@ namespace ShaderGen
                 {
                     sb.AppendLine($"    _outputColor_ = {CorrectIdentifier("output")};");
                 }
+                else if (mappedReturnType != "void")
+                {
+                    // Composite struct -- assign each field to output
+                    int colorTargetIndex = 0;
+                    foreach (FieldDefinition field in outputType.Fields)
+                    {
+                        Debug.Assert(field.SemanticType == SemanticType.ColorTarget);
+                        sb.AppendLine($"    _outputColor_{colorTargetIndex++} = {CorrectIdentifier("output")}.{CorrectIdentifier(field.Name)};");
+                    }
+                }
             }
             sb.AppendLine("}");
         }

+ 14 - 86
src/ShaderGen/HlslBackend.cs

@@ -11,11 +11,6 @@ namespace ShaderGen
 {
     public class HlslBackend : LanguageBackend
     {
-        private const string FragmentSemanticsSuffix = "__FRAGSEMANTICS";
-
-        private readonly Dictionary<string, List<StructureDefinition>> _synthesizedStructures
-            = new Dictionary<string, List<StructureDefinition>>();
-
         public HlslBackend(Compilation compilation) : base(compilation)
         {
         }
@@ -29,7 +24,6 @@ namespace ShaderGen
 
         protected void WriteStructure(StringBuilder sb, StructureDefinition sd)
         {
-            bool fragmentSemantics = sd.Name.EndsWith(FragmentSemanticsSuffix);
             sb.AppendLine($"struct {CSharpToShaderType(sd.Name)}");
             sb.AppendLine("{");
             HlslSemanticTracker tracker = new HlslSemanticTracker();
@@ -44,7 +38,7 @@ namespace ShaderGen
                 {
                     fb.Append('['); fb.Append(arrayCount); fb.Append(']');
                 }
-                fb.Append(HlslSemantic(field.SemanticType, fragmentSemantics, ref tracker));
+                fb.Append(HlslSemantic(field.SemanticType, ref tracker));
                 fb.Append(';');
                 sb.Append("    ");
                 sb.AppendLine(fb.ToString());
@@ -54,27 +48,13 @@ namespace ShaderGen
             sb.AppendLine();
         }
 
-        private string HlslSemantic(SemanticType semanticType, bool fragmentSemantics, ref HlslSemanticTracker tracker)
+        private string HlslSemantic(SemanticType semanticType, ref HlslSemanticTracker tracker)
         {
             switch (semanticType)
             {
                 case SemanticType.None:
                     return string.Empty;
                 case SemanticType.Position:
-                    if (fragmentSemantics)
-                    {
-                        if (!tracker.emittedSvPosition)
-                        {
-                            tracker.emittedSvPosition = true;
-                            return " : SV_POSITION";
-                        }
-                        else
-                        {
-                            int val = tracker.Position++;
-                            return " : POSITION" + val.ToString();
-                        }
-                    }
-                    else
                     {
                         int val = tracker.Position++;
                         return " : POSITION" + val.ToString();
@@ -99,6 +79,15 @@ namespace ShaderGen
                         int val = tracker.Tangent++;
                         return " : TANGENT" + val.ToString();
                     }
+                case SemanticType.SystemPosition:
+                    {
+                        return " : SV_Position";
+                    }
+                case SemanticType.ColorTarget:
+                    {
+                        int val = tracker.ColorTarget++;
+                        return " : SV_Target" + val.ToString();
+                    }
                 default: throw new ShaderGenerationException("Invalid semantic type: " + semanticType);
             }
         }
@@ -145,36 +134,12 @@ namespace ShaderGen
 
             ValidateRequiredSemantics(setName, entryPoint.Function, function.Type);
 
-            StructureDefinition input = GetRequiredStructureType(setName, entryPoint.Function.Parameters[0].Type);
-
-            if (function.Type == ShaderFunctionType.VertexEntryPoint)
-            {
-                // HLSL vertex outputs needs to have semantics applied to the structure fields.
-                StructureDefinition output = CreateOutputStructure(setName, GetRequiredStructureType(setName, entryPoint.Function.ReturnType));
-                setContext.Functions.Remove(entryPoint);
-                entryPoint = entryPoint.WithReturnType(new TypeReference(output.Name));
-                setContext.Functions.Add(entryPoint);
-            }
-
-            if (function.Type == ShaderFunctionType.FragmentEntryPoint)
-            {
-                // HLSL pixel shader inputs also need these semantics.
-                StructureDefinition modifiedInput = CreateOutputStructure(setName, input);
-                setContext.Functions.Remove(entryPoint);
-                entryPoint = entryPoint.WithParameter(0, new TypeReference(modifiedInput.Name));
-                setContext.Functions.Add(entryPoint);
-            }
-
             StructureDefinition[] orderedStructures
                 = StructureDependencyGraph.GetOrderedStructureList(Compilation, setContext.Structures);
             foreach (StructureDefinition sd in orderedStructures)
             {
                 WriteStructure(sb, sd);
             }
-            foreach (StructureDefinition sd in GetSynthesizedStructures(setName))
-            {
-                WriteStructure(sb, sd);
-            }
 
             FunctionCallGraphDiscoverer fcgd = new FunctionCallGraphDiscoverer(
                 Compilation,
@@ -235,40 +200,15 @@ namespace ShaderGen
             StructureDefinition result = GetContext(setName).Structures.SingleOrDefault(sd => sd.Name == type.Name);
             if (result == null)
             {
-                List<StructureDefinition> synthSDs = GetSynthesizedStructures(setName);
-                result = synthSDs.SingleOrDefault(sd => sd.Name == type.Name);
-                if (result == null)
+                if (!TryDiscoverStructure(setName, type.Name, out result))
                 {
-                    if (!TryDiscoverStructure(setName, type.Name, out result))
-                    {
-                        throw new ShaderGenerationException("Type referred by was not discovered: " + type.Name);
-                    }
+                    throw new ShaderGenerationException("Type referred by was not discovered: " + type.Name);
                 }
             }
 
             return result;
         }
 
-        private StructureDefinition CreateOutputStructure(string setName, StructureDefinition sd)
-        {
-            if (sd.Name.EndsWith(FragmentSemanticsSuffix))
-            {
-                return sd;
-            }
-
-            string newName = sd.Name + FragmentSemanticsSuffix;
-            List<StructureDefinition> synthesizedStructures = GetSynthesizedStructures(setName);
-            StructureDefinition existing = synthesizedStructures.SingleOrDefault(ssd => ssd.Name == newName);
-            if (existing != null)
-            {
-                return existing;
-            }
-
-            StructureDefinition clone = new StructureDefinition(newName, sd.Fields);
-            synthesizedStructures.Add(clone);
-            return clone;
-        }
-
         protected override string FormatInvocationCore(string setName, string type, string method, InvocationParameterInfo[] parameterInfos)
         {
             return HlslKnownFunctions.TranslateInvocation(type, method, parameterInfos);
@@ -286,24 +226,12 @@ namespace ShaderGen
             public int Normal;
             public int Tangent;
             public int Color;
-
-            public bool emittedSvPosition;
+            public int ColorTarget;
         }
 
         internal override string CorrectIdentifier(string identifier)
         {
             return identifier;
         }
-
-        private List<StructureDefinition> GetSynthesizedStructures(string setName)
-        {
-            if (!_synthesizedStructures.TryGetValue(setName, out List<StructureDefinition> ret))
-            {
-                ret = new List<StructureDefinition>();
-                _synthesizedStructures.Add(setName, ret);
-            }
-
-            return ret;
-        }
     }
 }

+ 0 - 4
src/ShaderGen/HlslMethodVisitor.cs

@@ -19,10 +19,6 @@ namespace ShaderGen
                 {
                     suffix = " : SV_Target";
                 }
-                else if (_shaderFunction.ReturnType.Name != "System.Void")
-                {
-                    throw new ShaderGenerationException("Unsupported fragment return type: " + _shaderFunction.ReturnType.Name);
-                }
             }
             string fullDeclType = _backend.CSharpToShaderType(_shaderFunction.DeclaringType);
             string funcName = _shaderFunction.IsEntryPoint

+ 9 - 1
src/ShaderGen/ShaderSyntaxWalker.cs

@@ -149,7 +149,11 @@ namespace ShaderGen
                 throw new ShaderGenerationException("Too many vertex semantics applied to field: " + vds.ToFullString());
             }
 
-            if (CheckSingleAttribute(vds, "PositionSemantic"))
+            if (CheckSingleAttribute(vds, "SystemPositionSemantic"))
+            {
+                return SemanticType.SystemPosition;
+            }
+            else if (CheckSingleAttribute(vds, "PositionSemantic"))
             {
                 return SemanticType.Position;
             }
@@ -169,6 +173,10 @@ namespace ShaderGen
             {
                 return SemanticType.Tangent;
             }
+            else if (CheckSingleAttribute(vds, "ColorTargetSemantic"))
+            {
+                return SemanticType.ColorTarget;
+            }
 
             return SemanticType.None;
         }