Commit ab5fb5ed by Olli Etuaho Committed by Commit Bot

Reland "Support EXT_blend_func_extended in the GLES2 context"

This re-lands EXT_blend_func_extended implementation. A lot of the implementation has been rewritten on top of changes that have been done since the last attempt. For now we only expose the extension on desktop GL. To support GLES, we'd need to investigate what's the most robust way to handle the compiler output and make the binding of the fragment outputs conditional. The extension is disabled on AMD and Intel because of test failures. BUG=angleproject:1085 TEST=angle_end2end_tests Change-Id: I619ae3162769b90aad095ddec158ce6c57a114a8 Reviewed-on: https://chromium-review.googlesource.com/1233713Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent 38fe6840
...@@ -251,7 +251,9 @@ Extensions::Extensions() ...@@ -251,7 +251,9 @@ Extensions::Extensions()
explicitContext(false), explicitContext(false),
parallelShaderCompile(false), parallelShaderCompile(false),
textureStorageMultisample2DArray(false), textureStorageMultisample2DArray(false),
multiviewMultisample(false) multiviewMultisample(false),
blendFuncExtended(false),
maxDualSourceDrawBuffers(0)
{ {
} }
...@@ -858,6 +860,7 @@ const ExtensionInfoMap &GetExtensionInfoMap() ...@@ -858,6 +860,7 @@ const ExtensionInfoMap &GetExtensionInfoMap()
map["GL_KHR_parallel_shader_compile"] = enableableExtension(&Extensions::parallelShaderCompile); map["GL_KHR_parallel_shader_compile"] = enableableExtension(&Extensions::parallelShaderCompile);
map["GL_OES_texture_storage_multisample_2d_array"] = enableableExtension(&Extensions::textureStorageMultisample2DArray); map["GL_OES_texture_storage_multisample_2d_array"] = enableableExtension(&Extensions::textureStorageMultisample2DArray);
map["GL_ANGLE_multiview_multisample"] = enableableExtension(&Extensions::multiviewMultisample); map["GL_ANGLE_multiview_multisample"] = enableableExtension(&Extensions::multiviewMultisample);
map["GL_EXT_blend_func_extended"] = enableableExtension(&Extensions::blendFuncExtended);
// GLES1 extensinos // GLES1 extensinos
map["GL_OES_point_size_array"] = enableableExtension(&Extensions::pointSizeArray); map["GL_OES_point_size_array"] = enableableExtension(&Extensions::pointSizeArray);
map["GL_OES_texture_cube_map"] = enableableExtension(&Extensions::textureCubeMap); map["GL_OES_texture_cube_map"] = enableableExtension(&Extensions::textureCubeMap);
......
...@@ -432,6 +432,10 @@ struct Extensions ...@@ -432,6 +432,10 @@ struct Extensions
// GL_ANGLE_multiview_multisample // GL_ANGLE_multiview_multisample
bool multiviewMultisample; bool multiviewMultisample;
// GL_EXT_blend_func_extended
bool blendFuncExtended;
GLuint maxDualSourceDrawBuffers;
}; };
struct ExtensionInfo struct ExtensionInfo
......
...@@ -99,6 +99,10 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const ContextState &state) ...@@ -99,6 +99,10 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const ContextState &state)
mResources.MinProgramTexelOffset = caps.minProgramTexelOffset; mResources.MinProgramTexelOffset = caps.minProgramTexelOffset;
mResources.MaxProgramTexelOffset = caps.maxProgramTexelOffset; mResources.MaxProgramTexelOffset = caps.maxProgramTexelOffset;
// EXT_blend_func_extended
mResources.EXT_blend_func_extended = extensions.blendFuncExtended;
mResources.MaxDualSourceDrawBuffers = extensions.maxDualSourceDrawBuffers;
// GLSL ES 3.1 constants // GLSL ES 3.1 constants
mResources.MaxProgramTextureGatherOffset = caps.maxProgramTextureGatherOffset; mResources.MaxProgramTextureGatherOffset = caps.maxProgramTextureGatherOffset;
mResources.MinProgramTextureGatherOffset = caps.minProgramTextureGatherOffset; mResources.MinProgramTextureGatherOffset = caps.minProgramTextureGatherOffset;
......
...@@ -1854,6 +1854,11 @@ void Context::getIntegervImpl(GLenum pname, GLint *params) ...@@ -1854,6 +1854,11 @@ void Context::getIntegervImpl(GLenum pname, GLint *params)
*params = mGLState.getMaxShaderCompilerThreads(); *params = mGLState.getMaxShaderCompilerThreads();
break; break;
// GL_EXT_blend_func_extended
case GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT:
*params = mExtensions.maxDualSourceDrawBuffers;
break;
default: default:
handleError(mGLState.getIntegerv(this, pname, params)); handleError(mGLState.getIntegerv(this, pname, params));
break; break;
...@@ -3176,6 +3181,11 @@ Extensions Context::generateSupportedExtensions() const ...@@ -3176,6 +3181,11 @@ Extensions Context::generateSupportedExtensions() const
{ {
// FIXME(geofflang): Don't support EXT_sRGB in non-ES2 contexts // FIXME(geofflang): Don't support EXT_sRGB in non-ES2 contexts
// supportedExtensions.sRGB = false; // supportedExtensions.sRGB = false;
// EXT_blend_func_extended is only implemented against GLES2 now
// TODO(http://anglebug.com/1085): remove this limitation once GLES3 binding API for the
// outputs is in place.
supportedExtensions.blendFuncExtended = false;
} }
// Some extensions are always available because they are implemented in the GL layer. // Some extensions are always available because they are implemented in the GL layer.
...@@ -7203,6 +7213,13 @@ bool Context::getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *nu ...@@ -7203,6 +7213,13 @@ bool Context::getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *nu
return true; return true;
} }
if (getExtensions().blendFuncExtended && pname == GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT)
{
*type = GL_INT;
*numParams = 1;
return true;
}
// Check for ES3.0+ parameter names which are also exposed as ES2 extensions // Check for ES3.0+ parameter names which are also exposed as ES2 extensions
switch (pname) switch (pname)
{ {
......
...@@ -212,6 +212,51 @@ gl::LinkResult ProgramGL::linkImpl(const gl::Context *context, ...@@ -212,6 +212,51 @@ gl::LinkResult ProgramGL::linkImpl(const gl::Context *context,
attribute.mappedName.c_str()); attribute.mappedName.c_str());
} }
// EXT_blend_func_extended.
// Bind the (transformed) secondary fragment color outputs.
//
// TODO(http://anglebug.com/2833): The bind done below is only valid in case the compiler
// transforms the shader outputs to the angle/webgl prefixed ones. If we added support
// for running EXT_blend_func_extended on top of GLES, some changes would be required:
// - If we're backed by GLES 2.0, we shouldn't do the bind because it's not needed.
// - If we're backed by GLES 3.0+, it's a bit unclear what should happen. Currently
// the compiler doesn't support transforming GLSL ES 1.00 shaders to GLSL ES 3.00
// shaders in general, but support for that might be required. Or we might be able
// to skip the bind in case the compiler outputs GLSL ES 1.00.
//
const auto &shaderOutputs =
mState.getAttachedShader(gl::ShaderType::Fragment)->getActiveOutputVariables();
for (const auto &output : shaderOutputs)
{
if (output.name == "gl_SecondaryFragColorEXT")
{
mFunctions->bindFragDataLocationIndexed(mProgramID, 0, 0, "webgl_FragColor");
mFunctions->bindFragDataLocationIndexed(mProgramID, 0, 1,
"angle_SecondaryFragColor");
}
else if (output.name == "gl_SecondaryFragDataEXT")
{
// Basically we should have a loop here going over the output
// array binding "webgl_FragData[i]" and "angle_SecondaryFragData[i]" array
// indices to the correct color buffers and color indices.
// However I'm not sure if this construct is legal or not, neither ARB or EXT
// version of the spec mention this.
//
// In practice it seems that binding array members works on some drivers and
// fails on others. One option could be to modify the shader translator to
// expand the arrays into individual output variables instead of using an array.
//
// For now we're going to have a limitation of assuming that
// GL_MAX_DUAL_SOURCE_DRAW_BUFFERS is *always* 1 and then only bind the basename
// of the variable ignoring any indices. This appears to work uniformly.
ASSERT(output.isArray() && output.getOutermostArraySize() == 1);
mFunctions->bindFragDataLocationIndexed(mProgramID, 0, 0, "webgl_FragData");
mFunctions->bindFragDataLocationIndexed(mProgramID, 0, 1,
"angle_SecondaryFragData");
}
}
// Link and verify // Link and verify
mFunctions->linkProgram(mProgramID); mFunctions->linkProgram(mProgramID);
......
...@@ -152,6 +152,12 @@ struct WorkaroundsGL ...@@ -152,6 +152,12 @@ struct WorkaroundsGL
// GLSL user-defined function have incorrect results. Rewrite this type of statements to fix // GLSL user-defined function have incorrect results. Rewrite this type of statements to fix
// this. // this.
bool rewriteRepeatedAssignToSwizzled = false; bool rewriteRepeatedAssignToSwizzled = false;
// On some AMD and Intel GL drivers ARB_blend_func_extended does not pass the tests.
// It might be possible to work around the Intel bug by rewriting *FragData to *FragColor
// instead of disabling the functionality entirely. The AMD bug looked like incorrect blending,
// not sure if a workaround is feasible. http://anglebug.com/1085
bool disableBlendFuncExtended = false;
}; };
inline WorkaroundsGL::WorkaroundsGL() = default; inline WorkaroundsGL::WorkaroundsGL() = default;
......
...@@ -1116,6 +1116,20 @@ void GenerateCaps(const FunctionsGL *functions, ...@@ -1116,6 +1116,20 @@ void GenerateCaps(const FunctionsGL *functions,
caps->maxShaderStorageBlocks[gl::ShaderType::Geometry] = caps->maxShaderStorageBlocks[gl::ShaderType::Geometry] =
QuerySingleGLInt(functions, GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT); QuerySingleGLInt(functions, GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT);
} }
// EXT_blend_func_extended.
// Note that this could be implemented also on top of native EXT_blend_func_extended, but it's
// currently not fully implemented.
extensions->blendFuncExtended = !workarounds.disableBlendFuncExtended &&
functions->standard == STANDARD_GL_DESKTOP &&
functions->hasGLExtension("GL_ARB_blend_func_extended");
if (extensions->blendFuncExtended)
{
// TODO(http://anglebug.com/1085): Support greater values of
// MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT queried from the driver. See comments in ProgramGL.cpp
// for more information about this limitation.
extensions->maxDualSourceDrawBuffers = 1;
}
} }
void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workarounds) void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workarounds)
...@@ -1208,6 +1222,8 @@ void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workaround ...@@ -1208,6 +1222,8 @@ void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workaround
workarounds->dontUseLoopsToInitializeVariables = !IsNvidia(vendor); workarounds->dontUseLoopsToInitializeVariables = !IsNvidia(vendor);
#endif #endif
workarounds->disableBlendFuncExtended = IsAMD(vendor) || IsIntel(vendor);
} }
void ApplyWorkarounds(const FunctionsGL *functions, gl::Workarounds *workarounds) void ApplyWorkarounds(const FunctionsGL *functions, gl::Workarounds *workarounds)
......
...@@ -1047,6 +1047,68 @@ bool ValidateMatrixMode(Context *context, GLenum matrixMode) ...@@ -1047,6 +1047,68 @@ bool ValidateMatrixMode(Context *context, GLenum matrixMode)
} }
return true; return true;
} }
bool ValidBlendFunc(const Context *context, GLenum val)
{
const gl::Extensions &ext = context->getExtensions();
// these are always valid for src and dst.
switch (val)
{
case GL_ZERO:
case GL_ONE:
case GL_SRC_COLOR:
case GL_ONE_MINUS_SRC_COLOR:
case GL_DST_COLOR:
case GL_ONE_MINUS_DST_COLOR:
case GL_SRC_ALPHA:
case GL_ONE_MINUS_SRC_ALPHA:
case GL_DST_ALPHA:
case GL_ONE_MINUS_DST_ALPHA:
case GL_CONSTANT_COLOR:
case GL_ONE_MINUS_CONSTANT_COLOR:
case GL_CONSTANT_ALPHA:
case GL_ONE_MINUS_CONSTANT_ALPHA:
return true;
// EXT_blend_func_extended.
case GL_SRC1_COLOR_EXT:
case GL_SRC1_ALPHA_EXT:
case GL_ONE_MINUS_SRC1_COLOR_EXT:
case GL_ONE_MINUS_SRC1_ALPHA_EXT:
case GL_SRC_ALPHA_SATURATE_EXT:
return ext.blendFuncExtended;
default:
return false;
}
}
bool ValidSrcBlendFunc(const Context *context, GLenum val)
{
if (ValidBlendFunc(context, val))
return true;
if (val == GL_SRC_ALPHA_SATURATE)
return true;
return false;
}
bool ValidDstBlendFunc(const Context *context, GLenum val)
{
if (ValidBlendFunc(context, val))
return true;
if (val == GL_SRC_ALPHA_SATURATE)
{
if (context->getClientMajorVersion() >= 3)
return true;
}
return false;
}
} // anonymous namespace } // anonymous namespace
bool ValidateES2TexImageParameters(Context *context, bool ValidateES2TexImageParameters(Context *context,
...@@ -4567,85 +4629,31 @@ bool ValidateBlendFunc(Context *context, GLenum sfactor, GLenum dfactor) ...@@ -4567,85 +4629,31 @@ bool ValidateBlendFunc(Context *context, GLenum sfactor, GLenum dfactor)
return ValidateBlendFuncSeparate(context, sfactor, dfactor, sfactor, dfactor); return ValidateBlendFuncSeparate(context, sfactor, dfactor, sfactor, dfactor);
} }
static bool ValidSrcBlendFunc(GLenum srcBlend)
{
switch (srcBlend)
{
case GL_ZERO:
case GL_ONE:
case GL_SRC_COLOR:
case GL_ONE_MINUS_SRC_COLOR:
case GL_DST_COLOR:
case GL_ONE_MINUS_DST_COLOR:
case GL_SRC_ALPHA:
case GL_ONE_MINUS_SRC_ALPHA:
case GL_DST_ALPHA:
case GL_ONE_MINUS_DST_ALPHA:
case GL_CONSTANT_COLOR:
case GL_ONE_MINUS_CONSTANT_COLOR:
case GL_CONSTANT_ALPHA:
case GL_ONE_MINUS_CONSTANT_ALPHA:
case GL_SRC_ALPHA_SATURATE:
return true;
default:
return false;
}
}
static bool ValidDstBlendFunc(GLenum dstBlend, GLint contextMajorVersion)
{
switch (dstBlend)
{
case GL_ZERO:
case GL_ONE:
case GL_SRC_COLOR:
case GL_ONE_MINUS_SRC_COLOR:
case GL_DST_COLOR:
case GL_ONE_MINUS_DST_COLOR:
case GL_SRC_ALPHA:
case GL_ONE_MINUS_SRC_ALPHA:
case GL_DST_ALPHA:
case GL_ONE_MINUS_DST_ALPHA:
case GL_CONSTANT_COLOR:
case GL_ONE_MINUS_CONSTANT_COLOR:
case GL_CONSTANT_ALPHA:
case GL_ONE_MINUS_CONSTANT_ALPHA:
return true;
case GL_SRC_ALPHA_SATURATE:
return (contextMajorVersion >= 3);
default:
return false;
}
}
bool ValidateBlendFuncSeparate(Context *context, bool ValidateBlendFuncSeparate(Context *context,
GLenum srcRGB, GLenum srcRGB,
GLenum dstRGB, GLenum dstRGB,
GLenum srcAlpha, GLenum srcAlpha,
GLenum dstAlpha) GLenum dstAlpha)
{ {
if (!ValidSrcBlendFunc(srcRGB)) if (!ValidSrcBlendFunc(context, srcRGB))
{ {
ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction); ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction);
return false; return false;
} }
if (!ValidDstBlendFunc(dstRGB, context->getClientMajorVersion())) if (!ValidDstBlendFunc(context, dstRGB))
{ {
ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction); ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction);
return false; return false;
} }
if (!ValidSrcBlendFunc(srcAlpha)) if (!ValidSrcBlendFunc(context, srcAlpha))
{ {
ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction); ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction);
return false; return false;
} }
if (!ValidDstBlendFunc(dstAlpha, context->getClientMajorVersion())) if (!ValidDstBlendFunc(context, dstAlpha))
{ {
ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction); ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction);
return false; return false;
......
...@@ -7,6 +7,7 @@ angle_end2end_tests_sources = [ ...@@ -7,6 +7,7 @@ angle_end2end_tests_sources = [
"gl_tests/AttributeLayoutTest.cpp", "gl_tests/AttributeLayoutTest.cpp",
"gl_tests/BindGeneratesResourceTest.cpp", "gl_tests/BindGeneratesResourceTest.cpp",
"gl_tests/BindUniformLocationTest.cpp", "gl_tests/BindUniformLocationTest.cpp",
"gl_tests/BlendFuncExtendedTest.cpp",
"gl_tests/BlendMinMaxTest.cpp", "gl_tests/BlendMinMaxTest.cpp",
"gl_tests/BlitFramebufferANGLETest.cpp", "gl_tests/BlitFramebufferANGLETest.cpp",
"gl_tests/BufferDataTest.cpp", "gl_tests/BufferDataTest.cpp",
......
//
// 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 "shader_utils.h"
#include <algorithm>
#include <cmath>
#include <fstream>
using namespace angle;
namespace
{
// Partial implementation of weight function for GLES 2 blend equation that
// is dual-source aware.
template <int factor, int index>
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<GLubyte>(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 <int RGBs, int RGBd, int As, int Ad>
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<RGBs, 0>(dst, src, src1) + dst[0] * Weight<RGBd, 0>(dst, src, src1);
r[1] = src[1] * Weight<RGBs, 1>(dst, src, src1) + dst[1] * Weight<RGBd, 1>(dst, src, src1);
r[2] = src[2] * Weight<RGBs, 2>(dst, src, src1) + dst[2] * Weight<RGBd, 2>(dst, src, src1);
r[3] = src[3] * Weight<As, 3>(dst, src, src1) + dst[3] * Weight<Ad, 3>(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 EXTBlendFuncExtendedDrawTest : public ANGLETest
{
protected:
EXTBlendFuncExtendedDrawTest() : mProgram(0)
{
setWindowWidth(kWidth);
setWindowHeight(kHeight);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
void SetUp() override
{
ANGLETest::SetUp();
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 TearDown() override
{
glDeleteBuffers(1, &mVBO);
if (mProgram)
{
glDeleteProgram(mProgram);
}
ASSERT_GL_NO_ERROR();
ANGLETest::TearDown();
}
void makeProgram(const char *vertSource, const char *fragSource)
{
mProgram = CompileProgram(vertSource, fragSource);
ASSERT_NE(0u, mProgram);
}
void drawTest()
{
glUseProgram(mProgram);
GLint position = glGetAttribLocation(mProgram, "position");
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<GL_SRC1_COLOR_EXT, GL_SRC_ALPHA, GL_ONE_MINUS_SRC1_COLOR_EXT,
GL_ONE_MINUS_SRC1_ALPHA_EXT>(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<GL_ONE_MINUS_SRC1_COLOR_EXT, GL_ONE_MINUS_SRC_ALPHA,
GL_ONE_MINUS_SRC_COLOR, GL_SRC1_ALPHA_EXT>(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;
};
} // namespace
// Test EXT_blend_func_extended related gets.
TEST_P(EXTBlendFuncExtendedTest, TestMaxDualSourceDrawBuffers)
{
ANGLE_SKIP_TEST_IF(!extensionEnabled("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(!extensionEnabled("GL_EXT_blend_func_extended"));
const char *kVertexShader =
"attribute vec4 position;\n"
"void main() {\n"
" gl_Position = position;\n"
"}\n";
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(kVertexShader, 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(!extensionEnabled("GL_EXT_blend_func_extended"));
const char *kVertexShader =
"attribute vec4 position;\n"
"void main() {\n"
" gl_Position = position;\n"
"}\n";
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(kVertexShader, kFragColorShader);
drawTest();
}
ANGLE_INSTANTIATE_TEST(EXTBlendFuncExtendedTest,
ES2_OPENGL(),
ES2_OPENGLES(),
ES3_OPENGL(),
ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(EXTBlendFuncExtendedDrawTest, ES2_OPENGL());
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment