Support ESSL structs containing samplers on D3D

Since HLSL can't natively handle samplers in structs, samplers need to
be extracted out of structs into separate variables in the translated
shader code. In HLSL 4.1, samplers that were in structs go into the
normal sampler arrays and are identified by index constants. In other
HLSL versions, samplers that were in structs are translated as uniform
variables.

These transformations are done inside the HLSL output classes, not as
tree transformations. This helps to keep the uniform API provided by
the shader translator intact.

Wherever a struct containing samplers is passed into a user-defined
function, the translated HLSL code passes the separate sampler
variables alongside a struct where the samplers have been removed.

The D3D backend in libANGLE queries the uniform registers of any
samplers that were in uniform structs, and adds them to the register
maps, so that correct sampler state gets assigned to them.

The extracted sampler variables are prefixed with "angle_" instead of
the usual "_" to prevent any name conflicts between them and regular
variables.

BUG=angleproject:504
TEST=angle_end2end_tests,
     dEQP-GLES*.functional.shaders.struct.uniform.* (all pass),
     dEQP-GLES*.functional.uniform_api.* (most now pass)

Change-Id: Ib79cba2fa0ff8257a973d70dfd917a64f0ca1efb
Reviewed-on: https://chromium-review.googlesource.com/333743
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index ac4fad8..b8514ff 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -1347,16 +1347,6 @@
 {
     reset();
 
-    // TODO(jmadill): structures containing samplers
-    for (const gl::LinkedUniform &linkedUniform : mData.getUniforms())
-    {
-        if (linkedUniform.isSampler() && linkedUniform.isField())
-        {
-            infoLog << "Structures containing samplers not currently supported in D3D.";
-            return LinkResult(false, gl::Error(GL_NO_ERROR));
-        }
-    }
-
     const gl::Shader *vertexShader   = mData.getAttachedVertexShader();
     const gl::Shader *fragmentShader = mData.getAttachedFragmentShader();
 
@@ -1820,7 +1810,8 @@
                                    const sh::Uniform &uniform,
                                    D3DUniformMap *uniformMap)
 {
-    if (uniform.isBuiltIn())
+    // Samplers get their registers assigned in assignAllSamplerRegisters.
+    if (uniform.isBuiltIn() || gl::IsSamplerType(uniform.type))
     {
         defineUniform(shader->getType(), uniform, uniform.name, nullptr, uniformMap);
         return;
@@ -1869,7 +1860,17 @@
                 const sh::ShaderVariable &field  = uniform.fields[fieldIndex];
                 const std::string &fieldFullName = (fullName + elementString + "." + field.name);
 
-                defineUniform(shaderType, field, fieldFullName, encoder, uniformMap);
+                // Samplers get their registers assigned in assignAllSamplerRegisters.
+                // Also they couldn't use the same encoder as the rest of the struct, since they are
+                // extracted out of the struct by the shader translator.
+                if (gl::IsSamplerType(field.type))
+                {
+                    defineUniform(shaderType, field, fieldFullName, nullptr, uniformMap);
+                }
+                else
+                {
+                    defineUniform(shaderType, field, fieldFullName, encoder, uniformMap);
+                }
             }
 
             if (encoder)
@@ -2066,7 +2067,7 @@
 
 void ProgramD3D::assignAllSamplerRegisters()
 {
-    for (const D3DUniform *d3dUniform : mD3DUniforms)
+    for (D3DUniform *d3dUniform : mD3DUniforms)
     {
         if (d3dUniform->isSampler())
         {
@@ -2075,20 +2076,23 @@
     }
 }
 
-void ProgramD3D::assignSamplerRegisters(const D3DUniform *d3dUniform)
+void ProgramD3D::assignSamplerRegisters(D3DUniform *d3dUniform)
 {
     ASSERT(d3dUniform->isSampler());
-    ASSERT(d3dUniform->vsRegisterIndex != GL_INVALID_INDEX ||
-           d3dUniform->psRegisterIndex != GL_INVALID_INDEX);
-
-    if (d3dUniform->vsRegisterIndex != GL_INVALID_INDEX)
+    const ShaderD3D *vertexShaderD3D   = GetImplAs<ShaderD3D>(mData.getAttachedVertexShader());
+    const ShaderD3D *fragmentShaderD3D = GetImplAs<ShaderD3D>(mData.getAttachedFragmentShader());
+    ASSERT(vertexShaderD3D->hasUniform(d3dUniform) || fragmentShaderD3D->hasUniform(d3dUniform));
+    if (vertexShaderD3D->hasUniform(d3dUniform))
     {
+        d3dUniform->vsRegisterIndex = vertexShaderD3D->getUniformRegister(d3dUniform->name);
+        ASSERT(d3dUniform->vsRegisterIndex != GL_INVALID_INDEX);
         AssignSamplers(d3dUniform->vsRegisterIndex, d3dUniform->type, d3dUniform->arraySize,
                        mSamplersVS, &mUsedVertexSamplerRange);
     }
-
-    if (d3dUniform->psRegisterIndex != GL_INVALID_INDEX)
+    if (fragmentShaderD3D->hasUniform(d3dUniform))
     {
+        d3dUniform->psRegisterIndex = fragmentShaderD3D->getUniformRegister(d3dUniform->name);
+        ASSERT(d3dUniform->psRegisterIndex != GL_INVALID_INDEX);
         AssignSamplers(d3dUniform->psRegisterIndex, d3dUniform->type, d3dUniform->arraySize,
                        mSamplersPS, &mUsedPixelSamplerRange);
     }