| // | |
| // 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, ¶m); | |
| // 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, ¶m); | |
| // 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, | |
| ¶m); | |
| // 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 |