Add a field to uniforms to track which element within a register the uniform should be uploaded to.

This is necessary to support HLSL-backed uniform structs.

TRAC #23750

Signed-off-by: Geoff Lang
Signed-off-by: Nicolas Capens
diff --git a/src/libGLESv2/ProgramBinary.cpp b/src/libGLESv2/ProgramBinary.cpp
index b0fef82..66c94c5 100644
--- a/src/libGLESv2/ProgramBinary.cpp
+++ b/src/libGLESv2/ProgramBinary.cpp
@@ -1725,6 +1725,7 @@
         stream.read(&mUniforms[i]->psRegisterIndex);
         stream.read(&mUniforms[i]->vsRegisterIndex);
         stream.read(&mUniforms[i]->registerCount);
+        stream.read(&mUniforms[i]->registerElement);
     }
 
     stream.read(&size);
@@ -1898,6 +1899,7 @@
         stream.write(uniform.psRegisterIndex);
         stream.write(uniform.vsRegisterIndex);
         stream.write(uniform.registerCount);
+        stream.write(uniform.registerElement);
     }
 
     stream.write(mUniformBlocks.size());
diff --git a/src/libGLESv2/Uniform.cpp b/src/libGLESv2/Uniform.cpp
index 3ee7769..77c6083 100644
--- a/src/libGLESv2/Uniform.cpp
+++ b/src/libGLESv2/Uniform.cpp
@@ -23,13 +23,14 @@
       dirty(true),
       psRegisterIndex(GL_INVALID_INDEX),
       vsRegisterIndex(GL_INVALID_INDEX),
-      registerCount(0)
+      registerCount(0),
+      registerElement(0)
 {
     // We use data storage for default block uniforms to cache values that are sent to D3D during rendering
     // Uniform blocks/buffers are treated separately by the Renderer (ES3 path only)
     if (isInDefaultBlock())
     {
-        size_t bytes = UniformInternalSize(type) * elementCount();
+        size_t bytes = dataSize();
         data = new unsigned char[bytes];
         memset(data, 0, bytes);
         registerCount = VariableRowCount(type) * elementCount();
@@ -66,6 +67,11 @@
     return blockIndex == -1;
 }
 
+size_t Uniform::dataSize() const
+{
+    return UniformInternalSize(type) * elementCount();
+}
+
 UniformBlock::UniformBlock(const std::string &name, unsigned int elementIndex, unsigned int dataSize)
     : name(name),
       elementIndex(elementIndex),
diff --git a/src/libGLESv2/Uniform.h b/src/libGLESv2/Uniform.h
index ca6541a..c90647b 100644
--- a/src/libGLESv2/Uniform.h
+++ b/src/libGLESv2/Uniform.h
@@ -33,6 +33,7 @@
     bool isReferencedByVertexShader() const;
     bool isReferencedByFragmentShader() const;
     bool isInDefaultBlock() const;
+    size_t dataSize() const;
 
     const GLenum type;
     const GLenum precision;
@@ -47,6 +48,10 @@
     unsigned int psRegisterIndex;
     unsigned int vsRegisterIndex;
     unsigned int registerCount;
+
+    // Register "elements" are used for uniform structs in ES3, to appropriately identify single uniforms
+    // inside aggregate types, which are packed according C-like structure rules.
+    unsigned int registerElement;
 };
 
 typedef std::vector<Uniform*> UniformArray;
diff --git a/src/libGLESv2/renderer/Renderer11.cpp b/src/libGLESv2/renderer/Renderer11.cpp
index 3872e71..63110db 100644
--- a/src/libGLESv2/renderer/Renderer11.cpp
+++ b/src/libGLESv2/renderer/Renderer11.cpp
@@ -1442,20 +1442,31 @@
         mapPS = (float(*)[4])map.pData;
     }
 
-    for (gl::UniformArray::iterator uniform_iterator = uniformArray->begin(); uniform_iterator != uniformArray->end(); uniform_iterator++)
+    for (size_t uniformIndex = 0; uniformIndex < uniformArray->size(); uniformIndex++)
     {
-        gl::Uniform *uniform = *uniform_iterator;
+        gl::Uniform *uniform = (*uniformArray)[uniformIndex];
 
         if (!gl::IsSampler(uniform->type))
         {
+            unsigned int componentCount = (4 - uniform->registerElement);
+
+            // we always assume that uniforms from structs are arranged in struct order in our uniforms list. otherwise we would
+            // overwrite previously written regions of memory.
+            if (uniformIndex > 0)
+            {
+                gl::Uniform *previousUniform = (*uniformArray)[uniformIndex-1];
+                ASSERT(!uniform->isReferencedByVertexShader() || previousUniform->vsRegisterIndex != uniform->vsRegisterIndex || uniform->registerElement > previousUniform->registerElement);
+                ASSERT(!uniform->isReferencedByFragmentShader() || previousUniform->psRegisterIndex != uniform->psRegisterIndex || uniform->registerElement > previousUniform->registerElement);
+            }
+
             if (uniform->isReferencedByVertexShader() && mapVS)
             {
-                memcpy(mapVS + uniform->vsRegisterIndex, uniform->data, uniform->registerCount * sizeof(float[4]));
+                memcpy(&mapVS[uniform->vsRegisterIndex][uniform->registerElement], uniform->data, uniform->registerCount * sizeof(float) * componentCount);
             }
 
             if (uniform->isReferencedByFragmentShader() && mapPS)
             {
-                memcpy(mapPS + uniform->psRegisterIndex, uniform->data, uniform->registerCount * sizeof(float[4]));
+                memcpy(&mapPS[uniform->psRegisterIndex][uniform->registerElement], uniform->data, uniform->registerCount * sizeof(float) * componentCount);
             }
         }