GL: Work around drivers that generate mipmaps in linear color space

Mac drivers generate mipmaps in linear color space. To work around
this, copy the sRGB texture to a linear texture, generate mipmaps
and then copy back.

TEST=conformance2/textures/misc/tex-srgb-mipmap.html

BUG=angleproject:4646

Change-Id: I8675d0ab004bcd2985f685d64cbb84deff5f1c86
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2211083
Reviewed-by: Jonah Ryan-Davis <jonahr@google.com>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Geoff Lang <geofflang@chromium.org>
This commit is contained in:
Geoff Lang 2020-05-20 17:24:49 -04:00 коммит произвёл Commit Bot
Родитель 9fa671d52b
Коммит 6080383848
8 изменённых файлов: 206 добавлений и 20 удалений

Просмотреть файл

@ -441,6 +441,13 @@ struct FeaturesGL : FeatureSetBase
Feature disableTimestampQueries = { Feature disableTimestampQueries = {
"disable_timestamp_queries", FeatureCategory::OpenGLWorkarounds, "disable_timestamp_queries", FeatureCategory::OpenGLWorkarounds,
"Disable GL_EXT_disjoint_timer_query extension", &members, "https://crbug.com/811661"}; "Disable GL_EXT_disjoint_timer_query extension", &members, "https://crbug.com/811661"};
// Some drivers use linear blending when generating mipmaps for sRGB textures. Work around this
// by generating mipmaps in a linear texture and copying back to sRGB.
Feature encodeAndDecodeSRGBForGenerateMipmap = {
"decode_encode_srgb_for_generatemipmap", FeatureCategory::OpenGLWorkarounds,
"Decode and encode before generateMipmap for srgb format textures.", &members,
"http://anglebug.com/4646"};
}; };
inline FeaturesGL::FeaturesGL() = default; inline FeaturesGL::FeaturesGL() = default;

Просмотреть файл

