зеркало из https://github.com/AvaloniaUI/angle.git
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:
Родитель
9fa671d52b
Коммит
6080383848
|
@ -441,6 +441,13 @@ struct FeaturesGL : FeatureSetBase
|
|||
Feature disableTimestampQueries = {
|
||||
"disable_timestamp_queries", FeatureCategory::OpenGLWorkarounds,
|
||||
"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;
|
||||
|
|
|
@ -988,6 +988,85 @@ angle::Result BlitGL::clearRenderableTextureAlphaToOne(const gl::Context *contex
|
|||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1049,12 +1149,34 @@ angle::Result BlitGL::orphanScratchTextures(const gl::Context *context)
|
|||
gl::PixelUnpackState unpack;
|
||||
mStateManager->setPixelUnpackState(unpack);
|
||||
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,
|
||||
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->texParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle));
|
||||
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,
|
||||
GL_UNSIGNED_BYTE, nullptr));
|
||||
}
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "common/angleutils.h"
|
||||
#include "libANGLE/Error.h"
|
||||
#include "libANGLE/angletypes.h"
|
||||
#include "libANGLE/renderer/gl/formatutilsgl.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
|
@ -136,6 +137,12 @@ class BlitGL : angle::NonCopyable
|
|||
gl::TextureTarget target,
|
||||
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);
|
||||
|
||||
private:
|
||||
|
@ -173,6 +180,8 @@ class BlitGL : angle::NonCopyable
|
|||
|
||||
GLuint mVAO;
|
||||
GLuint mVertexBuffer;
|
||||
|
||||
nativegl::TexImageFormat mSRGBMipmapGenerationFormat;
|
||||
};
|
||||
} // namespace rx
|
||||
|
||||
|
|
|
@ -133,6 +133,7 @@ StateManagerGL::StateManagerGL(const FunctionsGL *functions,
|
|||
mClearColor(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
mClearDepth(1.0f),
|
||||
mClearStencil(0),
|
||||
mFramebufferSRGBAvailable(extensions.sRGBWriteControl),
|
||||
mFramebufferSRGBEnabled(false),
|
||||
mDitherEnabled(true),
|
||||
mTextureCubemapSeamlessEnabled(false),
|
||||
|
@ -2071,7 +2072,7 @@ angle::Result StateManagerGL::syncState(const gl::Context *context,
|
|||
|
||||
void StateManagerGL::setFramebufferSRGBEnabled(const gl::Context *context, bool enabled)
|
||||
{
|
||||
if (!context->getExtensions().sRGBWriteControl)
|
||||
if (!mFramebufferSRGBAvailable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -320,7 +320,9 @@ class StateManagerGL final : angle::NonCopyable
|
|||
float mClearDepth;
|
||||
GLint mClearStencil;
|
||||
|
||||
bool mFramebufferSRGBAvailable;
|
||||
bool mFramebufferSRGBEnabled;
|
||||
|
||||
bool mDitherEnabled;
|
||||
bool mTextureCubemapSeamlessEnabled;
|
||||
|
||||
|
|
|
@ -1240,15 +1240,57 @@ angle::Result TextureGL::setImageExternal(const gl::Context *context,
|
|||
|
||||
angle::Result TextureGL::generateMipmap(const gl::Context *context)
|
||||
{
|
||||
const FunctionsGL *functions = GetFunctionsGL(context);
|
||||
StateManagerGL *stateManager = GetStateManagerGL(context);
|
||||
|
||||
stateManager->bindTexture(getType(), mTextureID);
|
||||
ANGLE_GL_TRY_ALWAYS_CHECK(context, functions->generateMipmap(ToGLenum(getType())));
|
||||
const FunctionsGL *functions = GetFunctionsGL(context);
|
||||
StateManagerGL *stateManager = GetStateManagerGL(context);
|
||||
const angle::FeaturesGL &features = GetFeaturesGL(context);
|
||||
|
||||
const GLuint effectiveBaseLevel = mState.getEffectiveBaseLevel();
|
||||
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,
|
||||
getBaseLevelInfo());
|
||||
|
||||
|
|
|
@ -63,9 +63,9 @@ const InternalFormat &GetInternalFormatInfo(GLenum internalFormat, StandardGL st
|
|||
|
||||
struct TexImageFormat
|
||||
{
|
||||
GLenum internalFormat;
|
||||
GLenum format;
|
||||
GLenum type;
|
||||
GLenum internalFormat = GL_NONE;
|
||||
GLenum format = GL_NONE;
|
||||
GLenum type = GL_NONE;
|
||||
};
|
||||
TexImageFormat GetTexImageFormat(const FunctionsGL *functions,
|
||||
const angle::FeaturesGL &features,
|
||||
|
@ -75,8 +75,8 @@ TexImageFormat GetTexImageFormat(const FunctionsGL *functions,
|
|||
|
||||
struct TexSubImageFormat
|
||||
{
|
||||
GLenum format;
|
||||
GLenum type;
|
||||
GLenum format = GL_NONE;
|
||||
GLenum type = GL_NONE;
|
||||
};
|
||||
TexSubImageFormat GetTexSubImageFormat(const FunctionsGL *functions,
|
||||
const angle::FeaturesGL &features,
|
||||
|
@ -85,7 +85,7 @@ TexSubImageFormat GetTexSubImageFormat(const FunctionsGL *functions,
|
|||
|
||||
struct CompressedTexImageFormat
|
||||
{
|
||||
GLenum internalFormat;
|
||||
GLenum internalFormat = GL_NONE;
|
||||
};
|
||||
CompressedTexImageFormat GetCompressedTexImageFormat(const FunctionsGL *functions,
|
||||
const angle::FeaturesGL &features,
|
||||
|
@ -93,7 +93,7 @@ CompressedTexImageFormat GetCompressedTexImageFormat(const FunctionsGL *function
|
|||
|
||||
struct CompressedTexSubImageFormat
|
||||
{
|
||||
GLenum format;
|
||||
GLenum format = GL_NONE;
|
||||
};
|
||||
CompressedTexSubImageFormat GetCompressedSubTexImageFormat(const FunctionsGL *functions,
|
||||
const angle::FeaturesGL &features,
|
||||
|
@ -101,7 +101,7 @@ CompressedTexSubImageFormat GetCompressedSubTexImageFormat(const FunctionsGL *fu
|
|||
|
||||
struct CopyTexImageImageFormat
|
||||
{
|
||||
GLenum internalFormat;
|
||||
GLenum internalFormat = GL_NONE;
|
||||
};
|
||||
CopyTexImageImageFormat GetCopyTexImageImageFormat(const FunctionsGL *functions,
|
||||
const angle::FeaturesGL &features,
|
||||
|
@ -110,7 +110,7 @@ CopyTexImageImageFormat GetCopyTexImageImageFormat(const FunctionsGL *functions,
|
|||
|
||||
struct TexStorageFormat
|
||||
{
|
||||
GLenum internalFormat;
|
||||
GLenum internalFormat = GL_NONE;
|
||||
};
|
||||
TexStorageFormat GetTexStorageFormat(const FunctionsGL *functions,
|
||||
const angle::FeaturesGL &features,
|
||||
|
@ -118,7 +118,7 @@ TexStorageFormat GetTexStorageFormat(const FunctionsGL *functions,
|
|||
|
||||
struct RenderbufferFormat
|
||||
{
|
||||
GLenum internalFormat;
|
||||
GLenum internalFormat = GL_NONE;
|
||||
};
|
||||
RenderbufferFormat GetRenderbufferFormat(const FunctionsGL *functions,
|
||||
const angle::FeaturesGL &features,
|
||||
|
@ -126,8 +126,8 @@ RenderbufferFormat GetRenderbufferFormat(const FunctionsGL *functions,
|
|||
|
||||
struct ReadPixelsFormat
|
||||
{
|
||||
GLenum format;
|
||||
GLenum type;
|
||||
GLenum format = GL_NONE;
|
||||
GLenum type = GL_NONE;
|
||||
};
|
||||
ReadPixelsFormat GetReadPixelsFormat(const FunctionsGL *functions,
|
||||
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}));
|
||||
|
||||
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)
|
||||
|
|
Загрузка…
Ссылка в новой задаче