diff options
Diffstat (limited to 'gfx/angle/src/tests/gl_tests/MultisampleCompatibilityTest.cpp')
-rwxr-xr-x | gfx/angle/src/tests/gl_tests/MultisampleCompatibilityTest.cpp | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/gfx/angle/src/tests/gl_tests/MultisampleCompatibilityTest.cpp b/gfx/angle/src/tests/gl_tests/MultisampleCompatibilityTest.cpp new file mode 100755 index 000000000..0edc221d7 --- /dev/null +++ b/gfx/angle/src/tests/gl_tests/MultisampleCompatibilityTest.cpp @@ -0,0 +1,438 @@ +// +// Copyright 2015 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. +// +// StateChangeTest: +// Specifically designed for an ANGLE implementation of GL, these tests validate that +// ANGLE's dirty bits systems don't get confused by certain sequences of state changes. +// + +#include "test_utils/ANGLETest.h" +#include "test_utils/gl_raii.h" +#include "shader_utils.h" + +using namespace angle; + +namespace +{ + +const GLint kWidth = 64; +const GLint kHeight = 64; + +// test drawing with GL_MULTISAMPLE_EXT enabled/disabled. +class EXTMultisampleCompatibilityTest : public ANGLETest +{ + +protected: + EXTMultisampleCompatibilityTest() + { + setWindowWidth(64); + setWindowHeight(64); + setConfigRedBits(8); + setConfigBlueBits(8); + setConfigAlphaBits(8); + } + + void SetUp() override + { + ANGLETest::SetUp(); + + static const char* v_shader_str = + "attribute vec4 a_Position;\n" + "void main()\n" + "{ gl_Position = a_Position; }"; + + static const char* f_shader_str = + "precision mediump float;\n" + "uniform vec4 color;" + "void main() { gl_FragColor = color; }"; + + mProgram = CompileProgram(v_shader_str, f_shader_str); + + GLuint position_loc = glGetAttribLocation(mProgram, "a_Position"); + mColorLoc = glGetUniformLocation(mProgram, "color"); + + glGenBuffers(1, &mVBO); + glBindBuffer(GL_ARRAY_BUFFER, mVBO); + 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); + } + + void TearDown() override + { + glDeleteBuffers(1, &mVBO); + glDeleteProgram(mProgram); + + ANGLETest::TearDown(); + } + + void prepareForDraw() + { + // Create a sample buffer. + GLsizei num_samples = 4, max_samples = 0; + glGetIntegerv(GL_MAX_SAMPLES, &max_samples); + num_samples = std::min(num_samples, max_samples); + + glGenRenderbuffers(1, &mSampleRB); + glBindRenderbuffer(GL_RENDERBUFFER, mSampleRB); + glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, num_samples, + GL_RGBA8_OES, kWidth, kHeight); + GLint param = 0; + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, + ¶m); + EXPECT_GE(param, num_samples); + + glGenFramebuffers(1, &mSampleFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mSampleFBO); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, mSampleRB); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + glCheckFramebufferStatus(GL_FRAMEBUFFER)); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Create another FBO to resolve the multisample buffer into. + glGenTextures(1, &mResolveTex); + glBindTexture(GL_TEXTURE_2D, mResolveTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, + GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glGenFramebuffers(1, &mResolveFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mResolveFBO); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + mResolveTex, 0); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + glCheckFramebufferStatus(GL_FRAMEBUFFER)); + + glUseProgram(mProgram); + glViewport(0, 0, kWidth, kHeight); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glBindFramebuffer(GL_FRAMEBUFFER, mSampleFBO); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + } + + void prepareForVerify() + { + // Resolve. + glBindFramebuffer(GL_READ_FRAMEBUFFER, mSampleFBO); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mResolveFBO); + glClearColor(1.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + glBlitFramebufferANGLE(0, 0, kWidth, kHeight, 0, 0, kWidth, kHeight, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBindFramebuffer(GL_READ_FRAMEBUFFER, mResolveFBO); + + ASSERT_GL_NO_ERROR(); + } + + void cleanup() + { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &mResolveFBO); + glDeleteFramebuffers(1, &mSampleFBO); + glDeleteTextures(1, &mResolveTex); + glDeleteRenderbuffers(1, &mSampleRB); + + ASSERT_GL_NO_ERROR(); + + } + + bool isApplicable() const + { + return extensionEnabled("GL_EXT_multisample_compatibility") && + extensionEnabled("GL_ANGLE_framebuffer_multisample") && + extensionEnabled("GL_OES_rgb8_rgba8") && + !IsAMD(); + } + GLuint mSampleFBO; + GLuint mResolveFBO; + GLuint mSampleRB; + GLuint mResolveTex; + + GLuint mColorLoc; + GLuint mProgram; + GLuint mVBO; +}; + +} // + +// Test simple state tracking +TEST_P(EXTMultisampleCompatibilityTest, TestStateTracking) +{ + if (!isApplicable()) + return; + + EXPECT_TRUE(glIsEnabled(GL_MULTISAMPLE_EXT)); + glDisable(GL_MULTISAMPLE_EXT); + EXPECT_FALSE(glIsEnabled(GL_MULTISAMPLE_EXT)); + glEnable(GL_MULTISAMPLE_EXT); + EXPECT_TRUE(glIsEnabled(GL_MULTISAMPLE_EXT)); + + EXPECT_FALSE(glIsEnabled(GL_SAMPLE_ALPHA_TO_ONE_EXT)); + glEnable(GL_SAMPLE_ALPHA_TO_ONE_EXT); + EXPECT_TRUE(glIsEnabled(GL_SAMPLE_ALPHA_TO_ONE_EXT)); + glDisable(GL_SAMPLE_ALPHA_TO_ONE_EXT); + EXPECT_FALSE(glIsEnabled(GL_SAMPLE_ALPHA_TO_ONE_EXT)); + + EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); +} + +// Test that disabling GL_MULTISAMPLE_EXT is handled correctly. +TEST_P(EXTMultisampleCompatibilityTest, DrawAndResolve) +{ + 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}; + static const float kRed[] = {1.0f, 0.0f, 0.0f, 1.0f}; + + // Different drivers seem to behave differently with respect to resulting + // values. These might be due to different MSAA sample counts causing + // different samples to hit. Other option is driver bugs. Just test that + // disabling multisample causes a difference. + std::unique_ptr<uint8_t[]> results[3]; + const GLint kResultSize = kWidth * kHeight * 4; + for (int pass = 0; pass < 3; pass++) + { + prepareForDraw(); + // Green: from top right to bottom left. + glUniform4fv(mColorLoc, 1, kGreen); + glDrawArrays(GL_TRIANGLES, 0, 3); + + // Blue: from top left to bottom right. + glUniform4fv(mColorLoc, 1, kBlue); + glDrawArrays(GL_TRIANGLES, 3, 3); + + // Red, with and without MSAA: from bottom left to top right. + if (pass == 1) + { + glDisable(GL_MULTISAMPLE_EXT); + } + glUniform4fv(mColorLoc, 1, kRed); + glDrawArrays(GL_TRIANGLES, 6, 3); + if (pass == 1) + { + glEnable(GL_MULTISAMPLE_EXT); + } + prepareForVerify(); + results[pass].reset(new uint8_t[kResultSize]); + memset(results[pass].get(), 123u, kResultSize); + glReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, + results[pass].get()); + + cleanup(); + } + 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)); +} + +// Test that enabling GL_SAMPLE_ALPHA_TO_ONE_EXT affects rendering. +TEST_P(EXTMultisampleCompatibilityTest, DrawAlphaOneAndResolve) +{ + if (!isApplicable()) + return; + + // SAMPLE_ALPHA_TO_ONE is specified to transform alpha values of + // covered samples to 1.0. In order to detect it, we use non-1.0 + // alpha. + static const float kBlue[] = {0.0f, 0.0f, 1.0f, 0.5f}; + static const float kGreen[] = {0.0f, 1.0f, 0.0f, 0.5f}; + static const float kRed[] = {1.0f, 0.0f, 0.0f, 0.5f}; + + // Different drivers seem to behave differently with respect to resulting + // alpha value. These might be due to different MSAA sample counts causing + // different samples to hit. Other option is driver bugs. Testing exact or + // even approximate sample values is not that easy. Thus, just test + // representative positions which have fractional pixels, inspecting that + // normal rendering is different to SAMPLE_ALPHA_TO_ONE rendering. + std::unique_ptr<uint8_t[]> results[3]; + const GLint kResultSize = kWidth * kHeight * 4; + + for (int pass = 0; pass < 3; ++pass) + { + prepareForDraw(); + if (pass == 1) + { + glEnable(GL_SAMPLE_ALPHA_TO_ONE_EXT); + } + glEnable(GL_MULTISAMPLE_EXT); + glUniform4fv(mColorLoc, 1, kGreen); + glDrawArrays(GL_TRIANGLES, 0, 3); + + glUniform4fv(mColorLoc, 1, kBlue); + glDrawArrays(GL_TRIANGLES, 3, 3); + + glDisable(GL_MULTISAMPLE_EXT); + glUniform4fv(mColorLoc, 1, kRed); + glDrawArrays(GL_TRIANGLES, 6, 3); + + prepareForVerify(); + results[pass].reset(new uint8_t[kResultSize]); + memset(results[pass].get(), 123u, kResultSize); + glReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, + results[pass].get()); + if (pass == 1) + { + glDisable(GL_SAMPLE_ALPHA_TO_ONE_EXT); + } + + cleanup(); + } + 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)); +} + +ANGLE_INSTANTIATE_TEST(EXTMultisampleCompatibilityTest, ES2_OPENGL(), ES2_OPENGLES(), ES3_OPENGL()); + +class MultisampleCompatibilityTest : public ANGLETest +{ + + protected: + MultisampleCompatibilityTest() + { + setWindowWidth(64); + setWindowHeight(64); + setConfigRedBits(8); + setConfigBlueBits(8); + setConfigAlphaBits(8); + } + + void prepareForDraw(GLsizei numSamples) + { + // Create a sample buffer. + glGenRenderbuffers(1, &mSampleRB); + glBindRenderbuffer(GL_RENDERBUFFER, mSampleRB); + glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, numSamples, GL_RGBA8, kWidth, + kHeight); + GLint param = 0; + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, ¶m); + EXPECT_GE(param, numSamples); + glGenFramebuffers(1, &mSampleFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mSampleFBO); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mSampleRB); + EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + // Create another FBO to resolve the multisample buffer into. + glGenTextures(1, &mResolveTex); + glBindTexture(GL_TEXTURE_2D, mResolveTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, + NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glGenFramebuffers(1, &mResolveFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mResolveFBO); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mResolveTex, 0); + EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); + glViewport(0, 0, kWidth, kHeight); + glBindFramebuffer(GL_FRAMEBUFFER, mSampleFBO); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + ASSERT_GL_NO_ERROR(); + } + + void prepareForVerify() + { + // Resolve. + glBindFramebuffer(GL_READ_FRAMEBUFFER, mSampleFBO); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mResolveFBO); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + glBlitFramebufferANGLE(0, 0, kWidth, kHeight, 0, 0, kWidth, kHeight, GL_COLOR_BUFFER_BIT, + GL_NEAREST); + glBindFramebuffer(GL_READ_FRAMEBUFFER, mResolveFBO); + + ASSERT_GL_NO_ERROR(); + } + + void cleanup() + { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &mResolveFBO); + glDeleteFramebuffers(1, &mSampleFBO); + glDeleteTextures(1, &mResolveTex); + glDeleteRenderbuffers(1, &mSampleRB); + + ASSERT_GL_NO_ERROR(); + } + + bool isApplicable() const + { + return extensionEnabled("GL_ANGLE_framebuffer_multisample") && + extensionEnabled("GL_OES_rgb8_rgba8"); + } + + GLuint mSampleFBO; + GLuint mResolveFBO; + GLuint mSampleRB; + GLuint mResolveTex; +}; + +// Test that enabling GL_SAMPLE_COVERAGE affects rendering. +TEST_P(MultisampleCompatibilityTest, DrawCoverageAndResolve) +{ + if (!isApplicable()) + return; + + // TODO: Figure out why this fails on Android. + if (IsAndroid()) + { + std::cout << "Test skipped on Android." << std::endl; + return; + } + + const std::string &vertex = + "attribute vec4 position;\n" + "void main()\n" + "{ gl_Position = position; }"; + const std::string &fragment = + "void main()\n" + "{ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }"; + + ANGLE_GL_PROGRAM(drawRed, vertex, fragment); + + GLsizei maxSamples = 0; + glGetIntegerv(GL_MAX_SAMPLES, &maxSamples); + int iterationCount = maxSamples + 1; + for (int samples = 1; samples < iterationCount; samples++) + { + prepareForDraw(samples); + glEnable(GL_SAMPLE_COVERAGE); + glSampleCoverage(1.0, false); + drawQuad(drawRed.get(), "position", 0.5f); + + prepareForVerify(); + GLsizei pixelCount = kWidth * kHeight; + std::vector<GLColor> actual(pixelCount, GLColor::black); + glReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, actual.data()); + glDisable(GL_SAMPLE_COVERAGE); + cleanup(); + + std::vector<GLColor> expected(pixelCount, GLColor::red); + EXPECT_EQ(expected, actual); + } +} + +ANGLE_INSTANTIATE_TEST(MultisampleCompatibilityTest, + ES2_D3D9(), + ES2_OPENGL(), + ES2_OPENGLES(), + ES3_D3D11(), + ES3_OPENGL(), + ES3_OPENGLES());
\ No newline at end of file |