@ -988,6 +988,85 @@ angle::Result BlitGL::clearRenderableTextureAlphaToOne(const gl::Context *contex
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result BlitGL::generateSRGBMipmap(const gl::Context *context,
TextureGL *source,
GLuint baseLevel,
GLuint levelCount,
const gl::Extents &sourceBaseLevelSize)
{
ANGLE_TRY(initializeResources(context));
const gl::TextureType sourceType = gl::TextureType::_2D;
const gl::TextureTarget sourceTarget = gl::TextureTarget::_2D;
ScopedGLState scopedState;
ANGLE_TRY(scopedState.enter(
context, gl::Rectangle(0, 0, sourceBaseLevelSize.width, sourceBaseLevelSize.height)));
scopedState.willUseTextureUnit(context, 0);
mStateManager->activeTexture(0);
// Copy source to a linear intermediate texture.
GLuint linearTexture = mScratchTextures[0];
mStateManager->bindTexture(sourceType, linearTexture);
ANGLE_GL_TRY(context, mFunctions->texImage2D(
ToGLenum(sourceTarget), 0, mSRGBMipmapGenerationFormat.internalFormat,
sourceBaseLevelSize.width, sourceBaseLevelSize.height, 0,
mSRGBMipmapGenerationFormat.format, mSRGBMipmapGenerationFormat.type,
nullptr));
mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
ANGLE_GL_TRY(context,
mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
ToGLenum(sourceTarget), linearTexture, 0));
mStateManager->setFramebufferSRGBEnabled(context, true);
// Use a shader to do the sRGB to linear conversion. glBlitFramebuffer does not always do this
// conversion for us.
BlitProgram *blitProgram = nullptr;
ANGLE_TRY(getBlitProgram(context, sourceType, GL_FLOAT, GL_FLOAT, &blitProgram));
mStateManager->useProgram(blitProgram->program);
ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->sourceTextureLocation, 0));
ANGLE_GL_TRY(context, mFunctions->uniform2f(blitProgram->scaleLocation, 1.0f, 1.0f));
ANGLE_GL_TRY(context, mFunctions->uniform2f(blitProgram->offsetLocation, 0.0f, 0.0f));
ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->multiplyAlphaLocation, 0));
ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->unMultiplyAlphaLocation, 0));
mStateManager->bindTexture(sourceType, source->getTextureID());
ANGLE_TRY(source->setMinFilter(context, GL_NEAREST));
mStateManager->bindVertexArray(mVAO, 0);
ANGLE_GL_TRY(context, mFunctions->drawArrays(GL_TRIANGLES, 0, 3));
// Generate mipmaps on the linear texture
mStateManager->bindTexture(sourceType, linearTexture);
ANGLE_GL_TRY_ALWAYS_CHECK(context, mFunctions->generateMipmap(ToGLenum(sourceTarget)));
ANGLE_GL_TRY(context, mFunctions->texParameteri(ToGLenum(sourceTarget), GL_TEXTURE_MIN_FILTER,
GL_NEAREST));
// Copy back to the source texture from the mips generated in the linear texture
for (GLuint levelIdx = 0; levelIdx < levelCount; levelIdx++)
{
gl::Extents levelSize(std::max(sourceBaseLevelSize.width >> levelIdx, 1),
std::max(sourceBaseLevelSize.height >> levelIdx, 1), 1);
ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, ToGLenum(sourceTarget),
source->getTextureID(), baseLevel + levelIdx));
mStateManager->setViewport(gl::Rectangle(0, 0, levelSize.width, levelSize.height));
ANGLE_GL_TRY(context, mFunctions->texParameteri(ToGLenum(sourceTarget),
GL_TEXTURE_BASE_LEVEL, levelIdx));
ANGLE_GL_TRY(context, mFunctions->drawArrays(GL_TRIANGLES, 0, 3));
}
ANGLE_TRY(orphanScratchTextures(context));
ANGLE_TRY(scopedState.exit(context));
return angle::Result::Continue;
}
angle::Result BlitGL::initializeResources(const gl::Context *context) angle::Result BlitGL::initializeResources(const gl::Context *context)
{ {
for (size_t i = 0; i < ArraySize(mScratchTextures); i++) for (size_t i = 0; i < ArraySize(mScratchTextures); i++)
@ -1038,6 +1117,27 @@ angle::Result BlitGL::initializeResources(const gl::Context *context)
} }
} }
constexpr GLenum potentialSRGBMipmapGenerationFormats[] = {
GL_RGBA16, GL_RGBA16F, GL_RGBA32F,
GL_RGBA8, // RGBA8 can have precision loss when generating mipmaps of a sRGBA8 texture
};
for (GLenum internalFormat : potentialSRGBMipmapGenerationFormats)
{
if (nativegl::SupportsNativeRendering(mFunctions, gl::TextureType::_2D, internalFormat))
{
const gl::InternalFormat &internalFormatInfo =
gl::GetSizedInternalFormatInfo(internalFormat);
// Pass the 'format' instead of 'internalFormat' to make sure we use unsized formats
// when available to increase support.
mSRGBMipmapGenerationFormat =
nativegl::GetTexImageFormat(mFunctions, mFeatures, internalFormatInfo.format,
internalFormatInfo.format, internalFormatInfo.type);
break;
}
}
ASSERT(mSRGBMipmapGenerationFormat.internalFormat != GL_NONE);
return angle::Result::Continue; return angle::Result::Continue;
} }
@ -1049,12 +1149,34 @@ angle::Result BlitGL::orphanScratchTextures(const gl::Context *context)
gl::PixelUnpackState unpack; gl::PixelUnpackState unpack;
mStateManager->setPixelUnpackState(unpack); mStateManager->setPixelUnpackState(unpack);
mStateManager->setPixelUnpackBuffer(nullptr); mStateManager->setPixelUnpackBuffer(nullptr);
GLint swizzle[4] = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; if (mFunctions->isAtLeastGL(gl::Version(3, 3)))
{
constexpr GLint swizzle[4] = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
ANGLE_GL_TRY(context, mFunctions->texParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA,
swizzle));
}
else if (mFunctions->isAtLeastGLES(gl::Version(3, 0)))
{
ANGLE_GL_TRY(context, ANGLE_GL_TRY(context,
mFunctions->texParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle)); mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED));
ANGLE_GL_TRY(context,
mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN));
ANGLE_GL_TRY(context,
mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_BLUE));
ANGLE_GL_TRY(context,
mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ALPHA));
}
ANGLE_GL_TRY(context, mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0));
ANGLE_GL_TRY(context, mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1000));
ANGLE_GL_TRY(context, mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST_MIPMAP_LINEAR));
ANGLE_GL_TRY(context,
mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
ANGLE_GL_TRY(context, mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA, ANGLE_GL_TRY(context, mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr)); GL_UNSIGNED_BYTE, nullptr));
} }
return angle::Result::Continue; return angle::Result::Continue;
} }

