blob: b5710b7d0051056c0fa808db7f23330a311757b9 [file] [log] [blame]
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// GetImageTest:
// Tests for the ANGLE_get_image extension.
//
#include "image_util/storeimage.h"
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
#include "util/random_utils.h"
using namespace angle;
namespace
{
constexpr uint32_t kSize = 32;
constexpr char kExtensionName[] = "GL_ANGLE_get_image";
constexpr uint32_t kSmallSize = 2;
constexpr uint8_t kUNormZero = 0x00;
constexpr uint8_t kUNormHalf = 0x7F;
constexpr uint8_t kUNormFull = 0xFF;
class GetImageTest : public ANGLETest<>
{
public:
GetImageTest()
{
setWindowWidth(kSize);
setWindowHeight(kSize);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
};
class GetImageTestNoExtensions : public ANGLETest<>
{
public:
GetImageTestNoExtensions() { setExtensionsEnabled(false); }
};
class GetImageTestES1 : public GetImageTest
{
public:
GetImageTestES1() {}
};
class GetImageTestES3 : public GetImageTest
{
public:
GetImageTestES3() {}
};
class GetImageTestES31 : public GetImageTest
{
public:
GetImageTestES31() {}
};
class GetImageTestES32 : public GetImageTest
{
public:
GetImageTestES32() {}
};
GLTexture InitTextureWithFormatAndSize(GLenum format, uint32_t size, void *pixelData)
{
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, format, size, size, 0, format, GL_UNSIGNED_BYTE, pixelData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
return tex;
}
GLTexture InitTextureWithSize(uint32_t size, void *pixelData)
{
// Create a simple texture.
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
return tex;
}
GLTexture InitSimpleTexture()
{
std::vector<GLColor> pixelData(kSize * kSize, GLColor::red);
return InitTextureWithSize(kSize, pixelData.data());
}
GLRenderbuffer InitRenderbufferWithSize(uint32_t size)
{
// Create a simple renderbuffer.
GLRenderbuffer renderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, size, size);
return renderbuf;
}
GLRenderbuffer InitSimpleRenderbuffer()
{
return InitRenderbufferWithSize(kSize);
}
// Test validation for the extension functions.
TEST_P(GetImageTest, NegativeAPI)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
// Draw once with simple texture.
GLTexture tex = InitSimpleTexture();
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
// Pack pixels tightly.
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Verify GetTexImage can work with correct parameters.
std::vector<GLColor> buffer(kSize * kSize);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_NO_ERROR();
// Test invalid texture target.
glGetTexImageANGLE(GL_TEXTURE_CUBE_MAP, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Test invalid texture level.
glGetTexImageANGLE(GL_TEXTURE_2D, -1, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_VALUE);
glGetTexImageANGLE(GL_TEXTURE_2D, 2000, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Test invalid format and type.
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_NONE, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_NONE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Tests GetCompressed on an uncompressed texture.
glGetCompressedTexImageANGLE(GL_TEXTURE_2D, 0, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
// Create a simple renderbuffer.
GLRenderbuffer renderbuf = InitSimpleRenderbuffer();
ASSERT_GL_NO_ERROR();
// Verify GetRenderbufferImage can work with correct parameters.
glGetRenderbufferImageANGLE(GL_RENDERBUFFER, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_NO_ERROR();
// Test invalid renderbuffer target.
glGetRenderbufferImageANGLE(GL_TEXTURE_2D, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Test invalid renderbuffer format/type.
glGetRenderbufferImageANGLE(GL_RENDERBUFFER, GL_NONE, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glGetRenderbufferImageANGLE(GL_RENDERBUFFER, GL_RGBA, GL_NONE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Pack buffer tests. Requires ES 3+ or extension.
if (getClientMajorVersion() >= 3 || IsGLExtensionEnabled("GL_NV_pixel_buffer_object"))
{
// Test valid pack buffer.
GLBuffer packBuffer;
glBindBuffer(GL_PIXEL_PACK_BUFFER, packBuffer);
glBufferData(GL_PIXEL_PACK_BUFFER, kSize * kSize * sizeof(GLColor), nullptr,
GL_STATIC_DRAW);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glGetRenderbufferImageANGLE(GL_RENDERBUFFER, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
// Test too small pack buffer.
glBufferData(GL_PIXEL_PACK_BUFFER, kSize, nullptr, GL_STATIC_DRAW);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glGetRenderbufferImageANGLE(GL_RENDERBUFFER, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
}
// Simple test for GetTexImage
TEST_P(GetImageTest, GetTexImage)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
std::vector<GLColor> expectedData = {GLColor::red, GLColor::blue, GLColor::green,
GLColor::yellow};
glViewport(0, 0, kSmallSize, kSmallSize);
// Draw once with simple texture.
GLTexture tex = InitTextureWithSize(kSmallSize, expectedData.data());
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
// Pack pixels tightly.
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Verify GetImage.
std::vector<GLColor> actualData(kSmallSize * kSmallSize);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, actualData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(expectedData, actualData);
}
// Simple cube map test for GetTexImage
TEST_P(GetImageTest, CubeMap)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
const std::array<std::array<GLColor, kSmallSize * kSmallSize>, kCubeFaces.size()> expectedData =
{{
{GLColor::red, GLColor::red, GLColor::red, GLColor::red},
{GLColor::green, GLColor::green, GLColor::green, GLColor::green},
{GLColor::blue, GLColor::blue, GLColor::blue, GLColor::blue},
{GLColor::yellow, GLColor::yellow, GLColor::yellow, GLColor::yellow},
{GLColor::cyan, GLColor::cyan, GLColor::cyan, GLColor::cyan},
{GLColor::magenta, GLColor::magenta, GLColor::magenta, GLColor::magenta},
}};
GLTexture texture;
glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
for (size_t faceIndex = 0; faceIndex < kCubeFaces.size(); ++faceIndex)
{
glTexImage2D(kCubeFaces[faceIndex], 0, GL_RGBA, kSmallSize, kSmallSize, 0, GL_RGBA,
GL_UNSIGNED_BYTE, expectedData[faceIndex].data());
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Pack pixels tightly.
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Verify GetImage.
std::array<GLColor, kSmallSize *kSmallSize> actualData = {};
for (size_t faceIndex = 0; faceIndex < kCubeFaces.size(); ++faceIndex)
{
glGetTexImageANGLE(kCubeFaces[faceIndex], 0, GL_RGBA, GL_UNSIGNED_BYTE, actualData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(expectedData[faceIndex], actualData);
}
}
// Simple test for GetRenderbufferImage
TEST_P(GetImageTest, GetRenderbufferImage)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
std::vector<GLColor> expectedData = {GLColor::red, GLColor::blue, GLColor::green,
GLColor::yellow};
glViewport(0, 0, kSmallSize, kSmallSize);
// Set up a simple Framebuffer with a Renderbuffer.
GLRenderbuffer renderbuffer = InitRenderbufferWithSize(kSmallSize);
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Draw once with simple texture.
GLTexture tex = InitTextureWithSize(kSmallSize, expectedData.data());
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
// Pack pixels tightly.
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Verify GetImage.
std::vector<GLColor> actualData(kSmallSize * kSmallSize);
glGetRenderbufferImageANGLE(GL_RENDERBUFFER, GL_RGBA, GL_UNSIGNED_BYTE, actualData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(expectedData, actualData);
}
// Verifies that the extension enums and entry points are invalid when the extension is disabled.
TEST_P(GetImageTestNoExtensions, EntryPointsInactive)
{
// Verify the extension is not enabled.
ASSERT_FALSE(IsGLExtensionEnabled(kExtensionName));
// Draw once with simple texture.
GLTexture tex = InitSimpleTexture();
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
// Query implementation format and type. Should give invalid enum.
GLint param;
glGetTexParameteriv(GL_TEXTURE_2D, GL_IMPLEMENTATION_COLOR_READ_FORMAT, &param);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glGetTexParameteriv(GL_TEXTURE_2D, GL_IMPLEMENTATION_COLOR_READ_TYPE, &param);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Verify calling GetTexImage produces an error.
std::vector<GLColor> buffer(kSize * kSize, 0);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
// Create a simple renderbuffer.
GLRenderbuffer renderbuf = InitSimpleRenderbuffer();
ASSERT_GL_NO_ERROR();
// Query implementation format and type. Should give invalid enum.
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_IMPLEMENTATION_COLOR_READ_FORMAT, &param);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_IMPLEMENTATION_COLOR_READ_TYPE, &param);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Verify calling GetRenderbufferImage produces an error.
glGetRenderbufferImageANGLE(GL_RENDERBUFFER, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Test LUMINANCE_ALPHA (non-renderable) format with GetTexImage
TEST_P(GetImageTest, GetTexImageLuminanceAlpha)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
constexpr GLColorRG kMediumLumAlpha = GLColorRG(kUNormHalf, kUNormHalf);
std::vector<GLColorRG> expectedData = {kMediumLumAlpha, kMediumLumAlpha, kMediumLumAlpha,
kMediumLumAlpha};
glViewport(0, 0, kSmallSize, kSmallSize);
// Set up a simple LUMINANCE_ALPHA texture
GLTexture tex =
InitTextureWithFormatAndSize(GL_LUMINANCE_ALPHA, kSmallSize, expectedData.data());
// Draw once with simple texture.
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor(kUNormHalf, kUNormHalf, kUNormHalf, kUNormHalf));
ASSERT_GL_NO_ERROR();
// Pack pixels tightly.
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Verify GetImage.
std::vector<GLColorRG> actualData(kSmallSize * kSmallSize);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, actualData.data());
EXPECT_GL_NO_ERROR();
for (uint32_t i = 0; i < kSmallSize * kSmallSize; ++i)
{
EXPECT_EQ(expectedData[i].R, actualData[i].R);
EXPECT_EQ(expectedData[i].G, actualData[i].G);
}
}
// Test LUMINANCE (non-renderable) format with GetTexImage
TEST_P(GetImageTest, GetTexImageLuminance)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
constexpr GLColorR kMediumLuminance = GLColorR(kUNormHalf);
std::vector<GLColorR> expectedData = {kMediumLuminance, kMediumLuminance, kMediumLuminance,
kMediumLuminance};
glViewport(0, 0, kSmallSize, kSmallSize);
// Set up a simple LUMINANCE texture
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
GLTexture tex = InitTextureWithFormatAndSize(GL_LUMINANCE, kSmallSize, expectedData.data());
// Draw once with simple texture.
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor(kUNormHalf, kUNormHalf, kUNormHalf, kUNormFull));
ASSERT_GL_NO_ERROR();
// Pack pixels tightly.
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Verify GetImage.
std::vector<GLColorR> actualData(kSmallSize * kSmallSize);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, actualData.data());
EXPECT_GL_NO_ERROR();
for (uint32_t i = 0; i < kSmallSize * kSmallSize; ++i)
{
EXPECT_EQ(expectedData[i].R, actualData[i].R);
}
}
// Test ALPHA (non-renderable) format with GetTexImage
TEST_P(GetImageTest, GetTexImageAlpha)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
constexpr GLColorR kMediumAlpha = GLColorR(kUNormHalf);
std::vector<GLColorR> expectedData = {kMediumAlpha, kMediumAlpha, kMediumAlpha, kMediumAlpha};
glViewport(0, 0, kSmallSize, kSmallSize);
// Set up a simple ALPHA texture
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
GLTexture tex = InitTextureWithFormatAndSize(GL_ALPHA, kSmallSize, expectedData.data());
// Draw once with simple texture
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor(kUNormZero, kUNormZero, kUNormZero, kUNormHalf));
ASSERT_GL_NO_ERROR();
// Pack pixels tightly.
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Verify we get back the correct pixels from GetTexImage
std::vector<GLColorR> actualData(kSmallSize * kSmallSize);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_ALPHA, GL_UNSIGNED_BYTE, actualData.data());
EXPECT_GL_NO_ERROR();
for (uint32_t i = 0; i < kSmallSize * kSmallSize; ++i)
{
EXPECT_EQ(expectedData[i].R, actualData[i].R);
}
}
// Tests GetImage behaviour with an RGB image.
TEST_P(GetImageTest, GetImageRGB)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
std::vector<GLColorRGB> expectedData = {GLColorRGB::red, GLColorRGB::blue, GLColorRGB::green,
GLColorRGB::yellow};
glViewport(0, 0, kSmallSize, kSmallSize);
// Pack pixels tightly.
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Init simple texture.
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, kSmallSize, kSmallSize, 0, GL_RGB, GL_UNSIGNED_BYTE,
expectedData.data());
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB, kSmallSize / 2, kSmallSize / 2, 0, GL_RGB,
GL_UNSIGNED_BYTE, expectedData.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Verify GetImage.
std::vector<GLColorRGB> actualData(kSmallSize * kSmallSize);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, actualData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(expectedData, actualData);
// Draw after the GetImage.
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(0, 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(1, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::yellow);
}
// Tests GetImage with 2D array textures.
TEST_P(GetImageTestES31, Texture2DArray)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
constexpr GLsizei kTextureSize = 2;
constexpr GLsizei kLayers = 4;
std::vector<GLColor> expectedPixels = {
GLColor::red, GLColor::red, GLColor::red, GLColor::red,
GLColor::green, GLColor::green, GLColor::green, GLColor::green,
GLColor::blue, GLColor::blue, GLColor::blue, GLColor::blue,
GLColor::yellow, GLColor::yellow, GLColor::yellow, GLColor::yellow,
};
GLTexture tex;
glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, kTextureSize, kTextureSize, kLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, expectedPixels.data());
ASSERT_GL_NO_ERROR();
std::vector<GLColor> actualPixels(expectedPixels.size(), GLColor::white);
glGetTexImageANGLE(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_UNSIGNED_BYTE, actualPixels.data());
ASSERT_GL_NO_ERROR();
EXPECT_EQ(expectedPixels, actualPixels);
}
// Tests GetImage with 3D textures.
TEST_P(GetImageTestES31, Texture3D)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
constexpr GLsizei kTextureSize = 2;
std::vector<GLColor> expectedPixels = {
GLColor::red, GLColor::red, GLColor::green, GLColor::green,
GLColor::blue, GLColor::blue, GLColor::yellow, GLColor::yellow,
};
GLTexture tex;
glBindTexture(GL_TEXTURE_3D, tex);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, kTextureSize, kTextureSize, kTextureSize, 0, GL_RGBA,
GL_UNSIGNED_BYTE, expectedPixels.data());
ASSERT_GL_NO_ERROR();
std::vector<GLColor> actualPixels(expectedPixels.size(), GLColor::white);
glGetTexImageANGLE(GL_TEXTURE_3D, 0, GL_RGBA, GL_UNSIGNED_BYTE, actualPixels.data());
ASSERT_GL_NO_ERROR();
EXPECT_EQ(expectedPixels, actualPixels);
}
// Tests GetImage with cube map array textures.
TEST_P(GetImageTestES31, TextureCubeMapArray)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_cube_map_array") &&
!IsGLExtensionEnabled("GL_OES_texture_cube_map_array"));
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
constexpr GLsizei kTextureSize = 1;
constexpr GLsizei kLayers = 2;
std::vector<GLColor> expectedPixels = {
GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow,
GLColor::cyan, GLColor::magenta, GLColor::red, GLColor::green,
GLColor::blue, GLColor::yellow, GLColor::cyan, GLColor::magenta,
};
ASSERT_EQ(expectedPixels.size(),
static_cast<size_t>(6 * kTextureSize * kTextureSize * kLayers));
GLTexture tex;
glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, tex);
glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, GL_RGBA, kTextureSize, kTextureSize, kLayers * 6, 0,
GL_RGBA, GL_UNSIGNED_BYTE, expectedPixels.data());
ASSERT_GL_NO_ERROR();
std::vector<GLColor> actualPixels(expectedPixels.size(), GLColor::white);
glGetTexImageANGLE(GL_TEXTURE_CUBE_MAP_ARRAY, 0, GL_RGBA, GL_UNSIGNED_BYTE,
actualPixels.data());
ASSERT_GL_NO_ERROR();
EXPECT_EQ(expectedPixels, actualPixels);
}
// Tests GetImage with an inconsistent 2D texture.
TEST_P(GetImageTest, InconsistentTexture2D)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
std::vector<GLColor> expectedData = {GLColor::red, GLColor::blue, GLColor::green,
GLColor::yellow};
glViewport(0, 0, kSmallSize, kSmallSize);
// Draw once with simple texture.
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSmallSize, kSmallSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
expectedData.data());
// The texture becomes inconsistent because a second 2x2 image does not fit in the mip chain.
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, kSmallSize, kSmallSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
expectedData.data());
// Pack pixels tightly.
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Verify GetImage.
std::vector<GLColor> actualData(kSmallSize * kSmallSize, GLColor::white);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, actualData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(expectedData, actualData);
std::fill(actualData.begin(), actualData.end(), GLColor::white);
glGetTexImageANGLE(GL_TEXTURE_2D, 1, GL_RGBA, GL_UNSIGNED_BYTE, actualData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(expectedData, actualData);
}
// Test GetImage with non-defined textures.
TEST_P(GetImageTest, EmptyTexture)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
std::vector<GLColor> expectedData(4, GLColor::white);
std::vector<GLColor> actualData(4, GLColor::white);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, actualData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(expectedData, actualData);
}
struct CompressedFormat
{
const GLenum id;
// Texel/Block size in bytes
const GLsizei size;
// Texel/Block dimensions
const GLsizei w;
const GLsizei h;
};
struct CompressionExtension
{
const char *name;
const std::vector<CompressedFormat> formats;
const bool supports2DArray;
const bool supports3D;
};
// clang-format off
const CompressionExtension kCompressionExtensions[] = {
// BC / DXT
{"GL_EXT_texture_compression_dxt1", {{GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 8, 4, 4},
{GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 8, 4, 4}},
true, false},
{"GL_ANGLE_texture_compression_dxt3", {{GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 16, 4, 4}},
true, false},
{"GL_ANGLE_texture_compression_dxt5", {{GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 16, 4, 4}},
true, false},
{"GL_EXT_texture_compression_s3tc_srgb", {{GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, 8, 4, 4},
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, 8, 4, 4},
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, 16, 4, 4},
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, 16, 4, 4}},
true, false},
{"GL_EXT_texture_compression_rgtc", {{GL_COMPRESSED_RED_RGTC1_EXT, 8, 4, 4},
{GL_COMPRESSED_SIGNED_RED_RGTC1_EXT, 8, 4, 4},
{GL_COMPRESSED_RED_GREEN_RGTC2_EXT, 16, 4, 4},
{GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT, 16, 4, 4}},
true, false},
{"GL_EXT_texture_compression_bptc", {{GL_COMPRESSED_RGBA_BPTC_UNORM_EXT, 16, 4, 4},
{GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT, 16, 4, 4},
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT, 16, 4, 4},
{GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT, 16, 4, 4}},
true, true},
// ETC
{"GL_OES_compressed_ETC1_RGB8_texture", {{GL_ETC1_RGB8_OES, 8, 4, 4}},
false, false},
{"GL_OES_compressed_EAC_R11_unsigned_texture", {{GL_COMPRESSED_R11_EAC, 8, 4, 4}},
true, false},
{"GL_OES_compressed_EAC_R11_signed_texture", {{GL_COMPRESSED_SIGNED_R11_EAC, 8, 4, 4}},
true, false},
{"GL_OES_compressed_EAC_RG11_unsigned_texture", {{GL_COMPRESSED_RG11_EAC, 16, 4, 4}},
true, false},
{"GL_OES_compressed_EAC_RG11_signed_texture", {{GL_COMPRESSED_SIGNED_RG11_EAC, 16, 4, 4}},
true, false},
{"GL_OES_compressed_ETC2_RGB8_texture", {{GL_COMPRESSED_RGB8_ETC2, 8, 4, 4}},
true, false},
{"GL_OES_compressed_ETC2_sRGB8_texture", {{GL_COMPRESSED_SRGB8_ETC2, 8, 4, 4}},
true, false},
{"GL_OES_compressed_ETC2_punchthroughA_RGBA8_texture", {{GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, 8, 4, 4}},
true, false},
{"GL_OES_compressed_ETC2_punchthroughA_sRGB8_alpha_texture", {{GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, 8, 4, 4}},
true, false},
{"GL_OES_compressed_ETC2_RGBA8_texture", {{GL_COMPRESSED_RGBA8_ETC2_EAC, 16, 4, 4}},
true, false},
{"GL_OES_compressed_ETC2_sRGB8_alpha8_texture", {{GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, 16, 4, 4}},
true, false},
// ASTC
{"GL_KHR_texture_compression_astc_ldr", {{GL_COMPRESSED_RGBA_ASTC_4x4_KHR, 16, 4, 4},
{GL_COMPRESSED_RGBA_ASTC_5x4_KHR, 16, 5, 4},
{GL_COMPRESSED_RGBA_ASTC_5x5_KHR, 16, 5, 5},
{GL_COMPRESSED_RGBA_ASTC_6x5_KHR, 16, 6, 5},
{GL_COMPRESSED_RGBA_ASTC_6x6_KHR, 16, 6, 6},
{GL_COMPRESSED_RGBA_ASTC_8x5_KHR, 16, 8, 5},
{GL_COMPRESSED_RGBA_ASTC_8x6_KHR, 16, 8, 6},
{GL_COMPRESSED_RGBA_ASTC_8x8_KHR, 16, 8, 8},
{GL_COMPRESSED_RGBA_ASTC_10x5_KHR, 16, 10, 5},
{GL_COMPRESSED_RGBA_ASTC_10x6_KHR, 16, 10, 6},
{GL_COMPRESSED_RGBA_ASTC_10x8_KHR, 16, 10, 8},
{GL_COMPRESSED_RGBA_ASTC_10x10_KHR, 16, 10, 10},
{GL_COMPRESSED_RGBA_ASTC_12x10_KHR, 16, 12, 10},
{GL_COMPRESSED_RGBA_ASTC_12x12_KHR, 16, 12, 12},
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, 16, 4, 4},
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, 16, 5, 4},
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, 16, 5, 5},
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, 16, 6, 5},
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, 16, 6, 6},
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, 16, 8, 5},
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, 16, 8, 6},
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, 16, 8, 8},
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, 16, 10, 5},
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, 16, 10, 6},
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, 16, 10, 8},
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, 16, 10, 10},
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, 16, 12, 10},
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, 16, 12, 12}},
true, true},
};
// clang-format on
// Basic GetCompressedTexImage.
TEST_P(GetImageTest, CompressedTexImage)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_compressed_ETC1_RGB8_texture"));
constexpr GLsizei kRes = 4;
constexpr GLsizei kImageSize = 8;
// This arbitrary 'compressed' data just has to be read back exactly as specified below.
constexpr std::array<uint8_t, kImageSize> kExpectedData = {1, 2, 3, 4, 5, 6, 7, 8};
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_ETC1_RGB8_OES, kRes, kRes, 0, kImageSize,
kExpectedData.data());
if (IsFormatEmulated(GL_TEXTURE_2D))
{
INFO()
<< "Skipping emulated format GL_ETC1_RGB8_OES from GL_OES_compressed_ETC1_RGB8_texture";
return;
}
std::array<uint8_t, kImageSize> actualData = {};
glGetCompressedTexImageANGLE(GL_TEXTURE_2D, 0, actualData.data());
ASSERT_GL_NO_ERROR();
EXPECT_EQ(kExpectedData, actualData);
}
// Test validation for the compressed extension function.
TEST_P(GetImageTest, CompressedTexImageNegativeAPI)
{
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
// Verify the extension is enabled.
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_compressed_ETC2_RGB8_texture"));
constexpr GLsizei kRes = 4;
constexpr GLsizei kImageSize = 8;
// This arbitrary 'compressed' data just has to be read back exactly as specified below.
constexpr std::array<uint8_t, kImageSize> kExpectedData = {1, 2, 3, 4, 5, 6, 7, 8};
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB8_ETC2, kRes, kRes, 0, kImageSize,
kExpectedData.data());
std::array<uint8_t, kImageSize> actualData = {};
glGetCompressedTexImageANGLE(GL_TEXTURE_2D, 0, actualData.data());
// Verify GetTexImage works with correct parameters or fails if format is emulated.
if (IsFormatEmulated(GL_TEXTURE_2D))
{
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
else
{
EXPECT_GL_NO_ERROR();
}
// Test invalid texture target.
glGetCompressedTexImageANGLE(GL_TEXTURE_CUBE_MAP, 0, actualData.data());
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Test invalid texture level.
glGetCompressedTexImageANGLE(GL_TEXTURE_2D, -1, actualData.data());
EXPECT_GL_ERROR(GL_INVALID_VALUE);
glGetCompressedTexImageANGLE(GL_TEXTURE_2D, 2000, actualData.data());
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Tests GetTexImage on a compressed texture.
if (!IsFormatEmulated(GL_TEXTURE_2D))
{
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB8_ETC2, GL_UNSIGNED_BYTE,
actualData.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
}
using TestFormatFunction =
std::function<void(const CompressionExtension &, const CompressedFormat &)>;
void TestAllCompressedFormats(TestFormatFunction fun)
{
for (CompressionExtension ext : kCompressionExtensions)
{
if (!IsGLExtensionEnabled(ext.name))
{
continue;
}
for (CompressedFormat format : ext.formats)
{
fun(ext, format);
}
}
}
// Test GetCompressedTexImage with all formats and
// and multiple resolution of the format's block size.
TEST_P(GetImageTest, CompressedTexImageAll)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
auto func = [](const CompressionExtension &ext, const CompressedFormat &format) {
// Test with multiples of block size
constexpr std::array<GLsizei, 2> multipliers = {1, 2};
for (GLsizei multiplier : multipliers)
{
const GLsizei kImageSize = format.size * multiplier * multiplier;
std::vector<uint8_t> expectedData;
for (uint8_t i = 1; i < kImageSize + 1; i++)
{
expectedData.push_back(i);
}
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glCompressedTexImage2D(GL_TEXTURE_2D, 0, format.id, format.w * multiplier,
format.h * multiplier, 0, kImageSize, expectedData.data());
if (IsFormatEmulated(GL_TEXTURE_2D))
{
INFO() << "Skipping emulated format 0x" << std::hex << format.id << " from "
<< ext.name;
return;
}
std::vector<uint8_t> actualData(kImageSize);
glGetCompressedTexImageANGLE(GL_TEXTURE_2D, 0, actualData.data());
ASSERT_GL_NO_ERROR();
EXPECT_EQ(expectedData, actualData);
}
};
TestAllCompressedFormats(func);
}
// Test a resolution that is not a multiple of the block size with an ETC2 4x4 format.
TEST_P(GetImageTest, CompressedTexImageNotBlockMultiple)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_compressed_ETC2_RGB8_texture"));
constexpr GLsizei kRes = 21;
constexpr GLsizei kImageSize = 288;
// This arbitrary 'compressed' data just has to be read back exactly as specified below.
std::vector<uint8_t> expectedData;
for (uint16_t j = 0; j < kImageSize; j++)
{
expectedData.push_back(j % std::numeric_limits<uint8_t>::max());
}
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB8_ETC2, kRes, kRes, 0, kImageSize,
expectedData.data());
if (IsFormatEmulated(GL_TEXTURE_2D))
{
INFO() << "Skipping emulated format GL_COMPRESSED_RGB8_ETC2 from "
"GL_OES_compressed_ETC2_RGB8_texture";
return;
}
std::vector<uint8_t> actualData(kImageSize);
glGetCompressedTexImageANGLE(GL_TEXTURE_2D, 0, actualData.data());
ASSERT_GL_NO_ERROR();
EXPECT_EQ(expectedData, actualData);
}
void TestCompressedTexImage3D(GLenum target, uint32_t numLayers)
{
auto func = [target, numLayers](const CompressionExtension &ext,
const CompressedFormat &format) {
// Skip extensions lacking 2D array and 3D support
if ((target == GL_TEXTURE_2D_ARRAY && !ext.supports2DArray) ||
(target == GL_TEXTURE_3D && !ext.supports3D))
{
return;
}
// GL_TEXTURE_3D with ASTC requires additional extension
if (target == GL_TEXTURE_3D &&
strcmp(ext.name, "GL_KHR_texture_compression_astc_ldr") == 0 &&
!IsGLExtensionEnabled("GL_KHR_texture_compression_astc_sliced_3d") &&
!IsGLExtensionEnabled("GL_KHR_texture_compression_astc_hdr"))
{
return;
}
const size_t size = format.size * numLayers;
GLTexture texture;
glBindTexture(target, texture);
std::vector<uint8_t> expectedData;
for (uint8_t i = 0; i < size; i++)
{
expectedData.push_back(i);
}
glCompressedTexImage3D(target, 0, format.id, format.w, format.h, numLayers, 0, size,
expectedData.data());
if (IsFormatEmulated(target))
{
INFO() << "Skipping emulated format 0x" << std::hex << format.id << " from "
<< ext.name;
return;
}
std::vector<uint8_t> actualData(size);
glGetCompressedTexImageANGLE(target, 0, actualData.data());
ASSERT_GL_NO_ERROR();
EXPECT_EQ(expectedData, actualData);
};
TestAllCompressedFormats(func);
}
// Tests GetCompressedTexImage with 2D array textures.
TEST_P(GetImageTestES3, CompressedTexImage2DArray)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
TestCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, 8);
}
// Tests GetCompressedTexImage with 3D textures.
TEST_P(GetImageTest, CompressedTexImage3D)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
TestCompressedTexImage3D(GL_TEXTURE_3D, 8);
}
// Simple cube map test for GetCompressedTexImage
TEST_P(GetImageTest, CompressedTexImageCubeMap)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
auto func = [](const CompressionExtension &ext, const CompressedFormat &format) {
GLTexture texture;
glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
std::vector<std::vector<uint8_t>> expectedData;
for (uint32_t i = 0; i < kCubeFaces.size(); i++)
{
std::vector<uint8_t> face;
for (uint8_t j = 0; j < format.size; j++)
{
face.push_back(static_cast<uint8_t>((i * format.size + j) %
std::numeric_limits<uint8_t>::max()));
}
expectedData.push_back(face);
}
for (size_t faceIndex = 0; faceIndex < kCubeFaces.size(); ++faceIndex)
{
glCompressedTexImage2D(kCubeFaces[faceIndex], 0, format.id, 4, 4, 0, format.size,
expectedData[faceIndex].data());
}
if (IsFormatEmulated(GL_TEXTURE_CUBE_MAP))
{
INFO() << "Skipping emulated format 0x" << std::hex << format.id << " from "
<< ext.name;
return;
}
// Verify GetImage.
for (size_t faceIndex = 0; faceIndex < kCubeFaces.size(); ++faceIndex)
{
std::vector<uint8_t> actualData(format.size);
glGetCompressedTexImageANGLE(kCubeFaces[faceIndex], 0, actualData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(expectedData[faceIndex], actualData);
}
};
TestAllCompressedFormats(func);
}
// Tests GetCompressedTexImage with cube map array textures.
TEST_P(GetImageTestES32, CompressedTexImageCubeMapArray)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
auto func = [](const CompressionExtension &ext, const CompressedFormat &format) {
std::vector<uint8_t> expectedData;
for (uint32_t i = 0; i < format.size * kCubeFaces.size(); i++)
{
expectedData.push_back(static_cast<uint8_t>(i % std::numeric_limits<uint8_t>::max()));
}
GLTexture tex;
glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, tex);
glCompressedTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, format.id, 4, 4, kCubeFaces.size(), 0,
expectedData.size() * sizeof(uint8_t), expectedData.data());
ASSERT_GL_NO_ERROR();
if (IsFormatEmulated(GL_TEXTURE_CUBE_MAP_ARRAY))
{
INFO() << "Skipping emulated format 0x" << std::hex << format.id << " from "
<< ext.name;
return;
}
// Verify GetImage.
std::vector<uint8_t> actualData(format.size * kCubeFaces.size());
glGetCompressedTexImageANGLE(GL_TEXTURE_CUBE_MAP_ARRAY, 0, actualData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(expectedData, actualData);
};
TestAllCompressedFormats(func);
}
// Tests GetCompressedTexImage with multiple mip levels.
TEST_P(GetImageTest, CompressedTexImageMultiLevel)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
auto func = [](const CompressionExtension &ext, const CompressedFormat &format) {
constexpr uint8_t kNumMipLevels = 8;
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
std::vector<std::vector<uint8_t>> expectedData;
for (uint32_t mipLevel = 0; mipLevel < kNumMipLevels; mipLevel++)
{
uint32_t multiplier = static_cast<uint32_t>(pow(2, (kNumMipLevels - mipLevel) - 1));
size_t levelSize = format.size * multiplier * multiplier;
std::vector<uint8_t> levelData;
for (size_t j = 0; j < levelSize; j++)
{
levelData.push_back(static_cast<uint8_t>(j % std::numeric_limits<uint8_t>::max()));
}
expectedData.push_back(levelData);
glCompressedTexImage2D(GL_TEXTURE_2D, mipLevel, format.id, format.w * multiplier,
format.h * multiplier, 0, levelSize, levelData.data());
}
ASSERT_GL_NO_ERROR();
if (IsFormatEmulated(GL_TEXTURE_2D))
{
INFO() << "Skipping emulated format 0x" << std::hex << format.id << " from "
<< ext.name;
return;
}
for (uint32_t mipLevel = 0; mipLevel < kNumMipLevels; mipLevel++)
{
uint32_t multiplier = static_cast<uint32_t>(pow(2, (kNumMipLevels - mipLevel) - 1));
size_t levelSize = format.size * multiplier * multiplier;
std::vector<uint8_t> actualData(levelSize);
glGetCompressedTexImageANGLE(GL_TEXTURE_2D, mipLevel, actualData.data());
ASSERT_GL_NO_ERROR();
EXPECT_EQ(expectedData[mipLevel], actualData);
}
};
TestAllCompressedFormats(func);
}
struct PalettedFormat
{
GLenum internalFormat;
uint32_t indexBits;
uint32_t redBlueBits;
uint32_t greenBits;
uint32_t alphaBits;
};
const PalettedFormat kPalettedFormats[] = {
{GL_PALETTE4_RGB8_OES, 4, 8, 8, 0}, {GL_PALETTE4_RGBA8_OES, 4, 8, 8, 8},
{GL_PALETTE4_R5_G6_B5_OES, 4, 5, 6, 0}, {GL_PALETTE4_RGBA4_OES, 4, 4, 4, 4},
{GL_PALETTE4_RGB5_A1_OES, 4, 5, 5, 1}, {GL_PALETTE8_RGB8_OES, 8, 8, 8, 0},
{GL_PALETTE8_RGBA8_OES, 8, 8, 8, 8}, {GL_PALETTE8_R5_G6_B5_OES, 8, 5, 6, 0},
{GL_PALETTE8_RGBA4_OES, 8, 4, 4, 4}, {GL_PALETTE8_RGB5_A1_OES, 8, 5, 5, 1},
};
// Tests that glGetTexImageANGLE captures paletted format images in
// R8G8B8A8_UNORM and StoreRGBA8ToPalettedImpl compresses such images to the
// same paletted image.
TEST_P(GetImageTestES1, PalettedTexImage)
{
ANGLE_SKIP_TEST_IF(!IsVulkan());
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
struct Vertex
{
GLfloat position[3];
GLfloat uv[2];
};
std::vector<Vertex> vertices = {
{{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f}},
{{-1.0f, 1.0f, 0.0f}, {0.0f, 1.0f}},
{{1.0f, -1.0f, 0.0f}, {1.0f, 0.0f}},
{{1.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
};
glVertexPointer(3, GL_FLOAT, sizeof vertices[0], &vertices[0].position);
glTexCoordPointer(2, GL_FLOAT, sizeof vertices[0], &vertices[0].uv);
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Locations in the framebuffer where we will take the samples to compare
const int width = 16;
const int height = width;
for (PalettedFormat format : kPalettedFormats)
{
size_t paletteSizeInBytes =
(1 << format.indexBits) *
((format.redBlueBits + format.greenBits + format.redBlueBits + format.alphaBits) / 8);
size_t imageSize = paletteSizeInBytes + width * height * format.indexBits / 8;
std::vector<uint8_t> testImage(imageSize);
FillVectorWithRandomUBytes(&testImage);
// Upload a random paletted image
glCompressedTexImage2D(GL_TEXTURE_2D, 0, format.internalFormat, width, height, 0,
testImage.size(), testImage.data());
EXPECT_GL_NO_ERROR();
// Render a quad using this texture and capture the entire framebuffer contents
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices.size());
EXPECT_GL_NO_ERROR();
std::vector<GLColor> framebuffer(width * height);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer.data());
// Read it back as R8G8B8A8 UNORM
std::vector<uint8_t> readback(width * height * 4);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, readback.data());
EXPECT_GL_NO_ERROR();
// Recompress. This should produce the same, up to permutation of used
// palette entries, image, as the one we uploaded. Upload it again.
std::vector<uint8_t> recompressedImage(imageSize);
angle::StoreRGBA8ToPalettedImpl(width, height, 1, format.indexBits, format.redBlueBits,
format.greenBits, format.alphaBits, readback.data(),
width * 4, width * height * 4, recompressedImage.data(),
width * format.indexBits / 8,
width * height * format.indexBits / 8);
glCompressedTexImage2D(GL_TEXTURE_2D, 0, format.internalFormat, width, height, 0,
recompressedImage.size(), recompressedImage.data());
EXPECT_GL_NO_ERROR();
// Read the framebuffer again
std::vector<GLColor> framebuffer2(width * height);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer2.data());
EXPECT_EQ(framebuffer, framebuffer2);
}
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GetImageTest);
ANGLE_INSTANTIATE_TEST(GetImageTest,
ES2_VULKAN(),
ES3_VULKAN(),
ES2_VULKAN_SWIFTSHADER(),
ES3_VULKAN_SWIFTSHADER());
ANGLE_INSTANTIATE_TEST_ES1(GetImageTestES1);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GetImageTestES3);
ANGLE_INSTANTIATE_TEST(GetImageTestES3, ES3_VULKAN(), ES3_VULKAN_SWIFTSHADER());
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GetImageTestES31);
ANGLE_INSTANTIATE_TEST(GetImageTestES31, ES31_VULKAN(), ES31_VULKAN_SWIFTSHADER());
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GetImageTestES32);
ANGLE_INSTANTIATE_TEST(GetImageTestES32, ES32_VULKAN(), ES32_VULKAN_SWIFTSHADER());
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GetImageTestNoExtensions);
ANGLE_INSTANTIATE_TEST(GetImageTestNoExtensions,
ES2_VULKAN(),
ES3_VULKAN(),
ES2_VULKAN_SWIFTSHADER(),
ES3_VULKAN_SWIFTSHADER());
} // namespace