| // | |
| // 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. | |
| // | |
| // MultisampleCompatibilityTest.cpp: | |
| // Tests for the EXT_multisample_compatibility extension. | |
| // | |
| #include "test_utils/ANGLETest.h" | |
| #include "test_utils/gl_raii.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 testSetUp() override | |
| { | |
| mProgram = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); | |
| GLuint position_loc = glGetAttribLocation(mProgram, essl1_shaders::PositionAttrib()); | |
| mColorLoc = glGetUniformLocation(mProgram, essl1_shaders::ColorUniform()); | |
| 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 testTearDown() override | |
| { | |
| glDeleteBuffers(1, &mVBO); | |
| glDeleteProgram(mProgram); | |
| } | |
| 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, | |
| nullptr); | |
| 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 IsGLExtensionEnabled("GL_EXT_multisample_compatibility") && | |
| IsGLExtensionEnabled("GL_ANGLE_framebuffer_multisample") && | |
| IsGLExtensionEnabled("GL_OES_rgb8_rgba8") && !IsAMD(); | |
| } | |
| GLuint mSampleFBO; | |
| GLuint mResolveFBO; | |
| GLuint mSampleRB; | |
| GLuint mResolveTex; | |
| GLuint mColorLoc; | |
| GLuint mProgram; | |
| GLuint mVBO; | |
| }; | |
| } // namespace | |
| // 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_ES2_AND_ES3(EXTMultisampleCompatibilityTest); | |
| 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, | |
| nullptr); | |
| 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 IsGLExtensionEnabled("GL_ANGLE_framebuffer_multisample") && | |
| IsGLExtensionEnabled("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. | |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); | |
| // http://anglebug.com/3855 | |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsVulkan() && IsIntel()); | |
| ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); | |
| 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(), essl1_shaders::PositionAttrib(), 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_ES2_AND_ES3(MultisampleCompatibilityTest); |