// // Copyright 2016 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. // // CopyTextureTest.cpp: Tests of the GL_CHROMIUM_copy_texture extension #include "test_utils/ANGLETest.h" #include "test_utils/gl_raii.h" namespace angle { class CopyTextureTest : public ANGLETest { protected: CopyTextureTest() { setWindowWidth(256); setWindowHeight(256); setConfigRedBits(8); setConfigGreenBits(8); setConfigBlueBits(8); setConfigAlphaBits(8); } void testSetUp() override { glGenTextures(2, mTextures); glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glGenFramebuffers(1, &mFramebuffer); glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); } void testTearDown() override { glDeleteTextures(2, mTextures); glDeleteFramebuffers(1, &mFramebuffer); } bool checkExtensions() const { if (!IsGLExtensionEnabled("GL_CHROMIUM_copy_texture")) { std::cout << "Test skipped because GL_CHROMIUM_copy_texture is not available." << std::endl; return false; } EXPECT_NE(nullptr, glCopyTextureCHROMIUM); EXPECT_NE(nullptr, glCopySubTextureCHROMIUM); return true; } void testGradientDownsampleUniqueValues(GLenum destFormat, GLenum destType, const std::array &expectedUniqueValues) { std::array sourceGradient; for (size_t i = 0; i < sourceGradient.size(); i++) { GLubyte value = static_cast(i); sourceGradient[i] = GLColor(value, value, value, value); } GLTexture sourceTexture; glBindTexture(GL_TEXTURE_2D, sourceTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, sourceGradient.data()); GLTexture destTexture; glBindTexture(GL_TEXTURE_2D, destTexture); glCopyTextureCHROMIUM(sourceTexture, 0, GL_TEXTURE_2D, destTexture, 0, destFormat, destType, GL_FALSE, GL_FALSE, GL_FALSE); EXPECT_GL_NO_ERROR(); GLFramebuffer fbo; glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destTexture, 0); std::array destData; glReadPixels(0, 0, 256, 1, GL_RGBA, GL_UNSIGNED_BYTE, destData.data()); EXPECT_GL_NO_ERROR(); std::set uniqueValues[4]; for (size_t i = 0; i < destData.size(); i++) { GLColor color = destData[i]; uniqueValues[0].insert(color.R); uniqueValues[1].insert(color.G); uniqueValues[2].insert(color.B); uniqueValues[3].insert(color.A); } EXPECT_EQ(expectedUniqueValues[0], uniqueValues[0].size()); EXPECT_EQ(expectedUniqueValues[1], uniqueValues[1].size()); EXPECT_EQ(expectedUniqueValues[2], uniqueValues[2].size()); EXPECT_EQ(expectedUniqueValues[3], uniqueValues[3].size()); } GLuint mTextures[2] = { 0, 0, }; GLuint mFramebuffer = 0; }; using CopyTextureVariationsTestParams = std::tuple; std::string CopyTextureVariationsTestPrint( const ::testing::TestParamInfo ¶msInfo) { const CopyTextureVariationsTestParams ¶ms = paramsInfo.param; std::ostringstream out; out << std::get<0>(params) << "__"; switch (std::get<1>(params)) { case GL_ALPHA: out << "A"; break; case GL_RGB: out << "RGB"; break; case GL_RGBA: out << "RGBA"; break; case GL_LUMINANCE: out << "L"; break; case GL_LUMINANCE_ALPHA: out << "LA"; break; case GL_BGRA_EXT: out << "BGRA"; break; case GL_SRGB_ALPHA_EXT: out << "SRGBA"; break; default: out << "UPDATE_THIS_SWITCH"; } out << "To"; switch (std::get<2>(params)) { case GL_RGB: out << "RGB"; break; case GL_RGBA: out << "RGBA"; break; case GL_BGRA_EXT: out << "BGRA"; break; case GL_SRGB_ALPHA_EXT: out << "SRGBA"; break; default: out << "UPDATE_THIS_SWITCH"; } if (std::get<3>(params)) { out << "FlipY"; } if (std::get<4>(params)) { out << "PremultiplyAlpha"; } if (std::get<5>(params)) { out << "UnmultiplyAlpha"; } return out.str(); } class CopyTextureVariationsTest : public ANGLETestWithParam { protected: CopyTextureVariationsTest() { setWindowWidth(256); setWindowHeight(256); setConfigRedBits(8); setConfigGreenBits(8); setConfigBlueBits(8); setConfigAlphaBits(8); } void testSetUp() override { glGenTextures(2, mTextures); glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glGenFramebuffers(1, &mFramebuffer); glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); } void testTearDown() override { glDeleteTextures(2, mTextures); glDeleteFramebuffers(1, &mFramebuffer); } bool checkExtensions(GLenum sourceFormat, GLenum destFormat) const { if (!IsGLExtensionEnabled("GL_CHROMIUM_copy_texture")) { std::cout << "Test skipped because GL_CHROMIUM_copy_texture is not available." << std::endl; return false; } if ((sourceFormat == GL_BGRA_EXT || destFormat == GL_BGRA_EXT) && !IsGLExtensionEnabled("GL_EXT_texture_format_BGRA8888")) { return false; } if ((sourceFormat == GL_SRGB_ALPHA_EXT || destFormat == GL_SRGB_ALPHA_EXT) && !IsGLExtensionEnabled("GL_EXT_sRGB")) { return false; } return true; } void calculateCopyTextureResults(GLenum sourceFormat, GLenum destFormat, bool premultiplyAlpha, bool unmultiplyAlpha, const uint8_t *sourceColor, GLColor *destColor) { GLColor color; switch (sourceFormat) { case GL_RGB: color = GLColor(sourceColor[0], sourceColor[1], sourceColor[2], 255); break; case GL_RGBA: color = GLColor(sourceColor[0], sourceColor[1], sourceColor[2], sourceColor[3]); break; case GL_LUMINANCE: color = GLColor(sourceColor[0], sourceColor[0], sourceColor[0], 255); break; case GL_ALPHA: color = GLColor(0, 0, 0, sourceColor[0]); break; case GL_LUMINANCE_ALPHA: color = GLColor(sourceColor[0], sourceColor[0], sourceColor[0], sourceColor[1]); break; case GL_BGRA_EXT: color = GLColor(sourceColor[2], sourceColor[1], sourceColor[0], sourceColor[3]); break; default: EXPECT_EQ(true, false); } if (premultiplyAlpha != unmultiplyAlpha) { float alpha = color.A / 255.0f; if (premultiplyAlpha) { color.R = static_cast(static_cast(color.R) * alpha); color.G = static_cast(static_cast(color.G) * alpha); color.B = static_cast(static_cast(color.B) * alpha); } else if (unmultiplyAlpha && color.A != 0) { color.R = static_cast(static_cast(color.R) / alpha); color.G = static_cast(static_cast(color.G) / alpha); color.B = static_cast(static_cast(color.B) / alpha); } } switch (destFormat) { case GL_RGB: color.A = 255; break; case GL_RGBA: case GL_BGRA_EXT: case GL_SRGB_ALPHA_EXT: break; default: EXPECT_EQ(true, false); } *destColor = color; } const uint8_t *getSourceColors(GLenum sourceFormat, size_t *colorCount, uint8_t *componentCount) { // Note: in all the following values, alpha is larger than RGB so unmultiply alpha doesn't // overflow constexpr static uint8_t kRgbaColors[7 * 4] = { 255u, 127u, 63u, 255u, // 0 31u, 127u, 63u, 127u, // 1 31u, 63u, 127u, 255u, // 2 15u, 127u, 31u, 127u, // 3 127u, 255u, 63u, 0u, // 4 31u, 63u, 127u, 0u, // 5 15u, 31u, 63u, 63u, // 6 }; constexpr static uint8_t kRgbColors[7 * 3] = { 255u, 127u, 63u, // 0 31u, 127u, 63u, // 1 31u, 63u, 127u, // 2 15u, 127u, 31u, // 3 127u, 255u, 63u, // 4 31u, 63u, 127u, // 5 15u, 31u, 63u, // 6 }; constexpr static uint8_t kLumColors[7 * 1] = { 255u, // 0 163u, // 1 78u, // 2 114u, // 3 51u, // 4 0u, // 5 217u, // 6 }; constexpr static uint8_t kLumaColors[7 * 2] = { 255u, 255u, // 0 67u, 163u, // 1 78u, 231u, // 2 8u, 114u, // 3 51u, 199u, // 4 0u, 173u, // 5 34u, 217u, // 6 }; constexpr static uint8_t kAlphaColors[7 * 1] = { 255u, // 0 67u, // 1 231u, // 2 8u, // 3 199u, // 4 173u, // 5 34u, // 6 }; *colorCount = 7; switch (sourceFormat) { case GL_RGB: *componentCount = 3; return kRgbColors; case GL_RGBA: case GL_BGRA_EXT: *componentCount = 4; return kRgbaColors; case GL_LUMINANCE: *componentCount = 1; return kLumColors; case GL_ALPHA: *componentCount = 1; return kAlphaColors; case GL_LUMINANCE_ALPHA: *componentCount = 2; return kLumaColors; default: EXPECT_EQ(true, false); return nullptr; } } void initializeSourceTexture(GLenum target, GLenum sourceFormat, const uint8_t *srcColors, uint8_t componentCount) { // The texture is initialized as 2x2. If the componentCount is 1 or 3, then the input data // will have a row pitch of 2 or 6, which needs to be padded to 4 or 8 respectively. uint8_t srcColorsPadded[4 * 4]; size_t srcRowPitch = 2 * componentCount + (componentCount == 1 || componentCount == 3 ? 2 : 0); size_t inputRowPitch = 2 * componentCount; for (size_t row = 0; row < 2; ++row) { memcpy(&srcColorsPadded[row * srcRowPitch], &srcColors[row * inputRowPitch], inputRowPitch); memset(&srcColorsPadded[row * srcRowPitch + inputRowPitch], 0, srcRowPitch - inputRowPitch); } glBindTexture(target, mTextures[0]); glTexImage2D(target, 0, sourceFormat, 2, 2, 0, sourceFormat, GL_UNSIGNED_BYTE, srcColorsPadded); } void testCopyTexture(GLenum sourceTarget, GLenum sourceFormat, GLenum destFormat, bool flipY, bool premultiplyAlpha, bool unmultiplyAlpha) { if (!checkExtensions(sourceFormat, destFormat)) { return; } if (sourceFormat == GL_LUMINANCE || sourceFormat == GL_LUMINANCE_ALPHA || sourceFormat == GL_ALPHA || destFormat == GL_LUMINANCE || destFormat == GL_LUMINANCE_ALPHA || destFormat == GL_ALPHA) { // Old drivers buggy with optimized ImageCopy shader given LUMA textures. // http://anglebug.com/4721 ANGLE_SKIP_TEST_IF(IsLinux() && IsNVIDIA() && IsVulkan()); // http://anglebug.com/4939 ANGLE_SKIP_TEST_IF(IsOpenGL() && destFormat == GL_SRGB_ALPHA_EXT); } size_t colorCount; uint8_t componentCount; const uint8_t *srcColors = getSourceColors(sourceFormat, &colorCount, &componentCount); std::vector destColors(colorCount); for (size_t i = 0; i < colorCount; ++i) { calculateCopyTextureResults(sourceFormat, destFormat, premultiplyAlpha, unmultiplyAlpha, &srcColors[i * componentCount], &destColors[i]); } for (size_t i = 0; i < colorCount - 3; ++i) { initializeSourceTexture(sourceTarget, sourceFormat, &srcColors[i * componentCount], componentCount); glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, destFormat, GL_UNSIGNED_BYTE, flipY, premultiplyAlpha, unmultiplyAlpha); EXPECT_GL_NO_ERROR(); // Check that FB is complete. EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); if (flipY) { EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 2], 1.0); EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 3], 1.0); EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 0], 1.0); EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 1], 1.0); } else { EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 0], 1.0); EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 1], 1.0); EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 2], 1.0); EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 3], 1.0); } EXPECT_GL_NO_ERROR(); } } void testCopySubTexture(GLenum sourceTarget, GLenum sourceFormat, GLenum destFormat, bool flipY, bool premultiplyAlpha, bool unmultiplyAlpha) { if (!checkExtensions(sourceFormat, destFormat)) { return; } if (sourceFormat == GL_LUMINANCE || sourceFormat == GL_LUMINANCE_ALPHA || sourceFormat == GL_ALPHA || destFormat == GL_LUMINANCE || destFormat == GL_LUMINANCE_ALPHA || destFormat == GL_ALPHA) { // Old drivers buggy with optimized ImageCopy shader given LUMA textures. // http://anglebug.com/4721 ANGLE_SKIP_TEST_IF(IsLinux() && IsNVIDIA() && IsVulkan()); // http://anglebug.com/4939 ANGLE_SKIP_TEST_IF(IsOpenGL() && destFormat == GL_SRGB_ALPHA_EXT); } size_t colorCount; uint8_t componentCount; const uint8_t *srcColors = getSourceColors(sourceFormat, &colorCount, &componentCount); std::vector destColors(colorCount); for (size_t i = 0; i < colorCount; ++i) { calculateCopyTextureResults(sourceFormat, destFormat, premultiplyAlpha, unmultiplyAlpha, &srcColors[i * componentCount], &destColors[i]); } for (size_t i = 0; i < colorCount - 3; ++i) { initializeSourceTexture(sourceTarget, sourceFormat, &srcColors[i * componentCount], componentCount); glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, destFormat, 2, 2, 0, destFormat, GL_UNSIGNED_BYTE, nullptr); glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 0, 0, 0, 0, 2, 2, flipY, premultiplyAlpha, unmultiplyAlpha); EXPECT_GL_NO_ERROR(); if (sourceFormat != GL_LUMINANCE && sourceFormat != GL_LUMINANCE_ALPHA && sourceFormat != GL_ALPHA) { // Check that FB is complete. EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); } if (flipY) { EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 2], 1.0); EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 3], 1.0); EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 0], 1.0); EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 1], 1.0); } else { EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 0], 1.0); EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 1], 1.0); EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 2], 1.0); EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 3], 1.0); } EXPECT_GL_NO_ERROR(); } } GLuint mTextures[2] = { 0, 0, }; GLuint mFramebuffer = 0; }; class CopyTextureTestDest : public CopyTextureTest {}; class CopyTextureTestWebGL : public CopyTextureTest { protected: CopyTextureTestWebGL() : CopyTextureTest() { setWebGLCompatibilityEnabled(true); } }; class CopyTextureTestES3 : public CopyTextureTest { protected: void invalidateBlitThenBlendCommon(GLsizei layerCount); }; // Test that CopyTexture cannot redefine an immutable texture and CopySubTexture can copy data to // immutable textures TEST_P(CopyTextureTest, ImmutableTexture) { if (!checkExtensions()) { return; } ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && (!IsGLExtensionEnabled("GL_EXT_texture_storage") || !IsGLExtensionEnabled("GL_OES_rgb8_rgba8"))); GLColor pixels = GLColor::red; glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, 1, 1); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixels); glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, 1, 1); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); EXPECT_GL_NO_ERROR(); // Should generate an error when the texture is redefined glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_ERROR(GL_INVALID_OPERATION); // Should succeed when using CopySubTexture glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 0, 0, 0, 0, 1, 1, false, false, false); EXPECT_GL_NO_ERROR(); // Check that FB is complete. EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); EXPECT_PIXEL_COLOR_EQ(0, 0, pixels); EXPECT_GL_NO_ERROR(); } // Test validation of internal formats in CopyTexture and CopySubTexture TEST_P(CopyTextureTest, InternalFormat) { if (!checkExtensions()) { return; } std::vector sourceFormats; sourceFormats.push_back(GL_ALPHA); sourceFormats.push_back(GL_RGB); sourceFormats.push_back(GL_RGBA); sourceFormats.push_back(GL_LUMINANCE); sourceFormats.push_back(GL_LUMINANCE_ALPHA); std::vector destFormats; destFormats.push_back(GL_RGB); destFormats.push_back(GL_RGBA); if (IsGLExtensionEnabled("GL_EXT_texture_format_BGRA8888")) { sourceFormats.push_back(GL_BGRA_EXT); destFormats.push_back(GL_BGRA_EXT); } // Test with glCopyTexture for (GLint sourceFormat : sourceFormats) { for (GLint destFormat : destFormats) { glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, sourceFormat, 1, 1, 0, sourceFormat, GL_UNSIGNED_BYTE, nullptr); EXPECT_GL_NO_ERROR(); glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, destFormat, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); } } // Test with glCopySubTexture for (GLint sourceFormat : sourceFormats) { for (GLint destFormat : destFormats) { glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, sourceFormat, 1, 1, 0, sourceFormat, GL_UNSIGNED_BYTE, nullptr); EXPECT_GL_NO_ERROR(); glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, destFormat, 1, 1, 0, destFormat, GL_UNSIGNED_BYTE, nullptr); EXPECT_GL_NO_ERROR(); glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 0, 0, 0, 0, 1, 1, false, false, false); EXPECT_GL_NO_ERROR(); } } } // Test to ensure that the destination texture is redefined if the properties are different. TEST_P(CopyTextureTest, RedefineDestinationTexture) { ANGLE_SKIP_TEST_IF(!checkExtensions()); ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_format_BGRA8888")); GLColor pixels[4] = {GLColor::red, GLColor::red, GLColor::red, GLColor::red}; glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, 1, 1, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels); EXPECT_GL_NO_ERROR(); // GL_INVALID_OPERATION due to "intrinsic format" != "internal format". glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels); EXPECT_GL_ERROR(GL_INVALID_OPERATION); // GL_INVALID_VALUE due to bad dimensions. glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 1, 1, 1, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels); EXPECT_GL_ERROR(GL_INVALID_VALUE); // If the dest texture has different properties, glCopyTextureCHROMIUM() // redefines them. glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); // glTexSubImage2D() succeeds because mTextures[1] is redefined into 2x2 // dimension and GL_RGBA format. glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels); EXPECT_GL_NO_ERROR(); // Check that FB is complete. EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); EXPECT_PIXEL_COLOR_EQ(1, 1, pixels[3]); EXPECT_GL_NO_ERROR(); } // Test that invalid dimensions in CopySubTexture are validated TEST_P(CopyTextureTest, CopySubTextureDimension) { if (!checkExtensions()) { return; } glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 3, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 1, 1, 0, 0, 1, 1, false, false, false); EXPECT_GL_NO_ERROR(); // xoffset < 0 glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, -1, 1, 0, 0, 1, 1, false, false, false); EXPECT_GL_ERROR(GL_INVALID_VALUE); // x < 0 glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 1, 1, -1, 0, 1, 1, false, false, false); EXPECT_GL_ERROR(GL_INVALID_VALUE); // xoffset + width > dest_width glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 2, 2, 0, 0, 2, 2, false, false, false); EXPECT_GL_ERROR(GL_INVALID_VALUE); // x + width > source_width glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 0, 0, 1, 1, 2, 2, false, false, false); EXPECT_GL_ERROR(GL_INVALID_VALUE); } // Test that invalid IDs in CopyTexture are validated TEST_P(CopyTextureTest, CopyTextureInvalidTextureIds) { if (!checkExtensions()) { return; } glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 3, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, 99993, 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_ERROR(GL_INVALID_VALUE); glCopyTextureCHROMIUM(99994, 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_ERROR(GL_INVALID_VALUE); glCopyTextureCHROMIUM(99995, 0, GL_TEXTURE_2D, 99996, 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_ERROR(GL_INVALID_VALUE); glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); } // Test that invalid IDs in CopySubTexture are validated TEST_P(CopyTextureTest, CopySubTextureInvalidTextureIds) { if (!checkExtensions()) { return; } glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 3, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, 99993, 0, 1, 1, 0, 0, 1, 1, false, false, false); EXPECT_GL_ERROR(GL_INVALID_VALUE); glCopySubTextureCHROMIUM(99994, 0, GL_TEXTURE_2D, mTextures[1], 0, 1, 1, 0, 0, 1, 1, false, false, false); EXPECT_GL_ERROR(GL_INVALID_VALUE); glCopySubTextureCHROMIUM(99995, 0, GL_TEXTURE_2D, 99996, 0, 1, 1, 0, 0, 1, 1, false, false, false); EXPECT_GL_ERROR(GL_INVALID_VALUE); glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 1, 1, 0, 0, 1, 1, false, false, false); EXPECT_GL_NO_ERROR(); } TEST_P(CopyTextureTest, InvalidTarget) { ANGLE_SKIP_TEST_IF(!checkExtensions()); GLTexture textures[2]; glBindTexture(GL_TEXTURE_2D, textures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glBindTexture(GL_TEXTURE_2D, textures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 3, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); // Invalid enum for a completely invalid target glCopySubTextureCHROMIUM(textures[0], 0, GL_INVALID_VALUE, textures[1], 0, 1, 1, 0, 0, 1, 1, false, false, false); EXPECT_GL_ERROR(GL_INVALID_ENUM); // Invalid value for a valid target enum but is not valid for the destination texture glCopySubTextureCHROMIUM(textures[0], 0, GL_TEXTURE_CUBE_MAP_POSITIVE_X, textures[1], 0, 1, 1, 0, 0, 1, 1, false, false, false); EXPECT_GL_ERROR(GL_INVALID_VALUE); } // Test that using an offset in CopySubTexture works correctly TEST_P(CopyTextureTest, CopySubTextureOffset) { if (!checkExtensions()) { return; } GLColor rgbaPixels[4 * 4] = {GLColor::red, GLColor::green, GLColor::blue, GLColor::black}; glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbaPixels); GLColor transparentPixels[4 * 4] = {GLColor::transparentBlack, GLColor::transparentBlack, GLColor::transparentBlack, GLColor::transparentBlack}; glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, transparentPixels); // Check that FB is complete. EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 1, 1, 0, 0, 1, 1, false, false, false); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red); glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 1, 0, 1, 0, 1, 1, false, false, false); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(1, 0, GLColor::green); glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 0, 1, 0, 1, 1, 1, false, false, false); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 1, GLColor::blue); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::transparentBlack); EXPECT_GL_NO_ERROR(); } // Test every combination of copy [sub]texture parameters: // source: ALPHA, RGB, RGBA, LUMINANCE, LUMINANCE_ALPHA, BGRA_EXT // destination: RGB, RGBA, BGRA_EXT // flipY: false, true // premultiplyAlpha: false, true // unmultiplyAlpha: false, true namespace { constexpr GLenum kCopyTextureVariationsSrcFormats[] = { GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_BGRA_EXT}; constexpr GLenum kCopyTextureVariationsDstFormats[] = {GL_RGB, GL_RGBA, GL_BGRA_EXT, GL_SRGB_ALPHA_EXT}; } // anonymous namespace TEST_P(CopyTextureVariationsTest, CopyTexture) { // http://anglebug.com/5723 ANGLE_SKIP_TEST_IF(IsOzone()); // http://anglebug.com/5246 if (std::get<1>(GetParam()) == GL_ALPHA && std::get<2>(GetParam()) == GL_RGB && std::get<3>(GetParam()) && std::get<5>(GetParam())) { ANGLE_SKIP_TEST_IF(IsWindows7() && IsNVIDIA() && IsOpenGLES()); } testCopyTexture(GL_TEXTURE_2D, std::get<1>(GetParam()), std::get<2>(GetParam()), std::get<3>(GetParam()), std::get<4>(GetParam()), std::get<5>(GetParam())); } TEST_P(CopyTextureVariationsTest, CopySubTexture) { // http://anglebug.com/5723 ANGLE_SKIP_TEST_IF(IsOzone()); testCopySubTexture(GL_TEXTURE_2D, std::get<1>(GetParam()), std::get<2>(GetParam()), std::get<3>(GetParam()), std::get<4>(GetParam()), std::get<5>(GetParam())); } TEST_P(CopyTextureVariationsTest, CopyTextureRectangle) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_texture_rectangle")); testCopyTexture(GL_TEXTURE_RECTANGLE_ANGLE, std::get<1>(GetParam()), std::get<2>(GetParam()), std::get<3>(GetParam()), std::get<4>(GetParam()), std::get<5>(GetParam())); } TEST_P(CopyTextureVariationsTest, CopySubTextureRectangle) { ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_texture_rectangle")); testCopySubTexture(GL_TEXTURE_RECTANGLE_ANGLE, std::get<1>(GetParam()), std::get<2>(GetParam()), std::get<3>(GetParam()), std::get<4>(GetParam()), std::get<5>(GetParam())); } // Test that copying to cube maps works TEST_P(CopyTextureTest, CubeMapTarget) { if (!checkExtensions()) { return; } // http://anglebug.com/1932 ANGLE_SKIP_TEST_IF(IsOSX() && IsIntel() && IsDesktopOpenGL()); // http://anglebug.com/3145 ANGLE_SKIP_TEST_IF(IsFuchsia() && IsIntel() && IsVulkan()); GLColor pixels[7] = { GLColor(10u, 13u, 16u, 19u), GLColor(20u, 23u, 26u, 29u), GLColor(30u, 33u, 36u, 39u), GLColor(40u, 43u, 46u, 49u), GLColor(50u, 53u, 56u, 59u), GLColor(60u, 63u, 66u, 69u), GLColor(70u, 73u, 76u, 79u), }; GLTexture textures[2]; glBindTexture(GL_TEXTURE_CUBE_MAP, textures[1]); for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; face++) { glTexImage2D(face, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); } for (size_t i = 0; i < 2; ++i) { for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; face++) { glBindTexture(GL_TEXTURE_2D, textures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[face - GL_TEXTURE_CUBE_MAP_POSITIVE_X + i]); glCopySubTextureCHROMIUM(textures[0], 0, face, textures[1], 0, 0, 0, 0, 0, 1, 1, false, false, false); } EXPECT_GL_NO_ERROR(); GLFramebuffer fbo; glBindFramebuffer(GL_FRAMEBUFFER, fbo); for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; face++) { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, face, textures[1], 0); // Check that FB is complete. EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); EXPECT_PIXEL_COLOR_EQ(0, 0, pixels[face - GL_TEXTURE_CUBE_MAP_POSITIVE_X + i]); EXPECT_GL_NO_ERROR(); } } } // Test that we can successfully copy into incomplete cube maps. Regression test for // http://anglebug.com/3384 TEST_P(CopyTextureTest, IncompleteCubeMap) { if (!checkExtensions()) { return; } GLTexture texture2D; GLColor rgbaPixels[4 * 4] = {GLColor::red, GLColor::green, GLColor::blue, GLColor::black}; glBindTexture(GL_TEXTURE_2D, texture2D); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbaPixels); GLTexture textureCube; glBindTexture(GL_TEXTURE_CUBE_MAP, textureCube); for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; face++) { glTexImage2D(face, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); } // Set one face to 2x2 glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbaPixels); // Copy into the incomplete face glCopySubTextureCHROMIUM(texture2D, 0, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, textureCube, 0, 0, 0, 0, 0, 2, 2, false, false, false); EXPECT_GL_NO_ERROR(); } // Test BGRA to RGBA cube map copy TEST_P(CopyTextureTest, CubeMapTargetBGRA) { if (!checkExtensions()) { return; } if (!IsGLExtensionEnabled("GL_EXT_texture_format_BGRA8888")) { return; } // http://anglebug.com/3145 ANGLE_SKIP_TEST_IF(IsFuchsia() && IsIntel() && IsVulkan()); GLColor pixels[7] = { GLColor(10u, 13u, 16u, 19u), GLColor(20u, 23u, 26u, 29u), GLColor(30u, 33u, 36u, 39u), GLColor(40u, 43u, 46u, 49u), GLColor(50u, 53u, 56u, 59u), GLColor(60u, 63u, 66u, 69u), GLColor(70u, 73u, 76u, 79u), }; GLTexture textures[2]; glBindTexture(GL_TEXTURE_CUBE_MAP, textures[1]); for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; face++) { glTexImage2D(face, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); } for (size_t i = 0; i < 2; ++i) { for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; face++) { glBindTexture(GL_TEXTURE_2D, textures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, 1, 1, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &pixels[face - GL_TEXTURE_CUBE_MAP_POSITIVE_X + i]); glCopySubTextureCHROMIUM(textures[0], 0, face, textures[1], 0, 0, 0, 0, 0, 1, 1, false, false, false); } EXPECT_GL_NO_ERROR(); GLFramebuffer fbo; glBindFramebuffer(GL_FRAMEBUFFER, fbo); for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; face++) { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, face, textures[1], 0); // Check that FB is complete. EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); GLColor converted = pixels[face - GL_TEXTURE_CUBE_MAP_POSITIVE_X + i]; std::swap(converted.R, converted.B); EXPECT_PIXEL_COLOR_EQ(0, 0, converted); EXPECT_GL_NO_ERROR(); } } } // Test cube map copies with RGB format TEST_P(CopyTextureTest, CubeMapTargetRGB) { if (!checkExtensions()) { return; } // http://anglebug.com/1932 ANGLE_SKIP_TEST_IF(IsOSX() && IsIntel() && IsDesktopOpenGL()); // http://anglebug.com/3145 ANGLE_SKIP_TEST_IF(IsFuchsia() && IsIntel() && IsVulkan()); constexpr uint8_t pixels[16 * 7] = { 0u, 3u, 6u, 10u, 13u, 16u, 0, 0, 20u, 23u, 26u, 30u, 33u, 36u, 0, 0, // 2x2 40u, 43u, 46u, 50u, 53u, 56u, 0, 0, 60u, 63u, 66u, 70u, 73u, 76u, 0, 0, // 2x2 80u, 83u, 86u, 90u, 93u, 96u, 0, 0, 100u, 103u, 106u, 110u, 113u, 116u, 0, 0, // 2x2 120u, 123u, 126u, 130u, 133u, 136u, 0, 0, 140u, 143u, 146u, 160u, 163u, 166u, 0, 0, // 2x2 170u, 173u, 176u, 180u, 183u, 186u, 0, 0, 190u, 193u, 196u, 200u, 203u, 206u, 0, 0, // 2x2 210u, 213u, 216u, 220u, 223u, 226u, 0, 0, 230u, 233u, 236u, 240u, 243u, 246u, 0, 0, // 2x2 10u, 50u, 100u, 30u, 80u, 130u, 0, 0, 60u, 110u, 160u, 90u, 140u, 200u, 0, 0, // 2x2 }; GLTexture textures[2]; glBindTexture(GL_TEXTURE_CUBE_MAP, textures[1]); for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; face++) { glTexImage2D(face, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); } for (size_t i = 0; i < 2; ++i) { for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; face++) { glBindTexture(GL_TEXTURE_2D, textures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, &pixels[(face - GL_TEXTURE_CUBE_MAP_POSITIVE_X + i) * 16]); glCopySubTextureCHROMIUM(textures[0], 0, face, textures[1], 0, 0, 0, 0, 0, 2, 2, false, false, false); } EXPECT_GL_NO_ERROR(); GLFramebuffer fbo; glBindFramebuffer(GL_FRAMEBUFFER, fbo); for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; face++) { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, face, textures[1], 0); // Check that FB is complete. EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); const uint8_t *faceData = &pixels[(face - GL_TEXTURE_CUBE_MAP_POSITIVE_X + i) * 16]; EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor(faceData[0], faceData[1], faceData[2], 255)); EXPECT_PIXEL_COLOR_EQ(1, 0, GLColor(faceData[3], faceData[4], faceData[5], 255)); EXPECT_PIXEL_COLOR_EQ(0, 1, GLColor(faceData[8], faceData[9], faceData[10], 255)); EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor(faceData[11], faceData[12], faceData[13], 255)); EXPECT_GL_NO_ERROR(); } } } // Test that copying to non-zero mipmaps works TEST_P(CopyTextureTest, CopyToMipmap) { if (!checkExtensions()) { return; } ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_OES_fbo_render_mipmap")); ANGLE_SKIP_TEST_IF(IsOSX() && IsIntel()); GLColor pixels[] = {GLColor::red, GLColor::red, GLColor::red, GLColor::red}; GLTexture textures[2]; glBindTexture(GL_TEXTURE_2D, textures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glBindTexture(GL_TEXTURE_2D, textures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); std::vector> soureDestPairs; soureDestPairs.push_back(std::make_pair(0, 1)); // ES3 allows copying from non-zero mips if (getClientMajorVersion() >= 3) { soureDestPairs.push_back(std::make_pair(1, 2)); } for (const auto &sourceDestPair : soureDestPairs) { const GLint sourceLevel = sourceDestPair.first; const GLint destLevel = sourceDestPair.second; glCopyTextureCHROMIUM(textures[0], sourceLevel, GL_TEXTURE_2D, textures[1], destLevel, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); GLFramebuffer fbo; glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1], destLevel); // Check that FB is complete. EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); EXPECT_PIXEL_COLOR_EQ(0, 0, pixels[0]); EXPECT_GL_NO_ERROR(); } } // Test that copying outside the mipmap range works TEST_P(CopyTextureTest, CopyOutsideMipmap) { if (!checkExtensions()) { return; } // http://anglebug.com/4716 ANGLE_SKIP_TEST_IF(IsD3D()); // Failing on older drivers. http://anglebug.com/4718 ANGLE_SKIP_TEST_IF(IsLinux() && IsNVIDIA() && IsOpenGL()); // http://anglebug.com/5246 ANGLE_SKIP_TEST_IF(IsWindows7() && IsNVIDIA() && IsOpenGLES()); glBindFramebuffer(GL_FRAMEBUFFER, 0); ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D()); glUseProgram(program); GLint textureLoc = glGetUniformLocation(program, essl1_shaders::Texture2DUniform()); ASSERT_NE(-1, textureLoc); glUniform1i(textureLoc, 0); GLTexture textures[2]; // Create two single-mip textures. glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::red); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, textures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::green); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Commit texture 0 glBindTexture(GL_TEXTURE_2D, textures[0]); drawQuad(program, std::string(essl1_shaders::PositionAttrib()), 0.5f); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); // Copy texture 1 into mip 1 of texture 0. This mip is outside the range of the image allocated // for texture 0. glCopyTextureCHROMIUM(textures[1], 0, GL_TEXTURE_2D, textures[0], 1, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); // Draw with texture 0 again drawQuad(program, std::string(essl1_shaders::PositionAttrib()), 0.5f); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); } // Test that copying from an RGBA8 texture to RGBA4 results in exactly 4-bit precision in the result TEST_P(CopyTextureTest, DownsampleRGBA4444) { // Downsampling on copy is only guarenteed on D3D11 ANGLE_SKIP_TEST_IF(!IsD3D11()); GLTexture textures[2]; GLColor pixels[] = {GLColor(0, 5, 6, 7), GLColor(17, 22, 25, 24), GLColor(34, 35, 36, 36), GLColor(51, 53, 55, 55)}; glBindTexture(GL_TEXTURE_2D, textures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glBindTexture(GL_TEXTURE_2D, textures[1]); glCopyTextureCHROMIUM(textures[0], 0, GL_TEXTURE_2D, textures[1], 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, GL_FALSE, GL_FALSE, GL_FALSE); GLFramebuffer fbo; glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1], 0); EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0, 0, 0, 0), 1.0); EXPECT_PIXEL_COLOR_NEAR(1, 0, GLColor(17, 17, 17, 17), 1.0); EXPECT_PIXEL_COLOR_NEAR(0, 1, GLColor(34, 34, 34, 34), 1.0); EXPECT_PIXEL_COLOR_NEAR(1, 1, GLColor(51, 51, 51, 51), 1.0); testGradientDownsampleUniqueValues(GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, {16, 16, 16, 16}); } // Test that copying from an RGBA8 texture to RGB565 results in exactly 4-bit precision in the // result TEST_P(CopyTextureTest, DownsampleRGB565) { // Downsampling on copy is only guarenteed on D3D11 ANGLE_SKIP_TEST_IF(!IsD3D11()); GLTexture textures[2]; GLColor pixels[] = {GLColor(0, 5, 2, 14), GLColor(17, 22, 25, 30), GLColor(34, 33, 36, 46), GLColor(50, 54, 49, 60)}; glBindTexture(GL_TEXTURE_2D, textures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glBindTexture(GL_TEXTURE_2D, textures[1]); glCopyTextureCHROMIUM(textures[0], 0, GL_TEXTURE_2D, textures[1], 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, GL_FALSE, GL_FALSE, GL_FALSE); GLFramebuffer fbo; glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1], 0); EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0, 4, 0, 255), 1.0); EXPECT_PIXEL_COLOR_NEAR(1, 0, GLColor(16, 20, 25, 255), 1.0); EXPECT_PIXEL_COLOR_NEAR(0, 1, GLColor(33, 32, 33, 255), 1.0); EXPECT_PIXEL_COLOR_NEAR(1, 1, GLColor(49, 53, 49, 255), 1.0); testGradientDownsampleUniqueValues(GL_RGB, GL_UNSIGNED_SHORT_5_6_5, {32, 64, 32, 1}); } // Test that copying from an RGBA8 texture to RGBA5551 results in exactly 4-bit precision in the // result TEST_P(CopyTextureTest, DownsampleRGBA5551) { // Downsampling on copy is only guarenteed on D3D11 ANGLE_SKIP_TEST_IF(!IsD3D11()); GLTexture textures[2]; GLColor pixels[] = {GLColor(0, 1, 2, 3), GLColor(14, 16, 17, 18), GLColor(33, 34, 36, 46), GLColor(50, 51, 52, 255)}; glBindTexture(GL_TEXTURE_2D, textures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glBindTexture(GL_TEXTURE_2D, textures[1]); glCopyTextureCHROMIUM(textures[0], 0, GL_TEXTURE_2D, textures[1], 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, GL_FALSE, GL_FALSE, GL_FALSE); GLFramebuffer fbo; glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1], 0); EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0, 0, 0, 0), 1.0); EXPECT_PIXEL_COLOR_NEAR(1, 0, GLColor(16, 16, 16, 0), 1.0); EXPECT_PIXEL_COLOR_NEAR(0, 1, GLColor(33, 33, 33, 0), 1.0); EXPECT_PIXEL_COLOR_NEAR(1, 1, GLColor(49, 49, 49, 255), 1.0); testGradientDownsampleUniqueValues(GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, {32, 32, 32, 2}); } // Test to ensure that CopyTexture works with LUMINANCE texture as a destination TEST_P(CopyTextureTestDest, Luminance) { if (!checkExtensions()) { return; } GLColor originalPixels(50u, 100u, 150u, 200u); GLColor expectedPixels(50u, 50u, 50u, 255u); // ReadPixels doesn't work with LUMINANCE (non-renderable), so we copy again back to an RGBA // texture to verify contents. glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels); glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, nullptr); glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels); } // Test to ensure that CopyTexture works with LUMINANCE texture as a destination with // UnpackPremultiply parameter TEST_P(CopyTextureTestDest, LuminanceMultiply) { if (!checkExtensions()) { return; } GLColor originalPixels(50u, 100u, 150u, 200u); GLColor expectedPixels(39u, 39u, 39u, 255u); // ReadPixels doesn't work with LUMINANCE (non-renderable), so we copy again back to an RGBA // texture to verify contents. glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels); glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, nullptr); glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, false, true, false); EXPECT_GL_NO_ERROR(); glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels); } // Test to ensure that CopyTexture works with LUMINANCE texture as a destination with // UnpackUnmultiply parameter TEST_P(CopyTextureTestDest, LuminanceUnmultiply) { if (!checkExtensions()) { return; } GLColor originalPixels(50u, 100u, 150u, 200u); GLColor expectedPixels(64u, 64u, 64u, 255u); // ReadPixels doesn't work with LUMINANCE (non-renderable), so we copy again back to an RGBA // texture to verify contents. glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels); glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, nullptr); glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, false, false, true); EXPECT_GL_NO_ERROR(); glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels); } // Test to ensure that CopyTexture works with LUMINANCE_ALPHA texture as a destination TEST_P(CopyTextureTestDest, LuminanceAlpha) { if (!checkExtensions()) { return; } GLColor originalPixels(50u, 100u, 150u, 200u); GLColor expectedPixels(50u, 50u, 50u, 200u); // ReadPixels doesn't work with LUMINANCE_ALPHA (non-renderable), so we copy again back to an // RGBA texture to verify contents. glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels); glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 1, 1, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, nullptr); glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels); } // Test to ensure that CopyTexture works with LUMINANCE_ALPHA texture as a destination with // UnpackPremultiply parameter TEST_P(CopyTextureTestDest, LuminanceAlphaMultiply) { if (!checkExtensions()) { return; } GLColor originalPixels(50u, 100u, 150u, 200u); GLColor expectedPixels(39u, 39u, 39u, 200u); // ReadPixels doesn't work with LUMINANCE_ALPHA (non-renderable), so we copy again back to an // RGBA texture to verify contents. glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels); glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 1, 1, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, nullptr); glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, false, true, false); EXPECT_GL_NO_ERROR(); glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels); } // Test to ensure that CopyTexture works with LUMINANCE_ALPHA texture as a destination with // UnpackUnmultiplyAlpha parameter TEST_P(CopyTextureTestDest, LuminanceAlphaUnmultiply) { if (!checkExtensions()) { return; } GLColor originalPixels(50u, 100u, 150u, 200u); GLColor expectedPixels(64u, 64u, 64u, 200u); // ReadPixels doesn't work with LUMINANCE_ALPHA (non-renderable), so we copy again back to an // RGBA texture to verify contents. glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels); glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 1, 1, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, nullptr); glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, false, false, true); EXPECT_GL_NO_ERROR(); glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels); } // Test to ensure that CopyTexture works with ALPHA texture as a destination TEST_P(CopyTextureTestDest, Alpha) { if (!checkExtensions()) { return; } GLColor originalPixels(50u, 100u, 150u, 155u); GLColor expectedPixels(0u, 0u, 0u, 155u); // ReadPixels doesn't work with ALPHA (non-renderable), so we copy again back to an RGBA // texture to verify contents. glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels); glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 1, 1, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr); glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_ALPHA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels); } // Test to ensure that CopyTexture works with ALPHA texture as a destination with // UnpackPremultiplyAlpha parameter TEST_P(CopyTextureTestDest, AlphaMultiply) { if (!checkExtensions()) { return; } GLColor originalPixels(50u, 100u, 150u, 155u); GLColor expectedPixels(0u, 0u, 0u, 155u); // ReadPixels doesn't work with ALPHA (non-renderable), so we copy again back to an RGBA // texture to verify contents. glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels); glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 1, 1, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr); glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_ALPHA, GL_UNSIGNED_BYTE, false, true, false); EXPECT_GL_NO_ERROR(); glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels); } // Test to ensure that CopyTexture works with ALPHA texture as a destination with // UnpackUnmultiplyAlpha parameter TEST_P(CopyTextureTestDest, AlphaUnmultiply) { if (!checkExtensions()) { return; } GLColor originalPixels(50u, 100u, 150u, 155u); GLColor expectedPixels(0u, 0u, 0u, 155u); // ReadPixels doesn't work with ALPHA (non-renderable), so we copy again back to an RGBA // texture to verify contents. glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels); glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 1, 1, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr); glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_ALPHA, GL_UNSIGNED_BYTE, false, false, true); EXPECT_GL_NO_ERROR(); glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels); } // Test to ensure that CopyTexture uses the correct ALPHA passthrough shader to ensure RGB channels // are set to 0. TEST_P(CopyTextureTestDest, AlphaCopyWithRGB) { // http://anglebug.com/4121 ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux() && IsOpenGLES()); ANGLE_SKIP_TEST_IF(!checkExtensions()); ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_texture_half_float")); GLColor originalPixels(50u, 100u, 150u, 155u); GLColor expectedPixels(0u, 0u, 0u, 155u); // ReadPixels doesn't work with ALPHA (non-renderable), so we copy again back to an RGBA // texture to verify contents. glBindTexture(GL_TEXTURE_2D, mTextures[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels); glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 1, 1, 0, GL_ALPHA, GL_HALF_FLOAT_OES, nullptr); glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_ALPHA, GL_HALF_FLOAT_OES, false, false, false); EXPECT_GL_NO_ERROR(); glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels); } // Bug where TEXTURE_SWIZZLE_RGBA was not reset after the Luminance workaround. (crbug.com/1022080) TEST_P(CopyTextureTestES3, LuminanceWorkaroundTextureSwizzleBug) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB")); { GLColor pixels(50u, 20u, 100u, 150u); // Hit BlitGL::copySubImageToLUMAWorkaroundTexture by copying an ALPHA texture GLTexture srcTexture; glBindTexture(GL_TEXTURE_2D, srcTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &pixels); GLFramebuffer srcFBO; glBindFramebuffer(GL_FRAMEBUFFER, srcFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, srcTexture, 0); GLTexture dstTexture; glBindTexture(GL_TEXTURE_2D, dstTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 1, 1, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1); EXPECT_GL_NO_ERROR(); } { // This time hit BlitGL::blitColorBufferWithShader by copying an SRGB texture GLColor pixels(100u, 200u, 50u, 210u); GLTexture srcTexture; glBindTexture(GL_TEXTURE_2D, srcTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE, &pixels); GLFramebuffer srcFBO; glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFBO); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, srcTexture, 0); GLTexture dstTexture; glBindTexture(GL_TEXTURE_2D, dstTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE, nullptr); GLFramebuffer dstFBO; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFBO); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstTexture, 0); glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST); // The previous workaround should not affect this copy glBindFramebuffer(GL_FRAMEBUFFER, dstFBO); EXPECT_PIXEL_COLOR_EQ(0, 0, pixels); } } // Test to ensure that CopyTexture will fail with a non-zero level and NPOT texture in WebGL TEST_P(CopyTextureTestWebGL, NPOT) { if (IsGLExtensionRequestable("GL_CHROMIUM_copy_texture")) { glRequestExtensionANGLE("GL_CHROMIUM_copy_texture"); } ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_CHROMIUM_copy_texture")); std::vector pixelData(10 * 10, GLColor::red); glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 10, 10, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelData.data()); // Do a basic copy to make sure things work glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); // Do the same operation with destLevel 1, which should fail glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 1, GL_RGBA, GL_UNSIGNED_BYTE, false, false, false); EXPECT_GL_ERROR(GL_INVALID_VALUE); } // Test the newly added ES3 unorm formats TEST_P(CopyTextureTestES3, ES3UnormFormats) { if (!checkExtensions()) { return; } // http://anglebug.com/4092 ANGLE_SKIP_TEST_IF(IsAndroid()); // http://anglebug.com/5127 ANGLE_SKIP_TEST_IF(IsWindows() && IsOpenGL() && IsIntel()); auto testOutput = [this](GLuint texture, const GLColor &expectedColor) { constexpr char kVS[] = "#version 300 es\n" "in vec4 position;\n" "out vec2 texcoord;\n" "void main()\n" "{\n" " gl_Position = vec4(position.xy, 0.0, 1.0);\n" " texcoord = (position.xy * 0.5) + 0.5;\n" "}\n"; constexpr char kFS[] = "#version 300 es\n" "precision mediump float;\n" "uniform sampler2D tex;\n" "in vec2 texcoord;\n" "out vec4 color;\n" "void main()\n" "{\n" " color = texture(tex, texcoord);\n" "}\n"; ANGLE_GL_PROGRAM(program, kVS, kFS); glUseProgram(program); GLRenderbuffer rbo; glBindRenderbuffer(GL_RENDERBUFFER, rbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 1, 1); GLFramebuffer fbo; glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glUniform1i(glGetUniformLocation(program.get(), "tex"), 0); drawQuad(program, "position", 0.5f, 1.0f, true); EXPECT_PIXEL_COLOR_NEAR(0, 0, expectedColor, 1.0); }; auto testCopyCombination = [testOutput](GLenum sourceInternalFormat, GLenum sourceFormat, GLenum sourceType, const GLColor &sourceColor, GLenum destInternalFormat, GLenum destType, bool flipY, bool premultiplyAlpha, bool unmultiplyAlpha, const GLColor &expectedColor) { GLTexture sourceTexture; glBindTexture(GL_TEXTURE_2D, sourceTexture); glTexImage2D(GL_TEXTURE_2D, 0, sourceInternalFormat, 1, 1, 0, sourceFormat, sourceType, &sourceColor); GLTexture destTexture; glBindTexture(GL_TEXTURE_2D, destTexture); glCopyTextureCHROMIUM(sourceTexture, 0, GL_TEXTURE_2D, destTexture, 0, destInternalFormat, destType, flipY, premultiplyAlpha, unmultiplyAlpha); ASSERT_GL_NO_ERROR(); testOutput(destTexture, expectedColor); }; auto testSubCopyCombination = [testOutput](GLenum sourceInternalFormat, GLenum sourceFormat, GLenum sourceType, const GLColor &sourceColor, GLenum destInternalFormat, GLenum destFormat, GLenum destType, bool flipY, bool premultiplyAlpha, bool unmultiplyAlpha, const GLColor &expectedColor) { GLTexture sourceTexture; glBindTexture(GL_TEXTURE_2D, sourceTexture); glTexImage2D(GL_TEXTURE_2D, 0, sourceInternalFormat, 1, 1, 0, sourceFormat, sourceType, &sourceColor); GLTexture destTexture; glBindTexture(GL_TEXTURE_2D, destTexture); glTexImage2D(GL_TEXTURE_2D, 0, destInternalFormat, 1, 1, 0, destFormat, destType, nullptr); glCopySubTextureCHROMIUM(sourceTexture, 0, GL_TEXTURE_2D, destTexture, 0, 0, 0, 0, 0, 1, 1, flipY, premultiplyAlpha, unmultiplyAlpha); ASSERT_GL_NO_ERROR(); testOutput(destTexture, expectedColor); }; // New LUMA source formats testCopyCombination(GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, GLColor(128, 0, 0, 0), GL_RGB, GL_UNSIGNED_BYTE, false, false, false, GLColor(128, 128, 128, 255)); testCopyCombination(GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, GLColor(128, 64, 0, 0), GL_RGB, GL_UNSIGNED_BYTE, false, false, false, GLColor(128, 128, 128, 255)); testCopyCombination(GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, GLColor(128, 64, 0, 0), GL_RGB, GL_UNSIGNED_BYTE, false, true, false, GLColor(32, 32, 32, 255)); testCopyCombination(GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, GLColor(128, 128, 0, 0), GL_RGB, GL_UNSIGNED_BYTE, false, false, true, GLColor(255, 255, 255, 255)); testCopyCombination(GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, GLColor(128, 0, 0, 0), GL_RGBA, GL_UNSIGNED_BYTE, false, false, false, GLColor(0, 0, 0, 128)); testCopyCombination(GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, GLColor(128, 0, 0, 0), GL_RGBA, GL_UNSIGNED_BYTE, false, false, true, GLColor(0, 0, 0, 128)); testCopyCombination(GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, GLColor(128, 0, 0, 0), GL_RGBA, GL_UNSIGNED_BYTE, false, true, false, GLColor(0, 0, 0, 128)); // New sRGB dest formats if (IsGLExtensionEnabled("GL_EXT_sRGB")) { testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_SRGB, GL_UNSIGNED_BYTE, false, false, false, GLColor(55, 13, 4, 255)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_SRGB, GL_UNSIGNED_BYTE, false, true, false, GLColor(13, 4, 1, 255)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE, false, false, false, GLColor(55, 13, 4, 128)); testSubCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_SRGB, GL_SRGB, GL_UNSIGNED_BYTE, false, false, false, GLColor(55, 13, 4, 255)); testSubCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_SRGB, GL_SRGB, GL_UNSIGNED_BYTE, false, true, false, GLColor(13, 4, 1, 255)); testSubCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_SRGB_ALPHA_EXT, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE, false, false, false, GLColor(55, 13, 4, 128)); } } // Test the newly added ES3 float formats TEST_P(CopyTextureTestES3, ES3FloatFormats) { if (!checkExtensions()) { return; } ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_color_buffer_float")); auto testOutput = [this](GLuint texture, const GLColor32F &expectedColor) { constexpr char kVS[] = "#version 300 es\n" "in vec4 position;\n" "out vec2 texcoord;\n" "void main()\n" "{\n" " gl_Position = vec4(position.xy, 0.0, 1.0);\n" " texcoord = (position.xy * 0.5) + 0.5;\n" "}\n"; constexpr char kFS[] = "#version 300 es\n" "precision mediump float;\n" "uniform sampler2D tex;\n" "in vec2 texcoord;\n" "out vec4 color;\n" "void main()\n" "{\n" " color = texture(tex, texcoord);\n" "}\n"; ANGLE_GL_PROGRAM(program, kVS, kFS); glUseProgram(program); GLRenderbuffer rbo; glBindRenderbuffer(GL_RENDERBUFFER, rbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA32F, 1, 1); GLFramebuffer fbo; glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glUniform1i(glGetUniformLocation(program.get(), "tex"), 0); drawQuad(program, "position", 0.5f, 1.0f, true); EXPECT_PIXEL_COLOR32F_NEAR(0, 0, expectedColor, 0.05); }; auto testCopyCombination = [testOutput](GLenum sourceInternalFormat, GLenum sourceFormat, GLenum sourceType, const GLColor &sourceColor, GLenum destInternalFormat, GLenum destType, bool flipY, bool premultiplyAlpha, bool unmultiplyAlpha, const GLColor32F &expectedColor) { GLTexture sourceTexture; glBindTexture(GL_TEXTURE_2D, sourceTexture); glTexImage2D(GL_TEXTURE_2D, 0, sourceInternalFormat, 1, 1, 0, sourceFormat, sourceType, &sourceColor); GLTexture destTexture; glBindTexture(GL_TEXTURE_2D, destTexture); glCopyTextureCHROMIUM(sourceTexture, 0, GL_TEXTURE_2D, destTexture, 0, destInternalFormat, destType, flipY, premultiplyAlpha, unmultiplyAlpha); ASSERT_GL_NO_ERROR(); testOutput(destTexture, expectedColor); }; testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGBA32F, GL_FLOAT, false, false, false, GLColor32F(0.5f, 0.25f, 0.125f, 0.5f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGBA32F, GL_FLOAT, false, true, false, GLColor32F(0.25f, 0.125f, 0.0625f, 0.5f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGBA32F, GL_FLOAT, false, false, true, GLColor32F(1.0f, 0.5f, 0.25f, 0.5f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_R16F, GL_FLOAT, false, false, false, GLColor32F(0.5f, 0.0f, 0.0f, 1.0f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_R16F, GL_FLOAT, false, true, false, GLColor32F(0.25f, 0.0f, 0.0f, 1.0f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_R16F, GL_FLOAT, false, false, true, GLColor32F(1.0f, 0.0f, 0.0f, 1.0f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RG16F, GL_FLOAT, false, false, false, GLColor32F(0.5f, 0.25f, 0.0f, 1.0f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RG16F, GL_FLOAT, false, true, false, GLColor32F(0.25f, 0.125f, 0.0f, 1.0f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RG16F, GL_FLOAT, false, false, true, GLColor32F(1.0f, 0.5f, 0.0f, 1.0f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB16F, GL_FLOAT, false, false, false, GLColor32F(0.5f, 0.25f, 0.125f, 1.0f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB16F, GL_FLOAT, false, true, false, GLColor32F(0.25f, 0.125f, 0.0625f, 1.0f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB16F, GL_FLOAT, false, false, true, GLColor32F(1.0f, 0.5f, 0.25f, 1.0f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_R11F_G11F_B10F, GL_FLOAT, false, false, false, GLColor32F(0.5f, 0.25f, 0.125f, 1.0f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_R11F_G11F_B10F, GL_FLOAT, false, true, false, GLColor32F(0.25f, 0.125f, 0.0625f, 1.0f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_R11F_G11F_B10F, GL_FLOAT, false, false, true, GLColor32F(1.0f, 0.5f, 0.25f, 1.0f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB9_E5, GL_FLOAT, false, false, false, GLColor32F(0.5f, 0.25f, 0.125f, 1.0f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB9_E5, GL_FLOAT, false, true, false, GLColor32F(0.25f, 0.125f, 0.0625f, 1.0f)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB9_E5, GL_FLOAT, false, false, true, GLColor32F(1.0f, 0.5f, 0.25f, 1.0f)); } // Test the newly added ES3 unsigned integer formats TEST_P(CopyTextureTestES3, ES3UintFormats) { ANGLE_SKIP_TEST_IF(IsLinux() && IsOpenGL() && IsIntel()); if (!checkExtensions()) { return; } using GLColor32U = std::tuple; auto testOutput = [this](GLuint texture, const GLColor32U &expectedColor) { constexpr char kVS[] = "#version 300 es\n" "in vec4 position;\n" "out vec2 texcoord;\n" "void main()\n" "{\n" " gl_Position = vec4(position.xy, 0.0, 1.0);\n" " texcoord = (position.xy * 0.5) + 0.5;\n" "}\n"; constexpr char kFS[] = "#version 300 es\n" "precision mediump float;\n" "precision mediump usampler2D;\n" "in vec2 texcoord;\n" "uniform usampler2D tex;\n" "out uvec4 color;\n" "void main()\n" "{\n" " color = texture(tex, texcoord);\n" "}\n"; ANGLE_GL_PROGRAM(program, kVS, kFS); glUseProgram(program); GLRenderbuffer rbo; glBindRenderbuffer(GL_RENDERBUFFER, rbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8UI, 1, 1); GLFramebuffer fbo; glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glUniform1i(glGetUniformLocation(program.get(), "tex"), 0); drawQuad(program, "position", 0.5f, 1.0f, true); ASSERT_GL_NO_ERROR(); GLuint pixel[4] = {0}; glReadPixels(0, 0, 1, 1, GL_RGBA_INTEGER, GL_UNSIGNED_INT, pixel); ASSERT_GL_NO_ERROR(); EXPECT_NEAR(std::get<0>(expectedColor), pixel[0], 1); EXPECT_NEAR(std::get<1>(expectedColor), pixel[1], 1); EXPECT_NEAR(std::get<2>(expectedColor), pixel[2], 1); EXPECT_NEAR(std::get<3>(expectedColor), pixel[3], 1); }; auto testCopyCombination = [testOutput](GLenum sourceInternalFormat, GLenum sourceFormat, GLenum sourceType, const GLColor &sourceColor, GLenum destInternalFormat, GLenum destType, bool flipY, bool premultiplyAlpha, bool unmultiplyAlpha, const GLColor32U &expectedColor) { GLTexture sourceTexture; glBindTexture(GL_TEXTURE_2D, sourceTexture); glTexImage2D(GL_TEXTURE_2D, 0, sourceInternalFormat, 1, 1, 0, sourceFormat, sourceType, &sourceColor); GLTexture destTexture; glBindTexture(GL_TEXTURE_2D, destTexture); glCopyTextureCHROMIUM(sourceTexture, 0, GL_TEXTURE_2D, destTexture, 0, destInternalFormat, destType, flipY, premultiplyAlpha, unmultiplyAlpha); ASSERT_GL_NO_ERROR(); testOutput(destTexture, expectedColor); }; testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGBA8UI, GL_UNSIGNED_BYTE, false, false, false, GLColor32U(128, 64, 32, 128)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGBA8UI, GL_UNSIGNED_BYTE, false, true, false, GLColor32U(64, 32, 16, 128)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGBA8UI, GL_UNSIGNED_BYTE, false, false, true, GLColor32U(255, 128, 64, 128)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB8UI, GL_UNSIGNED_BYTE, false, false, false, GLColor32U(128, 64, 32, 1)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB8UI, GL_UNSIGNED_BYTE, false, true, false, GLColor32U(64, 32, 16, 1)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB8UI, GL_UNSIGNED_BYTE, false, false, true, GLColor32U(255, 128, 64, 1)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RG8UI, GL_UNSIGNED_BYTE, false, false, false, GLColor32U(128, 64, 0, 1)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RG8UI, GL_UNSIGNED_BYTE, false, true, false, GLColor32U(64, 32, 0, 1)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RG8UI, GL_UNSIGNED_BYTE, false, false, true, GLColor32U(255, 128, 0, 1)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_R8UI, GL_UNSIGNED_BYTE, false, false, false, GLColor32U(128, 0, 0, 1)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_R8UI, GL_UNSIGNED_BYTE, false, true, false, GLColor32U(64, 0, 0, 1)); testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(120, 64, 32, 128), GL_R8UI, GL_UNSIGNED_BYTE, false, false, true, GLColor32U(240, 0, 0, 1)); } // Test that using an offset in CopySubTexture works correctly for non-renderable float targets TEST_P(CopyTextureTestES3, CopySubTextureOffsetNonRenderableFloat) { if (!checkExtensions()) { return; } ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_color_buffer_float")); auto testOutput = [this](GLuint texture, const GLColor32F &expectedColor) { constexpr char kVS[] = "#version 300 es\n" "in vec4 position;\n" "out vec2 texcoord;\n" "void main()\n" "{\n" " gl_Position = vec4(position.xy, 0.0, 1.0);\n" " texcoord = (position.xy * 0.5) + 0.5;\n" "}\n"; constexpr char kFS[] = "#version 300 es\n" "precision mediump float;\n" "uniform sampler2D tex;\n" "in vec2 texcoord;\n" "out vec4 color;\n" "void main()\n" "{\n" " color = texture(tex, texcoord);\n" "}\n"; ANGLE_GL_PROGRAM(program, kVS, kFS); glUseProgram(program); GLRenderbuffer rbo; glBindRenderbuffer(GL_RENDERBUFFER, rbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA32F, 1, 1); GLFramebuffer fbo; glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glUniform1i(glGetUniformLocation(program.get(), "tex"), 0); drawQuad(program, "position", 0.5f, 1.0f, true); EXPECT_PIXEL_COLOR32F_NEAR(0, 0, expectedColor, 0.05); }; auto testCopy = [this, testOutput](GLenum destInternalFormat, GLenum destFormat, GLenum destType) { GLColor rgbaPixels[4 * 4] = {GLColor::red, GLColor::green, GLColor::blue, GLColor::black}; glBindTexture(GL_TEXTURE_2D, mTextures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbaPixels); GLTexture destTexture; glBindTexture(GL_TEXTURE_2D, destTexture); glTexImage2D(GL_TEXTURE_2D, 0, destInternalFormat, 1, 1, 0, destFormat, destType, nullptr); glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, destTexture, 0, 0, 0, 0, 0, 1, 1, false, false, false); EXPECT_GL_NO_ERROR(); testOutput(destTexture, kFloatRed); glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, destTexture, 0, 0, 0, 1, 0, 1, 1, false, false, false); EXPECT_GL_NO_ERROR(); testOutput(destTexture, kFloatGreen); glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, destTexture, 0, 0, 0, 0, 1, 1, 1, false, false, false); EXPECT_GL_NO_ERROR(); testOutput(destTexture, kFloatBlue); glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, destTexture, 0, 0, 0, 1, 1, 1, 1, false, false, false); EXPECT_GL_NO_ERROR(); testOutput(destTexture, kFloatBlack); }; testCopy(GL_RGB9_E5, GL_RGB, GL_FLOAT); } // Test that copying from one mip to another works TEST_P(CopyTextureTestES3, CopyBetweenMips) { if (!checkExtensions()) { return; } glBindFramebuffer(GL_FRAMEBUFFER, 0); ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Texture2DLod(), essl3_shaders::fs::Texture2DLod()); glUseProgram(program); GLint textureLoc = glGetUniformLocation(program, essl3_shaders::Texture2DUniform()); GLint lodLoc = glGetUniformLocation(program, essl3_shaders::LodUniform()); ASSERT_NE(-1, textureLoc); ASSERT_NE(-1, lodLoc); glUniform1i(textureLoc, 0); glUniform1f(lodLoc, 0); GLTexture texture; // Create a texture with 3 mips. Mip0 will contain an image as follows: // // G G B G // G R R G // G R R G // G G G G // // The 2x2 red square and 1x1 blue square will be copied to the other mips. const GLColor kMip0InitColor[4 * 4] = { GLColor::green, GLColor::green, GLColor::blue, GLColor::green, GLColor::green, GLColor::red, GLColor::red, GLColor::green, GLColor::green, GLColor::red, GLColor::red, GLColor::green, GLColor::green, GLColor::green, GLColor::green, GLColor::green, }; const GLColor kMipOtherInitColor[4] = { GLColor::black, GLColor::black, GLColor::black, GLColor::black, }; glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, kMip0InitColor); glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, kMipOtherInitColor); glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, kMipOtherInitColor); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); ASSERT_GL_NO_ERROR(); // Commit texture glUniform1f(lodLoc, 0); drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); EXPECT_PIXEL_COLOR_EQ(0, 0, kMip0InitColor[0]); glUniform1f(lodLoc, 1); drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); EXPECT_PIXEL_COLOR_EQ(0, 0, kMipOtherInitColor[0]); glUniform1f(lodLoc, 2); drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); EXPECT_PIXEL_COLOR_EQ(0, 0, kMipOtherInitColor[0]); ASSERT_GL_NO_ERROR(); // Copy from mip 0 to mip 1. The level is not redefined, so a direct copy can potentially be // done. glCopySubTextureCHROMIUM(texture, 0, GL_TEXTURE_2D, texture, 1, 0, 0, 1, 1, 2, 2, false, false, false); EXPECT_GL_NO_ERROR(); // Copy from mip 0 to mip 2. Again, the level is not redefined. glCopySubTextureCHROMIUM(texture, 0, GL_TEXTURE_2D, texture, 2, 0, 0, 2, 0, 1, 1, false, false, false); EXPECT_GL_NO_ERROR(); // Verify mips 1 and 2. int w = getWindowWidth() - 1; int h = getWindowHeight() - 1; glUniform1f(lodLoc, 1); drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); EXPECT_PIXEL_COLOR_EQ(0, 0, kMip0InitColor[4 * 1 + 1]); EXPECT_PIXEL_COLOR_EQ(w, 0, kMip0InitColor[4 * 1 + 2]); EXPECT_PIXEL_COLOR_EQ(0, h, kMip0InitColor[4 * 2 + 1]); EXPECT_PIXEL_COLOR_EQ(w, h, kMip0InitColor[4 * 2 + 2]); glUniform1f(lodLoc, 2); drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); EXPECT_PIXEL_COLOR_EQ(0, 0, kMip0InitColor[4 * 0 + 2]); EXPECT_PIXEL_COLOR_EQ(w, 0, kMip0InitColor[4 * 0 + 2]); EXPECT_PIXEL_COLOR_EQ(0, h, kMip0InitColor[4 * 0 + 2]); EXPECT_PIXEL_COLOR_EQ(w, h, kMip0InitColor[4 * 0 + 2]); } // Test that swizzle on source texture does not affect the copy. TEST_P(CopyTextureTestES3, SwizzleOnSource) { const GLColor kSourceColor = GLColor(31, 73, 146, 228); // Create image with swizzle. If swizzle is mistakenly applied, resulting color would be // kSourceColor.gbar GLTexture sourceTexture; glBindTexture(GL_TEXTURE_2D, sourceTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &kSourceColor); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_GREEN); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_BLUE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ALPHA); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED); GLTexture destTexture; glBindTexture(GL_TEXTURE_2D, destTexture); // Note: flipY is used to avoid direct transfer between textures and force a draw-based path. glCopyTextureCHROMIUM(sourceTexture, 0, GL_TEXTURE_2D, destTexture, 0, GL_RGBA8, GL_UNSIGNED_BYTE, true, false, false); ASSERT_GL_NO_ERROR(); // Verify the copy. ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D()); glUseProgram(program); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, destTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); GLint textureLocation = glGetUniformLocation(program, essl1_shaders::Texture2DUniform()); ASSERT_NE(-1, textureLocation); glUniform1i(textureLocation, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, kSourceColor); } // Test that copy after invalidate works TEST_P(CopyTextureTestES3, InvalidateCopyThenBlend) { ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); // http://anglebug.com/5155 ANGLE_SKIP_TEST_IF(IsOSX() && IsIntel() && IsOpenGL()); // http://anglebug.com/5156 ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsOpenGL()); constexpr GLsizei kSize = 4; GLTexture texture; glBindTexture(GL_TEXTURE_2D, texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kSize * 2, kSize * 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kSize * 2, kSize * 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 3, GL_RGBA8, kSize / 2, kSize / 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 4, GL_RGBA8, kSize / 4, kSize / 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4); GLFramebuffer framebuffer; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 2); EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); // Invalidate the framebuffer. const GLenum discards[] = {GL_COLOR_ATTACHMENT0}; glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, discards); ASSERT_GL_NO_ERROR(); // Copy into the framebuffer attachment. const std::vector kSourceColor(kSize * kSize, GLColor::green); GLTexture sourceTexture; glBindTexture(GL_TEXTURE_2D, sourceTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, kSourceColor.data()); // Note: flipY is used to avoid direct transfer between textures and force a draw-based path. glCopyTextureCHROMIUM(sourceTexture, 0, GL_TEXTURE_2D, texture, 2, GL_RGBA8, GL_UNSIGNED_BYTE, true, false, false); ASSERT_GL_NO_ERROR(); // Draw and blend, making sure both the copy and draw happen correctly. glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f); ASSERT_GL_NO_ERROR(); // Make sure the blend was correctly done. EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow); EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::yellow); EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::yellow); EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::yellow); } void CopyTextureTestES3::invalidateBlitThenBlendCommon(GLsizei layerCount) { // http://anglebug.com/5152 ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGL()); // http://anglebug.com/5155 ANGLE_SKIP_TEST_IF(IsOSX() && IsIntel() && IsOpenGL()); // http://anglebug.com/5156 ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsOpenGL()); ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); constexpr GLsizei kSize = 4; GLTexture texture; glBindTexture(GL_TEXTURE_2D_ARRAY, texture); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kSize * 2, kSize * 2, layerCount, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, kSize * 2, kSize * 2, layerCount, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage3D(GL_TEXTURE_2D_ARRAY, 2, GL_RGBA8, kSize, kSize, layerCount, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage3D(GL_TEXTURE_2D_ARRAY, 3, GL_RGBA8, kSize / 2, kSize / 2, layerCount, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage3D(GL_TEXTURE_2D_ARRAY, 4, GL_RGBA8, kSize / 4, kSize / 4, layerCount, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BASE_LEVEL, 1); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 4); GLFramebuffer framebuffer; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 2, layerCount / 2); EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); // Invalidate the framebuffer. const GLenum discards[] = {GL_COLOR_ATTACHMENT0}; glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, discards); ASSERT_GL_NO_ERROR(); // Blit into the framebuffer attachment. const std::vector kSourceColor(kSize * kSize, GLColor::green); GLTexture sourceTexture; glBindTexture(GL_TEXTURE_2D, sourceTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, kSourceColor.data()); GLFramebuffer sourceFramebuffer; glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebuffer); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexture, 0); EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_READ_FRAMEBUFFER); glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST); ASSERT_GL_NO_ERROR(); // Draw and blend, making sure both the blit and draw happen correctly. glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f); ASSERT_GL_NO_ERROR(); // Make sure the blend was correctly done. glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow); EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::yellow); EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::yellow); EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::yellow); } // Test that blit after invalidate works with non-zero layer TEST_P(CopyTextureTestES3, InvalidateBlitThenBlend3Layers) { invalidateBlitThenBlendCommon(3); } // Test that blit after invalidate works with non-zero layer that is very large TEST_P(CopyTextureTestES3, InvalidateBlitThenBlend1000Layers) { invalidateBlitThenBlendCommon(1000); } #ifdef Bool // X11 craziness. # undef Bool #endif ANGLE_INSTANTIATE_TEST_ES2(CopyTextureTest); ANGLE_INSTANTIATE_TEST_COMBINE_5(CopyTextureVariationsTest, CopyTextureVariationsTestPrint, testing::ValuesIn(kCopyTextureVariationsSrcFormats), testing::ValuesIn(kCopyTextureVariationsDstFormats), testing::Bool(), // flipY testing::Bool(), // premultiplyAlpha testing::Bool(), // unmultiplyAlpha ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES(), ES2_VULKAN(), ES2_METAL()); ANGLE_INSTANTIATE_TEST_ES2(CopyTextureTestWebGL); ANGLE_INSTANTIATE_TEST(CopyTextureTestDest, ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES(), ES2_VULKAN(), ES2_METAL()); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CopyTextureTestES3); ANGLE_INSTANTIATE_TEST_ES3(CopyTextureTestES3); } // namespace angle