Set correct uniform size for Metal.

There are two fixes here. The first pads out the uniform buffer to
match the alignment of the largest element in the buffer, as required
by Metal. The second ensures that the RTHeight uniform is only added
to the shader if required by the program inputs, as otherwise it wasn't
being included in the uniform data.

Bug: skia:8737
Change-Id: Icbe57456f3ec79faada231278c58137ca9865dd2
Reviewed-on: https://skia-review.googlesource.com/c/189875
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/mtl/GrMtlPipelineStateBuilder.mm b/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
index 5168358..93ba094 100644
--- a/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
+++ b/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
@@ -304,6 +304,16 @@
     return mtlColorAttachment;
 }
 
+uint32_t buffer_size(uint32_t offset, uint32_t maxAlignment) {
+    // Metal expects the buffer to be padded at the end according to the alignment
+    // of the largest element in the buffer.
+    uint32_t offsetDiff = offset & maxAlignment;
+    if (offsetDiff != 0) {
+        offsetDiff = maxAlignment - offsetDiff + 1;
+    }
+    return offset + offsetDiff;
+}
+
 GrMtlPipelineState* GrMtlPipelineStateBuilder::finalize(const GrPrimitiveProcessor& primProc,
                                                         const GrPipeline& pipeline,
                                                         GrProgramDesc* desc) {
@@ -359,17 +369,21 @@
                  [[error localizedDescription] cStringUsingEncoding: NSASCIIStringEncoding]);
         return nullptr;
     }
