Commit 7f539ea5 by Sami Väisänen Committed by Commit Bot

Support EXT_blend_func_extended in the GLES2 context

BUG=angleproject:1379 Change-Id: Ibda6e84c0a7f86e838247cd5c538ef956dd786fe Reviewed-on: https://chromium-review.googlesource.com/346410Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Sami Väisänen <svaisanen@nvidia.com>
parent a8503cef
...@@ -164,7 +164,9 @@ Extensions::Extensions() ...@@ -164,7 +164,9 @@ Extensions::Extensions()
multisampleCompatibility(false), multisampleCompatibility(false),
framebufferMixedSamples(false), framebufferMixedSamples(false),
textureNorm16(false), textureNorm16(false),
pathRendering(false) pathRendering(false),
blendFuncExtended(false),
maxDualSourceDrawBuffers(0)
{ {
} }
...@@ -242,6 +244,7 @@ std::vector<std::string> Extensions::getStrings() const ...@@ -242,6 +244,7 @@ std::vector<std::string> Extensions::getStrings() const
InsertExtensionString("GL_CHROMIUM_framebuffer_mixed_samples", framebufferMixedSamples, &extensionStrings); InsertExtensionString("GL_CHROMIUM_framebuffer_mixed_samples", framebufferMixedSamples, &extensionStrings);
InsertExtensionString("GL_EXT_texture_norm16", textureNorm16, &extensionStrings); InsertExtensionString("GL_EXT_texture_norm16", textureNorm16, &extensionStrings);
InsertExtensionString("GL_CHROMIUM_path_rendering", pathRendering, &extensionStrings); InsertExtensionString("GL_CHROMIUM_path_rendering", pathRendering, &extensionStrings);
InsertExtensionString("GL_EXT_blend_func_extended", blendFuncExtended, &extensionStrings);
// clang-format on // clang-format on
return extensionStrings; return extensionStrings;
......
...@@ -306,6 +306,10 @@ struct Extensions ...@@ -306,6 +306,10 @@ struct Extensions
// GL_CHROMIUM_path_rendering // GL_CHROMIUM_path_rendering
bool pathRendering; bool pathRendering;
// GL_EXT_blend_func_extended
bool blendFuncExtended;
GLuint maxDualSourceDrawBuffers;
}; };
struct Limitations struct Limitations
......
...@@ -52,6 +52,7 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const ContextState &state) ...@@ -52,6 +52,7 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const ContextState &state)
mResources.EXT_shader_texture_lod = extensions.shaderTextureLOD; mResources.EXT_shader_texture_lod = extensions.shaderTextureLOD;
mResources.OES_EGL_image_external = extensions.eglImageExternal; mResources.OES_EGL_image_external = extensions.eglImageExternal;
mResources.OES_EGL_image_external_essl3 = extensions.eglImageExternalEssl3; mResources.OES_EGL_image_external_essl3 = extensions.eglImageExternalEssl3;
mResources.EXT_blend_func_extended = extensions.blendFuncExtended;
mResources.NV_EGL_stream_consumer_external = extensions.eglStreamConsumerExternal; mResources.NV_EGL_stream_consumer_external = extensions.eglStreamConsumerExternal;
// TODO: use shader precision caps to determine if high precision is supported? // TODO: use shader precision caps to determine if high precision is supported?
mResources.FragmentPrecisionHigh = 1; mResources.FragmentPrecisionHigh = 1;
...@@ -62,6 +63,9 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const ContextState &state) ...@@ -62,6 +63,9 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const ContextState &state)
mResources.MaxFragmentInputVectors = caps.maxFragmentInputComponents / 4; mResources.MaxFragmentInputVectors = caps.maxFragmentInputComponents / 4;
mResources.MinProgramTexelOffset = caps.minProgramTexelOffset; mResources.MinProgramTexelOffset = caps.minProgramTexelOffset;
mResources.MaxProgramTexelOffset = caps.maxProgramTexelOffset; mResources.MaxProgramTexelOffset = caps.maxProgramTexelOffset;
// EXT_blend_func_extended
mResources.MaxDualSourceDrawBuffers = extensions.maxDualSourceDrawBuffers;
} }
Compiler::~Compiler() Compiler::~Compiler()
......
...@@ -1282,6 +1282,11 @@ void Context::getIntegerv(GLenum pname, GLint *params) ...@@ -1282,6 +1282,11 @@ void Context::getIntegerv(GLenum pname, GLint *params)
*params = mImplementation->getGPUDisjoint(); *params = mImplementation->getGPUDisjoint();
break; break;
// GL_EXT_blend_func_extended
case GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT:
*params = mExtensions.maxDualSourceDrawBuffers;
break;
default: default:
mGLState.getIntegerv(mState, pname, params); mGLState.getIntegerv(mState, pname, params);
break; break;
...@@ -2083,6 +2088,10 @@ void Context::initCaps() ...@@ -2083,6 +2088,10 @@ void Context::initCaps()
{ {
// FIXME(geofflang): Don't support EXT_sRGB in non-ES2 contexts // FIXME(geofflang): Don't support EXT_sRGB in non-ES2 contexts
//mExtensions.sRGB = false; //mExtensions.sRGB = false;
// EXT_blend_func_extended is only implemented against GLES2 now
// TODO(svaisanen@nvidia.com): remove this limitation once GLES3 support is in place.
mExtensions.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.
......
...@@ -324,6 +324,17 @@ bool ValidationContext::getQueryParameterInfo(GLenum pname, GLenum *type, unsign ...@@ -324,6 +324,17 @@ bool ValidationContext::getQueryParameterInfo(GLenum pname, GLenum *type, unsign
} }
} }
if (getExtensions().blendFuncExtended)
{
switch (pname)
{
case 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)
{ {
......
...@@ -1023,6 +1023,9 @@ void FunctionsGL::initializeProcsDesktopGL() ...@@ -1023,6 +1023,9 @@ void FunctionsGL::initializeProcsDesktopGL()
AssignGLExtensionEntryPoint(extensions, "GL_ARB_get_program_binary", loadProcAddress("glProgramBinary"), &programBinary); AssignGLExtensionEntryPoint(extensions, "GL_ARB_get_program_binary", loadProcAddress("glProgramBinary"), &programBinary);
AssignGLExtensionEntryPoint(extensions, "GL_ARB_get_program_binary", loadProcAddress("glProgramParameteri"), &programParameteri); AssignGLExtensionEntryPoint(extensions, "GL_ARB_get_program_binary", loadProcAddress("glProgramParameteri"), &programParameteri);
// GL_ARB_blend_func_extended (loading only functions relevant to supporting EXT_blend_func_extended)
AssignGLExtensionEntryPoint(extensions, "GL_ARB_blend_func_extended", loadProcAddress("glBindFragDataLocationIndexed"), &bindFragDataLocationIndexed);
// 1.0 // 1.0
if (isAtLeastGL(gl::Version(1, 0))) if (isAtLeastGL(gl::Version(1, 0)))
{ {
...@@ -1885,6 +1888,9 @@ void FunctionsGL::initializeProcsGLES() ...@@ -1885,6 +1888,9 @@ void FunctionsGL::initializeProcsGLES()
AssignGLExtensionEntryPoint(extensions, "GL_OES_get_program_binary", loadProcAddress("glGetProgramBinaryOES"), &getProgramBinary); AssignGLExtensionEntryPoint(extensions, "GL_OES_get_program_binary", loadProcAddress("glGetProgramBinaryOES"), &getProgramBinary);
AssignGLExtensionEntryPoint(extensions, "GL_OES_get_program_binary", loadProcAddress("glProgramBinaryOES"), &programBinary); AssignGLExtensionEntryPoint(extensions, "GL_OES_get_program_binary", loadProcAddress("glProgramBinaryOES"), &programBinary);
// EXT_blend_func_extended (loading only functions relevant to supporting EXT_blend_func_extenteded)
AssignGLExtensionEntryPoint(extensions, "GL_EXT_blend_func_extended", loadProcAddress("glBindFragLocationIndexedEXT"), &bindFragDataLocationIndexed);
// 2.0 // 2.0
if (isAtLeastGLES(gl::Version(2, 0))) if (isAtLeastGLES(gl::Version(2, 0)))
{ {
......
...@@ -142,6 +142,45 @@ LinkResult ProgramGL::link(const gl::ContextState &data, gl::InfoLog &infoLog) ...@@ -142,6 +142,45 @@ LinkResult ProgramGL::link(const gl::ContextState &data, gl::InfoLog &infoLog)
mFunctions->bindAttribLocation(mProgramID, attribute.location, attribute.name.c_str()); mFunctions->bindAttribLocation(mProgramID, attribute.location, attribute.name.c_str());
} }
// EXT_blend_func_extended.
// Bind the (transformed) secondary fragment color outputs.
//
// TODO(svaisanen@nvidia.com) We should probably check the actual context that we're
// using here. If our backing context happens to be only GLES2 then:
// - the shader translator should not transform the gl_SecondaryFragColor/Data
// and thus we shouldn't do the bind because it's not needed and the bind API
// is not even available.
//
const auto &shaderOutputs = mState.getAttachedFragmentShader()->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.arraySize == 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);
......
...@@ -682,7 +682,6 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM ...@@ -682,7 +682,6 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM
functions->hasGLExtension("GL_NV_framebuffer_mixed_samples") || functions->hasGLExtension("GL_NV_framebuffer_mixed_samples") ||
functions->hasGLESExtension("GL_NV_framebuffer_mixed_samples"); functions->hasGLESExtension("GL_NV_framebuffer_mixed_samples");
// NV_path_rendering // NV_path_rendering
// We also need interface query which is available in // We also need interface query which is available in
// >= 4.3 core or ARB_interface_query or >= GLES 3.1 // >= 4.3 core or ARB_interface_query or >= GLES 3.1
...@@ -696,6 +695,16 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM ...@@ -696,6 +695,16 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM
functions->isAtLeastGLES(gl::Version(3, 1)); functions->isAtLeastGLES(gl::Version(3, 1));
extensions->pathRendering = canEnableGLPathRendering || canEnableESPathRendering; extensions->pathRendering = canEnableGLPathRendering || canEnableESPathRendering;
// EXT_blend_func_extended.
// written against OpenGL ES3.1 but can apply to earlier versions.
extensions->blendFuncExtended = functions->hasGLExtension("GL_ARB_blend_func_extended") ||
functions->hasGLESExtension("GL_EXT_blend_func_extended");
if (extensions->blendFuncExtended)
{
extensions->maxDualSourceDrawBuffers =
QuerySingleGLInt(functions, GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT);
}
} }
void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workarounds) void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workarounds)
......
...@@ -99,8 +99,69 @@ bool ValidateDrawAttribs(ValidationContext *context, GLint primcount, GLint maxV ...@@ -99,8 +99,69 @@ bool ValidateDrawAttribs(ValidationContext *context, GLint primcount, GLint maxV
return true; return true;
} }
bool ValidBlendEnum(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;
}
}
} // anonymous namespace } // anonymous namespace
bool ValidSrcBlendEnum(const Context *context, GLenum val)
{
if (ValidBlendEnum(context, val))
return true;
if (val == GL_SRC_ALPHA_SATURATE)
return true;
return false;
}
bool ValidDstBlendEnum(const Context *context, GLenum val)
{
if (ValidBlendEnum(context, val))
return true;
if (val == GL_SRC_ALPHA_SATURATE)
{
if (context->getClientVersion() >= 3)
return true;
}
return false;
}
bool ValidCap(const Context *context, GLenum cap) bool ValidCap(const Context *context, GLenum cap)
{ {
switch (cap) switch (cap)
......
...@@ -27,6 +27,8 @@ class Program; ...@@ -27,6 +27,8 @@ class Program;
class Shader; class Shader;
class ValidationContext; class ValidationContext;
bool ValidSrcBlendEnum(const Context *context, GLenum val);
bool ValidDstBlendEnum(const Context *context, GLenum val);
bool ValidCap(const Context *context, GLenum cap); bool ValidCap(const Context *context, GLenum cap);
bool ValidTextureTarget(const ValidationContext *context, GLenum target); bool ValidTextureTarget(const ValidationContext *context, GLenum target);
bool ValidTexture2DTarget(const ValidationContext *context, GLenum target); bool ValidTexture2DTarget(const ValidationContext *context, GLenum target);
......
...@@ -282,116 +282,17 @@ void GL_APIENTRY BlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha ...@@ -282,116 +282,17 @@ void GL_APIENTRY BlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha
srcRGB, dstRGB, srcAlpha, dstAlpha); srcRGB, dstRGB, srcAlpha, dstAlpha);
Context *context = GetValidGlobalContext(); Context *context = GetValidGlobalContext();
if (context)
{
switch (srcRGB)
{
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:
break;
default:
context->handleError(Error(GL_INVALID_ENUM));
return;
}
switch (dstRGB)
{
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:
break;
case GL_SRC_ALPHA_SATURATE:
if (context->getClientVersion() < 3)
{
context->handleError(Error(GL_INVALID_ENUM));
return;
}
break;
default:
context->handleError(Error(GL_INVALID_ENUM));
return;
}
switch (srcAlpha) if (context)
{ {
case GL_ZERO: if (!context->skipValidation())
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:
break;
default:
context->handleError(Error(GL_INVALID_ENUM));
return;
}
switch (dstAlpha)
{ {
case GL_ZERO: if (!ValidSrcBlendEnum(context, srcRGB) || !ValidSrcBlendEnum(context, srcAlpha) ||
case GL_ONE: !ValidDstBlendEnum(context, dstRGB) || !ValidDstBlendEnum(context, dstAlpha))
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:
break;
case GL_SRC_ALPHA_SATURATE:
if (context->getClientVersion() < 3)
{ {
context->handleError(Error(GL_INVALID_ENUM)); context->handleError(Error(GL_INVALID_ENUM));
return; return;
} }
break;
default:
context->handleError(Error(GL_INVALID_ENUM));
return;
} }
if (context->getLimitations().noSimultaneousConstantColorAndAlphaBlendFunc) if (context->getLimitations().noSimultaneousConstantColorAndAlphaBlendFunc)
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
'angle_end2end_tests_sources': 'angle_end2end_tests_sources':
[ [
'<(angle_path)/src/tests/gl_tests/BindUniformLocationTest.cpp', '<(angle_path)/src/tests/gl_tests/BindUniformLocationTest.cpp',
'<(angle_path)/src/tests/gl_tests/BlendFuncExtendedTest.cpp',
'<(angle_path)/src/tests/gl_tests/BlendMinMaxTest.cpp', '<(angle_path)/src/tests/gl_tests/BlendMinMaxTest.cpp',
'<(angle_path)/src/tests/gl_tests/BlitFramebufferANGLETest.cpp', '<(angle_path)/src/tests/gl_tests/BlitFramebufferANGLETest.cpp',
'<(angle_path)/src/tests/gl_tests/BufferDataTest.cpp', '<(angle_path)/src/tests/gl_tests/BufferDataTest.cpp',
......
//
// Copyright 2016 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
{
protected:
bool isApplicable() const { return extensionEnabled("GL_EXT_blend_func_extended"); }
};
class EXTBlendFuncExtendedDrawTest : public ANGLETest
{
protected:
EXTBlendFuncExtendedDrawTest() : mProgram(0)
{
setWindowWidth(kWidth);
setWindowHeight(kHeight);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
bool isApplicable() const { return extensionEnabled("GL_EXT_blend_func_extended"); }
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)
{
if (!isApplicable())
return;
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)
{
if (!isApplicable())
return;
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)
{
if (!isApplicable())
return;
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(), ES2_OPENGLES());
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