// // Copyright 2018 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // BlendFuncExtendedTest // Test EXT_blend_func_extended #include "test_utils/ANGLETest.h" #include "util/shader_utils.h" #include #include #include using namespace angle; namespace { // Partial implementation of weight function for GLES 2 blend equation that // is dual-source aware. template float Weight(const float /*dst*/[4], const float src[4], const float src1[4]) { if (factor == GL_SRC_COLOR) return src[index]; if (factor == GL_SRC_ALPHA) return src[3]; if (factor == GL_SRC1_COLOR_EXT) return src1[index]; if (factor == GL_SRC1_ALPHA_EXT) return src1[3]; if (factor == GL_ONE_MINUS_SRC1_COLOR_EXT) return 1.0f - src1[index]; if (factor == GL_ONE_MINUS_SRC1_ALPHA_EXT) return 1.0f - src1[3]; return 0.0f; } GLubyte ScaleChannel(float weight) { return static_cast(std::floor(std::max(0.0f, std::min(1.0f, weight)) * 255.0f)); } // Implementation of GLES 2 blend equation that is dual-source aware. template void BlendEquationFuncAdd(const float dst[4], const float src[4], const float src1[4], angle::GLColor *result) { float r[4]; r[0] = src[0] * Weight(dst, src, src1) + dst[0] * Weight(dst, src, src1); r[1] = src[1] * Weight(dst, src, src1) + dst[1] * Weight(dst, src, src1); r[2] = src[2] * Weight(dst, src, src1) + dst[2] * Weight(dst, src, src1); r[3] = src[3] * Weight(dst, src, src1) + dst[3] * Weight(dst, src, src1); result->R = ScaleChannel(r[0]); result->G = ScaleChannel(r[1]); result->B = ScaleChannel(r[2]); result->A = ScaleChannel(r[3]); } void CheckPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLint tolerance, const angle::GLColor &color) { for (GLint yy = 0; yy < height; ++yy) { for (GLint xx = 0; xx < width; ++xx) { const auto px = x + xx; const auto py = y + yy; EXPECT_PIXEL_COLOR_NEAR(px, py, color, 1); } } } const GLuint kWidth = 100; const GLuint kHeight = 100; class EXTBlendFuncExtendedTest : public ANGLETest {}; class EXTBlendFuncExtendedTestES3 : public ANGLETest {}; class EXTBlendFuncExtendedDrawTest : public ANGLETest { protected: EXTBlendFuncExtendedDrawTest() : mProgram(0) { setWindowWidth(kWidth); setWindowHeight(kHeight); setConfigRedBits(8); setConfigGreenBits(8); setConfigBlueBits(8); setConfigAlphaBits(8); } void testSetUp() override { glGenBuffers(1, &mVBO); glBindBuffer(GL_ARRAY_BUFFER, mVBO); static const float vertices[] = { 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, }; glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); ASSERT_GL_NO_ERROR(); } void testTearDown() override { glDeleteBuffers(1, &mVBO); if (mProgram) { glDeleteProgram(mProgram); } ASSERT_GL_NO_ERROR(); } void makeProgram(const char *vertSource, const char *fragSource) { mProgram = CompileProgram(vertSource, fragSource); ASSERT_NE(0u, mProgram); } void drawTest() { glUseProgram(mProgram); GLint position = glGetAttribLocation(mProgram, essl1_shaders::PositionAttrib()); GLint src0 = glGetUniformLocation(mProgram, "src0"); GLint src1 = glGetUniformLocation(mProgram, "src1"); ASSERT_GL_NO_ERROR(); glBindBuffer(GL_ARRAY_BUFFER, mVBO); glEnableVertexAttribArray(position); glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 0, 0); ASSERT_GL_NO_ERROR(); static const float kDst[4] = {0.5f, 0.5f, 0.5f, 0.5f}; static const float kSrc0[4] = {1.0f, 1.0f, 1.0f, 1.0f}; static const float kSrc1[4] = {0.3f, 0.6f, 0.9f, 0.7f}; glUniform4f(src0, kSrc0[0], kSrc0[1], kSrc0[2], kSrc0[3]); glUniform4f(src1, kSrc1[0], kSrc1[1], kSrc1[2], kSrc1[3]); ASSERT_GL_NO_ERROR(); glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); glViewport(0, 0, kWidth, kHeight); glClearColor(kDst[0], kDst[1], kDst[2], kDst[3]); ASSERT_GL_NO_ERROR(); { glBlendFuncSeparate(GL_SRC1_COLOR_EXT, GL_SRC_ALPHA, GL_ONE_MINUS_SRC1_COLOR_EXT, GL_ONE_MINUS_SRC1_ALPHA_EXT); glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLES, 0, 6); ASSERT_GL_NO_ERROR(); // verify angle::GLColor color; BlendEquationFuncAdd(kDst, kSrc0, kSrc1, &color); CheckPixels(kWidth / 4, (3 * kHeight) / 4, 1, 1, 1, color); CheckPixels(kWidth - 1, 0, 1, 1, 1, color); } { glBlendFuncSeparate(GL_ONE_MINUS_SRC1_COLOR_EXT, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR, GL_SRC1_ALPHA_EXT); glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLES, 0, 6); ASSERT_GL_NO_ERROR(); // verify angle::GLColor color; BlendEquationFuncAdd(kDst, kSrc0, kSrc1, &color); CheckPixels(kWidth / 4, (3 * kHeight) / 4, 1, 1, 1, color); CheckPixels(kWidth - 1, 0, 1, 1, 1, color); } } GLuint mVBO; GLuint mProgram; }; class EXTBlendFuncExtendedDrawTestES3 : public EXTBlendFuncExtendedDrawTest { protected: EXTBlendFuncExtendedDrawTestES3() : EXTBlendFuncExtendedDrawTest(), mIsES31OrNewer(false) {} void testSetUp() override { EXTBlendFuncExtendedDrawTest::testSetUp(); if (getClientMajorVersion() > 3 || (getClientMajorVersion() == 3 && getClientMinorVersion() >= 1)) { mIsES31OrNewer = true; } } void checkOutputIndexQuery(const char *name, GLint expectedIndex) { GLint index = glGetFragDataIndexEXT(mProgram, name); EXPECT_EQ(expectedIndex, index); if (mIsES31OrNewer) { index = glGetProgramResourceLocationIndexEXT(mProgram, GL_PROGRAM_OUTPUT, name); EXPECT_EQ(expectedIndex, index); } else { glGetProgramResourceLocationIndexEXT(mProgram, GL_PROGRAM_OUTPUT, name); EXPECT_GL_ERROR(GL_INVALID_OPERATION); } } void LinkProgram() { glLinkProgram(mProgram); GLint linked = 0; glGetProgramiv(mProgram, GL_LINK_STATUS, &linked); EXPECT_NE(0, linked); glUseProgram(mProgram); return; } private: bool mIsES31OrNewer; }; } // namespace // Test EXT_blend_func_extended related gets. TEST_P(EXTBlendFuncExtendedTest, TestMaxDualSourceDrawBuffers) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended")); GLint maxDualSourceDrawBuffers = 0; glGetIntegerv(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT, &maxDualSourceDrawBuffers); EXPECT_GT(maxDualSourceDrawBuffers, 0); ASSERT_GL_NO_ERROR(); } // Test a shader with EXT_blend_func_extended and gl_SecondaryFragColorEXT. // Outputs to primary color buffer using primary and secondary colors. TEST_P(EXTBlendFuncExtendedDrawTest, FragColor) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended")); const char *kFragColorShader = "#extension GL_EXT_blend_func_extended : require\n" "precision mediump float;\n" "uniform vec4 src0;\n" "uniform vec4 src1;\n" "void main() {\n" " gl_FragColor = src0;\n" " gl_SecondaryFragColorEXT = src1;\n" "}\n"; makeProgram(essl1_shaders::vs::Simple(), kFragColorShader); drawTest(); } // Test a shader with EXT_blend_func_extended and gl_FragData. // Outputs to a color buffer using primary and secondary frag data. TEST_P(EXTBlendFuncExtendedDrawTest, FragData) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended")); const char *kFragColorShader = "#extension GL_EXT_blend_func_extended : require\n" "precision mediump float;\n" "uniform vec4 src0;\n" "uniform vec4 src1;\n" "void main() {\n" " gl_FragData[0] = src0;\n" " gl_SecondaryFragDataEXT[0] = src1;\n" "}\n"; makeProgram(essl1_shaders::vs::Simple(), kFragColorShader); drawTest(); } // Test an ESSL 3.00 shader that uses two fragment outputs with locations specified in the shader. TEST_P(EXTBlendFuncExtendedDrawTestES3, FragmentOutputLocationsInShader) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended")); const char *kFragColorShader = R"(#version 300 es #extension GL_EXT_blend_func_extended : require precision mediump float; uniform vec4 src0; uniform vec4 src1; layout(location = 0, index = 1) out vec4 outSrc1; layout(location = 0, index = 0) out vec4 outSrc0; void main() { outSrc0 = src0; outSrc1 = src1; })"; makeProgram(essl3_shaders::vs::Simple(), kFragColorShader); checkOutputIndexQuery("outSrc0", 0); checkOutputIndexQuery("outSrc1", 1); drawTest(); } // Test an ESSL 3.00 shader that uses two fragment outputs with locations specified through the API. TEST_P(EXTBlendFuncExtendedDrawTestES3, FragmentOutputLocationAPI) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended")); constexpr char kFS[] = R"(#version 300 es #extension GL_EXT_blend_func_extended : require precision mediump float; uniform vec4 src0; uniform vec4 src1; out vec4 outSrc1; out vec4 outSrc0; void main() { outSrc0 = src0; outSrc1 = src1; })"; mProgram = CompileProgram(essl3_shaders::vs::Simple(), kFS, [](GLuint program) { glBindFragDataLocationIndexedEXT(program, 0, 0, "outSrc0"); glBindFragDataLocationIndexedEXT(program, 0, 1, "outSrc1"); }); ASSERT_NE(0u, mProgram); checkOutputIndexQuery("outSrc0", 0); checkOutputIndexQuery("outSrc1", 1); drawTest(); } // Test an ESSL 3.00 shader that uses two fragment outputs, with location for one specified through // the API and location for another being set automatically. TEST_P(EXTBlendFuncExtendedDrawTestES3, FragmentOutputLocationsAPIAndAutomatic) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended")); constexpr char kFS[] = R"(#version 300 es #extension GL_EXT_blend_func_extended : require precision mediump float; uniform vec4 src0; uniform vec4 src1; out vec4 outSrc1; out vec4 outSrc0; void main() { outSrc0 = src0; outSrc1 = src1; })"; mProgram = CompileProgram(essl3_shaders::vs::Simple(), kFS, [](GLuint program) { glBindFragDataLocationIndexedEXT(program, 0, 1, "outSrc1"); }); ASSERT_NE(0u, mProgram); checkOutputIndexQuery("outSrc0", 0); checkOutputIndexQuery("outSrc1", 1); drawTest(); } // Test an ESSL 3.00 shader that uses two array fragment outputs with locations specified through // the API. TEST_P(EXTBlendFuncExtendedDrawTestES3, FragmentArrayOutputLocationsAPI) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended")); // TODO: Investigate this mac-only failure. http://angleproject.com/1085 ANGLE_SKIP_TEST_IF(IsOSX()); constexpr char kFS[] = R"(#version 300 es #extension GL_EXT_blend_func_extended : require precision mediump float; uniform vec4 src0; uniform vec4 src1; out vec4 outSrc1[1]; out vec4 outSrc0[1]; void main() { outSrc0[0] = src0; outSrc1[0] = src1; })"; mProgram = CompileProgram(essl3_shaders::vs::Simple(), kFS, [](GLuint program) { // Specs aren't very clear on what kind of name should be used when binding location for // array variables. We only allow names that do include the "[0]" suffix. glBindFragDataLocationIndexedEXT(program, 0, 0, "outSrc0[0]"); glBindFragDataLocationIndexedEXT(program, 0, 1, "outSrc1[0]"); }); ASSERT_NE(0u, mProgram); // The extension spec is not very clear on what name can be used for the queries for array // variables. We're checking that the queries work in the same way as specified in OpenGL 4.4 // page 107. checkOutputIndexQuery("outSrc0[0]", 0); checkOutputIndexQuery("outSrc1[0]", 1); checkOutputIndexQuery("outSrc0", 0); checkOutputIndexQuery("outSrc1", 1); // These queries use an out of range array index so they should return -1. checkOutputIndexQuery("outSrc0[1]", -1); checkOutputIndexQuery("outSrc1[1]", -1); drawTest(); } // Ported from TranslatorVariants/EXTBlendFuncExtendedES3DrawTest // Test that tests glBindFragDataLocationEXT, glBindFragDataLocationIndexedEXT, // glGetFragDataLocation, glGetFragDataIndexEXT work correctly with // GLSL array output variables. The output variable can be bound by // referring to the variable name with or without the first element array // accessor. The getters can query location of the individual elements in // the array. The test does not actually use the base test drawing, // since the drivers at the time of writing do not support multiple // buffers and dual source blending. TEST_P(EXTBlendFuncExtendedDrawTestES3, ES3GettersArray) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended")); // TODO(zmo): Figure out why this fails on AMD. crbug.com/585132. // Also fails on the Intel Mesa driver, see // https://bugs.freedesktop.org/show_bug.cgi?id=96765 ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD()); ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel()); const GLint kTestArraySize = 2; const GLint kFragData0Location = 2; const GLint kFragData1Location = 1; const GLint kUnusedLocation = 5; // The test binds kTestArraySize -sized array to location 1 for test purposes. // The GL_MAX_DRAW_BUFFERS must be > kTestArraySize, since an // array will be bound to continuous locations, starting from the first // location. GLint maxDrawBuffers = 0; glGetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &maxDrawBuffers); EXPECT_LT(kTestArraySize, maxDrawBuffers); constexpr char kFragColorShader[] = R"(#version 300 es #extension GL_EXT_blend_func_extended : require precision mediump float; uniform vec4 src; uniform vec4 src1; out vec4 FragData[2]; void main() { FragData[0] = src; FragData[1] = src1; })"; struct testCase { std::string unusedLocationName; std::string fragData0LocationName; std::string fragData1LocationName; }; testCase testCases[4]{{"FragData[0]", "FragData", "FragData[1]"}, {"FragData", "FragData[0]", "FragData[1]"}, {"FragData[0]", "FragData", "FragData[1]"}, {"FragData", "FragData[0]", "FragData[1]"}}; for (const testCase &test : testCases) { mProgram = CompileProgram(essl3_shaders::vs::Simple(), kFragColorShader, [&](GLuint program) { glBindFragDataLocationEXT(program, kUnusedLocation, test.unusedLocationName.c_str()); glBindFragDataLocationEXT(program, kFragData0Location, test.fragData0LocationName.c_str()); glBindFragDataLocationEXT(program, kFragData1Location, test.fragData1LocationName.c_str()); }); EXPECT_EQ(static_cast(GL_NO_ERROR), glGetError()); LinkProgram(); EXPECT_EQ(kFragData0Location, glGetFragDataLocation(mProgram, "FragData")); EXPECT_EQ(0, glGetFragDataIndexEXT(mProgram, "FragData")); EXPECT_EQ(kFragData0Location, glGetFragDataLocation(mProgram, "FragData[0]")); EXPECT_EQ(0, glGetFragDataIndexEXT(mProgram, "FragData[0]")); EXPECT_EQ(kFragData1Location, glGetFragDataLocation(mProgram, "FragData[1]")); EXPECT_EQ(0, glGetFragDataIndexEXT(mProgram, "FragData[1]")); // Index bigger than the GLSL variable array length does not find anything. EXPECT_EQ(-1, glGetFragDataLocation(mProgram, "FragData[3]")); } } // Ported from TranslatorVariants/EXTBlendFuncExtendedES3DrawTest TEST_P(EXTBlendFuncExtendedDrawTestES3, ESSL3BindSimpleVarAsArrayNoBind) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended")); constexpr char kFragDataShader[] = R"(#version 300 es #extension GL_EXT_blend_func_extended : require precision mediump float; uniform vec4 src; uniform vec4 src1; out vec4 FragData; out vec4 SecondaryFragData; void main() { FragData = src; SecondaryFragData = src1; })"; mProgram = CompileProgram(essl3_shaders::vs::Simple(), kFragDataShader, [](GLuint program) { glBindFragDataLocationEXT(program, 0, "FragData[0]"); glBindFragDataLocationIndexedEXT(program, 0, 1, "SecondaryFragData[0]"); }); LinkProgram(); EXPECT_EQ(-1, glGetFragDataLocation(mProgram, "FragData[0]")); EXPECT_EQ(0, glGetFragDataLocation(mProgram, "FragData")); EXPECT_EQ(1, glGetFragDataLocation(mProgram, "SecondaryFragData")); // Did not bind index. EXPECT_EQ(0, glGetFragDataIndexEXT(mProgram, "SecondaryFragData")); glBindFragDataLocationEXT(mProgram, 0, "FragData"); glBindFragDataLocationIndexedEXT(mProgram, 0, 1, "SecondaryFragData"); LinkProgram(); } // Test an ESSL 3.00 program with a link-time fragment output location conflict. TEST_P(EXTBlendFuncExtendedTestES3, FragmentOutputLocationConflict) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended")); constexpr char kFS[] = R"(#version 300 es #extension GL_EXT_blend_func_extended : require precision mediump float; uniform vec4 src0; uniform vec4 src1; out vec4 out0; out vec4 out1; void main() { out0 = src0; out1 = src1; })"; GLuint vs = CompileShader(GL_VERTEX_SHADER, essl3_shaders::vs::Simple()); GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kFS); ASSERT_NE(0u, vs); ASSERT_NE(0u, fs); GLuint program = glCreateProgram(); glAttachShader(program, vs); glDeleteShader(vs); glAttachShader(program, fs); glDeleteShader(fs); glBindFragDataLocationIndexedEXT(program, 0, 0, "out0"); glBindFragDataLocationIndexedEXT(program, 0, 0, "out1"); // The program should fail to link. glLinkProgram(program); GLint linkStatus; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_EQ(0, linkStatus); glDeleteProgram(program); } // Test an ESSL 3.00 program with some bindings set for nonexistent variables. These should not // create link-time conflicts. TEST_P(EXTBlendFuncExtendedTestES3, FragmentOutputLocationForNonexistentOutput) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended")); constexpr char kFS[] = R"(#version 300 es #extension GL_EXT_blend_func_extended : require precision mediump float; uniform vec4 src0; out vec4 out0; void main() { out0 = src0; })"; GLuint vs = CompileShader(GL_VERTEX_SHADER, essl3_shaders::vs::Simple()); GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kFS); ASSERT_NE(0u, vs); ASSERT_NE(0u, fs); GLuint program = glCreateProgram(); glAttachShader(program, vs); glDeleteShader(vs); glAttachShader(program, fs); glDeleteShader(fs); glBindFragDataLocationIndexedEXT(program, 0, 0, "out0"); glBindFragDataLocationIndexedEXT(program, 0, 0, "out1"); glBindFragDataLocationIndexedEXT(program, 0, 0, "out2[0]"); // The program should link successfully - conflicting location for nonexistent variables out1 or // out2 should not be an issue. glLinkProgram(program); GLint linkStatus; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_NE(0, linkStatus); glDeleteProgram(program); } // Test mixing shader-assigned and automatic output locations. TEST_P(EXTBlendFuncExtendedTestES3, FragmentOutputLocationsPartiallyAutomatic) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended")); GLint maxDrawBuffers; glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); ANGLE_SKIP_TEST_IF(maxDrawBuffers < 4); constexpr char kFS[] = R"(#version 300 es #extension GL_EXT_blend_func_extended : require precision mediump float; uniform vec4 src0; uniform vec4 src1; uniform vec4 src2; uniform vec4 src3; layout(location=0) out vec4 out0; layout(location=3) out vec4 out3; out vec4 out12[2]; void main() { out0 = src0; out12[0] = src1; out12[1] = src2; out3 = src3; })"; GLuint program = CompileProgram(essl3_shaders::vs::Simple(), kFS); ASSERT_NE(0u, program); GLint location = glGetFragDataLocation(program, "out0"); EXPECT_EQ(0, location); location = glGetFragDataLocation(program, "out12"); EXPECT_EQ(1, location); location = glGetFragDataLocation(program, "out3"); EXPECT_EQ(3, location); glDeleteProgram(program); } // Test a fragment output array that doesn't fit because contiguous locations are not available. TEST_P(EXTBlendFuncExtendedTestES3, FragmentOutputArrayDoesntFit) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended")); GLint maxDrawBuffers; glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); ANGLE_SKIP_TEST_IF(maxDrawBuffers < 4); std::stringstream fragShader; fragShader << R"(#version 300 es #extension GL_EXT_blend_func_extended : require precision mediump float; layout(location=2) out vec4 out0; out vec4 outArray[)" << (maxDrawBuffers - 1) << R"(]; void main() { out0 = vec4(1.0); outArray[0] = vec4(1.0); })"; GLuint vs = CompileShader(GL_VERTEX_SHADER, essl3_shaders::vs::Simple()); GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fragShader.str().c_str()); ASSERT_NE(0u, vs); ASSERT_NE(0u, fs); GLuint program = glCreateProgram(); glAttachShader(program, vs); glDeleteShader(vs); glAttachShader(program, fs); glDeleteShader(fs); // The program should not link - there's no way to fit "outArray" into available output // locations. glLinkProgram(program); GLint linkStatus; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_EQ(0, linkStatus); glDeleteProgram(program); } ANGLE_INSTANTIATE_TEST_ES2(EXTBlendFuncExtendedTest); ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(EXTBlendFuncExtendedTestES3); ANGLE_INSTANTIATE_TEST_ES2(EXTBlendFuncExtendedDrawTest); ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(EXTBlendFuncExtendedDrawTestES3);