| // |
| // 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. |
| // |
| // ReadPixelsTest: |
| // Tests calls related to glReadPixels. |
| // |
| |
| #include "test_utils/ANGLETest.h" |
| |
| #include <array> |
| |
| #include "test_utils/gl_raii.h" |
| #include "util/random_utils.h" |
| |
| using namespace angle; |
| |
| namespace |
| { |
| |
| class ReadPixelsTest : public ANGLETest<> |
| { |
| protected: |
| ReadPixelsTest() |
| { |
| setWindowWidth(32); |
| setWindowHeight(32); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| }; |
| |
| // Test out of bounds framebuffer reads. |
| TEST_P(ReadPixelsTest, OutOfBounds) |
| { |
| // TODO: re-enable once root cause of http://anglebug.com/1413 is fixed |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES()); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLsizei pixelsWidth = 32; |
| GLsizei pixelsHeight = 32; |
| GLint offset = 16; |
| std::vector<GLColor> pixels((pixelsWidth + offset) * (pixelsHeight + offset)); |
| |
| glReadPixels(-offset, -offset, pixelsWidth + offset, pixelsHeight + offset, GL_RGBA, |
| GL_UNSIGNED_BYTE, &pixels[0]); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Expect that all pixels which fell within the framebuffer are red |
| for (int y = pixelsHeight / 2; y < pixelsHeight; y++) |
| { |
| for (int x = pixelsWidth / 2; x < pixelsWidth; x++) |
| { |
| EXPECT_EQ(GLColor::red, pixels[y * (pixelsWidth + offset) + x]); |
| } |
| } |
| } |
| |
| class ReadPixelsPBONVTest : public ReadPixelsTest |
| { |
| protected: |
| ReadPixelsPBONVTest() : mPBO(0), mTexture(0), mFBO(0) {} |
| |
| void testSetUp() override |
| { |
| glGenBuffers(1, &mPBO); |
| glGenFramebuffers(1, &mFBO); |
| |
| Reset(4 * getWindowWidth() * getWindowHeight(), 4, 4); |
| } |
| |
| virtual void Reset(GLuint bufferSize, GLuint fboWidth, GLuint fboHeight) |
| { |
| ANGLE_SKIP_TEST_IF(!hasPBOExts()); |
| |
| mPBOBufferSize = bufferSize; |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glBufferData(GL_PIXEL_PACK_BUFFER, mPBOBufferSize, nullptr, GL_STATIC_DRAW); |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| |
| glDeleteTextures(1, &mTexture); |
| glGenTextures(1, &mTexture); |
| glBindTexture(GL_TEXTURE_2D, mTexture); |
| glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, fboWidth, fboHeight); |
| mFBOWidth = fboWidth; |
| mFBOHeight = fboHeight; |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0); |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void testTearDown() override |
| { |
| glDeleteBuffers(1, &mPBO); |
| glDeleteTextures(1, &mTexture); |
| glDeleteFramebuffers(1, &mFBO); |
| } |
| |
| bool hasPBOExts() const |
| { |
| return IsGLExtensionEnabled("GL_NV_pixel_buffer_object") && |
| IsGLExtensionEnabled("GL_EXT_texture_storage"); |
| } |
| |
| GLuint mPBO = 0; |
| GLuint mTexture = 0; |
| GLuint mFBO = 0; |
| GLuint mFBOWidth = 0; |
| GLuint mFBOHeight = 0; |
| GLuint mPBOBufferSize = 0; |
| }; |
| |
| // Test basic usage of PBOs. |
| TEST_P(ReadPixelsPBONVTest, Basic) |
| { |
| ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") || |
| !IsGLExtensionEnabled("GL_OES_mapbuffer")); |
| |
| // http://anglebug.com/5022 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsDesktopOpenGL()); |
| // http://anglebug.com/5386 |
| ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsDesktopOpenGL()); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| // Clear last pixel to green |
| glScissor(15, 15, 1, 1); |
| glEnable(GL_SCISSOR_TEST); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
| |
| void *mappedPtr = glMapBufferRangeEXT(GL_PIXEL_PACK_BUFFER, 0, mPBOBufferSize, GL_MAP_READ_BIT); |
| GLColor *dataColor = static_cast<GLColor *>(mappedPtr); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(GLColor::red, dataColor[0]); |
| EXPECT_EQ(GLColor::red, dataColor[16 * 16 - 2]); |
| EXPECT_EQ(GLColor::green, dataColor[16 * 16 - 1]); |
| |
| glUnmapBufferOES(GL_PIXEL_PACK_BUFFER); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test that calling SubData preserves PBO data. |
| TEST_P(ReadPixelsPBONVTest, SubDataPreservesContents) |
| { |
| ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") || |
| !IsGLExtensionEnabled("GL_OES_mapbuffer")); |
| |
| // anglebug.com/2185 |
| ANGLE_SKIP_TEST_IF(IsMac() && IsNVIDIA() && IsDesktopOpenGL()); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
| |
| unsigned char data[4] = {1, 2, 3, 4}; |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| glBindBuffer(GL_ARRAY_BUFFER, mPBO); |
| glBufferSubData(GL_ARRAY_BUFFER, 0, 4, data); |
| |
| void *mappedPtr = glMapBufferRangeEXT(GL_ARRAY_BUFFER, 0, 32, GL_MAP_READ_BIT); |
| GLColor *dataColor = static_cast<GLColor *>(mappedPtr); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(GLColor(1, 2, 3, 4), dataColor[0]); |
| EXPECT_EQ(GLColor::red, dataColor[1]); |
| |
| glUnmapBufferOES(GL_ARRAY_BUFFER); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test that calling ReadPixels with GL_DYNAMIC_DRAW buffer works |
| TEST_P(ReadPixelsPBONVTest, DynamicPBO) |
| { |
| ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") || |
| !IsGLExtensionEnabled("GL_OES_mapbuffer")); |
| |
| // anglebug.com/2185 |
| ANGLE_SKIP_TEST_IF(IsMac() && IsNVIDIA() && IsDesktopOpenGL()); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glBufferData(GL_PIXEL_PACK_BUFFER, 4 * getWindowWidth() * getWindowHeight(), nullptr, |
| GL_DYNAMIC_DRAW); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
| |
| unsigned char data[4] = {1, 2, 3, 4}; |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| glBindBuffer(GL_ARRAY_BUFFER, mPBO); |
| glBufferSubData(GL_ARRAY_BUFFER, 0, 4, data); |
| |
| void *mappedPtr = glMapBufferRangeEXT(GL_ARRAY_BUFFER, 0, 32, GL_MAP_READ_BIT); |
| GLColor *dataColor = static_cast<GLColor *>(mappedPtr); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(GLColor(1, 2, 3, 4), dataColor[0]); |
| EXPECT_EQ(GLColor::red, dataColor[1]); |
| |
| glUnmapBufferOES(GL_ARRAY_BUFFER); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| TEST_P(ReadPixelsPBONVTest, ReadFromFBO) |
| { |
| ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") || |
| !IsGLExtensionEnabled("GL_OES_mapbuffer")); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| glViewport(0, 0, mFBOWidth, mFBOHeight); |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| // Clear last pixel to green |
| glScissor(mFBOWidth - 1, mFBOHeight - 1, 1, 1); |
| glEnable(GL_SCISSOR_TEST); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glReadPixels(0, 0, mFBOWidth, mFBOHeight, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
| |
| void *mappedPtr = |
| glMapBufferRangeEXT(GL_PIXEL_PACK_BUFFER, 0, 4 * mFBOWidth * mFBOHeight, GL_MAP_READ_BIT); |
| GLColor *dataColor = static_cast<GLColor *>(mappedPtr); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(GLColor::red, dataColor[0]); |
| EXPECT_EQ(GLColor::red, dataColor[mFBOWidth * mFBOHeight - 2]); |
| EXPECT_EQ(GLColor::green, dataColor[mFBOWidth * mFBOHeight - 1]); |
| |
| glUnmapBufferOES(GL_PIXEL_PACK_BUFFER); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test calling ReadPixels with a non-zero "data" param into a PBO |
| TEST_P(ReadPixelsPBONVTest, ReadFromFBOWithDataOffset) |
| { |
| ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") || |
| !IsGLExtensionEnabled("GL_OES_mapbuffer")); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| glViewport(0, 0, mFBOWidth, mFBOHeight); |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| // Clear first pixel to green |
| glScissor(0, 0, 1, 1); |
| glEnable(GL_SCISSOR_TEST); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| |
| // Read (height - 1) rows offset by width * 4. |
| glReadPixels(0, 0, mFBOWidth, mFBOHeight - 1, GL_RGBA, GL_UNSIGNED_BYTE, |
| reinterpret_cast<void *>(mFBOWidth * static_cast<uintptr_t>(4))); |
| |
| void *mappedPtr = |
| glMapBufferRangeEXT(GL_PIXEL_PACK_BUFFER, 0, 4 * mFBOWidth * mFBOHeight, GL_MAP_READ_BIT); |
| GLColor *dataColor = static_cast<GLColor *>(mappedPtr); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(GLColor::green, dataColor[mFBOWidth]); |
| EXPECT_EQ(GLColor::red, dataColor[mFBOWidth + 1]); |
| EXPECT_EQ(GLColor::red, dataColor[mFBOWidth * mFBOHeight - 1]); |
| |
| glUnmapBufferOES(GL_PIXEL_PACK_BUFFER); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| class ReadPixelsPBOTest : public ReadPixelsPBONVTest |
| { |
| protected: |
| ReadPixelsPBOTest() : ReadPixelsPBONVTest() {} |
| |
| void testSetUp() override |
| { |
| glGenBuffers(1, &mPBO); |
| glGenFramebuffers(1, &mFBO); |
| |
| Reset(4 * getWindowWidth() * getWindowHeight(), 4, 1); |
| } |
| |
| void Reset(GLuint bufferSize, GLuint fboWidth, GLuint fboHeight) override |
| { |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glBufferData(GL_PIXEL_PACK_BUFFER, bufferSize, nullptr, GL_STATIC_DRAW); |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| |
| glDeleteTextures(1, &mTexture); |
| glGenTextures(1, &mTexture); |
| glBindTexture(GL_TEXTURE_2D, mTexture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, fboWidth, fboHeight); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0); |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| |
| mFBOWidth = fboWidth; |
| mFBOHeight = fboHeight; |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| }; |
| |
| // Test basic usage of PBOs. |
| TEST_P(ReadPixelsPBOTest, Basic) |
| { |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
| |
| 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(); |
| } |
| |
| // Test copy to snorm |
| TEST_P(ReadPixelsPBOTest, Snorm) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_render_snorm")); |
| |
| constexpr GLsizei kSize = 6; |
| |
| GLRenderbuffer rbo; |
| glBindRenderbuffer(GL_RENDERBUFFER, rbo); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_SNORM, kSize, kSize); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| ASSERT_GL_NO_ERROR(); |
| |
| glEnable(GL_SCISSOR_TEST); |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glScissor(0, 0, kSize / 2, kSize / 2); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glScissor(kSize / 2, 0, kSize / 2, kSize / 2); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glClearColor(0.0f, 0.0f, 1.0f, 1.0f); |
| glScissor(0, kSize / 2, kSize / 2, kSize / 2); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glClearColor(1.0f, 1.0f, 0.0f, 1.0f); |
| glScissor(kSize / 2, kSize / 2, kSize / 2, kSize / 2); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| glDisable(GL_SCISSOR_TEST); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glReadPixels(0, 0, kSize, kSize, GL_RGBA, GL_BYTE, 0); |
| |
| std::vector<GLColor> result(kSize * kSize); |
| void *mappedPtr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, kSize * kSize * 4, GL_MAP_READ_BIT); |
| memcpy(result.data(), mappedPtr, kSize * kSize * 4); |
| glUnmapBuffer(GL_PIXEL_PACK_BUFFER); |
| EXPECT_GL_NO_ERROR(); |
| |
| auto verify = [&](const GLColor expect[4]) { |
| for (size_t i = 0; i < kSize; ++i) |
| { |
| for (size_t j = 0; j < kSize; ++j) |
| { |
| uint32_t index = (i < kSize / 2 ? 0 : 1) << 1 | (j < kSize / 2 ? 0 : 1); |
| EXPECT_EQ(result[i * kSize + j], expect[index]) << i << " " << j; |
| } |
| } |
| }; |
| |
| // The image should have the following colors |
| // |
| // +---+---+ |
| // | R | G | |
| // +---+---+ |
| // | B | Y | |
| // +---+---+ |
| // |
| const GLColor kColors[4] = { |
| GLColor(127, 0, 0, 127), |
| GLColor(0, 127, 0, 127), |
| GLColor(0, 0, 127, 127), |
| GLColor(127, 127, 0, 127), |
| }; |
| verify(kColors); |
| |
| // Test again, but this time with reverse order |
| if (EnsureGLExtensionEnabled("GL_ANGLE_pack_reverse_row_order")) |
| { |
| glPixelStorei(GL_PACK_REVERSE_ROW_ORDER_ANGLE, GL_TRUE); |
| glReadPixels(0, 0, kSize, kSize, GL_RGBA, GL_BYTE, 0); |
| |
| mappedPtr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, kSize * kSize * 4, GL_MAP_READ_BIT); |
| memcpy(result.data(), mappedPtr, kSize * kSize * 4); |
| glUnmapBuffer(GL_PIXEL_PACK_BUFFER); |
| EXPECT_GL_NO_ERROR(); |
| |
| const GLColor kReversedColors[4] = { |
| GLColor(0, 0, 127, 127), |
| GLColor(127, 127, 0, 127), |
| GLColor(127, 0, 0, 127), |
| GLColor(0, 127, 0, 127), |
| }; |
| verify(kReversedColors); |
| } |
| } |
| |
| // Test an error is generated when the PBO is too small. |
| TEST_P(ReadPixelsPBOTest, PBOTooSmall) |
| { |
| Reset(4 * 16 * 16 - 1, 16, 16); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
| |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| |
| // Test an error is generated when the PBO is mapped. |
| TEST_P(ReadPixelsPBOTest, PBOMapped) |
| { |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, 32, GL_MAP_READ_BIT); |
| glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
| |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| |
| // Test that binding a PBO to ARRAY_BUFFER works as expected. |
| TEST_P(ReadPixelsPBOTest, ArrayBufferTarget) |
| { |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| glBindBuffer(GL_ARRAY_BUFFER, mPBO); |
| |
| void *mappedPtr = glMapBufferRange(GL_ARRAY_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_ARRAY_BUFFER); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test that using a PBO does not overwrite existing data. |
| TEST_P(ReadPixelsPBOTest, ExistingDataPreserved) |
| { |
| // Clear backbuffer to red |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Read 16x16 region from red backbuffer to PBO |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
| |
| // Clear backbuffer to green |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Read 16x16 region from green backbuffer to PBO at offset 16 |
| glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<void *>(16)); |
| void *mappedPtr = |
| glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, 17 * sizeof(GLColor), GL_MAP_READ_BIT); |
| GLColor *dataColor = static_cast<GLColor *>(mappedPtr); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Test pixel 0 is red (existing data) |
| EXPECT_EQ(GLColor::red, dataColor[0]); |
| |
| // Test pixel 16 is green (new data) |
| EXPECT_EQ(GLColor::green, dataColor[16]); |
| |
| glUnmapBuffer(GL_PIXEL_PACK_BUFFER); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test that calling SubData preserves PBO data. |
| TEST_P(ReadPixelsPBOTest, SubDataPreservesContents) |
| { |
| // anglebug.com/2185 |
| ANGLE_SKIP_TEST_IF(IsMac() && IsNVIDIA() && IsDesktopOpenGL()); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
| |
| unsigned char data[4] = {1, 2, 3, 4}; |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| glBindBuffer(GL_ARRAY_BUFFER, mPBO); |
| glBufferSubData(GL_ARRAY_BUFFER, 0, 4, data); |
| |
| void *mappedPtr = glMapBufferRange(GL_ARRAY_BUFFER, 0, 32, GL_MAP_READ_BIT); |
| GLColor *dataColor = static_cast<GLColor *>(mappedPtr); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(GLColor(1, 2, 3, 4), dataColor[0]); |
| |
| glUnmapBuffer(GL_ARRAY_BUFFER); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Same as the prior test, but with an offset. |
| TEST_P(ReadPixelsPBOTest, SubDataOffsetPreservesContents) |
| { |
| // anglebug.com/1415 |
| ANGLE_SKIP_TEST_IF(IsNexus5X() && IsAdreno() && IsOpenGLES()); |
| // anglebug.com/2185 |
| ANGLE_SKIP_TEST_IF(IsMac() && IsNVIDIA() && IsDesktopOpenGL()); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
| |
| unsigned char data[4] = {1, 2, 3, 4}; |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| glBindBuffer(GL_ARRAY_BUFFER, mPBO); |
| glBufferSubData(GL_ARRAY_BUFFER, 16, 4, data); |
| |
| void *mappedPtr = glMapBufferRange(GL_ARRAY_BUFFER, 0, 32, GL_MAP_READ_BIT); |
| GLColor *dataColor = static_cast<GLColor *>(mappedPtr); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(GLColor::red, dataColor[0]); |
| EXPECT_EQ(GLColor(1, 2, 3, 4), dataColor[4]); |
| |
| glUnmapBuffer(GL_ARRAY_BUFFER); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test that uploading data to buffer that's in use then writing to it as PBO works. |
| TEST_P(ReadPixelsPBOTest, UseAsUBOThenUpdateThenReadFromFBO) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| glViewport(0, 0, mFBOWidth, mFBOHeight); |
| |
| const std::array<GLColor, 4> kInitialData = {GLColor::red, GLColor::red, GLColor::red, |
| GLColor::red}; |
| const std::array<GLColor, 4> kUpdateData = {GLColor::white, GLColor::white, GLColor::white, |
| GLColor::white}; |
| |
| GLBuffer buffer; |
| glBindBuffer(GL_UNIFORM_BUFFER, buffer); |
| glBufferData(GL_UNIFORM_BUFFER, sizeof(kInitialData), kInitialData.data(), GL_DYNAMIC_COPY); |
| glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| constexpr char kVerifyUBO[] = R"(#version 300 es |
| precision mediump float; |
| uniform block { |
| uvec4 data; |
| } ubo; |
| out vec4 colorOut; |
| void main() |
| { |
| if (all(equal(ubo.data, uvec4(0xFF0000FFu)))) |
| colorOut = vec4(0, 1.0, 0, 1.0); |
| else |
| colorOut = vec4(1.0, 0, 0, 1.0); |
| })"; |
| |
| ANGLE_GL_PROGRAM(verifyUbo, essl3_shaders::vs::Simple(), kVerifyUBO); |
| drawQuad(verifyUbo, essl3_shaders::PositionAttrib(), 0.5); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Update buffer data |
| glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(kInitialData), kUpdateData.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Clear first pixel to blue |
| glClearColor(0.0f, 0.0f, 1.0f, 1.0f); |
| glScissor(0, 0, 1, 1); |
| glEnable(GL_SCISSOR_TEST); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer); |
| |
| // Read the framebuffer pixels |
| glReadPixels(0, 0, mFBOWidth, mFBOHeight, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| |
| void *mappedPtr = |
| glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, sizeof(kInitialData), GL_MAP_READ_BIT); |
| GLColor *dataColor = static_cast<GLColor *>(mappedPtr); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(GLColor::blue, dataColor[0]); |
| EXPECT_EQ(GLColor::green, dataColor[1]); |
| EXPECT_EQ(GLColor::green, dataColor[2]); |
| EXPECT_EQ(GLColor::green, dataColor[3]); |
| |
| glUnmapBuffer(GL_PIXEL_PACK_BUFFER); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| EXPECT_PIXEL_COLOR_EQ(1, 0, GLColor::green); |
| EXPECT_PIXEL_COLOR_EQ(2, 0, GLColor::green); |
| EXPECT_PIXEL_COLOR_EQ(3, 0, GLColor::green); |
| } |
| |
| class ReadPixelsPBODrawTest : public ReadPixelsPBOTest |
| { |
| protected: |
| ReadPixelsPBODrawTest() : mProgram(0), mPositionVBO(0) {} |
| |
| void testSetUp() override |
| { |
| ReadPixelsPBOTest::testSetUp(); |
| |
| constexpr char kVS[] = |
| "attribute vec4 aTest; attribute vec2 aPosition; varying vec4 vTest;\n" |
| "void main()\n" |
| "{\n" |
| " vTest = aTest;\n" |
| " gl_Position = vec4(aPosition, 0.0, 1.0);\n" |
| " gl_PointSize = 1.0;\n" |
| "}"; |
| |
| constexpr char kFS[] = |
| "precision mediump float; varying vec4 vTest;\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = vTest;\n" |
| "}"; |
| |
| mProgram = CompileProgram(kVS, kFS); |
| ASSERT_NE(0u, mProgram); |
| |
| glGenBuffers(1, &mPositionVBO); |
| glBindBuffer(GL_ARRAY_BUFFER, mPositionVBO); |
| glBufferData(GL_ARRAY_BUFFER, 128, nullptr, GL_DYNAMIC_DRAW); |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| } |
| |
| void testTearDown() override |
| { |
| glDeleteProgram(mProgram); |
| glDeleteBuffers(1, &mPositionVBO); |
| ReadPixelsPBOTest::testTearDown(); |
| } |
| |
| GLuint mProgram; |
| GLuint mPositionVBO; |
| }; |
| |
| // Test that we can draw with PBO data. |
| TEST_P(ReadPixelsPBODrawTest, DrawWithPBO) |
| { |
| GLColor color(1, 2, 3, 4); |
| glBindTexture(GL_TEXTURE_2D, mTexture); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &color); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, mFBO); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| EXPECT_GL_NO_ERROR(); |
| |
| float positionData[] = {0.5f, 0.5f}; |
| |
| glUseProgram(mProgram); |
| glViewport(0, 0, 1, 1); |
| glBindBuffer(GL_ARRAY_BUFFER, mPositionVBO); |
| glBufferSubData(GL_ARRAY_BUFFER, 0, 1 * 2 * 4, positionData); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLint positionLocation = glGetAttribLocation(mProgram, "aPosition"); |
| EXPECT_NE(-1, positionLocation); |
| |
| GLint testLocation = glGetAttribLocation(mProgram, "aTest"); |
| EXPECT_NE(-1, testLocation); |
| |
| glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); |
| glEnableVertexAttribArray(positionLocation); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_ARRAY_BUFFER, mPBO); |
| glVertexAttribPointer(testLocation, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, 0); |
| glEnableVertexAttribArray(testLocation); |
| EXPECT_GL_NO_ERROR(); |
| |
| glDrawArrays(GL_POINTS, 0, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| color = GLColor(0, 0, 0, 0); |
| glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &color); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(GLColor(1, 2, 3, 4), color); |
| } |
| |
| // Test that we can correctly update a buffer bound to the vertex stage with PBO. |
| TEST_P(ReadPixelsPBODrawTest, UpdateVertexArrayWithPixelPack) |
| { |
| glUseProgram(mProgram); |
| glViewport(0, 0, 1, 1); |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| ASSERT_GL_NO_ERROR(); |
| |
| // First draw with pre-defined data. |
| std::array<float, 2> positionData = {0.5f, 0.5f}; |
| |
| glBindBuffer(GL_ARRAY_BUFFER, mPositionVBO); |
| glBufferSubData(GL_ARRAY_BUFFER, 0, positionData.size() * sizeof(positionData[0]), |
| positionData.data()); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLint positionLocation = glGetAttribLocation(mProgram, "aPosition"); |
| EXPECT_NE(-1, positionLocation); |
| |
| GLint testLocation = glGetAttribLocation(mProgram, "aTest"); |
| EXPECT_NE(-1, testLocation); |
| |
| glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); |
| glEnableVertexAttribArray(positionLocation); |
| ASSERT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_ARRAY_BUFFER, mPBO); |
| glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLColor), &GLColor::red); |
| glVertexAttribPointer(testLocation, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, 0); |
| glEnableVertexAttribArray(testLocation); |
| ASSERT_GL_NO_ERROR(); |
| |
| glDrawArrays(GL_POINTS, 0, 1); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Update the buffer bound to the VAO with a PBO. |
| glBindTexture(GL_TEXTURE_2D, mTexture); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::green); |
| ASSERT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw again and verify the VAO has the updated data. |
| glDrawArrays(GL_POINTS, 0, 1); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| class ReadPixelsMultisampleTest : public ReadPixelsTest |
| { |
| protected: |
| ReadPixelsMultisampleTest() : mFBO(0), mRBO(0), mPBO(0) {} |
| |
| void testSetUp() override |
| { |
| glGenFramebuffers(1, &mFBO); |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| |
| glGenRenderbuffers(1, &mRBO); |
| glBindRenderbuffer(GL_RENDERBUFFER, mRBO); |
| |
| glGenBuffers(1, &mPBO); |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| glBufferData(GL_PIXEL_PACK_BUFFER, 4 * getWindowWidth() * getWindowHeight(), nullptr, |
| GL_STATIC_DRAW); |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void testTearDown() override |
| { |
| glDeleteFramebuffers(1, &mFBO); |
| glDeleteRenderbuffers(1, &mRBO); |
| glDeleteBuffers(1, &mPBO); |
| } |
| |
| GLuint mFBO; |
| GLuint mRBO; |
| GLuint mPBO; |
| }; |
| |
| // Test ReadPixels from a multisampled framebuffer. |
| TEST_P(ReadPixelsMultisampleTest, BasicClear) |
| { |
| if (getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_ANGLE_framebuffer_multisample")) |
| { |
| std::cout |
| << "Test skipped because ES3 or GL_ANGLE_framebuffer_multisample is not available." |
| << std::endl; |
| return; |
| } |
| |
| if (IsGLExtensionEnabled("GL_ANGLE_framebuffer_multisample")) |
| { |
| glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, 2, GL_RGBA8, 4, 4); |
| } |
| else |
| { |
| glRenderbufferStorageMultisample(GL_RENDERBUFFER, 2, GL_RGBA8, 4, 4); |
| } |
| |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRBO); |
| ASSERT_GL_NO_ERROR(); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO); |
| EXPECT_GL_NO_ERROR(); |
| |
| glReadPixels(0, 0, 1, 1, GL_RGBA8, GL_UNSIGNED_BYTE, nullptr); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| |
| class ReadPixelsTextureTest : public ANGLETest<> |
| { |
| public: |
| ReadPixelsTextureTest() : mFBO(0), mTextureRGBA(0), mTextureBGRA(0) |
| { |
| setWindowWidth(32); |
| setWindowHeight(32); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| |
| void testSetUp() override |
| { |
| glGenTextures(1, &mTextureRGBA); |
| glGenTextures(1, &mTextureBGRA); |
| glGenFramebuffers(1, &mFBO); |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| } |
| |
| void testTearDown() override |
| { |
| glDeleteFramebuffers(1, &mFBO); |
| glDeleteTextures(1, &mTextureRGBA); |
| glDeleteTextures(1, &mTextureBGRA); |
| } |
| |
| void initTextureRGBA(GLenum textureTarget, |
| GLint levels, |
| GLint attachmentLevel, |
| GLint attachmentLayer) |
| { |
| glBindTexture(textureTarget, mTextureRGBA); |
| glTexStorage3D(textureTarget, levels, GL_RGBA8, kSize, kSize, kSize); |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextureRGBA, |
| attachmentLevel, attachmentLayer); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| ASSERT_GL_NO_ERROR(); |
| initializeTextureData(textureTarget, levels, GL_RGBA); |
| } |
| |
| void initTextureBGRA(GLenum textureTarget, |
| GLint levels, |
| GLint attachmentLevel, |
| GLint attachmentLayer) |
| { |
| glBindTexture(textureTarget, mTextureBGRA); |
| for (GLint level = 0; level < levels; ++level) |
| { |
| glTexImage3D(textureTarget, level, GL_BGRA_EXT, kSize >> level, kSize >> level, |
| textureTarget == GL_TEXTURE_3D ? kSize >> level : kSize, 0, GL_BGRA_EXT, |
| GL_UNSIGNED_BYTE, nullptr); |
| } |
| glTexParameteri(textureTarget, GL_TEXTURE_BASE_LEVEL, 0); |
| glTexParameteri(textureTarget, GL_TEXTURE_MAX_LEVEL, levels - 1); |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextureBGRA, |
| attachmentLevel, attachmentLayer); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| ASSERT_GL_NO_ERROR(); |
| initializeTextureData(textureTarget, levels, GL_BGRA_EXT); |
| } |
| |
| void testRead(GLenum textureTarget, GLint levels, GLint attachmentLevel, GLint attachmentLayer) |
| { |
| initTextureRGBA(textureTarget, levels, attachmentLevel, attachmentLayer); |
| verifyColor(attachmentLevel, attachmentLayer); |
| |
| // Skip BGRA test on GL/Nvidia, leading to internal incomplete framebuffer error. |
| // http://anglebug.com/8239 |
| ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGL()); |
| |
| if (IsGLExtensionEnabled("GL_EXT_texture_format_BGRA8888")) |
| { |
| initTextureBGRA(textureTarget, levels, attachmentLevel, attachmentLayer); |
| verifyColor(attachmentLevel, attachmentLayer); |
| } |
| } |
| |
| void initPBO() |
| { |
| // Create a buffer big enough to hold mip 0 + allow some offset during readback. |
| glGenBuffers(1, &mBuffer); |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, mBuffer); |
| glBufferData(GL_PIXEL_PACK_BUFFER, sizeof(angle::GLColor) * 16 * 2, nullptr, |
| GL_STREAM_COPY); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void testPBORead(GLenum textureTarget, |
| GLint levels, |
| GLint attachmentLevel, |
| GLint attachmentLayer) |
| { |
| initPBO(); |
| initTextureRGBA(textureTarget, levels, attachmentLevel, attachmentLayer); |
| verifyPBO(attachmentLevel, attachmentLayer); |
| |
| // Skip BGRA test on GL/Nvidia, leading to internal incomplete framebuffer error. |
| // http://anglebug.com/8239 |
| ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGL()); |
| |
| if (IsGLExtensionEnabled("GL_EXT_texture_format_BGRA8888")) |
| { |
| initTextureBGRA(textureTarget, levels, attachmentLevel, attachmentLayer); |
| verifyPBO(attachmentLevel, attachmentLayer); |
| } |
| } |
| |
| // Give each {level,layer} pair a (probably) unique color via random. |
| GLuint getColorValue(GLint level, GLint layer) |
| { |
| mRNG.reseed(level + layer * 32); |
| return mRNG.randomUInt(); |
| } |
| |
| void verifyColor(GLint level, GLint layer) |
| { |
| const angle::GLColor colorValue(getColorValue(level, layer)); |
| const GLint size = kSize >> level; |
| EXPECT_PIXEL_RECT_EQ(0, 0, size, size, colorValue); |
| } |
| |
| void verifyPBO(GLint level, GLint layer) |
| { |
| const GLint size = kSize >> level; |
| const GLsizei offset = kSize * (level + layer); |
| glReadPixels(0, 0, size, size, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<void *>(offset)); |
| |
| const std::vector<angle::GLColor> expectedColor(size * size, getColorValue(level, layer)); |
| std::vector<angle::GLColor> actualColor(size * size); |
| |
| void *mapPointer = glMapBufferRange(GL_PIXEL_PACK_BUFFER, offset, |
| sizeof(angle::GLColor) * size * size, GL_MAP_READ_BIT); |
| ASSERT_NE(nullptr, mapPointer); |
| memcpy(actualColor.data(), mapPointer, sizeof(angle::GLColor) * size * size); |
| glUnmapBuffer(GL_PIXEL_PACK_BUFFER); |
| |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_EQ(expectedColor, actualColor); |
| } |
| |
| void initializeTextureData(GLenum textureTarget, GLint levels, GLenum format) |
| { |
| for (GLint level = 0; level < levels; ++level) |
| { |
| GLint mipSize = kSize >> level; |
| GLint layers = (textureTarget == GL_TEXTURE_3D ? mipSize : kSize); |
| |
| size_t layerSize = mipSize * mipSize; |
| std::vector<GLuint> textureData(layers * layerSize); |
| |
| for (GLint layer = 0; layer < layers; ++layer) |
| { |
| GLuint colorValue = getColorValue(level, layer); |
| size_t offset = (layer * layerSize); |
| |
| if (format == GL_BGRA_EXT) |
| { |
| const GLuint rb = colorValue & 0x00FF00FF; |
| const GLuint br = (rb & 0xFF) << 16 | rb >> 16; |
| const GLuint ga = colorValue & 0xFF00FF00; |
| colorValue = ga | br; |
| } |
| |
| std::fill(textureData.begin() + offset, textureData.begin() + offset + layerSize, |
| colorValue); |
| } |
| |
| glTexSubImage3D(textureTarget, level, 0, 0, 0, mipSize, mipSize, layers, format, |
| GL_UNSIGNED_BYTE, textureData.data()); |
| } |
| } |
| |
| static constexpr GLint kSize = 4; |
| |
| angle::RNG mRNG; |
| GLuint mFBO; |
| GLuint mTextureRGBA; |
| GLuint mTextureBGRA; |
| GLuint mBuffer; |
| }; |
| |
| // Test 3D attachment readback. |
| TEST_P(ReadPixelsTextureTest, BasicAttachment3D) |
| { |
| testRead(GL_TEXTURE_3D, 1, 0, 0); |
| } |
| |
| // Test 3D attachment readback, non-zero mip. |
| TEST_P(ReadPixelsTextureTest, MipAttachment3D) |
| { |
| testRead(GL_TEXTURE_3D, 2, 1, 0); |
| } |
| |
| // Test 3D attachment readback, non-zero layer. |
| TEST_P(ReadPixelsTextureTest, LayerAttachment3D) |
| { |
| testRead(GL_TEXTURE_3D, 1, 0, 1); |
| } |
| |
| // Test 3D attachment readback, non-zero mip and layer. |
| TEST_P(ReadPixelsTextureTest, MipLayerAttachment3D) |
| { |
| testRead(GL_TEXTURE_3D, 2, 1, 1); |
| } |
| |
| // Test 2D array attachment readback. |
| TEST_P(ReadPixelsTextureTest, BasicAttachment2DArray) |
| { |
| testRead(GL_TEXTURE_2D_ARRAY, 1, 0, 0); |
| } |
| |
| // Test 3D attachment readback, non-zero mip. |
| TEST_P(ReadPixelsTextureTest, MipAttachment2DArray) |
| { |
| testRead(GL_TEXTURE_2D_ARRAY, 2, 1, 0); |
| } |
| |
| // Test 3D attachment readback, non-zero layer. |
| TEST_P(ReadPixelsTextureTest, LayerAttachment2DArray) |
| { |
| testRead(GL_TEXTURE_2D_ARRAY, 1, 0, 1); |
| } |
| |
| // Test 3D attachment readback, non-zero mip and layer. |
| TEST_P(ReadPixelsTextureTest, MipLayerAttachment2DArray) |
| { |
| testRead(GL_TEXTURE_2D_ARRAY, 2, 1, 1); |
| } |
| |
| // Test 3D attachment PBO readback. |
| TEST_P(ReadPixelsTextureTest, BasicAttachment3DPBO) |
| { |
| testPBORead(GL_TEXTURE_3D, 1, 0, 0); |
| } |
| |
| // Test 3D attachment readback, non-zero mip. |
| TEST_P(ReadPixelsTextureTest, MipAttachment3DPBO) |
| { |
| testPBORead(GL_TEXTURE_3D, 2, 1, 0); |
| } |
| |
| // Test 3D attachment readback, non-zero layer. |
| TEST_P(ReadPixelsTextureTest, LayerAttachment3DPBO) |
| { |
| // http://anglebug.com/5267 |
| ANGLE_SKIP_TEST_IF(IsMac() && IsIntelUHD630Mobile() && IsDesktopOpenGL()); |
| |
| testPBORead(GL_TEXTURE_3D, 1, 0, 1); |
| } |
| |
| // Test 3D attachment readback, non-zero mip and layer. |
| TEST_P(ReadPixelsTextureTest, MipLayerAttachment3DPBO) |
| { |
| // http://anglebug.com/5267 |
| ANGLE_SKIP_TEST_IF(IsMac() && IsIntelUHD630Mobile() && IsDesktopOpenGL()); |
| |
| testPBORead(GL_TEXTURE_3D, 2, 1, 1); |
| } |
| |
| // Test 2D array attachment readback. |
| TEST_P(ReadPixelsTextureTest, BasicAttachment2DArrayPBO) |
| { |
| testPBORead(GL_TEXTURE_2D_ARRAY, 1, 0, 0); |
| } |
| |
| // Test 3D attachment readback, non-zero mip. |
| TEST_P(ReadPixelsTextureTest, MipAttachment2DArrayPBO) |
| { |
| testPBORead(GL_TEXTURE_2D_ARRAY, 2, 1, 0); |
| } |
| |
| // Test 3D attachment readback, non-zero layer. |
| TEST_P(ReadPixelsTextureTest, LayerAttachment2DArrayPBO) |
| { |
| // http://anglebug.com/5267 |
| ANGLE_SKIP_TEST_IF(IsMac() && IsIntelUHD630Mobile() && IsDesktopOpenGL()); |
| |
| testPBORead(GL_TEXTURE_2D_ARRAY, 1, 0, 1); |
| } |
| |
| // Test 3D attachment readback, non-zero mip and layer. |
| TEST_P(ReadPixelsTextureTest, MipLayerAttachment2DArrayPBO) |
| { |
| // http://anglebug.com/5267 |
| ANGLE_SKIP_TEST_IF(IsMac() && IsIntelUHD630Mobile() && IsDesktopOpenGL()); |
| |
| testPBORead(GL_TEXTURE_2D_ARRAY, 2, 1, 1); |
| } |
| |
| // a test class to be used for error checking of glReadPixels |
| class ReadPixelsErrorTest : public ReadPixelsTest |
| { |
| protected: |
| ReadPixelsErrorTest() : mTexture(0), mFBO(0) {} |
| |
| void testSetUp() override |
| { |
| glGenTextures(1, &mTexture); |
| glBindTexture(GL_TEXTURE_2D, mTexture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 1); |
| |
| glGenFramebuffers(1, &mFBO); |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0); |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void testTearDown() override |
| { |
| glDeleteTextures(1, &mTexture); |
| glDeleteFramebuffers(1, &mFBO); |
| } |
| |
| void testUnsupportedTypeConversions(std::vector<GLenum> internalFormats, |
| std::vector<GLenum> unsupportedTypes) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| for (GLenum internalFormat : internalFormats) |
| { |
| GLRenderbuffer rbo; |
| glBindRenderbuffer(GL_RENDERBUFFER, rbo); |
| glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, 1, 1); |
| ASSERT_GL_NO_ERROR(); |
| |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| GLenum implementationFormat, implementationType; |
| glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, |
| reinterpret_cast<GLint *>(&implementationFormat)); |
| ASSERT_GL_NO_ERROR(); |
| glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, |
| reinterpret_cast<GLint *>(&implementationType)); |
| ASSERT_GL_NO_ERROR(); |
| |
| for (GLenum type : unsupportedTypes) |
| { |
| uint8_t pixel[8] = {}; |
| if (implementationFormat != GL_RGBA || implementationType != type) |
| { |
| glReadPixels(0, 0, 1, 1, GL_RGBA, type, pixel); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| } |
| } |
| } |
| |
| GLuint mTexture; |
| GLuint mFBO; |
| }; |
| |
| // The test verifies that glReadPixels generates a GL_INVALID_OPERATION error |
| // when the read buffer is GL_NONE. |
| // Reference: GLES 3.0.4, Section 4.3.2 Reading Pixels |
| TEST_P(ReadPixelsErrorTest, ReadBufferIsNone) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| glReadBuffer(GL_NONE); |
| std::vector<GLubyte> pixels(4); |
| EXPECT_GL_NO_ERROR(); |
| glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| |
| // The test verifies that glReadPixels generates a GL_INVALID_OPERATION |
| // error when reading signed 8-bit color buffers using incompatible types. |
| TEST_P(ReadPixelsErrorTest, ColorBufferSnorm8) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_render_snorm")); |
| |
| testUnsupportedTypeConversions({GL_R8_SNORM, GL_RG8_SNORM, GL_RGBA8_SNORM}, |
| {GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT}); |
| } |
| |
| // The test verifies that glReadPixels generates a GL_INVALID_OPERATION |
| // error when reading signed 16-bit color buffers using incompatible types. |
| TEST_P(ReadPixelsErrorTest, ColorBufferSnorm16) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_render_snorm")); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_norm16")); |
| |
| testUnsupportedTypeConversions({GL_R16_SNORM_EXT, GL_RG16_SNORM_EXT, GL_RGBA16_SNORM_EXT}, |
| {GL_BYTE, GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT}); |
| } |
| |
| // a test class to be used for error checking of glReadPixels with WebGLCompatibility |
| class ReadPixelsWebGLErrorTest : public ReadPixelsTest |
| { |
| protected: |
| ReadPixelsWebGLErrorTest() : mTexture(0), mFBO(0) { setWebGLCompatibilityEnabled(true); } |
| |
| void testSetUp() override |
| { |
| glGenTextures(1, &mTexture); |
| glBindTexture(GL_TEXTURE_2D, mTexture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 1); |
| |
| glGenFramebuffers(1, &mFBO); |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void testTearDown() override |
| { |
| glDeleteTextures(1, &mTexture); |
| glDeleteFramebuffers(1, &mFBO); |
| } |
| |
| GLuint mTexture; |
| GLuint mFBO; |
| }; |
| |
| // Test that WebGL context readpixels generates an error when reading GL_UNSIGNED_INT_24_8 type. |
| TEST_P(ReadPixelsWebGLErrorTest, TypeIsUnsignedInt24_8) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0); |
| glReadBuffer(GL_COLOR_ATTACHMENT0); |
| std::vector<GLuint> pixels(4); |
| EXPECT_GL_NO_ERROR(); |
| glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_INT_24_8, pixels.data()); |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); |
| } |
| |
| // Test that WebGL context readpixels generates an error when reading GL_DEPTH_COMPONENT format. |
| TEST_P(ReadPixelsWebGLErrorTest, FormatIsDepthComponent) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0); |
| glReadBuffer(GL_COLOR_ATTACHMENT0); |
| std::vector<GLubyte> pixels(4); |
| EXPECT_GL_NO_ERROR(); |
| glReadPixels(0, 0, 1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, pixels.data()); |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); |
| } |
| |
| } // anonymous namespace |
| |
| // Use this to select which configurations (e.g. which renderer, which GLES major version) these |
| // tests should be run against. |
| ANGLE_INSTANTIATE_TEST_ES2(ReadPixelsTest); |
| ANGLE_INSTANTIATE_TEST_ES2(ReadPixelsPBONVTest); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReadPixelsPBOTest); |
| ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsPBOTest); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReadPixelsPBODrawTest); |
| ANGLE_INSTANTIATE_TEST_ES3_AND(ReadPixelsPBODrawTest, |
| ES3_VULKAN().enable(Feature::ForceFallbackFormat)); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReadPixelsMultisampleTest); |
| ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsMultisampleTest); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReadPixelsTextureTest); |
| ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsTextureTest); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReadPixelsErrorTest); |
| ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsErrorTest); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReadPixelsWebGLErrorTest); |
| ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsWebGLErrorTest); |