Vulkan: Implement EXT_texture_sRGB_decode

Implement EXT_texture_sRGB_decode. This builds on the existing
functionality from EXT_texture_sRGB_override, with 2 major edge
cases:
1. sRGB_decode allows the texture state to be overridden by
sampler state, which is implemented by forcing a a texture state
sync during updateActiveTextures if a texture is bound to the same
unit as a sampler with that state
2. texelFetch calls require us to reenable decoding, regardless
of decode state. We add a new compiler pass
(FlagSamplersWithTexelFetch) to mark samplers that are used with
texelFetch in order to support this.
This change also re-enables EXT_texture_sRGB_R8, which was disabled
due to a dEQP bug that this change will bypass.

Bug: angleproject:3609
Bug: angleproject:4503
Test: dEQP.GLES31/functional_srgb_texture_decode_skip_decode_*
Test: GLES31/functional_state_query_texture_*_srgb_decode_*
Test: GLES31/functional_state_query_sampler_*_srgb_decode_*
Test: GLES31/functional_debug_negative_coverage_*_srgb_decode_*
Test: GLES31/functional_android_extension_pack_extensions_ext_texture_srgb_decode
Test: angle_end2end_tests --gtest_filter=SRGBTextureTest.*Vulkan*
Change-Id: I4a67e487dc82e2f57c8c87d4bcd8ef442b6fe220
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2359481
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Tim Van Patten <timvp@google.com>
This commit is contained in:
Mohan Maiya 2020-10-16 14:46:45 -07:00 коммит произвёл Commit Bot
Родитель e3a573811a
Коммит 7bbe497aa8
28 изменённых файлов: 668 добавлений и 77 удалений

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

@ -200,6 +200,9 @@ struct ShaderVariable
// Deprecated version of isSameVaryingAtLinkTime, which assumes ESSL1.
bool isSameVaryingAtLinkTime(const ShaderVariable &other) const;
// If the variable is a sampler that has ever been statically used with texelFetch
bool texelFetchInvoked;
protected:
bool isSameVariableAtLinkTime(const ShaderVariable &other,
bool matchPrecision,

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

@ -223,12 +223,19 @@ const char *GetDebugMessageTypeString(GLenum type);
const char *GetDebugMessageSeverityString(GLenum severity);
// For use with EXT_texture_format_sRGB_override and EXT_texture_sRGB_decode
// A texture may either have SRGB decoding forced on, or use whatever decode state is default for
// the texture format.
// A texture may be forced to decode to a nonlinear colorspace, to a linear colorspace, or to the
// default colorspace of its current format.
//
// Default corresponds to "the texture should use the imageview that corresponds to its format"
// Linear corresponds to "the texture has sRGB decoding disabled by extension, and should use a
// linear imageview even if it is in a nonlinear format" NonLinear corresponds to "the texture has
// sRGB override enabled by extension, and should use a nonlinear imageview even if it is in a
// linear format"
enum class SrgbOverride
{
Default = 0,
Enabled
Linear,
NonLinear
};
} // namespace gl

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

@ -134,6 +134,8 @@ angle_translator_sources = [
"src/compiler/translator/tree_ops/EmulatePrecision.h",
"src/compiler/translator/tree_ops/ExpandIntegerPowExpressions.cpp",
"src/compiler/translator/tree_ops/ExpandIntegerPowExpressions.h",
"src/compiler/translator/tree_ops/FlagSamplersWithTexelFetch.cpp",
"src/compiler/translator/tree_ops/FlagSamplersWithTexelFetch.h",
"src/compiler/translator/tree_ops/FoldExpressions.cpp",
"src/compiler/translator/tree_ops/FoldExpressions.h",
"src/compiler/translator/tree_ops/InitializeVariables.cpp",

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

@ -48,6 +48,7 @@ ShaderVariable::ShaderVariable(GLenum typeIn)
index(-1),
interpolation(INTERPOLATION_SMOOTH),
isInvariant(false),
texelFetchInvoked(false),
flattenedOffsetInParentArrays(-1)
{}
@ -79,6 +80,7 @@ ShaderVariable::ShaderVariable(const ShaderVariable &other)
index(other.index),
interpolation(other.interpolation),
isInvariant(other.isInvariant),
texelFetchInvoked(other.texelFetchInvoked),
flattenedOffsetInParentArrays(other.flattenedOffsetInParentArrays)
{}
@ -104,6 +106,7 @@ ShaderVariable &ShaderVariable::operator=(const ShaderVariable &other)
index = other.index;
interpolation = other.interpolation;
isInvariant = other.isInvariant;
texelFetchInvoked = other.texelFetchInvoked;
return *this;
}
@ -117,7 +120,7 @@ bool ShaderVariable::operator==(const ShaderVariable &other) const
binding != other.binding || imageUnitFormat != other.imageUnitFormat ||
offset != other.offset || readonly != other.readonly || writeonly != other.writeonly ||
index != other.index || interpolation != other.interpolation ||
isInvariant != other.isInvariant)
isInvariant != other.isInvariant || texelFetchInvoked != other.texelFetchInvoked)
{
return false;
}

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

