Commit e4109f27 by James Darpinian Committed by Commit Bot

WebGL: validate texture format matches sampler type

WebGL requires that drawing produces INVALID_OPERATION if a texture's format doesn't match the sampler type it is bound to. This is a little confusing because samplers have two attributes that could be called "type": addressing mode (2D/3D/Cube), and component format (float/signed/unsigned/shadow). ANGLE already handled checking the addressing mode; this change adds checking for the component format. Fixes WebGL conformance test conformance2/uniforms/incompatible-texture-type-for-sampler.html Bug: chromium:809237 Change-Id: I52ebfecd92625e3ee10274cb5f548d7e53de72dd Reviewed-on: https://chromium-review.googlesource.com/c/1377611Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarYuly Novikov <ynovikov@chromium.org> Commit-Queue: James Darpinian <jdarpinian@chromium.org>
parent 225f08bf
...@@ -122,5 +122,5 @@ ...@@ -122,5 +122,5 @@
"proc table:src/libGLESv2/proc_table_data.json": "proc table:src/libGLESv2/proc_table_data.json":
"68772eb4798f438df2eb997394999855", "68772eb4798f438df2eb997394999855",
"uniform type:src/common/gen_uniform_type_table.py": "uniform type:src/common/gen_uniform_type_table.py":
"59cb4ffd0f584c4bd37f2f4ff59a2b93" "e185802e66950dfc5fc7a8fc19751206"
} }
\ No newline at end of file
...@@ -138,7 +138,7 @@ const UniformTypeInfo &GetUniformTypeInfo(GLenum uniformType) ...@@ -138,7 +138,7 @@ const UniformTypeInfo &GetUniformTypeInfo(GLenum uniformType)
}} // namespace gl }} // namespace gl
""" """
type_info_data_template = """{{{type}, {component_type}, {texture_type}, {transposed_type}, {bool_type}, {rows}, {columns}, {components}, {component_size}, {internal_size}, {external_size}, {is_sampler}, {is_matrix}, {is_image} }}""" type_info_data_template = """{{{type}, {component_type}, {texture_type}, {transposed_type}, {bool_type}, {sampler_format}, {rows}, {columns}, {components}, {component_size}, {internal_size}, {external_size}, {is_sampler}, {is_matrix}, {is_image} }}"""
type_index_case_template = """case {enum_value}: return {index_value};""" type_index_case_template = """case {enum_value}: return {index_value};"""
def cpp_bool(value): def cpp_bool(value):
...@@ -181,6 +181,18 @@ def get_bool_type(uniform_type): ...@@ -181,6 +181,18 @@ def get_bool_type(uniform_type):
else: else:
return "GL_NONE" return "GL_NONE"
def get_sampler_format(uniform_type):
if not "_SAMPLER_" in uniform_type:
return "SamplerFormat::InvalidEnum"
elif "_SHADOW" in uniform_type:
return "SamplerFormat::Shadow"
elif "GL_UNSIGNED_INT_SAMPLER_" in uniform_type:
return "SamplerFormat::Unsigned"
elif "GL_INT_SAMPLER_" in uniform_type:
return "SamplerFormat::Signed"
else:
return "SamplerFormat::Float"
def get_rows(uniform_type): def get_rows(uniform_type):
if uniform_type == "GL_NONE": if uniform_type == "GL_NONE":
return "0" return "0"
...@@ -239,6 +251,7 @@ def gen_type_info(uniform_type): ...@@ -239,6 +251,7 @@ def gen_type_info(uniform_type):
texture_type = get_texture_type(uniform_type), texture_type = get_texture_type(uniform_type),
transposed_type = get_transposed_type(uniform_type), transposed_type = get_transposed_type(uniform_type),
bool_type = get_bool_type(uniform_type), bool_type = get_bool_type(uniform_type),
sampler_format = get_sampler_format(uniform_type),
rows = get_rows(uniform_type), rows = get_rows(uniform_type),
columns = get_columns(uniform_type), columns = get_columns(uniform_type),
components = get_components(uniform_type), components = get_components(uniform_type),
......
...@@ -85,6 +85,17 @@ unsigned int ArraySizeProduct(const std::vector<unsigned int> &arraySizes); ...@@ -85,6 +85,17 @@ unsigned int ArraySizeProduct(const std::vector<unsigned int> &arraySizes);
// GL_INVALID_INDEX and write the length of the original string. // GL_INVALID_INDEX and write the length of the original string.
unsigned int ParseArrayIndex(const std::string &name, size_t *nameLengthWithoutArrayIndexOut); unsigned int ParseArrayIndex(const std::string &name, size_t *nameLengthWithoutArrayIndexOut);
enum class SamplerFormat : uint8_t
{
Float = 0,
Unsigned = 1,
Signed = 2,
Shadow = 3,
InvalidEnum = 4,
EnumCount = 4,
};
struct UniformTypeInfo final : angle::NonCopyable struct UniformTypeInfo final : angle::NonCopyable
{ {
constexpr UniformTypeInfo(GLenum type, constexpr UniformTypeInfo(GLenum type,
...@@ -92,6 +103,7 @@ struct UniformTypeInfo final : angle::NonCopyable ...@@ -92,6 +103,7 @@ struct UniformTypeInfo final : angle::NonCopyable
GLenum textureType, GLenum textureType,
GLenum transposedMatrixType, GLenum transposedMatrixType,
GLenum boolVectorType, GLenum boolVectorType,
SamplerFormat samplerFormat,
int rowCount, int rowCount,
int columnCount, int columnCount,
int componentCount, int componentCount,
...@@ -106,6 +118,7 @@ struct UniformTypeInfo final : angle::NonCopyable ...@@ -106,6 +118,7 @@ struct UniformTypeInfo final : angle::NonCopyable
textureType(textureType), textureType(textureType),
transposedMatrixType(transposedMatrixType), transposedMatrixType(transposedMatrixType),
boolVectorType(boolVectorType), boolVectorType(boolVectorType),
samplerFormat(samplerFormat),
rowCount(rowCount), rowCount(rowCount),
columnCount(columnCount), columnCount(columnCount),
componentCount(componentCount), componentCount(componentCount),
...@@ -122,6 +135,7 @@ struct UniformTypeInfo final : angle::NonCopyable ...@@ -122,6 +135,7 @@ struct UniformTypeInfo final : angle::NonCopyable
GLenum textureType; GLenum textureType;
GLenum transposedMatrixType; GLenum transposedMatrixType;
GLenum boolVectorType; GLenum boolVectorType;
SamplerFormat samplerFormat;
int rowCount; int rowCount;
int columnCount; int columnCount;
int componentCount; int componentCount;
......
...@@ -421,6 +421,7 @@ MSG kRenderableInternalFormat = "SizedInternalformat must be color-renderable = ...@@ -421,6 +421,7 @@ MSG kRenderableInternalFormat = "SizedInternalformat must be color-renderable =
MSG kRenderbufferNotBound = "A renderbuffer must be bound."; MSG kRenderbufferNotBound = "A renderbuffer must be bound.";
MSG kResourceMaxRenderbufferSize = "Desired resource size is greater than max renderbuffer size."; MSG kResourceMaxRenderbufferSize = "Desired resource size is greater than max renderbuffer size.";
MSG kResourceMaxTextureSize = "Desired resource size is greater than max texture size."; MSG kResourceMaxTextureSize = "Desired resource size is greater than max texture size.";
MSG kSamplerFormatMismatch = "Mismatch between texture format and sampler type (signed/unsigned/float/shadow).";
MSG kSamplerUniformValueOutOfRange = "Sampler uniform value out of range."; MSG kSamplerUniformValueOutOfRange = "Sampler uniform value out of range.";
MSG kSamplesOutOfRange = "Samples must not be greater than maximum supported value for the format."; MSG kSamplesOutOfRange = "Samples must not be greater than maximum supported value for the format.";
MSG kSamplesZero = "Samples may not be zero."; MSG kSamplesZero = "Samples may not be zero.";
......
...@@ -414,10 +414,10 @@ angle::Result MemoryProgramCache::Deserialize(const Context *context, ...@@ -414,10 +414,10 @@ angle::Result MemoryProgramCache::Deserialize(const Context *context,
for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex) for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
{ {
TextureType textureType = stream.readEnum<TextureType>(); TextureType textureType = stream.readEnum<TextureType>();
SamplerFormat format = stream.readEnum<SamplerFormat>();
size_t bindingCount = stream.readInt<size_t>(); size_t bindingCount = stream.readInt<size_t>();
bool unreferenced = stream.readBool(); bool unreferenced = stream.readBool();
state->mSamplerBindings.emplace_back( state->mSamplerBindings.emplace_back(textureType, format, bindingCount, unreferenced);
SamplerBinding(textureType, bindingCount, unreferenced));
} }
unsigned int imageRangeLow = stream.readInt<unsigned int>(); unsigned int imageRangeLow = stream.readInt<unsigned int>();
...@@ -611,6 +611,7 @@ void MemoryProgramCache::Serialize(const Context *context, ...@@ -611,6 +611,7 @@ void MemoryProgramCache::Serialize(const Context *context,
for (const auto &samplerBinding : state.getSamplerBindings()) for (const auto &samplerBinding : state.getSamplerBindings())
{ {
stream.writeEnum(samplerBinding.textureType); stream.writeEnum(samplerBinding.textureType);
stream.writeEnum(samplerBinding.format);
stream.writeInt(samplerBinding.boundTextureUnits.size()); stream.writeInt(samplerBinding.boundTextureUnits.size());
stream.writeInt(samplerBinding.unreferenced); stream.writeInt(samplerBinding.unreferenced);
} }
......
...@@ -724,8 +724,14 @@ VariableLocation::VariableLocation(unsigned int arrayIndex, unsigned int index) ...@@ -724,8 +724,14 @@ VariableLocation::VariableLocation(unsigned int arrayIndex, unsigned int index)
} }
// SamplerBindings implementation. // SamplerBindings implementation.
SamplerBinding::SamplerBinding(TextureType textureTypeIn, size_t elementCount, bool unreferenced) SamplerBinding::SamplerBinding(TextureType textureTypeIn,
: textureType(textureTypeIn), boundTextureUnits(elementCount, 0), unreferenced(unreferenced) SamplerFormat formatIn,
size_t elementCount,
bool unreferenced)
: textureType(textureTypeIn),
format(formatIn),
boundTextureUnits(elementCount, 0),
unreferenced(unreferenced)
{} {}
SamplerBinding::SamplerBinding(const SamplerBinding &other) = default; SamplerBinding::SamplerBinding(const SamplerBinding &other) = default;
...@@ -1356,6 +1362,8 @@ void ProgramState::updateTransformFeedbackStrides() ...@@ -1356,6 +1362,8 @@ void ProgramState::updateTransformFeedbackStrides()
void ProgramState::updateActiveSamplers() void ProgramState::updateActiveSamplers()
{ {
mActiveSamplerRefCounts.fill(0);
for (SamplerBinding &samplerBinding : mSamplerBindings) for (SamplerBinding &samplerBinding : mSamplerBindings)
{ {
if (samplerBinding.unreferenced) if (samplerBinding.unreferenced)
...@@ -1363,8 +1371,22 @@ void ProgramState::updateActiveSamplers() ...@@ -1363,8 +1371,22 @@ void ProgramState::updateActiveSamplers()
for (GLint textureUnit : samplerBinding.boundTextureUnits) for (GLint textureUnit : samplerBinding.boundTextureUnits)
{ {
mActiveSamplerRefCounts[textureUnit]++; if (++mActiveSamplerRefCounts[textureUnit] == 1)
mActiveSamplerTypes[textureUnit] = getSamplerUniformTextureType(textureUnit); {
mActiveSamplerTypes[textureUnit] = samplerBinding.textureType;
mActiveSamplerFormats[textureUnit] = samplerBinding.format;
}
else
{
if (mActiveSamplerTypes[textureUnit] != samplerBinding.textureType)
{
mActiveSamplerTypes[textureUnit] = TextureType::InvalidEnum;
}
if (mActiveSamplerFormats[textureUnit] != samplerBinding.format)
{
mActiveSamplerFormats[textureUnit] = SamplerFormat::InvalidEnum;
}
}
mActiveSamplersMask.set(textureUnit); mActiveSamplersMask.set(textureUnit);
} }
} }
...@@ -2895,7 +2917,8 @@ void Program::linkSamplerAndImageBindings(GLuint *combinedImageUniforms) ...@@ -2895,7 +2917,8 @@ void Program::linkSamplerAndImageBindings(GLuint *combinedImageUniforms)
const auto &samplerUniform = mState.mUniforms[samplerIndex]; const auto &samplerUniform = mState.mUniforms[samplerIndex];
TextureType textureType = SamplerTypeToTextureType(samplerUniform.type); TextureType textureType = SamplerTypeToTextureType(samplerUniform.type);
unsigned int elementCount = samplerUniform.getBasicTypeElementCount(); unsigned int elementCount = samplerUniform.getBasicTypeElementCount();
mState.mSamplerBindings.emplace_back(textureType, elementCount, false); SamplerFormat format = samplerUniform.typeInfo->samplerFormat;
mState.mSamplerBindings.emplace_back(textureType, format, elementCount, false);
} }
} }
...@@ -3896,30 +3919,45 @@ void Program::updateSamplerUniform(Context *context, ...@@ -3896,30 +3919,45 @@ void Program::updateSamplerUniform(Context *context,
newRefCount++; newRefCount++;
// Check for binding type change. // Check for binding type change.
TextureType &newSamplerType = mState.mActiveSamplerTypes[newTextureUnit]; TextureType &newSamplerType = mState.mActiveSamplerTypes[newTextureUnit];
TextureType &oldSamplerType = mState.mActiveSamplerTypes[oldTextureUnit]; TextureType &oldSamplerType = mState.mActiveSamplerTypes[oldTextureUnit];
SamplerFormat &newSamplerFormat = mState.mActiveSamplerFormats[newTextureUnit];
SamplerFormat &oldSamplerFormat = mState.mActiveSamplerFormats[oldTextureUnit];
if (newRefCount == 1) if (newRefCount == 1)
{ {
newSamplerType = samplerBinding.textureType; newSamplerType = samplerBinding.textureType;
newSamplerFormat = samplerBinding.format;
mState.mActiveSamplersMask.set(newTextureUnit); mState.mActiveSamplersMask.set(newTextureUnit);
} }
else if (newSamplerType != samplerBinding.textureType) else
{ {
// Conflict detected. Ensure we reset it properly. if (newSamplerType != samplerBinding.textureType)
newSamplerType = TextureType::InvalidEnum; {
// Conflict detected. Ensure we reset it properly.
newSamplerType = TextureType::InvalidEnum;
}
if (newSamplerFormat != samplerBinding.format)
{
newSamplerFormat = SamplerFormat::InvalidEnum;
}
} }
// Unset previously active sampler. // Unset previously active sampler.
if (oldRefCount == 0) if (oldRefCount == 0)
{ {
oldSamplerType = TextureType::InvalidEnum; oldSamplerType = TextureType::InvalidEnum;
oldSamplerFormat = SamplerFormat::InvalidEnum;
mState.mActiveSamplersMask.reset(oldTextureUnit); mState.mActiveSamplersMask.reset(oldTextureUnit);
} }
else if (oldSamplerType == TextureType::InvalidEnum) else
{ {
// Previous conflict. Check if this new change fixed the conflict. if (oldSamplerType == TextureType::InvalidEnum ||
oldSamplerType = mState.getSamplerUniformTextureType(oldTextureUnit); oldSamplerFormat == SamplerFormat::InvalidEnum)
{
// Previous conflict. Check if this new change fixed the conflict.
mState.setSamplerUniformTextureTypeAndFormat(oldTextureUnit);
}
} }
// Notify context. // Notify context.
...@@ -3934,9 +3972,11 @@ void Program::updateSamplerUniform(Context *context, ...@@ -3934,9 +3972,11 @@ void Program::updateSamplerUniform(Context *context,
mCachedValidateSamplersResult.reset(); mCachedValidateSamplersResult.reset();
} }
TextureType ProgramState::getSamplerUniformTextureType(size_t textureUnitIndex) const void ProgramState::setSamplerUniformTextureTypeAndFormat(size_t textureUnitIndex)
{ {
TextureType foundType = TextureType::InvalidEnum; bool foundBinding = false;
TextureType foundType = TextureType::InvalidEnum;
SamplerFormat foundFormat = SamplerFormat::InvalidEnum;
for (const SamplerBinding &binding : mSamplerBindings) for (const SamplerBinding &binding : mSamplerBindings)
{ {
...@@ -3949,19 +3989,29 @@ TextureType ProgramState::getSamplerUniformTextureType(size_t textureUnitIndex) ...@@ -3949,19 +3989,29 @@ TextureType ProgramState::getSamplerUniformTextureType(size_t textureUnitIndex)
{ {
if (textureUnit == textureUnitIndex) if (textureUnit == textureUnitIndex)
{ {
if (foundType == TextureType::InvalidEnum) if (!foundBinding)
{ {
foundType = binding.textureType; foundBinding = true;
foundType = binding.textureType;
foundFormat = binding.format;
} }
else if (foundType != binding.textureType) else
{ {
return TextureType::InvalidEnum; if (foundType != binding.textureType)
{
foundType = TextureType::InvalidEnum;
}
if (foundFormat != binding.format)
{
foundFormat = SamplerFormat::InvalidEnum;
}
} }
} }
} }
} }
return foundType; mActiveSamplerTypes[textureUnitIndex] = foundType;
mActiveSamplerFormats[textureUnitIndex] = foundFormat;
} }
template <typename T> template <typename T>
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "common/Optional.h" #include "common/Optional.h"
#include "common/angleutils.h" #include "common/angleutils.h"
#include "common/mathutil.h" #include "common/mathutil.h"
#include "common/utilities.h"
#include "libANGLE/Constants.h" #include "libANGLE/Constants.h"
#include "libANGLE/Debug.h" #include "libANGLE/Debug.h"
...@@ -209,13 +210,18 @@ struct BindingInfo ...@@ -209,13 +210,18 @@ struct BindingInfo
// This small structure encapsulates binding sampler uniforms to active GL textures. // This small structure encapsulates binding sampler uniforms to active GL textures.
struct SamplerBinding struct SamplerBinding
{ {
SamplerBinding(TextureType textureTypeIn, size_t elementCount, bool unreferenced); SamplerBinding(TextureType textureTypeIn,
SamplerFormat formatIn,
size_t elementCount,
bool unreferenced);
SamplerBinding(const SamplerBinding &other); SamplerBinding(const SamplerBinding &other);
~SamplerBinding(); ~SamplerBinding();
// Necessary for retrieving active textures from the GL state. // Necessary for retrieving active textures from the GL state.
TextureType textureType; TextureType textureType;
SamplerFormat format;
// List of all textures bound to this sampler, of type textureType. // List of all textures bound to this sampler, of type textureType.
std::vector<GLuint> boundTextureUnits; std::vector<GLuint> boundTextureUnits;
...@@ -359,6 +365,10 @@ class ProgramState final : angle::NonCopyable ...@@ -359,6 +365,10 @@ class ProgramState final : angle::NonCopyable
bool hasAttachedShader() const; bool hasAttachedShader() const;
const ActiveTextureMask &getActiveSamplersMask() const { return mActiveSamplersMask; } const ActiveTextureMask &getActiveSamplersMask() const { return mActiveSamplersMask; }
SamplerFormat getSamplerFormatForTextureUnitIndex(size_t textureUnitIndex) const
{
return mActiveSamplerFormats[textureUnitIndex];
}
private: private:
friend class MemoryProgramCache; friend class MemoryProgramCache;
...@@ -369,7 +379,7 @@ class ProgramState final : angle::NonCopyable ...@@ -369,7 +379,7 @@ class ProgramState final : angle::NonCopyable
void updateActiveImages(); void updateActiveImages();
// Scans the sampler bindings for type conflicts with sampler 'textureUnitIndex'. // Scans the sampler bindings for type conflicts with sampler 'textureUnitIndex'.
TextureType getSamplerUniformTextureType(size_t textureUnitIndex) const; void setSamplerUniformTextureTypeAndFormat(size_t textureUnitIndex);
std::string mLabel; std::string mLabel;
...@@ -456,6 +466,7 @@ class ProgramState final : angle::NonCopyable ...@@ -456,6 +466,7 @@ class ProgramState final : angle::NonCopyable
ActiveTextureMask mActiveSamplersMask; ActiveTextureMask mActiveSamplersMask;
ActiveTextureArray<uint32_t> mActiveSamplerRefCounts; ActiveTextureArray<uint32_t> mActiveSamplerRefCounts;
ActiveTextureArray<TextureType> mActiveSamplerTypes; ActiveTextureArray<TextureType> mActiveSamplerTypes;
ActiveTextureArray<SamplerFormat> mActiveSamplerFormats;
// Cached mask of active images. // Cached mask of active images.
ActiveTextureMask mActiveImagesMask; ActiveTextureMask mActiveImagesMask;
......
...@@ -281,6 +281,7 @@ State::State(ContextID contextIn, ...@@ -281,6 +281,7 @@ State::State(ContextID contextIn,
mVertexArray(nullptr), mVertexArray(nullptr),
mActiveSampler(0), mActiveSampler(0),
mActiveTexturesCache{}, mActiveTexturesCache{},
mTexturesIncompatibleWithSamplers(0),
mPrimitiveRestart(false), mPrimitiveRestart(false),
mDebug(debug), mDebug(debug),
mMultiSampling(false), mMultiSampling(false),
...@@ -536,6 +537,10 @@ ANGLE_INLINE void State::updateActiveTextureState(const Context *context, ...@@ -536,6 +537,10 @@ ANGLE_INLINE void State::updateActiveTextureState(const Context *context,
} }
} }
mTexturesIncompatibleWithSamplers[textureIndex] =
!texture->getTextureState().compatibleWithSamplerFormat(
mProgram->getState().getSamplerFormatForTextureUnitIndex(textureIndex));
mDirtyBits.set(DIRTY_BIT_TEXTURE_BINDINGS); mDirtyBits.set(DIRTY_BIT_TEXTURE_BINDINGS);
} }
......
...@@ -601,6 +601,11 @@ class State : angle::NonCopyable ...@@ -601,6 +601,11 @@ class State : angle::NonCopyable
using BufferBindingSetter = void (State::*)(const Context *, Buffer *); using BufferBindingSetter = void (State::*)(const Context *, Buffer *);
ANGLE_INLINE bool validateSamplerFormats() const
{
return (mTexturesIncompatibleWithSamplers & mProgram->getActiveSamplersMask()).none();
}
private: private:
friend class Context; friend class Context;
...@@ -734,6 +739,8 @@ class State : angle::NonCopyable ...@@ -734,6 +739,8 @@ class State : angle::NonCopyable
ActiveTexturePointerArray mActiveTexturesCache; ActiveTexturePointerArray mActiveTexturesCache;
std::vector<angle::ObserverBinding> mCompleteTextureBindings; std::vector<angle::ObserverBinding> mCompleteTextureBindings;
ActiveTextureMask mTexturesIncompatibleWithSamplers;
SamplerBindingVector mSamplers; SamplerBindingVector mSamplers;
// It would be nice to merge the image and observer binding. Same for textures. // It would be nice to merge the image and observer binding. Same for textures.
......
...@@ -106,7 +106,9 @@ TextureState::TextureState(TextureType type) ...@@ -106,7 +106,9 @@ TextureState::TextureState(TextureType type)
mImageDescs((IMPLEMENTATION_MAX_TEXTURE_LEVELS + 1) * (type == TextureType::CubeMap ? 6 : 1)), mImageDescs((IMPLEMENTATION_MAX_TEXTURE_LEVELS + 1) * (type == TextureType::CubeMap ? 6 : 1)),
mCropRect(0, 0, 0, 0), mCropRect(0, 0, 0, 0),
mGenerateMipmapHint(GL_FALSE), mGenerateMipmapHint(GL_FALSE),
mInitState(InitState::MayNeedInit) mInitState(InitState::MayNeedInit),
mCachedSamplerFormat(SamplerFormat::InvalidEnum),
mCachedSamplerFormatValid(false)
{} {}
TextureState::~TextureState() {} TextureState::~TextureState() {}
...@@ -239,6 +241,33 @@ GLenum TextureState::getGenerateMipmapHint() const ...@@ -239,6 +241,33 @@ GLenum TextureState::getGenerateMipmapHint() const
return mGenerateMipmapHint; return mGenerateMipmapHint;
} }
SamplerFormat TextureState::computeRequiredSamplerFormat() const
{
const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel());
if ((baseImageDesc.format.info->format == GL_DEPTH_COMPONENT ||
baseImageDesc.format.info->format == GL_DEPTH_STENCIL) &&
mSamplerState.getCompareMode() != GL_NONE)
{
return SamplerFormat::Shadow;
}
else
{
switch (baseImageDesc.format.info->componentType)
{
case GL_UNSIGNED_NORMALIZED:
case GL_SIGNED_NORMALIZED:
case GL_FLOAT:
return SamplerFormat::Float;
case GL_INT:
return SamplerFormat::Signed;
case GL_UNSIGNED_INT:
return SamplerFormat::Unsigned;
default:
return SamplerFormat::InvalidEnum;
}
}
}
bool TextureState::computeSamplerCompleteness(const SamplerState &samplerState, bool TextureState::computeSamplerCompleteness(const SamplerState &samplerState,
const State &data) const const State &data) const
{ {
...@@ -964,6 +993,7 @@ void Texture::signalDirtyStorage(const Context *context, InitState initState) ...@@ -964,6 +993,7 @@ void Texture::signalDirtyStorage(const Context *context, InitState initState)
{ {
mState.mInitState = initState; mState.mInitState = initState;
invalidateCompletenessCache(); invalidateCompletenessCache();
mState.mCachedSamplerFormatValid = false;
onStateChange(context, angle::SubjectMessage::STORAGE_CHANGED); onStateChange(context, angle::SubjectMessage::STORAGE_CHANGED);
} }
...@@ -971,6 +1001,7 @@ void Texture::signalDirtyState(const Context *context, size_t dirtyBit) ...@@ -971,6 +1001,7 @@ void Texture::signalDirtyState(const Context *context, size_t dirtyBit)
{ {
mDirtyBits.set(dirtyBit); mDirtyBits.set(dirtyBit);
invalidateCompletenessCache(); invalidateCompletenessCache();
mState.mCachedSamplerFormatValid = false;
onStateChange(context, angle::SubjectMessage::DEPENDENT_DIRTY_BITS); onStateChange(context, angle::SubjectMessage::DEPENDENT_DIRTY_BITS);
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "angle_gl.h" #include "angle_gl.h"
#include "common/Optional.h" #include "common/Optional.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/utilities.h"
#include "libANGLE/Caps.h" #include "libANGLE/Caps.h"
#include "libANGLE/Constants.h" #include "libANGLE/Constants.h"
#include "libANGLE/Debug.h" #include "libANGLE/Debug.h"
...@@ -109,6 +110,17 @@ struct TextureState final : private angle::NonCopyable ...@@ -109,6 +110,17 @@ struct TextureState final : private angle::NonCopyable
bool isCubeComplete() const; bool isCubeComplete() const;
ANGLE_INLINE bool compatibleWithSamplerFormat(SamplerFormat format) const
{
if (!mCachedSamplerFormatValid)
{
mCachedSamplerFormat = computeRequiredSamplerFormat();
mCachedSamplerFormatValid = true;
}
// Incomplete textures are compatible with any sampler format.
return mCachedSamplerFormat == SamplerFormat::InvalidEnum || format == mCachedSamplerFormat;
}
const ImageDesc &getImageDesc(TextureTarget target, size_t level) const; const ImageDesc &getImageDesc(TextureTarget target, size_t level) const;
const ImageDesc &getImageDesc(const ImageIndex &imageIndex) const; const ImageDesc &getImageDesc(const ImageIndex &imageIndex) const;
...@@ -139,6 +151,7 @@ struct TextureState final : private angle::NonCopyable ...@@ -139,6 +151,7 @@ struct TextureState final : private angle::NonCopyable
bool computeSamplerCompleteness(const SamplerState &samplerState, const State &data) const; bool computeSamplerCompleteness(const SamplerState &samplerState, const State &data) const;
bool computeMipmapCompleteness() const; bool computeMipmapCompleteness() const;
bool computeLevelCompleteness(TextureTarget target, size_t level) const; bool computeLevelCompleteness(TextureTarget target, size_t level) const;
SamplerFormat computeRequiredSamplerFormat() const;
TextureTarget getBaseImageTarget() const; TextureTarget getBaseImageTarget() const;
...@@ -184,6 +197,9 @@ struct TextureState final : private angle::NonCopyable ...@@ -184,6 +197,9 @@ struct TextureState final : private angle::NonCopyable
GLenum mGenerateMipmapHint; GLenum mGenerateMipmapHint;
InitState mInitState; InitState mInitState;
mutable SamplerFormat mCachedSamplerFormat;
mutable bool mCachedSamplerFormatValid;
}; };
bool operator==(const TextureState &a, const TextureState &b); bool operator==(const TextureState &a, const TextureState &b);
......
...@@ -2727,6 +2727,11 @@ const char *ValidateDrawStates(Context *context) ...@@ -2727,6 +2727,11 @@ const char *ValidateDrawStates(Context *context)
// Do some additonal WebGL-specific validation // Do some additonal WebGL-specific validation
if (extensions.webglCompatibility) if (extensions.webglCompatibility)
{ {
if (!state.validateSamplerFormats())
{
return kSamplerFormatMismatch;
}
const TransformFeedback *transformFeedbackObject = state.getCurrentTransformFeedback(); const TransformFeedback *transformFeedbackObject = state.getCurrentTransformFeedback();
if (transformFeedbackObject != nullptr && transformFeedbackObject->isActive() && if (transformFeedbackObject != nullptr && transformFeedbackObject->isActive() &&
transformFeedbackObject->buffersBoundForOtherUse()) transformFeedbackObject->buffersBoundForOtherUse())
......
...@@ -3075,6 +3075,67 @@ void main() ...@@ -3075,6 +3075,67 @@ void main()
EXPECT_GL_ERROR(GL_INVALID_OPERATION) << "Simultaneous pack buffer binding should fail"; EXPECT_GL_ERROR(GL_INVALID_OPERATION) << "Simultaneous pack buffer binding should fail";
} }
// Test sampler format validation caching works.
TEST_P(WebGL2ValidationStateChangeTest, SamplerFormatCache)
{
constexpr char kFS[] = R"(#version 300 es
precision mediump float;
uniform sampler2D sampler;
out vec4 colorOut;
void main()
{
colorOut = texture(sampler, vec2(0));
})";
std::vector<std::string> tfVaryings = {"gl_Position"};
ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
glUseProgram(program);
std::array<GLColor, 4> colors = {
{GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow}};
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data());
const auto &quadVertices = GetQuadVertices();
GLBuffer arrayBuffer;
glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer);
glBufferData(GL_ARRAY_BUFFER, quadVertices.size() * sizeof(Vector3), quadVertices.data(),
GL_STATIC_DRAW);
GLint samplerLoc = glGetUniformLocation(program, "sampler");
ASSERT_NE(-1, samplerLoc);
glUniform1i(samplerLoc, 0);
GLint positionLoc = glGetAttribLocation(program, essl3_shaders::PositionAttrib());
ASSERT_NE(-1, positionLoc);
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLoc);
ASSERT_GL_NO_ERROR();
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
// TexImage2D should update the sampler format cache.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 2, 2, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE,
colors.data());
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_GL_ERROR(GL_INVALID_OPERATION) << "Sampling integer texture with a float sampler.";
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, 2, 2, 0, GL_DEPTH_COMPONENT,
GL_UNSIGNED_INT, colors.data());
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR() << "Depth texture with no compare mode.";
// TexParameteri should update the sampler format cache.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_GL_ERROR(GL_INVALID_OPERATION) << "Depth texture with compare mode set.";
}
// Tests that we retain the correct draw mode settings with transform feedback changes. // Tests that we retain the correct draw mode settings with transform feedback changes.
TEST_P(ValidationStateChangeTest, TransformFeedbackDrawModes) TEST_P(ValidationStateChangeTest, TransformFeedbackDrawModes)
{ {
......
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