blob: cb90c5bb63f1060a78bcda39b680fd28d7b1d305 [file] [log] [blame]
//
// Copyright 2019 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.
//
// MultisampledRenderToTextureTest: Tests of EXT_multisampled_render_to_texture extension
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle;
namespace
{
constexpr char kBasicVertexShader[] =
R"(attribute vec3 position;
void main()
{
gl_Position = vec4(position, 1);
})";
constexpr char kGreenFragmentShader[] =
R"(void main()
{
gl_FragColor = vec4(0, 1, 0, 1);
})";
constexpr char kRedFragmentShader[] =
R"(void main()
{
gl_FragColor = vec4(1, 0, 0, 1);
})";
constexpr char kVS[] =
"precision highp float;\n"
"attribute vec4 position;\n"
"varying vec2 texcoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = position;\n"
" texcoord = (position.xy * 0.5) + 0.5;\n"
"}\n";
constexpr char kFS[] =
"precision highp float;\n"
"uniform sampler2D tex;\n"
"varying vec2 texcoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_FragColor = texture2D(tex, texcoord);\n"
"}\n";
class MultisampledRenderToTextureTest : public ANGLETest
{
protected:
MultisampledRenderToTextureTest()
{
setWindowWidth(64);
setWindowHeight(64);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
void testSetUp() override {}
void testTearDown() override {}
void setupCopyTexProgram()
{
mCopyTextureProgram.makeRaster(kVS, kFS);
ASSERT_GL_TRUE(mCopyTextureProgram.valid());
mCopyTextureUniformLocation = glGetUniformLocation(mCopyTextureProgram, "tex");
ASSERT_GL_NO_ERROR();
}
void verifyResults(GLuint texture,
GLubyte data[4],
GLint fboSize,
GLint xs,
GLint ys,
GLint xe,
GLint ye)
{
glViewport(0, 0, fboSize, fboSize);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Draw a quad with the target texture
glUseProgram(mCopyTextureProgram);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(mCopyTextureUniformLocation, 0);
drawQuad(mCopyTextureProgram, "position", 0.5f);
// Expect that the rendered quad has the same color as the source texture
EXPECT_PIXEL_NEAR(xs, ys, data[0], data[1], data[2], data[3], 1.0);
EXPECT_PIXEL_NEAR(xs, ye - 1, data[0], data[1], data[2], data[3], 1.0);
EXPECT_PIXEL_NEAR(xe - 1, ys, data[0], data[1], data[2], data[3], 1.0);
EXPECT_PIXEL_NEAR(xe - 1, ye - 1, data[0], data[1], data[2], data[3], 1.0);
EXPECT_PIXEL_NEAR((xs + xe) / 2, (ys + ye) / 2, data[0], data[1], data[2], data[3], 1.0);
}
void clearAndDrawQuad(GLuint program, GLsizei viewportWidth, GLsizei viewportHeight)
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, viewportWidth, viewportHeight);
ASSERT_GL_NO_ERROR();
drawQuad(program, "position", 0.0f);
}
GLProgram mCopyTextureProgram;
GLint mCopyTextureUniformLocation = -1;
};
class MultisampledRenderToTextureES3Test : public MultisampledRenderToTextureTest
{};
// Checking against invalid parameters for RenderbufferStorageMultisampleEXT.
TEST_P(MultisampledRenderToTextureTest, RenderbufferParameterCheck)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
// Positive test case
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, 64, 64);
ASSERT_GL_NO_ERROR();
GLint samples;
glGetIntegerv(GL_MAX_SAMPLES_EXT, &samples);
ASSERT_GL_NO_ERROR();
EXPECT_GE(samples, 1);
// Samples too large
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples + 1, GL_DEPTH_COMPONENT16, 64, 64);
ASSERT_GL_ERROR(GL_INVALID_VALUE);
// Renderbuffer size too large
GLint maxSize;
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxSize);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 2, GL_DEPTH_COMPONENT16, maxSize + 1,
maxSize);
ASSERT_GL_ERROR(GL_INVALID_VALUE);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 2, GL_DEPTH_COMPONENT16, maxSize,
maxSize + 1);
ASSERT_GL_ERROR(GL_INVALID_VALUE);
// Retrieving samples
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, 64, 64);
GLint param = 0;
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES_EXT, &param);
// GE because samples may vary base on implementation. Spec says "the resulting value for
// RENDERBUFFER_SAMPLES_EXT is guaranteed to be greater than or equal to samples and no more
// than the next larger sample count supported by the implementation"
EXPECT_GE(param, 4);
}
// Checking against invalid parameters for FramebufferTexture2DMultisampleEXT.
TEST_P(MultisampledRenderToTextureTest, Texture2DParameterCheck)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
ASSERT_GL_NO_ERROR();
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// Positive test case
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture, 0, 4);
ASSERT_GL_NO_ERROR();
// Attachment not COLOR_ATTACHMENT0
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
texture, 0, 4);
ASSERT_GL_ERROR(GL_INVALID_ENUM);
// Target not framebuffer
glFramebufferTexture2DMultisampleEXT(GL_RENDERBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture, 0, 4);
ASSERT_GL_ERROR(GL_INVALID_ENUM);
GLint samples;
glGetIntegerv(GL_MAX_SAMPLES_EXT, &samples);
ASSERT_GL_NO_ERROR();
EXPECT_GE(samples, 1);
// Samples too large
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture, 0, samples + 1);
ASSERT_GL_ERROR(GL_INVALID_VALUE);
// Retrieving samples
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture, 0, 4);
GLint param = 0;
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT, &param);
// GE because samples may vary base on implementation. Spec says "the resulting value for
// TEXTURE_SAMPLES_EXT is guaranteed to be greater than or equal to samples and no more than the
// next larger sample count supported by the implementation"
EXPECT_GE(param, 4);
}
// Checking against invalid parameters for FramebufferTexture2DMultisampleEXT (cubemap).
TEST_P(MultisampledRenderToTextureTest, TextureCubeMapParameterCheck)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
GLTexture texture;
glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
for (GLenum face = 0; face < 6; face++)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGBA, 64, 64, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
ASSERT_GL_NO_ERROR();
}
GLint samples;
glGetIntegerv(GL_MAX_SAMPLES_EXT, &samples);
ASSERT_GL_NO_ERROR();
EXPECT_GE(samples, 1);
GLFramebuffer FBO;
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
for (GLenum face = 0; face < 6; face++)
{
// Positive test case
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, 0, 4);
ASSERT_GL_NO_ERROR();
// Attachment not COLOR_ATTACHMENT0
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, 0, 4);
ASSERT_GL_ERROR(GL_INVALID_ENUM);
// Target not framebuffer
glFramebufferTexture2DMultisampleEXT(GL_RENDERBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, 0, 4);
ASSERT_GL_ERROR(GL_INVALID_ENUM);
// Samples too large
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, 0,
samples + 1);
ASSERT_GL_ERROR(GL_INVALID_VALUE);
// Retrieving samples
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, 0, 4);
GLint param = 0;
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT,
&param);
// GE because samples may vary base on implementation. Spec says "the resulting value for
// TEXTURE_SAMPLES_EXT is guaranteed to be greater than or equal to samples and no more than
// the next larger sample count supported by the implementation"
EXPECT_GE(param, 4);
}
}
// Checking for framebuffer completeness using extension methods.
TEST_P(MultisampledRenderToTextureTest, FramebufferCompleteness)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
// Checking that Renderbuffer and texture2d having different number of samples results
// in a FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
ASSERT_GL_NO_ERROR();
GLFramebuffer FBO;
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture, 0, 4);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 8, GL_DEPTH_COMPONENT16, 64, 64);
ASSERT_GL_NO_ERROR();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
// Draw test with color attachment only.
TEST_P(MultisampledRenderToTextureTest, 2DColorAttachmentMultisampleDrawTest)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
// Set up texture and bind to FBO
GLsizei size = 6;
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
ASSERT_GL_NO_ERROR();
GLFramebuffer FBO;
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture, 0, 4);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Set viewport and clear to black
glViewport(0, 0, size, size);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// Set up Green square program
ANGLE_GL_PROGRAM(program, kBasicVertexShader, kGreenFragmentShader);
glUseProgram(program);
GLint positionLocation = glGetAttribLocation(program, "position");
ASSERT_NE(-1, positionLocation);
setupQuadVertexBuffer(0.5f, 0.5f);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(positionLocation);
// Draw green square
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
EXPECT_PIXEL_COLOR_EQ(size / 2, size / 2, GLColor::green);
// Set up Red square program
ANGLE_GL_PROGRAM(program2, kBasicVertexShader, kRedFragmentShader);
glUseProgram(program2);
GLint positionLocation2 = glGetAttribLocation(program2, "position");
ASSERT_NE(-1, positionLocation2);
setupQuadVertexBuffer(0.5f, 0.75f);
glVertexAttribPointer(positionLocation2, 3, GL_FLOAT, GL_FALSE, 0, 0);
// Draw red square
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
EXPECT_PIXEL_COLOR_EQ(size / 2, size / 2, GLColor::red);
glDisableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// Draw test using both color and depth attachments.
TEST_P(MultisampledRenderToTextureTest, 2DColorDepthMultisampleDrawTest)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
GLsizei size = 6;
// create complete framebuffer with depth buffer
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
ASSERT_GL_NO_ERROR();
GLFramebuffer FBO;
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture, 0, 4);
GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, size, size);
ASSERT_GL_NO_ERROR();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Set viewport and clear framebuffer
glViewport(0, 0, size, size);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClearDepthf(0.5f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Draw first green square
ANGLE_GL_PROGRAM(program, kBasicVertexShader, kGreenFragmentShader);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GREATER);
glUseProgram(program);
GLint positionLocation = glGetAttribLocation(program, "position");
ASSERT_NE(-1, positionLocation);
setupQuadVertexBuffer(0.8f, 0.5f);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(positionLocation);
// Tests that TRIANGLES works.
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
EXPECT_PIXEL_COLOR_EQ(size / 2, size / 2, GLColor::green);
// Draw red square behind green square
ANGLE_GL_PROGRAM(program2, kBasicVertexShader, kRedFragmentShader);
glUseProgram(program2);
GLint positionLocation2 = glGetAttribLocation(program2, "position");
ASSERT_NE(-1, positionLocation2);
setupQuadVertexBuffer(0.7f, 1.0f);
glVertexAttribPointer(positionLocation2, 3, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
glDisable(GL_DEPTH_TEST);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(size / 2, size / 2, GLColor::green);
glDisableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// Read pixels with pack buffer. ES3+.
TEST_P(MultisampledRenderToTextureES3Test, MultisampleReadPixelsTest)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
// PBO only available ES3 and above
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
GLsizei size = 6;
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, size, size);
ASSERT_GL_NO_ERROR();
GLFramebuffer FBO;
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture, 0, 4);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Set viewport and clear to red
glViewport(0, 0, size, size);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
// Bind Pack Pixel Buffer and read to it
GLBuffer PBO;
glBindBuffer(GL_PIXEL_PACK_BUFFER, PBO);
glBufferData(GL_PIXEL_PACK_BUFFER, 4 * size * size, nullptr, GL_STATIC_DRAW);
glReadPixels(0, 0, size, size, GL_RGBA, GL_UNSIGNED_BYTE, 0);
ASSERT_GL_NO_ERROR();
// Retrieving pixel color
void *mappedPtr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, 32, GL_MAP_READ_BIT);
GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(GLColor::red, dataColor[0]);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
EXPECT_GL_NO_ERROR();
}
// CopyTexImage from a multisampled texture functionality test.
TEST_P(MultisampledRenderToTextureTest, MultisampleCopyTexImageTest)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
GLsizei size = 16;
setupCopyTexProgram();
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// Disable mipmapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLFramebuffer FBO;
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture, 0, 4);
// Set color for framebuffer
glClearColor(0.25f, 1.0f, 0.75f, 0.5f);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
GLTexture copyToTex;
glBindTexture(GL_TEXTURE_2D, copyToTex);
// Disable mipmapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, size, size, 0);
ASSERT_GL_NO_ERROR();
GLubyte expected[4] = {64, 255, 191, 255};
verifyResults(copyToTex, expected, size, 0, 0, size, size);
}
// CopyTexSubImage from a multisampled texture functionality test.
TEST_P(MultisampledRenderToTextureTest, MultisampleCopyTexSubImageTest)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
GLsizei size = 16;
setupCopyTexProgram();
GLTexture texture;
// Create texture in copyFBO0 with color (.25, 1, .75, .5)
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// Disable mipmapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLFramebuffer copyFBO0;
glBindFramebuffer(GL_FRAMEBUFFER, copyFBO0);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture, 0, 4);
// Set color for
glClearColor(0.25f, 1.0f, 0.75f, 0.5f);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
// Create texture in copyFBO[1] with color (1, .75, .5, .25)
GLTexture texture1;
glBindTexture(GL_TEXTURE_2D, texture1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// Disable mipmapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLFramebuffer copyFBO1;
glBindFramebuffer(GL_FRAMEBUFFER, copyFBO1);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture1, 0, 4);
// Set color for
glClearColor(1.0f, 0.75f, 0.5f, 0.25f);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
GLTexture copyToTex;
glBindTexture(GL_TEXTURE_2D, copyToTex);
// Disable mipmapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// copyFBO0 -> copyToTex
// copyToTex should hold what was originally in copyFBO0 : (.25, 1, .75, .5)
glBindFramebuffer(GL_FRAMEBUFFER, copyFBO0);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, size, size, 0);
ASSERT_GL_NO_ERROR();
GLubyte expected0[4] = {64, 255, 191, 255};
verifyResults(copyToTex, expected0, size, 0, 0, size, size);
// copyFBO[1] - copySubImage -> copyToTex
// copyToTex should have subportion what was in copyFBO[1] : (1, .75, .5, .25)
// The rest should still be untouched: (.25, 1, .75, .5)
GLint half = size / 2;
glBindFramebuffer(GL_FRAMEBUFFER, copyFBO1);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, half, half, half, half, half, half);
ASSERT_GL_NO_ERROR();
GLubyte expected1[4] = {255, 191, 127, 255};
verifyResults(copyToTex, expected1, size, half, half, size, size);
// Verify rest is untouched
verifyResults(copyToTex, expected0, size, 0, 0, half, half);
verifyResults(copyToTex, expected0, size, 0, half, half, size);
verifyResults(copyToTex, expected0, size, half, 0, size, half);
}
// BlitFramebuffer functionality test. ES3+.
TEST_P(MultisampledRenderToTextureES3Test, MultisampleBlitFramebufferTest)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
// blitFramebuffer only available ES3 and above
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
GLsizei size = 16;
// Create multisampled framebuffer to use as source.
GLRenderbuffer depthMS;
glBindRenderbuffer(GL_RENDERBUFFER, depthMS.get());
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT24, size, size);
GLTexture colorMS;
glBindTexture(GL_TEXTURE_2D, colorMS);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLFramebuffer fboMS;
glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthMS.get());
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
colorMS, 0, 4);
ASSERT_GL_NO_ERROR();
// Clear depth to 0.5 and color to green.
glClearDepthf(0.5f);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glFlush();
ASSERT_GL_NO_ERROR();
// Draw red into the multisampled color buffer.
ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_EQUAL);
drawQuad(drawRed.get(), essl1_shaders::PositionAttrib(), 0.0f);
ASSERT_GL_NO_ERROR();
// Create single sampled framebuffer to use as dest.
GLFramebuffer fboSS;
glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
GLTexture colorSS;
glBindTexture(GL_TEXTURE_2D, colorSS);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorSS, 0);
ASSERT_GL_NO_ERROR();
// Bind MS to READ as SS is already bound to DRAW.
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMS.get());
glBlitFramebuffer(0, 0, size, size, 0, 0, size, size, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Bind SS to READ so we can readPixels from it
glBindFramebuffer(GL_FRAMEBUFFER, fboSS.get());
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(size - 1, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(0, size - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(size - 1, size - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(size / 2, size / 2, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// GenerateMipmap functionality test
TEST_P(MultisampledRenderToTextureTest, MultisampleGenerateMipmapTest)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
GLsizei size = 64;
// Vertex Shader source
constexpr char kVS[] = R"(attribute vec4 position;
varying vec2 vTexCoord;
void main()
{
gl_Position = position;
vTexCoord = (position.xy * 0.5) + 0.5;
})";
// Fragment Shader source
constexpr char kFS[] = R"(precision mediump float;
uniform sampler2D uTexture;
varying vec2 vTexCoord;
void main()
{
gl_FragColor = texture2D(uTexture, vTexCoord);
})";
GLProgram m2DProgram;
m2DProgram.makeRaster(kVS, kFS);
ASSERT_GL_TRUE(m2DProgram.valid());
ASSERT_GL_NO_ERROR();
// Initialize texture with blue
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size, size, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GLFramebuffer FBO;
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture, 0, 4);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, size, size);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
ASSERT_GL_NO_ERROR();
// Generate mipmap
glGenerateMipmap(GL_TEXTURE_2D);
ASSERT_GL_NO_ERROR();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
// Now draw the texture to various different sized areas.
clearAndDrawQuad(m2DProgram, size, size);
EXPECT_PIXEL_COLOR_EQ(size / 2, size / 2, GLColor::blue);
// Use mip level 1
clearAndDrawQuad(m2DProgram, size / 2, size / 2);
EXPECT_PIXEL_COLOR_EQ(size / 4, size / 4, GLColor::blue);
// Use mip level 2
clearAndDrawQuad(m2DProgram, size / 4, size / 4);
EXPECT_PIXEL_COLOR_EQ(size / 8, size / 8, GLColor::blue);
ASSERT_GL_NO_ERROR();
}
ANGLE_INSTANTIATE_TEST(MultisampledRenderToTextureTest,
ES2_D3D9(),
ES2_D3D11(),
ES3_D3D11(),
ES2_OPENGL(),
ES3_OPENGL(),
ES2_OPENGLES(),
ES3_OPENGLES(),
ES2_VULKAN(),
ES3_VULKAN());
ANGLE_INSTANTIATE_TEST(MultisampledRenderToTextureES3Test,
ES3_D3D11(),
ES3_OPENGL(),
ES3_OPENGLES(),
ES3_VULKAN());
} // namespace