| 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
|
|
|