Commit e8528d89 by Olli Etuaho Committed by Commit Bot

Fix determining texture mipmap completeness

The code didn't previously take base level properly into account when determining how many levels to check when checking for texture completeness. The code is refactored so that the "q" value in spec, that is the maximum mipmap level for determining completeness, can be queried from TextureState. This value is used now for checking completeness. This requires moving ImageDescs under TextureState. Functions that operate on the ImageDesc array are also moved from Texture to TextureState. TextureState members are also renamed to start with the "m" prefix and made private. Also handle levels outside the base/max level range consistently in eglCreateImageKHR validation. We interpret the spec so that if the level used for the EGL image is not a part of the texture levels that affect texture completeness, an error is generated. BUG=angleproject:596 TEST=angle_end2end_tests Change-Id: I038ef24aa83e0a6905ca3c0bbada5989eecb00d9 Reviewed-on: https://chromium-review.googlesource.com/344880Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent b66a9097
......@@ -437,7 +437,8 @@ GLenum Framebuffer::checkStatus(const ContextState &data) const
// TODO(jmadill): Check if OpenGL ES2 drivers enforce cube completeness.
const Texture *texture = colorAttachment.getTexture();
ASSERT(texture);
if (texture->getTarget() == GL_TEXTURE_CUBE_MAP && !texture->isCubeComplete())
if (texture->getTarget() == GL_TEXTURE_CUBE_MAP &&
!texture->getTextureState().isCubeComplete())
{
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
......
......@@ -55,51 +55,430 @@ bool IsMipmapFiltered(const gl::SamplerState &samplerState)
}
}
TextureState::TextureState(GLenum target)
: target(target),
swizzleRed(GL_RED),
swizzleGreen(GL_GREEN),
swizzleBlue(GL_BLUE),
swizzleAlpha(GL_ALPHA),
samplerState(SamplerState::CreateDefaultForTarget(target)),
baseLevel(0),
maxLevel(1000),
immutableFormat(false),
immutableLevels(0),
usage(GL_NONE)
SwizzleState::SwizzleState()
: swizzleRed(GL_INVALID_INDEX),
swizzleGreen(GL_INVALID_INDEX),
swizzleBlue(GL_INVALID_INDEX),
swizzleAlpha(GL_INVALID_INDEX)
{
}
bool TextureState::swizzleRequired() const
SwizzleState::SwizzleState(GLenum red, GLenum green, GLenum blue, GLenum alpha)
: swizzleRed(red), swizzleGreen(green), swizzleBlue(blue), swizzleAlpha(alpha)
{
}
bool SwizzleState::swizzleRequired() const
{
return swizzleRed != GL_RED || swizzleGreen != GL_GREEN || swizzleBlue != GL_BLUE ||
swizzleAlpha != GL_ALPHA;
}
bool SwizzleState::operator==(const SwizzleState &other) const
{
return swizzleRed == other.swizzleRed && swizzleGreen == other.swizzleGreen &&
swizzleBlue == other.swizzleBlue && swizzleAlpha == other.swizzleAlpha;
}
bool SwizzleState::operator!=(const SwizzleState &other) const
{
return !(*this == other);
}
TextureState::TextureState(GLenum target)
: mTarget(target),
mSwizzleState(GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA),
mSamplerState(SamplerState::CreateDefaultForTarget(target)),
mBaseLevel(0),
mMaxLevel(1000),
mImmutableFormat(false),
mImmutableLevels(0),
mUsage(GL_NONE),
mImageDescs((IMPLEMENTATION_MAX_TEXTURE_LEVELS + 1) *
(target == GL_TEXTURE_CUBE_MAP ? 6 : 1)),
mCompletenessCache()
{
}
bool TextureState::swizzleRequired() const
{
return mSwizzleState.swizzleRequired();
}
GLuint TextureState::getEffectiveBaseLevel() const
{
if (immutableFormat)
if (mImmutableFormat)
{
// GLES 3.0.4 section 3.8.10
return std::min(baseLevel, immutableLevels - 1);
return std::min(mBaseLevel, mImmutableLevels - 1);
}
// Some classes use the effective base level to index arrays with level data. By clamping the
// effective base level to max levels these arrays need just one extra item to store properties
// that should be returned for all out-of-range base level values, instead of needing special
// handling for out-of-range base levels.
return std::min(baseLevel, static_cast<GLuint>(gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS));
return std::min(mBaseLevel, static_cast<GLuint>(gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS));
}
GLuint TextureState::getEffectiveMaxLevel() const
{
if (immutableFormat)
if (mImmutableFormat)
{
// GLES 3.0.4 section 3.8.10
GLuint clampedMaxLevel = std::max(maxLevel, getEffectiveBaseLevel());
clampedMaxLevel = std::min(clampedMaxLevel, immutableLevels - 1);
GLuint clampedMaxLevel = std::max(mMaxLevel, getEffectiveBaseLevel());
clampedMaxLevel = std::min(clampedMaxLevel, mImmutableLevels - 1);
return clampedMaxLevel;
}
return maxLevel;
return mMaxLevel;
}
size_t TextureState::getMipmapMaxLevel() const
{
const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel());
size_t expectedMipLevels = 0;
if (mTarget == GL_TEXTURE_3D)
{
const int maxDim = std::max(std::max(baseImageDesc.size.width, baseImageDesc.size.height),
baseImageDesc.size.depth);
expectedMipLevels = log2(maxDim);
}
else
{
expectedMipLevels = log2(std::max(baseImageDesc.size.width, baseImageDesc.size.height));
}
return std::min<size_t>(getEffectiveBaseLevel() + expectedMipLevels, getEffectiveMaxLevel());
}
bool TextureState::setBaseLevel(GLuint baseLevel)
{
if (mBaseLevel != baseLevel)
{
mBaseLevel = baseLevel;
mCompletenessCache.cacheValid = false;
return true;
}
return false;
}
void TextureState::setMaxLevel(GLuint maxLevel)
{
if (mMaxLevel != maxLevel)
{
mMaxLevel = maxLevel;
mCompletenessCache.cacheValid = false;
}
}
// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
bool TextureState::isCubeComplete() const
{
ASSERT(mTarget == GL_TEXTURE_CUBE_MAP);
const ImageDesc &baseImageDesc = getImageDesc(FirstCubeMapTextureTarget, 0);
if (baseImageDesc.size.width == 0 || baseImageDesc.size.width != baseImageDesc.size.height)
{
return false;
}
for (GLenum face = FirstCubeMapTextureTarget + 1; face <= LastCubeMapTextureTarget; face++)
{
const ImageDesc &faceImageDesc = getImageDesc(face, 0);
if (faceImageDesc.size.width != baseImageDesc.size.width ||
faceImageDesc.size.height != baseImageDesc.size.height ||
faceImageDesc.internalFormat != baseImageDesc.internalFormat)
{
return false;
}
}
return true;
}
bool TextureState::isSamplerComplete(const SamplerState &samplerState,
const ContextState &data) const
{
const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel());
const TextureCaps &textureCaps = data.textureCaps->get(baseImageDesc.internalFormat);
if (!mCompletenessCache.cacheValid || mCompletenessCache.samplerState != samplerState ||
mCompletenessCache.filterable != textureCaps.filterable ||
mCompletenessCache.clientVersion != data.clientVersion ||
mCompletenessCache.supportsNPOT != data.extensions->textureNPOT)
{
mCompletenessCache.cacheValid = true;
mCompletenessCache.samplerState = samplerState;
mCompletenessCache.filterable = textureCaps.filterable;
mCompletenessCache.clientVersion = data.clientVersion;
mCompletenessCache.supportsNPOT = data.extensions->textureNPOT;
mCompletenessCache.samplerComplete = computeSamplerCompleteness(samplerState, data);
}
return mCompletenessCache.samplerComplete;
}
bool TextureState::computeSamplerCompleteness(const SamplerState &samplerState,
const ContextState &data) const
{
if (mBaseLevel > mMaxLevel)
{
return false;
}
const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel());
if (baseImageDesc.size.width == 0 || baseImageDesc.size.height == 0 ||
baseImageDesc.size.depth == 0)
{
return false;
}
// The cases where the texture is incomplete because base level is out of range should be
// handled by the above condition.
ASSERT(mBaseLevel < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS || mImmutableFormat);
if (mTarget == GL_TEXTURE_CUBE_MAP && baseImageDesc.size.width != baseImageDesc.size.height)
{
return false;
}
const TextureCaps &textureCaps = data.textureCaps->get(baseImageDesc.internalFormat);
if (!textureCaps.filterable && !IsPointSampled(samplerState))
{
return false;
}
bool npotSupport = data.extensions->textureNPOT || data.clientVersion >= 3;
if (!npotSupport)
{
if ((samplerState.wrapS != GL_CLAMP_TO_EDGE && !gl::isPow2(baseImageDesc.size.width)) ||
(samplerState.wrapT != GL_CLAMP_TO_EDGE && !gl::isPow2(baseImageDesc.size.height)))
{
return false;
}
}
if (IsMipmapFiltered(samplerState))
{
if (!npotSupport)
{
if (!gl::isPow2(baseImageDesc.size.width) || !gl::isPow2(baseImageDesc.size.height))
{
return false;
}
}
if (!computeMipmapCompleteness())
{
return false;
}
}
else
{
if (mTarget == GL_TEXTURE_CUBE_MAP && !isCubeComplete())
{
return false;
}
}
// From GL_OES_EGL_image_external_essl3: If state is present in a sampler object bound to a
// texture unit that would have been rejected by a call to TexParameter* for the texture bound
// to that unit, the behavior of the implementation is as if the texture were incomplete. For
// example, if TEXTURE_WRAP_S or TEXTURE_WRAP_T is set to anything but CLAMP_TO_EDGE on the
// sampler object bound to a texture unit and the texture bound to that unit is an external
// texture, the texture will be considered incomplete.
// Sampler object state which does not affect sampling for the type of texture bound to a
// texture unit, such as TEXTURE_WRAP_R for an external texture, does not affect completeness.
if (mTarget == GL_TEXTURE_EXTERNAL_OES)
{
if (samplerState.wrapS != GL_CLAMP_TO_EDGE || samplerState.wrapT != GL_CLAMP_TO_EDGE)
{
return false;
}
if (samplerState.minFilter != GL_LINEAR && samplerState.minFilter != GL_NEAREST)
{
return false;
}
}
// OpenGLES 3.0.2 spec section 3.8.13 states that a texture is not mipmap complete if:
// The internalformat specified for the texture arrays is a sized internal depth or
// depth and stencil format (see table 3.13), the value of TEXTURE_COMPARE_-
// MODE is NONE, and either the magnification filter is not NEAREST or the mini-
// fication filter is neither NEAREST nor NEAREST_MIPMAP_NEAREST.
const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(baseImageDesc.internalFormat);
if (formatInfo.depthBits > 0 && data.clientVersion > 2)
{
if (samplerState.compareMode == GL_NONE)
{
if ((samplerState.minFilter != GL_NEAREST &&
samplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST) ||
samplerState.magFilter != GL_NEAREST)
{
return false;
}
}
}
return true;
}
bool TextureState::computeMipmapCompleteness() const
{
const size_t maxLevel = getMipmapMaxLevel();
for (size_t level = getEffectiveBaseLevel(); level <= maxLevel; level++)
{
if (mTarget == GL_TEXTURE_CUBE_MAP)
{
for (GLenum face = FirstCubeMapTextureTarget; face <= LastCubeMapTextureTarget; face++)
{
if (!computeLevelCompleteness(face, level))
{
return false;
}
}
}
else
{
if (!computeLevelCompleteness(mTarget, level))
{
return false;
}
}
}
return true;
}
bool TextureState::computeLevelCompleteness(GLenum target, size_t level) const
{
ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
if (mImmutableFormat)
{
return true;
}
const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel());
if (baseImageDesc.size.width == 0 || baseImageDesc.size.height == 0 ||
baseImageDesc.size.depth == 0)
{
return false;
}
const ImageDesc &levelImageDesc = getImageDesc(target, level);
if (levelImageDesc.size.width == 0 || levelImageDesc.size.height == 0 ||
levelImageDesc.size.depth == 0)
{
return false;
}
if (levelImageDesc.internalFormat != baseImageDesc.internalFormat)
{
return false;
}
ASSERT(level >= getEffectiveBaseLevel());
const size_t relativeLevel = level - getEffectiveBaseLevel();
if (levelImageDesc.size.width != std::max(1, baseImageDesc.size.width >> relativeLevel))
{
return false;
}
if (levelImageDesc.size.height != std::max(1, baseImageDesc.size.height >> relativeLevel))
{
return false;
}
if (mTarget == GL_TEXTURE_3D)
{
if (levelImageDesc.size.depth != std::max(1, baseImageDesc.size.depth >> relativeLevel))
{
return false;
}
}
else if (mTarget == GL_TEXTURE_2D_ARRAY)
{
if (levelImageDesc.size.depth != baseImageDesc.size.depth)
{
return false;
}
}
return true;
}
GLenum TextureState::getBaseImageTarget() const
{
return mTarget == GL_TEXTURE_CUBE_MAP ? FirstCubeMapTextureTarget : mTarget;
}
ImageDesc::ImageDesc() : ImageDesc(Extents(0, 0, 0), GL_NONE)
{
}
ImageDesc::ImageDesc(const Extents &size, GLenum internalFormat)
: size(size), internalFormat(internalFormat)
{
}
const ImageDesc &TextureState::getImageDesc(GLenum target, size_t level) const
{
size_t descIndex = GetImageDescIndex(target, level);
ASSERT(descIndex < mImageDescs.size());
return mImageDescs[descIndex];
}
void TextureState::setImageDesc(GLenum target, size_t level, const ImageDesc &desc)
{
size_t descIndex = GetImageDescIndex(target, level);
ASSERT(descIndex < mImageDescs.size());
mImageDescs[descIndex] = desc;
mCompletenessCache.cacheValid = false;
}
void TextureState::setImageDescChain(size_t levels, Extents baseSize, GLenum sizedInternalFormat)
{
for (int level = 0; level < static_cast<int>(levels); level++)
{
Extents levelSize(
std::max<int>(baseSize.width >> level, 1), std::max<int>(baseSize.height >> level, 1),
(mTarget == GL_TEXTURE_2D_ARRAY) ? baseSize.depth
: std::max<int>(baseSize.depth >> level, 1));
ImageDesc levelInfo(levelSize, sizedInternalFormat);
if (mTarget == GL_TEXTURE_CUBE_MAP)
{
for (GLenum face = FirstCubeMapTextureTarget; face <= LastCubeMapTextureTarget; face++)
{
setImageDesc(face, level, levelInfo);
}
}
else
{
setImageDesc(mTarget, level, levelInfo);
}
}
}
void TextureState::clearImageDesc(GLenum target, size_t level)
{
setImageDesc(target, level, ImageDesc());
}
void TextureState::clearImageDescs()
{
for (size_t descIndex = 0; descIndex < mImageDescs.size(); descIndex++)
{
mImageDescs[descIndex] = ImageDesc();
}
mCompletenessCache.cacheValid = false;
}
TextureState::SamplerCompletenessCache::SamplerCompletenessCache()
: cacheValid(false),
samplerState(),
filterable(false),
clientVersion(0),
supportsNPOT(false),
samplerComplete(false)
{
}
Texture::Texture(rx::GLImplFactory *factory, GLuint id, GLenum target)
......@@ -107,9 +486,6 @@ Texture::Texture(rx::GLImplFactory *factory, GLuint id, GLenum target)
mState(target),
mTexture(factory->createTexture(mState)),
mLabel(),
mImageDescs((IMPLEMENTATION_MAX_TEXTURE_LEVELS + 1) *
(target == GL_TEXTURE_CUBE_MAP ? 6 : 1)),
mCompletenessCache(),
mBoundSurface(nullptr),
mBoundStream(nullptr)
{
......@@ -142,202 +518,195 @@ const std::string &Texture::getLabel() const
GLenum Texture::getTarget() const
{
return mState.target;
return mState.mTarget;
}
void Texture::setSwizzleRed(GLenum swizzleRed)
{
mState.swizzleRed = swizzleRed;
mState.mSwizzleState.swizzleRed = swizzleRed;
}
GLenum Texture::getSwizzleRed() const
{
return mState.swizzleRed;
return mState.mSwizzleState.swizzleRed;
}
void Texture::setSwizzleGreen(GLenum swizzleGreen)
{
mState.swizzleGreen = swizzleGreen;
mState.mSwizzleState.swizzleGreen = swizzleGreen;
}
GLenum Texture::getSwizzleGreen() const
{
return mState.swizzleGreen;
return mState.mSwizzleState.swizzleGreen;
}
void Texture::setSwizzleBlue(GLenum swizzleBlue)
{
mState.swizzleBlue = swizzleBlue;
mState.mSwizzleState.swizzleBlue = swizzleBlue;
}
GLenum Texture::getSwizzleBlue() const
{
return mState.swizzleBlue;
return mState.mSwizzleState.swizzleBlue;
}
void Texture::setSwizzleAlpha(GLenum swizzleAlpha)
{
mState.swizzleAlpha = swizzleAlpha;
mState.mSwizzleState.swizzleAlpha = swizzleAlpha;
}
GLenum Texture::getSwizzleAlpha() const
{
return mState.swizzleAlpha;
return mState.mSwizzleState.swizzleAlpha;
}
void Texture::setMinFilter(GLenum minFilter)
{
mState.samplerState.minFilter = minFilter;
mState.mSamplerState.minFilter = minFilter;
}
GLenum Texture::getMinFilter() const
{
return mState.samplerState.minFilter;
return mState.mSamplerState.minFilter;
}
void Texture::setMagFilter(GLenum magFilter)
{
mState.samplerState.magFilter = magFilter;
mState.mSamplerState.magFilter = magFilter;
}
GLenum Texture::getMagFilter() const
{
return mState.samplerState.magFilter;
return mState.mSamplerState.magFilter;
}
void Texture::setWrapS(GLenum wrapS)
{
mState.samplerState.wrapS = wrapS;
mState.mSamplerState.wrapS = wrapS;
}
GLenum Texture::getWrapS() const
{
return mState.samplerState.wrapS;
return mState.mSamplerState.wrapS;
}
void Texture::setWrapT(GLenum wrapT)
{
mState.samplerState.wrapT = wrapT;
mState.mSamplerState.wrapT = wrapT;
}
GLenum Texture::getWrapT() const
{
return mState.samplerState.wrapT;
return mState.mSamplerState.wrapT;
}
void Texture::setWrapR(GLenum wrapR)
{
mState.samplerState.wrapR = wrapR;
mState.mSamplerState.wrapR = wrapR;
}
GLenum Texture::getWrapR() const
{
return mState.samplerState.wrapR;
return mState.mSamplerState.wrapR;
}
void Texture::setMaxAnisotropy(float maxAnisotropy)
{
mState.samplerState.maxAnisotropy = maxAnisotropy;
mState.mSamplerState.maxAnisotropy = maxAnisotropy;
}
float Texture::getMaxAnisotropy() const
{
return mState.samplerState.maxAnisotropy;
return mState.mSamplerState.maxAnisotropy;
}
void Texture::setMinLod(GLfloat minLod)
{
mState.samplerState.minLod = minLod;
mState.mSamplerState.minLod = minLod;
}
GLfloat Texture::getMinLod() const
{
return mState.samplerState.minLod;
return mState.mSamplerState.minLod;
}
void Texture::setMaxLod(GLfloat maxLod)
{
mState.samplerState.maxLod = maxLod;
mState.mSamplerState.maxLod = maxLod;
}
GLfloat Texture::getMaxLod() const
{
return mState.samplerState.maxLod;
return mState.mSamplerState.maxLod;
}
void Texture::setCompareMode(GLenum compareMode)
{
mState.samplerState.compareMode = compareMode;
mState.mSamplerState.compareMode = compareMode;
}
GLenum Texture::getCompareMode() const
{
return mState.samplerState.compareMode;
return mState.mSamplerState.compareMode;
}
void Texture::setCompareFunc(GLenum compareFunc)
{
mState.samplerState.compareFunc = compareFunc;
mState.mSamplerState.compareFunc = compareFunc;
}
GLenum Texture::getCompareFunc() const
{
return mState.samplerState.compareFunc;
return mState.mSamplerState.compareFunc;
}
const SamplerState &Texture::getSamplerState() const
{
return mState.samplerState;
return mState.mSamplerState;
}
void Texture::setBaseLevel(GLuint baseLevel)
{
if (mState.baseLevel != baseLevel)
if (mState.setBaseLevel(baseLevel))
{
mState.baseLevel = baseLevel;
mCompletenessCache.cacheValid = false;
mTexture->setBaseLevel(mState.getEffectiveBaseLevel());
}
}
GLuint Texture::getBaseLevel() const
{
return mState.baseLevel;
return mState.mBaseLevel;
}
void Texture::setMaxLevel(GLuint maxLevel)
{
if (mState.maxLevel != maxLevel)
{
mState.maxLevel = maxLevel;
mCompletenessCache.cacheValid = false;
}
mState.setMaxLevel(maxLevel);
}
GLuint Texture::getMaxLevel() const
{
return mState.maxLevel;
return mState.mMaxLevel;
}
bool Texture::getImmutableFormat() const
{
return mState.immutableFormat;
return mState.mImmutableFormat;
}
GLuint Texture::getImmutableLevels() const
{
return mState.immutableLevels;
return mState.mImmutableLevels;
}
void Texture::setUsage(GLenum usage)
{
mState.usage = usage;
getImplementation()->setUsage(usage);
mState.mUsage = usage;
}
GLenum Texture::getUsage() const
{
return mState.usage;
return mState.mUsage;
}
const TextureState &Texture::getTextureState() const
......@@ -347,96 +716,35 @@ const TextureState &Texture::getTextureState() const
size_t Texture::getWidth(GLenum target, size_t level) const
{
ASSERT(target == mState.target ||
(mState.target == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
return getImageDesc(target, level).size.width;
}
size_t Texture::getHeight(GLenum target, size_t level) const
{
ASSERT(target == mState.target ||
(mState.target == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
return getImageDesc(target, level).size.height;
ASSERT(target == mState.mTarget ||
(mState.mTarget == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
return mState.getImageDesc(target, level).size.width;
}
size_t Texture::getDepth(GLenum target, size_t level) const
{
ASSERT(target == mState.target ||
(mState.target == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
return getImageDesc(target, level).size.depth;
}
GLenum Texture::getInternalFormat(GLenum target, size_t level) const
{
ASSERT(target == mState.target ||
(mState.target == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
return getImageDesc(target, level).internalFormat;
}
bool Texture::isSamplerComplete(const SamplerState &samplerState, const ContextState &data) const
{
const ImageDesc &baseImageDesc =
getImageDesc(getBaseImageTarget(), mState.getEffectiveBaseLevel());
const TextureCaps &textureCaps = data.textureCaps->get(baseImageDesc.internalFormat);
if (!mCompletenessCache.cacheValid ||
mCompletenessCache.samplerState != samplerState ||
mCompletenessCache.filterable != textureCaps.filterable ||
mCompletenessCache.clientVersion != data.clientVersion ||
mCompletenessCache.supportsNPOT != data.extensions->textureNPOT)
{
mCompletenessCache.cacheValid = true;
mCompletenessCache.samplerState = samplerState;
mCompletenessCache.filterable = textureCaps.filterable;
mCompletenessCache.clientVersion = data.clientVersion;
mCompletenessCache.supportsNPOT = data.extensions->textureNPOT;
mCompletenessCache.samplerComplete = computeSamplerCompleteness(samplerState, data);
}
return mCompletenessCache.samplerComplete;
}
bool Texture::isMipmapComplete() const
{
return computeMipmapCompleteness();
}
// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
bool Texture::isCubeComplete() const
{
ASSERT(mState.target == GL_TEXTURE_CUBE_MAP);
const ImageDesc &baseImageDesc = getImageDesc(FirstCubeMapTextureTarget, 0);
if (baseImageDesc.size.width == 0 || baseImageDesc.size.width != baseImageDesc.size.height)
{
return false;
}
size_t Texture::getHeight(GLenum target, size_t level) const
{
ASSERT(target == mState.mTarget ||
(mState.mTarget == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
return mState.getImageDesc(target, level).size.height;
}
for (GLenum face = FirstCubeMapTextureTarget + 1; face <= LastCubeMapTextureTarget; face++)
{
const ImageDesc &faceImageDesc = getImageDesc(face, 0);
if (faceImageDesc.size.width != baseImageDesc.size.width ||
faceImageDesc.size.height != baseImageDesc.size.height ||
faceImageDesc.internalFormat != baseImageDesc.internalFormat)
{
return false;
}
}
size_t Texture::getDepth(GLenum target, size_t level) const
{
ASSERT(target == mState.mTarget ||
(mState.mTarget == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
return mState.getImageDesc(target, level).size.depth;
}
return true;
GLenum Texture::getInternalFormat(GLenum target, size_t level) const
{
ASSERT(target == mState.mTarget ||
(mState.mTarget == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
return mState.getImageDesc(target, level).internalFormat;
}
size_t Texture::getMipCompleteLevels() const
bool Texture::isMipmapComplete() const
{
const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), 0);
if (mState.target == GL_TEXTURE_3D)
{
const int maxDim = std::max(std::max(baseImageDesc.size.width, baseImageDesc.size.height),
baseImageDesc.size.depth);
return log2(maxDim) + 1;
}
else
{
return log2(std::max(baseImageDesc.size.width, baseImageDesc.size.height)) + 1;
}
return mState.computeMipmapCompleteness();
}
egl::Surface *Texture::getBoundSurface() const
......@@ -458,8 +766,8 @@ Error Texture::setImage(const PixelUnpackState &unpackState,
GLenum type,
const uint8_t *pixels)
{
ASSERT(target == mState.target ||
(mState.target == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
ASSERT(target == mState.mTarget ||
(mState.mTarget == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
// Release from previous calls to eglBindTexImage, to avoid calling the Impl after
releaseTexImageInternal();
......@@ -472,7 +780,8 @@ Error Texture::setImage(const PixelUnpackState &unpackState,
return error;
}
setImageDesc(target, level, ImageDesc(size, GetSizedInternalFormat(internalFormat, type)));
mState.setImageDesc(target, level,
ImageDesc(size, GetSizedInternalFormat(internalFormat, type)));
return Error(GL_NO_ERROR);
}
......@@ -485,8 +794,8 @@ Error Texture::setSubImage(const PixelUnpackState &unpackState,
GLenum type,
const uint8_t *pixels)
{
ASSERT(target == mState.target ||
(mState.target == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
ASSERT(target == mState.mTarget ||
(mState.mTarget == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
return mTexture->setSubImage(target, level, area, format, type, unpackState, pixels);
}
......@@ -498,8 +807,8 @@ Error Texture::setCompressedImage(const PixelUnpackState &unpackState,
size_t imageSize,
const uint8_t *pixels)
{
ASSERT(target == mState.target ||
(mState.target == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
ASSERT(target == mState.mTarget ||
(mState.mTarget == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
// Release from previous calls to eglBindTexImage, to avoid calling the Impl after
releaseTexImageInternal();
......@@ -512,7 +821,8 @@ Error Texture::setCompressedImage(const PixelUnpackState &unpackState,
return error;
}
setImageDesc(target, level, ImageDesc(size, GetSizedInternalFormat(internalFormat, GL_UNSIGNED_BYTE)));
mState.setImageDesc(target, level,
ImageDesc(size, GetSizedInternalFormat(internalFormat, GL_UNSIGNED_BYTE)));
return Error(GL_NO_ERROR);
}
......@@ -525,8 +835,8 @@ Error Texture::setCompressedSubImage(const PixelUnpackState &unpackState,
size_t imageSize,
const uint8_t *pixels)
{
ASSERT(target == mState.target ||
(mState.target == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
ASSERT(target == mState.mTarget ||
(mState.mTarget == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
return mTexture->setCompressedSubImage(target, level, area, format, unpackState, imageSize,
pixels);
......@@ -535,8 +845,8 @@ Error Texture::setCompressedSubImage(const PixelUnpackState &unpackState,
Error Texture::copyImage(GLenum target, size_t level, const Rectangle &sourceArea, GLenum internalFormat,
const Framebuffer *source)
{
ASSERT(target == mState.target ||
(mState.target == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
ASSERT(target == mState.mTarget ||
(mState.mTarget == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
// Release from previous calls to eglBindTexImage, to avoid calling the Impl after
releaseTexImageInternal();
......@@ -548,7 +858,8 @@ Error Texture::copyImage(GLenum target, size_t level, const Rectangle &sourceAre
return error;
}
setImageDesc(target, level, ImageDesc(Extents(sourceArea.width, sourceArea.height, 1),
mState.setImageDesc(target, level,
ImageDesc(Extents(sourceArea.width, sourceArea.height, 1),
GetSizedInternalFormat(internalFormat, GL_UNSIGNED_BYTE)));
return Error(GL_NO_ERROR);
......@@ -557,15 +868,15 @@ Error Texture::copyImage(GLenum target, size_t level, const Rectangle &sourceAre
Error Texture::copySubImage(GLenum target, size_t level, const Offset &destOffset, const Rectangle &sourceArea,
const Framebuffer *source)
{
ASSERT(target == mState.target ||
(mState.target == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
ASSERT(target == mState.mTarget ||
(mState.mTarget == GL_TEXTURE_CUBE_MAP && IsCubeMapTextureTarget(target)));
return mTexture->copySubImage(target, level, destOffset, sourceArea, source);
}
Error Texture::setStorage(GLenum target, size_t levels, GLenum internalFormat, const Extents &size)
{
ASSERT(target == mState.target);
ASSERT(target == mState.mTarget);
// Release from previous calls to eglBindTexImage, to avoid calling the Impl after
releaseTexImageInternal();
......@@ -577,10 +888,10 @@ Error Texture::setStorage(GLenum target, size_t levels, GLenum internalFormat, c
return error;
}
mState.immutableFormat = true;
mState.immutableLevels = static_cast<GLuint>(levels);
clearImageDescs();
setImageDescChain(levels, size, internalFormat);
mState.mImmutableFormat = true;
mState.mImmutableLevels = static_cast<GLuint>(levels);
mState.clearImageDescs();
mState.setImageDescChain(levels, size, internalFormat);
return Error(GL_NO_ERROR);
}
......@@ -603,77 +914,13 @@ Error Texture::generateMipmaps()
return error;
}
const ImageDesc &baseImageInfo = getImageDesc(getBaseImageTarget(), 0);
const ImageDesc &baseImageInfo = mState.getImageDesc(mState.getBaseImageTarget(), 0);
size_t mipLevels = log2(std::max(std::max(baseImageInfo.size.width, baseImageInfo.size.height), baseImageInfo.size.depth)) + 1;
setImageDescChain(mipLevels, baseImageInfo.size, baseImageInfo.internalFormat);
mState.setImageDescChain(mipLevels, baseImageInfo.size, baseImageInfo.internalFormat);
return Error(GL_NO_ERROR);
}
void Texture::setImageDescChain(size_t levels, Extents baseSize, GLenum sizedInternalFormat)
{
for (int level = 0; level < static_cast<int>(levels); level++)
{
Extents levelSize(
std::max<int>(baseSize.width >> level, 1), std::max<int>(baseSize.height >> level, 1),
(mState.target == GL_TEXTURE_2D_ARRAY) ? baseSize.depth
: std::max<int>(baseSize.depth >> level, 1));
ImageDesc levelInfo(levelSize, sizedInternalFormat);
if (mState.target == GL_TEXTURE_CUBE_MAP)
{
for (GLenum face = FirstCubeMapTextureTarget; face <= LastCubeMapTextureTarget; face++)
{
setImageDesc(face, level, levelInfo);
}
}
else
{
setImageDesc(mState.target, level, levelInfo);
}
}
}
Texture::ImageDesc::ImageDesc()
: ImageDesc(Extents(0, 0, 0), GL_NONE)
{
}
Texture::ImageDesc::ImageDesc(const Extents &size, GLenum internalFormat)
: size(size),
internalFormat(internalFormat)
{
}
const Texture::ImageDesc &Texture::getImageDesc(GLenum target, size_t level) const
{
size_t descIndex = GetImageDescIndex(target, level);
ASSERT(descIndex < mImageDescs.size());
return mImageDescs[descIndex];
}
void Texture::setImageDesc(GLenum target, size_t level, const ImageDesc &desc)
{
size_t descIndex = GetImageDescIndex(target, level);
ASSERT(descIndex < mImageDescs.size());
mImageDescs[descIndex] = desc;
mCompletenessCache.cacheValid = false;
}
void Texture::clearImageDesc(GLenum target, size_t level)
{
setImageDesc(target, level, ImageDesc());
}
void Texture::clearImageDescs()
{
for (size_t descIndex = 0; descIndex < mImageDescs.size(); descIndex++)
{
mImageDescs[descIndex] = ImageDesc();
}
mCompletenessCache.cacheValid = false;
}
void Texture::bindTexImageFromSurface(egl::Surface *surface)
{
ASSERT(surface);
......@@ -687,10 +934,10 @@ void Texture::bindTexImageFromSurface(egl::Surface *surface)
mBoundSurface = surface;
// Set the image info to the size and format of the surface
ASSERT(mState.target == GL_TEXTURE_2D);
ASSERT(mState.mTarget == GL_TEXTURE_2D);
Extents size(surface->getWidth(), surface->getHeight(), 1);
ImageDesc desc(size, surface->getConfig()->renderTargetFormat);
setImageDesc(mState.target, 0, desc);
mState.setImageDesc(mState.mTarget, 0, desc);
}
void Texture::releaseTexImageFromSurface()
......@@ -700,8 +947,8 @@ void Texture::releaseTexImageFromSurface()
mTexture->releaseTexImage();
// Erase the image info for level 0
ASSERT(mState.target == GL_TEXTURE_2D);
clearImageDesc(mState.target, 0);
ASSERT(mState.mTarget == GL_TEXTURE_2D);
mState.clearImageDesc(mState.mTarget, 0);
}
void Texture::bindStream(egl::Stream *stream)
......@@ -713,7 +960,7 @@ void Texture::bindStream(egl::Stream *stream)
mBoundStream = stream;
ASSERT(mState.target == GL_TEXTURE_EXTERNAL_OES);
ASSERT(mState.mTarget == GL_TEXTURE_EXTERNAL_OES);
}
void Texture::releaseStream()
......@@ -725,19 +972,19 @@ void Texture::releaseStream()
void Texture::acquireImageFromStream(const egl::Stream::GLTextureDescription &desc)
{
ASSERT(mBoundStream != nullptr);
mTexture->setImageExternal(mState.target, mBoundStream, desc);
mTexture->setImageExternal(mState.mTarget, mBoundStream, desc);
Extents size(desc.width, desc.height, 1);
setImageDesc(mState.target, 0, ImageDesc(size, desc.internalFormat));
mState.setImageDesc(mState.mTarget, 0, ImageDesc(size, desc.internalFormat));
}
void Texture::releaseImageFromStream()
{
ASSERT(mBoundStream != nullptr);
mTexture->setImageExternal(mState.target, nullptr, egl::Stream::GLTextureDescription());
mTexture->setImageExternal(mState.mTarget, nullptr, egl::Stream::GLTextureDescription());
// Set to incomplete
clearImageDesc(mState.target, 0);
mState.clearImageDesc(mState.mTarget, 0);
}
void Texture::releaseTexImageInternal()
......@@ -754,7 +1001,7 @@ void Texture::releaseTexImageInternal()
Error Texture::setEGLImageTarget(GLenum target, egl::Image *imageTarget)
{
ASSERT(target == mState.target);
ASSERT(target == mState.mTarget);
ASSERT(target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES);
// Release from previous calls to eglBindTexImage, to avoid calling the Impl after
......@@ -774,222 +1021,15 @@ Error Texture::setEGLImageTarget(GLenum target, egl::Image *imageTarget)
GLenum internalFormat = imageTarget->getInternalFormat();
GLenum type = GetInternalFormatInfo(internalFormat).type;
clearImageDescs();
setImageDesc(target, 0, ImageDesc(size, GetSizedInternalFormat(internalFormat, type)));
mState.clearImageDescs();
mState.setImageDesc(target, 0, ImageDesc(size, GetSizedInternalFormat(internalFormat, type)));
return Error(GL_NO_ERROR);
}
GLenum Texture::getBaseImageTarget() const
{
return mState.target == GL_TEXTURE_CUBE_MAP ? FirstCubeMapTextureTarget : mState.target;
}
bool Texture::computeSamplerCompleteness(const SamplerState &samplerState,
const ContextState &data) const
{
if (mState.baseLevel > mState.maxLevel)
{
return false;
}
const ImageDesc &baseImageDesc =
getImageDesc(getBaseImageTarget(), mState.getEffectiveBaseLevel());
if (baseImageDesc.size.width == 0 || baseImageDesc.size.height == 0 || baseImageDesc.size.depth == 0)
{
return false;
}
// The cases where the texture is incomplete because base level is out of range should be
// handled by the above condition.
ASSERT(mState.baseLevel < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS || mState.immutableFormat);
if (mState.target == GL_TEXTURE_CUBE_MAP &&
baseImageDesc.size.width != baseImageDesc.size.height)
{
return false;
}
const TextureCaps &textureCaps = data.textureCaps->get(baseImageDesc.internalFormat);
if (!textureCaps.filterable && !IsPointSampled(samplerState))
{
return false;
}
bool npotSupport = data.extensions->textureNPOT || data.clientVersion >= 3;
if (!npotSupport)
{
if ((samplerState.wrapS != GL_CLAMP_TO_EDGE && !gl::isPow2(baseImageDesc.size.width)) ||
(samplerState.wrapT != GL_CLAMP_TO_EDGE && !gl::isPow2(baseImageDesc.size.height)))
{
return false;
}
}
if (IsMipmapFiltered(samplerState))
{
if (!npotSupport)
{
if (!gl::isPow2(baseImageDesc.size.width) || !gl::isPow2(baseImageDesc.size.height))
{
return false;
}
}
if (!computeMipmapCompleteness())
{
return false;
}
}
else
{
if (mState.target == GL_TEXTURE_CUBE_MAP && !isCubeComplete())
{
return false;
}
}
// From GL_OES_EGL_image_external_essl3: If state is present in a sampler object bound to a
// texture unit that would have been rejected by a call to TexParameter* for the texture bound
// to that unit, the behavior of the implementation is as if the texture were incomplete. For
// example, if TEXTURE_WRAP_S or TEXTURE_WRAP_T is set to anything but CLAMP_TO_EDGE on the
// sampler object bound to a texture unit and the texture bound to that unit is an external
// texture, the texture will be considered incomplete.
// Sampler object state which does not affect sampling for the type of texture bound to a
// texture unit, such as TEXTURE_WRAP_R for an external texture, does not affect completeness.
if (mState.target == GL_TEXTURE_EXTERNAL_OES)
{
if (samplerState.wrapS != GL_CLAMP_TO_EDGE || samplerState.wrapT != GL_CLAMP_TO_EDGE)
{
return false;
}
if (samplerState.minFilter != GL_LINEAR && samplerState.minFilter != GL_NEAREST)
{
return false;
}
}
// OpenGLES 3.0.2 spec section 3.8.13 states that a texture is not mipmap complete if:
// The internalformat specified for the texture arrays is a sized internal depth or
// depth and stencil format (see table 3.13), the value of TEXTURE_COMPARE_-
// MODE is NONE, and either the magnification filter is not NEAREST or the mini-
// fication filter is neither NEAREST nor NEAREST_MIPMAP_NEAREST.
const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(baseImageDesc.internalFormat);
if (formatInfo.depthBits > 0 && data.clientVersion > 2)
{
if (samplerState.compareMode == GL_NONE)
{
if ((samplerState.minFilter != GL_NEAREST && samplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST) ||
samplerState.magFilter != GL_NEAREST)
{
return false;
}
}
}
return true;
}
bool Texture::computeMipmapCompleteness() const
{
size_t expectedMipLevels = getMipCompleteLevels();
size_t maxLevel = std::min<size_t>(expectedMipLevels, mState.maxLevel + 1);
for (size_t level = mState.getEffectiveBaseLevel(); level < maxLevel; level++)
{
if (mState.target == GL_TEXTURE_CUBE_MAP)
{
for (GLenum face = FirstCubeMapTextureTarget; face <= LastCubeMapTextureTarget; face++)
{
if (!computeLevelCompleteness(face, level))
{
return false;
}
}
}
else
{
if (!computeLevelCompleteness(mState.target, level))
{
return false;
}
}
}
return true;
}
bool Texture::computeLevelCompleteness(GLenum target, size_t level) const
{
ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
if (mState.immutableFormat)
{
return true;
}
const ImageDesc &baseImageDesc =
getImageDesc(getBaseImageTarget(), mState.getEffectiveBaseLevel());
if (baseImageDesc.size.width == 0 || baseImageDesc.size.height == 0 || baseImageDesc.size.depth == 0)
{
return false;
}
const ImageDesc &levelImageDesc = getImageDesc(target, level);
if (levelImageDesc.size.width == 0 || levelImageDesc.size.height == 0 ||
levelImageDesc.size.depth == 0)
{
return false;
}
if (levelImageDesc.internalFormat != baseImageDesc.internalFormat)
{
return false;
}
ASSERT(level >= mState.getEffectiveBaseLevel());
const size_t relativeLevel = level - mState.getEffectiveBaseLevel();
if (levelImageDesc.size.width != std::max(1, baseImageDesc.size.width >> relativeLevel))
{
return false;
}
if (levelImageDesc.size.height != std::max(1, baseImageDesc.size.height >> relativeLevel))
{
return false;
}
if (mState.target == GL_TEXTURE_3D)
{
if (levelImageDesc.size.depth != std::max(1, baseImageDesc.size.depth >> relativeLevel))
{
return false;
}
}
else if (mState.target == GL_TEXTURE_2D_ARRAY)
{
if (levelImageDesc.size.depth != baseImageDesc.size.depth)
{
return false;
}
}
return true;
}
Texture::SamplerCompletenessCache::SamplerCompletenessCache()
: cacheValid(false),
samplerState(),
filterable(false),
clientVersion(0),
supportsNPOT(false),
samplerComplete(false)
{
}
Extents Texture::getAttachmentSize(const gl::FramebufferAttachment::Target &target) const
{
return getImageDesc(target.textureIndex().type, target.textureIndex().mipIndex).size;
return mState.getImageDesc(target.textureIndex().type, target.textureIndex().mipIndex).size;
}
GLenum Texture::getAttachmentInternalFormat(const gl::FramebufferAttachment::Target &target) const
......
......@@ -33,15 +33,48 @@ namespace rx
{
class GLImplFactory;
class TextureImpl;
class TextureGL;
class Renderer11;
}
namespace gl
{
struct ContextState;
class Framebuffer;
class Texture;
bool IsMipmapFiltered(const SamplerState &samplerState);
struct ImageDesc final
{
ImageDesc();
ImageDesc(const Extents &size, GLenum internalFormat);
ImageDesc(const ImageDesc &other) = default;
ImageDesc &operator=(const ImageDesc &other) = default;
Extents size;
GLenum internalFormat;
};
struct SwizzleState final
{
SwizzleState();
SwizzleState(GLenum red, GLenum green, GLenum blue, GLenum alpha);
SwizzleState(const SwizzleState &other) = default;
SwizzleState &operator=(const SwizzleState &other) = default;
bool swizzleRequired() const;
bool operator==(const SwizzleState &other) const;
bool operator!=(const SwizzleState &other) const;
GLenum swizzleRed;
GLenum swizzleGreen;
GLenum swizzleBlue;
GLenum swizzleAlpha;
};
// State from Table 6.9 (state per texture object) in the OpenGL ES 3.0.2 spec.
struct TextureState final : public angle::NonCopyable
{
......@@ -51,25 +84,79 @@ struct TextureState final : public angle::NonCopyable
GLuint getEffectiveBaseLevel() const;
GLuint getEffectiveMaxLevel() const;
// TODO(jmadill): Make the data members here private.
// Returns the value called "q" in the GLES 3.0.4 spec section 3.8.10.
size_t getMipmapMaxLevel() const;
const GLenum target;
// Returns true if base level changed.
bool setBaseLevel(GLuint baseLevel);
void setMaxLevel(GLuint maxLevel);
GLenum swizzleRed;
GLenum swizzleGreen;
GLenum swizzleBlue;
GLenum swizzleAlpha;
bool isCubeComplete() const;
bool isSamplerComplete(const SamplerState &samplerState, const ContextState &data) const;
SamplerState samplerState;
const ImageDesc &getImageDesc(GLenum target, size_t level) const;
GLenum getTarget() const { return mTarget; }
const SwizzleState &getSwizzleState() const { return mSwizzleState; }
const SamplerState &getSamplerState() const { return mSamplerState; }
GLenum getUsage() const { return mUsage; }
private:
// Texture needs access to the ImageDesc functions.
friend class Texture;
// TODO(jmadill): Remove TextureGL from friends.
friend class rx::TextureGL;
friend bool operator==(const TextureState &a, const TextureState &b);
GLuint baseLevel;
GLuint maxLevel;
// TODO(oetuaho): Remove Renderer11 from friends when GenerateMipmap is fixed.
friend class rx::Renderer11;
bool computeSamplerCompleteness(const SamplerState &samplerState,
const ContextState &data) const;
bool computeMipmapCompleteness() const;
bool computeLevelCompleteness(GLenum target, size_t level) const;
bool immutableFormat;
GLuint immutableLevels;
GLenum getBaseImageTarget() const;
void setImageDesc(GLenum target, size_t level, const ImageDesc &desc);
void setImageDescChain(size_t levels, Extents baseSize, GLenum sizedInternalFormat);
void clearImageDesc(GLenum target, size_t level);
void clearImageDescs();
const GLenum mTarget;
SwizzleState mSwizzleState;
SamplerState mSamplerState;
GLuint mBaseLevel;
GLuint mMaxLevel;
bool mImmutableFormat;
GLuint mImmutableLevels;
// From GL_ANGLE_texture_usage
GLenum usage;
GLenum mUsage;
std::vector<ImageDesc> mImageDescs;
struct SamplerCompletenessCache
{
SamplerCompletenessCache();
bool cacheValid;
// All values that affect sampler completeness that are not stored within
// the texture itself
SamplerState samplerState;
bool filterable;
GLint clientVersion;
bool supportsNPOT;
// Result of the sampler completeness with the above parameters
bool samplerComplete;
};
mutable SamplerCompletenessCache mCompletenessCache;
};
bool operator==(const TextureState &a, const TextureState &b);
......@@ -152,10 +239,7 @@ class Texture final : public egl::ImageSibling,
size_t getDepth(GLenum target, size_t level) const;
GLenum getInternalFormat(GLenum target, size_t level) const;
bool isSamplerComplete(const SamplerState &samplerState, const ContextState &data) const;
bool isMipmapComplete() const;
bool isCubeComplete() const;
size_t getMipCompleteLevels() const;
Error setImage(const PixelUnpackState &unpackState,
GLenum target,
......@@ -240,60 +324,18 @@ class Texture final : public egl::ImageSibling,
std::string mLabel;
struct ImageDesc
{
Extents size;
GLenum internalFormat;
ImageDesc();
ImageDesc(const Extents &size, GLenum internalFormat);
};
GLenum getBaseImageTarget() const;
bool computeSamplerCompleteness(const SamplerState &samplerState,
const ContextState &data) const;
bool computeMipmapCompleteness() const;
bool computeLevelCompleteness(GLenum target, size_t level) const;
const ImageDesc &getImageDesc(GLenum target, size_t level) const;
void setImageDesc(GLenum target, size_t level, const ImageDesc &desc);
void setImageDescChain(size_t levels, Extents baseSize, GLenum sizedInternalFormat);
void clearImageDesc(GLenum target, size_t level);
void clearImageDescs();
void releaseTexImageInternal();
std::vector<ImageDesc> mImageDescs;
struct SamplerCompletenessCache
{
SamplerCompletenessCache();
bool cacheValid;
// All values that affect sampler completeness that are not stored within
// the texture itself
SamplerState samplerState;
bool filterable;
GLint clientVersion;
bool supportsNPOT;
// Result of the sampler completeness with the above parameters
bool samplerComplete;
};
mutable SamplerCompletenessCache mCompletenessCache;
egl::Surface *mBoundSurface;
egl::Stream *mBoundStream;
};
inline bool operator==(const TextureState &a, const TextureState &b)
{
return a.swizzleRed == b.swizzleRed && a.swizzleGreen == b.swizzleGreen &&
a.swizzleBlue == b.swizzleBlue && a.swizzleAlpha == b.swizzleAlpha &&
a.samplerState == b.samplerState && a.baseLevel == b.baseLevel &&
a.maxLevel == b.maxLevel && a.immutableFormat == b.immutableFormat &&
a.immutableLevels == b.immutableLevels && a.usage == b.usage;
return a.mSwizzleState == b.mSwizzleState && a.mSamplerState == b.mSamplerState &&
a.mBaseLevel == b.mBaseLevel && a.mMaxLevel == b.mMaxLevel &&
a.mImmutableFormat == b.mImmutableFormat && a.mImmutableLevels == b.mImmutableLevels &&
a.mUsage == b.mUsage;
}
inline bool operator!=(const TextureState &a, const TextureState &b)
......
......@@ -110,6 +110,9 @@ struct Extents
Extents() : width(0), height(0), depth(0) { }
Extents(int width_, int height_, int depth_) : width(width_), height(height_), depth(depth_) { }
Extents(const Extents &other) = default;
Extents &operator=(const Extents &other) = default;
bool empty() const { return (width * height * depth) == 0; }
};
......
......@@ -44,8 +44,6 @@ class TextureImpl : public FramebufferAttachmentObjectImpl
TextureImpl(const gl::TextureState &state) : mState(state) {}
virtual ~TextureImpl() {}
virtual void setUsage(GLenum usage) = 0;
virtual gl::Error setImage(GLenum target, size_t level, GLenum internalFormat, const gl::Extents &size, GLenum format, GLenum type,
const gl::PixelUnpackState &unpack, const uint8_t *pixels) = 0;
virtual gl::Error setSubImage(GLenum target, size_t level, const gl::Box &area, GLenum format, GLenum type,
......
......@@ -21,7 +21,6 @@ class MockTextureImpl : public TextureImpl
public:
MockTextureImpl() : TextureImpl(gl::TextureState(GL_TEXTURE_2D)) {}
virtual ~MockTextureImpl() { destructor(); }
MOCK_METHOD1(setUsage, void(GLenum));
MOCK_METHOD8(setImage, gl::Error(GLenum, size_t, GLenum, const gl::Extents &, GLenum, GLenum, const gl::PixelUnpackState &, const uint8_t *));
MOCK_METHOD7(setSubImage, gl::Error(GLenum, size_t, const gl::Box &, GLenum, GLenum, const gl::PixelUnpackState &, const uint8_t *));
MOCK_METHOD7(setCompressedImage, gl::Error(GLenum, size_t, GLenum, const gl::Extents &, const gl::PixelUnpackState &, size_t, const uint8_t *));
......
......@@ -180,7 +180,7 @@ gl::Error RendererD3D::applyTextures(GLImplFactory *implFactory,
samplerObject ? samplerObject->getSamplerState() : texture->getSamplerState();
// TODO: std::binary_search may become unavailable using older versions of GCC
if (texture->isSamplerComplete(samplerState, data) &&
if (texture->getTextureState().isSamplerComplete(samplerState, data) &&
!std::binary_search(framebufferTextures.begin(),
framebufferTextures.begin() + framebufferTextureCount, texture))
{
......
......@@ -78,7 +78,6 @@ bool IsRenderTargetUsage(GLenum usage)
TextureD3D::TextureD3D(const gl::TextureState &state, RendererD3D *renderer)
: TextureImpl(state),
mRenderer(renderer),
mUsage(GL_NONE),
mDirtyImages(true),
mImmutable(false),
mTexStorage(nullptr),
......@@ -985,7 +984,7 @@ gl::Error TextureD3D_2D::setStorage(GLenum target, size_t levels, GLenum interna
}
// TODO(geofflang): Verify storage creation had no errors
bool renderTarget = IsRenderTargetUsage(mUsage);
bool renderTarget = IsRenderTargetUsage(mState.getUsage());
TextureStorage *storage = mRenderer->createTextureStorage2D(
internalFormat, renderTarget, size.width, size.height, static_cast<int>(levels), false);
......@@ -1166,7 +1165,7 @@ gl::Error TextureD3D_2D::initializeStorage(bool renderTarget)
return gl::Error(GL_NO_ERROR);
}
bool createRenderTarget = (renderTarget || IsRenderTargetUsage(mUsage));
bool createRenderTarget = (renderTarget || IsRenderTargetUsage(mState.getUsage()));
TextureStorage *storage = NULL;
gl::Error error = createCompleteStorage(createRenderTarget, &storage);
......@@ -1598,7 +1597,7 @@ gl::Error TextureD3D_Cube::setStorage(GLenum target, size_t levels, GLenum inter
}
// TODO(geofflang): Verify storage creation had no errors
bool renderTarget = IsRenderTargetUsage(mUsage);
bool renderTarget = IsRenderTargetUsage(mState.getUsage());
TextureStorage *storage = mRenderer->createTextureStorageCube(
internalFormat, renderTarget, size.width, static_cast<int>(levels), false);
......@@ -1709,7 +1708,7 @@ gl::Error TextureD3D_Cube::initializeStorage(bool renderTarget)
return gl::Error(GL_NO_ERROR);
}
bool createRenderTarget = (renderTarget || IsRenderTargetUsage(mUsage));
bool createRenderTarget = (renderTarget || IsRenderTargetUsage(mState.getUsage()));
TextureStorage *storage = NULL;
gl::Error error = createCompleteStorage(createRenderTarget, &storage);
......@@ -2224,7 +2223,7 @@ gl::Error TextureD3D_3D::setStorage(GLenum target, size_t levels, GLenum interna
}
// TODO(geofflang): Verify storage creation had no errors
bool renderTarget = IsRenderTargetUsage(mUsage);
bool renderTarget = IsRenderTargetUsage(mState.getUsage());
TextureStorage *storage =
mRenderer->createTextureStorage3D(internalFormat, renderTarget, size.width, size.height,
size.depth, static_cast<int>(levels));
......@@ -2315,7 +2314,7 @@ gl::Error TextureD3D_3D::initializeStorage(bool renderTarget)
return gl::Error(GL_NO_ERROR);
}
bool createRenderTarget = (renderTarget || IsRenderTargetUsage(mUsage));
bool createRenderTarget = (renderTarget || IsRenderTargetUsage(mState.getUsage()));
TextureStorage *storage = NULL;
gl::Error error = createCompleteStorage(createRenderTarget, &storage);
......@@ -2814,7 +2813,7 @@ gl::Error TextureD3D_2DArray::setStorage(GLenum target, size_t levels, GLenum in
}
// TODO(geofflang): Verify storage creation had no errors
bool renderTarget = IsRenderTargetUsage(mUsage);
bool renderTarget = IsRenderTargetUsage(mState.getUsage());
TextureStorage *storage =
mRenderer->createTextureStorage2DArray(internalFormat, renderTarget, size.width,
size.height, size.depth, static_cast<int>(levels));
......@@ -2899,7 +2898,7 @@ gl::Error TextureD3D_2DArray::initializeStorage(bool renderTarget)
return gl::Error(GL_NO_ERROR);
}
bool createRenderTarget = (renderTarget || IsRenderTargetUsage(mUsage));
bool createRenderTarget = (renderTarget || IsRenderTargetUsage(mState.getUsage()));
TextureStorage *storage = NULL;
gl::Error error = createCompleteStorage(createRenderTarget, &storage);
......
......@@ -35,7 +35,6 @@ class TextureD3D : public TextureImpl
gl::Error getNativeTexture(TextureStorage **outStorage);
virtual void setUsage(GLenum usage) { mUsage = usage; }
bool hasDirtyImages() const { return mDirtyImages; }
void resetDirty() { mDirtyImages = false; }
......@@ -113,8 +112,6 @@ class TextureD3D : public TextureImpl
RendererD3D *mRenderer;
GLenum mUsage;
bool mDirtyImages;
bool mImmutable;
......@@ -213,7 +210,6 @@ class TextureD3D_Cube : public TextureD3D
virtual bool hasDirtyImages() const { return mDirtyImages; }
virtual void resetDirty() { mDirtyImages = false; }
virtual void setUsage(GLenum usage) { mUsage = usage; }
GLenum getInternalFormat(GLint level, GLint layer) const;
bool isDepth(GLint level, GLint layer) const;
......
......@@ -608,10 +608,7 @@ Blit11::ShaderSupport Blit11::getShaderSupport(const Shader &shader)
gl::Error Blit11::swizzleTexture(ID3D11ShaderResourceView *source,
ID3D11RenderTargetView *dest,
const gl::Extents &size,
GLenum swizzleRed,
GLenum swizzleGreen,
GLenum swizzleBlue,
GLenum swizzleAlpha)
const gl::SwizzleState &swizzleTarget)
{
gl::Error error = initResources();
if (error.isError())
......@@ -698,10 +695,10 @@ gl::Error Blit11::swizzleTexture(ID3D11ShaderResourceView *source,
}
unsigned int *swizzleIndices = reinterpret_cast<unsigned int*>(mappedResource.pData);
swizzleIndices[0] = GetSwizzleIndex(swizzleRed);
swizzleIndices[1] = GetSwizzleIndex(swizzleGreen);
swizzleIndices[2] = GetSwizzleIndex(swizzleBlue);
swizzleIndices[3] = GetSwizzleIndex(swizzleAlpha);
swizzleIndices[0] = GetSwizzleIndex(swizzleTarget.swizzleRed);
swizzleIndices[1] = GetSwizzleIndex(swizzleTarget.swizzleGreen);
swizzleIndices[2] = GetSwizzleIndex(swizzleTarget.swizzleBlue);
swizzleIndices[3] = GetSwizzleIndex(swizzleTarget.swizzleAlpha);
deviceContext->Unmap(mSwizzleCB, 0);
......
......@@ -26,8 +26,10 @@ class Blit11 : angle::NonCopyable
explicit Blit11(Renderer11 *renderer);
~Blit11();
gl::Error swizzleTexture(ID3D11ShaderResourceView *source, ID3D11RenderTargetView *dest, const gl::Extents &size,
GLenum swizzleRed, GLenum swizzleGreen, GLenum swizzleBlue, GLenum swizzleAlpha);
gl::Error swizzleTexture(ID3D11ShaderResourceView *source,
ID3D11RenderTargetView *dest,
const gl::Extents &size,
const gl::SwizzleState &swizzleTarget);
gl::Error copyTexture(ID3D11ShaderResourceView *source,
const gl::Box &sourceArea,
......
......@@ -69,7 +69,7 @@ FramebufferImpl *Context11::createFramebuffer(const gl::FramebufferState &data)
TextureImpl *Context11::createTexture(const gl::TextureState &state)
{
switch (state.target)
switch (state.getTarget())
{
case GL_TEXTURE_2D:
return new TextureD3D_2D(state, mRenderer);
......
......@@ -1167,9 +1167,7 @@ gl::Error Renderer11::generateSwizzle(gl::Texture *texture)
{
TextureStorage11 *storage11 = GetAs<TextureStorage11>(texStorage);
const gl::TextureState &textureState = texture->getTextureState();
error =
storage11->generateSwizzles(textureState.swizzleRed, textureState.swizzleGreen,
textureState.swizzleBlue, textureState.swizzleAlpha);
error = storage11->generateSwizzles(textureState.getSwizzleState());
if (error.isError())
{
return error;
......@@ -3609,7 +3607,8 @@ gl::Error Renderer11::generateMipmapsUsingD3D(TextureStorage *storage,
ASSERT(storage11->supportsNativeMipmapFunction());
ID3D11ShaderResourceView *srv;
gl::Error error = storage11->getSRVLevels(textureState.baseLevel, textureState.maxLevel, &srv);
gl::Error error =
storage11->getSRVLevels(textureState.mBaseLevel, textureState.mMaxLevel, &srv);
if (error.isError())
{
return error;
......
......@@ -31,33 +31,6 @@
namespace rx
{
TextureStorage11::SwizzleCacheValue::SwizzleCacheValue()
: swizzleRed(GL_INVALID_INDEX),
swizzleGreen(GL_INVALID_INDEX),
swizzleBlue(GL_INVALID_INDEX),
swizzleAlpha(GL_INVALID_INDEX)
{
}
TextureStorage11::SwizzleCacheValue::SwizzleCacheValue(GLenum red,
GLenum green,
GLenum blue,
GLenum alpha)
: swizzleRed(red), swizzleGreen(green), swizzleBlue(blue), swizzleAlpha(alpha)
{
}
bool TextureStorage11::SwizzleCacheValue::operator==(const SwizzleCacheValue &other) const
{
return swizzleRed == other.swizzleRed && swizzleGreen == other.swizzleGreen &&
swizzleBlue == other.swizzleBlue && swizzleAlpha == other.swizzleAlpha;
}
bool TextureStorage11::SwizzleCacheValue::operator!=(const SwizzleCacheValue &other) const
{
return !(*this == other);
}
TextureStorage11::SRVKey::SRVKey(int baseLevel, int mipLevels, bool swizzle)
: baseLevel(baseLevel), mipLevels(mipLevels), swizzle(swizzle)
{
......@@ -215,8 +188,9 @@ gl::Error TextureStorage11::getSRV(const gl::TextureState &textureState,
// Make sure to add the level offset for our tiny compressed texture workaround
const GLuint effectiveBaseLevel = textureState.getEffectiveBaseLevel();
bool swizzleRequired = textureState.swizzleRequired();
bool mipmapping = gl::IsMipmapFiltered(textureState.samplerState);
unsigned int mipLevels = mipmapping ? (textureState.maxLevel - effectiveBaseLevel + 1) : 1;
bool mipmapping = gl::IsMipmapFiltered(textureState.getSamplerState());
unsigned int mipLevels =
mipmapping ? (textureState.getEffectiveMaxLevel() - effectiveBaseLevel + 1) : 1;
// Make sure there's 'mipLevels' mipmap levels below the base level (offset by the top level,
// which corresponds to GL level 0)
......@@ -240,8 +214,7 @@ gl::Error TextureStorage11::getSRV(const gl::TextureState &textureState,
if (swizzleRequired)
{
verifySwizzleExists(textureState.swizzleRed, textureState.swizzleGreen,
textureState.swizzleBlue, textureState.swizzleAlpha);
verifySwizzleExists(textureState.getSwizzleState());
}
SRVKey key(effectiveBaseLevel, mipLevels, swizzleRequired);
......@@ -359,12 +332,8 @@ d3d11::ANGLEFormat TextureStorage11::getANGLEFormat() const
return mTextureFormatSet->format;
}
gl::Error TextureStorage11::generateSwizzles(GLenum swizzleRed,
GLenum swizzleGreen,
GLenum swizzleBlue,
GLenum swizzleAlpha)
gl::Error TextureStorage11::generateSwizzles(const gl::SwizzleState &swizzleTarget)
{
SwizzleCacheValue swizzleTarget(swizzleRed, swizzleGreen, swizzleBlue, swizzleAlpha);
for (int level = 0; level < getLevelCount(); level++)
{
// Check if the swizzle for this level is out of date
......@@ -390,8 +359,7 @@ gl::Error TextureStorage11::generateSwizzles(GLenum swizzleRed,
Blit11 *blitter = mRenderer->getBlitter();
error = blitter->swizzleTexture(sourceSRV, destRTV, size, swizzleRed, swizzleGreen,
swizzleBlue, swizzleAlpha);
error = blitter->swizzleTexture(sourceSRV, destRTV, size, swizzleTarget);
if (error.isError())
{
return error;
......@@ -408,9 +376,9 @@ void TextureStorage11::invalidateSwizzleCacheLevel(int mipLevel)
{
if (mipLevel >= 0 && static_cast<unsigned int>(mipLevel) < ArraySize(mSwizzleCache))
{
// The default constructor of SwizzleCacheValue has GL_NONE for all channels which is not a
// valid swizzle combination
mSwizzleCache[mipLevel] = SwizzleCacheValue();
// The default constructor of SwizzleState has GL_INVALID_INDEX for all channels which is
// not a valid swizzle combination
mSwizzleCache[mipLevel] = gl::SwizzleState();
}
}
......@@ -587,15 +555,11 @@ gl::Error TextureStorage11::generateMipmap(const gl::ImageIndex &sourceIndex,
gl::GetInternalFormatInfo(source->getInternalFormat()).format, GL_LINEAR, false);
}
void TextureStorage11::verifySwizzleExists(GLenum swizzleRed,
GLenum swizzleGreen,
GLenum swizzleBlue,
GLenum swizzleAlpha)
void TextureStorage11::verifySwizzleExists(const gl::SwizzleState &swizzleState)
{
SwizzleCacheValue swizzleTarget(swizzleRed, swizzleGreen, swizzleBlue, swizzleAlpha);
for (unsigned int level = 0; level < mMipLevels; level++)
{
ASSERT(mSwizzleCache[level] == swizzleTarget);
ASSERT(mSwizzleCache[level] == swizzleState);
}
}
......
......@@ -58,7 +58,7 @@ class TextureStorage11 : public TextureStorage
virtual int getLevelCount() const;
virtual UINT getSubresourceIndex(const gl::ImageIndex &index) const;
gl::Error generateSwizzles(GLenum swizzleRed, GLenum swizzleGreen, GLenum swizzleBlue, GLenum swizzleAlpha);
gl::Error generateSwizzles(const gl::SwizzleState &swizzleTarget);
void invalidateSwizzleCacheLevel(int mipLevel);
void invalidateSwizzleCache();
......@@ -98,7 +98,7 @@ class TextureStorage11 : public TextureStorage
virtual gl::Error createSRV(int baseLevel, int mipLevels, DXGI_FORMAT format, ID3D11Resource *texture,
ID3D11ShaderResourceView **outSRV) const = 0;
void verifySwizzleExists(GLenum swizzleRed, GLenum swizzleGreen, GLenum swizzleBlue, GLenum swizzleAlpha);
void verifySwizzleExists(const gl::SwizzleState &swizzleState);
// Clear all cached non-swizzle SRVs and invalidate the swizzle cache.
void clearSRVCache();
......@@ -114,20 +114,7 @@ class TextureStorage11 : public TextureStorage
unsigned int mTextureHeight;
unsigned int mTextureDepth;
struct SwizzleCacheValue
{
GLenum swizzleRed;
GLenum swizzleGreen;
GLenum swizzleBlue;
GLenum swizzleAlpha;
SwizzleCacheValue();
SwizzleCacheValue(GLenum red, GLenum green, GLenum blue, GLenum alpha);
bool operator ==(const SwizzleCacheValue &other) const;
bool operator !=(const SwizzleCacheValue &other) const;
};
SwizzleCacheValue mSwizzleCache[gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS];
gl::SwizzleState mSwizzleCache[gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS];
private:
const UINT mBindFlags;
......
......@@ -63,7 +63,7 @@ FramebufferImpl *Context9::createFramebuffer(const gl::FramebufferState &data)
TextureImpl *Context9::createTexture(const gl::TextureState &state)
{
switch (state.target)
switch (state.getTarget())
{
case GL_TEXTURE_2D:
return new TextureD3D_2D(state, mRenderer);
......
......@@ -117,7 +117,7 @@ TextureGL::TextureGL(const gl::TextureState &state,
mStateManager(stateManager),
mBlitter(blitter),
mLevelInfo(gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS + 1),
mAppliedTextureState(state.target),
mAppliedTextureState(state.mTarget),
mTextureID(0)
{
ASSERT(mFunctions);
......@@ -125,7 +125,7 @@ TextureGL::TextureGL(const gl::TextureState &state,
ASSERT(mBlitter);
mFunctions->genTextures(1, &mTextureID);
mStateManager->bindTexture(mState.target, mTextureID);
mStateManager->bindTexture(mState.mTarget, mTextureID);
}
TextureGL::~TextureGL()
......@@ -134,30 +134,24 @@ TextureGL::~TextureGL()
mTextureID = 0;
}
void TextureGL::setUsage(GLenum usage)
{
// GL_ANGLE_texture_usage not implemented for desktop GL
UNREACHABLE();
}
gl::Error TextureGL::setImage(GLenum target, size_t level, GLenum internalFormat, const gl::Extents &size, GLenum format, GLenum type,
const gl::PixelUnpackState &unpack, const uint8_t *pixels)
{
UNUSED_ASSERTION_VARIABLE(&CompatibleTextureTarget); // Reference this function to avoid warnings.
ASSERT(CompatibleTextureTarget(mState.target, target));
ASSERT(CompatibleTextureTarget(mState.mTarget, target));
nativegl::TexImageFormat texImageFormat =
nativegl::GetTexImageFormat(mFunctions, mWorkarounds, internalFormat, format, type);
mStateManager->bindTexture(mState.target, mTextureID);
if (UseTexImage2D(mState.target))
mStateManager->bindTexture(mState.mTarget, mTextureID);
if (UseTexImage2D(mState.mTarget))
{
ASSERT(size.depth == 1);
mFunctions->texImage2D(target, static_cast<GLint>(level), texImageFormat.internalFormat,
size.width, size.height, 0, texImageFormat.format,
texImageFormat.type, pixels);
}
else if (UseTexImage3D(mState.target))
else if (UseTexImage3D(mState.mTarget))
{
mFunctions->texImage3D(target, static_cast<GLint>(level), texImageFormat.internalFormat,
size.width, size.height, size.depth, 0, texImageFormat.format,
......@@ -176,20 +170,20 @@ gl::Error TextureGL::setImage(GLenum target, size_t level, GLenum internalFormat
gl::Error TextureGL::setSubImage(GLenum target, size_t level, const gl::Box &area, GLenum format, GLenum type,
const gl::PixelUnpackState &unpack, const uint8_t *pixels)
{
ASSERT(CompatibleTextureTarget(mState.target, target));
ASSERT(CompatibleTextureTarget(mState.mTarget, target));
nativegl::TexSubImageFormat texSubImageFormat =
nativegl::GetTexSubImageFormat(mFunctions, mWorkarounds, format, type);
mStateManager->bindTexture(mState.target, mTextureID);
if (UseTexImage2D(mState.target))
mStateManager->bindTexture(mState.mTarget, mTextureID);
if (UseTexImage2D(mState.mTarget))
{
ASSERT(area.z == 0 && area.depth == 1);
mFunctions->texSubImage2D(target, static_cast<GLint>(level), area.x, area.y, area.width,
area.height, texSubImageFormat.format, texSubImageFormat.type,
pixels);
}
else if (UseTexImage3D(mState.target))
else if (UseTexImage3D(mState.mTarget))
{
mFunctions->texSubImage3D(target, static_cast<GLint>(level), area.x, area.y, area.z,
area.width, area.height, area.depth, texSubImageFormat.format,
......@@ -209,20 +203,20 @@ gl::Error TextureGL::setSubImage(GLenum target, size_t level, const gl::Box &are
gl::Error TextureGL::setCompressedImage(GLenum target, size_t level, GLenum internalFormat, const gl::Extents &size,
const gl::PixelUnpackState &unpack, size_t imageSize, const uint8_t *pixels)
{
ASSERT(CompatibleTextureTarget(mState.target, target));
ASSERT(CompatibleTextureTarget(mState.mTarget, target));
nativegl::CompressedTexImageFormat compressedTexImageFormat =
nativegl::GetCompressedTexImageFormat(mFunctions, mWorkarounds, internalFormat);
mStateManager->bindTexture(mState.target, mTextureID);
if (UseTexImage2D(mState.target))
mStateManager->bindTexture(mState.mTarget, mTextureID);
if (UseTexImage2D(mState.mTarget))
{
ASSERT(size.depth == 1);
mFunctions->compressedTexImage2D(target, static_cast<GLint>(level),
compressedTexImageFormat.internalFormat, size.width,
size.height, 0, static_cast<GLsizei>(imageSize), pixels);
}
else if (UseTexImage3D(mState.target))
else if (UseTexImage3D(mState.mTarget))
{
mFunctions->compressedTexImage3D(
target, static_cast<GLint>(level), compressedTexImageFormat.internalFormat, size.width,
......@@ -242,20 +236,20 @@ gl::Error TextureGL::setCompressedImage(GLenum target, size_t level, GLenum inte
gl::Error TextureGL::setCompressedSubImage(GLenum target, size_t level, const gl::Box &area, GLenum format,
const gl::PixelUnpackState &unpack, size_t imageSize, const uint8_t *pixels)
{
ASSERT(CompatibleTextureTarget(mState.target, target));
ASSERT(CompatibleTextureTarget(mState.mTarget, target));
nativegl::CompressedTexSubImageFormat compressedTexSubImageFormat =
nativegl::GetCompressedSubTexImageFormat(mFunctions, mWorkarounds, format);
mStateManager->bindTexture(mState.target, mTextureID);
if (UseTexImage2D(mState.target))
mStateManager->bindTexture(mState.mTarget, mTextureID);
if (UseTexImage2D(mState.mTarget))
{
ASSERT(area.z == 0 && area.depth == 1);
mFunctions->compressedTexSubImage2D(
target, static_cast<GLint>(level), area.x, area.y, area.width, area.height,
compressedTexSubImageFormat.format, static_cast<GLsizei>(imageSize), pixels);
}
else if (UseTexImage3D(mState.target))
else if (UseTexImage3D(mState.mTarget))
{
mFunctions->compressedTexSubImage3D(target, static_cast<GLint>(level), area.x, area.y,
area.z, area.width, area.height, area.depth,
......@@ -283,7 +277,7 @@ gl::Error TextureGL::copyImage(GLenum target, size_t level, const gl::Rectangle
if (levelInfo.lumaWorkaround.enabled)
{
gl::Error error = mBlitter->copyImageToLUMAWorkaroundTexture(
mTextureID, mState.target, target, levelInfo.sourceFormat, level, sourceArea,
mTextureID, mState.mTarget, target, levelInfo.sourceFormat, level, sourceArea,
copyTexImageFormat.internalFormat, source);
if (error.isError())
{
......@@ -294,11 +288,11 @@ gl::Error TextureGL::copyImage(GLenum target, size_t level, const gl::Rectangle
{
const FramebufferGL *sourceFramebufferGL = GetImplAs<FramebufferGL>(source);
mStateManager->bindTexture(mState.target, mTextureID);
mStateManager->bindTexture(mState.mTarget, mTextureID);
mStateManager->bindFramebuffer(GL_READ_FRAMEBUFFER,
sourceFramebufferGL->getFramebufferID());
if (UseTexImage2D(mState.target))
if (UseTexImage2D(mState.mTarget))
{
mFunctions->copyTexImage2D(target, static_cast<GLint>(level),
copyTexImageFormat.internalFormat, sourceArea.x,
......@@ -320,14 +314,14 @@ gl::Error TextureGL::copySubImage(GLenum target, size_t level, const gl::Offset
{
const FramebufferGL *sourceFramebufferGL = GetImplAs<FramebufferGL>(source);
mStateManager->bindTexture(mState.target, mTextureID);
mStateManager->bindTexture(mState.mTarget, mTextureID);
mStateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebufferGL->getFramebufferID());
const LevelInfoGL &levelInfo = mLevelInfo[level];
if (levelInfo.lumaWorkaround.enabled)
{
gl::Error error = mBlitter->copySubImageToLUMAWorkaroundTexture(
mTextureID, mState.target, target, levelInfo.sourceFormat, level, destOffset,
mTextureID, mState.mTarget, target, levelInfo.sourceFormat, level, destOffset,
sourceArea, source);
if (error.isError())
{
......@@ -336,14 +330,14 @@ gl::Error TextureGL::copySubImage(GLenum target, size_t level, const gl::Offset
}
else
{
if (UseTexImage2D(mState.target))
if (UseTexImage2D(mState.mTarget))
{
ASSERT(destOffset.z == 0);
mFunctions->copyTexSubImage2D(target, static_cast<GLint>(level), destOffset.x,
destOffset.y, sourceArea.x, sourceArea.y,
sourceArea.width, sourceArea.height);
}
else if (UseTexImage3D(mState.target))
else if (UseTexImage3D(mState.mTarget))
{
mFunctions->copyTexSubImage3D(target, static_cast<GLint>(level), destOffset.x,
destOffset.y, destOffset.z, sourceArea.x, sourceArea.y,
......@@ -366,8 +360,8 @@ gl::Error TextureGL::setStorage(GLenum target, size_t levels, GLenum internalFor
nativegl::TexStorageFormat texStorageFormat =
nativegl::GetTexStorageFormat(mFunctions, mWorkarounds, internalFormat);
mStateManager->bindTexture(mState.target, mTextureID);
if (UseTexImage2D(mState.target))
mStateManager->bindTexture(mState.mTarget, mTextureID);
if (UseTexImage2D(mState.mTarget))
{
ASSERT(size.depth == 1);
if (mFunctions->texStorage2D)
......@@ -391,7 +385,7 @@ gl::Error TextureGL::setStorage(GLenum target, size_t levels, GLenum internalFor
std::max(size.height >> level, 1),
1);
if (mState.target == GL_TEXTURE_2D)
if (mState.mTarget == GL_TEXTURE_2D)
{
if (internalFormatInfo.compressed)
{
......@@ -409,7 +403,7 @@ gl::Error TextureGL::setStorage(GLenum target, size_t levels, GLenum internalFor
internalFormatInfo.type, nullptr);
}
}
else if (mState.target == GL_TEXTURE_CUBE_MAP)
else if (mState.mTarget == GL_TEXTURE_CUBE_MAP)
{
for (GLenum face = gl::FirstCubeMapTextureTarget; face <= gl::LastCubeMapTextureTarget; face++)
{
......@@ -437,7 +431,7 @@ gl::Error TextureGL::setStorage(GLenum target, size_t levels, GLenum internalFor
}
}
}
else if (UseTexImage3D(mState.target))
else if (UseTexImage3D(mState.mTarget))
{
if (mFunctions->texStorage3D)
{
......@@ -459,7 +453,7 @@ gl::Error TextureGL::setStorage(GLenum target, size_t levels, GLenum internalFor
{
gl::Extents levelSize(
std::max(size.width >> i, 1), std::max(size.height >> i, 1),
mState.target == GL_TEXTURE_3D ? std::max(size.depth >> i, 1) : size.depth);
mState.mTarget == GL_TEXTURE_3D ? std::max(size.depth >> i, 1) : size.depth);
if (internalFormatInfo.compressed)
{
......@@ -504,12 +498,12 @@ gl::Error TextureGL::setImageExternal(GLenum target,
gl::Error TextureGL::generateMipmaps()
{
mStateManager->bindTexture(mState.target, mTextureID);
mFunctions->generateMipmap(mState.target);
mStateManager->bindTexture(mState.mTarget, mTextureID);
mFunctions->generateMipmap(mState.mTarget);
for (size_t level = mState.baseLevel; level < mLevelInfo.size(); level++)
for (size_t level = mState.mBaseLevel; level < mLevelInfo.size(); level++)
{
mLevelInfo[level] = mLevelInfo[mState.baseLevel];
mLevelInfo[level] = mLevelInfo[mState.mBaseLevel];
}
return gl::Error(GL_NO_ERROR);
......@@ -517,10 +511,10 @@ gl::Error TextureGL::generateMipmaps()
void TextureGL::bindTexImage(egl::Surface *surface)
{
ASSERT(mState.target == GL_TEXTURE_2D);
ASSERT(mState.mTarget == GL_TEXTURE_2D);
// Make sure this texture is bound
mStateManager->bindTexture(mState.target, mTextureID);
mStateManager->bindTexture(mState.mTarget, mTextureID);
mLevelInfo[0] = LevelInfoGL();
}
......@@ -528,12 +522,12 @@ void TextureGL::bindTexImage(egl::Surface *surface)
void TextureGL::releaseTexImage()
{
// Not all Surface implementations reset the size of mip 0 when releasing, do it manually
ASSERT(mState.target == GL_TEXTURE_2D);
ASSERT(mState.mTarget == GL_TEXTURE_2D);
mStateManager->bindTexture(mState.target, mTextureID);
if (UseTexImage2D(mState.target))
mStateManager->bindTexture(mState.mTarget, mTextureID);
if (UseTexImage2D(mState.mTarget))
{
mFunctions->texImage2D(mState.target, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE,
mFunctions->texImage2D(mState.mTarget, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
}
else
......@@ -586,15 +580,15 @@ template <typename T, typename ApplyTextureFuncType>
static inline void SyncTextureStateSwizzle(const FunctionsGL *functions,
ApplyTextureFuncType applyTextureFunc,
const LevelInfoGL &levelInfo,
const gl::TextureState &newState,
gl::TextureState &curState,
const gl::SwizzleState &newState,
gl::SwizzleState &curState,
GLenum textureType,
GLenum name,
T(gl::TextureState::*stateMember))
T(gl::SwizzleState::*stateMember))
{
GLenum resultSwizzle = newState.*stateMember;
if (levelInfo.lumaWorkaround.enabled || levelInfo.depthStencilWorkaround)
{
GLenum resultSwizzle = GL_NONE;
if (levelInfo.lumaWorkaround.enabled)
{
UNUSED_ASSERTION_VARIABLE(levelInfo.lumaWorkaround.workaroundFormat);
......@@ -697,19 +691,14 @@ static inline void SyncTextureStateSwizzle(const FunctionsGL *functions,
UNREACHABLE();
}
// Apply the new swizzle state if needed
}
if (curState.*stateMember != resultSwizzle)
{
applyTextureFunc();
curState.*stateMember = resultSwizzle;
functions->texParameterf(textureType, name, static_cast<GLfloat>(resultSwizzle));
}
}
else
{
SyncTextureStateMember(functions, applyTextureFunc, newState, curState, textureType, name,
stateMember);
}
}
void TextureGL::syncState(size_t textureUnit) const
......@@ -721,7 +710,7 @@ void TextureGL::syncState(size_t textureUnit) const
if (!textureApplied)
{
mStateManager->activeTexture(textureUnit);
mStateManager->bindTexture(mState.target, mTextureID);
mStateManager->bindTexture(mState.mTarget, mTextureID);
textureApplied = true;
}
};
......@@ -732,36 +721,36 @@ void TextureGL::syncState(size_t textureUnit) const
if (mAppliedTextureState.getEffectiveBaseLevel() != mState.getEffectiveBaseLevel())
{
applyTextureFunc();
mFunctions->texParameteri(mState.target, GL_TEXTURE_BASE_LEVEL,
mFunctions->texParameteri(mState.mTarget, GL_TEXTURE_BASE_LEVEL,
mState.getEffectiveBaseLevel());
}
mAppliedTextureState.baseLevel = mState.baseLevel;
mAppliedTextureState.mBaseLevel = mState.mBaseLevel;
if (mAppliedTextureState.getEffectiveMaxLevel() != mState.getEffectiveMaxLevel())
{
applyTextureFunc();
mFunctions->texParameteri(mState.target, GL_TEXTURE_MAX_LEVEL,
mFunctions->texParameteri(mState.mTarget, GL_TEXTURE_MAX_LEVEL,
mState.getEffectiveMaxLevel());
}
mAppliedTextureState.maxLevel = mState.maxLevel;
mAppliedTextureState.mMaxLevel = mState.mMaxLevel;
// clang-format off
const LevelInfoGL &levelInfo = mLevelInfo[mState.getEffectiveBaseLevel()];
SyncTextureStateSwizzle(mFunctions, applyTextureFunc, levelInfo, mState, mAppliedTextureState, mState.target, GL_TEXTURE_SWIZZLE_R, &gl::TextureState::swizzleRed);
SyncTextureStateSwizzle(mFunctions, applyTextureFunc, levelInfo, mState, mAppliedTextureState, mState.target, GL_TEXTURE_SWIZZLE_G, &gl::TextureState::swizzleGreen);
SyncTextureStateSwizzle(mFunctions, applyTextureFunc, levelInfo, mState, mAppliedTextureState, mState.target, GL_TEXTURE_SWIZZLE_B, &gl::TextureState::swizzleBlue);
SyncTextureStateSwizzle(mFunctions, applyTextureFunc, levelInfo, mState, mAppliedTextureState, mState.target, GL_TEXTURE_SWIZZLE_A, &gl::TextureState::swizzleAlpha);
SyncTextureStateSwizzle(mFunctions, applyTextureFunc, levelInfo, mState.mSwizzleState, mAppliedTextureState.mSwizzleState, mState.mTarget, GL_TEXTURE_SWIZZLE_R, &gl::SwizzleState::swizzleRed);
SyncTextureStateSwizzle(mFunctions, applyTextureFunc, levelInfo, mState.mSwizzleState, mAppliedTextureState.mSwizzleState, mState.mTarget, GL_TEXTURE_SWIZZLE_G, &gl::SwizzleState::swizzleGreen);
SyncTextureStateSwizzle(mFunctions, applyTextureFunc, levelInfo, mState.mSwizzleState, mAppliedTextureState.mSwizzleState, mState.mTarget, GL_TEXTURE_SWIZZLE_B, &gl::SwizzleState::swizzleBlue);
SyncTextureStateSwizzle(mFunctions, applyTextureFunc, levelInfo, mState.mSwizzleState, mAppliedTextureState.mSwizzleState, mState.mTarget, GL_TEXTURE_SWIZZLE_A, &gl::SwizzleState::swizzleAlpha);
// Sync sampler state
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.samplerState, mAppliedTextureState.samplerState, mState.target, GL_TEXTURE_MIN_FILTER, &gl::SamplerState::minFilter);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.samplerState, mAppliedTextureState.samplerState, mState.target, GL_TEXTURE_MAG_FILTER, &gl::SamplerState::magFilter);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.samplerState, mAppliedTextureState.samplerState, mState.target, GL_TEXTURE_WRAP_S, &gl::SamplerState::wrapS);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.samplerState, mAppliedTextureState.samplerState, mState.target, GL_TEXTURE_WRAP_T, &gl::SamplerState::wrapT);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.samplerState, mAppliedTextureState.samplerState, mState.target, GL_TEXTURE_WRAP_R, &gl::SamplerState::wrapR);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.samplerState, mAppliedTextureState.samplerState, mState.target, GL_TEXTURE_MAX_ANISOTROPY_EXT, &gl::SamplerState::maxAnisotropy);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.samplerState, mAppliedTextureState.samplerState, mState.target, GL_TEXTURE_MIN_LOD, &gl::SamplerState::minLod);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.samplerState, mAppliedTextureState.samplerState, mState.target, GL_TEXTURE_MAX_LOD, &gl::SamplerState::maxLod);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.samplerState, mAppliedTextureState.samplerState, mState.target, GL_TEXTURE_COMPARE_MODE, &gl::SamplerState::compareMode);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.samplerState, mAppliedTextureState.samplerState, mState.target, GL_TEXTURE_COMPARE_FUNC, &gl::SamplerState::compareFunc);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.mSamplerState, mAppliedTextureState.mSamplerState, mState.mTarget, GL_TEXTURE_MIN_FILTER, &gl::SamplerState::minFilter);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.mSamplerState, mAppliedTextureState.mSamplerState, mState.mTarget, GL_TEXTURE_MAG_FILTER, &gl::SamplerState::magFilter);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.mSamplerState, mAppliedTextureState.mSamplerState, mState.mTarget, GL_TEXTURE_WRAP_S, &gl::SamplerState::wrapS);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.mSamplerState, mAppliedTextureState.mSamplerState, mState.mTarget, GL_TEXTURE_WRAP_T, &gl::SamplerState::wrapT);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.mSamplerState, mAppliedTextureState.mSamplerState, mState.mTarget, GL_TEXTURE_WRAP_R, &gl::SamplerState::wrapR);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.mSamplerState, mAppliedTextureState.mSamplerState, mState.mTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, &gl::SamplerState::maxAnisotropy);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.mSamplerState, mAppliedTextureState.mSamplerState, mState.mTarget, GL_TEXTURE_MIN_LOD, &gl::SamplerState::minLod);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.mSamplerState, mAppliedTextureState.mSamplerState, mState.mTarget, GL_TEXTURE_MAX_LOD, &gl::SamplerState::maxLod);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.mSamplerState, mAppliedTextureState.mSamplerState, mState.mTarget, GL_TEXTURE_COMPARE_MODE, &gl::SamplerState::compareMode);
SyncSamplerStateMember(mFunctions, applyTextureFunc, mState.mSamplerState, mAppliedTextureState.mSamplerState, mState.mTarget, GL_TEXTURE_COMPARE_FUNC, &gl::SamplerState::compareFunc);
// clang-format on
}
......
......@@ -59,8 +59,6 @@ class TextureGL : public TextureImpl
BlitGL *blitter);
~TextureGL() override;
void setUsage(GLenum usage) override;
gl::Error setImage(GLenum target, size_t level, GLenum internalFormat, const gl::Extents &size, GLenum format, GLenum type,
const gl::PixelUnpackState &unpack, const uint8_t *pixels) override;
gl::Error setSubImage(GLenum target, size_t level, const gl::Box &area, GLenum format, GLenum type,
......
......@@ -123,6 +123,34 @@ egl::Error ValidateStreamAttribute(const EGLAttrib attribute,
}
return egl::Error(EGL_SUCCESS);
}
egl::Error ValidateCreateImageKHRMipLevelCommon(gl::Context *context,
const gl::Texture *texture,
EGLAttrib level)
{
// Note that the spec EGL_KHR_create_image spec does not explicitly specify an error
// when the level is outside the base/max level range, but it does mention that the
// level "must be a part of the complete texture object <buffer>". It can be argued
// that out-of-range levels are not a part of the complete texture.
const GLuint effectiveBaseLevel = texture->getTextureState().getEffectiveBaseLevel();
if (level > 0 &&
(!texture->isMipmapComplete() || static_cast<GLuint>(level) < effectiveBaseLevel ||
static_cast<size_t>(level) > texture->getTextureState().getMipmapMaxLevel()))
{
return egl::Error(EGL_BAD_PARAMETER, "texture must be complete if level is non-zero.");
}
if (level == 0 && !texture->isMipmapComplete() &&
TextureHasNonZeroMipLevelsSpecified(context, texture))
{
return egl::Error(EGL_BAD_PARAMETER,
"if level is zero and the texture is incomplete, it must have no mip "
"levels specified except zero.");
}
return egl::Error(EGL_SUCCESS);
}
} // namespace
namespace egl
......@@ -862,18 +890,10 @@ Error ValidateCreateImageKHR(const Display *display,
"target 2D texture does not have a valid size at specified level.");
}
if (level > 0 && (!texture->isMipmapComplete() ||
static_cast<size_t>(level) >= texture->getMipCompleteLevels()))
{
return Error(EGL_BAD_PARAMETER, "texture must be complete if level is non-zero.");
}
if (level == 0 && !texture->isMipmapComplete() &&
TextureHasNonZeroMipLevelsSpecified(context, texture))
Error error = ValidateCreateImageKHRMipLevelCommon(context, texture, level);
if (error.isError())
{
return Error(EGL_BAD_PARAMETER,
"if level is zero and the texture is incomplete, it must have no mip "
"levels specified except zero.");
return error;
}
}
break;
......@@ -918,18 +938,10 @@ Error ValidateCreateImageKHR(const Display *display,
"and face.");
}
if (level > 0 && (!texture->isMipmapComplete() ||
static_cast<size_t>(level) >= texture->getMipCompleteLevels()))
{
return Error(EGL_BAD_PARAMETER, "texture must be complete if level is non-zero.");
}
if (level == 0 && !texture->isMipmapComplete() &&
TextureHasNonZeroMipLevelsSpecified(context, texture))
Error error = ValidateCreateImageKHRMipLevelCommon(context, texture, level);
if (error.isError())
{
return Error(EGL_BAD_PARAMETER,
"if level is zero and the texture is incomplete, it must have no mip "
"levels specified except zero.");
return error;
}
if (level == 0 && !texture->isMipmapComplete() &&
......@@ -985,18 +997,10 @@ Error ValidateCreateImageKHR(const Display *display,
"offset at the specified level.");
}
if (level > 0 && (!texture->isMipmapComplete() ||
static_cast<size_t>(level) >= texture->getMipCompleteLevels()))
{
return Error(EGL_BAD_PARAMETER, "texture must be complete if level is non-zero.");
}
if (level == 0 && !texture->isMipmapComplete() &&
TextureHasNonZeroMipLevelsSpecified(context, texture))
Error error = ValidateCreateImageKHRMipLevelCommon(context, texture, level);
if (error.isError())
{
return Error(EGL_BAD_PARAMETER,
"if level is zero and the texture is incomplete, it must have no mip "
"levels specified except zero.");
return error;
}
}
break;
......
......@@ -1298,7 +1298,7 @@ void GL_APIENTRY GenerateMipmap(GLenum target)
}
// Cube completeness check
if (target == GL_TEXTURE_CUBE_MAP && !texture->isCubeComplete())
if (target == GL_TEXTURE_CUBE_MAP && !texture->getTextureState().isCubeComplete())
{
context->handleError(Error(GL_INVALID_OPERATION));
return;
......
......@@ -1701,6 +1701,45 @@ TEST_P(Texture2DTestES3, DrawWithLevelsOutsideRangeUndefined)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test that drawing works correctly when level 0 is undefined and base level is 1.
TEST_P(Texture2DTestES3, DrawWithLevelZeroUndefined)
{
if (IsAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
{
// Observed crashing on AMD. Oddly the crash only happens with 2D textures, not 3D or array.
std::cout << "Test skipped on AMD OpenGL." << std::endl;
return;
}
if (IsOSX())
{
// Observed incorrect rendering on OSX.
std::cout << "Test skipped on OSX." << std::endl;
return;
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mTexture2D);
std::vector<GLColor> texDataGreen(2u * 2u, GLColor::green);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
texDataGreen.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
EXPECT_GL_NO_ERROR();
// Texture is incomplete.
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
texDataGreen.data());
// Texture is now complete.
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test that drawing works correctly when levels outside the BASE_LEVEL/MAX_LEVEL range have
// dimensions that don't fit the images inside the range.
// GLES 3.0.4 section 3.8.13 Texture completeness
......
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