| Index: gpu/command_buffer/tests/gl_chromium_framebuffer_mixed_samples_unittest.cc | 
| diff --git a/gpu/command_buffer/tests/gl_chromium_framebuffer_mixed_samples_unittest.cc b/gpu/command_buffer/tests/gl_chromium_framebuffer_mixed_samples_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..2297aee15da8a57773b84529523627d293f1c453 | 
| --- /dev/null | 
| +++ b/gpu/command_buffer/tests/gl_chromium_framebuffer_mixed_samples_unittest.cc | 
| @@ -0,0 +1,243 @@ | 
| +// 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 "base/command_line.h" | 
| +#include "gpu/command_buffer/service/gpu_switches.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" | 
| + | 
| +#define SHADER(Src) #Src | 
| + | 
| +namespace gpu { | 
| + | 
| +class CHROMIUMFramebufferMixedSamplesTest : public testing::Test { | 
| + protected: | 
| +  const GLuint kWidth = 100; | 
| +  const GLuint kHeight = 100; | 
| + | 
| +  void SetUp() override { | 
| +    base::CommandLine command_line(*base::CommandLine::ForCurrentProcess()); | 
| +    command_line.AppendSwitch(switches::kEnableGLPathRendering); | 
| +    gl_.InitializeWithCommandLine(GLManager::Options(), &command_line); | 
| +  } | 
| + | 
| +  void TearDown() override { gl_.Destroy(); } | 
| + | 
| +  bool IsApplicable() const { | 
| +    return GLTestHelper::HasExtension( | 
| +               "GL_CHROMIUM_framebuffer_mixed_samples") && | 
| +           GLTestHelper::HasExtension("GL_OES_rgb8_rgba8"); | 
| +  } | 
| + | 
| +  enum SetupFBOType { | 
| +    MixedSampleFBO,   // 1 color sample, N stencil samples. | 
| +    SingleSampleFBO,  // 1 color sample, 1 stencil sample. | 
| +  }; | 
| + | 
| +  void SetupState(SetupFBOType fbo_type) { | 
| +    // clang-format off | 
| +    static const char* kVertexShaderSource = SHADER( | 
| +        attribute mediump vec4 position; | 
| +        void main() { | 
| +          gl_Position = position; | 
| +        }); | 
| +    static const char* kFragmentShaderSource = SHADER( | 
| +        uniform mediump vec4 color; | 
| +        void main() { | 
| +          gl_FragColor = color; | 
| +        }); | 
| +    // clang-format on | 
| +    GLuint program = | 
| +        GLTestHelper::LoadProgram(kVertexShaderSource, kFragmentShaderSource); | 
| +    glUseProgram(program); | 
| +    GLuint position_loc = glGetAttribLocation(program, "position"); | 
| +    color_loc_ = glGetUniformLocation(program, "color"); | 
| + | 
| +    GLuint vbo = 0; | 
| +    glGenBuffers(1, &vbo); | 
| +    glBindBuffer(GL_ARRAY_BUFFER, vbo); | 
| +    static 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, -1.0f, -1.0f, 1.0f,  -1.0f, 1.0f, 1.0f, | 
| +    }; | 
| +    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); | 
| +    glEnableVertexAttribArray(position_loc); | 
| +    glVertexAttribPointer(position_loc, 2, GL_FLOAT, GL_FALSE, 0, 0); | 
| +    glBindBuffer(GL_ARRAY_BUFFER, 0); | 
| + | 
| +    GLuint texture = 0; | 
| +    glActiveTexture(GL_TEXTURE0); | 
| +    glGenTextures(1, &texture); | 
| +    glBindTexture(GL_TEXTURE_2D, texture); | 
| +    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 
| +    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 
| +    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 
| +    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 
| +    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, | 
| +                 GL_UNSIGNED_BYTE, NULL); | 
| +    glBindTexture(GL_TEXTURE_2D, 0); | 
| + | 
| +    GLuint stencil_rb = 0; | 
| +    glGenRenderbuffers(1, &stencil_rb); | 
| +    glBindRenderbuffer(GL_RENDERBUFFER, stencil_rb); | 
| + | 
| +    if (fbo_type == MixedSampleFBO) { | 
| +      // Create a sample buffer. | 
| +      GLsizei num_samples = 8, max_samples = 0; | 
| +      glGetIntegerv(GL_MAX_SAMPLES, &max_samples); | 
| +      num_samples = std::min(num_samples, max_samples); | 
| +      glRenderbufferStorageMultisampleCHROMIUM( | 
| +          GL_RENDERBUFFER, num_samples, GL_STENCIL_INDEX8, kWidth, kHeight); | 
| +      GLint param = 0; | 
| +      glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, | 
| +                                   ¶m); | 
| +      EXPECT_GT(param, 1); | 
| +    } else { | 
| +      glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, kWidth, | 
| +                            kHeight); | 
| +    } | 
| +    glBindRenderbuffer(GL_RENDERBUFFER, 0); | 
| + | 
| +    GLuint sample_fbo = 0; | 
| +    glGenFramebuffers(1, &sample_fbo); | 
| +    glBindFramebuffer(GL_FRAMEBUFFER, sample_fbo); | 
| +    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | 
| +                           texture, 0); | 
| +    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, | 
| +                              GL_RENDERBUFFER, stencil_rb); | 
| +    EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), | 
| +              glCheckFramebufferStatus(GL_FRAMEBUFFER)); | 
| + | 
| +    glViewport(0, 0, kWidth, kHeight); | 
| +    glClearColor(0.0f, 0.0f, 0.0f, 0.0f); | 
| +    glClearStencil(1); | 
| +    glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); | 
| +    glEnable(GL_STENCIL_TEST); | 
| +    glEnable(GL_BLEND); | 
| +    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | 
| +    glStencilMask(0xffffffff); | 
| +    glStencilFunc(GL_EQUAL, 1, 0xffffffff); | 
| +    glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); | 
| +  } | 
| + | 
| +  GLManager gl_; | 
| +  GLint color_loc_; | 
| +}; | 
| + | 
| +TEST_F(CHROMIUMFramebufferMixedSamplesTest, Simple) { | 
| +  if (!IsApplicable()) { | 
| +    return; | 
| +  } | 
| +  GLint value = -1; | 
| +  glGetIntegerv(GL_COVERAGE_MODULATION_CHROMIUM, &value); | 
| +  EXPECT_EQ(0, value); | 
| +  GLenum kValues[] = {GL_NONE, GL_RGB, GL_RGBA, GL_ALPHA}; | 
| +  for (auto expect : kValues) { | 
| +    glCoverageModulationCHROMIUM(expect); | 
| +    value = -1; | 
| +    glGetIntegerv(GL_COVERAGE_MODULATION_CHROMIUM, &value); | 
| +    EXPECT_EQ(expect, static_cast<GLenum>(value)); | 
| +    EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | 
| +  } | 
| + | 
| +  glCoverageModulationCHROMIUM(GL_BYTE); | 
| +  EXPECT_EQ(static_cast<GLenum>(GL_INVALID_ENUM), glGetError()); | 
| +  value = -1; | 
| +  glGetIntegerv(GL_COVERAGE_MODULATION_CHROMIUM, &value); | 
| +  EXPECT_EQ(static_cast<GLenum>(GL_ALPHA), static_cast<GLenum>(value)); | 
| +  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | 
| +} | 
| + | 
| +// The test pattern is as follows: | 
| +// A green triangle (bottom left, top right, top left). | 
| +// A blue triangle (top left, bottom right, bottom left). | 
| +// The triangles will overlap but overlap only contains green pixels, | 
| +// due to each draw erasing its area from stencil. | 
| +// The blue triangle will fill only the area (bottom left, center, | 
| +// bottom right). | 
| + | 
| +// The test tests that CoverageModulation call works. | 
| +// The fractional pixels of both triangles end up being modulated | 
| +// by the coverage of the fragment. Test that drawing with and without | 
| +// CoverageModulation causes the result to be different. | 
| +TEST_F(CHROMIUMFramebufferMixedSamplesTest, CoverageModulation) { | 
| +  if (!IsApplicable()) { | 
| +    return; | 
| +  } | 
| +  static const float kBlue[] = {0.0f, 0.0f, 1.0f, 1.0f}; | 
| +  static const float kGreen[] = {0.0f, 1.0f, 0.0f, 1.0f}; | 
| +  scoped_ptr<uint8[]> results[3]; | 
| +  const GLint kResultSize = kWidth * kHeight * 4; | 
| + | 
| +  for (int pass = 0; pass < 3; ++pass) { | 
| +    SetupState(MixedSampleFBO); | 
| +    if (pass == 1) { | 
| +      glCoverageModulationCHROMIUM(GL_RGBA); | 
| +    } | 
| +    glUniform4fv(color_loc_, 1, kGreen); | 
| +    glDrawArrays(GL_TRIANGLES, 0, 3); | 
| + | 
| +    glUniform4fv(color_loc_, 1, kBlue); | 
| +    glDrawArrays(GL_TRIANGLES, 3, 3); | 
| +    if (pass == 1) { | 
| +      glCoverageModulationCHROMIUM(GL_NONE); | 
| +    } | 
| + | 
| +    results[pass].reset(new uint8[kResultSize]); | 
| +    memset(results[pass].get(), GLTestHelper::kCheckClearValue, kResultSize); | 
| +    glReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, | 
| +                 results[pass].get()); | 
| +  } | 
| + | 
| +  EXPECT_NE(0, memcmp(results[0].get(), results[1].get(), kResultSize)); | 
| +  // Verify that rendering is deterministic, so that the pass above does not | 
| +  // come from non-deterministic rendering. | 
| +  EXPECT_EQ(0, memcmp(results[0].get(), results[2].get(), kResultSize)); | 
| +} | 
| + | 
| +// The test tests that the stencil buffer can be multisampled, even though the | 
| +// color buffer is single-sampled. Draws the same pattern with single-sample | 
| +// stencil buffer and with multisample stencil buffer. The images should differ. | 
| +TEST_F(CHROMIUMFramebufferMixedSamplesTest, MultisampleStencilEffective) { | 
| +  if (!IsApplicable()) { | 
| +    return; | 
| +  } | 
| + | 
| +  static const float kBlue[] = {0.0f, 0.0f, 1.0f, 1.0f}; | 
| +  static const float kGreen[] = {0.0f, 1.0f, 0.0f, 1.0f}; | 
| + | 
| +  scoped_ptr<uint8[]> results[3]; | 
| +  const GLint kResultSize = kWidth * kHeight * 4; | 
| + | 
| +  for (int pass = 0; pass < 3; ++pass) { | 
| +    if (pass == 1) { | 
| +      SetupState(MixedSampleFBO); | 
| +    } else { | 
| +      SetupState(SingleSampleFBO); | 
| +    } | 
| +    glUniform4fv(color_loc_, 1, kGreen); | 
| +    glDrawArrays(GL_TRIANGLES, 0, 3); | 
| + | 
| +    glUniform4fv(color_loc_, 1, kBlue); | 
| +    glDrawArrays(GL_TRIANGLES, 3, 3); | 
| + | 
| +    results[pass].reset(new uint8[kResultSize]); | 
| +    memset(results[pass].get(), GLTestHelper::kCheckClearValue, kResultSize); | 
| +    glReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, | 
| +                 results[pass].get()); | 
| +  } | 
| + | 
| +  EXPECT_NE(0, memcmp(results[0].get(), results[1].get(), kResultSize)); | 
| +  // Verify that rendering is deterministic, so that the pass above does not | 
| +  // come from non-deterministic rendering. | 
| +  EXPECT_EQ(0, memcmp(results[0].get(), results[2].get(), kResultSize)); | 
| +} | 
| + | 
| +}  // namespace gpu | 
|  |