Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <GLES2/gl2.h> | |
| 6 #include <GLES2/gl2ext.h> | |
| 7 #include <GLES2/gl2extchromium.h> | |
| 8 #include <GLES3/gl3.h> | |
| 9 | |
| 10 #include "base/command_line.h" | |
| 11 #include "gpu/command_buffer/service/gpu_switches.h" | |
| 12 #include "gpu/command_buffer/tests/gl_manager.h" | |
| 13 #include "gpu/command_buffer/tests/gl_test_utils.h" | |
| 14 #include "testing/gmock/include/gmock/gmock.h" | |
| 15 #include "testing/gtest/include/gtest/gtest.h" | |
| 16 #include "ui/gl/gl_switches.h" | |
| 17 | |
| 18 #define SHADER(Src) #Src | |
| 19 #define BFE_SHADER(Src) "#extension GL_EXT_blend_func_extended : require\n" #Src | |
| 20 | |
| 21 namespace { | |
| 22 // Partial implementation of weight function for GLES 2 blend equation that | |
| 23 // is dual-source aware. | |
| 24 template <int factor, int index> | |
| 25 float Weight(float /*dst*/[4], float src[4], float src1[4]) { | |
| 26 if (factor == GL_SRC_COLOR) | |
| 27 return src[index]; | |
| 28 if (factor == GL_SRC_ALPHA) | |
| 29 return src[3]; | |
| 30 if (factor == GL_SRC1_COLOR_EXT) | |
| 31 return src1[index]; | |
| 32 if (factor == GL_SRC1_ALPHA_EXT) | |
| 33 return src1[3]; | |
| 34 if (factor == GL_ONE_MINUS_SRC1_COLOR_EXT) | |
| 35 return 1.0f - src1[index]; | |
| 36 if (factor == GL_ONE_MINUS_SRC1_ALPHA_EXT) | |
| 37 return 1.0f - src1[3]; | |
| 38 return 0.0f; | |
| 39 } | |
| 40 | |
| 41 // Implementation of GLES 2 blend equation that is dual-source aware. | |
| 42 template <int RGBs, int RGBd, int As, int Ad> | |
| 43 void BlendEquationFuncAdd(float dst[4], | |
| 44 float src[4], | |
| 45 float src1[4], | |
| 46 uint8 result[4]) { | |
| 47 float r[4]; | |
| 48 r[0] = src[0] * Weight<RGBs, 0>(dst, src, src1) + | |
| 49 dst[0] * Weight<RGBd, 0>(dst, src, src1); | |
| 50 r[1] = src[1] * Weight<RGBs, 1>(dst, src, src1) + | |
| 51 dst[1] * Weight<RGBd, 1>(dst, src, src1); | |
| 52 r[2] = src[2] * Weight<RGBs, 2>(dst, src, src1) + | |
| 53 dst[2] * Weight<RGBd, 2>(dst, src, src1); | |
| 54 r[3] = src[3] * Weight<As, 3>(dst, src, src1) + | |
| 55 dst[3] * Weight<Ad, 3>(dst, src, src1); | |
| 56 for (int i = 0; i < 4; ++i) { | |
| 57 result[i] = static_cast<uint8>( | |
| 58 std::floor(std::max(0.0f, std::min(1.0f, r[i])) * 255.0f)); | |
| 59 } | |
| 60 } | |
| 61 | |
| 62 // Variant for test. EXT_blend_func_extended can not be emulated | |
| 63 // with OpenGL without translator. | |
| 64 enum TestVariant { NoTranslator, Translator, TranslatorWithNameHashing }; | |
| 65 | |
| 66 } // namespace | |
| 67 | |
| 68 namespace gpu { | |
| 69 | |
| 70 class EXTBlendFuncExtendedTest : public testing::Test { | |
| 71 public: | |
| 72 protected: | |
| 73 void SetUp() override { gl_.Initialize(GLManager::Options()); } | |
| 74 | |
| 75 void TearDown() override { gl_.Destroy(); } | |
| 76 bool IsApplicable() const { | |
| 77 return GLTestHelper::HasExtension("GL_EXT_blend_func_extended"); | |
| 78 } | |
| 79 GLManager gl_; | |
| 80 }; | |
| 81 | |
| 82 TEST_F(EXTBlendFuncExtendedTest, TestMaxDualSourceDrawBuffers) { | |
| 83 if (!IsApplicable()) { | |
| 84 return; | |
| 85 } | |
| 86 | |
| 87 GLint maxDualSourceDrawBuffers = 0; | |
| 88 glGetIntegerv(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT, &maxDualSourceDrawBuffers); | |
| 89 EXPECT_GT(maxDualSourceDrawBuffers, 0); | |
| 90 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 91 } | |
| 92 | |
| 93 class EXTBlendFuncExtendedDrawTest | |
| 94 : public testing::TestWithParam<TestVariant> { | |
| 95 public: | |
| 96 static const GLsizei kWidth = 100; | |
| 97 static const GLsizei kHeight = 100; | |
| 98 EXTBlendFuncExtendedDrawTest() : program_(0) {} | |
| 99 | |
| 100 protected: | |
| 101 void SetUp() override { | |
| 102 GLManager::Options options; | |
| 103 options.size = gfx::Size(kWidth, kHeight); | |
| 104 options.force_shader_name_hashing = GetParam() == TranslatorWithNameHashing; | |
| 105 base::CommandLine command_line(*base::CommandLine::ForCurrentProcess()); | |
| 106 if (GetParam() == NoTranslator) { | |
| 107 command_line.AppendSwitch(switches::kDisableGLSLTranslator); | |
| 108 } | |
| 109 gl_.InitializeWithCommandLine(options, &command_line); | |
| 110 } | |
| 111 | |
| 112 bool IsApplicable() const { | |
| 113 return GLTestHelper::HasExtension("GL_EXT_blend_func_extended"); | |
| 114 } | |
| 115 | |
| 116 virtual const char* GetVertexShader() { | |
| 117 // clang-format off | |
| 118 static const char* kVertexShader = | |
| 119 SHADER( | |
| 120 attribute vec4 position; | |
| 121 void main() { | |
| 122 gl_Position = position; | |
| 123 }); | |
| 124 // clang-format on | |
| 125 return kVertexShader; | |
| 126 } | |
| 127 | |
| 128 void CreateProgramWithFragmentShader(const char* fragment_shader_str) { | |
| 129 GLuint vertex_shader = | |
| 130 GLTestHelper::LoadShader(GL_VERTEX_SHADER, GetVertexShader()); | |
| 131 GLuint fragment_shader = | |
| 132 GLTestHelper::LoadShader(GL_FRAGMENT_SHADER, fragment_shader_str); | |
| 133 ASSERT_NE(0u, vertex_shader); | |
| 134 ASSERT_NE(0u, fragment_shader); | |
| 135 program_ = glCreateProgram(); | |
| 136 ASSERT_NE(0u, program_); | |
| 137 glAttachShader(program_, vertex_shader); | |
| 138 glAttachShader(program_, fragment_shader); | |
| 139 glDeleteShader(vertex_shader); | |
| 140 glDeleteShader(fragment_shader); | |
| 141 } | |
| 142 | |
| 143 void LinkProgram() { | |
| 144 glLinkProgram(program_); | |
| 145 GLint linked = 0; | |
| 146 glGetProgramiv(program_, GL_LINK_STATUS, &linked); | |
| 147 if (linked == 0) { | |
| 148 char buffer[1024]; | |
| 149 GLsizei length = 0; | |
| 150 glGetProgramInfoLog(program_, sizeof(buffer), &length, buffer); | |
| 151 std::string log(buffer, length); | |
| 152 EXPECT_EQ(1, linked) << "Error linking program: " << log; | |
| 153 glDeleteProgram(program_); | |
| 154 program_ = 0; | |
|
Zhenyao Mo
2015/09/30 00:23:48
Should we return early here?
Kimmo Kinnunen
2015/10/08 13:18:12
Done.
| |
| 155 } | |
| 156 glUseProgram(program_); | |
| 157 position_loc_ = glGetAttribLocation(program_, "position"); | |
| 158 src_loc_ = glGetUniformLocation(program_, "src"); | |
| 159 src1_loc_ = glGetUniformLocation(program_, "src1"); | |
| 160 } | |
| 161 | |
| 162 void TearDown() override { | |
| 163 if (program_ != 0) { | |
| 164 glDeleteProgram(program_); | |
| 165 } | |
| 166 gl_.Destroy(); | |
| 167 } | |
| 168 | |
| 169 void DrawAndVerify() { | |
| 170 float kDst[4] = {0.5f, 0.5f, 0.5f, 0.5f}; | |
| 171 float kSrc[4] = {1.0f, 1.0f, 1.0f, 1.0f}; | |
| 172 float kSrc1[4] = {0.3f, 0.6f, 0.9f, 0.7f}; | |
| 173 | |
| 174 glUniform4f(src_loc_, kSrc[0], kSrc[1], kSrc[2], kSrc[3]); | |
| 175 glUniform4f(src1_loc_, kSrc1[0], kSrc1[1], kSrc1[2], kSrc1[3]); | |
| 176 | |
| 177 GLTestHelper::SetupUnitQuad(position_loc_); | |
| 178 | |
| 179 glEnable(GL_BLEND); | |
| 180 glBlendEquation(GL_FUNC_ADD); | |
| 181 glBlendFuncSeparate(GL_SRC1_COLOR_EXT, GL_SRC_ALPHA, | |
| 182 GL_ONE_MINUS_SRC1_COLOR_EXT, | |
| 183 GL_ONE_MINUS_SRC1_ALPHA_EXT); | |
| 184 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 185 | |
| 186 // Draw one triangle (bottom left half). | |
| 187 glViewport(0, 0, kWidth, kHeight); | |
| 188 glClearColor(kDst[0], kDst[1], kDst[2], kDst[3]); | |
| 189 glClear(GL_COLOR_BUFFER_BIT); | |
| 190 glDrawArrays(GL_TRIANGLES, 0, 6); | |
| 191 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 192 | |
| 193 // Verify. | |
| 194 uint8 color[4]; | |
| 195 BlendEquationFuncAdd<GL_SRC1_COLOR_EXT, GL_SRC_ALPHA, | |
| 196 GL_ONE_MINUS_SRC1_COLOR_EXT, | |
| 197 GL_ONE_MINUS_SRC1_ALPHA_EXT>(kDst, kSrc, kSrc1, color); | |
| 198 | |
| 199 EXPECT_TRUE(GLTestHelper::CheckPixels(kWidth / 4, (3 * kHeight) / 4, 1, 1, | |
| 200 1, color)); | |
| 201 EXPECT_TRUE(GLTestHelper::CheckPixels(kWidth - 1, 0, 1, 1, 1, color)); | |
| 202 } | |
| 203 | |
| 204 protected: | |
| 205 GLuint program_; | |
| 206 GLuint position_loc_; | |
| 207 GLuint src_loc_; | |
| 208 GLuint src1_loc_; | |
| 209 GLManager gl_; | |
| 210 }; | |
| 211 | |
| 212 TEST_P(EXTBlendFuncExtendedDrawTest, ESSL1FragColor) { | |
| 213 if (!IsApplicable()) { | |
| 214 return; | |
| 215 } | |
| 216 // clang-format off | |
| 217 static const char* kFragColorShader = | |
| 218 BFE_SHADER( | |
| 219 precision mediump float; | |
| 220 uniform vec4 src; | |
| 221 uniform vec4 src1; | |
| 222 void main() { | |
| 223 gl_FragColor = src; | |
| 224 gl_SecondaryFragColorEXT = src1; | |
| 225 }); | |
| 226 // clang-format on | |
| 227 CreateProgramWithFragmentShader(kFragColorShader); | |
| 228 LinkProgram(); | |
| 229 DrawAndVerify(); | |
| 230 } | |
| 231 | |
| 232 TEST_P(EXTBlendFuncExtendedDrawTest, ESSL1FragData) { | |
| 233 if (!IsApplicable()) { | |
| 234 return; | |
| 235 } | |
| 236 // clang-format off | |
| 237 static const char* kFragDataShader = | |
| 238 BFE_SHADER( | |
| 239 precision mediump float; | |
| 240 uniform vec4 src; | |
| 241 uniform vec4 src1; | |
| 242 void main() { | |
| 243 gl_FragData[0] = src; | |
| 244 gl_SecondaryFragDataEXT[0] = src1; | |
| 245 }); | |
| 246 // clang-format on | |
| 247 CreateProgramWithFragmentShader(kFragDataShader); | |
| 248 LinkProgram(); | |
| 249 DrawAndVerify(); | |
| 250 } | |
| 251 | |
| 252 class EXTBlendFuncExtendedES3DrawTest : public EXTBlendFuncExtendedDrawTest { | |
| 253 protected: | |
| 254 void SetUp() override { | |
| 255 GLManager::Options options; | |
| 256 options.size = gfx::Size(kWidth, kHeight); | |
| 257 options.context_type = gles2::CONTEXT_TYPE_OPENGLES3; | |
| 258 options.force_shader_name_hashing = GetParam() == TranslatorWithNameHashing; | |
| 259 base::CommandLine command_line(*base::CommandLine::ForCurrentProcess()); | |
| 260 if (GetParam() == NoTranslator) { | |
| 261 command_line.AppendSwitch(switches::kDisableGLSLTranslator); | |
| 262 } | |
| 263 command_line.AppendSwitch(switches::kEnableUnsafeES3APIs); | |
| 264 gl_.InitializeWithCommandLine(options, &command_line); | |
| 265 } | |
| 266 bool IsApplicable() const { | |
| 267 return gl_.IsInitialized() && EXTBlendFuncExtendedDrawTest::IsApplicable(); | |
| 268 } | |
| 269 const char* GetVertexShader() override { | |
| 270 // clang-format off | |
| 271 static const char* kVertexShader = | |
| 272 "#version 300 es\n" | |
| 273 SHADER( | |
| 274 in vec4 position; | |
| 275 void main() { | |
| 276 gl_Position = position; | |
| 277 }); | |
| 278 // clang-format on | |
| 279 return kVertexShader; | |
| 280 } | |
| 281 }; | |
| 282 | |
| 283 TEST_P(EXTBlendFuncExtendedES3DrawTest, ESSL3Var) { | |
| 284 if (!IsApplicable()) { | |
| 285 return; | |
| 286 } | |
| 287 // clang-format off | |
| 288 static const char* kFragColorShader = | |
| 289 "#version 300 es\n" | |
| 290 BFE_SHADER( | |
| 291 precision mediump float; | |
| 292 uniform vec4 src; | |
| 293 uniform vec4 src1; | |
| 294 out vec4 FragColor; | |
| 295 out vec4 SecondaryFragColor; | |
| 296 void main() { | |
| 297 FragColor = src; | |
| 298 SecondaryFragColor = src1; | |
| 299 }); | |
| 300 // clang-format on | |
| 301 CreateProgramWithFragmentShader(kFragColorShader); | |
| 302 glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragColor"); | |
| 303 LinkProgram(); | |
| 304 DrawAndVerify(); | |
| 305 } | |
| 306 | |
| 307 TEST_P(EXTBlendFuncExtendedES3DrawTest, ESSL3Array) { | |
| 308 if (!IsApplicable()) { | |
| 309 return; | |
| 310 } | |
| 311 // clang-format off | |
| 312 static const char* kFragDataShader = | |
| 313 "#version 300 es\n" | |
| 314 BFE_SHADER( | |
| 315 precision mediump float; | |
| 316 uniform vec4 src; | |
| 317 uniform vec4 src1; | |
| 318 out vec4 FragData[1]; | |
| 319 out vec4 SecondaryFragData[1]; | |
| 320 void main() { | |
| 321 FragData[0] = src; | |
| 322 SecondaryFragData[0] = src1; | |
| 323 }); | |
| 324 // clang-format on | |
| 325 CreateProgramWithFragmentShader(kFragDataShader); | |
| 326 glBindFragDataLocationEXT(program_, 0, "FragData"); | |
| 327 glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragData"); | |
| 328 LinkProgram(); | |
| 329 DrawAndVerify(); | |
| 330 } | |
| 331 | |
| 332 TEST_P(EXTBlendFuncExtendedES3DrawTest, ES3Getters) { | |
| 333 if (!IsApplicable()) { | |
| 334 return; | |
| 335 } | |
| 336 // clang-format off | |
| 337 static const char* kFragColorShader = | |
| 338 "#version 300 es\n" | |
| 339 BFE_SHADER( | |
| 340 precision mediump float; | |
| 341 uniform vec4 src; | |
| 342 uniform vec4 src1; | |
| 343 out vec4 FragColor; | |
| 344 out vec4 SecondaryFragColor; | |
| 345 void main() { | |
| 346 FragColor = src; | |
| 347 SecondaryFragColor = src1; | |
| 348 }); | |
| 349 // clang-format on | |
| 350 CreateProgramWithFragmentShader(kFragColorShader); | |
| 351 glBindFragDataLocationEXT(program_, 0, "FragColor"); | |
| 352 glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragColor"); | |
| 353 | |
| 354 // Getters return GL error before linking. | |
| 355 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 356 GLint location = glGetFragDataLocation(program_, "FragColor"); | |
| 357 EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError()); | |
| 358 GLint index = glGetFragDataIndexEXT(program_, "FragColor"); | |
| 359 EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError()); | |
| 360 location = glGetFragDataLocation(program_, "SecondaryFragColor"); | |
| 361 EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError()); | |
| 362 index = glGetFragDataIndexEXT(program_, "SecondaryFragColor"); | |
| 363 EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError()); | |
| 364 LinkProgram(); | |
| 365 | |
| 366 // Getters return location and index after linking. Run twice to confirm that | |
| 367 // setters do not affect the getters until next link. | |
| 368 for (int i = 0; i < 2; ++i) { | |
| 369 SCOPED_TRACE(testing::Message() << "Testing getters after link, iteration " | |
| 370 << i); | |
| 371 | |
| 372 location = glGetFragDataLocation(program_, "FragColor"); | |
| 373 EXPECT_EQ(0, location); | |
| 374 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 375 index = glGetFragDataIndexEXT(program_, "FragColor"); | |
| 376 EXPECT_EQ(0, index); | |
| 377 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 378 location = glGetFragDataLocation(program_, "SecondaryFragColor"); | |
| 379 EXPECT_EQ(0, location); | |
| 380 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 381 index = glGetFragDataIndexEXT(program_, "SecondaryFragColor"); | |
| 382 EXPECT_EQ(1, index); | |
| 383 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 384 | |
| 385 // The calls should not affect the getters until re-linking. | |
| 386 glBindFragDataLocationEXT(program_, 0, "SecondaryFragColor"); | |
| 387 glBindFragDataLocationIndexedEXT(program_, 0, 1, "FragColor"); | |
| 388 } | |
| 389 | |
| 390 LinkProgram(); | |
| 391 | |
| 392 location = glGetFragDataLocation(program_, "FragColor"); | |
| 393 EXPECT_EQ(0, location); | |
| 394 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 395 index = glGetFragDataIndexEXT(program_, "FragColor"); | |
| 396 EXPECT_EQ(1, index); | |
| 397 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 398 location = glGetFragDataLocation(program_, "SecondaryFragColor"); | |
| 399 EXPECT_EQ(0, location); | |
| 400 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 401 index = glGetFragDataIndexEXT(program_, "SecondaryFragColor"); | |
| 402 EXPECT_EQ(0, index); | |
| 403 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 404 | |
| 405 // Unknown colors return location -1, index -1. | |
| 406 location = glGetFragDataLocation(program_, "UnknownColor"); | |
| 407 EXPECT_EQ(-1, location); | |
| 408 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 409 index = glGetFragDataIndexEXT(program_, "UnknownColor"); | |
| 410 EXPECT_EQ(-1, index); | |
| 411 | |
| 412 // Reset the settings and verify that the driver gets them correct. | |
| 413 glBindFragDataLocationEXT(program_, 0, "FragColor"); | |
| 414 glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragColor"); | |
| 415 LinkProgram(); | |
| 416 DrawAndVerify(); | |
| 417 } | |
| 418 | |
| 419 INSTANTIATE_TEST_CASE_P(TranslatorVariants, | |
| 420 EXTBlendFuncExtendedDrawTest, | |
| 421 ::testing::Values(NoTranslator, | |
| 422 Translator, | |
| 423 TranslatorWithNameHashing)); | |
| 424 | |
| 425 INSTANTIATE_TEST_CASE_P(TranslatorVariants, | |
| 426 EXTBlendFuncExtendedES3DrawTest, | |
| 427 ::testing::Values(NoTranslator, | |
| 428 Translator, | |
| 429 TranslatorWithNameHashing)); | |
| 430 | |
| 431 } // namespace gpu | |
| OLD | NEW |