Bug 1549646 - Cherry-pick EXT_blend_func_extended into ANGLE. r=upstream
authorJeff Gilbert <jgilbert@mozilla.com>
Mon, 06 May 2019 19:00:17 -0700
changeset 534725 4b84f663a807b7fb7429691f254b566e314e0bfe
parent 534724 ab7db57ad2ac2ebb1818346220f2045968b49415
child 534726 84810c2018def430bc45f54c024d84567e7cee2b
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersupstream
bugs1549646
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1549646 - Cherry-pick EXT_blend_func_extended into ANGLE. r=upstream
gfx/angle/checkout/out/gen/angle/id/commit.h
gfx/angle/checkout/src/compiler/translator/OutputHLSL.cpp
gfx/angle/checkout/src/compiler/translator/OutputHLSL.h
gfx/angle/checkout/src/compiler/translator/TranslatorHLSL.cpp
gfx/angle/checkout/src/libANGLE/MemoryProgramCache.cpp
gfx/angle/checkout/src/libANGLE/Program.cpp
gfx/angle/checkout/src/libANGLE/Program.h
gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp
gfx/angle/checkout/src/libANGLE/renderer/d3d/DynamicHLSL.cpp
gfx/angle/checkout/src/libANGLE/renderer/d3d/DynamicHLSL.h
gfx/angle/checkout/src/libANGLE/renderer/d3d/ProgramD3D.cpp
gfx/angle/checkout/src/libANGLE/renderer/d3d/ProgramD3D.h
gfx/angle/checkout/src/libANGLE/renderer/d3d/ShaderD3D.cpp
gfx/angle/checkout/src/libANGLE/renderer/d3d/ShaderD3D.h
gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
gfx/angle/cherry_picks.txt
gfx/angle/targets/angle_common/moz.build
gfx/angle/targets/angle_gpu_info_util/moz.build
gfx/angle/targets/angle_image_util/moz.build
gfx/angle/targets/libANGLE/moz.build
gfx/angle/targets/libEGL/moz.build
gfx/angle/targets/libGLESv2/moz.build
gfx/angle/targets/preprocessor/moz.build
gfx/angle/targets/translator/moz.build
--- a/gfx/angle/checkout/out/gen/angle/id/commit.h
+++ b/gfx/angle/checkout/out/gen/angle/id/commit.h
@@ -1,3 +1,3 @@
-#define ANGLE_COMMIT_HASH "11fe1e4ce42b"
+#define ANGLE_COMMIT_HASH "5814bb88b10e"
 #define ANGLE_COMMIT_HASH_SIZE 12
-#define ANGLE_COMMIT_DATE "2019-04-12 12:49:22 -0700"
+#define ANGLE_COMMIT_DATE "2019-05-06 18:40:01 -0700"
--- a/gfx/angle/checkout/src/compiler/translator/OutputHLSL.cpp
+++ b/gfx/angle/checkout/src/compiler/translator/OutputHLSL.cpp
@@ -228,32 +228,34 @@ const TConstantUnion *OutputHLSL::writeC
 }
 
 OutputHLSL::OutputHLSL(sh::GLenum shaderType,
                        int shaderVersion,
                        const TExtensionBehavior &extensionBehavior,
                        const char *sourcePath,
                        ShShaderOutput outputType,
                        int numRenderTargets,
+                       int maxDualSourceDrawBuffers,
                        const std::vector<Uniform> &uniforms,
                        ShCompileOptions compileOptions,
                        sh::WorkGroupSize workGroupSize,
                        TSymbolTable *symbolTable,
                        PerformanceDiagnostics *perfDiagnostics,
                        const std::vector<InterfaceBlock> &shaderStorageBlocks)
     : TIntermTraverser(true, true, true, symbolTable),
       mShaderType(shaderType),
       mShaderVersion(shaderVersion),
       mExtensionBehavior(extensionBehavior),
       mSourcePath(sourcePath),
       mOutputType(outputType),
       mCompileOptions(compileOptions),
       mInsideFunction(false),
       mInsideMain(false),
       mNumRenderTargets(numRenderTargets),
+      mMaxDualSourceDrawBuffers(maxDualSourceDrawBuffers),
       mCurrentFunctionMetadata(nullptr),
       mWorkGroupSize(workGroupSize),
       mPerfDiagnostics(perfDiagnostics)
 {
     mUsesFragColor   = false;
     mUsesFragData    = false;
     mUsesDepthRange  = false;
     mUsesFragCoord   = false;
@@ -271,16 +273,17 @@ OutputHLSL::OutputHLSL(sh::GLenum shader
     mUsesLocalInvocationID       = false;
     mUsesGlobalInvocationID      = false;
     mUsesLocalInvocationIndex    = false;
     mUsesXor                     = false;
     mUsesDiscardRewriting        = false;
     mUsesNestedBreak             = false;
     mRequiresIEEEStrictCompiling = false;
     mUseZeroArray                = false;
+    mUsesSecondaryColor          = false;
 
     mUniqueIndex = 0;
 
     mOutputLod0Function      = false;
     mInsideDiscontinuousLoop = false;
     mNestedLoopDepth         = 0;
 
     mExcessiveLoopIndex = nullptr;
@@ -624,16 +627,18 @@ void OutputHLSL::header(TInfoSinkBase &o
     {
         out << DefineZeroArray() << "\n";
     }
 
     if (mShaderType == GL_FRAGMENT_SHADER)
     {
         const bool usingMRTExtension =
             IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers);
+        const bool usingBFEExtension =
+            IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_blend_func_extended);
 
         out << "// Varyings\n";
         writeReferencedVaryings(out);
         out << "\n";
 
         if (mShaderVersion >= 300)
         {
             for (const auto &outputVariable : mReferencedOutputVariables)
@@ -658,16 +663,33 @@ void OutputHLSL::header(TInfoSinkBase &o
                 if (i + 1 != numColorValues)
                 {
                     out << ",";
                 }
                 out << "\n";
             }
 
             out << "};\n";
+
+            if (usingBFEExtension && mUsesSecondaryColor)
+            {
+                out << "static float4 gl_SecondaryColor[" << mMaxDualSourceDrawBuffers
+                    << "] = \n"
+                       "{\n";
+                for (int i = 0; i < mMaxDualSourceDrawBuffers; i++)
+                {
+                    out << "    float4(0, 0, 0, 0)";
+                    if (i + 1 != mMaxDualSourceDrawBuffers)
+                    {
+                        out << ",";
+                    }
+                    out << "\n";
+                }
+                out << "};\n";
+            }
         }
 
         if (mUsesFragDepth)
         {
             out << "static float gl_Depth = 0.0;\n";
         }
 
         if (mUsesFragCoord)
@@ -775,16 +797,21 @@ void OutputHLSL::header(TInfoSinkBase &o
         {
             out << "#define GL_USES_FRAG_COLOR\n";
         }
 
         if (mUsesFragData)
         {
             out << "#define GL_USES_FRAG_DATA\n";
         }
+
+        if (mShaderVersion < 300 && usingBFEExtension && mUsesSecondaryColor)
+        {
+            out << "#define GL_USES_SECONDARY_COLOR\n";
+        }
     }
     else if (mShaderType == GL_VERTEX_SHADER)
     {
         out << "// Attributes\n";
         writeReferencedAttributes(out);
         out << "\n"
                "static float4 gl_Position = float4(0, 0, 0, 0);\n";
 
@@ -1102,16 +1129,26 @@ void OutputHLSL::visitSymbol(TIntermSymb
             out << "gl_Color[0]";
             mUsesFragColor = true;
         }
         else if (qualifier == EvqFragData)
         {
             out << "gl_Color";
             mUsesFragData = true;
         }
+        else if (qualifier == EvqSecondaryFragColorEXT)
+        {
+            out << "gl_SecondaryColor[0]";
+            mUsesSecondaryColor = true;
+        }
+        else if (qualifier == EvqSecondaryFragDataEXT)
+        {
+            out << "gl_SecondaryColor";
+            mUsesSecondaryColor = true;
+        }
         else if (qualifier == EvqFragCoord)
         {
             mUsesFragCoord = true;
             out << name;
         }
         else if (qualifier == EvqPointCoord)
         {
             mUsesPointCoord = true;
--- a/gfx/angle/checkout/src/compiler/translator/OutputHLSL.h
+++ b/gfx/angle/checkout/src/compiler/translator/OutputHLSL.h
@@ -38,16 +38,17 @@ class OutputHLSL : public TIntermTravers
 {
   public:
     OutputHLSL(sh::GLenum shaderType,
                int shaderVersion,
                const TExtensionBehavior &extensionBehavior,
                const char *sourcePath,
                ShShaderOutput outputType,
                int numRenderTargets,
+               int maxDualSourceDrawBuffers,
                const std::vector<Uniform> &uniforms,
                ShCompileOptions compileOptions,
                sh::WorkGroupSize workGroupSize,
                TSymbolTable *symbolTable,
                PerformanceDiagnostics *perfDiagnostics,
                const std::vector<InterfaceBlock> &shaderStorageBlocks);
 
     ~OutputHLSL();
@@ -202,18 +203,20 @@ class OutputHLSL : public TIntermTravers
     bool mUsesLocalInvocationID;
     bool mUsesGlobalInvocationID;
     bool mUsesLocalInvocationIndex;
     bool mUsesXor;
     bool mUsesDiscardRewriting;
     bool mUsesNestedBreak;
     bool mRequiresIEEEStrictCompiling;
     mutable bool mUseZeroArray;
+    bool mUsesSecondaryColor;
 
     int mNumRenderTargets;
+    int mMaxDualSourceDrawBuffers;
 
     int mUniqueIndex;  // For creating unique names
 
     CallDAG mCallDag;
     MetadataList mASTMetadataList;
     ASTMetadataHLSL *mCurrentFunctionMetadata;
     bool mOutputLod0Function;
     bool mInsideDiscontinuousLoop;
--- a/gfx/angle/checkout/src/compiler/translator/TranslatorHLSL.cpp
+++ b/gfx/angle/checkout/src/compiler/translator/TranslatorHLSL.cpp
@@ -37,16 +37,18 @@ TranslatorHLSL::TranslatorHLSL(sh::GLenu
 {}
 
 void TranslatorHLSL::translate(TIntermBlock *root,
                                ShCompileOptions compileOptions,
                                PerformanceDiagnostics *perfDiagnostics)
 {
     const ShBuiltInResources &resources = getResources();
     int numRenderTargets                = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1;
+    int maxDualSourceDrawBuffers =
+        resources.EXT_blend_func_extended ? resources.MaxDualSourceDrawBuffers : 0;
 
     sh::AddDefaultReturnStatements(root);
 
     // Note that SimplifyLoopConditions needs to be run before any other AST transformations that
     // may need to generate new statements from loop conditions or loop expressions.
     // Note that SeparateDeclarations has already been run in TCompiler::compileTreeImpl().
     SimplifyLoopConditions(root,
                            IntermNodePatternMatcher::kExpressionReturningArray |
@@ -130,20 +132,20 @@ void TranslatorHLSL::translate(TIntermBl
     if (getShaderVersion() >= 310)
     {
         // Due to ssbo also can be used as the argument of atomic memory functions, we should put
         // RewriteExpressionsWithShaderStorageBlock before RewriteAtomicFunctionExpressions.
         sh::RewriteExpressionsWithShaderStorageBlock(root, &getSymbolTable());
         sh::RewriteAtomicFunctionExpressions(root, &getSymbolTable(), getShaderVersion());
     }
 
-    sh::OutputHLSL outputHLSL(getShaderType(), getShaderVersion(), getExtensionBehavior(),
-                              getSourcePath(), getOutputType(), numRenderTargets, getUniforms(),
-                              compileOptions, getComputeShaderLocalSize(), &getSymbolTable(),
-                              perfDiagnostics, mShaderStorageBlocks);
+    sh::OutputHLSL outputHLSL(
+        getShaderType(), getShaderVersion(), getExtensionBehavior(), getSourcePath(),
+        getOutputType(), numRenderTargets, maxDualSourceDrawBuffers, getUniforms(), compileOptions,
+        getComputeShaderLocalSize(), &getSymbolTable(), perfDiagnostics, mShaderStorageBlocks);
 
     outputHLSL.output(root, getInfoSink().obj);
 
     mShaderStorageBlockRegisterMap = outputHLSL.getShaderStorageBlockRegisterMap();
     mUniformBlockRegisterMap       = outputHLSL.getUniformBlockRegisterMap();
     mUniformRegisterMap            = outputHLSL.getUniformRegisterMap();
     mReadonlyImage2DRegisterIndex  = outputHLSL.getReadonlyImage2DRegisterIndex();
     mImage2DRegisterIndex          = outputHLSL.getImage2DRegisterIndex();
--- a/gfx/angle/checkout/src/libANGLE/MemoryProgramCache.cpp
+++ b/gfx/angle/checkout/src/libANGLE/MemoryProgramCache.cpp
@@ -54,17 +54,17 @@ HashStream &operator<<(HashStream &strea
     }
     return stream;
 }
 
 HashStream &operator<<(HashStream &stream, const ProgramBindings &bindings)
 {
     for (const auto &binding : bindings)
     {
-        stream << binding.first << binding.second;
+        stream << binding.first << binding.second.location;
     }
     return stream;
 }
 
 HashStream &operator<<(HashStream &stream, const std::vector<std::string> &strings)
 {
     for (const auto &str : strings)
     {
--- a/gfx/angle/checkout/src/libANGLE/Program.cpp
+++ b/gfx/angle/checkout/src/libANGLE/Program.cpp
@@ -137,17 +137,21 @@ GLint GetVariableLocation(const std::vec
         const VariableLocation &variableLocation = locationList[location];
         if (!variableLocation.used())
         {
             continue;
         }
 
         const VarT &variable = list[variableLocation.index];
 
-        if (angle::BeginsWith(variable.name, name))
+        // Array output variables may be bound out of order, so we need to ensure we only pick the
+        // first element if given the base name. Uniforms don't allow this behavior and some code
+        // seemingly depends on the opposite behavior, so only enable it for output variables.
+        if (angle::BeginsWith(variable.name, name) &&
+            (!std::is_base_of<sh::OutputVariable, VarT>::value || variableLocation.arrayIndex == 0))
         {
             if (name.length() == variable.name.length())
             {
                 ASSERT(name == variable.name);
                 // GLES 3.1 November 2016 page 87.
                 // The string exactly matches the name of the active variable.
                 return static_cast<GLint>(location);
             }
@@ -857,23 +861,66 @@ SamplerBinding::~SamplerBinding() = defa
 
 // ProgramBindings implementation.
 ProgramBindings::ProgramBindings() {}
 
 ProgramBindings::~ProgramBindings() {}
 
 void ProgramBindings::bindLocation(GLuint index, const std::string &name)
 {
-    mBindings[name] = index;
-}
-
-int ProgramBindings::getBinding(const std::string &name) const
+    mBindings[name] = ProgramBinding(index);
+
+    // EXT_blend_func_extended spec: "If it specifies the base name of an array,
+    // it identifies the resources associated with the first element of the array."
+    //
+    // Normalize array bindings so that "name" and "name[0]" map to the same entry.
+    // If this binding is of the form "name[0]", then mark the "name" binding as
+    // aliased but do not update it yet in case "name" is not actually an array.
+    size_t nameLengthWithoutArrayIndex;
+    unsigned int arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
+    if (arrayIndex == 0)
+    {
+        std::string baseName = name.substr(0u, nameLengthWithoutArrayIndex);
+        auto iter            = mBindings.find(baseName);
+        if (iter != mBindings.end())
+        {
+            iter->second.aliased = true;
+        }
+    }
+}
+
+int ProgramBindings::getBindingByName(const std::string &name) const
 {
     auto iter = mBindings.find(name);
-    return (iter != mBindings.end()) ? iter->second : -1;
+    return (iter != mBindings.end()) ? iter->second.location : -1;
+}
+
+int ProgramBindings::getBinding(const sh::VariableWithLocation &variable) const
+{
+    const std::string &name = variable.name;
+
+    // Check with the normalized array name if applicable.
+    if (variable.isArray())
+    {
+        size_t nameLengthWithoutArrayIndex;
+        unsigned int arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
+        if (arrayIndex == 0)
+        {
+            std::string baseName = name.substr(0u, nameLengthWithoutArrayIndex);
+            auto iter            = mBindings.find(baseName);
+            // If "name" exists and is not aliased, that means it was modified more
+            // recently than its "name[0]" form and should be used instead of that.
+            if (iter != mBindings.end() && !iter->second.aliased)
+            {
+                return iter->second.location;
+            }
+        }
+    }
+
+    return getBindingByName(name);
 }
 
 ProgramBindings::const_iterator ProgramBindings::begin() const
 {
     return mBindings.begin();
 }
 
 ProgramBindings::const_iterator ProgramBindings::end() const
@@ -1146,17 +1193,17 @@ BindingInfo Program::getFragmentInputBin
     Shader *fragmentShader = mState.getAttachedShader(ShaderType::Fragment);
     ASSERT(fragmentShader);
 
     // Find the actual fragment shader varying we're interested in
     const std::vector<sh::Varying> &inputs = fragmentShader->getInputVaryings();
 
     for (const auto &binding : mFragmentInputBindings)
     {
-        if (binding.second != static_cast<GLuint>(index))
+        if (binding.second.location != static_cast<GLuint>(index))
             continue;
 
         ret.valid = true;
 
         size_t nameLengthWithoutArrayIndex;
         unsigned int arrayIndex = ParseArrayIndex(binding.first, &nameLengthWithoutArrayIndex);
 
         for (const auto &in : inputs)
@@ -1534,16 +1581,17 @@ void Program::unlink()
     mState.mLinkedTransformFeedbackVaryings.clear();
     mState.mUniforms.clear();
     mState.mUniformLocations.clear();
     mState.mUniformBlocks.clear();
     mState.mActiveUniformBlockBindings.reset();
     mState.mAtomicCounterBuffers.clear();
     mState.mOutputVariables.clear();
     mState.mOutputLocations.clear();
+    mState.mSecondaryOutputLocations.clear();
     mState.mOutputVariableTypes.clear();
     mState.mDrawBufferTypeMask.reset();
     mState.mActiveOutputVariables.reset();
     mState.mComputeShaderLocalSize.fill(1);
     mState.mSamplerBindings.clear();
     mState.mImageBindings.clear();
     mState.mActiveImagesMask.reset();
     mState.mNumViews                          = -1;
@@ -2924,17 +2972,17 @@ bool Program::linkValidateFragmentInputB
         mState.mAttachedShaders[ShaderType::Fragment]->getInputVaryings();
     for (const sh::Varying &input : fragmentInputVaryings)
     {
         if (input.isBuiltIn() || !input.staticUse)
         {
             continue;
         }
 
-        const auto inputBinding = mFragmentInputBindings.getBinding(input.name);
+        const auto inputBinding = mFragmentInputBindings.getBinding(input);
         if (inputBinding == -1)
             continue;
 
         const auto it = staticFragmentInputLocations.find(inputBinding);
         if (it == std::end(staticFragmentInputLocations))
         {
             staticFragmentInputLocations.insert(std::make_pair(inputBinding, input.name));
         }
@@ -3115,17 +3163,17 @@ bool Program::linkAttributes(const Caps 
     // Assign locations to attributes that have a binding location and check for attribute aliasing.
     for (sh::Attribute &attribute : mState.mAttributes)
     {
         // GLSL ES 3.10 January 2016 section 4.3.4: Vertex shader inputs can't be arrays or
         // structures, so we don't need to worry about adjusting their names or generating entries
         // for each member/element (unlike uniforms for example).
         ASSERT(!attribute.isArray() && !attribute.isStruct());
 
-        int bindingLocation = mAttributeBindings.getBinding(attribute.name);
+        int bindingLocation = mAttributeBindings.getBinding(attribute);
         if (attribute.location == -1 && bindingLocation != -1)
         {
             attribute.location = bindingLocation;
         }
 
         if (attribute.location != -1)
         {
             // Location is set by glBindAttribLocation or by location layout qualifier
@@ -3703,43 +3751,97 @@ bool CompareOutputVariable(const sh::Out
 }
 
 int Program::getOutputLocationForLink(const sh::OutputVariable &outputVariable) const
 {
     if (outputVariable.location != -1)
     {
         return outputVariable.location;
     }
-    int apiLocation = mFragmentOutputLocations.getBinding(outputVariable.name);
+    int apiLocation = mFragmentOutputLocations.getBinding(outputVariable);
     if (apiLocation != -1)
     {
         return apiLocation;
     }
     return -1;
 }
 
 bool Program::isOutputSecondaryForLink(const sh::OutputVariable &outputVariable) const
 {
     if (outputVariable.index != -1)
     {
         ASSERT(outputVariable.index == 0 || outputVariable.index == 1);
         return (outputVariable.index == 1);
     }
-    int apiIndex = mFragmentOutputIndexes.getBinding(outputVariable.name);
+    int apiIndex = mFragmentOutputIndexes.getBinding(outputVariable);
     if (apiIndex != -1)
     {
         // Index layout qualifier from the shader takes precedence, so the index from the API is
         // checked only if the index was not set in the shader. This is not specified in the EXT
         // spec, but is specified in desktop OpenGL specs.
         return (apiIndex == 1);
     }
     // EXT_blend_func_extended: Outputs get index 0 by default.
     return false;
 }
 
+namespace
+{
+
+bool FindUsedOutputLocation(std::vector<VariableLocation> &outputLocations,
+                            unsigned int baseLocation,
+                            unsigned int elementCount,
+                            const std::vector<VariableLocation> &reservedLocations,
+                            unsigned int variableIndex)
+{
+    if (baseLocation + elementCount > outputLocations.size())
+    {
+        elementCount =
+            baseLocation < outputLocations.size() ? outputLocations.size() - baseLocation : 0;
+    }
+    for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
+    {
+        const unsigned int location = baseLocation + elementIndex;
+        if (outputLocations[location].used())
+        {
+            VariableLocation locationInfo(elementIndex, variableIndex);
+            if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) ==
+                reservedLocations.end())
+            {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void AssignOutputLocations(std::vector<VariableLocation> &outputLocations,
+                           unsigned int baseLocation,
+                           unsigned int elementCount,
+                           const std::vector<VariableLocation> &reservedLocations,
+                           unsigned int variableIndex)
+{
+    if (baseLocation + elementCount > outputLocations.size())
+    {
+        outputLocations.resize(baseLocation + elementCount);
+    }
+    for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
+    {
+        VariableLocation locationInfo(elementIndex, variableIndex);
+        if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) ==
+            reservedLocations.end())
+        {
+            const unsigned int location = baseLocation + elementIndex;
+            outputLocations[location]   = locationInfo;
+        }
+    }
+}
+
+}  // anonymous namespace
+
 bool Program::linkOutputVariables(const Caps &caps,
                                   const Extensions &extensions,
                                   const Version &version,
                                   GLuint combinedImageUniformsCount,
                                   GLuint combinedShaderStorageBlocksCount)
 {
     Shader *fragmentShader = mState.mAttachedShaders[ShaderType::Fragment];
     ASSERT(fragmentShader != nullptr);
@@ -3814,166 +3916,184 @@ bool Program::linkOutputVariables(const 
         {
             // We're following the GLES 3.1 November 2016 spec section 7.3.1.1 Naming Active
             // Resources and including [0] at the end of array variable names.
             outputVariable.name += "[0]";
             outputVariable.mappedName += "[0]";
         }
     }
 
-    bool hasSecondaryOutputs = false;
+    // EXT_blend_func_extended doesn't specify anything related to binding specific elements of an
+    // output array in explicit terms.
+    //
+    // Assuming fragData is an output array, you can defend the position that:
+    // P1) you must support binding "fragData" because it's specified
+    // P2) you must support querying "fragData[x]" because it's specified
+    // P3) you must support binding "fragData[0]" because it's a frequently used pattern
+    //
+    // Then you can make the leap of faith:
+    // P4) you must support binding "fragData[x]" because you support "fragData[0]"
+    // P5) you must support binding "fragData[x]" because you support querying "fragData[x]"
+    //
+    // The spec brings in the "world of arrays" when it mentions binding the arrays and the
+    // automatic binding. Thus it must be interpreted that the thing is not undefined, rather you
+    // must infer the only possible interpretation (?). Note again: this need of interpretation
+    // might be completely off of what GL spec logic is.
+    //
+    // The other complexity is that unless you implement this feature, it's hard to understand what
+    // should happen when the client invokes the feature. You cannot add an additional error as it
+    // is not specified. One can ignore it, but obviously it creates the discrepancies...
+
+    std::vector<VariableLocation> reservedLocations;
+
+    // Process any output API bindings for arrays that don't alias to the first element.
+    for (const auto &binding : mFragmentOutputLocations)
+    {
+        size_t nameLengthWithoutArrayIndex;
+        unsigned int arrayIndex = ParseArrayIndex(binding.first, &nameLengthWithoutArrayIndex);
+        if (arrayIndex == 0 || arrayIndex == GL_INVALID_INDEX)
+        {
+            continue;
+        }
+        for (unsigned int outputVariableIndex = 0;
+             outputVariableIndex < mState.mOutputVariables.size(); outputVariableIndex++)
+        {
+            const sh::OutputVariable &outputVariable = mState.mOutputVariables[outputVariableIndex];
+            // Check that the binding corresponds to an output array and its array index fits.
+            if (outputVariable.isBuiltIn() || !outputVariable.isArray() ||
+                !angle::BeginsWith(outputVariable.name, binding.first,
+                                   nameLengthWithoutArrayIndex) ||
+                arrayIndex >= outputVariable.getOutermostArraySize())
+            {
+                continue;
+            }
+
+            // Get the API index that corresponds to this exact binding.
+            // This index may differ from the index used for the array's base.
+            auto &outputLocations = mFragmentOutputIndexes.getBindingByName(binding.first) == 1
+                                        ? mState.mSecondaryOutputLocations
+                                        : mState.mOutputLocations;
+            unsigned int location = binding.second.location;
+            VariableLocation locationInfo(arrayIndex, outputVariableIndex);
+            if (location >= outputLocations.size())
+            {
+                outputLocations.resize(location + 1);
+            }
+            if (outputLocations[location].used())
+            {
+                mInfoLog << "Location of variable " << outputVariable.name
+                         << " conflicts with another variable.";
+                return false;
+            }
+            outputLocations[location] = locationInfo;
+
+            // Note the array binding location so that it can be skipped later.
+            reservedLocations.push_back(locationInfo);
+        }
+    }
 
     // Reserve locations for output variables whose location is fixed in the shader or through the
-    // API.
+    // API. Otherwise, the remaining unallocated outputs will be processed later.
     for (unsigned int outputVariableIndex = 0; outputVariableIndex < mState.mOutputVariables.size();
          outputVariableIndex++)
     {
         const sh::OutputVariable &outputVariable = mState.mOutputVariables[outputVariableIndex];
 
         // Don't store outputs for gl_FragDepth, gl_FragColor, etc.
         if (outputVariable.isBuiltIn())
             continue;
 
-        int baseLocation = getOutputLocationForLink(outputVariable);
-        if (baseLocation == -1)
+        int fixedLocation = getOutputLocationForLink(outputVariable);
+        if (fixedLocation == -1)
         {
             // Here we're only reserving locations for variables whose location is fixed.
             continue;
         }
-
-        auto *outputLocations = &mState.mOutputLocations;
-        if (isOutputSecondaryForLink(outputVariable))
-        {
-            outputLocations = &mState.mSecondaryOutputLocations;
-            // Note that this check doesn't need to be before checking baseLocation == -1 above. If
-            // an output has an index specified it will always also have the location specified.
-            hasSecondaryOutputs = true;
-        }
+        unsigned int baseLocation = static_cast<unsigned int>(fixedLocation);
+
+        auto &outputLocations = isOutputSecondaryForLink(outputVariable)
+                                    ? mState.mSecondaryOutputLocations
+                                    : mState.mOutputLocations;
 
         // GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
         // structures, so we may use getBasicTypeElementCount().
         unsigned int elementCount          = outputVariable.getBasicTypeElementCount();
-        unsigned int outputLocationsNeeded = static_cast<unsigned int>(baseLocation) + elementCount;
-        if (outputLocationsNeeded > outputLocations->size())
-        {
-            outputLocations->resize(outputLocationsNeeded);
-        }
-        for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
+        if (FindUsedOutputLocation(outputLocations, baseLocation, elementCount, reservedLocations,
+                                   outputVariableIndex))
         {
-            const unsigned int location = static_cast<unsigned int>(baseLocation) + elementIndex;
-            ASSERT(location < outputLocations->size());
-            if (outputLocations->at(location).used())
-            {
-                mInfoLog << "Location of variable " << outputVariable.name
-                         << " conflicts with another variable.";
-                return false;
-            }
-            if (outputVariable.isArray())
-            {
-                (*outputLocations)[location] = VariableLocation(elementIndex, outputVariableIndex);
-            }
-            else
-            {
-                VariableLocation locationInfo;
-                locationInfo.index           = outputVariableIndex;
-                (*outputLocations)[location] = locationInfo;
-            }
+            mInfoLog << "Location of variable " << outputVariable.name
+                     << " conflicts with another variable.";
+            return false;
         }
+        AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations,
+                              outputVariableIndex);
     }
 
     // Here we assign locations for the output variables that don't yet have them. Note that we're
     // not necessarily able to fit the variables optimally, since then we might have to try
     // different arrangements of output arrays. Now we just assign the locations in the order that
     // we got the output variables. The spec isn't clear on what kind of algorithm is required for
     // finding locations for the output variables, so this should be acceptable at least for now.
     GLuint maxLocation = caps.maxDrawBuffers;
-    if (hasSecondaryOutputs)
+    if (!mState.mSecondaryOutputLocations.empty())
     {
         // EXT_blend_func_extended: Program outputs will be validated against
         // MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT if there's even one output with index one.
         maxLocation = extensions.maxDualSourceDrawBuffers;
     }
 
     for (unsigned int outputVariableIndex = 0; outputVariableIndex < mState.mOutputVariables.size();
          outputVariableIndex++)
     {
         const sh::OutputVariable &outputVariable = mState.mOutputVariables[outputVariableIndex];
 
         // Don't store outputs for gl_FragDepth, gl_FragColor, etc.
         if (outputVariable.isBuiltIn())
             continue;
 
-        if (getOutputLocationForLink(outputVariable) != -1)
-        {
-            continue;
-        }
-
-        auto *outputLocations = &mState.mOutputLocations;
-        if (isOutputSecondaryForLink(outputVariable))
+        int fixedLocation     = getOutputLocationForLink(outputVariable);
+        auto &outputLocations = isOutputSecondaryForLink(outputVariable)
+                                    ? mState.mSecondaryOutputLocations
+                                    : mState.mOutputLocations;
+        unsigned int baseLocation = 0;
+        unsigned int elementCount = outputVariable.getBasicTypeElementCount();
+        if (fixedLocation != -1)
         {
-            outputLocations = &mState.mSecondaryOutputLocations;
+            // Secondary inputs might have caused the max location to drop below what has already
+            // been explicitly assigned locations. Check for any fixed locations above the max
+            // that should cause linking to fail.
+            baseLocation = static_cast<unsigned int>(fixedLocation);
         }
-
-        int baseLocation          = 0;
-        unsigned int elementCount = outputVariable.getBasicTypeElementCount();
-        bool elementsFit          = false;
-        while (!elementsFit)
+        else
         {
+            // No fixed location, so try to fit the output in unassigned locations.
             // Try baseLocations starting from 0 one at a time and see if the variable fits.
-            elementsFit = true;
-            if (baseLocation + elementCount > maxLocation)
+            while (FindUsedOutputLocation(outputLocations, baseLocation, elementCount,
+                                          reservedLocations, outputVariableIndex))
             {
-                // EXT_blend_func_extended: Linking can fail:
-                // "if the explicit binding assignments do not leave enough space for the linker to
-                // automatically assign a location for a varying out array, which requires multiple
-                // contiguous locations."
-                mInfoLog << "Could not fit output variable into available locations: "
-                         << outputVariable.name;
-                return false;
-            }
-            unsigned int outputLocationsNeeded =
-                static_cast<unsigned int>(baseLocation) + elementCount;
-            if (outputLocationsNeeded > outputLocations->size())
-            {
-                outputLocations->resize(outputLocationsNeeded);
+                baseLocation++;
             }
-            for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
-            {
-                const unsigned int location =
-                    static_cast<unsigned int>(baseLocation) + elementIndex;
-                ASSERT(location < outputLocations->size());
-                if (outputLocations->at(location).used())
-                {
-                    elementsFit = false;
-                    break;
-                }
-            }
-            if (elementsFit)
-            {
-                for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
-                {
-                    const unsigned int location =
-                        static_cast<unsigned int>(baseLocation) + elementIndex;
-                    if (outputVariable.isArray())
-                    {
-                        (*outputLocations)[location] =
-                            VariableLocation(elementIndex, outputVariableIndex);
-                    }
-                    else
-                    {
-                        VariableLocation locationInfo;
-                        locationInfo.index           = outputVariableIndex;
-                        (*outputLocations)[location] = locationInfo;
-                    }
-                }
-            }
-            else
-            {
-                ++baseLocation;
-            }
+            AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations,
+                                  outputVariableIndex);
+        }
+
+        // Check for any elements assigned above the max location that are actually used.
+        if (baseLocation + elementCount > maxLocation &&
+            (baseLocation >= maxLocation ||
+             FindUsedOutputLocation(outputLocations, maxLocation,
+                                    baseLocation + elementCount - maxLocation, reservedLocations,
+                                    outputVariableIndex)))
+        {
+            // EXT_blend_func_extended: Linking can fail:
+            // "if the explicit binding assignments do not leave enough space for the linker to
+            // automatically assign a location for a varying out array, which requires multiple
+            // contiguous locations."
+            mInfoLog << "Could not fit output variable into available locations: "
+                     << outputVariable.name;
+            return false;
         }
     }
 
     return true;
 }
 
 void Program::setUniformValuesFromBindingQualifiers()
 {
--- a/gfx/angle/checkout/src/libANGLE/Program.h
+++ b/gfx/angle/checkout/src/libANGLE/Program.h
@@ -174,16 +174,21 @@ struct VariableLocation
 
     // If used is false, it means this location is only used to fill an empty space in an array,
     // and there is no corresponding uniform variable for this location. It can also mean the
     // uniform was optimized out by the implementation.
     bool used() const { return (index != kUnused); }
     void markUnused() { index = kUnused; }
     void markIgnored() { ignored = true; }
 
+    bool operator==(const VariableLocation &other) const
+    {
+        return arrayIndex == other.arrayIndex && index == other.index;
+    }
+
     // "arrayIndex" stores the index of the innermost GLSL array. It's zero for non-arrays.
     unsigned int arrayIndex;
     // "index" is an index of the variable. The variable contains the indices for other than the
     // innermost GLSL arrays.
     unsigned int index;
 
     // If this location was bound to an unreferenced uniform.  Setting data on this uniform is a
     // no-op.
@@ -468,31 +473,42 @@ class ProgramState final : angle::NonCop
     ActiveTextureArray<uint32_t> mActiveSamplerRefCounts;
     ActiveTextureArray<TextureType> mActiveSamplerTypes;
     ActiveTextureArray<SamplerFormat> mActiveSamplerFormats;
 
     // Cached mask of active images.
     ActiveTextureMask mActiveImagesMask;
 };
 
+struct ProgramBinding
+{
+    ProgramBinding() : location(GL_INVALID_INDEX), aliased(false) {}
+    ProgramBinding(GLuint index) : location(index), aliased(false) {}
+
+    GLuint location;
+    // Whether another binding was set that may potentially alias this.
+    bool aliased;
+};
+
 class ProgramBindings final : angle::NonCopyable
 {
   public:
     ProgramBindings();
     ~ProgramBindings();
 
     void bindLocation(GLuint index, const std::string &name);
-    int getBinding(const std::string &name) const;
+    int getBindingByName(const std::string &name) const;
+    int getBinding(const sh::VariableWithLocation &variable) const;
 
-    using const_iterator = std::unordered_map<std::string, GLuint>::const_iterator;
+    using const_iterator = std::unordered_map<std::string, ProgramBinding>::const_iterator;
     const_iterator begin() const;
     const_iterator end() const;
 
   private:
-    std::unordered_map<std::string, GLuint> mBindings;
+    std::unordered_map<std::string, ProgramBinding> mBindings;
 };
 
 struct ProgramVaryingRef
 {
     const sh::Varying *get() const { return vertex ? vertex : fragment; }
 
     const sh::Varying *vertex   = nullptr;
     const sh::Varying *fragment = nullptr;
--- a/gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp
+++ b/gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp
@@ -27,30 +27,16 @@ LinkedUniform *FindUniform(std::vector<L
     {
         if (uniform.name == name)
             return &uniform;
     }
 
     return nullptr;
 }
 
-int GetUniformLocationBinding(const ProgramBindings &uniformLocationBindings,
-                              const sh::Uniform &uniform)
-{
-    int binding = uniformLocationBindings.getBinding(uniform.name);
-    if (uniform.isArray() && binding == -1)
-    {
-        // Bindings for array uniforms can be set either with or without [0] in the end.
-        ASSERT(angle::EndsWith(uniform.name, "[0]"));
-        std::string nameWithoutIndex = uniform.name.substr(0u, uniform.name.length() - 3u);
-        return uniformLocationBindings.getBinding(nameWithoutIndex);
-    }
-    return binding;
-}
-
 template <typename VarT>
 void SetActive(std::vector<VarT> *list, const std::string &name, ShaderType shaderType, bool active)
 {
     for (auto &variable : *list)
     {
         if (variable.name == name)
         {
             variable.setActive(shaderType, active);
@@ -659,17 +645,17 @@ bool UniformLinker::indexUniforms(InfoLo
         const LinkedUniform &uniform = mUniforms[uniformIndex];
 
         if ((uniform.isBuiltIn() && !uniform.isEmulatedBuiltIn()) ||
             IsAtomicCounterType(uniform.type))
         {
             continue;
         }
 
-        int preSetLocation = GetUniformLocationBinding(uniformLocationBindings, uniform);
+        int preSetLocation = uniformLocationBindings.getBinding(uniform);
         int shaderLocation = uniform.location;
 
         if (shaderLocation != -1)
         {
             preSetLocation = shaderLocation;
         }
 
         unsigned int elementCount = uniform.getBasicTypeElementCount();
@@ -735,17 +721,17 @@ bool UniformLinker::gatherUniformLocatio
 
     for (const LinkedUniform &uniform : mUniforms)
     {
         if (uniform.isBuiltIn() && !uniform.isEmulatedBuiltIn())
         {
             continue;
         }
 
-        int apiBoundLocation = GetUniformLocationBinding(uniformLocationBindings, uniform);
+        int apiBoundLocation = uniformLocationBindings.getBinding(uniform);
         int shaderLocation   = uniform.location;
 
         if (shaderLocation != -1)
         {
             unsigned int elementCount = uniform.getBasicTypeElementCount();
 
             for (unsigned int arrayIndex = 0; arrayIndex < elementCount; arrayIndex++)
             {
@@ -780,17 +766,17 @@ bool UniformLinker::gatherUniformLocatio
             }
         }
     }
 
     // Record the uniform locations that were bound using the API for uniforms that were not found
     // from the shader. Other uniforms should not be assigned to those locations.
     for (const auto &locationBinding : uniformLocationBindings)
     {
-        GLuint location = locationBinding.second;
+        GLuint location = locationBinding.second.location;
         if (reservedLocations.find(location) == reservedLocations.end())
         {
             ignoredLocations->insert(location);
             *maxUniformLocation = std::max(*maxUniformLocation, static_cast<int>(location));
         }
     }
 
     return true;
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/DynamicHLSL.cpp
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/DynamicHLSL.cpp
@@ -92,23 +92,24 @@ void HLSLTypeString(std::ostringstream &
     }
 
     HLSLComponentTypeString(ostream, gl::VariableComponentType(type),
                             gl::VariableComponentCount(type));
 }
 
 const PixelShaderOutputVariable *FindOutputAtLocation(
     const std::vector<PixelShaderOutputVariable> &outputVariables,
-    unsigned int location)
+    unsigned int location,
+    size_t index = 0)
 {
-    for (size_t variableIndex = 0; variableIndex < outputVariables.size(); ++variableIndex)
+    for (auto &outputVar : outputVariables)
     {
-        if (outputVariables[variableIndex].outputIndex == location)
+        if (outputVar.outputLocation == location && outputVar.outputIndex == index)
         {
-            return &outputVariables[variableIndex];
+            return &outputVar;
         }
     }
 
     return nullptr;
 }
 
 void WriteArrayString(std::ostringstream &strstr, unsigned int i)
 {
@@ -283,34 +284,37 @@ std::string DynamicHLSL::generatePixelSh
 
     size_t numOutputs = outputLayout.size();
 
     // Workaround for HLSL 3.x: We can't do a depth/stencil only render, the runtime will complain.
     if (numOutputs == 0 && (shaderModel == 3 || !mRenderer->getShaderModelSuffix().empty()))
     {
         numOutputs = 1u;
     }
-    const PixelShaderOutputVariable defaultOutput(GL_FLOAT_VEC4, "dummy", "float4(0, 0, 0, 1)", 0);
+    const PixelShaderOutputVariable defaultOutput(GL_FLOAT_VEC4, "dummy", "float4(0, 0, 0, 1)", 0,
+                                                  0);
+    size_t outputIndex = 0;
 
     for (size_t layoutIndex = 0; layoutIndex < numOutputs; ++layoutIndex)
     {
         GLenum binding = outputLayout.empty() ? GL_COLOR_ATTACHMENT0 : outputLayout[layoutIndex];
 
         if (binding != GL_NONE)
         {
             unsigned int location = (binding - GL_COLOR_ATTACHMENT0);
+            outputIndex =
+                layoutIndex > 0 && binding == outputLayout[layoutIndex - 1] ? outputIndex + 1 : 0;
 
             const PixelShaderOutputVariable *outputVariable =
                 outputLayout.empty() ? &defaultOutput
-                                     : FindOutputAtLocation(outputVariables, location);
+                                     : FindOutputAtLocation(outputVariables, location, outputIndex);
 
             // OpenGL ES 3.0 spec $4.2.1
             // If [...] not all user-defined output variables are written, the values of fragment
-            // colors
-            // corresponding to unwritten variables are similarly undefined.
+            // colors corresponding to unwritten variables are similarly undefined.
             if (outputVariable)
             {
                 declarationStream << "    ";
                 HLSLTypeString(declarationStream, outputVariable->type);
                 declarationStream << " " << outputVariable->name << " : " << targetSemantic
                                   << static_cast<int>(layoutIndex) << ";\n";
 
                 copyStream << "    output." << outputVariable->name << " = "
@@ -1193,20 +1197,36 @@ void DynamicHLSL::getPixelShaderOutputKe
         for (unsigned int renderTargetIndex = 0; renderTargetIndex < numRenderTargets;
              renderTargetIndex++)
         {
             PixelShaderOutputVariable outputKeyVariable;
             outputKeyVariable.type = GL_FLOAT_VEC4;
             outputKeyVariable.name = "gl_Color" + Str(renderTargetIndex);
             outputKeyVariable.source =
                 broadcast ? "gl_Color[0]" : "gl_Color[" + Str(renderTargetIndex) + "]";
-            outputKeyVariable.outputIndex = renderTargetIndex;
+            outputKeyVariable.outputLocation = renderTargetIndex;
 
             outPixelShaderKey->push_back(outputKeyVariable);
         }
+
+        if (metadata.usesSecondaryColor())
+        {
+            for (unsigned int secondaryIndex = 0;
+                 secondaryIndex < data.getExtensions().maxDualSourceDrawBuffers; secondaryIndex++)
+            {
+                PixelShaderOutputVariable outputKeyVariable;
+                outputKeyVariable.type           = GL_FLOAT_VEC4;
+                outputKeyVariable.name           = "gl_SecondaryColor" + Str(secondaryIndex);
+                outputKeyVariable.source         = "gl_SecondaryColor[" + Str(secondaryIndex) + "]";
+                outputKeyVariable.outputLocation = secondaryIndex;
+                outputKeyVariable.outputIndex    = 1;
+
+                outPixelShaderKey->push_back(outputKeyVariable);
+            }
+        }
     }
     else
     {
         const auto &shaderOutputVars =
             metadata.getFragmentShader()->getData().getActiveOutputVariables();
 
         for (size_t outputLocationIndex = 0u;
              outputLocationIndex < programData.getOutputLocations().size(); ++outputLocationIndex)
@@ -1227,17 +1247,49 @@ void DynamicHLSL::getPixelShaderOutputKe
             ASSERT(outputVariable.active);
 
             PixelShaderOutputVariable outputKeyVariable;
             outputKeyVariable.type = outputVariable.type;
             outputKeyVariable.name = variableName + elementString;
             outputKeyVariable.source =
                 variableName +
                 (outputVariable.isArray() ? ArrayString(outputLocation.arrayIndex) : "");
-            outputKeyVariable.outputIndex = outputLocationIndex;
+            outputKeyVariable.outputLocation = outputLocationIndex;
+
+            outPixelShaderKey->push_back(outputKeyVariable);
+        }
+
+        // Now generate any secondary outputs...
+        for (size_t outputLocationIndex = 0u;
+             outputLocationIndex < programData.getSecondaryOutputLocations().size();
+             ++outputLocationIndex)
+        {
+            const VariableLocation &outputLocation =
+                programData.getSecondaryOutputLocations().at(outputLocationIndex);
+            if (!outputLocation.used())
+            {
+                continue;
+            }
+            const sh::ShaderVariable &outputVariable = shaderOutputVars[outputLocation.index];
+            const std::string &variableName          = "out_" + outputVariable.name;
+
+            // Fragment outputs can't be arrays of arrays. ESSL 3.10 section 4.3.6.
+            const std::string &elementString =
+                (outputVariable.isArray() ? Str(outputLocation.arrayIndex) : "");
+
+            ASSERT(outputVariable.active);
+
+            PixelShaderOutputVariable outputKeyVariable;
+            outputKeyVariable.type = outputVariable.type;
+            outputKeyVariable.name = variableName + elementString;
+            outputKeyVariable.source =
+                variableName +
+                (outputVariable.isArray() ? ArrayString(outputLocation.arrayIndex) : "");
+            outputKeyVariable.outputLocation = outputLocationIndex;
+            outputKeyVariable.outputIndex    = 1;
 
             outPixelShaderKey->push_back(outputKeyVariable);
         }
     }
 }
 
 // BuiltinVarying Implementation.
 BuiltinVarying::BuiltinVarying() : enabled(false), index(0), systemValue(false) {}
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/DynamicHLSL.h
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/DynamicHLSL.h
@@ -62,23 +62,29 @@ inline std::ostream &operator<<(std::ost
 }
 
 struct PixelShaderOutputVariable
 {
     PixelShaderOutputVariable() {}
     PixelShaderOutputVariable(GLenum typeIn,
                               const std::string &nameIn,
                               const std::string &sourceIn,
+                              size_t outputLocationIn,
                               size_t outputIndexIn)
-        : type(typeIn), name(nameIn), source(sourceIn), outputIndex(outputIndexIn)
+        : type(typeIn),
+          name(nameIn),
+          source(sourceIn),
+          outputLocation(outputLocationIn),
+          outputIndex(outputIndexIn)
     {}
 
     GLenum type = GL_NONE;
     std::string name;
     std::string source;
+    size_t outputLocation = 0;
     size_t outputIndex = 0;
 };
 
 struct BuiltinVarying final : private angle::NonCopyable
 {
     BuiltinVarying();
 
     std::string str() const;
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -62,26 +62,42 @@ void GetDefaultInputLayoutFromShader(gl:
                     gl::GetVertexFormatID(attribType, GL_FALSE, components, pureInt);
 
                 inputLayoutOut->push_back(defaultID);
             }
         }
     }
 }
 
+size_t GetMaxOutputIndex(const std::vector<PixelShaderOutputVariable> &shaderOutputVars,
+                         size_t location)
+{
+    size_t maxIndex = 0;
+    for (auto &outputVar : shaderOutputVars)
+    {
+        if (outputVar.outputLocation == location)
+        {
+            maxIndex = std::max(maxIndex, outputVar.outputIndex);
+        }
+    }
+    return maxIndex;
+}
+
 void GetDefaultOutputLayoutFromShader(
     const std::vector<PixelShaderOutputVariable> &shaderOutputVars,
     std::vector<GLenum> *outputLayoutOut)
 {
     outputLayoutOut->clear();
 
     if (!shaderOutputVars.empty())
     {
-        outputLayoutOut->push_back(GL_COLOR_ATTACHMENT0 +
-                                   static_cast<unsigned int>(shaderOutputVars[0].outputIndex));
+        size_t location = shaderOutputVars[0].outputLocation;
+        size_t maxIndex = GetMaxOutputIndex(shaderOutputVars, location);
+        outputLayoutOut->assign(maxIndex + 1,
+                                GL_COLOR_ATTACHMENT0 + static_cast<unsigned int>(location));
     }
 }
 
 void GetDefaultImage2DBindLayoutFromComputeShader(const std::vector<sh::Uniform> &image2DUniforms,
                                                   gl::ImageUnitTextureTypeMap *image2DBindLayout)
 {
     image2DBindLayout->clear();
 
@@ -472,16 +488,21 @@ int ProgramD3DMetadata::getRendererMajor
 
 bool ProgramD3DMetadata::usesBroadcast(const gl::State &data) const
 {
     return (mAttachedShaders[gl::ShaderType::Fragment]->usesFragColor() &&
             mAttachedShaders[gl::ShaderType::Fragment]->usesMultipleRenderTargets() &&
             data.getClientMajorVersion() < 3);
 }
 
+bool ProgramD3DMetadata::usesSecondaryColor() const
+{
+    return mAttachedShaders[gl::ShaderType::Fragment]->usesSecondaryColor();
+}
+
 bool ProgramD3DMetadata::usesFragDepth() const
 {
     return mAttachedShaders[gl::ShaderType::Fragment]->usesFragDepth();
 }
 
 bool ProgramD3DMetadata::usesPointCoord() const
 {
     return mAttachedShaders[gl::ShaderType::Fragment]->usesPointCoord();
@@ -1155,16 +1176,17 @@ std::unique_ptr<rx::LinkEvent> ProgramD3
     const size_t pixelShaderKeySize = stream->readInt<unsigned int>();
     mPixelShaderKey.resize(pixelShaderKeySize);
     for (size_t pixelShaderKeyIndex = 0; pixelShaderKeyIndex < pixelShaderKeySize;
          pixelShaderKeyIndex++)
     {
         stream->readInt(&mPixelShaderKey[pixelShaderKeyIndex].type);
         stream->readString(&mPixelShaderKey[pixelShaderKeyIndex].name);
         stream->readString(&mPixelShaderKey[pixelShaderKeyIndex].source);
+        stream->readInt(&mPixelShaderKey[pixelShaderKeyIndex].outputLocation);
         stream->readInt(&mPixelShaderKey[pixelShaderKeyIndex].outputIndex);
     }
 
     stream->readString(&mGeometryShaderPreamble);
 
     return std::make_unique<LoadBinaryLinkEvent>(context->getWorkerThreadPool(), context, this,
                                                  stream, infoLog);
 }
@@ -1437,16 +1459,17 @@ void ProgramD3D::save(const gl::Context 
     stream->writeInt(pixelShaderKey.size());
     for (size_t pixelShaderKeyIndex = 0; pixelShaderKeyIndex < pixelShaderKey.size();
          pixelShaderKeyIndex++)
     {
         const PixelShaderOutputVariable &variable = pixelShaderKey[pixelShaderKeyIndex];
         stream->writeInt(variable.type);
         stream->writeString(variable.name);
         stream->writeString(variable.source);
+        stream->writeInt(variable.outputLocation);
         stream->writeInt(variable.outputIndex);
     }
 
     stream->writeString(mGeometryShaderPreamble);
 
     stream->writeInt(mVertexExecutables.size());
     for (size_t vertexExecutableIndex = 0; vertexExecutableIndex < mVertexExecutables.size();
          vertexExecutableIndex++)
@@ -2977,17 +3000,21 @@ void ProgramD3D::updateCachedOutputLayou
     for (size_t colorAttachment = 0; colorAttachment < colorbuffers.size(); ++colorAttachment)
     {
         const gl::FramebufferAttachment *colorbuffer = colorbuffers[colorAttachment];
 
         if (colorbuffer)
         {
             auto binding = colorbuffer->getBinding() == GL_BACK ? GL_COLOR_ATTACHMENT0
                                                                 : colorbuffer->getBinding();
-            mPixelShaderOutputLayoutCache.push_back(binding);
+            size_t maxIndex = binding != GL_NONE ? GetMaxOutputIndex(mPixelShaderKey,
+                                                                     binding - GL_COLOR_ATTACHMENT0)
+                                                 : 0;
+            mPixelShaderOutputLayoutCache.insert(mPixelShaderOutputLayoutCache.end(), maxIndex + 1,
+                                                 binding);
         }
         else
         {
             mPixelShaderOutputLayoutCache.push_back(GL_NONE);
         }
     }
 
     updateCachedPixelExecutableIndex();
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/ProgramD3D.h
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/ProgramD3D.h
@@ -117,16 +117,17 @@ class ProgramD3DMetadata final : angle::
 {
   public:
     ProgramD3DMetadata(RendererD3D *renderer,
                        const gl::ShaderMap<const ShaderD3D *> &attachedShaders);
     ~ProgramD3DMetadata();
 
     int getRendererMajorShaderModel() const;
     bool usesBroadcast(const gl::State &data) const;
+    bool usesSecondaryColor() const;
     bool usesFragDepth() const;
     bool usesPointCoord() const;
     bool usesFragCoord() const;
     bool usesPointSize() const;
     bool usesInsertedPointCoordValue() const;
     bool usesViewScale() const;
     bool hasANGLEMultiviewEnabled() const;
     bool usesViewID() const;
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/ShaderD3D.cpp
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/ShaderD3D.cpp
@@ -75,16 +75,17 @@ std::string ShaderD3D::getDebugInfo() co
 void ShaderD3D::uncompile()
 {
     // set by compileToHLSL
     mCompilerOutputType = SH_ESSL_OUTPUT;
 
     mUsesMultipleRenderTargets   = false;
     mUsesFragColor               = false;
     mUsesFragData                = false;
+    mUsesSecondaryColor          = false;
     mUsesFragCoord               = false;
     mUsesFrontFacing             = false;
     mUsesPointSize               = false;
     mUsesPointCoord              = false;
     mUsesDepthRange              = false;
     mUsesFragDepth               = false;
     mHasANGLEMultiviewEnabled    = false;
     mUsesViewID                  = false;
@@ -202,16 +203,17 @@ bool ShaderD3D::postTranslateCompile(gl:
 {
     // TODO(jmadill): We shouldn't need to cache this.
     mCompilerOutputType = compiler->getShaderOutputType();
 
     const std::string &translatedSource = mData.getTranslatedSource();
 
     mUsesMultipleRenderTargets = translatedSource.find("GL_USES_MRT") != std::string::npos;
     mUsesFragColor             = translatedSource.find("GL_USES_FRAG_COLOR") != std::string::npos;
+    mUsesSecondaryColor        = translatedSource.find("GL_USES_SECONDARY_COLOR") != std::string::npos;
     mUsesFragData              = translatedSource.find("GL_USES_FRAG_DATA") != std::string::npos;
     mUsesFragCoord             = translatedSource.find("GL_USES_FRAG_COORD") != std::string::npos;
     mUsesFrontFacing           = translatedSource.find("GL_USES_FRONT_FACING") != std::string::npos;
     mUsesPointSize             = translatedSource.find("GL_USES_POINT_SIZE") != std::string::npos;
     mUsesPointCoord            = translatedSource.find("GL_USES_POINT_COORD") != std::string::npos;
     mUsesDepthRange            = translatedSource.find("GL_USES_DEPTH_RANGE") != std::string::npos;
     mUsesFragDepth             = translatedSource.find("GL_USES_FRAG_DEPTH") != std::string::npos;
     mHasANGLEMultiviewEnabled =
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/ShaderD3D.h
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/ShaderD3D.h
@@ -61,31 +61,33 @@ class ShaderD3D : public ShaderImpl
     bool useImage2DFunction(const std::string &functionName) const;
     void appendDebugInfo(const std::string &info) const { mDebugInfo += info; }
 
     void generateWorkarounds(angle::CompilerWorkaroundsD3D *workarounds) const;
 
     bool usesMultipleRenderTargets() const { return mUsesMultipleRenderTargets; }
     bool usesFragColor() const { return mUsesFragColor; }
     bool usesFragData() const { return mUsesFragData; }
+    bool usesSecondaryColor() const { return mUsesSecondaryColor; }
     bool usesFragCoord() const { return mUsesFragCoord; }
     bool usesFrontFacing() const { return mUsesFrontFacing; }
     bool usesPointSize() const { return mUsesPointSize; }
     bool usesPointCoord() const { return mUsesPointCoord; }
     bool usesDepthRange() const { return mUsesDepthRange; }
     bool usesFragDepth() const { return mUsesFragDepth; }
     bool usesViewID() const { return mUsesViewID; }
     bool hasANGLEMultiviewEnabled() const { return mHasANGLEMultiviewEnabled; }
 
     ShShaderOutput getCompilerOutputType() const;
 
   private:
     bool mUsesMultipleRenderTargets;
     bool mUsesFragColor;
     bool mUsesFragData;
+    bool mUsesSecondaryColor;
     bool mUsesFragCoord;
     bool mUsesFrontFacing;
     bool mUsesPointSize;
     bool mUsesPointCoord;
     bool mUsesDepthRange;
     bool mUsesFragDepth;
     bool mHasANGLEMultiviewEnabled;
     bool mUsesViewID;
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
@@ -1644,16 +1644,18 @@ void GenerateCaps(ID3D11Device *device,
     extensions->copyCompressedTexture            = true;
     extensions->textureStorageMultisample2DArray = true;
     extensions->multiviewMultisample =
         (extensions->multiview2 && extensions->textureStorageMultisample2DArray);
     extensions->copyTexture3d      = true;
     extensions->textureBorderClamp = true;
     extensions->textureMultisample = true;
     extensions->provokingVertex    = true;
+    extensions->blendFuncExtended        = true;
+    extensions->maxDualSourceDrawBuffers = 1;
 
     // D3D11 Feature Level 10_0+ uses SV_IsFrontFace in HLSL to emulate gl_FrontFacing.
     // D3D11 Feature Level 9_3 doesn't support SV_IsFrontFace, and has no equivalent, so can't
     // support gl_FrontFacing.
     limitations->noFrontFacingSupport =
         (renderer11DeviceCaps.featureLevel <= D3D_FEATURE_LEVEL_9_3);
 
     // D3D11 Feature Level 9_3 doesn't support alpha-to-coverage
@@ -1746,16 +1748,28 @@ D3D11_BLEND ConvertBlendFunc(GLenum glBl
             d3dBlend = D3D11_BLEND_BLEND_FACTOR;
             break;
         case GL_ONE_MINUS_CONSTANT_ALPHA:
             d3dBlend = D3D11_BLEND_INV_BLEND_FACTOR;
             break;
         case GL_SRC_ALPHA_SATURATE:
             d3dBlend = D3D11_BLEND_SRC_ALPHA_SAT;
             break;
+        case GL_SRC1_COLOR_EXT:
+            d3dBlend = (isAlpha ? D3D11_BLEND_SRC1_ALPHA : D3D11_BLEND_SRC1_COLOR);
+            break;
+        case GL_SRC1_ALPHA_EXT:
+            d3dBlend = D3D11_BLEND_SRC1_ALPHA;
+            break;
+        case GL_ONE_MINUS_SRC1_COLOR_EXT:
+            d3dBlend = (isAlpha ? D3D11_BLEND_INV_SRC1_ALPHA : D3D11_BLEND_INV_SRC1_COLOR);
+            break;
+        case GL_ONE_MINUS_SRC1_ALPHA_EXT:
+            d3dBlend = D3D11_BLEND_INV_SRC1_ALPHA;
+            break;
         default:
             UNREACHABLE();
     }
 
     return d3dBlend;
 }
 
 D3D11_BLEND_OP ConvertBlendOp(GLenum glBlendOp)
--- a/gfx/angle/cherry_picks.txt
+++ b/gfx/angle/cherry_picks.txt
@@ -1,8 +1,19 @@
+commit 5814bb88b10ef903abcf297f2de72619d853b4e9
+Author: Lee Salzman <lsalzman@mozilla.com>
+Date:   Tue Apr 30 23:42:31 2019 -0400
+
+    add support for EXT_blend_func_extended to D3D11
+    
+    Change-Id: Id66868851a490d0a68a7e76280720825c4844a45
+    Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1591192
+    Commit-Queue: Geoff Lang <geofflang@chromium.org>
+    Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
+
 commit f2915830eaf4b2970c37d4baecf0dc8030c85dba
 Author: Jan Beich <jbeich@FreeBSD.org>
 Date:   Tue Apr 23 16:25:18 2019 -0700
 
     `environ` needs to be declared for (at least) FreeBSD.
     
     Declaring it unconditionally seems to build fine on all other platforms.
     
--- a/gfx/angle/targets/angle_common/moz.build
+++ b/gfx/angle/targets/angle_common/moz.build
@@ -6,17 +6,17 @@ DEFINES['ANGLE_ENABLE_DEBUG_ANNOTATIONS'
 #DEFINES['ANGLE_IS_64_BIT_CPU'] = True
 #DEFINES['CERT_CHAIN_PARA_HAS_EXTRA_FIELDS'] = True
 #DEFINES['CHROMIUM_BUILD'] = True
 #DEFINES['COMPONENT_BUILD'] = True
 DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1'
 #DEFINES['FULL_SAFE_BROWSING'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -40,16 +40,17 @@ LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
     '../../checkout/src/common/third_party/base/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
--- a/gfx/angle/targets/angle_gpu_info_util/moz.build
+++ b/gfx/angle/targets/angle_gpu_info_util/moz.build
@@ -6,17 +6,17 @@ include('../../moz.build.common')
 #DEFINES['CERT_CHAIN_PARA_HAS_EXTRA_FIELDS'] = True
 #DEFINES['CHROMIUM_BUILD'] = True
 #DEFINES['COMPONENT_BUILD'] = True
 DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1'
 #DEFINES['FULL_SAFE_BROWSING'] = True
 DEFINES['GPU_INFO_USE_SETUPAPI'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -40,16 +40,17 @@ LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
     '../../checkout/src/common/third_party/base/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
--- a/gfx/angle/targets/angle_image_util/moz.build
+++ b/gfx/angle/targets/angle_image_util/moz.build
@@ -5,17 +5,17 @@ include('../../moz.build.common')
 #DEFINES['ANGLE_IS_64_BIT_CPU'] = True
 #DEFINES['CERT_CHAIN_PARA_HAS_EXTRA_FIELDS'] = True
 #DEFINES['CHROMIUM_BUILD'] = True
 #DEFINES['COMPONENT_BUILD'] = True
 DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1'
 #DEFINES['FULL_SAFE_BROWSING'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -39,16 +39,17 @@ LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
     '../../checkout/src/common/third_party/base/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
--- a/gfx/angle/targets/libANGLE/moz.build
+++ b/gfx/angle/targets/libANGLE/moz.build
@@ -11,17 +11,17 @@ DEFINES['ANGLE_GENERATE_SHADER_DEBUG_INF
 #DEFINES['CERT_CHAIN_PARA_HAS_EXTRA_FIELDS'] = True
 #DEFINES['CHROMIUM_BUILD'] = True
 #DEFINES['COMPONENT_BUILD'] = True
 DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1'
 #DEFINES['FULL_SAFE_BROWSING'] = True
 DEFINES['LIBANGLE_IMPLEMENTATION'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -45,16 +45,17 @@ LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
     '../../checkout/src/common/third_party/base/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
--- a/gfx/angle/targets/libEGL/moz.build
+++ b/gfx/angle/targets/libEGL/moz.build
@@ -15,17 +15,17 @@ DEFINES['EGLAPI'] = ''
 DEFINES['EGL_EGLEXT_PROTOTYPES'] = True
 DEFINES['EGL_EGL_PROTOTYPES'] = '1'
 #DEFINES['FULL_SAFE_BROWSING'] = True
 DEFINES['GL_GLES_PROTOTYPES'] = '1'
 DEFINES['GL_GLEXT_PROTOTYPES'] = True
 DEFINES['LIBEGL_IMPLEMENTATION'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -48,16 +48,17 @@ DEFINES['_UNICODE'] = True
 LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
@@ -159,16 +160,17 @@ OS_LIBS += [
     'winmm',
     'winspool',
     'ws2_32',
 ]
 
 #LDFLAGS += [
 #    '/DEBUG',
 #    '/DYNAMICBASE',
+#    '/fastfail',
 #    '/FIXED:NO',
 #    '/ignore:4199',
 #    '/ignore:4221',
 #    '/INCREMENTAL',
 #    '/MACHINE:X64',
 #    '/NXCOMPAT',
 #    '/pdbaltpath:%_PDB%',
 #    '/SUBSYSTEM:CONSOLE,5.02',
--- a/gfx/angle/targets/libGLESv2/moz.build
+++ b/gfx/angle/targets/libGLESv2/moz.build
@@ -16,17 +16,17 @@ DEFINES['EGL_EGL_PROTOTYPES'] = '1'
 DEFINES['GL_API'] = ''
 DEFINES['GL_APICALL'] = ''
 DEFINES['GL_GLES_PROTOTYPES'] = '1'
 DEFINES['GL_GLEXT_PROTOTYPES'] = True
 DEFINES['LIBANGLE_IMPLEMENTATION'] = True
 DEFINES['LIBGLESV2_IMPLEMENTATION'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -50,16 +50,17 @@ LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
     '../../checkout/src/common/third_party/base/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
@@ -167,16 +168,17 @@ OS_LIBS += [
     'winmm',
     'winspool',
     'ws2_32',
 ]
 
 #LDFLAGS += [
 #    '/DEBUG',
 #    '/DYNAMICBASE',
+#    '/fastfail',
 #    '/FIXED:NO',
 #    '/ignore:4199',
 #    '/ignore:4221',
 #    '/INCREMENTAL',
 #    '/MACHINE:X64',
 #    '/NXCOMPAT',
 #    '/pdbaltpath:%_PDB%',
 #    '/SUBSYSTEM:CONSOLE,5.02',
--- a/gfx/angle/targets/preprocessor/moz.build
+++ b/gfx/angle/targets/preprocessor/moz.build
@@ -5,17 +5,17 @@ include('../../moz.build.common')
 #DEFINES['ANGLE_IS_64_BIT_CPU'] = True
 #DEFINES['CERT_CHAIN_PARA_HAS_EXTRA_FIELDS'] = True
 #DEFINES['CHROMIUM_BUILD'] = True
 #DEFINES['COMPONENT_BUILD'] = True
 DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1'
 #DEFINES['FULL_SAFE_BROWSING'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -39,16 +39,17 @@ LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
     '../../checkout/src/common/third_party/base/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
--- a/gfx/angle/targets/translator/moz.build
+++ b/gfx/angle/targets/translator/moz.build
@@ -8,17 +8,17 @@ DEFINES['ANGLE_ENABLE_HLSL'] = True
 #DEFINES['ANGLE_IS_64_BIT_CPU'] = True
 #DEFINES['CERT_CHAIN_PARA_HAS_EXTRA_FIELDS'] = True
 #DEFINES['CHROMIUM_BUILD'] = True
 #DEFINES['COMPONENT_BUILD'] = True
 DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1'
 #DEFINES['FULL_SAFE_BROWSING'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -42,16 +42,17 @@ LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
     '../../checkout/src/common/third_party/base/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',