Commit 0f2b1560 by Olli Etuaho Committed by Commit Bot

Fix GenerateMipmap when base level or max level are set

According to GLES 3.0.4 section 3.8.10, GenerateMipmap should generate levels based on the base level, and generate them at most up to the max level. Levels outside the base/max level range should be unchanged by GenerateMipmap. The Texture class is fixed so that the image descs are set only for the changed mipmap range when GenerateMipmap is called. The D3D backend is fixed so that mipmap generation is correctly started from the base level instead of level 0, and making sure that mipmaps are generated only up to the max level. Generating mipmaps for array textures is also fixed for cases where the base level depth >= max(width, height) * 2. The GL backend is fixed to sync texture state before GenerateMipmap is called, so that base level and max level are set correctly in the driver. The GenerateMipmap entry point is refactored so that it has a separate validation function and a context function which does the work. Validation for out-of-range base levels is added. New tests are added to verify the functionality. One corner case in the tests fails on NVIDIA GL drivers likely due to a driver bug - similar rules for GenerateMipmap are found from newer GLES specs and also OpenGL specs (checked versions 3.3 and 4.4). BUG=angleproject:596 TEST=angle_end2end_tests Change-Id: Ifc7b4126281967fc4f6dc4f9452e5b01e39f83d7 Reviewed-on: https://chromium-review.googlesource.com/344514Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent d3a5b185
......@@ -467,6 +467,43 @@ inline T shiftData(T input)
return (input & mask) << inputBitStart;
}
inline unsigned int CountLeadingZeros(uint32_t x)
{
// Use binary search to find the amount of leading zeros.
unsigned int zeros = 32u;
uint32_t y;
y = x >> 16u;
if (y != 0)
{
zeros = zeros - 16u;
x = y;
}
y = x >> 8u;
if (y != 0)
{
zeros = zeros - 8u;
x = y;
}
y = x >> 4u;
if (y != 0)
{
zeros = zeros - 4u;
x = y;
}
y = x >> 2u;
if (y != 0)
{
zeros = zeros - 2u;
x = y;
}
y = x >> 1u;
if (y != 0)
{
return zeros - 2u;
}
return zeros - x;
}
inline unsigned char average(unsigned char a, unsigned char b)
{
......
......@@ -168,5 +168,14 @@ TEST(MathUtilTest, isInf)
EXPECT_FALSE(isInf(bitCast<float>(1u << 31 | 0xfeu << 23 | 0x7fffffu)));
}
TEST(MathUtilTest, CountLeadingZeros)
{
for (unsigned int i = 0; i < 32u; ++i)
{
uint32_t iLeadingZeros = 1u << (31u - i);
EXPECT_EQ(i, CountLeadingZeros(iLeadingZeros));
}
EXPECT_EQ(32u, CountLeadingZeros(0));
}
}
......@@ -2643,6 +2643,12 @@ void Context::compressedTexSubImage3D(GLenum target,
imageSize, reinterpret_cast<const uint8_t *>(data)));
}
void Context::generateMipmap(GLenum target)
{
Texture *texture = getTargetTexture(target);
handleError(texture->generateMipmap());
}
void Context::getBufferPointerv(GLenum target, GLenum /*pname*/, void **params)
{
Buffer *buffer = getState().getTargetBuffer(target);
......
......@@ -370,6 +370,8 @@ class Context final : public ValidationContext
GLsizei imageSize,
const GLvoid *data);
void generateMipmap(GLenum target);
Error flush();
Error finish();
......
......@@ -131,22 +131,23 @@ GLuint TextureState::getEffectiveMaxLevel() const
return mMaxLevel;
}
size_t TextureState::getMipmapMaxLevel() const
GLuint TextureState::getMipmapMaxLevel() const
{
const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel());
size_t expectedMipLevels = 0;
GLuint 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);
expectedMipLevels = static_cast<GLuint>(log2(maxDim));
}
else
{
expectedMipLevels = log2(std::max(baseImageDesc.size.width, baseImageDesc.size.height));
expectedMipLevels = static_cast<GLuint>(
log2(std::max(baseImageDesc.size.width, baseImageDesc.size.height)));
}
return std::min<size_t>(getEffectiveBaseLevel() + expectedMipLevels, getEffectiveMaxLevel());
return std::min<GLuint>(getEffectiveBaseLevel() + expectedMipLevels, getEffectiveMaxLevel());
}
bool TextureState::setBaseLevel(GLuint baseLevel)
......@@ -320,9 +321,9 @@ bool TextureState::computeSamplerCompleteness(const SamplerState &samplerState,
bool TextureState::computeMipmapCompleteness() const
{
const size_t maxLevel = getMipmapMaxLevel();
const GLuint maxLevel = getMipmapMaxLevel();
for (size_t level = getEffectiveBaseLevel(); level <= maxLevel; level++)
for (GLuint level = getEffectiveBaseLevel(); level <= maxLevel; level++)
{
if (mTarget == GL_TEXTURE_CUBE_MAP)
{
......@@ -433,14 +434,19 @@ void TextureState::setImageDesc(GLenum target, size_t level, const ImageDesc &de
mCompletenessCache.cacheValid = false;
}
void TextureState::setImageDescChain(size_t levels, Extents baseSize, GLenum sizedInternalFormat)
void TextureState::setImageDescChain(GLuint baseLevel,
GLuint maxLevel,
Extents baseSize,
GLenum sizedInternalFormat)
{
for (int level = 0; level < static_cast<int>(levels); level++)
for (GLuint level = baseLevel; level <= maxLevel; 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));
int relativeLevel = (level - baseLevel);
Extents levelSize(std::max<int>(baseSize.width >> relativeLevel, 1),
std::max<int>(baseSize.height >> relativeLevel, 1),
(mTarget == GL_TEXTURE_2D_ARRAY)
? baseSize.depth
: std::max<int>(baseSize.depth >> relativeLevel, 1));
ImageDesc levelInfo(levelSize, sizedInternalFormat);
if (mTarget == GL_TEXTURE_CUBE_MAP)
......@@ -874,7 +880,7 @@ Error Texture::copySubImage(GLenum target, size_t level, const Offset &destOffse
return mTexture->copySubImage(target, level, destOffset, sourceArea, source);
}
Error Texture::setStorage(GLenum target, size_t levels, GLenum internalFormat, const Extents &size)
Error Texture::setStorage(GLenum target, GLsizei levels, GLenum internalFormat, const Extents &size)
{
ASSERT(target == mState.mTarget);
......@@ -891,12 +897,11 @@ Error Texture::setStorage(GLenum target, size_t levels, GLenum internalFormat, c
mState.mImmutableFormat = true;
mState.mImmutableLevels = static_cast<GLuint>(levels);
mState.clearImageDescs();
mState.setImageDescChain(levels, size, internalFormat);
mState.setImageDescChain(0, static_cast<GLuint>(levels - 1), size, internalFormat);
return Error(GL_NO_ERROR);
}
Error Texture::generateMipmaps()
Error Texture::generateMipmap()
{
// Release from previous calls to eglBindTexImage, to avoid calling the Impl after
releaseTexImageInternal();
......@@ -908,17 +913,20 @@ Error Texture::generateMipmaps()
orphanImages();
}
Error error = mTexture->generateMipmaps();
if (error.isError())
const GLuint baseLevel = mState.getEffectiveBaseLevel();
const GLuint maxLevel = mState.getMipmapMaxLevel();
if (maxLevel > baseLevel)
{
return error;
}
ANGLE_TRY(mTexture->generateMipmap());
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;
mState.setImageDescChain(mipLevels, baseImageInfo.size, baseImageInfo.internalFormat);
const ImageDesc &baseImageInfo =
mState.getImageDesc(mState.getBaseImageTarget(), baseLevel);
mState.setImageDescChain(baseLevel, maxLevel, baseImageInfo.size,
baseImageInfo.internalFormat);
}
return Error(GL_NO_ERROR);
return NoError();
}
void Texture::bindTexImageFromSurface(egl::Surface *surface)
......
......@@ -34,7 +34,6 @@ namespace rx
class GLImplFactory;
class TextureImpl;
class TextureGL;
class Renderer11;
}
namespace gl
......@@ -85,7 +84,7 @@ struct TextureState final : public angle::NonCopyable
GLuint getEffectiveMaxLevel() const;
// Returns the value called "q" in the GLES 3.0.4 spec section 3.8.10.
size_t getMipmapMaxLevel() const;
GLuint getMipmapMaxLevel() const;
// Returns true if base level changed.
bool setBaseLevel(GLuint baseLevel);
......@@ -108,9 +107,6 @@ struct TextureState final : public angle::NonCopyable
friend class rx::TextureGL;
friend bool operator==(const TextureState &a, const TextureState &b);
// 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;
......@@ -119,7 +115,10 @@ struct TextureState final : public angle::NonCopyable
GLenum getBaseImageTarget() const;
void setImageDesc(GLenum target, size_t level, const ImageDesc &desc);
void setImageDescChain(size_t levels, Extents baseSize, GLenum sizedInternalFormat);
void setImageDescChain(GLuint baselevel,
GLuint maxLevel,
Extents baseSize,
GLenum sizedInternalFormat);
void clearImageDesc(GLenum target, size_t level);
void clearImageDescs();
......@@ -283,11 +282,11 @@ class Texture final : public egl::ImageSibling,
const Rectangle &sourceArea,
const Framebuffer *source);
Error setStorage(GLenum target, size_t levels, GLenum internalFormat, const Extents &size);
Error setStorage(GLenum target, GLsizei levels, GLenum internalFormat, const Extents &size);
Error setEGLImageTarget(GLenum target, egl::Image *imageTarget);
Error generateMipmaps();
Error generateMipmap();
egl::Surface *getBoundSurface() const;
egl::Stream *getBoundStream() const;
......
......@@ -67,7 +67,7 @@ class TextureImpl : public FramebufferAttachmentObjectImpl
egl::Stream *stream,
const egl::Stream::GLTextureDescription &desc) = 0;
virtual gl::Error generateMipmaps() = 0;
virtual gl::Error generateMipmap() = 0;
virtual void setBaseLevel(GLuint baseLevel) = 0;
......
......@@ -31,7 +31,7 @@ class MockTextureImpl : public TextureImpl
MOCK_METHOD3(setImageExternal,
gl::Error(GLenum, egl::Stream *, const egl::Stream::GLTextureDescription &));
MOCK_METHOD2(setEGLImageTarget, gl::Error(GLenum, egl::Image *));
MOCK_METHOD0(generateMipmaps, gl::Error());
MOCK_METHOD0(generateMipmap, gl::Error());
MOCK_METHOD1(bindTexImage, void(egl::Surface *));
MOCK_METHOD0(releaseTexImage, void(void));
......
......@@ -187,8 +187,8 @@ class RendererD3D : public BufferFactoryD3D
// Image operations
virtual ImageD3D *createImage() = 0;
virtual gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) = 0;
virtual gl::Error generateMipmapsUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState) = 0;
virtual gl::Error generateMipmapUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState) = 0;
virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain) = 0;
virtual TextureStorage *createTextureStorageEGLImage(EGLImageD3D *eglImage) = 0;
virtual TextureStorage *createTextureStorageExternal(
......
......@@ -115,11 +115,13 @@ gl::Error TextureD3D::getNativeTexture(TextureStorage **outStorage)
GLint TextureD3D::getLevelZeroWidth() const
{
ASSERT(gl::CountLeadingZeros(static_cast<uint32_t>(getBaseLevelWidth())) > getBaseLevel());
return getBaseLevelWidth() << mBaseLevel;
}
GLint TextureD3D::getLevelZeroHeight() const
{
ASSERT(gl::CountLeadingZeros(static_cast<uint32_t>(getBaseLevelHeight())) > getBaseLevel());
return getBaseLevelHeight() << mBaseLevel;
}
......@@ -374,13 +376,6 @@ GLint TextureD3D::creationLevels(GLsizei width, GLsizei height, GLsizei depth) c
}
}
int TextureD3D::mipLevels() const
{
return gl::log2(
std::max(std::max(getLevelZeroWidth(), getLevelZeroHeight()), getLevelZeroDepth())) +
1;
}
TextureStorage *TextureD3D::getStorage()
{
ASSERT(mTexStorage);
......@@ -405,14 +400,11 @@ gl::Error TextureD3D::setImageExternal(GLenum target,
return gl::Error(GL_INVALID_OPERATION);
}
gl::Error TextureD3D::generateMipmaps()
gl::Error TextureD3D::generateMipmap()
{
GLint mipCount = mipLevels();
if (mipCount == 1)
{
return gl::Error(GL_NO_ERROR); // no-op
}
const GLuint baseLevel = mState.getEffectiveBaseLevel();
const GLuint maxLevel = mState.getMipmapMaxLevel();
ASSERT(maxLevel > baseLevel); // Should be checked before calling this.
if (mTexStorage && mRenderer->getWorkarounds().zeroMaxLodWorkaround)
{
......@@ -432,7 +424,7 @@ gl::Error TextureD3D::generateMipmaps()
}
// Set up proper mipmap chain in our Image array.
initMipmapsImages();
initMipmapImages();
if (mTexStorage && mTexStorage->supportsNativeMipmapFunction())
{
......@@ -443,7 +435,7 @@ gl::Error TextureD3D::generateMipmaps()
}
// Generate the mipmap chain using the ad-hoc DirectX function.
error = mRenderer->generateMipmapsUsingD3D(mTexStorage, mState);
error = mRenderer->generateMipmapUsingD3D(mTexStorage, mState);
if (error.isError())
{
return error;
......@@ -452,7 +444,7 @@ gl::Error TextureD3D::generateMipmaps()
else
{
// Generate the mipmap chain, one level at a time.
gl::Error error = generateMipmapsUsingImages();
gl::Error error = generateMipmapUsingImages(maxLevel);
if (error.isError())
{
return error;
......@@ -462,12 +454,10 @@ gl::Error TextureD3D::generateMipmaps()
return gl::Error(GL_NO_ERROR);
}
gl::Error TextureD3D::generateMipmapsUsingImages()
gl::Error TextureD3D::generateMipmapUsingImages(const GLuint maxLevel)
{
GLint mipCount = mipLevels();
// We know that all layers have the same dimension, for the texture to be complete
GLint layerCount = static_cast<GLint>(getLayerCount(getBaseLevel()));
GLint layerCount = static_cast<GLint>(getLayerCount(mBaseLevel));
// When making mipmaps with the setData workaround enabled, the texture storage has
// the image data already. For non-render-target storage, we have to pull it out into
......@@ -479,7 +469,7 @@ gl::Error TextureD3D::generateMipmapsUsingImages()
// Copy from the storage mip 0 to Image mip 0
for (GLint layer = 0; layer < layerCount; ++layer)
{
gl::ImageIndex srcIndex = getImageIndex(0, layer);
gl::ImageIndex srcIndex = getImageIndex(mBaseLevel, layer);
ImageD3D *image = getImage(srcIndex);
gl::Error error = image->copyFromTexStorage(srcIndex, mTexStorage);
......@@ -508,7 +498,7 @@ gl::Error TextureD3D::generateMipmapsUsingImages()
for (GLint layer = 0; layer < layerCount; ++layer)
{
for (GLint mip = 1; mip < mipCount; ++mip)
for (GLuint mip = mBaseLevel + 1; mip <= maxLevel; ++mip)
{
ASSERT(getLayerCount(mip) == layerCount);
......@@ -1065,11 +1055,13 @@ gl::Error TextureD3D_2D::setEGLImageTarget(GLenum target, egl::Image *image)
return gl::Error(GL_NO_ERROR);
}
void TextureD3D_2D::initMipmapsImages()
void TextureD3D_2D::initMipmapImages()
{
// Purge array levels 1 through q and reset them to represent the generated mipmap levels.
int levelCount = mipLevels();
for (int level = 1; level < levelCount; level++)
const GLuint baseLevel = mState.getEffectiveBaseLevel();
const GLuint maxLevel = mState.getMipmapMaxLevel();
// Purge array levels baseLevel + 1 through q and reset them to represent the generated mipmap
// levels.
for (GLuint level = baseLevel + 1; level <= maxLevel; level++)
{
gl::Extents levelSize(std::max(getLevelZeroWidth() >> level, 1),
std::max(getLevelZeroHeight() >> level, 1), 1);
......@@ -1658,17 +1650,19 @@ void TextureD3D_Cube::releaseTexImage()
UNREACHABLE();
}
void TextureD3D_Cube::initMipmapsImages()
void TextureD3D_Cube::initMipmapImages()
{
// Purge array levels 1 through q and reset them to represent the generated mipmap levels.
int levelCount = mipLevels();
const GLuint baseLevel = mState.getEffectiveBaseLevel();
const GLuint maxLevel = mState.getMipmapMaxLevel();
// Purge array levels baseLevel + 1 through q and reset them to represent the generated mipmap
// levels.
for (int faceIndex = 0; faceIndex < 6; faceIndex++)
{
for (int level = 1; level < levelCount; level++)
for (GLuint level = baseLevel + 1; level <= maxLevel; level++)
{
int faceLevelSize = (std::max(mImageArray[faceIndex][0]->getWidth() >> level, 1));
redefineImage(faceIndex, level, mImageArray[faceIndex][0]->getInternalFormat(),
int faceLevelSize =
(std::max(mImageArray[faceIndex][baseLevel]->getWidth() >> (level - baseLevel), 1));
redefineImage(faceIndex, level, mImageArray[faceIndex][baseLevel]->getInternalFormat(),
gl::Extents(faceLevelSize, faceLevelSize, 1));
}
}
......@@ -2257,12 +2251,13 @@ void TextureD3D_3D::releaseTexImage()
UNREACHABLE();
}
void TextureD3D_3D::initMipmapsImages()
void TextureD3D_3D::initMipmapImages()
{
// Purge array levels 1 through q and reset them to represent the generated mipmap levels.
int levelCount = mipLevels();
for (int level = 1; level < levelCount; level++)
const GLuint baseLevel = mState.getEffectiveBaseLevel();
const GLuint maxLevel = mState.getMipmapMaxLevel();
// Purge array levels baseLevel + 1 through q and reset them to represent the generated mipmap
// levels.
for (GLuint level = baseLevel + 1; level <= maxLevel; level++)
{
gl::Extents levelSize(std::max(getLevelZeroWidth() >> level, 1),
std::max(getLevelZeroHeight() >> level, 1),
......@@ -2523,6 +2518,7 @@ void TextureD3D_3D::markAllImagesDirty()
GLint TextureD3D_3D::getLevelZeroDepth() const
{
ASSERT(gl::CountLeadingZeros(static_cast<uint32_t>(getBaseLevelDepth())) > getBaseLevel());
return getBaseLevelDepth() << getBaseLevel();
}
......@@ -2847,18 +2843,20 @@ void TextureD3D_2DArray::releaseTexImage()
UNREACHABLE();
}
void TextureD3D_2DArray::initMipmapsImages()
void TextureD3D_2DArray::initMipmapImages()
{
const GLuint baseLevel = mState.getEffectiveBaseLevel();
const GLuint maxLevel = mState.getMipmapMaxLevel();
int baseWidth = getLevelZeroWidth();
int baseHeight = getLevelZeroHeight();
int baseDepth = getLayerCount(getBaseLevel());
GLenum baseFormat = getBaseLevelInternalFormat();
// Purge array levels 1 through q and reset them to represent the generated mipmap levels.
int levelCount = mipLevels();
for (int level = 1; level < levelCount; level++)
// Purge array levels baseLevel + 1 through q and reset them to represent the generated mipmap
// levels.
for (GLuint level = baseLevel + 1u; level <= maxLevel; level++)
{
ASSERT((baseWidth >> level) > 0 || (baseHeight >> level) > 0);
gl::Extents levelLayerSize(std::max(baseWidth >> level, 1),
std::max(baseHeight >> level, 1),
baseDepth);
......@@ -3311,7 +3309,7 @@ gl::Error TextureD3D_External::setEGLImageTarget(GLenum target, egl::Image *imag
return gl::NoError();
}
void TextureD3D_External::initMipmapsImages()
void TextureD3D_External::initMipmapImages()
{
UNREACHABLE();
}
......
......@@ -60,7 +60,7 @@ class TextureD3D : public TextureImpl
virtual gl::Error setImageExternal(GLenum target,
egl::Stream *stream,
const egl::Stream::GLTextureDescription &desc) override;
gl::Error generateMipmaps() override;
gl::Error generateMipmap() override;
TextureStorage *getStorage();
ImageD3D *getBaseLevelImage() const;
......@@ -92,8 +92,7 @@ class TextureD3D : public TextureImpl
virtual GLint getLevelZeroDepth() const;
GLint creationLevels(GLsizei width, GLsizei height, GLsizei depth) const;
int mipLevels() const;
virtual void initMipmapsImages() = 0;
virtual void initMipmapImages() = 0;
bool isBaseImageZeroSize() const;
virtual bool isImageComplete(const gl::ImageIndex &index) const = 0;
......@@ -124,7 +123,7 @@ class TextureD3D : public TextureImpl
bool shouldUseSetData(const ImageD3D *image) const;
gl::Error generateMipmapsUsingImages();
gl::Error generateMipmapUsingImages(const GLuint maxLevel);
GLuint mBaseLevel;
};
......@@ -181,7 +180,7 @@ class TextureD3D_2D : public TextureD3D
virtual gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage);
virtual gl::Error updateStorage();
virtual void initMipmapsImages();
virtual void initMipmapImages();
bool isValidLevel(int level) const;
bool isLevelComplete(int level) const;
......@@ -251,7 +250,7 @@ class TextureD3D_Cube : public TextureD3D
virtual gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage);
virtual gl::Error updateStorage();
virtual void initMipmapsImages();
void initMipmapImages() override;
bool isValidFaceLevel(int faceIndex, int level) const;
bool isFaceLevelComplete(int faceIndex, int level) const;
......@@ -318,7 +317,7 @@ class TextureD3D_3D : public TextureD3D
virtual gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage);
virtual gl::Error updateStorage();
virtual void initMipmapsImages();
void initMipmapImages() override;
bool isValidLevel(int level) const;
bool isLevelComplete(int level) const;
......@@ -382,7 +381,7 @@ class TextureD3D_2DArray : public TextureD3D
virtual gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage);
virtual gl::Error updateStorage();
virtual void initMipmapsImages();
void initMipmapImages() override;
bool isValidLevel(int level) const;
bool isLevelComplete(int level) const;
......@@ -481,7 +480,7 @@ class TextureD3D_External : public TextureD3D
gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage) override;
gl::Error updateStorage() override;
void initMipmapsImages() override;
void initMipmapImages() override;
bool isImageComplete(const gl::ImageIndex &index) const override;
};
......
......@@ -3598,8 +3598,8 @@ gl::Error Renderer11::generateMipmap(ImageD3D *dest, ImageD3D *src)
return Image11::generateMipmap(dest11, src11, mRenderer11DeviceCaps);
}
gl::Error Renderer11::generateMipmapsUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState)
gl::Error Renderer11::generateMipmapUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState)
{
TextureStorage11 *storage11 = GetAs<TextureStorage11>(storage);
......@@ -3607,8 +3607,8 @@ gl::Error Renderer11::generateMipmapsUsingD3D(TextureStorage *storage,
ASSERT(storage11->supportsNativeMipmapFunction());
ID3D11ShaderResourceView *srv;
gl::Error error =
storage11->getSRVLevels(textureState.mBaseLevel, textureState.mMaxLevel, &srv);
gl::Error error = storage11->getSRVLevels(textureState.getEffectiveBaseLevel(),
textureState.getEffectiveMaxLevel(), &srv);
if (error.isError())
{
return error;
......
......@@ -229,8 +229,8 @@ class Renderer11 : public RendererD3D
// Image operations
ImageD3D *createImage() override;
gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override;
gl::Error generateMipmapsUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState) override;
gl::Error generateMipmapUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState) override;
TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain) override;
TextureStorage *createTextureStorageEGLImage(EGLImageD3D *eglImage) override;
TextureStorage *createTextureStorageExternal(
......
......@@ -2638,8 +2638,8 @@ gl::Error Renderer9::generateMipmap(ImageD3D *dest, ImageD3D *src)
return Image9::generateMipmap(dst9, src9);
}
gl::Error Renderer9::generateMipmapsUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState)
gl::Error Renderer9::generateMipmapUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState)
{
UNREACHABLE();
return gl::Error(GL_NO_ERROR);
......
......@@ -226,8 +226,8 @@ class Renderer9 : public RendererD3D
// Image operations
ImageD3D *createImage() override;
gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override;
gl::Error generateMipmapsUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState) override;
gl::Error generateMipmapUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState) override;
virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain);
TextureStorage *createTextureStorageEGLImage(EGLImageD3D *eglImage) override;
TextureStorage *createTextureStorageExternal(
......
......@@ -496,14 +496,21 @@ gl::Error TextureGL::setImageExternal(GLenum target,
return gl::Error(GL_INVALID_OPERATION);
}
gl::Error TextureGL::generateMipmaps()
gl::Error TextureGL::generateMipmap()
{
// Need to sync base level and max level to driver before calling GenerateMipmap.
syncState(0);
mStateManager->bindTexture(mState.mTarget, mTextureID);
mFunctions->generateMipmap(mState.mTarget);
for (size_t level = mState.mBaseLevel; level < mLevelInfo.size(); level++)
const GLuint effectiveBaseLevel = mState.getEffectiveBaseLevel();
const GLuint maxLevel = mState.getMipmapMaxLevel();
ASSERT(maxLevel < mLevelInfo.size());
for (GLuint level = effectiveBaseLevel; level <= maxLevel; level++)
{
mLevelInfo[level] = mLevelInfo[mState.mBaseLevel];
mLevelInfo[level] = mLevelInfo[effectiveBaseLevel];
}
return gl::Error(GL_NO_ERROR);
......
......@@ -80,7 +80,7 @@ class TextureGL : public TextureImpl
egl::Stream *stream,
const egl::Stream::GLTextureDescription &desc) override;
gl::Error generateMipmaps() override;
gl::Error generateMipmap() override;
void bindTexImage(egl::Surface *surface) override;
void releaseTexImage() override;
......
......@@ -135,7 +135,7 @@ egl::Error ValidateCreateImageKHRMipLevelCommon(gl::Context *context,
const GLuint effectiveBaseLevel = texture->getTextureState().getEffectiveBaseLevel();
if (level > 0 &&
(!texture->isMipmapComplete() || static_cast<GLuint>(level) < effectiveBaseLevel ||
static_cast<size_t>(level) > texture->getTextureState().getMipmapMaxLevel()))
static_cast<GLuint>(level) > texture->getTextureState().getMipmapMaxLevel()))
{
return egl::Error(EGL_BAD_PARAMETER, "texture must be complete if level is non-zero.");
}
......
......@@ -2925,6 +2925,83 @@ bool ValidateFlushMappedBufferRangeBase(Context *context,
return true;
}
bool ValidateGenerateMipmap(Context *context, GLenum target)
{
if (!ValidTextureTarget(context, target))
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
Texture *texture = context->getTargetTexture(target);
if (texture == nullptr)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
const GLuint effectiveBaseLevel = texture->getTextureState().getEffectiveBaseLevel();
// This error isn't spelled out in the spec in a very explicit way, but we interpret the spec so
// that out-of-range base level has a non-color-renderable / non-texture-filterable format.
if (effectiveBaseLevel >= gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
GLenum baseTarget = (target == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
GLenum internalFormat = texture->getInternalFormat(baseTarget, effectiveBaseLevel);
const TextureCaps &formatCaps = context->getTextureCaps().get(internalFormat);
const InternalFormat &formatInfo = GetInternalFormatInfo(internalFormat);
// GenerateMipmap should not generate an INVALID_OPERATION for textures created with
// unsized formats or that are color renderable and filterable. Since we do not track if
// the texture was created with sized or unsized format (only sized formats are stored),
// it is not possible to make sure the the LUMA formats can generate mipmaps (they should
// be able to) because they aren't color renderable. Simply do a special case for LUMA
// textures since they're the only texture format that can be created with unsized formats
// that is not color renderable. New unsized formats are unlikely to be added, since ES2
// was the last version to use add them.
bool isLUMA = internalFormat == GL_LUMINANCE8_EXT ||
internalFormat == GL_LUMINANCE8_ALPHA8_EXT || internalFormat == GL_ALPHA8_EXT;
if (formatInfo.depthBits > 0 || formatInfo.stencilBits > 0 || !formatCaps.filterable ||
(!formatCaps.renderable && !isLUMA) || formatInfo.compressed)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
// GL_EXT_sRGB does not support mipmap generation on sRGB textures
if (context->getClientVersion() == 2 && formatInfo.colorEncoding == GL_SRGB)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
// Non-power of 2 ES2 check
if (!context->getExtensions().textureNPOT &&
(!isPow2(static_cast<int>(texture->getWidth(baseTarget, 0))) ||
!isPow2(static_cast<int>(texture->getHeight(baseTarget, 0)))))
{
ASSERT(context->getClientVersion() <= 2 &&
(target == GL_TEXTURE_2D || target == GL_TEXTURE_CUBE_MAP));
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
// Cube completeness check
if (target == GL_TEXTURE_CUBE_MAP && !texture->getTextureState().isCubeComplete())
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
return true;
}
bool ValidateGenBuffers(Context *context, GLint n, GLuint *)
{
return ValidateGenOrDelete(context, n);
......
......@@ -248,6 +248,8 @@ bool ValidateFlushMappedBufferRangeBase(Context *context,
GLintptr offset,
GLsizeiptr length);
bool ValidateGenerateMipmap(Context *context, GLenum target);
bool ValidateGenBuffers(Context *context, GLint n, GLuint *buffers);
bool ValidateDeleteBuffers(Context *context, GLint n, const GLuint *buffers);
bool ValidateGenFramebuffers(Context *context, GLint n, GLuint *framebuffers);
......
......@@ -1242,74 +1242,12 @@ void GL_APIENTRY GenerateMipmap(GLenum target)
Context *context = GetValidGlobalContext();
if (context)
{
if (!ValidTextureTarget(context, target))
if (!context->skipValidation() && !ValidateGenerateMipmap(context, target))
{
context->handleError(Error(GL_INVALID_ENUM));
return;
}
Texture *texture = context->getTargetTexture(target);
if (texture == NULL)
{
context->handleError(Error(GL_INVALID_OPERATION));
return;
}
GLenum baseTarget = (target == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
GLenum internalFormat = texture->getInternalFormat(baseTarget, texture->getBaseLevel());
const TextureCaps &formatCaps = context->getTextureCaps().get(internalFormat);
const InternalFormat &formatInfo = GetInternalFormatInfo(internalFormat);
// GenerateMipmap should not generate an INVALID_OPERATION for textures created with
// unsized formats or that are color renderable and filterable. Since we do not track if
// the texture was created with sized or unsized format (only sized formats are stored),
// it is not possible to make sure the the LUMA formats can generate mipmaps (they should
// be able to) because they aren't color renderable. Simply do a special case for LUMA
// textures since they're the only texture format that can be created with unsized formats
// that is not color renderable. New unsized formats are unlikely to be added, since ES2
// was the last version to use add them.
bool isLUMA = internalFormat == GL_LUMINANCE8_EXT ||
internalFormat == GL_LUMINANCE8_ALPHA8_EXT ||
internalFormat == GL_ALPHA8_EXT;
if (formatInfo.depthBits > 0 || formatInfo.stencilBits > 0 || !formatCaps.filterable ||
(!formatCaps.renderable && !isLUMA) || formatInfo.compressed)
{
context->handleError(Error(GL_INVALID_OPERATION));
return;
}
// GL_EXT_sRGB does not support mipmap generation on sRGB textures
if (context->getClientVersion() == 2 && formatInfo.colorEncoding == GL_SRGB)
{
context->handleError(Error(GL_INVALID_OPERATION));
return;
}
// Non-power of 2 ES2 check
if (!context->getExtensions().textureNPOT &&
(!isPow2(static_cast<int>(texture->getWidth(baseTarget, 0))) ||
!isPow2(static_cast<int>(texture->getHeight(baseTarget, 0)))))
{
ASSERT(context->getClientVersion() <= 2 && (target == GL_TEXTURE_2D || target == GL_TEXTURE_CUBE_MAP));
context->handleError(Error(GL_INVALID_OPERATION));
return;
}
// Cube completeness check
if (target == GL_TEXTURE_CUBE_MAP && !texture->getTextureState().isCubeComplete())
{
context->handleError(Error(GL_INVALID_OPERATION));
return;
}
Error error = texture->generateMipmaps();
if (error.isError())
{
context->handleError(error);
return;
}
context->generateMipmap(target);
}
}
......
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