Commit 451b2b7f by Geoff Lang Committed by Commit Bot

Correctly generate mipmaps for sRGB textures.

sRGB textures need to linearize their data before averaging, this worked correctly on the GPU mipmap generation functions but not the CPU ones. BUG=angleproject:2019 TEST=conformance/extensions/ext-sRGB Change-Id: I554dac89b12acf5ebf5b7cab6f6faf0bce5168a6 Reviewed-on: https://chromium-review.googlesource.com/506241 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 44653302
...@@ -791,6 +791,42 @@ inline void unpackHalf2x16(uint32_t u, float *f1, float *f2) ...@@ -791,6 +791,42 @@ inline void unpackHalf2x16(uint32_t u, float *f1, float *f2)
*f2 = float16ToFloat32(mostSignificantBits); *f2 = float16ToFloat32(mostSignificantBits);
} }
inline uint8_t sRGBToLinear(uint8_t srgbValue)
{
float value = srgbValue / 255.0f;
if (value <= 0.04045f)
{
value = value / 12.92f;
}
else
{
value = std::pow((value + 0.055f) / 1.055f, 2.4f);
}
return static_cast<uint8_t>(clamp(value * 255.0f + 0.5f, 0.0f, 255.0f));
}
inline uint8_t linearToSRGB(uint8_t linearValue)
{
float value = linearValue / 255.0f;
if (value <= 0.0f)
{
value = 0.0f;
}
else if (value < 0.0031308f)
{
value = value * 12.92f;
}
else if (value < 1.0f)
{
value = std::pow(value, 0.41666f) * 1.055f - 0.055f;
}
else
{
value = 1.0f;
}
return static_cast<uint8_t>(clamp(value * 255.0f + 0.5f, 0.0f, 255.0f));
}
// Reverse the order of the bits. // Reverse the order of the bits.
inline uint32_t BitfieldReverse(uint32_t value) inline uint32_t BitfieldReverse(uint32_t value)
{ {
......
...@@ -358,6 +358,32 @@ void R8G8B8A8::average(R8G8B8A8 *dst, const R8G8B8A8 *src1, const R8G8B8A8 *src2 ...@@ -358,6 +358,32 @@ void R8G8B8A8::average(R8G8B8A8 *dst, const R8G8B8A8 *src1, const R8G8B8A8 *src2
(*(uint32_t *)src1 & *(uint32_t *)src2); (*(uint32_t *)src1 & *(uint32_t *)src2);
} }
void R8G8B8A8SRGB::readColor(gl::ColorF *dst, const R8G8B8A8SRGB *src)
{
dst->red = gl::normalizedToFloat(src->R);
dst->green = gl::normalizedToFloat(src->G);
dst->blue = gl::normalizedToFloat(src->B);
dst->alpha = gl::normalizedToFloat(src->A);
}
void R8G8B8A8SRGB::average(R8G8B8A8SRGB *dst, const R8G8B8A8SRGB *src1, const R8G8B8A8SRGB *src2)
{
dst->R =
gl::linearToSRGB(static_cast<uint8_t>((static_cast<uint16_t>(gl::sRGBToLinear(src1->R)) +
static_cast<uint16_t>(gl::sRGBToLinear(src2->R))) >>
1));
dst->G =
gl::linearToSRGB(static_cast<uint8_t>((static_cast<uint16_t>(gl::sRGBToLinear(src1->G)) +
static_cast<uint16_t>(gl::sRGBToLinear(src2->G))) >>
1));
dst->B =
gl::linearToSRGB(static_cast<uint8_t>((static_cast<uint16_t>(gl::sRGBToLinear(src1->B)) +
static_cast<uint16_t>(gl::sRGBToLinear(src2->B))) >>
1));
dst->A = static_cast<uint8_t>(
(static_cast<uint16_t>(src1->A) + static_cast<uint16_t>(src2->A)) >> 1);
}
void B8G8R8A8::readColor(gl::ColorUI *dst, const B8G8R8A8 *src) void B8G8R8A8::readColor(gl::ColorUI *dst, const B8G8R8A8 *src)
{ {
dst->red = src->R; dst->red = src->R;
......
...@@ -157,6 +157,17 @@ struct R8G8B8A8 ...@@ -157,6 +157,17 @@ struct R8G8B8A8
static void average(R8G8B8A8 *dst, const R8G8B8A8 *src1, const R8G8B8A8 *src2); static void average(R8G8B8A8 *dst, const R8G8B8A8 *src1, const R8G8B8A8 *src2);
}; };
struct R8G8B8A8SRGB
{
uint8_t R;
uint8_t G;
uint8_t B;
uint8_t A;
static void readColor(gl::ColorF *dst, const R8G8B8A8SRGB *src);
static void average(R8G8B8A8SRGB *dst, const R8G8B8A8SRGB *src1, const R8G8B8A8SRGB *src2);
};
struct B8G8R8A8 struct B8G8R8A8
{ {
uint8_t B; uint8_t B;
......
...@@ -133,7 +133,7 @@ constexpr Format g_formatInfoTable[] = { ...@@ -133,7 +133,7 @@ constexpr Format g_formatInfoTable[] = {
{ Format::ID::R8G8B8A8_SNORM, GL_RGBA8_SNORM, GL_RGBA8_SNORM, GenerateMip<R8G8B8A8S>, NoCopyFunctions, ReadColor<R8G8B8A8S, GLfloat>, GL_SIGNED_NORMALIZED, 8, 8, 8, 8, 0, 0 }, { Format::ID::R8G8B8A8_SNORM, GL_RGBA8_SNORM, GL_RGBA8_SNORM, GenerateMip<R8G8B8A8S>, NoCopyFunctions, ReadColor<R8G8B8A8S, GLfloat>, GL_SIGNED_NORMALIZED, 8, 8, 8, 8, 0, 0 },
{ Format::ID::R8G8B8A8_UINT, GL_RGBA8UI, GL_RGBA8UI, GenerateMip<R8G8B8A8>, NoCopyFunctions, ReadColor<R8G8B8A8, GLuint>, GL_UNSIGNED_INT, 8, 8, 8, 8, 0, 0 }, { Format::ID::R8G8B8A8_UINT, GL_RGBA8UI, GL_RGBA8UI, GenerateMip<R8G8B8A8>, NoCopyFunctions, ReadColor<R8G8B8A8, GLuint>, GL_UNSIGNED_INT, 8, 8, 8, 8, 0, 0 },
{ Format::ID::R8G8B8A8_UNORM, GL_RGBA8, GL_RGBA8, GenerateMip<R8G8B8A8>, NoCopyFunctions, ReadColor<R8G8B8A8, GLfloat>, GL_UNSIGNED_NORMALIZED, 8, 8, 8, 8, 0, 0 }, { Format::ID::R8G8B8A8_UNORM, GL_RGBA8, GL_RGBA8, GenerateMip<R8G8B8A8>, NoCopyFunctions, ReadColor<R8G8B8A8, GLfloat>, GL_UNSIGNED_NORMALIZED, 8, 8, 8, 8, 0, 0 },
{ Format::ID::R8G8B8A8_UNORM_SRGB, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GenerateMip<R8G8B8A8>, NoCopyFunctions, ReadColor<R8G8B8A8, GLfloat>, GL_UNSIGNED_NORMALIZED, 8, 8, 8, 8, 0, 0 }, { Format::ID::R8G8B8A8_UNORM_SRGB, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GenerateMip<R8G8B8A8SRGB>, NoCopyFunctions, ReadColor<R8G8B8A8SRGB, GLfloat>, GL_UNSIGNED_NORMALIZED, 8, 8, 8, 8, 0, 0 },
{ Format::ID::R8G8B8_SINT, GL_RGB8I, GL_RGB8I, GenerateMip<R8G8B8S>, NoCopyFunctions, ReadColor<R8G8B8S, GLint>, GL_INT, 8, 8, 8, 0, 0, 0 }, { Format::ID::R8G8B8_SINT, GL_RGB8I, GL_RGB8I, GenerateMip<R8G8B8S>, NoCopyFunctions, ReadColor<R8G8B8S, GLint>, GL_INT, 8, 8, 8, 0, 0, 0 },
{ Format::ID::R8G8B8_SNORM, GL_RGB8_SNORM, GL_RGB8_SNORM, GenerateMip<R8G8B8S>, NoCopyFunctions, ReadColor<R8G8B8S, GLfloat>, GL_SIGNED_NORMALIZED, 8, 8, 8, 0, 0, 0 }, { Format::ID::R8G8B8_SNORM, GL_RGB8_SNORM, GL_RGB8_SNORM, GenerateMip<R8G8B8S>, NoCopyFunctions, ReadColor<R8G8B8S, GLfloat>, GL_SIGNED_NORMALIZED, 8, 8, 8, 0, 0, 0 },
{ Format::ID::R8G8B8_UINT, GL_RGB8UI, GL_RGB8UI, GenerateMip<R8G8B8>, NoCopyFunctions, ReadColor<R8G8B8, GLuint>, GL_UNSIGNED_INT, 8, 8, 8, 0, 0, 0 }, { Format::ID::R8G8B8_UINT, GL_RGB8UI, GL_RGB8UI, GenerateMip<R8G8B8>, NoCopyFunctions, ReadColor<R8G8B8, GLuint>, GL_UNSIGNED_INT, 8, 8, 8, 0, 0, 0 },
......
...@@ -17,5 +17,8 @@ ...@@ -17,5 +17,8 @@
"B4G4R4A4_UNORM": { "B4G4R4A4_UNORM": {
"fboImplementationInternalFormat": "GL_RGBA4", "fboImplementationInternalFormat": "GL_RGBA4",
"channelStruct": "A4R4G4B4" "channelStruct": "A4R4G4B4"
},
"R8G8B8A8_UNORM_SRGB": {
"channelStruct": "R8G8B8A8SRGB"
} }
} }
...@@ -262,6 +262,74 @@ TEST_P(SRGBTextureTest, SRGBDecodeSamplerParameter) ...@@ -262,6 +262,74 @@ TEST_P(SRGBTextureTest, SRGBDecodeSamplerParameter)
EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0); EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
} }
// Test that mipmaps are generated correctly for sRGB textures
TEST_P(SRGBTextureTest, GenerateMipmaps)
{
if (getClientMajorVersion() < 3)
{
std::cout << "Test skipped because ES3 is not available." << std::endl;
return;
}
if (IsOpenGL() && (IsIntel() || IsAMD()))
{
std::cout << "Test skipped on Intel and AMD OpenGL drivers." << std::endl;
return;
}
auto createAndReadBackTexture = [this](GLenum internalFormat, const GLColor &color) {
constexpr GLsizei width = 128;
constexpr GLsizei height = 128;
std::array<GLColor, width * height> buf;
std::fill(buf.begin(), buf.end(), color);
// Set up-left region of the texture as red color.
// In order to make sure bi-linear interpolation operates on different colors, red region
// is 1 pixel smaller than a quarter of the full texture on each side.
constexpr GLsizei redWidth = width / 2 - 1;
constexpr GLsizei redHeight = height / 2 - 1;
std::array<GLColor, redWidth * redHeight> redBuf;
std::fill(redBuf.begin(), redBuf.end(), GLColor::red);
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
buf.data());
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, redWidth, redHeight, GL_RGBA, GL_UNSIGNED_BYTE,
redBuf.data());
glGenerateMipmap(GL_TEXTURE_2D);
constexpr GLsizei drawWidth = 32;
constexpr GLsizei drawHeight = 32;
glViewport(0, 0, drawWidth, drawHeight);
drawQuad(mProgram, "position", 0.5f);
std::array<GLColor, drawWidth * drawHeight> result;
glReadPixels(0, 0, drawWidth, drawHeight, GL_RGBA, GL_UNSIGNED_BYTE, result.data());
EXPECT_GL_NO_ERROR();
return result;
};
GLColor srgbaColor(0, 63, 127, 255);
auto srgbaReadback = createAndReadBackTexture(GL_SRGB8_ALPHA8, srgbaColor);
GLColor linearColor(0, 13, 54, 255);
auto rgbaReadback = createAndReadBackTexture(GL_RGBA8, linearColor);
ASSERT_EQ(srgbaReadback.size(), rgbaReadback.size());
for (size_t i = 0; i < srgbaReadback.size(); i++)
{
constexpr double tolerence = 7.0;
EXPECT_COLOR_NEAR(srgbaReadback[i], rgbaReadback[i], tolerence);
}
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(SRGBTextureTest, ANGLE_INSTANTIATE_TEST(SRGBTextureTest,
ES2_D3D9(), ES2_D3D9(),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment