Chromium Code Reviews| Index: gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc |
| diff --git a/gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc b/gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b3cb52d73d0b085e24116dbd9b10860cc7f9a4be |
| --- /dev/null |
| +++ b/gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc |
| @@ -0,0 +1,358 @@ |
| +// Copyright (c) 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include <GLES2/gl2.h> |
| +#include <GLES2/gl2ext.h> |
| +#include <GLES2/gl2extchromium.h> |
| +#include <GLES3/gl3.h> |
| + |
| +#include "base/command_line.h" |
| +#include "gpu/command_buffer/tests/gl_manager.h" |
| +#include "gpu/command_buffer/tests/gl_test_utils.h" |
| +#include "testing/gmock/include/gmock/gmock.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| +#include "ui/gl/gl_switches.h" |
| + |
| +#define SHADER(Src) #Src |
| +#define BFE_SHADER(Src) "#extension GL_EXT_blend_func_extended : require\n" #Src |
| + |
| +namespace { |
| +// Partial implementation of weight function for GLES 2 blend equation that |
| +// is dual-source aware. |
| +template <int factor, int index> |
| +float Weight(float dst[4], float src[4], 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; |
| +} |
| + |
| +// Implementation of GLES 2 blend equation that is dual-source aware. |
| +template <int RGBs, int RGBd, int As, int Ad> |
| +void BlendEquationFuncAdd(float dst[4], |
| + float src[4], |
| + float src1[4], |
| + uint8 result[4]) { |
| + 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); |
| + for (int i = 0; i < 4; ++i) { |
| + result[i] = static_cast<uint8>( |
| + std::floor(std::max(0.0f, std::min(1.0f, r[i])) * 255.0f)); |
| + } |
| +} |
| +} |
| + |
| +namespace gpu { |
| + |
| +class EXTBlendFuncExtendedTest : public testing::Test { |
| + public: |
| + protected: |
| + void SetUp() override { gl_.Initialize(GLManager::Options()); } |
| + |
| + void TearDown() override { gl_.Destroy(); } |
| + bool IsApplicable() const { |
| + return GLTestHelper::HasExtension("GL_EXT_blend_func_extended"); |
| + } |
| + GLManager gl_; |
| +}; |
| + |
| +TEST_F(EXTBlendFuncExtendedTest, TestMaxDualSourceDrawBuffers) { |
| + if (!IsApplicable()) { |
| + return; |
| + } |
| + |
| + GLint maxDualSourceDrawBuffers = 0; |
| + glGetIntegerv(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT, &maxDualSourceDrawBuffers); |
| + EXPECT_GT(maxDualSourceDrawBuffers, 0); |
| + EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| +} |
| + |
| +class EXTBlendFuncExtendedDrawTest : public testing::Test { |
| + public: |
| + static const GLsizei kWidth = 100; |
| + static const GLsizei kHeight = 100; |
| + EXTBlendFuncExtendedDrawTest() : program_(0) {} |
| + |
| + protected: |
| + void SetUp() override { |
| + GLManager::Options options; |
| + options.size = gfx::Size(kWidth, kHeight); |
| + gl_.Initialize(options); |
| + } |
| + |
| + bool IsApplicable() const { |
| + return GLTestHelper::HasExtension("GL_EXT_blend_func_extended"); |
| + } |
| + |
| + virtual const char* GetVertexShader() { |
| + static const char* kVertexShader = SHADER( |
| + attribute vec4 position; void main() { gl_Position = position; }); |
| + return kVertexShader; |
| + } |
| + |
| + void CreateProgramWithFragmentShader(const char* fragment_shader_str) { |
| + GLuint vertex_shader = |
| + GLTestHelper::LoadShader(GL_VERTEX_SHADER, GetVertexShader()); |
| + GLuint fragment_shader = |
| + GLTestHelper::LoadShader(GL_FRAGMENT_SHADER, fragment_shader_str); |
| + ASSERT_NE(0u, vertex_shader); |
| + ASSERT_NE(0u, fragment_shader); |
| + program_ = glCreateProgram(); |
| + ASSERT_NE(0u, program_); |
| + glAttachShader(program_, vertex_shader); |
| + glAttachShader(program_, fragment_shader); |
| + glDeleteShader(vertex_shader); |
| + glDeleteShader(fragment_shader); |
| + } |
| + |
| + void LinkProgram() { |
| + glLinkProgram(program_); |
| + GLint linked = 0; |
| + glGetProgramiv(program_, GL_LINK_STATUS, &linked); |
| + if (linked == 0) { |
| + char buffer[1024]; |
| + GLsizei length = 0; |
| + glGetProgramInfoLog(program_, sizeof(buffer), &length, buffer); |
| + std::string log(buffer, length); |
| + EXPECT_EQ(1, linked) << "Error linking program: " << log; |
| + glDeleteProgram(program_); |
| + program_ = 0; |
| + } |
| + glUseProgram(program_); |
| + position_loc_ = glGetAttribLocation(program_, "position"); |
| + src_loc_ = glGetUniformLocation(program_, "src"); |
| + src1_loc_ = glGetUniformLocation(program_, "src1"); |
| + } |
| + |
| + void TearDown() override { |
| + if (program_ != 0) { |
| + glDeleteProgram(program_); |
| + } |
| + gl_.Destroy(); |
| + } |
| + |
| + void DrawAndVerify() { |
| + float kDst[4] = {0.5f, 0.5f, 0.5f, 0.5f}; |
| + float kSrc[4] = {1.0f, 1.0f, 1.0f, 1.0f}; |
| + float kSrc1[4] = {0.3f, 0.6f, 0.9f, 0.7f}; |
| + |
| + glUniform4f(src_loc_, kSrc[0], kSrc[1], kSrc[2], kSrc[3]); |
| + glUniform4f(src1_loc_, kSrc1[0], kSrc1[1], kSrc1[2], kSrc1[3]); |
| + |
| + GLTestHelper::SetupUnitQuad(position_loc_); |
| + |
| + glEnable(GL_BLEND); |
| + glBlendEquation(GL_FUNC_ADD); |
| + glBlendFuncSeparate(GL_SRC1_COLOR_EXT, GL_SRC_ALPHA, |
| + GL_ONE_MINUS_SRC1_COLOR_EXT, |
| + GL_ONE_MINUS_SRC1_ALPHA_EXT); |
| + EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| + |
| + // Draw one triangle (bottom left half). |
| + glViewport(0, 0, kWidth, kHeight); |
| + glClearColor(kDst[0], kDst[1], kDst[2], kDst[3]); |
| + glClear(GL_COLOR_BUFFER_BIT); |
| + glDrawArrays(GL_TRIANGLES, 0, 6); |
| + EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| + |
| + // Verify. |
| + uint8 color[4]; |
| + BlendEquationFuncAdd<GL_SRC1_COLOR_EXT, GL_SRC_ALPHA, |
| + GL_ONE_MINUS_SRC1_COLOR_EXT, |
| + GL_ONE_MINUS_SRC1_ALPHA_EXT>(kDst, kSrc, kSrc1, color); |
| + |
| + EXPECT_TRUE(GLTestHelper::CheckPixels(kWidth / 4, (3 * kHeight) / 4, 1, 1, |
| + 1, color)); |
| + EXPECT_TRUE(GLTestHelper::CheckPixels(kWidth - 1, 0, 1, 1, 1, color)); |
| + } |
| + |
| + protected: |
| + GLuint program_; |
| + GLuint position_loc_; |
| + GLuint src_loc_; |
| + GLuint src1_loc_; |
| + GLManager gl_; |
| +}; |
| + |
| +TEST_F(EXTBlendFuncExtendedDrawTest, ESSL1FragColor) { |
| + if (!IsApplicable()) { |
| + return; |
| + } |
| + static const char* kFragColorShader = |
| + BFE_SHADER(precision mediump float; uniform vec4 src; uniform vec4 src1; |
| + void main() { |
| + gl_FragColor = src; |
| + gl_SecondaryFragColorEXT = src1; |
| + }); |
| + |
| + CreateProgramWithFragmentShader(kFragColorShader); |
| + LinkProgram(); |
| + DrawAndVerify(); |
| +} |
| + |
| +TEST_F(EXTBlendFuncExtendedDrawTest, ESSL1FragData) { |
| + if (!IsApplicable()) { |
| + return; |
| + } |
| + static const char* kFragDataShader = |
| + BFE_SHADER(precision mediump float; uniform vec4 src; uniform vec4 src1; |
| + void main() { |
| + gl_FragData[0] = src; |
| + gl_SecondaryFragDataEXT[0] = src1; |
| + }); |
| + CreateProgramWithFragmentShader(kFragDataShader); |
| + LinkProgram(); |
| + DrawAndVerify(); |
| +} |
| + |
| +class EXTBlendFuncExtendedES3DrawTest : public EXTBlendFuncExtendedDrawTest { |
| + protected: |
| + void SetUp() override { |
| + GLManager::Options options; |
| + options.context_type = GLManager::CONTEXT_TYPE_OPENGLES3; |
| + options.size = gfx::Size(kWidth, kHeight); |
| + base::CommandLine command_line(*base::CommandLine::ForCurrentProcess()); |
| + command_line.AppendSwitch(switches::kEnableUnsafeES3APIs); |
| + gl_.InitializeWithCommandLine(options, &command_line); |
| + } |
| + bool IsApplicable() const { |
| + return gl_.IsInitialized() && EXTBlendFuncExtendedDrawTest::IsApplicable(); |
| + } |
| + const char* GetVertexShader() override { |
| + static const char* kVertexShader = "#version 300 es\n" SHADER( |
| + in vec4 position; void main() { gl_Position = position; }); |
| + return kVertexShader; |
| + } |
| +}; |
| + |
| +TEST_F(EXTBlendFuncExtendedES3DrawTest, ESSL3Var) { |
| + if (!IsApplicable()) { |
| + return; |
| + } |
| + static const char* kFragColorShader = "#version 300 es\n" BFE_SHADER( |
| + precision mediump float; uniform vec4 src; uniform vec4 src1; |
| + out vec4 FragColor; out vec4 SecondaryFragColor; void main() { |
| + FragColor = src; |
| + SecondaryFragColor = src1; |
| + }); |
| + |
| + CreateProgramWithFragmentShader(kFragColorShader); |
| + glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragColor"); |
| + LinkProgram(); |
| + DrawAndVerify(); |
| +} |
| + |
| +TEST_F(EXTBlendFuncExtendedES3DrawTest, ESSL3Array) { |
| + if (!IsApplicable()) { |
| + return; |
| + } |
| + |
| + static const char* kFragDataShader = "#version 300 es\n" BFE_SHADER( |
| + precision mediump float; uniform vec4 src; uniform vec4 src1; |
| + out vec4 FragData[1]; out vec4 SecondaryFragData[1]; void main() { |
| + FragData[0] = src; |
| + SecondaryFragData[0] = src1; |
| + }); |
| + |
| + CreateProgramWithFragmentShader(kFragDataShader); |
| + glBindFragDataLocationEXT(program_, 0, "FragData"); |
| + glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragData"); |
| + LinkProgram(); |
| + DrawAndVerify(); |
| +} |
| + |
| +TEST_F(EXTBlendFuncExtendedES3DrawTest, ES3Getters) { |
| + if (!IsApplicable()) { |
| + return; |
| + } |
| + |
| + static const char* kFragColorShader = "#version 300 es\n" BFE_SHADER( |
| + precision mediump float; uniform vec4 src; uniform vec4 src1; |
| + out vec4 FragColor; out vec4 SecondaryFragColor; void main() { |
| + FragColor = src; |
| + SecondaryFragColor = src1; |
| + }); |
| + |
| + CreateProgramWithFragmentShader(kFragColorShader); |
| + glBindFragDataLocationEXT(program_, 0, "FragColor"); |
| + glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragColor"); |
| + |
| + // Getters return GL error before linking. |
| + EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| + GLint location = glGetFragDataLocation(program_, "FragColor"); |
| + EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError()); |
| + GLint index = glGetFragDataIndexEXT(program_, "FragColor"); |
| + EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError()); |
| + location = glGetFragDataLocation(program_, "SecondaryFragColor"); |
| + EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError()); |
| + index = glGetFragDataIndexEXT(program_, "SecondaryFragColor"); |
| + EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError()); |
| + LinkProgram(); |
| + |
| + // Getters return location and index after linking. |
| + for (int i = 0; i < 2; ++i) { |
| + location = glGetFragDataLocation(program_, "FragColor"); |
| + EXPECT_EQ(0, location); |
| + EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| + index = glGetFragDataIndexEXT(program_, "FragColor"); |
| + EXPECT_EQ(0, index); |
| + EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| + location = glGetFragDataLocation(program_, "SecondaryFragColor"); |
| + EXPECT_EQ(0, location); |
| + EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| + index = glGetFragDataIndexEXT(program_, "SecondaryFragColor"); |
| + EXPECT_EQ(1, index); |
| + EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| + |
| + // The calls should not affect the getters until re-linking. |
| + glBindFragDataLocationEXT(program_, 0, "SecondaryFragColor"); |
| + glBindFragDataLocationIndexedEXT(program_, 0, 1, "FragColor"); |
| + } |
| + |
| + LinkProgram(); |
| + |
| + location = glGetFragDataLocation(program_, "FragColor"); |
| + EXPECT_EQ(0, location); |
| + EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| + index = glGetFragDataIndexEXT(program_, "FragColor"); |
| + EXPECT_EQ(1, index); |
| + EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| + location = glGetFragDataLocation(program_, "SecondaryFragColor"); |
| + EXPECT_EQ(0, location); |
| + EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| + index = glGetFragDataIndexEXT(program_, "SecondaryFragColor"); |
| + EXPECT_EQ(0, index); |
| + EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| + |
| + // Unknown colors return location -1, index -1. |
| + location = glGetFragDataLocation(program_, "UnknownColor"); |
| + EXPECT_EQ(-1, location); |
| + EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| + index = glGetFragDataIndexEXT(program_, "UnknownColor"); |
| + EXPECT_EQ(-1, index); |
| + |
| + // Reset the settings and verify that the driver gets them correct. |
| + glBindFragDataLocationEXT(program_, 0, "FragColor"); |
| + glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragColor"); |
| + LinkProgram(); |
| + DrawAndVerify(); |
| +} |
|
Mark Kilgard
2015/08/28 19:53:25
very nice testing
|
| + |
| +} // namespace gpu |