Преглед изворни кода

Several Compute-related fixes

* GLSL backends now use "std430" layout qualifier instead of "std140"
  for storage buffer blocks. With std140, primitive arrays are now
  tightly-packed, which is not useful.
* GLSL ES does not actually support read-write storage images, EXCEPT
  for ones using the R32f format. Other backends support this, but the
  GLSL ES backend will throw an exception. Unfortunately, there's still
  no way to "exclude" a backend so you can't really use
  RWTexture2D<Vector4> yet. In the future we should add support for
  skippind a backend for a particular shader (or globally).
* There was some missing code in GlslEs300KnownFunctions related to
  RWTexture2D, and the compute test wasn't running for it.
* Bump version to beta2
Eric Mellino пре 7 година
родитељ
комит
3ef8a74d76

+ 4 - 0
src/ShaderGen.Tests/ShaderGen.Tests.csproj

@@ -22,6 +22,10 @@
     </Content>
   </ItemGroup>
 
+  <ItemGroup>
+    <Compile Remove="TestAssets\ComplexCompute.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <None Remove="xunit.runner.json" />
   </ItemGroup>

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

@@ -55,8 +55,14 @@ namespace ShaderGen.Tests
         public static IEnumerable<object[]> ComputeShaders()
         {
             yield return new object[] { "TestShaders.SimpleCompute.CS" };
+            yield return new object[] { "TestShaders.ComplexCompute.CS" };
         }
 
+        private static readonly HashSet<string> s_glslesSkippedShaders = new HashSet<string>()
+        {
+            "TestShaders.ComplexCompute.CS"
+        };
+
         [Theory]
         [MemberData(nameof(ShaderSets))]
         public void HlslEndToEnd(string vsName, string fsName)
@@ -224,6 +230,7 @@ namespace ShaderGen.Tests
             LanguageBackend[] backends = new LanguageBackend[]
             {
                 new HlslBackend(compilation),
+                new GlslEs300Backend(compilation),
                 new Glsl330Backend(compilation),
                 new Glsl450Backend(compilation),
                 new MetalBackend(compilation),
@@ -281,6 +288,15 @@ namespace ShaderGen.Tests
                         }
                         else
                         {
+                            if (backend is GlslEs300Backend)
+                            {
+                                string fullname = set.ComputeFunction.DeclaringType + "." + set.ComputeFunction.Name;
+                                if (s_glslesSkippedShaders.Contains(fullname))
+                                {
+                                    continue;
+                                }
+                            }
+
                             bool is450 = backend is Glsl450Backend;
                             GlsLangValidatorTool.AssertCompilesCode(set.ComputeShaderCode, "comp", is450);
                         }

+ 22 - 0
src/ShaderGen.Tests/TestAssets/ComplexCompute.cs

@@ -0,0 +1,22 @@
+using ShaderGen;
+using System.Numerics;
+using static ShaderGen.ShaderBuiltins;
+
+namespace TestShaders
+{
+    public class ComplexCompute
+    {
+        // Not supported by GLSL ES
+        public RWTexture2DResource<Vector4> RWTex;
+
+        [ComputeShader(1, 1, 1)]
+        public void CS()
+        {
+            Vector4 existing = Load(RWTex, new UInt2(10, 20));
+            Store(
+                RWTex,
+                new UInt2(10, 20),
+                existing + new Vector4(DispatchThreadID.X, DispatchThreadID.Y, DispatchThreadID.Z, 1));
+        }
+    }
+}

+ 3 - 3
src/ShaderGen.Tests/TestAssets/SimpleCompute.cs

@@ -10,7 +10,7 @@ namespace TestShaders
         public RWStructuredBuffer<Vector4> StructuredInOut;
 
         public RWStructuredBuffer<PointLightInfo> RWBufferWithCustomStruct;
-        public RWTexture2DResource<Vector4> RWTex;
+        public RWTexture2DResource<float> RWTex;
 
         [ComputeShader(1, 1, 1)]
         public void CS()
@@ -21,11 +21,11 @@ namespace TestShaders
 
             RWBufferWithCustomStruct[0].Color = new Vector3(1, 2, 3);
 
-            Vector4 existing = Load(RWTex, new UInt2(10, 20));
+            float existing = Load(RWTex, new UInt2(10, 20));
             Store(
                 RWTex,
                 new UInt2(10, 20),
-                existing + new Vector4(DispatchThreadID.Z, DispatchThreadID.Y, DispatchThreadID.X, 1));
+                existing + DispatchThreadID.X);
         }
     }
 }

+ 12 - 2
src/ShaderGen/Glsl/Glsl330Backend.cs

@@ -88,7 +88,7 @@ namespace ShaderGen.Glsl
         protected override void WriteStructuredBuffer(StringBuilder sb, ResourceDefinition rd, bool isReadOnly)
         {
             string readOnlyStr = isReadOnly ? " readonly" : " ";
-            sb.AppendLine($"layout(std140){readOnlyStr} buffer {rd.Name}");
+            sb.AppendLine($"layout(std430){readOnlyStr} buffer {rd.Name}");
             sb.AppendLine("{");
             sb.AppendLine($"    {CSharpToShaderType(rd.ValueType.Name)} field_{CorrectIdentifier(rd.Name.Trim())}[];");
             sb.AppendLine("};");
@@ -96,7 +96,17 @@ namespace ShaderGen.Glsl
 
         protected override void WriteRWTexture2D(StringBuilder sb, ResourceDefinition rd)
         {
-            string layoutType = "rgba32f"; // TODO: Support other types ?
+            string layoutType;
+            switch (rd.ValueType.Name)
+            {
+                case "System.Numerics.Vector4":
+                    layoutType = "rgba32f";
+                    break;
+                case "System.Single":
+                    layoutType = "r32f";
+                    break;
+                default: throw new ShaderGenerationException($"Invalid RWTexture2D type. T must be Vector4 or float.");
+            }
             sb.AppendLine($"layout({layoutType}) uniform image2D {CorrectIdentifier(rd.Name)};");
             sb.AppendLine();
         }

+ 16 - 2
src/ShaderGen/Glsl/Glsl330KnownFunctions.cs

@@ -292,7 +292,14 @@ namespace ShaderGen.Glsl
         {
             if (parameters[0].FullTypeName.Contains("RWTexture2D"))
             {
-                return $"imageLoad({parameters[0].Identifier}, ivec2({parameters[1].Identifier}))";
+                if (parameters[0].FullTypeName.Contains("<float>"))
+                {
+                    return $"imageLoad({parameters[0].Identifier}, ivec2({parameters[1].Identifier})).r";
+                }
+                else
+                {
+                    return $"imageLoad({parameters[0].Identifier}, ivec2({parameters[1].Identifier}))";
+                }
             }
             else
             {
@@ -302,7 +309,14 @@ namespace ShaderGen.Glsl
 
         private static string Store(string typeName, string methodName, InvocationParameterInfo[] parameters)
         {
-            return $"imageStore({parameters[0].Identifier}, ivec2({parameters[1].Identifier}), {parameters[2].Identifier})";
+            if (parameters[0].FullTypeName.Contains("<float>"))
+            {
+                return $"imageStore({parameters[0].Identifier}, ivec2({parameters[1].Identifier}), vec4({parameters[2].Identifier}))";
+            }
+            else
+            {
+                return $"imageStore({parameters[0].Identifier}, ivec2({parameters[1].Identifier}), {parameters[2].Identifier})";
+            }
         }
 
         private static string Discard(string typeName, string methodName, InvocationParameterInfo[] parameters)

+ 12 - 2
src/ShaderGen/Glsl/Glsl450Backend.cs

@@ -36,7 +36,7 @@ namespace ShaderGen.Glsl
 
         protected override void WriteStructuredBuffer(StringBuilder sb, ResourceDefinition rd, bool isReadOnly)
         {
-            string layout = FormatLayoutStr(rd, "std140");
+            string layout = FormatLayoutStr(rd, "std430");
             string readOnlyStr = isReadOnly ? " readonly" : " ";
             sb.AppendLine($"{layout}{readOnlyStr} buffer {rd.Name}");
             sb.AppendLine("{");
@@ -133,7 +133,17 @@ namespace ShaderGen.Glsl
 
         protected override void WriteRWTexture2D(StringBuilder sb, ResourceDefinition rd)
         {
-            string layoutType = "rgba32f"; // TODO: Support other types ?
+            string layoutType;
+            switch (rd.ValueType.Name)
+            {
+                case "System.Numerics.Vector4":
+                    layoutType = "rgba32f";
+                    break;
+                case "System.Single":
+                    layoutType = "r32f";
+                    break;
+                default: throw new ShaderGenerationException($"Invalid RWTexture2D type. T must be Vector4 or float.");
+            }
             sb.Append(FormatLayoutStr(rd, layoutType));
             sb.Append(' ');
             sb.Append("uniform image2D ");

+ 16 - 2
src/ShaderGen/Glsl/Glsl450KnownFunctions.cs

@@ -310,7 +310,14 @@ namespace ShaderGen.Glsl
         {
             if (parameters[0].FullTypeName.Contains("RWTexture2D"))
             {
-                return $"imageLoad({parameters[0].Identifier}, ivec2({parameters[1].Identifier}))";
+                if (parameters[0].FullTypeName.Contains("<float>"))
+                {
+                    return $"imageLoad({parameters[0].Identifier}, ivec2({parameters[1].Identifier})).r";
+                }
+                else
+                {
+                    return $"imageLoad({parameters[0].Identifier}, ivec2({parameters[1].Identifier}))";
+                }
             }
             else if (parameters[0].FullTypeName == "ShaderGen.Texture2DResource")
             {
@@ -324,7 +331,14 @@ namespace ShaderGen.Glsl
 
         private static string Store(string typeName, string methodName, InvocationParameterInfo[] parameters)
         {
-            return $"imageStore({parameters[0].Identifier}, ivec2({parameters[1].Identifier}), {parameters[2].Identifier})";
+            if (parameters[0].FullTypeName.Contains("<float>"))
+            {
+                return $"imageStore({parameters[0].Identifier}, ivec2({parameters[1].Identifier}), vec4({parameters[2].Identifier}))";
+            }
+            else
+            {
+                return $"imageStore({parameters[0].Identifier}, ivec2({parameters[1].Identifier}), {parameters[2].Identifier})";
+            }
         }
 
         private static string Discard(string typeName, string methodName, InvocationParameterInfo[] parameters)

+ 16 - 2
src/ShaderGen/Glsl/GlslEs300Backend.cs

@@ -35,6 +35,10 @@ namespace ShaderGen.Glsl
             {
                 sb.AppendLine($"precision mediump sampler2DMS;");
             }
+            if (function.UsesRWTexture2D)
+            {
+                sb.AppendLine($"precision mediump image2D;");
+            }
             sb.AppendLine();
 
             sb.AppendLine($"struct SamplerDummy {{ int _dummyValue; }};");
@@ -102,7 +106,7 @@ namespace ShaderGen.Glsl
         protected override void WriteStructuredBuffer(StringBuilder sb, ResourceDefinition rd, bool isReadOnly)
         {
             string readOnlyStr = isReadOnly ? " readonly" : " ";
-            sb.AppendLine($"layout(std140){readOnlyStr} buffer {rd.Name}");
+            sb.AppendLine($"layout(std430){readOnlyStr} buffer {rd.Name}");
             sb.AppendLine("{");
             sb.AppendLine($"    {CSharpToShaderType(rd.ValueType.Name)} field_{CorrectIdentifier(rd.Name.Trim())}[];");
             sb.AppendLine("};");
@@ -110,7 +114,17 @@ namespace ShaderGen.Glsl
 
         protected override void WriteRWTexture2D(StringBuilder sb, ResourceDefinition rd)
         {
-            string layoutType = "rgba32f"; // TODO: Support other types ?
+            string layoutType;
+            switch (rd.ValueType.Name)
+            {
+                case "System.Numerics.Vector4":
+                    layoutType = "rgba32f";
+                    break;
+                case "System.Single":
+                    layoutType = "r32f";
+                    break;
+                default: throw new ShaderGenerationException($"Invalid RWTexture2D type. T must be Vector4 or float.");
+            }
             sb.AppendLine($"layout({layoutType}) uniform image2D {CorrectIdentifier(rd.Name)};");
             sb.AppendLine();
         }

+ 37 - 1
src/ShaderGen/Glsl/GlslEs300KnownFunctions.cs

@@ -37,6 +37,7 @@ namespace ShaderGen.Glsl
                 { "SampleGrad", SampleGrad },
                 { "SampleComparisonLevelZero", SampleComparisonLevelZero },
                 { "Load", Load },
+                { "Store", Store },
                 { "Discard", Discard },
                 { "Saturate", Saturate },
                 { nameof(ShaderBuiltins.ClipToTextureCoordinates), ClipToTextureCoordinates },
@@ -145,6 +146,13 @@ namespace ShaderGen.Glsl
             };
             ret.Add("System.Numerics.Vector4", new DictionaryTypeInvocationTranslator(v4Mappings));
 
+            Dictionary<string, InvocationTranslator> u2Mappings = new Dictionary<string, InvocationTranslator>()
+            {
+                { "ctor", VectorCtor },
+            };
+            ret.Add("ShaderGen.UInt2", new DictionaryTypeInvocationTranslator(u2Mappings));
+            ret.Add("ShaderGen.Int2", new DictionaryTypeInvocationTranslator(u2Mappings));
+
             Dictionary<string, InvocationTranslator> m4x4Mappings = new Dictionary<string, InvocationTranslator>()
             {
                 { "ctor", MatrixCtor }
@@ -302,7 +310,33 @@ namespace ShaderGen.Glsl
 
         private static string Load(string typeName, string methodName, InvocationParameterInfo[] parameters)
         {
-            return $"texelFetch({parameters[0].Identifier}, ivec2({parameters[2].Identifier}), {parameters[3].Identifier})";
+            if (parameters[0].FullTypeName.Contains("RWTexture2D"))
+            {
+                if (parameters[0].FullTypeName.Contains("<float>"))
+                {
+                    return $"imageLoad({parameters[0].Identifier}, ivec2({parameters[1].Identifier})).r";
+                }
+                else
+                {
+                    return $"imageLoad({parameters[0].Identifier}, ivec2({parameters[1].Identifier}))";
+                }
+            }
+            else
+            {
+                return $"texelFetch({parameters[0].Identifier}, ivec2({parameters[2].Identifier}), {parameters[3].Identifier})";
+            }
+        }
+
+        private static string Store(string typeName, string methodName, InvocationParameterInfo[] parameters)
+        {
+            if (parameters[0].FullTypeName.Contains("<float>"))
+            {
+                return $"imageStore({parameters[0].Identifier}, ivec2({parameters[1].Identifier}), vec4({parameters[2].Identifier}))";
+            }
+            else
+            {
+                return $"imageStore({parameters[0].Identifier}, ivec2({parameters[1].Identifier}), {parameters[2].Identifier})";
+            }
         }
 
         private static string Discard(string typeName, string methodName, InvocationParameterInfo[] parameters)
@@ -465,6 +499,8 @@ namespace ShaderGen.Glsl
             if (name == "System.Numerics.Vector2") { shaderType = "vec2"; elementCount = 2; }
             else if (name == "System.Numerics.Vector3") { shaderType = "vec3"; elementCount = 3; }
             else if (name == "System.Numerics.Vector4") { shaderType = "vec4"; elementCount = 4; }
+            else if (name == "ShaderGen.Int2") { shaderType = "ivec2"; elementCount = 2; }
+            else if (name == "ShaderGen.UInt2") { shaderType = "uvec2"; elementCount = 2; }
             else { throw new ShaderGenerationException("VectorCtor translator was called on an invalid type: " + name); }
         }
     }

+ 1 - 1
src/ShaderGen/Hlsl/HlslBackend.cs

@@ -158,7 +158,7 @@ namespace ShaderGen.Hlsl
 
             BackendContext setContext = GetContext(setName);
             ShaderFunctionAndMethodDeclarationSyntax entryPoint = setContext.Functions.SingleOrDefault(
-                sfabs => sfabs.Function.Name == function.Name);
+                sfabs => sfabs.Function.DeclaringType == function.DeclaringType && sfabs.Function.Name == function.Name);
             if (entryPoint == null)
             {
                 throw new ShaderGenerationException("Couldn't find given function: " + function.Name);

+ 1 - 0
src/ShaderGen/ShaderFunction.cs

@@ -17,6 +17,7 @@
         public bool UsesFrontFace { get; internal set; }
         public bool UsesTexture2DMS { get; internal set; }
         public bool UsesStructuredBuffer { get; internal set; }
+        public bool UsesRWTexture2D { get; internal set; }
 
         public ShaderFunction(
             string declaringType,

+ 1 - 0
src/ShaderGen/ShaderMethodVisitor.cs

@@ -480,6 +480,7 @@ namespace ShaderGen
                 _resourcesUsed.Add(referencedResource);
                 _shaderFunction.UsesTexture2DMS |= referencedResource.ResourceKind == ShaderResourceKind.Texture2DMS;
                 _shaderFunction.UsesStructuredBuffer |= referencedResource.ResourceKind == ShaderResourceKind.StructuredBuffer;
+                _shaderFunction.UsesRWTexture2D |= referencedResource.ResourceKind == ShaderResourceKind.RWTexture2D;
 
                 return _backend.CorrectFieldAccess(symbolInfo);
             }

+ 1 - 1
version.json

@@ -1,6 +1,6 @@
 {
   "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
-  "version": "1.2.0.0-beta1",
+  "version": "1.2.0.0-beta2",
   "publicReleaseRefSpec": [
     "^refs/tags/v\\d+\\.\\d+" // we also release tags starting with vN.N
   ],