Просмотреть файл

@ -13,6 +13,7 @@
#include "common/angleutils.h" #include "common/angleutils.h"
#include "libANGLE/Error.h" #include "libANGLE/Error.h"
#include "libANGLE/angletypes.h" #include "libANGLE/angletypes.h"
#include "libANGLE/renderer/gl/formatutilsgl.h"
#include <map> #include <map>
@ -136,6 +137,12 @@ class BlitGL : angle::NonCopyable
gl::TextureTarget target, gl::TextureTarget target,
size_t level); size_t level);
angle::Result generateSRGBMipmap(const gl::Context *context,
TextureGL *source,
GLuint baseLevel,
GLuint levelCount,
const gl::Extents &sourceBaseLevelSize);
angle::Result initializeResources(const gl::Context *context); angle::Result initializeResources(const gl::Context *context);
private: private:
@ -173,6 +180,8 @@ class BlitGL : angle::NonCopyable
GLuint mVAO; GLuint mVAO;
GLuint mVertexBuffer; GLuint mVertexBuffer;
nativegl::TexImageFormat mSRGBMipmapGenerationFormat;
}; };
} // namespace rx } // namespace rx

Просмотреть файл

@ -133,6 +133,7 @@ StateManagerGL::StateManagerGL(const FunctionsGL *functions,
mClearColor(0.0f, 0.0f, 0.0f, 0.0f), mClearColor(0.0f, 0.0f, 0.0f, 0.0f),
mClearDepth(1.0f), mClearDepth(1.0f),
mClearStencil(0), mClearStencil(0),
mFramebufferSRGBAvailable(extensions.sRGBWriteControl),
mFramebufferSRGBEnabled(false), mFramebufferSRGBEnabled(false),
mDitherEnabled(true), mDitherEnabled(true),
mTextureCubemapSeamlessEnabled(false), mTextureCubemapSeamlessEnabled(false),
@ -2071,7 +2072,7 @@ angle::Result StateManagerGL::syncState(const gl::Context *context,
void StateManagerGL::setFramebufferSRGBEnabled(const gl::Context *context, bool enabled) void StateManagerGL::setFramebufferSRGBEnabled(const gl::Context *context, bool enabled)
{ {
if (!context->getExtensions().sRGBWriteControl) if (!mFramebufferSRGBAvailable)
{ {
return; return;
} }

Просмотреть файл

@ -320,7 +320,9 @@ class StateManagerGL final : angle::NonCopyable
float mClearDepth; float mClearDepth;
GLint mClearStencil; GLint mClearStencil;
bool mFramebufferSRGBAvailable;
bool mFramebufferSRGBEnabled; bool mFramebufferSRGBEnabled;
bool mDitherEnabled; bool mDitherEnabled;
bool mTextureCubemapSeamlessEnabled; bool mTextureCubemapSeamlessEnabled;

Просмотреть файл

@ -1242,13 +1242,55 @@ angle::Result TextureGL::generateMipmap(const gl::Context *context)
{ {
const FunctionsGL *functions = GetFunctionsGL(context); const FunctionsGL *functions = GetFunctionsGL(context);
StateManagerGL *stateManager = GetStateManagerGL(context); StateManagerGL *stateManager = GetStateManagerGL(context);
const angle::FeaturesGL &features = GetFeaturesGL(context);
stateManager->bindTexture(getType(), mTextureID);
ANGLE_GL_TRY_ALWAYS_CHECK(context, functions->generateMipmap(ToGLenum(getType())));
const GLuint effectiveBaseLevel = mState.getEffectiveBaseLevel(); const GLuint effectiveBaseLevel = mState.getEffectiveBaseLevel();
const GLuint maxLevel = mState.getMipmapMaxLevel(); const GLuint maxLevel = mState.getMipmapMaxLevel();
const gl::ImageDesc &baseLevelDesc = mState.getBaseLevelDesc();
const gl::InternalFormat &baseLevelInternalFormat = *baseLevelDesc.format.info;
stateManager->bindTexture(getType(), mTextureID);
if (baseLevelInternalFormat.colorEncoding == GL_SRGB &&
features.encodeAndDecodeSRGBForGenerateMipmap.enabled && getType() == gl::TextureType::_2D)
{
nativegl::TexImageFormat texImageFormat = nativegl::GetTexImageFormat(
functions, features, baseLevelInternalFormat.internalFormat,
baseLevelInternalFormat.format, baseLevelInternalFormat.type);
// Manually allocate the mip levels of this texture if they don't exist
GLuint levelCount = maxLevel - effectiveBaseLevel + 1;
for (GLuint levelIdx = 1; levelIdx < levelCount; levelIdx++)
{
gl::Extents levelSize(std::max(baseLevelDesc.size.width >> levelIdx, 1),
std::max(baseLevelDesc.size.height >> levelIdx, 1), 1);
const gl::ImageDesc &levelDesc =
mState.getImageDesc(gl::TextureTarget::_2D, effectiveBaseLevel + levelIdx);
// Make sure no pixel unpack buffer is bound
stateManager->bindBuffer(gl::BufferBinding::PixelUnpack, 0);
if (levelDesc.size != levelSize || *levelDesc.format.info != baseLevelInternalFormat)
{
ANGLE_GL_TRY_ALWAYS_CHECK(
context, functions->texImage2D(
ToGLenum(getType()), effectiveBaseLevel + levelIdx,
texImageFormat.internalFormat, levelSize.width, levelSize.height,
0, texImageFormat.format, texImageFormat.type, nullptr));
}
}
// Use the blitter to generate the mips
BlitGL *blitter = GetBlitGL(context);
ANGLE_TRY(blitter->generateSRGBMipmap(context, this, effectiveBaseLevel, levelCount,
baseLevelDesc.size));
}
else
{
ANGLE_GL_TRY_ALWAYS_CHECK(context, functions->generateMipmap(ToGLenum(getType())));
}
setLevelInfo(context, getType(), effectiveBaseLevel, maxLevel - effectiveBaseLevel, setLevelInfo(context, getType(), effectiveBaseLevel, maxLevel - effectiveBaseLevel,
getBaseLevelInfo()); getBaseLevelInfo());

Просмотреть файл

@ -63,9 +63,9 @@ const InternalFormat &GetInternalFormatInfo(GLenum internalFormat, StandardGL st
struct TexImageFormat struct TexImageFormat
{ {
GLenum internalFormat; GLenum internalFormat = GL_NONE;
GLenum format; GLenum format = GL_NONE;
GLenum type; GLenum type = GL_NONE;
}; };
TexImageFormat GetTexImageFormat(const FunctionsGL *functions, TexImageFormat GetTexImageFormat(const FunctionsGL *functions,
const angle::FeaturesGL &features, const angle::FeaturesGL &features,
@ -75,8 +75,8 @@ TexImageFormat GetTexImageFormat(const FunctionsGL *functions,
struct TexSubImageFormat struct TexSubImageFormat
{ {
GLenum format; GLenum format = GL_NONE;
GLenum type; GLenum type = GL_NONE;
}; };
TexSubImageFormat GetTexSubImageFormat(const FunctionsGL *functions, TexSubImageFormat GetTexSubImageFormat(const FunctionsGL *functions,
const angle::FeaturesGL &features, const angle::FeaturesGL &features,
@ -85,7 +85,7 @@ TexSubImageFormat GetTexSubImageFormat(const FunctionsGL *functions,
struct CompressedTexImageFormat struct CompressedTexImageFormat
{ {
GLenum internalFormat; GLenum internalFormat = GL_NONE;
}; };
CompressedTexImageFormat GetCompressedTexImageFormat(const FunctionsGL *functions, CompressedTexImageFormat GetCompressedTexImageFormat(const FunctionsGL *functions,
const angle::FeaturesGL &features, const angle::FeaturesGL &features,
@ -93,7 +93,7 @@ CompressedTexImageFormat GetCompressedTexImageFormat(const FunctionsGL *function
struct CompressedTexSubImageFormat struct CompressedTexSubImageFormat
{ {
GLenum format; GLenum format = GL_NONE;
}; };
CompressedTexSubImageFormat GetCompressedSubTexImageFormat(const FunctionsGL *functions, CompressedTexSubImageFormat GetCompressedSubTexImageFormat(const FunctionsGL *functions,
const angle::FeaturesGL &features, const angle::FeaturesGL &features,
@ -101,7 +101,7 @@ CompressedTexSubImageFormat GetCompressedSubTexImageFormat(const FunctionsGL *fu
struct CopyTexImageImageFormat struct CopyTexImageImageFormat
{ {
GLenum internalFormat; GLenum internalFormat = GL_NONE;
}; };
CopyTexImageImageFormat GetCopyTexImageImageFormat(const FunctionsGL *functions, CopyTexImageImageFormat GetCopyTexImageImageFormat(const FunctionsGL *functions,
const angle::FeaturesGL &features, const angle::FeaturesGL &features,
@ -110,7 +110,7 @@ CopyTexImageImageFormat GetCopyTexImageImageFormat(const FunctionsGL *functions,
struct TexStorageFormat struct TexStorageFormat
{ {
GLenum internalFormat; GLenum internalFormat = GL_NONE;
}; };
TexStorageFormat GetTexStorageFormat(const FunctionsGL *functions, TexStorageFormat GetTexStorageFormat(const FunctionsGL *functions,
const angle::FeaturesGL &features, const angle::FeaturesGL &features,
@ -118,7 +118,7 @@ TexStorageFormat GetTexStorageFormat(const FunctionsGL *functions,
struct RenderbufferFormat struct RenderbufferFormat
{ {
GLenum internalFormat; GLenum internalFormat = GL_NONE;
}; };
RenderbufferFormat GetRenderbufferFormat(const FunctionsGL *functions, RenderbufferFormat GetRenderbufferFormat(const FunctionsGL *functions,
const angle::FeaturesGL &features, const angle::FeaturesGL &features,
@ -126,8 +126,8 @@ RenderbufferFormat GetRenderbufferFormat(const FunctionsGL *functions,
struct ReadPixelsFormat struct ReadPixelsFormat
{ {
GLenum format; GLenum format = GL_NONE;
GLenum type; GLenum type = GL_NONE;
}; };
ReadPixelsFormat GetReadPixelsFormat(const FunctionsGL *functions, ReadPixelsFormat GetReadPixelsFormat(const FunctionsGL *functions,
const angle::FeaturesGL &features, const angle::FeaturesGL &features,

Просмотреть файл

@ -1743,6 +1743,9 @@ void InitializeFeatures(const FunctionsGL *functions, angle::FeaturesGL *feature
IsLinux() && isAMD && isMesa && mesaVersion < (std::array<int, 3>{19, 3, 5})); IsLinux() && isAMD && isMesa && mesaVersion < (std::array<int, 3>{19, 3, 5}));
ANGLE_FEATURE_CONDITION(features, disableTimestampQueries, IsLinux() && isVMWare); ANGLE_FEATURE_CONDITION(features, disableTimestampQueries, IsLinux() && isVMWare);
ANGLE_FEATURE_CONDITION(features, encodeAndDecodeSRGBForGenerateMipmap,
IsApple() && functions->standard == STANDARD_GL_DESKTOP);
} }
void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFeatures *features) void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFeatures *features)