+    uint32_t geomBufferSize = buffer_size(fUniformHandler.fCurrentGeometryUBOOffset,
+                                          fUniformHandler.fCurrentGeometryUBOMaxAlignment);
+    uint32_t fragBufferSize = buffer_size(fUniformHandler.fCurrentFragmentUBOOffset,
+                                          fUniformHandler.fCurrentFragmentUBOMaxAlignment);
     return new GrMtlPipelineState(fGpu,
                                   pipelineState,
                                   pipelineDescriptor.colorAttachments[0].pixelFormat,
                                   fUniformHandles,
                                   fUniformHandler.fUniforms,
                                   GrMtlBuffer::Make(fGpu,
-                                                    fUniformHandler.fCurrentGeometryUBOOffset,
+                                                    geomBufferSize,
                                                     GrGpuBufferType::kVertex,
                                                     kStatic_GrAccessPattern),
                                   GrMtlBuffer::Make(fGpu,
-                                                    fUniformHandler.fCurrentFragmentUBOOffset,
+                                                    fragBufferSize,
                                                     GrGpuBufferType::kVertex,
                                                     kStatic_GrAccessPattern),
                                   (uint32_t)fUniformHandler.numSamplers(),
diff --git a/src/gpu/mtl/GrMtlUniformHandler.h b/src/gpu/mtl/GrMtlUniformHandler.h
index 226b4a4..6e60cb0 100644
--- a/src/gpu/mtl/GrMtlUniformHandler.h
+++ b/src/gpu/mtl/GrMtlUniformHandler.h
@@ -48,7 +48,9 @@
         , fUniforms(kUniformsPerBlock)
         , fSamplers(kUniformsPerBlock)
         , fCurrentGeometryUBOOffset(0)
-        , fCurrentFragmentUBOOffset(0) {
+        , fCurrentGeometryUBOMaxAlignment(0x0)
+        , fCurrentFragmentUBOOffset(0)
+        , fCurrentFragmentUBOMaxAlignment(0x0) {
     }
 
     UniformHandle internalAddUniformArray(uint32_t visibility,
@@ -80,18 +82,18 @@
     bool hasGeometryUniforms() const { return fCurrentGeometryUBOOffset > 0; }
     bool hasFragmentUniforms() const { return fCurrentFragmentUBOOffset > 0; }
 
-
     const UniformInfo& getUniformInfo(UniformHandle u) const {
         return fUniforms[u.toIndex()];
     }
 
-
     UniformInfoArray    fUniforms;
     UniformInfoArray    fSamplers;
     SkTArray<GrSwizzle> fSamplerSwizzles;
 
     uint32_t            fCurrentGeometryUBOOffset;
+    uint32_t            fCurrentGeometryUBOMaxAlignment;
     uint32_t            fCurrentFragmentUBOOffset;
+    uint32_t            fCurrentFragmentUBOMaxAlignment;
 
     friend class GrMtlPipelineStateBuilder;
 
diff --git a/src/gpu/mtl/GrMtlUniformHandler.mm b/src/gpu/mtl/GrMtlUniformHandler.mm
index 0d27eab..da51e65 100644
--- a/src/gpu/mtl/GrMtlUniformHandler.mm
+++ b/src/gpu/mtl/GrMtlUniformHandler.mm
@@ -174,9 +174,13 @@
 // the new uniform, and currentOffset is updated to be the offset to the end of the new uniform.
 static void get_ubo_aligned_offset(uint32_t* uniformOffset,
                                    uint32_t* currentOffset,
+                                   uint32_t* maxAlignment,
                                    GrSLType type,
                                    int arrayCount) {
     uint32_t alignmentMask = grsltype_to_alignment_mask(type);
+    if (alignmentMask > *maxAlignment) {
+        *maxAlignment = alignmentMask;
+    }
     uint32_t offsetDiff = *currentOffset & alignmentMask;
     if (offsetDiff != 0) {
         offsetDiff = alignmentMask - offsetDiff + 1;
@@ -231,14 +235,17 @@
     uni.fVariable.setTypeModifier(GrShaderVar::kNone_TypeModifier);
 
     uint32_t* currentOffset;
+    uint32_t* maxAlignment;
     uint32_t geomStages = kVertex_GrShaderFlag | kGeometry_GrShaderFlag;
     if (geomStages & visibility) {
         currentOffset = &fCurrentGeometryUBOOffset;
+        maxAlignment = &fCurrentGeometryUBOMaxAlignment;
     } else {
         SkASSERT(kFragment_GrShaderFlag == visibility);
         currentOffset = &fCurrentFragmentUBOOffset;
+        maxAlignment = &fCurrentFragmentUBOMaxAlignment;
     }
-    get_ubo_aligned_offset(&uni.fUBOffset, currentOffset, type, arrayCount);
+    get_ubo_aligned_offset(&uni.fUBOffset, currentOffset, maxAlignment, type, arrayCount);
 
     SkString layoutQualifier;
     layoutQualifier.appendf("offset=%d", uni.fUBOffset);
diff --git a/src/sksl/SkSLMetalCodeGenerator.cpp b/src/sksl/SkSLMetalCodeGenerator.cpp
index 13c171a..9b88143 100644
--- a/src/sksl/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/SkSLMetalCodeGenerator.cpp
@@ -687,7 +687,7 @@
                 this->write(")]]");
             }
         }
-        if (fProgram.fKind == Program::kFragment_Kind) {
+        if (fProgram.fInputs.fRTHeight) {
             if (fInterfaceBlockNameMap.empty()) {
             // FIXME - Possibly have a different way of passing in u_skRTHeight or flip y axis
             // in a different way altogether.
@@ -844,7 +844,7 @@
     }
     fIndentation++;
     writeFields(structType->fields(), structType->fOffset, &intf);
-    if (fProgram.fKind == Program::kFragment_Kind) {
+    if (fProgram.fInputs.fRTHeight) {
         this->writeLine("float u_skRTHeight;");
     }
     fIndentation--;
@@ -1261,7 +1261,7 @@
             wroteInterfaceBlock = true;
         }
     }
-    if (!wroteInterfaceBlock && (fProgram.fKind == Program::kFragment_Kind)) {
+    if (!wroteInterfaceBlock && fProgram.fInputs.fRTHeight) {
         // FIXME - Possibly have a different way of passing in u_skRTHeight or flip y axis
         // in a different way altogether.
         this->writeLine("struct sksl_synthetic_uniforms {");
diff --git a/tests/SkSLMetalTest.cpp b/tests/SkSLMetalTest.cpp
index 38611ac..d565b7b 100644
--- a/tests/SkSLMetalTest.cpp
+++ b/tests/SkSLMetalTest.cpp
@@ -52,10 +52,7 @@
          "struct Outputs {\n"
          "    float4 sk_FragColor [[color(0)]];\n"
          "};\n"
-         "struct sksl_synthetic_uniforms {\n"
-         "    float u_skRTHeight;\n"
-         "};\n"
-         "fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {\n"
+         "fragment Outputs fragmentMain(Inputs _in [[stage_in]]) {\n"
          "    Outputs _outputStruct;\n"
          "    thread Outputs* _out = &_outputStruct;\n"
          "    _out->sk_FragColor = float4(0.75);\n"
@@ -89,9 +86,6 @@
          "struct Outputs {\n"
          "    float4 sk_FragColor [[color(0)]];\n"
          "};\n"
-         "struct sksl_synthetic_uniforms {\n"
-         "    float u_skRTHeight;\n"
-         "};\n"
          "float2x2 float2x2_from_float(float x) {\n"
          "    return float2x2(float2(x, 0), float2(0, x));\n"
          "}\n"
@@ -107,7 +101,7 @@
          "float4x4 float4x4_from_float(float x) {\n"
          "    return float4x4(float4(x, 0, 0, 0), float4(0, x, 0, 0), float4(0, 0, x, 0), float4(0, 0, 0, x));\n"
          "}\n"
-         "fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {\n"
+         "fragment Outputs fragmentMain(Inputs _in [[stage_in]]) {\n"
          "    Outputs _outputStruct;\n"
          "    thread Outputs* _out = &_outputStruct;\n"
          "    float2x2 m5 = float2x2_from_float(float2x2_from_float4(float4(1.0, 2.0, 3.0, 4.0))[0][0]);\n"