blob: e2203be41f6e7043a76d371987f9437f450504e0 [file] [log] [blame]
//
// 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"
#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,
&param);
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 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;
// TODO: Figure out why this fails on Android.
if (IsAndroid())
{
std::cout << "Test skipped on Android." << std::endl;
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, &param);
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 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());