Flatten uniform arrays
authorsteve-lunarg <steve_gh@khasekhemwy.net>
Fri, 16 Sep 2016 19:26:37 +0000 (13:26 -0600)
committersteve-lunarg <steve_gh@khasekhemwy.net>
Thu, 22 Sep 2016 14:47:48 +0000 (08:47 -0600)
This checkin adds a --flatten-uniform-arrays option which can break
uniform arrays of samplers, textures, or UBOs up into individual
scalars named (e.g) myarray[0], myarray[1], etc.  These appear as
individual linkage objects.

Code notes:

- shouldFlatten internally calls shouldFlattenIO, and shouldFlattenUniform,
  but is the only flattening query directly called.

- flattenVariable will handle structs or arrays (but not yet arrayed structs;
  this is tested an an error is generated).

- There's some error checking around unhandled situations.  E.g, flattening
  uniform arrays with initializer lists is not implemented.

- This piggybacks on as much of the existing mechanism for struct flattening
  as it can.  E.g, it uses the same flattenMap, and the same
  flattenAccess() method.

- handleAssign() has been generalized to cope with either structs or arrays.

- Extended test infrastructure to test flattening ability.

StandAlone/StandAlone.cpp
Test/baseResults/hlsl.array.flatten.frag.out [new file with mode: 0644]
Test/hlsl.array.flatten.frag [new file with mode: 0644]
glslang/MachineIndependent/Intermediate.cpp
glslang/MachineIndependent/ShaderLang.cpp
glslang/MachineIndependent/localintermediate.h
glslang/Public/ShaderLang.h
gtests/Hlsl.FromFile.cpp
gtests/TestFixture.h
hlsl/hlslParseHelper.cpp
hlsl/hlslParseHelper.h

index 063e416..30af4fe 100644 (file)
@@ -81,6 +81,7 @@ enum TOptions {
     EOptionReadHlsl           = (1 << 17),
     EOptionCascadingErrors    = (1 << 18),
     EOptionAutoMapBindings    = (1 << 19),
+    EOptionFlattenUniformArrays = (1 << 20),
 };
 
 //
@@ -285,6 +286,10 @@ void ProcessArguments(int argc, char* argv[])
                                lowerword == "auto-map-binding"  || 
                                lowerword == "amb") {
                         Options |= EOptionAutoMapBindings;
+                    } else if (lowerword == "flatten-uniform-arrays" || // synonyms
+                               lowerword == "flatten-uniform-array"  ||
+                               lowerword == "fua") {
+                        Options |= EOptionFlattenUniformArrays;
                     } else {
                         usage();
                     }
@@ -407,6 +412,10 @@ void ProcessArguments(int argc, char* argv[])
     // -o or -x makes no sense if there is no target binary
     if (binaryFileName && (Options & EOptionSpv) == 0)
         Error("no binary generation requested (e.g., -V)");
+
+    if ((Options & EOptionFlattenUniformArrays) != 0 &&
+        (Options & EOptionReadHlsl) == 0)
+        Error("uniform array flattening only valid when compiling HLSL source.");
 }
 
 //
@@ -532,6 +541,7 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
         shader->setShiftSamplerBinding(baseSamplerBinding[compUnit.stage]);
         shader->setShiftTextureBinding(baseTextureBinding[compUnit.stage]);
         shader->setShiftUboBinding(baseUboBinding[compUnit.stage]);
+        shader->setFlattenUniformArrays((Options & EOptionFlattenUniformArrays) != 0);
 
         if (Options & EOptionAutoMapBindings)
             shader->setAutoMapBindings(true);
@@ -930,6 +940,9 @@ void usage()
            "  --auto-map-bindings                     automatically bind uniform variables without\n"
            "                                          explicit bindings.\n"
            "  --amb                                   synonym for --auto-map-bindings\n"
+           "\n"
+           "  --flatten-uniform-arrays                flatten uniform array references to scalars\n"
+           "  --fua                                   synonym for --flatten-uniform-arrays\n"
            );
 
     exit(EFailUsage);
diff --git a/Test/baseResults/hlsl.array.flatten.frag.out b/Test/baseResults/hlsl.array.flatten.frag.out
new file mode 100644 (file)
index 0000000..a375225
--- /dev/null
@@ -0,0 +1,588 @@
+hlsl.array.flatten.frag
+Shader version: 450
+gl_FragCoord origin is upper left
+0:? Sequence
+0:17  Function Definition: TestFn1( (global 4-component vector of float)
+0:17    Function Parameters: 
+0:?     Sequence
+0:18      Branch: Return with expression
+0:18        texture (global 4-component vector of float)
+0:18          Construct combined texture-sampler (temp sampler1D)
+0:?             'g_tex[1]' (temp texture1D)
+0:?             'g_samp[1]' (temp sampler)
+0:18          Constant:
+0:18            0.200000
+0:22  Function Definition: TestFn2(t11[3];p1[3]; (global 4-component vector of float)
+0:22    Function Parameters: 
+0:22      'l_tex' (in 3-element array of texture1D)
+0:22      'l_samp' (in 3-element array of sampler)
+0:?     Sequence
+0:23      Branch: Return with expression
+0:23        texture (global 4-component vector of float)
+0:23          Construct combined texture-sampler (temp sampler1D)
+0:23            direct index (temp texture1D)
+0:23              'l_tex' (in 3-element array of texture1D)
+0:23              Constant:
+0:23                2 (const int)
+0:23            direct index (temp sampler)
+0:23              'l_samp' (in 3-element array of sampler)
+0:23              Constant:
+0:23                2 (const int)
+0:23          Constant:
+0:23            0.200000
+0:26  Sequence
+0:26    move second child to first child (temp 5-element array of int)
+0:26      'not_flattened_a' (global 5-element array of int)
+0:26      Constant:
+0:26        1 (const int)
+0:26        2 (const int)
+0:26        3 (const int)
+0:26        4 (const int)
+0:26        5 (const int)
+0:31  Function Definition: main(struct-PS_OUTPUT-vf41; (global void)
+0:31    Function Parameters: 
+0:31      'ps_output' (out structure{temp 4-component vector of float color})
+0:?     Sequence
+0:33      Sequence
+0:?         Sequence
+0:33          move second child to first child (temp sampler)
+0:33            direct index (temp sampler)
+0:33              'local_sampler_array' (temp 3-element array of sampler)
+0:33              Constant:
+0:33                0 (const int)
+0:?             'g_samp[0]' (uniform sampler)
+0:33          move second child to first child (temp sampler)
+0:33            direct index (temp sampler)
+0:33              'local_sampler_array' (temp 3-element array of sampler)
+0:33              Constant:
+0:33                1 (const int)
+0:?             'g_samp[1]' (uniform sampler)
+0:33          move second child to first child (temp sampler)
+0:33            direct index (temp sampler)
+0:33              'local_sampler_array' (temp 3-element array of sampler)
+0:33              Constant:
+0:33                2 (const int)
+0:?             'g_samp[2]' (uniform sampler)
+0:34      Sequence
+0:?         Sequence
+0:34          move second child to first child (temp texture1D)
+0:34            direct index (temp texture1D)
+0:34              'local_texture_array' (temp 3-element array of texture1D)
+0:34              Constant:
+0:34                0 (const int)
+0:?             'g_tex[0]' (uniform texture1D)
+0:34          move second child to first child (temp texture1D)
+0:34            direct index (temp texture1D)
+0:34              'local_texture_array' (temp 3-element array of texture1D)
+0:34              Constant:
+0:34                1 (const int)
+0:?             'g_tex[1]' (uniform texture1D)
+0:34          move second child to first child (temp texture1D)
+0:34            direct index (temp texture1D)
+0:34              'local_texture_array' (temp 3-element array of texture1D)
+0:34              Constant:
+0:34                2 (const int)
+0:?             'g_tex[2]' (uniform texture1D)
+0:35      Sequence
+0:?         Sequence
+0:35          move second child to first child (temp float)
+0:35            direct index (temp float)
+0:35              'local_float_array' (temp 4-element array of float)
+0:35              Constant:
+0:35                0 (const int)
+0:?             'g_floats[0]' (uniform float)
+0:35          move second child to first child (temp float)
+0:35            direct index (temp float)
+0:35              'local_float_array' (temp 4-element array of float)
+0:35              Constant:
+0:35                1 (const int)
+0:?             'g_floats[1]' (uniform float)
+0:35          move second child to first child (temp float)
+0:35            direct index (temp float)
+0:35              'local_float_array' (temp 4-element array of float)
+0:35              Constant:
+0:35                2 (const int)
+0:?             'g_floats[2]' (uniform float)
+0:35          move second child to first child (temp float)
+0:35            direct index (temp float)
+0:35              'local_float_array' (temp 4-element array of float)
+0:35              Constant:
+0:35                3 (const int)
+0:?             'g_floats[3]' (uniform float)
+0:37      move second child to first child (temp 4-component vector of float)
+0:?         'color' (layout(location=0 ) out 4-component vector of float)
+0:37        add (temp 4-component vector of float)
+0:37          Function Call: TestFn1( (global 4-component vector of float)
+0:37          Function Call: TestFn2(t11[3];p1[3]; (global 4-component vector of float)
+0:?             Comma (temp 3-element array of texture1D)
+0:?               Sequence
+0:?                 move second child to first child (temp texture1D)
+0:?                   direct index (temp texture1D)
+0:?                     'aggShadow' (temp 3-element array of texture1D)
+0:?                     Constant:
+0:?                       0 (const int)
+0:?                   'g_tex[0]' (uniform texture1D)
+0:?                 move second child to first child (temp texture1D)
+0:?                   direct index (temp texture1D)
+0:?                     'aggShadow' (temp 3-element array of texture1D)
+0:?                     Constant:
+0:?                       1 (const int)
+0:?                   'g_tex[1]' (uniform texture1D)
+0:?                 move second child to first child (temp texture1D)
+0:?                   direct index (temp texture1D)
+0:?                     'aggShadow' (temp 3-element array of texture1D)
+0:?                     Constant:
+0:?                       2 (const int)
+0:?                   'g_tex[2]' (uniform texture1D)
+0:?               'aggShadow' (temp 3-element array of texture1D)
+0:?             Comma (temp 3-element array of sampler)
+0:?               Sequence
+0:?                 move second child to first child (temp sampler)
+0:?                   direct index (temp sampler)
+0:?                     'aggShadow' (temp 3-element array of sampler)
+0:?                     Constant:
+0:?                       0 (const int)
+0:?                   'g_samp[0]' (uniform sampler)
+0:?                 move second child to first child (temp sampler)
+0:?                   direct index (temp sampler)
+0:?                     'aggShadow' (temp 3-element array of sampler)
+0:?                     Constant:
+0:?                       1 (const int)
+0:?                   'g_samp[1]' (uniform sampler)
+0:?                 move second child to first child (temp sampler)
+0:?                   direct index (temp sampler)
+0:?                     'aggShadow' (temp 3-element array of sampler)
+0:?                     Constant:
+0:?                       2 (const int)
+0:?                   'g_samp[2]' (uniform sampler)
+0:?               'aggShadow' (temp 3-element array of sampler)
+0:?   Linker Objects
+0:?     'g_tex[0]' (uniform texture1D)
+0:?     'g_tex[1]' (uniform texture1D)
+0:?     'g_tex[2]' (uniform texture1D)
+0:?     'g_tex_explicit[0]' (layout(binding=1 ) uniform texture1D)
+0:?     'g_tex_explicit[1]' (layout(binding=2 ) uniform texture1D)
+0:?     'g_tex_explicit[2]' (layout(binding=3 ) uniform texture1D)
+0:?     'g_samp[0]' (uniform sampler)
+0:?     'g_samp[1]' (uniform sampler)
+0:?     'g_samp[2]' (uniform sampler)
+0:?     'g_samp_explicit[0]' (layout(binding=5 ) uniform sampler)
+0:?     'g_samp_explicit[1]' (layout(binding=6 ) uniform sampler)
+0:?     'g_samp_explicit[2]' (layout(binding=7 ) uniform sampler)
+0:?     'g_mats[0]' (uniform 3X3 matrix of float)
+0:?     'g_mats[1]' (uniform 3X3 matrix of float)
+0:?     'g_mats[2]' (uniform 3X3 matrix of float)
+0:?     'g_mats[3]' (uniform 3X3 matrix of float)
+0:?     'g_mats_explicit[0]' (layout(binding=10 ) uniform 3X3 matrix of float)
+0:?     'g_mats_explicit[1]' (layout(binding=11 ) uniform 3X3 matrix of float)
+0:?     'g_mats_explicit[2]' (layout(binding=12 ) uniform 3X3 matrix of float)
+0:?     'g_mats_explicit[3]' (layout(binding=13 ) uniform 3X3 matrix of float)
+0:?     'g_floats[0]' (uniform float)
+0:?     'g_floats[1]' (uniform float)
+0:?     'g_floats[2]' (uniform float)
+0:?     'g_floats[3]' (uniform float)
+0:?     'not_flattened_a' (global 5-element array of int)
+0:?     'color' (layout(location=0 ) out 4-component vector of float)
+
+
+Linked fragment stage:
+
+
+Shader version: 450
+gl_FragCoord origin is upper left
+0:? Sequence
+0:17  Function Definition: TestFn1( (global 4-component vector of float)
+0:17    Function Parameters: 
+0:?     Sequence
+0:18      Branch: Return with expression
+0:18        texture (global 4-component vector of float)
+0:18          Construct combined texture-sampler (temp sampler1D)
+0:?             'g_tex[1]' (temp texture1D)
+0:?             'g_samp[1]' (temp sampler)
+0:18          Constant:
+0:18            0.200000
+0:22  Function Definition: TestFn2(t11[3];p1[3]; (global 4-component vector of float)
+0:22    Function Parameters: 
+0:22      'l_tex' (in 3-element array of texture1D)
+0:22      'l_samp' (in 3-element array of sampler)
+0:?     Sequence
+0:23      Branch: Return with expression
+0:23        texture (global 4-component vector of float)
+0:23          Construct combined texture-sampler (temp sampler1D)
+0:23            direct index (temp texture1D)
+0:23              'l_tex' (in 3-element array of texture1D)
+0:23              Constant:
+0:23                2 (const int)
+0:23            direct index (temp sampler)
+0:23              'l_samp' (in 3-element array of sampler)
+0:23              Constant:
+0:23                2 (const int)
+0:23          Constant:
+0:23            0.200000
+0:26  Sequence
+0:26    move second child to first child (temp 5-element array of int)
+0:26      'not_flattened_a' (global 5-element array of int)
+0:26      Constant:
+0:26        1 (const int)
+0:26        2 (const int)
+0:26        3 (const int)
+0:26        4 (const int)
+0:26        5 (const int)
+0:31  Function Definition: main(struct-PS_OUTPUT-vf41; (global void)
+0:31    Function Parameters: 
+0:31      'ps_output' (out structure{temp 4-component vector of float color})
+0:?     Sequence
+0:33      Sequence
+0:?         Sequence
+0:33          move second child to first child (temp sampler)
+0:33            direct index (temp sampler)
+0:33              'local_sampler_array' (temp 3-element array of sampler)
+0:33              Constant:
+0:33                0 (const int)
+0:?             'g_samp[0]' (uniform sampler)
+0:33          move second child to first child (temp sampler)
+0:33            direct index (temp sampler)
+0:33              'local_sampler_array' (temp 3-element array of sampler)
+0:33              Constant:
+0:33                1 (const int)
+0:?             'g_samp[1]' (uniform sampler)
+0:33          move second child to first child (temp sampler)
+0:33            direct index (temp sampler)
+0:33              'local_sampler_array' (temp 3-element array of sampler)
+0:33              Constant:
+0:33                2 (const int)
+0:?             'g_samp[2]' (uniform sampler)
+0:34      Sequence
+0:?         Sequence
+0:34          move second child to first child (temp texture1D)
+0:34            direct index (temp texture1D)
+0:34              'local_texture_array' (temp 3-element array of texture1D)
+0:34              Constant:
+0:34                0 (const int)
+0:?             'g_tex[0]' (uniform texture1D)
+0:34          move second child to first child (temp texture1D)
+0:34            direct index (temp texture1D)
+0:34              'local_texture_array' (temp 3-element array of texture1D)
+0:34              Constant:
+0:34                1 (const int)
+0:?             'g_tex[1]' (uniform texture1D)
+0:34          move second child to first child (temp texture1D)
+0:34            direct index (temp texture1D)
+0:34              'local_texture_array' (temp 3-element array of texture1D)
+0:34              Constant:
+0:34                2 (const int)
+0:?             'g_tex[2]' (uniform texture1D)
+0:35      Sequence
+0:?         Sequence
+0:35          move second child to first child (temp float)
+0:35            direct index (temp float)
+0:35              'local_float_array' (temp 4-element array of float)
+0:35              Constant:
+0:35                0 (const int)
+0:?             'g_floats[0]' (uniform float)
+0:35          move second child to first child (temp float)
+0:35            direct index (temp float)
+0:35              'local_float_array' (temp 4-element array of float)
+0:35              Constant:
+0:35                1 (const int)
+0:?             'g_floats[1]' (uniform float)
+0:35          move second child to first child (temp float)
+0:35            direct index (temp float)
+0:35              'local_float_array' (temp 4-element array of float)
+0:35              Constant:
+0:35                2 (const int)
+0:?             'g_floats[2]' (uniform float)
+0:35          move second child to first child (temp float)
+0:35            direct index (temp float)
+0:35              'local_float_array' (temp 4-element array of float)
+0:35              Constant:
+0:35                3 (const int)
+0:?             'g_floats[3]' (uniform float)
+0:37      move second child to first child (temp 4-component vector of float)
+0:?         'color' (layout(location=0 ) out 4-component vector of float)
+0:37        add (temp 4-component vector of float)
+0:37          Function Call: TestFn1( (global 4-component vector of float)
+0:37          Function Call: TestFn2(t11[3];p1[3]; (global 4-component vector of float)
+0:?             Comma (temp 3-element array of texture1D)
+0:?               Sequence
+0:?                 move second child to first child (temp texture1D)
+0:?                   direct index (temp texture1D)
+0:?                     'aggShadow' (temp 3-element array of texture1D)
+0:?                     Constant:
+0:?                       0 (const int)
+0:?                   'g_tex[0]' (uniform texture1D)
+0:?                 move second child to first child (temp texture1D)
+0:?                   direct index (temp texture1D)
+0:?                     'aggShadow' (temp 3-element array of texture1D)
+0:?                     Constant:
+0:?                       1 (const int)
+0:?                   'g_tex[1]' (uniform texture1D)
+0:?                 move second child to first child (temp texture1D)
+0:?                   direct index (temp texture1D)
+0:?                     'aggShadow' (temp 3-element array of texture1D)
+0:?                     Constant:
+0:?                       2 (const int)
+0:?                   'g_tex[2]' (uniform texture1D)
+0:?               'aggShadow' (temp 3-element array of texture1D)
+0:?             Comma (temp 3-element array of sampler)
+0:?               Sequence
+0:?                 move second child to first child (temp sampler)
+0:?                   direct index (temp sampler)
+0:?                     'aggShadow' (temp 3-element array of sampler)
+0:?                     Constant:
+0:?                       0 (const int)
+0:?                   'g_samp[0]' (uniform sampler)
+0:?                 move second child to first child (temp sampler)
+0:?                   direct index (temp sampler)
+0:?                     'aggShadow' (temp 3-element array of sampler)
+0:?                     Constant:
+0:?                       1 (const int)
+0:?                   'g_samp[1]' (uniform sampler)
+0:?                 move second child to first child (temp sampler)
+0:?                   direct index (temp sampler)
+0:?                     'aggShadow' (temp 3-element array of sampler)
+0:?                     Constant:
+0:?                       2 (const int)
+0:?                   'g_samp[2]' (uniform sampler)
+0:?               'aggShadow' (temp 3-element array of sampler)
+0:?   Linker Objects
+0:?     'g_tex[0]' (uniform texture1D)
+0:?     'g_tex[1]' (uniform texture1D)
+0:?     'g_tex[2]' (uniform texture1D)
+0:?     'g_tex_explicit[0]' (layout(binding=1 ) uniform texture1D)
+0:?     'g_tex_explicit[1]' (layout(binding=2 ) uniform texture1D)
+0:?     'g_tex_explicit[2]' (layout(binding=3 ) uniform texture1D)
+0:?     'g_samp[0]' (uniform sampler)
+0:?     'g_samp[1]' (uniform sampler)
+0:?     'g_samp[2]' (uniform sampler)
+0:?     'g_samp_explicit[0]' (layout(binding=5 ) uniform sampler)
+0:?     'g_samp_explicit[1]' (layout(binding=6 ) uniform sampler)
+0:?     'g_samp_explicit[2]' (layout(binding=7 ) uniform sampler)
+0:?     'g_mats[0]' (uniform 3X3 matrix of float)
+0:?     'g_mats[1]' (uniform 3X3 matrix of float)
+0:?     'g_mats[2]' (uniform 3X3 matrix of float)
+0:?     'g_mats[3]' (uniform 3X3 matrix of float)
+0:?     'g_mats_explicit[0]' (layout(binding=10 ) uniform 3X3 matrix of float)
+0:?     'g_mats_explicit[1]' (layout(binding=11 ) uniform 3X3 matrix of float)
+0:?     'g_mats_explicit[2]' (layout(binding=12 ) uniform 3X3 matrix of float)
+0:?     'g_mats_explicit[3]' (layout(binding=13 ) uniform 3X3 matrix of float)
+0:?     'g_floats[0]' (uniform float)
+0:?     'g_floats[1]' (uniform float)
+0:?     'g_floats[2]' (uniform float)
+0:?     'g_floats[3]' (uniform float)
+0:?     'not_flattened_a' (global 5-element array of int)
+0:?     'color' (layout(location=0 ) out 4-component vector of float)
+
+// Module Version 10000
+// Generated by (magic number): 80001
+// Id's are bound by 128
+
+                              Capability Shader
+                              Capability Sampled1D
+               1:             ExtInstImport  "GLSL.std.450"
+                              MemoryModel Logical GLSL450
+                              EntryPoint Fragment 4  "main" 93
+                              ExecutionMode 4 OriginUpperLeft
+                              Name 4  "main"
+                              Name 9  "TestFn1("
+                              Name 22  "TestFn2(t11[3];p1[3];"
+                              Name 20  "l_tex"
+                              Name 21  "l_samp"
+                              Name 28  "not_flattened_a"
+                              Name 36  "g_tex[1]"
+                              Name 39  "g_samp[1]"
+                              Name 55  "local_sampler_array"
+                              Name 57  "g_samp[0]"
+                              Name 62  "g_samp[2]"
+                              Name 65  "local_texture_array"
+                              Name 66  "g_tex[0]"
+                              Name 71  "g_tex[2]"
+                              Name 77  "local_float_array"
+                              Name 79  "g_floats[0]"
+                              Name 83  "g_floats[1]"
+                              Name 86  "g_floats[2]"
+                              Name 89  "g_floats[3]"
+                              Name 93  "color"
+                              Name 95  "aggShadow"
+                              Name 102  "aggShadow"
+                              Name 111  "g_tex_explicit[0]"
+                              Name 112  "g_tex_explicit[1]"
+                              Name 113  "g_tex_explicit[2]"
+                              Name 114  "g_samp_explicit[0]"
+                              Name 115  "g_samp_explicit[1]"
+                              Name 116  "g_samp_explicit[2]"
+                              Name 120  "g_mats[0]"
+                              Name 121  "g_mats[1]"
+                              Name 122  "g_mats[2]"
+                              Name 123  "g_mats[3]"
+                              Name 124  "g_mats_explicit[0]"
+                              Name 125  "g_mats_explicit[1]"
+                              Name 126  "g_mats_explicit[2]"
+                              Name 127  "g_mats_explicit[3]"
+                              Decorate 57(g_samp[0]) DescriptorSet 0
+                              Decorate 62(g_samp[2]) DescriptorSet 0
+                              Decorate 66(g_tex[0]) DescriptorSet 0
+                              Decorate 71(g_tex[2]) DescriptorSet 0
+                              Decorate 93(color) Location 0
+                              Decorate 111(g_tex_explicit[0]) DescriptorSet 0
+                              Decorate 111(g_tex_explicit[0]) Binding 1
+                              Decorate 112(g_tex_explicit[1]) DescriptorSet 0
+                              Decorate 112(g_tex_explicit[1]) Binding 2
+                              Decorate 113(g_tex_explicit[2]) DescriptorSet 0
+                              Decorate 113(g_tex_explicit[2]) Binding 3
+                              Decorate 114(g_samp_explicit[0]) DescriptorSet 0
+                              Decorate 114(g_samp_explicit[0]) Binding 5
+                              Decorate 115(g_samp_explicit[1]) DescriptorSet 0
+                              Decorate 115(g_samp_explicit[1]) Binding 6
+                              Decorate 116(g_samp_explicit[2]) DescriptorSet 0
+                              Decorate 116(g_samp_explicit[2]) Binding 7
+                              Decorate 124(g_mats_explicit[0]) Binding 10
+                              Decorate 125(g_mats_explicit[1]) Binding 11
+                              Decorate 126(g_mats_explicit[2]) Binding 12
+                              Decorate 127(g_mats_explicit[3]) Binding 13
+               2:             TypeVoid
+               3:             TypeFunction 2
+               6:             TypeFloat 32
+               7:             TypeVector 6(float) 4
+               8:             TypeFunction 7(fvec4)
+              11:             TypeImage 6(float) 1D sampled format:Unknown
+              12:             TypeInt 32 0
+              13:     12(int) Constant 3
+              14:             TypeArray 11 13
+              15:             TypePointer UniformConstant 14
+              16:             TypeSampler
+              17:             TypeArray 16 13
+              18:             TypePointer UniformConstant 17
+              19:             TypeFunction 7(fvec4) 15(ptr) 18(ptr)
+              24:             TypeInt 32 1
+              25:     12(int) Constant 5
+              26:             TypeArray 24(int) 25
+              27:             TypePointer Private 26
+28(not_flattened_a):     27(ptr) Variable Private
+              29:     24(int) Constant 1
+              30:     24(int) Constant 2
+              31:     24(int) Constant 3
+              32:     24(int) Constant 4
+              33:     24(int) Constant 5
+              34:          26 ConstantComposite 29 30 31 32 33
+              35:             TypePointer UniformConstant 11
+    36(g_tex[1]):     35(ptr) Variable UniformConstant
+              38:             TypePointer UniformConstant 16
+   39(g_samp[1]):     38(ptr) Variable UniformConstant
+              41:             TypeSampledImage 11
+              43:    6(float) Constant 1045220557
+55(local_sampler_array):     18(ptr) Variable UniformConstant
+              56:     24(int) Constant 0
+   57(g_samp[0]):     38(ptr) Variable UniformConstant
+   62(g_samp[2]):     38(ptr) Variable UniformConstant
+65(local_texture_array):     15(ptr) Variable UniformConstant
+    66(g_tex[0]):     35(ptr) Variable UniformConstant
+    71(g_tex[2]):     35(ptr) Variable UniformConstant
+              74:     12(int) Constant 4
+              75:             TypeArray 6(float) 74
+              76:             TypePointer Function 75
+              78:             TypePointer UniformConstant 6(float)
+ 79(g_floats[0]):     78(ptr) Variable UniformConstant
+              81:             TypePointer Function 6(float)
+ 83(g_floats[1]):     78(ptr) Variable UniformConstant
+ 86(g_floats[2]):     78(ptr) Variable UniformConstant
+ 89(g_floats[3]):     78(ptr) Variable UniformConstant
+              92:             TypePointer Output 7(fvec4)
+       93(color):     92(ptr) Variable Output
+   95(aggShadow):     15(ptr) Variable UniformConstant
+  102(aggShadow):     18(ptr) Variable UniformConstant
+111(g_tex_explicit[0]):     35(ptr) Variable UniformConstant
+112(g_tex_explicit[1]):     35(ptr) Variable UniformConstant
+113(g_tex_explicit[2]):     35(ptr) Variable UniformConstant
+114(g_samp_explicit[0]):     38(ptr) Variable UniformConstant
+115(g_samp_explicit[1]):     38(ptr) Variable UniformConstant
+116(g_samp_explicit[2]):     38(ptr) Variable UniformConstant
+             117:             TypeVector 6(float) 3
+             118:             TypeMatrix 117(fvec3) 3
+             119:             TypePointer UniformConstant 118
+  120(g_mats[0]):    119(ptr) Variable UniformConstant
+  121(g_mats[1]):    119(ptr) Variable UniformConstant
+  122(g_mats[2]):    119(ptr) Variable UniformConstant
+  123(g_mats[3]):    119(ptr) Variable UniformConstant
+124(g_mats_explicit[0]):    119(ptr) Variable UniformConstant
+125(g_mats_explicit[1]):    119(ptr) Variable UniformConstant
+126(g_mats_explicit[2]):    119(ptr) Variable UniformConstant
+127(g_mats_explicit[3]):    119(ptr) Variable UniformConstant
+         4(main):           2 Function None 3
+               5:             Label
+77(local_float_array):     76(ptr) Variable Function
+                              Store 28(not_flattened_a) 34
+              58:          16 Load 57(g_samp[0])
+              59:     38(ptr) AccessChain 55(local_sampler_array) 56
+                              Store 59 58
+              60:          16 Load 39(g_samp[1])
+              61:     38(ptr) AccessChain 55(local_sampler_array) 29
+                              Store 61 60
+              63:          16 Load 62(g_samp[2])
+              64:     38(ptr) AccessChain 55(local_sampler_array) 30
+                              Store 64 63
+              67:          11 Load 66(g_tex[0])
+              68:     35(ptr) AccessChain 65(local_texture_array) 56
+                              Store 68 67
+              69:          11 Load 36(g_tex[1])
+              70:     35(ptr) AccessChain 65(local_texture_array) 29
+                              Store 70 69
+              72:          11 Load 71(g_tex[2])
+              73:     35(ptr) AccessChain 65(local_texture_array) 30
+                              Store 73 72
+              80:    6(float) Load 79(g_floats[0])
+              82:     81(ptr) AccessChain 77(local_float_array) 56
+                              Store 82 80
+              84:    6(float) Load 83(g_floats[1])
+              85:     81(ptr) AccessChain 77(local_float_array) 29
+                              Store 85 84
+              87:    6(float) Load 86(g_floats[2])
+              88:     81(ptr) AccessChain 77(local_float_array) 30
+                              Store 88 87
+              90:    6(float) Load 89(g_floats[3])
+              91:     81(ptr) AccessChain 77(local_float_array) 31
+                              Store 91 90
+              94:    7(fvec4) FunctionCall 9(TestFn1()
+              96:          11 Load 66(g_tex[0])
+              97:     35(ptr) AccessChain 95(aggShadow) 56
+                              Store 97 96
+              98:          11 Load 36(g_tex[1])
+              99:     35(ptr) AccessChain 95(aggShadow) 29
+                              Store 99 98
+             100:          11 Load 71(g_tex[2])
+             101:     35(ptr) AccessChain 95(aggShadow) 30
+                              Store 101 100
+             103:          16 Load 57(g_samp[0])
+             104:     38(ptr) AccessChain 102(aggShadow) 56
+                              Store 104 103
+             105:          16 Load 39(g_samp[1])
+             106:     38(ptr) AccessChain 102(aggShadow) 29
+                              Store 106 105
+             107:          16 Load 62(g_samp[2])
+             108:     38(ptr) AccessChain 102(aggShadow) 30
+                              Store 108 107
+             109:    7(fvec4) FunctionCall 22(TestFn2(t11[3];p1[3];) 95(aggShadow) 102(aggShadow)
+             110:    7(fvec4) FAdd 94 109
+                              Store 93(color) 110
+                              Return
+                              FunctionEnd
+     9(TestFn1():    7(fvec4) Function None 8
+              10:             Label
+              37:          11 Load 36(g_tex[1])
+              40:          16 Load 39(g_samp[1])
+              42:          41 SampledImage 37 40
+              44:    7(fvec4) ImageSampleImplicitLod 42 43
+                              ReturnValue 44
+                              FunctionEnd
+22(TestFn2(t11[3];p1[3];):    7(fvec4) Function None 19
+       20(l_tex):     15(ptr) FunctionParameter
+      21(l_samp):     18(ptr) FunctionParameter
+              23:             Label
+              47:     35(ptr) AccessChain 20(l_tex) 30
+              48:          11 Load 47
+              49:     38(ptr) AccessChain 21(l_samp) 30
+              50:          16 Load 49
+              51:          41 SampledImage 48 50
+              52:    7(fvec4) ImageSampleImplicitLod 51 43
+                              ReturnValue 52
+                              FunctionEnd
diff --git a/Test/hlsl.array.flatten.frag b/Test/hlsl.array.flatten.frag
new file mode 100644 (file)
index 0000000..b243bec
--- /dev/null
@@ -0,0 +1,38 @@
+
+// uniform Texture1D g_tex3[3][2];  // TODO: legal in HLSL, but we don't handle it yet.
+
+uniform Texture1D g_tex[3];
+uniform Texture1D g_tex_explicit[3] : register(t1);
+
+SamplerState g_samp[3];
+SamplerState g_samp_explicit[3] : register(s5);
+
+uniform float3x3 g_mats[4];
+uniform float3x3 g_mats_explicit[4] : register(b10);
+uniform float g_floats[4];
+
+// uniform float g_floats[4] = { 10, 11, 12, 13 };  // TODO: ... add when initializer lists can be flattened.
+
+float4 TestFn1()
+{
+    return g_tex[1].Sample(g_samp[1], 0.2);
+}
+
+float4 TestFn2(Texture1D l_tex[3], SamplerState l_samp[3])
+{
+    return l_tex[2].Sample(l_samp[2], 0.2);
+}
+
+int not_flattened_a[5] = { 1, 2, 3, 4, 5 };
+
+struct PS_OUTPUT { float4 color : SV_Target0; };
+
+void main(out PS_OUTPUT ps_output)
+{
+    // test flattening for local assignment initialization
+    SamplerState local_sampler_array[3] = g_samp;
+    Texture1D local_texture_array[3]    = g_tex;
+    float local_float_array[4]          = g_floats;
+
+    ps_output.color = TestFn1() + TestFn2(g_tex, g_samp);
+}
index 677cef3..a0bee74 100644 (file)
@@ -424,6 +424,11 @@ TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TInt
         // opaque types can be passed to functions
         if (op == EOpFunction)
             break;
+
+        // HLSL can assign samplers directly (no constructor)
+        if (source == EShSourceHlsl && node->getBasicType() == EbtSampler)
+            break;
+
         // samplers can get assigned via a sampler constructor
         // (well, not yet, but code in the rest of this function is ready for it)
         if (node->getBasicType() == EbtSampler && op == EOpAssign && 
index dc0c01b..ffd6b00 100644 (file)
@@ -1493,6 +1493,8 @@ void TShader::setShiftSamplerBinding(unsigned int base) { intermediate->setShift
 void TShader::setShiftTextureBinding(unsigned int base) { intermediate->setShiftTextureBinding(base); }
 void TShader::setShiftUboBinding(unsigned int base)     { intermediate->setShiftUboBinding(base); }
 void TShader::setAutoMapBindings(bool map)              { intermediate->setAutoMapBindings(map); }
+void TShader::setFlattenUniformArrays(bool flatten)     { intermediate->setFlattenUniformArrays(flatten); }
+
 //
 // Turn the shader strings into a parse tree in the TIntermediate.
 //
index 4de811c..14b8a00 100644 (file)
@@ -145,7 +145,8 @@ public:
         shiftSamplerBinding(0),
         shiftTextureBinding(0),
         shiftUboBinding(0),
-        autoMapBindings(false)
+        autoMapBindings(false),
+        flattenUniformArrays(false)
     {
         localSize[0] = 1;
         localSize[1] = 1;
@@ -176,6 +177,8 @@ public:
     unsigned int getShiftUboBinding()     const { return shiftUboBinding; }
     void setAutoMapBindings(bool map)               { autoMapBindings = map; }
     bool getAutoMapBindings()             const { return autoMapBindings; }
+    void setFlattenUniformArrays(bool flatten)      { flattenUniformArrays = flatten; }
+    bool getFlattenUniformArrays()        const { return flattenUniformArrays; }
 
     void setVersion(int v) { version = v; }
     int getVersion() const { return version; }
@@ -385,6 +388,7 @@ protected:
     unsigned int shiftTextureBinding;
     unsigned int shiftUboBinding;
     bool autoMapBindings;
+    bool flattenUniformArrays;
 
     EProfile profile;
     int version;
index 80605ba..496e6d4 100644 (file)
@@ -304,6 +304,7 @@ public:
     void setShiftTextureBinding(unsigned int base);
     void setShiftUboBinding(unsigned int base);
     void setAutoMapBindings(bool map);
+    void setFlattenUniformArrays(bool flatten);
 
     // Interface to #include handlers.
     //
index 01cc032..55450a7 100644 (file)
@@ -58,6 +58,7 @@ std::string FileNameAsCustomTestSuffix(
 }
 
 using HlslCompileTest = GlslangTest<::testing::TestWithParam<FileNameEntryPointPair>>;
+using HlslCompileAndFlattenTest = GlslangTest<::testing::TestWithParam<FileNameEntryPointPair>>;
 
 // Compiling HLSL to SPIR-V under Vulkan semantics. Expected to successfully
 // generate both AST and SPIR-V.
@@ -68,6 +69,13 @@ TEST_P(HlslCompileTest, FromFile)
                             Target::BothASTAndSpv, GetParam().entryPoint);
 }
 
+TEST_P(HlslCompileAndFlattenTest, FromFile)
+{
+    loadFileCompileFlattenUniformsAndCheck(GLSLANG_TEST_DIRECTORY, GetParam().fileName,
+                                           Source::HLSL, Semantics::Vulkan,
+                                           Target::BothASTAndSpv, GetParam().entryPoint);
+}
+
 // clang-format off
 INSTANTIATE_TEST_CASE_P(
     ToSpirv, HlslCompileTest,
@@ -181,5 +189,15 @@ INSTANTIATE_TEST_CASE_P(
 );
 // clang-format on
 
+// clang-format off
+INSTANTIATE_TEST_CASE_P(
+    ToSpirv, HlslCompileAndFlattenTest,
+    ::testing::ValuesIn(std::vector<FileNameEntryPointPair>{
+        {"hlsl.array.flatten.frag", "main"},
+    }),
+    FileNameAsCustomTestSuffix
+);
+
+// clang-format on
 }  // anonymous namespace
 }  // namespace glslangtest
index 47e2703..4b364c4 100644 (file)
@@ -204,11 +204,14 @@ public:
     // the result and returns disassembly text.
     GlslangResult compileAndLink(
             const std::string shaderName, const std::string& code,
-            const std::string& entryPointName, EShMessages controls)
+            const std::string& entryPointName, EShMessages controls,
+            bool flattenUniformArrays = false)
     {
         const EShLanguage kind = GetShaderStage(GetSuffix(shaderName));
 
         glslang::TShader shader(kind);
+        shader.setFlattenUniformArrays(flattenUniformArrays);
+
         bool success = compile(&shader, code, entryPointName, controls);
 
         glslang::TProgram program;
@@ -395,6 +398,32 @@ public:
                                     expectedOutputFname);
     }
 
+    void loadFileCompileFlattenUniformsAndCheck(const std::string& testDir,
+                                                const std::string& testName,
+                                                Source source,
+                                                Semantics semantics,
+                                                Target target,
+                                                const std::string& entryPointName="")
+    {
+        const std::string inputFname = testDir + "/" + testName;
+        const std::string expectedOutputFname =
+            testDir + "/baseResults/" + testName + ".out";
+        std::string input, expectedOutput;
+
+        tryLoadFile(inputFname, "input", &input);
+        tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
+
+        const EShMessages controls = DeriveOptions(source, semantics, target);
+        GlslangResult result = compileAndLink(testName, input, entryPointName, controls, true);
+
+        // Generate the hybrid output in the way of glslangValidator.
+        std::ostringstream stream;
+        outputResultToStream(&stream, result, controls);
+
+        checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
+                                    expectedOutputFname);
+    }
+
     void loadFileCompileIoMapAndCheck(const std::string& testDir,
                                       const std::string& testName,
                                       Source source,
index 5af7904..d5db6b2 100755 (executable)
@@ -403,12 +403,19 @@ TIntermTyped* HlslParseContext::handleBracketDereference(const TSourceLoc& loc,
         if (base->getAsSymbolNode() && isIoResizeArray(base->getType()))
             handleIoResizeArrayAccess(loc, base);
 
-        if (index->getQualifier().storage == EvqConst) {
-            if (base->getType().isImplicitlySizedArray())
-                updateImplicitArraySize(loc, base, indexValue);
-            result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
+        if (base->getAsSymbolNode() && shouldFlatten(base->getType())) {
+            if (index->getQualifier().storage != EvqConst)
+                error(loc, "Invalid variable index to flattened uniform array", base->getAsSymbolNode()->getName().c_str(), "");
+
+            result = flattenAccess(base, indexValue);
         } else {
-            result = intermediate.addIndex(EOpIndexIndirect, base, index, loc);
+            if (index->getQualifier().storage == EvqConst) {
+                if (base->getType().isImplicitlySizedArray())
+                    updateImplicitArraySize(loc, base, indexValue);
+                result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
+            } else {
+                result = intermediate.addIndex(EOpIndexIndirect, base, index, loc);
+            }
         }
     }
 
@@ -701,9 +708,9 @@ TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TInt
     return result;
 }
 
-// Is this an aggregate that can't be passed down the stack?
+// Is this an IO variable that can't be passed down the stack?
 // E.g., pipeline inputs to the vertex stage and outputs from the fragment stage.
-bool HlslParseContext::shouldFlatten(const TType& type) const
+bool HlslParseContext::shouldFlattenIO(const TType& type) const
 {
     if (! inEntryPoint)
         return false;
@@ -715,6 +722,33 @@ bool HlslParseContext::shouldFlatten(const TType& type) const
             qualifier == EvqVaryingOut);
 }
 
+// Is this a uniform array which should be flattened?
+bool HlslParseContext::shouldFlattenUniform(const TType& type) const
+{
+    const TStorageQualifier qualifier = type.getQualifier().storage;
+
+    return type.isArray() &&
+        intermediate.getFlattenUniformArrays() &&
+        qualifier == EvqUniform;
+}
+
+void HlslParseContext::flatten(const TSourceLoc& loc, const TVariable& variable)
+{
+    const TType& type = variable.getType();
+
+    // Presently, flattening of structure arrays is unimplemented.
+    // We handle one, or the other.
+    if (type.isArray() && type.isStruct()) {
+        error(loc, "cannot flatten structure array", variable.getName().c_str(), "");
+    }
+
+    if (type.isStruct())
+        flattenStruct(variable);
+    
+    if (type.isArray())
+        flattenArray(loc, variable);
+}
+
 // Figure out the mapping between an aggregate's top members and an
 // equivalent set of individual variables.
 //
@@ -724,7 +758,7 @@ bool HlslParseContext::shouldFlatten(const TType& type) const
 // Assumes shouldFlatten() or equivalent was called first.
 //
 // TODO: generalize this to arbitrary nesting?
-void HlslParseContext::flatten(const TVariable& variable)
+void HlslParseContext::flattenStruct(const TVariable& variable)
 {
     TVector<TVariable*> memberVariables;
 
@@ -742,8 +776,54 @@ void HlslParseContext::flatten(const TVariable& variable)
     flattenMap[variable.getUniqueId()] = memberVariables;
 }
 
-// Turn an access into aggregate that was flattened to instead be
-// an access to the individual variable the element/member was flattened to.
+// Figure out mapping between an array's members and an
+// equivalent set of individual variables.
+//
+// Assumes shouldFlatten() or equivalent was called first.
+void HlslParseContext::flattenArray(const TSourceLoc& loc, const TVariable& variable)
+{
+    const TType& type = variable.getType();
+    assert(type.isArray());
+
+    if (type.isImplicitlySizedArray())
+        error(loc, "cannot flatten implicitly sized array", variable.getName().c_str(), "");
+
+    if (type.getArraySizes()->getNumDims() != 1)
+        error(loc, "cannot flatten multi-dimensional array", variable.getName().c_str(), "");
+
+    const int size = type.getCumulativeArraySize();
+
+    TVector<TVariable*> memberVariables;
+
+    const TType dereferencedType(type, 0);
+    int binding = type.getQualifier().layoutBinding;
+
+    if (dereferencedType.isStruct() || dereferencedType.isArray()) {
+        error(loc, "cannot flatten array of aggregate types", variable.getName().c_str(), "");
+    }
+
+    for (int element=0; element < size; ++element) {
+        char elementNumBuf[20];  // sufficient for MAXINT
+        snprintf(elementNumBuf, sizeof(elementNumBuf)-1, "[%d]", element);
+        const TString memberName = variable.getName() + elementNumBuf;
+
+        TVariable* memberVariable = makeInternalVariable(memberName.c_str(), dereferencedType);
+        memberVariable->getWritableType().getQualifier() = variable.getType().getQualifier();
+
+        memberVariable->getWritableType().getQualifier().layoutBinding = binding;
+
+        if (binding != TQualifier::layoutBindingEnd)
+            ++binding;
+
+        memberVariables.push_back(memberVariable);
+        intermediate.addSymbolLinkageNode(linkage, *memberVariable);
+    }
+    
+    flattenMap[variable.getUniqueId()] = memberVariables;
+}
+
+// Turn an access into an aggregate that was flattened to instead be
+// an access to the individual variable the member was flattened to.
 // Assumes shouldFlatten() or equivalent was called first.
 TIntermTyped* HlslParseContext::flattenAccess(TIntermTyped* base, int member)
 {
@@ -864,7 +944,7 @@ TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& l
         remapEntryPointIO(function);
         if (entryPointOutput) {
             if (shouldFlatten(entryPointOutput->getType()))
-                flatten(*entryPointOutput);
+                flatten(loc, *entryPointOutput);
             assignLocations(*entryPointOutput);
         }
     } else
@@ -896,7 +976,7 @@ TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& l
                 // get IO straightened out
                 if (inEntryPoint) {
                     if (shouldFlatten(*param.type))
-                        flatten(*variable);
+                        flatten(loc, *variable);
                     assignLocations(*variable);
                 }
 
@@ -1051,40 +1131,68 @@ TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op
                flattenMap.find(node.getAsSymbolNode()->getId()) != flattenMap.end();
     };
 
-    bool flattenLeft = mustFlatten(*left);
-    bool flattenRight = mustFlatten(*right);
+    const bool flattenLeft = mustFlatten(*left);
+    const bool flattenRight = mustFlatten(*right);
     if (! flattenLeft && ! flattenRight)
         return intermediate.addAssign(op, left, right, loc);
 
-    // If we get here, we are assigning to or from a whole struct that must be
-    // flattened, so have to do member-by-member assignment:
-    const auto& members = *left->getType().getStruct();
+    TIntermAggregate* assignList = nullptr;
+    const TVector<TVariable*>* leftVariables = nullptr;
+    const TVector<TVariable*>* rightVariables = nullptr;
+
+    if (flattenLeft)
+        leftVariables = &flattenMap.find(left->getAsSymbolNode()->getId())->second;
+    if (flattenRight)
+        rightVariables = &flattenMap.find(right->getAsSymbolNode()->getId())->second;
+
     const auto getMember = [&](bool flatten, TIntermTyped* node,
-                               const TVector<TVariable*>& memberVariables, int member) {
+                               const TVector<TVariable*>& memberVariables, int member,
+                               TOperator op, const TType& memberType) {
         TIntermTyped* subTree;
         if (flatten)
             subTree = intermediate.addSymbol(*memberVariables[member]);
         else {
-            subTree = intermediate.addIndex(EOpIndexDirectStruct, node,
-                                            intermediate.addConstantUnion(member, loc), loc);
-            subTree->setType(*members[member].type);
+            subTree = intermediate.addIndex(op, node, intermediate.addConstantUnion(member, loc), loc);
+            subTree->setType(memberType);
         }
 
         return subTree;
     };
-    
-    const TVector<TVariable*>* leftVariables = nullptr;
-    const TVector<TVariable*>* rightVariables = nullptr;
-    if (flattenLeft)
-        leftVariables = &flattenMap.find(left->getAsSymbolNode()->getId())->second;
-    if (flattenRight)
-        rightVariables = &flattenMap.find(right->getAsSymbolNode()->getId())->second;
-    TIntermAggregate* assignList = nullptr;
-    for (int member = 0; member < (int)members.size(); ++member) {
-        TIntermTyped* subRight = getMember(flattenRight, right, *rightVariables, member);
-        TIntermTyped* subLeft = getMember(flattenLeft, left, *leftVariables, member);
-        assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, subLeft, subRight, loc));
+
+    // Handle struct assignment
+    if (left->getType().isStruct()) {
+        // If we get here, we are assigning to or from a whole struct that must be
+        // flattened, so have to do member-by-member assignment:
+        const auto& members = *left->getType().getStruct();
+
+        for (int member = 0; member < (int)members.size(); ++member) {
+            TIntermTyped* subRight = getMember(flattenRight, right, *rightVariables, member,
+                                               EOpIndexDirectStruct, *members[member].type);
+            TIntermTyped* subLeft = getMember(flattenLeft, left, *leftVariables, member,
+                                              EOpIndexDirectStruct, *members[member].type);
+            assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, subLeft, subRight, loc));
+        }
+    }
+
+    // Handle array assignment
+    if (left->getType().isArray()) {
+        // If we get here, we are assigning to or from a whole array that must be
+        // flattened, so have to do member-by-member assignment:
+
+        const TType dereferencedType(left->getType(), 0);
+        const int size = left->getType().getCumulativeArraySize();
+        
+        for (int element=0; element < size; ++element) {
+            TIntermTyped* subRight = getMember(flattenRight, right, *rightVariables, element,
+                                               EOpIndexDirect, dereferencedType);
+            TIntermTyped* subLeft = getMember(flattenLeft, left, *leftVariables, element,
+                                              EOpIndexDirect, dereferencedType);
+
+            assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, subLeft, subRight, loc));
+        }
     }
+
+    assert(assignList != nullptr);
     assignList->setOperator(EOpSequence);
 
     return assignList;
@@ -4095,13 +4203,21 @@ TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, TString& i
 
     inheritGlobalDefaults(type.getQualifier());
 
+    bool flattenVar = false;
+
     // Declare the variable
     if (arraySizes || type.isArray()) {
         // Arrayness is potentially coming both from the type and from the 
         // variable: "int[] a[];" or just one or the other.
         // Merge it all to the type, so all arrayness is part of the type.
-        arrayDimMerge(type, arraySizes);
+        arrayDimMerge(type, arraySizes);  // Safe if there are no arraySizes
+
         declareArray(loc, identifier, type, symbol, newDeclaration);
+
+        flattenVar = shouldFlatten(type);
+
+        if (flattenVar)
+            flatten(loc, *symbol->getAsVariable());
     } else {
         // non-array case
         if (! symbol)
@@ -4116,6 +4232,9 @@ TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, TString& i
     // Deal with initializer
     TIntermNode* initNode = nullptr;
     if (symbol && initializer) {
+        if (flattenVar)
+            error(loc, "flattened array with initializer list unsupported", identifier.c_str(), "");
+
         TVariable* variable = symbol->getAsVariable();
         if (! variable) {
             error(loc, "initializer requires a variable, not a member", identifier.c_str(), "");
@@ -4124,9 +4243,12 @@ TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, TString& i
         initNode = executeInitializer(loc, initializer, variable);
     }
 
-    // see if it's a linker-level object to track
-    if (newDeclaration && symbolTable.atGlobalLevel())
-        intermediate.addSymbolLinkageNode(linkage, *symbol);
+    // see if it's a linker-level object to track.  if it's flattened above,
+    // that process added linkage objects for the flattened symbols, we don't
+    // add the aggregate here.
+    if (!flattenVar)
+        if (newDeclaration && symbolTable.atGlobalLevel())
+            intermediate.addSymbolLinkageNode(linkage, *symbol);
 
     return initNode;
 }
@@ -4257,7 +4379,7 @@ TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TInterm
         // normal assigning of a value to a variable...
         specializationCheck(loc, initializer->getType(), "initializer");
         TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
-        TIntermNode* initNode = intermediate.addAssign(EOpAssign, intermSymbol, initializer, loc);
+        TIntermNode* initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer);
         if (! initNode)
             assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
 
index 0162088..b7d3323 100755 (executable)
@@ -83,9 +83,6 @@ public:
     TIntermTyped* handleBinaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right);
     TIntermTyped* handleUnaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* childNode);
     TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field);
-    bool shouldFlatten(const TType&) const;
-    void flatten(const TVariable& variable);
-    TIntermTyped* flattenAccess(TIntermTyped* base, int member);
     void assignLocations(TVariable& variable);
     TFunction& handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype);
     TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&);
@@ -181,6 +178,15 @@ protected:
                        const char* szExtraInfoFormat, TPrefixType prefix,
                        va_list args);
 
+    // Array and struct flattening
+    bool shouldFlatten(const TType& type) const { return shouldFlattenIO(type) || shouldFlattenUniform(type); }
+    TIntermTyped* flattenAccess(TIntermTyped* base, int member);
+    bool shouldFlattenIO(const TType&) const;
+    bool shouldFlattenUniform(const TType&) const;
+    void flatten(const TSourceLoc& loc, const TVariable& variable);
+    void flattenStruct(const TVariable& variable);
+    void flattenArray(const TSourceLoc& loc, const TVariable& variable);
+
     // Current state of parsing
     struct TPragma contextPragma;
     int loopNestingLevel;        // 0 if outside all loops