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;
}
......
......@@ -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; }
GLuint baseLevel;
GLuint maxLevel;
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);
bool immutableFormat;
GLuint immutableLevels;
// 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;
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);
......
......@@ -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()))
Error error = ValidateCreateImageKHRMipLevelCommon(context, texture, level);
if (error.isError())
{
return Error(EGL_BAD_PARAMETER, "texture must be complete if level is non-zero.");
}
if (level == 0 && !texture->isMipmapComplete() &&
TextureHasNonZeroMipLevelsSpecified(context, texture))
{
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