@ -18,6 +18,7 @@
#include "compiler/translator/ImmutableStringBuilder.h"
#include "compiler/translator/OutputVulkanGLSL.h"
#include "compiler/translator/StaticType.h"
#include "compiler/translator/tree_ops/FlagSamplersWithTexelFetch.h"
#include "compiler/translator/tree_ops/NameEmbeddedUniformStructs.h"
#include "compiler/translator/tree_ops/RemoveAtomicCounterBuiltins.h"
#include "compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.h"
@ -848,6 +849,11 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
}
}
if (!FlagSamplersForTexelFetch(this, root, &getSymbolTable(), &mUniforms))
{
return false;
}
if (defaultUniformCount > 0)
{
gl::ShaderType shaderType = gl::FromGLenum<gl::ShaderType>(getShaderType());

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

@ -0,0 +1,100 @@
//
// Copyright 2020 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.
//
// FlagSamplersForTexelFetch.cpp: finds all instances of texelFetch used with a static reference to
// a sampler uniform, and flag that uniform as having been used with texelFetch
//
#include "compiler/translator/tree_ops/FlagSamplersWithTexelFetch.h"
#include "angle_gl.h"
#include "common/utilities.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/tree_util/IntermNode_util.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
#include "compiler/translator/tree_util/ReplaceVariable.h"
namespace sh
{
namespace
{
// gvec4 texelFetch(gsamplerXD sampler, ivecX P, int lod)
constexpr uint32_t kTexelFetchSequenceLength = 3u;
// gvec4 texelFetchOffset(gsamplerXD sampler, ivecX P, int lod, ivecX offset)
constexpr uint32_t kTexelFetchOffsetSequenceLength = 4u;
class FlagSamplersWithTexelFetchTraverser : public TIntermTraverser
{
public:
FlagSamplersWithTexelFetchTraverser(TSymbolTable *symbolTable,
std::vector<ShaderVariable> *uniforms)
: TIntermTraverser(true, true, true, symbolTable), mUniforms(uniforms)
{}
bool visitAggregate(Visit visit, TIntermAggregate *node) override
{
// Decide if the node is a call to texelFetch[Offset]
if (node->getOp() != EOpCallBuiltInFunction)
{
return true;
}
ASSERT(node->getFunction()->symbolType() == SymbolType::BuiltIn);
if (node->getFunction()->name() != "texelFetch" &&
node->getFunction()->name() != "texelFetchOffset")
{
return true;
}
const TIntermSequence *sequence = node->getSequence();
// This must be a valid texelFetch invokation with the required number of arguments
ASSERT(sequence->size() == (node->getFunction()->name() == "texelFetch"
? kTexelFetchSequenceLength
: kTexelFetchOffsetSequenceLength));
TIntermSymbol *samplerSymbol = sequence->at(0)->getAsSymbolNode();
ASSERT(samplerSymbol != nullptr);
const TVariable &samplerVariable = samplerSymbol->variable();
for (ShaderVariable &uniform : *mUniforms)
{
if (samplerVariable.name() == uniform.name)
{
ASSERT(gl::IsSamplerType(uniform.type));
uniform.texelFetchInvoked = true;
break;
}
}
return true;
}
private:
std::vector<ShaderVariable> *mUniforms;
};
} // anonymous namespace
bool FlagSamplersForTexelFetch(TCompiler *compiler,
TIntermBlock *root,
TSymbolTable *symbolTable,
std::vector<ShaderVariable> *uniforms)
{
ASSERT(uniforms != nullptr);
if (uniforms->size() > 0)
{
FlagSamplersWithTexelFetchTraverser traverser(symbolTable, uniforms);
root->traverse(&traverser);
}
return true;
}
} // namespace sh

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

@ -0,0 +1,31 @@
//
// Copyright 2020 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.
//
// FlagSamplersForTexelFetch.h: finds all instances of texelFetch used with a static reference to a
// sampler uniform, and flag that uniform as having been used with texelFetch
//
#ifndef COMPILER_TRANSLATOR_TREEOPS_FLAGSAMPLERSWITHTEXELFETCH_H_
#define COMPILER_TRANSLATOR_TREEOPS_FLAGSAMPLERSWITHTEXELFETCH_H_
#include "GLSLANG/ShaderVars.h"
#include "common/angleutils.h"
namespace sh
{
class TCompiler;
class TIntermBlock;
class TSymbolTable;
// This flags all samplers which are statically accessed by a texelFetch invokation- that is, the
// sampler is used as a direct argument to the call to texelFetch. Dynamic accesses, or accesses
// with any amount of indirection, are not counted.
ANGLE_NO_DISCARD bool FlagSamplersForTexelFetch(TCompiler *compiler,
TIntermBlock *root,
TSymbolTable *symbolTable,
std::vector<sh::ShaderVariable> *uniforms);
} // namespace sh
#endif // COMPILER_TRANSLATOR_TREEOPS_FLAGSAMPLERSWITHTEXELFETCH_H_

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

@ -5611,7 +5611,7 @@ void Context::texImage2DExternal(TextureTarget target,
void Context::invalidateTexture(TextureType target)
{
mImplementation->invalidateTexture(target);
mState.invalidateTexture(target);
mState.invalidateTextureBindings(target);
}
void Context::getMultisamplefv(GLenum pname, GLuint index, GLfloat *val)

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

@ -889,6 +889,7 @@ void WriteShaderVar(BinaryOutputStream *stream, const sh::ShaderVariable &var)
stream->writeInt(var.offset);
stream->writeInt(var.readonly);
stream->writeInt(var.writeonly);
stream->writeInt(var.texelFetchInvoked);
ASSERT(var.fields.empty());
}
@ -906,10 +907,11 @@ void LoadShaderVar(BinaryInputStream *stream, sh::ShaderVariable *var)
var->structName = stream->readString();
var->setParentArrayIndex(stream->readInt<int>());
var->imageUnitFormat = stream->readInt<GLenum>();
var->offset = stream->readInt<int>();
var->readonly = stream->readBool();
var->writeonly = stream->readBool();
var->imageUnitFormat = stream->readInt<GLenum>();
var->offset = stream->readInt<int>();
var->readonly = stream->readBool();
var->writeonly = stream->readBool();
var->texelFetchInvoked = stream->readBool();
}
// VariableLocation implementation.

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

@ -469,10 +469,11 @@ class FlattenUniformVisitor : public sh::VariableNameVisitor
LinkedUniform linkedUniform(variable.type, variable.precision, fullNameWithArrayIndex,
variable.arraySizes, getBinding(), getOffset(), mLocation,
-1, sh::kDefaultBlockMemberInfo);
linkedUniform.mappedName = fullMappedNameWithArrayIndex;
linkedUniform.active = mMarkActive;
linkedUniform.staticUse = mMarkStaticUse;
linkedUniform.outerArraySizes = arraySizes;
linkedUniform.mappedName = fullMappedNameWithArrayIndex;
linkedUniform.active = mMarkActive;
linkedUniform.staticUse = mMarkStaticUse;
linkedUniform.outerArraySizes = arraySizes;
linkedUniform.texelFetchInvoked = variable.texelFetchInvoked;
if (variable.hasParentArrayIndex())
{
linkedUniform.setParentArrayIndex(variable.parentArrayIndex());

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

@ -1549,7 +1549,7 @@ void State::initializeZeroTextures(const Context *context, const TextureMap &zer
}
}
void State::invalidateTexture(TextureType type)
void State::invalidateTextureBindings(TextureType type)
{
mDirtyBits.set(DIRTY_BIT_TEXTURE_BINDINGS);
}

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

@ -307,7 +307,7 @@ class State : angle::NonCopyable
void detachTexture(const Context *context, const TextureMap &zeroTextures, TextureID texture);
void initializeZeroTextures(const Context *context, const TextureMap &zeroTextures);
void invalidateTexture(TextureType type);
void invalidateTextureBindings(TextureType type);
// Sampler object binding manipulation
void setSamplerBinding(const Context *context, GLuint textureUnit, Sampler *sampler);

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

@ -893,7 +893,7 @@ void Texture::setSRGBOverride(const Context *context, GLenum sRGBOverride)
{
SrgbOverride oldOverride = mState.mSrgbOverride;
mState.mSrgbOverride =
(sRGBOverride == GL_SRGB) ? SrgbOverride::Enabled : SrgbOverride::Default;
(sRGBOverride == GL_SRGB) ? SrgbOverride::NonLinear : SrgbOverride::Default;
if (mState.mSrgbOverride != oldOverride)
{
signalDirtyState(DIRTY_BIT_SRGB_OVERRIDE);
@ -902,7 +902,7 @@ void Texture::setSRGBOverride(const Context *context, GLenum sRGBOverride)
GLenum Texture::getSRGBOverride() const
{
return (mState.mSrgbOverride == SrgbOverride::Enabled) ? GL_SRGB : GL_NONE;
return (mState.mSrgbOverride == SrgbOverride::NonLinear) ? GL_SRGB : GL_NONE;
}
const SamplerState &Texture::getSamplerState() const

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

@ -597,6 +597,7 @@ void SerializeShaderVariable(gl::BinaryOutputStream *bos, const sh::ShaderVariab
bos->writeInt(shaderVariable.index);
bos->writeEnum(shaderVariable.interpolation);
bos->writeInt(shaderVariable.isInvariant);
bos->writeInt(shaderVariable.texelFetchInvoked);
}
void SerializeShaderVariablesVector(gl::BinaryOutputStream *bos,

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

@ -51,6 +51,7 @@ struct Format final : private angle::NonCopyable
constexpr bool hasDepthOrStencilBits() const;
constexpr bool isLUMA() const;
constexpr bool isSRGB() const;
constexpr bool isSint() const;
constexpr bool isUint() const;
@ -185,6 +186,40 @@ constexpr bool Format::isLUMA() const
return redBits == 0 && (luminanceBits > 0 || alphaBits > 0);
}
constexpr bool Format::isSRGB() const
{
// R8G8_UNORM_SRGB and B8G8R8_UNORM_SRGB are not yet supported by ANGLE
// clang-format off
return
id == FormatID::R8_UNORM_SRGB ||
id == FormatID::R8G8B8_UNORM_SRGB ||
id == FormatID::R8G8B8A8_UNORM_SRGB ||
id == FormatID::B8G8R8A8_UNORM_SRGB ||
id == FormatID::BC1_RGB_UNORM_SRGB_BLOCK ||
id == FormatID::BC1_RGBA_UNORM_SRGB_BLOCK ||
id == FormatID::BC2_RGBA_UNORM_SRGB_BLOCK ||
id == FormatID::BC3_RGBA_UNORM_SRGB_BLOCK ||
id == FormatID::BPTC_SRGB_ALPHA_UNORM_BLOCK ||
id == FormatID::ETC2_R8G8B8_SRGB_BLOCK ||
id == FormatID::ETC2_R8G8B8A1_SRGB_BLOCK ||
id == FormatID::ETC2_R8G8B8A8_SRGB_BLOCK ||
id == FormatID::ASTC_4x4_SRGB_BLOCK ||
id == FormatID::ASTC_5x4_SRGB_BLOCK ||
id == FormatID::ASTC_5x5_SRGB_BLOCK ||
id == FormatID::ASTC_6x5_SRGB_BLOCK ||
id == FormatID::ASTC_6x6_SRGB_BLOCK ||
id == FormatID::ASTC_8x5_SRGB_BLOCK ||
id == FormatID::ASTC_8x6_SRGB_BLOCK ||
id == FormatID::ASTC_8x8_SRGB_BLOCK ||
id == FormatID::ASTC_10x5_SRGB_BLOCK ||
id == FormatID::ASTC_10x6_SRGB_BLOCK ||
id == FormatID::ASTC_10x8_SRGB_BLOCK ||
id == FormatID::ASTC_10x10_SRGB_BLOCK ||
id == FormatID::ASTC_12x10_SRGB_BLOCK ||
id == FormatID::ASTC_12x12_SRGB_BLOCK;
// clang-format on
}
constexpr bool Format::isSint() const
{
return componentType == GL_INT;

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

@ -730,7 +730,7 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk
mGraphicsDirtyBits = mNewGraphicsCommandBufferDirtyBits;
mComputeDirtyBits = mNewComputeCommandBufferDirtyBits;
mActiveTextures.fill({nullptr, nullptr});
mActiveTextures.fill({nullptr, nullptr, true});
mActiveImages.fill(nullptr);
mPipelineDirtyBitsMask.set();
@ -4105,14 +4105,28 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context)
TextureVk *textureVk = vk::GetImpl(texture);
ASSERT(textureVk != nullptr);
const vk::SamplerHelper &samplerVk =
sampler ? vk::GetImpl(sampler)->getSampler() : textureVk->getSampler();
const SamplerVk *samplerVk = sampler ? vk::GetImpl(sampler) : nullptr;
if (samplerVk != nullptr && samplerVk->skipSamplerSRGBDecode())
{
// TODO (http://anglebug.com/5176) Refactor to use ensureImageInitialized instead of
// syncState
// A sampler may force a texture to reallocate in order to support sRGB_decode
// state
gl::Texture::DirtyBits decodeBit;
decodeBit.set(gl::Texture::DIRTY_BIT_SRGB_DECODE);
ANGLE_TRY(textureVk->syncState(context, decodeBit, gl::Command::Other));
}
const vk::SamplerHelper &samplerHelper =
samplerVk ? samplerVk->getSampler() : textureVk->getSampler();
activeTexture.texture = textureVk;
activeTexture.sampler = &samplerVk;
activeTexture.sampler = &samplerHelper;
activeTexture.useLinearImageView =
textureVk->shouldUseLinearColorspaceWithSampler(samplerVk);
vk::ImageViewSubresourceSerial imageViewSerial = textureVk->getImageViewSubresourceSerial();
mActiveTexturesDesc.update(textureUnit, imageViewSerial, samplerVk.getSamplerSerial());
mActiveTexturesDesc.update(textureUnit, imageViewSerial, samplerHelper.getSamplerSerial());
if (textureVk->getImage().hasImmutableSampler())
{

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

@ -334,6 +334,8 @@ class ContextVk : public ContextImpl, public vk::Context
angle::Result memoryBarrier(const gl::Context *context, GLbitfield barriers) override;
angle::Result memoryBarrierByRegion(const gl::Context *context, GLbitfield barriers) override;
ANGLE_INLINE void invalidateTexture(gl::TextureType target) override {}
VkDevice getDevice() const;
egl::ContextPriority getPriority() const { return mContextPriority; }

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

@ -1420,13 +1420,17 @@ angle::Result ProgramExecutableVk::updateTexturesDescriptorSet(ContextVk *contex
VkWriteDescriptorSet *writeInfos = contextVk->allocWriteDescriptorSets(arraySize);
for (uint32_t arrayElement = 0; arrayElement < arraySize; ++arrayElement)
{
GLuint textureUnit = samplerBinding.boundTextureUnits[arrayElement];
TextureVk *textureVk = activeTextures[textureUnit].texture;
const vk::SamplerHelper &samplerVk = *activeTextures[textureUnit].sampler;
GLuint textureUnit = samplerBinding.boundTextureUnits[arrayElement];
TextureVk *textureVk = activeTextures[textureUnit].texture;
const vk::SamplerHelper &samplerHelper = *activeTextures[textureUnit].sampler;
bool linearColorspaceWithSampler = activeTextures[textureUnit].useLinearImageView;
vk::ImageHelper &image = textureVk->getImage();
imageInfos[arrayElement].sampler = samplerVk.get().getHandle();
bool shouldUseLinearColorspace = textureVk->shouldUseLinearColorspaceWithTexelFetch(
linearColorspaceWithSampler, samplerUniform.texelFetchInvoked);
imageInfos[arrayElement].sampler = samplerHelper.get().getHandle();
imageInfos[arrayElement].imageLayout = image.getCurrentLayout();
if (emulateSeamfulCubeMapSampling)
@ -1434,13 +1438,15 @@ angle::Result ProgramExecutableVk::updateTexturesDescriptorSet(ContextVk *contex
// If emulating seamful cubemapping, use the fetch image view. This is
// basically the same image view as read, except it's a 2DArray view for
// cube maps.
imageInfos[arrayElement].imageView =
textureVk->getFetchImageViewAndRecordUse(contextVk).getHandle();
const vk::ImageView &imageView = textureVk->getFetchImageViewAndRecordUse(
contextVk, shouldUseLinearColorspace);
imageInfos[arrayElement].imageView = imageView.getHandle();
}
else
{
imageInfos[arrayElement].imageView =
textureVk->getReadImageViewAndRecordUse(contextVk).getHandle();
const vk::ImageView &imageView = textureVk->getReadImageViewAndRecordUse(
contextVk, shouldUseLinearColorspace);
imageInfos[arrayElement].imageView = imageView.getHandle();
}
if (textureVk->getImage().hasImmutableSampler())

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

@ -2159,23 +2159,26 @@ Serial RendererVk::issueShaderSerial()
// These functions look at the mandatory format for support, and fallback to querying the device (if
// necessary) to test the availability of the bits.
bool RendererVk::hasLinearImageFormatFeatureBits(VkFormat format,
const VkFormatFeatureFlags featureBits)
const VkFormatFeatureFlags featureBits) const
{
return hasFormatFeatureBits<&VkFormatProperties::linearTilingFeatures>(format, featureBits);
}
VkFormatFeatureFlags RendererVk::getImageFormatFeatureBits(VkFormat format,
const VkFormatFeatureFlags featureBits)
VkFormatFeatureFlags RendererVk::getImageFormatFeatureBits(
VkFormat format,
const VkFormatFeatureFlags featureBits) const
{
return getFormatFeatureBits<&VkFormatProperties::optimalTilingFeatures>(format, featureBits);
}
bool RendererVk::hasImageFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits)
bool RendererVk::hasImageFormatFeatureBits(VkFormat format,
const VkFormatFeatureFlags featureBits) const
{
return hasFormatFeatureBits<&VkFormatProperties::optimalTilingFeatures>(format, featureBits);
}
bool RendererVk::hasBufferFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits)
bool RendererVk::hasBufferFormatFeatureBits(VkFormat format,
const VkFormatFeatureFlags featureBits) const
{
return hasFormatFeatureBits<&VkFormatProperties::bufferFeatures>(format, featureBits);
}
@ -2328,7 +2331,7 @@ angle::Result RendererVk::newSharedFence(vk::Context *context,
template <VkFormatFeatureFlags VkFormatProperties::*features>
VkFormatFeatureFlags RendererVk::getFormatFeatureBits(VkFormat format,
const VkFormatFeatureFlags featureBits)
const VkFormatFeatureFlags featureBits) const
{
ASSERT(static_cast<uint32_t>(format) < vk::kNumVkFormats);
VkFormatProperties &deviceProperties = mFormatProperties[format];
@ -2357,7 +2360,7 @@ VkFormatFeatureFlags RendererVk::getFormatFeatureBits(VkFormat format,
}
template <VkFormatFeatureFlags VkFormatProperties::*features>
bool RendererVk::hasFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits)
bool RendererVk::hasFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits) const
{
return IsMaskFlagSet(getFormatFeatureBits<features>(format, featureBits), featureBits);
}

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

@ -156,11 +156,12 @@ class RendererVk : angle::NonCopyable
// Query the format properties for select bits (linearTilingFeatures, optimalTilingFeatures and
// bufferFeatures). Looks through mandatory features first, and falls back to querying the
// device (first time only).
bool hasLinearImageFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits);
bool hasLinearImageFormatFeatureBits(VkFormat format,
const VkFormatFeatureFlags featureBits) const;
VkFormatFeatureFlags getImageFormatFeatureBits(VkFormat format,
const VkFormatFeatureFlags featureBits);
bool hasImageFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits);
bool hasBufferFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits);
const VkFormatFeatureFlags featureBits) const;
bool hasImageFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits) const;
bool hasBufferFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits) const;
ANGLE_INLINE egl::ContextPriority getDriverPriority(egl::ContextPriority priority)
{
@ -289,10 +290,10 @@ class RendererVk : angle::NonCopyable
template <VkFormatFeatureFlags VkFormatProperties::*features>
VkFormatFeatureFlags getFormatFeatureBits(VkFormat format,
const VkFormatFeatureFlags featureBits);
const VkFormatFeatureFlags featureBits) const;
template <VkFormatFeatureFlags VkFormatProperties::*features>
bool hasFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits);
bool hasFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits) const;
angle::Result cleanupGarbage(bool block);
@ -366,7 +367,7 @@ class RendererVk : angle::NonCopyable
bool mPipelineCacheInitialized;
// A cache of VkFormatProperties as queried from the device over time.
std::array<VkFormatProperties, vk::kNumVkFormats> mFormatProperties;
mutable std::array<VkFormatProperties, vk::kNumVkFormats> mFormatProperties;
// Latest validation data for debug overlay.
std::string mLastValidationMessage;

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

@ -32,6 +32,8 @@ class SamplerVk : public SamplerImpl
return mSampler.get();
}
bool skipSamplerSRGBDecode() const { return mState.getSRGBDecode() == GL_SKIP_DECODE_EXT; }
private:
vk::SamplerBinding mSampler;
};

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

@ -1157,7 +1157,8 @@ angle::Result TextureVk::setEGLImageTarget(const gl::Context *context,
gl::LevelIndex(mState.getEffectiveBaseLevel()), false);
ASSERT(type != gl::TextureType::CubeMap);
ANGLE_TRY(initImageViews(contextVk, format, image->getFormat().info->sized, 1, 1));
ANGLE_TRY(initImageViews(contextVk, imageVk->getImage()->getFormat(),
image->getFormat().info->sized, 1, 1));
// Transfer the image to this queue if needed
uint32_t rendererQueueFamilyIndex = renderer->getQueueFamilyIndex();
@ -2106,13 +2107,15 @@ angle::Result TextureVk::syncState(const gl::Context *context,
mImageUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
}
if (dirtyBits.test(gl::Texture::DIRTY_BIT_SRGB_OVERRIDE))
// If we're handling dirty srgb decode/override state, we may have to reallocate the image with
// VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT. Vulkan requires this bit to be set in order to use
// imageviews with a format that does not match the texture's internal format.
if (dirtyBits.test(gl::Texture::DIRTY_BIT_SRGB_DECODE) ||
(dirtyBits.test(gl::Texture::DIRTY_BIT_SRGB_OVERRIDE) &&
mState.getSRGBOverride() != gl::SrgbOverride::Default))
{
if (mState.getSRGBOverride() != gl::SrgbOverride::Default)
{
mImageCreateFlags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
mRequiresSRGBViews = true;
}
mRequiresSRGBViews = true;
mImageCreateFlags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
}
// Before redefining the image for any reason, check to see if it's about to go through mipmap
@ -2195,7 +2198,8 @@ angle::Result TextureVk::syncState(const gl::Context *context,
localBits.test(gl::Texture::DIRTY_BIT_SWIZZLE_GREEN) ||
localBits.test(gl::Texture::DIRTY_BIT_SWIZZLE_BLUE) ||
localBits.test(gl::Texture::DIRTY_BIT_SWIZZLE_ALPHA) ||
localBits.test(gl::Texture::DIRTY_BIT_SRGB_OVERRIDE))
localBits.test(gl::Texture::DIRTY_BIT_SRGB_OVERRIDE) ||
localBits.test(gl::Texture::DIRTY_BIT_SRGB_DECODE))
{
// We use a special layer count here to handle EGLImages. They might only be
// looking at one layer of a cube or 2D array texture.
@ -2241,7 +2245,110 @@ void TextureVk::releaseOwnershipOfImage(const gl::Context *context)
releaseAndDeleteImage(contextVk);
}
const vk::ImageView &TextureVk::getReadImageViewAndRecordUse(ContextVk *contextVk) const
// TODO (http://anglebug.com/5176) Refactor to frontend
bool TextureVk::shouldUseLinearColorspaceWithSampler(const SamplerVk *samplerVk) const
{
ASSERT(mImage->valid());
// True if GL_TEXTURE_SRGB_DECODE_EXT == GL_SKIP_DECODE_EXT in the texture state
bool textureSRGBDecodeDisabled =
(mState.getSamplerState().getSRGBDecode() == GL_SKIP_DECODE_EXT);
// True if GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT == GL_SRGB in the texture state
bool textureSRGBOverriddenToNonLinear =
(mState.getSRGBOverride() == gl::SrgbOverride::NonLinear);
gl::SrgbOverride samplerDecodeOverride = gl::SrgbOverride::Default;
if (samplerVk != nullptr)
{
samplerDecodeOverride = (samplerVk->skipSamplerSRGBDecode() ? gl::SrgbOverride::Linear
: gl::SrgbOverride::NonLinear);
}
switch (samplerDecodeOverride)
{
case gl::SrgbOverride::Linear:
// If the sampler state skips decoding, we must choose the linear imageview,
// regardless of the texture state. This takes precedence over sRGB_override
return true;
case gl::SrgbOverride::NonLinear:
// If the sampler state does not skip decoding, we should choose the imageview
// required by sRGB_override- we should not force a linear format to use a nonlinear
// imageview if sRGB_override has not forced that
if (textureSRGBOverriddenToNonLinear)
{
return false;
}
else
{
ASSERT(mImage->getFormat().actualImageFormat().isSRGB() ==
(vk::ConvertToLinear(mImage->getFormat().vkImageFormat) !=
VK_FORMAT_UNDEFINED));
return !mImage->getFormat().actualImageFormat().isSRGB();
}
default:
// sRGB_decode texture state takes precedence over sRGB_override texture state
if (textureSRGBDecodeDisabled)
{
return true;
}
else if (textureSRGBOverriddenToNonLinear)
{
return false;
}
else
{
ASSERT(mImage->getFormat().actualImageFormat().isSRGB() ==
(vk::ConvertToLinear(mImage->getFormat().vkImageFormat) !=
VK_FORMAT_UNDEFINED));
return !mImage->getFormat().actualImageFormat().isSRGB();
}
}
}
// TODO (http://anglebug.com/5176) Refactor to frontend
bool TextureVk::shouldUseLinearColorspaceWithTexelFetch(bool colorspaceWithSampler,
bool texelFetchForcesDecodeOn) const
{
ASSERT(mImage->valid());
// True if GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT == GL_SRGB in the texture state
bool textureSRGBOverriddenToNonLinear =
(mState.getSRGBOverride() == gl::SrgbOverride::NonLinear);
// Enable sRGB decoding regardless of skipSamplerSRGBDecode, due to an edge
// case in the EXT_texture_sRGB_decode spec:
//
// "The conversion of sRGB color space components to linear color space is
// always applied if the TEXTURE_SRGB_DECODE_EXT parameter is DECODE_EXT.
// Table X.1 describes whether the conversion is skipped if the
// TEXTURE_SRGB_DECODE_EXT parameter is SKIP_DECODE_EXT, depending on
// the function used for the access, whether the access occurs through a
// bindless sampler, and whether the texture is statically accessed
// elsewhere with a texelFetch function."
if (texelFetchForcesDecodeOn)
{
// This imageview is used with a texelFetch invocation, so we must ignore all sRGB_decode
// state. sRGB_override state should still be considered.
if (textureSRGBOverriddenToNonLinear)
{
return false;
}
else
{
ASSERT(mImage->getFormat().actualImageFormat().isSRGB() ==
(vk::ConvertToLinear(mImage->getFormat().vkImageFormat) != VK_FORMAT_UNDEFINED));
return !mImage->getFormat().actualImageFormat().isSRGB();
}
}
else
{
return colorspaceWithSampler;
}
}
const vk::ImageView &TextureVk::getReadImageViewAndRecordUse(ContextVk *contextVk,
bool useLinearColorspace) const
{
ASSERT(mImage->valid());
@ -2253,16 +2360,20 @@ const vk::ImageView &TextureVk::getReadImageViewAndRecordUse(ContextVk *contextV
return imageViews.getStencilReadImageView();
}
if (mState.getSRGBOverride() == gl::SrgbOverride::Enabled)
if (useLinearColorspace)
{
ASSERT(imageViews.getLinearReadImageView().valid());
return imageViews.getLinearReadImageView();
}
else
{
ASSERT(imageViews.getNonLinearReadImageView().valid());
return imageViews.getNonLinearReadImageView();
}
return imageViews.getReadImageView();
}
const vk::ImageView &TextureVk::getFetchImageViewAndRecordUse(ContextVk *contextVk) const
const vk::ImageView &TextureVk::getFetchImageViewAndRecordUse(ContextVk *contextVk,
bool useLinearColorspace) const
{
ASSERT(mImage->valid());
@ -2272,14 +2383,16 @@ const vk::ImageView &TextureVk::getFetchImageViewAndRecordUse(ContextVk *context
// We don't currently support fetch for depth/stencil cube map textures.
ASSERT(!imageViews.hasStencilReadImageView() || !imageViews.hasFetchImageView());
if (mState.getSRGBOverride() == gl::SrgbOverride::Enabled)
if (useLinearColorspace)
{
return (imageViews.hasFetchImageView() ? imageViews.getLinearFetchImageView()
: imageViews.getLinearReadImageView());
}
else
{
return (imageViews.hasFetchImageView() ? imageViews.getNonLinearFetchImageView()
: imageViews.getNonLinearReadImageView());
}
return (imageViews.hasFetchImageView() ? imageViews.getFetchImageView()
: imageViews.getReadImageView());
}
const vk::ImageView &TextureVk::getCopyImageViewAndRecordUse(ContextVk *contextVk) const
@ -2289,12 +2402,16 @@ const vk::ImageView &TextureVk::getCopyImageViewAndRecordUse(ContextVk *contextV
const vk::ImageViewHelper &imageViews = getImageViews();
imageViews.retain(&contextVk->getResourceUseList());
if (mState.getSRGBOverride() == gl::SrgbOverride::Enabled)
ASSERT(mImage->getFormat().actualImageFormat().isSRGB() ==
(vk::ConvertToLinear(mImage->getFormat().vkImageFormat) != VK_FORMAT_UNDEFINED));
if (!mImage->getFormat().actualImageFormat().isSRGB())
{
return imageViews.getLinearCopyImageView();
}
else
{
return imageViews.getNonLinearCopyImageView();
}
return imageViews.getCopyImageView();
}
angle::Result TextureVk::getLevelLayerImageView(ContextVk *contextVk,

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

@ -174,10 +174,14 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
void releaseOwnershipOfImage(const gl::Context *context);
const vk::ImageView &getReadImageViewAndRecordUse(ContextVk *contextVk) const;
const vk::ImageView &getReadImageViewAndRecordUse(ContextVk *contextVk,
bool useLinearColorspace) const;
// A special view for cube maps as a 2D array, used with shaders that do texelFetch() and for
// seamful cube map emulation.
const vk::ImageView &getFetchImageViewAndRecordUse(ContextVk *contextVk) const;
const vk::ImageView &getFetchImageViewAndRecordUse(ContextVk *contextVk,
bool useLinearColorspace) const;
// A special view used for texture copies that shouldn't perform swizzle.
const vk::ImageView &getCopyImageViewAndRecordUse(ContextVk *contextVk) const;
angle::Result getStorageImageView(ContextVk *contextVk,
@ -214,6 +218,12 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
ANGLE_INLINE bool hasBeenBoundAsImage() const { return mState.hasBeenBoundAsImage(); }
ANGLE_INLINE bool hasSRGBViews() const { return mRequiresSRGBViews; }
bool shouldUseLinearColorspaceWithSampler(const SamplerVk *samplerVk) const;
bool shouldUseLinearColorspaceWithTexelFetch(bool colorspaceWithSampler,
bool texelFetchForcesDecodeOn) const;
private:
// Transform an image index from the frontend into one that can be used on the backing
// ImageHelper, taking into account mipmap or cube face offsets

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

@ -27,6 +27,66 @@ constexpr unsigned int kComponentsPerVector = 4;
namespace rx
{
namespace vk
{
namespace
{
bool FormatReinterpretationSupported(const std::vector<GLenum> &optionalSizedFormats,
const RendererVk *rendererVk,
bool checkLinearColorspace)
{
for (GLenum glFormat : optionalSizedFormats)
{
const gl::TextureCaps &baseCaps = rendererVk->getNativeTextureCaps().get(glFormat);
if (baseCaps.texturable && baseCaps.filterable)
{
const vk::Format &vkFormat = rendererVk->getFormat(glFormat);
VkFormat reinterpretedFormat = checkLinearColorspace
? vk::ConvertToLinear(vkFormat.vkImageFormat)
: vk::ConvertToNonLinear(vkFormat.vkImageFormat);
ASSERT(reinterpretedFormat != VK_FORMAT_UNDEFINED);
constexpr uint32_t kBitsSampleFilter =
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
if (!rendererVk->hasImageFormatFeatureBits(reinterpretedFormat, kBitsSampleFilter))
{
return false;
}
}
}
return true;
}
bool GetTextureSRGBDecodeSupport(const RendererVk *rendererVk)
{
static constexpr bool kLinearColorspace = true;
// GL_SRGB and GL_SRGB_ALPHA unsized formats are also required by the spec, but the only valid
// type for them is GL_UNSIGNED_BYTE, so they are fully included in the sized formats listed
// here
std::vector<GLenum> optionalSizedNonLinearFormats = {
GL_SRGB8,
GL_SRGB8_ALPHA8_EXT,
GL_COMPRESSED_SRGB_S3TC_DXT1_EXT,
GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,
GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT,
GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT,
};
if (!FormatReinterpretationSupported(optionalSizedNonLinearFormats, rendererVk,
kLinearColorspace))
{
return false;
}
return true;
}
} // namespace
} // namespace vk
GLint LimitToInt(const uint32_t physicalDeviceValue)
{
// Limit to INT_MAX / 2 instead of INT_MAX. If the limit is queried as float, the imprecision
@ -48,14 +108,6 @@ void RendererVk::ensureCapsInitialized() const
mNativeExtensions.setTextureExtensionSupport(mNativeTextureCaps);
// Enable GL_EXT_buffer_storage"
mNativeExtensions.bufferStorageEXT = true;
// TODO: http://anglebug.com/3609
// Due to a dEQP bug, this extension cannot be exposed until EXT_texture_sRGB_decode is
// implemented
mNativeExtensions.sRGBR8EXT = false;
// To ensure that ETC2/EAC formats are enabled only on hardware that supports them natively,
// this flag is not set by the function above and must be set explicitly. It exposes
// ANGLE_compressed_texture_etc extension string.
@ -200,8 +252,10 @@ void RendererVk::ensureCapsInitialized() const
mNativeExtensions.depthTextureCubeMapOES =
mNativeExtensions.depthTextureOES && mNativeExtensions.packedDepthStencilOES;
// Vulkan natively supports format reinterpretation
mNativeExtensions.textureSRGBOverride = mNativeExtensions.sRGB;
// Vulkan natively supports format reinterpretation, but we still require support for all
// formats we may reinterpret to
mNativeExtensions.textureSRGBOverride = true;
mNativeExtensions.textureSRGBDecode = vk::GetTextureSRGBDecodeSupport(this);
mNativeExtensions.gpuShader5EXT = vk::CanSupportGPUShader5EXT(mPhysicalDeviceFeatures);

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

@ -6020,8 +6020,18 @@ angle::Result ImageViewHelper::initSRGBReadViewsImpl(ContextVk *contextVk,
uint32_t layerCount,
VkImageUsageFlags imageUsageFlags)
{
// When we select the linear/nonlinear counterpart formats, we must first make sure they're
// actually supported by the ICD. If they are not supported by the ICD, then we treat that as if
// there is no counterpart format. (In this case, the relevant extension should not be exposed)
VkFormat nonLinearOverrideFormat = ConvertToNonLinear(image.getFormat().vkImageFormat);
VkFormat linearOverrideFormat = ConvertToLinear(image.getFormat().vkImageFormat);
ASSERT(
(nonLinearOverrideFormat == VK_FORMAT_UNDEFINED) ||
(HasNonRenderableTextureFormatSupport(contextVk->getRenderer(), nonLinearOverrideFormat)));
VkFormat linearOverrideFormat = ConvertToLinear(image.getFormat().vkImageFormat);
ASSERT((linearOverrideFormat == VK_FORMAT_UNDEFINED) ||
(HasNonRenderableTextureFormatSupport(contextVk->getRenderer(), linearOverrideFormat)));
VkFormat linearFormat =
(linearOverrideFormat != VK_FORMAT_UNDEFINED) ? linearOverrideFormat : format.vkImageFormat;
ASSERT(linearFormat != VK_FORMAT_UNDEFINED);

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

@ -44,6 +44,7 @@ struct TextureUnit final
{
TextureVk *texture;
const SamplerHelper *sampler;
bool useLinearImageView;
};
// A dynamic buffer is conceptually an infinitely long buffer. Each time you write to the buffer,

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

@ -747,6 +747,80 @@ TEST_P(ProgramBinaryES3Test, BinaryWithLargeUniformCount)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta);
}
// Test that sampler texelFetch references are saved and loaded correctly
TEST_P(ProgramBinaryTest, SRGBDecodeWithSamplerAndTexelFetchTest)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode") ||
getClientMajorVersion() < 3);
// These OpenGL drivers appear not to respect the texelFetch exception
// http://anglebug.com/4991
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsWindows());
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsAMD() && IsWindows());
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsNVIDIA() && IsOSX());
ANGLE_SKIP_TEST_IF(IsOpenGLES() && IsNexus5X());
constexpr char kVS[] =
"#version 300 es\n"
"precision highp float;\n"
"in vec4 position;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(position.xy, 0.0, 1.0);\n"
"}\n";
constexpr char kFS[] =
"#version 300 es\n"
"precision highp float;\n"
"uniform sampler2D tex;\n"
"in vec2 texcoord;\n"
"out vec4 out_color;\n"
"\n"
"void main()\n"
"{\n"
" out_color = texelFetch(tex, ivec2(0, 0), 0);\n"
"}\n";
GLProgram program;
program.makeRaster(kVS, kFS);
ASSERT_NE(0u, program.get());
GLuint reloadedProgram = glCreateProgram();
saveAndLoadProgram(program.get(), reloadedProgram);
GLint textureLocation = glGetUniformLocation(reloadedProgram, "tex");
ASSERT_NE(-1, textureLocation);
GLColor linearColor(64, 127, 191, 255);
GLColor srgbColor(13, 54, 133, 255);
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
&linearColor);
ASSERT_GL_NO_ERROR();
GLSampler sampler;
glBindSampler(0, sampler.get());
glSamplerParameteri(sampler.get(), GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT);
glUseProgram(reloadedProgram);
glUniform1i(textureLocation, 0);
glDisable(GL_DEPTH_TEST);
drawQuad(reloadedProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
glSamplerParameteri(sampler.get(), GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
drawQuad(reloadedProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
glDeleteProgram(reloadedProgram);
}
ANGLE_INSTANTIATE_TEST_ES3(ProgramBinaryES3Test);
class ProgramBinaryES31Test : public ANGLETest

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

@ -304,6 +304,46 @@ TEST_P(SRGBTextureTest, SRGBOverrideTextureParameter)
EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
}
// Test interaction between sRGB_override and sampler objects
TEST_P(SRGBTextureTest, SRGBOverrideTextureParameterWithSampler)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_override") ||
getClientMajorVersion() < 3);
GLColor linearColor = kLinearColor;
GLColor srgbColor = kNonlinearColor;
GLenum internalFormat = getClientMajorVersion() >= 3 ? GL_RGBA8 : GL_RGBA;
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
&linearColor);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_NONE);
ASSERT_GL_NO_ERROR();
GLSampler sampler;
glBindSampler(0, sampler.get());
glUseProgram(mProgram);
glUniform1i(mTextureLocation, 0);
glDisable(GL_DEPTH_TEST);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_SRGB);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_NONE);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
}
// Test that SRGB override is a noop when used on a nonlinear texture format
// EXT_texture_format_sRGB_override spec says:
// "If the internal format is not one of the above formats, then
@ -370,6 +410,72 @@ TEST_P(SRGBTextureTest, SRGBDecodeSamplerParameter)
EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
}
// Test that sampler state overrides texture state for srgb decode
TEST_P(SRGBTextureTest, SRGBDecodeTextureAndSamplerParameter)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode") ||
getClientMajorVersion() < 3);
GLColor linearColor = kLinearColor;
GLColor srgbColor = kNonlinearColor;
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexImage2D(GL_TEXTURE_2D, 0, getSRGBA8TextureInternalFormat(), 1, 1, 0,
getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE, &linearColor);
ASSERT_GL_NO_ERROR();
GLSampler sampler;
glBindSampler(0, sampler.get());
glUseProgram(mProgram);
glUniform1i(mTextureLocation, 0);
glDisable(GL_DEPTH_TEST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
glSamplerParameteri(sampler.get(), GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT);
glSamplerParameteri(sampler.get(), GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
}
// Test that srgb decode state takes priority over srgb override state
TEST_P(SRGBTextureTest, SRGBDecodeOverridePriority)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode") ||
getClientMajorVersion() < 3);
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_override"));
GLColor linearColor = kLinearColor;
GLenum internalFormat = getClientMajorVersion() >= 3 ? GL_RGBA8 : GL_RGBA;
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
&linearColor);
ASSERT_GL_NO_ERROR();
glUseProgram(mProgram);
glUniform1i(mTextureLocation, 0);
glDisable(GL_DEPTH_TEST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_SRGB);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
}
// Test that mipmaps are generated correctly for sRGB textures
TEST_P(SRGBTextureTest, GenerateMipmaps)
{