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) ...@@ -467,6 +467,43 @@ inline T shiftData(T input)
return (input & mask) << inputBitStart; 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) inline unsigned char average(unsigned char a, unsigned char b)
{ {
......
...@@ -168,5 +168,14 @@ TEST(MathUtilTest, isInf) ...@@ -168,5 +168,14 @@ TEST(MathUtilTest, isInf)
EXPECT_FALSE(isInf(bitCast<float>(1u << 31 | 0xfeu << 23 | 0x7fffffu))); 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, ...@@ -2643,6 +2643,12 @@ void Context::compressedTexSubImage3D(GLenum target,
imageSize, reinterpret_cast<const uint8_t *>(data))); 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) void Context::getBufferPointerv(GLenum target, GLenum /*pname*/, void **params)
{ {
Buffer *buffer = getState().getTargetBuffer(target); Buffer *buffer = getState().getTargetBuffer(target);
......
...@@ -370,6 +370,8 @@ class Context final : public ValidationContext ...@@ -370,6 +370,8 @@ class Context final : public ValidationContext
GLsizei imageSize, GLsizei imageSize,
const GLvoid *data); const GLvoid *data);
void generateMipmap(GLenum target);
Error flush(); Error flush();
Error finish(); Error finish();
......
...@@ -131,22 +131,23 @@ GLuint TextureState::getEffectiveMaxLevel() const ...@@ -131,22 +131,23 @@ GLuint TextureState::getEffectiveMaxLevel() const
return mMaxLevel; return mMaxLevel;
} }
size_t TextureState::getMipmapMaxLevel() const GLuint TextureState::getMipmapMaxLevel() const
{ {
const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel()); const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel());
size_t expectedMipLevels = 0; GLuint expectedMipLevels = 0;
if (mTarget == GL_TEXTURE_3D) if (mTarget == GL_TEXTURE_3D)
{ {
const int maxDim = std::max(std::max(baseImageDesc.size.width, baseImageDesc.size.height), const int maxDim = std::max(std::max(baseImageDesc.size.width, baseImageDesc.size.height),
baseImageDesc.size.depth); baseImageDesc.size.depth);
expectedMipLevels = log2(maxDim); expectedMipLevels = static_cast<GLuint>(log2(maxDim));
} }
else 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) bool TextureState::setBaseLevel(GLuint baseLevel)
...@@ -320,9 +321,9 @@ bool TextureState::computeSamplerCompleteness(const SamplerState &samplerState, ...@@ -320,9 +321,9 @@ bool TextureState::computeSamplerCompleteness(const SamplerState &samplerState,
bool TextureState::computeMipmapCompleteness() const 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) if (mTarget == GL_TEXTURE_CUBE_MAP)
{ {
...@@ -433,14 +434,19 @@ void TextureState::setImageDesc(GLenum target, size_t level, const ImageDesc &de ...@@ -433,14 +434,19 @@ void TextureState::setImageDesc(GLenum target, size_t level, const ImageDesc &de
mCompletenessCache.cacheValid = false; 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( int relativeLevel = (level - baseLevel);
std::max<int>(baseSize.width >> level, 1), std::max<int>(baseSize.height >> level, 1), Extents levelSize(std::max<int>(baseSize.width >> relativeLevel, 1),
(mTarget == GL_TEXTURE_2D_ARRAY) ? baseSize.depth std::max<int>(baseSize.height >> relativeLevel, 1),
: std::max<int>(baseSize.depth >> level, 1)); (mTarget == GL_TEXTURE_2D_ARRAY)
? baseSize.depth
: std::max<int>(baseSize.depth >> relativeLevel, 1));
ImageDesc levelInfo(levelSize, sizedInternalFormat); ImageDesc levelInfo(levelSize, sizedInternalFormat);
if (mTarget == GL_TEXTURE_CUBE_MAP) if (mTarget == GL_TEXTURE_CUBE_MAP)
...@@ -874,7 +880,7 @@ Error Texture::copySubImage(GLenum target, size_t level, const Offset &destOffse ...@@ -874,7 +880,7 @@ Error Texture::copySubImage(GLenum target, size_t level, const Offset &destOffse
return mTexture->copySubImage(target, level, destOffset, sourceArea, source); 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); ASSERT(target == mState.mTarget);
...@@ -891,12 +897,11 @@ Error Texture::setStorage(GLenum target, size_t levels, GLenum internalFormat, c ...@@ -891,12 +897,11 @@ Error Texture::setStorage(GLenum target, size_t levels, GLenum internalFormat, c
mState.mImmutableFormat = true; mState.mImmutableFormat = true;
mState.mImmutableLevels = static_cast<GLuint>(levels); mState.mImmutableLevels = static_cast<GLuint>(levels);
mState.clearImageDescs(); mState.clearImageDescs();
mState.setImageDescChain(levels, size, internalFormat); mState.setImageDescChain(0, static_cast<GLuint>(levels - 1), size, internalFormat);
return Error(GL_NO_ERROR); return Error(GL_NO_ERROR);
} }
Error Texture::generateMipmaps() Error Texture::generateMipmap()
{ {
// Release from previous calls to eglBindTexImage, to avoid calling the Impl after // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
releaseTexImageInternal(); releaseTexImageInternal();
...@@ -908,17 +913,20 @@ Error Texture::generateMipmaps() ...@@ -908,17 +913,20 @@ Error Texture::generateMipmaps()
orphanImages(); orphanImages();
} }
Error error = mTexture->generateMipmaps(); const GLuint baseLevel = mState.getEffectiveBaseLevel();
if (error.isError()) const GLuint maxLevel = mState.getMipmapMaxLevel();
if (maxLevel > baseLevel)
{ {
return error; ANGLE_TRY(mTexture->generateMipmap());
}
const ImageDesc &baseImageInfo = mState.getImageDesc(mState.getBaseImageTarget(), 0); const ImageDesc &baseImageInfo =
size_t mipLevels = log2(std::max(std::max(baseImageInfo.size.width, baseImageInfo.size.height), baseImageInfo.size.depth)) + 1; mState.getImageDesc(mState.getBaseImageTarget(), baseLevel);
mState.setImageDescChain(mipLevels, baseImageInfo.size, baseImageInfo.internalFormat); mState.setImageDescChain(baseLevel, maxLevel, baseImageInfo.size,
baseImageInfo.internalFormat);
}
return Error(GL_NO_ERROR); return NoError();
} }
void Texture::bindTexImageFromSurface(egl::Surface *surface) void Texture::bindTexImageFromSurface(egl::Surface *surface)
......
...@@ -34,7 +34,6 @@ namespace rx ...@@ -34,7 +34,6 @@ namespace rx
class GLImplFactory; class GLImplFactory;
class TextureImpl; class TextureImpl;
class TextureGL; class TextureGL;
class Renderer11;
} }
namespace gl namespace gl
...@@ -85,7 +84,7 @@ struct TextureState final : public angle::NonCopyable ...@@ -85,7 +84,7 @@ struct TextureState final : public angle::NonCopyable
GLuint getEffectiveMaxLevel() const; GLuint getEffectiveMaxLevel() const;
// Returns the value called "q" in the GLES 3.0.4 spec section 3.8.10. // 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. // Returns true if base level changed.
bool setBaseLevel(GLuint baseLevel); bool setBaseLevel(GLuint baseLevel);
...@@ -108,9 +107,6 @@ struct TextureState final : public angle::NonCopyable ...@@ -108,9 +107,6 @@ struct TextureState final : public angle::NonCopyable
friend class rx::TextureGL; friend class rx::TextureGL;
friend bool operator==(const TextureState &a, const TextureState &b); 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, bool computeSamplerCompleteness(const SamplerState &samplerState,
const ContextState &data) const; const ContextState &data) const;
bool computeMipmapCompleteness() const; bool computeMipmapCompleteness() const;
...@@ -119,7 +115,10 @@ struct TextureState final : public angle::NonCopyable ...@@ -119,7 +115,10 @@ struct TextureState final : public angle::NonCopyable
GLenum getBaseImageTarget() const; GLenum getBaseImageTarget() const;
void setImageDesc(GLenum target, size_t level, const ImageDesc &desc); 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 clearImageDesc(GLenum target, size_t level);
void clearImageDescs(); void clearImageDescs();
...@@ -283,11 +282,11 @@ class Texture final : public egl::ImageSibling, ...@@ -283,11 +282,11 @@ class Texture final : public egl::ImageSibling,
const Rectangle &sourceArea, const Rectangle &sourceArea,
const Framebuffer *source); 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 setEGLImageTarget(GLenum target, egl::Image *imageTarget);
Error generateMipmaps(); Error generateMipmap();
egl::Surface *getBoundSurface() const; egl::Surface *getBoundSurface() const;
egl::Stream *getBoundStream() const; egl::Stream *getBoundStream() const;
......
...@@ -67,7 +67,7 @@ class TextureImpl : public FramebufferAttachmentObjectImpl ...@@ -67,7 +67,7 @@ class TextureImpl : public FramebufferAttachmentObjectImpl
egl::Stream *stream, egl::Stream *stream,
const egl::Stream::GLTextureDescription &desc) = 0; const egl::Stream::GLTextureDescription &desc) = 0;
virtual gl::Error generateMipmaps() = 0; virtual gl::Error generateMipmap() = 0;
virtual void setBaseLevel(GLuint baseLevel) = 0; virtual void setBaseLevel(GLuint baseLevel) = 0;
......
...@@ -31,7 +31,7 @@ class MockTextureImpl : public TextureImpl ...@@ -31,7 +31,7 @@ class MockTextureImpl : public TextureImpl
MOCK_METHOD3(setImageExternal, MOCK_METHOD3(setImageExternal,
gl::Error(GLenum, egl::Stream *, const egl::Stream::GLTextureDescription &)); gl::Error(GLenum, egl::Stream *, const egl::Stream::GLTextureDescription &));
MOCK_METHOD2(setEGLImageTarget, gl::Error(GLenum, egl::Image *)); 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_METHOD1(bindTexImage, void(egl::Surface *));
MOCK_METHOD0(releaseTexImage, void(void)); MOCK_METHOD0(releaseTexImage, void(void));
......
...@@ -187,8 +187,8 @@ class RendererD3D : public BufferFactoryD3D ...@@ -187,8 +187,8 @@ class RendererD3D : public BufferFactoryD3D
// Image operations // Image operations
virtual ImageD3D *createImage() = 0; virtual ImageD3D *createImage() = 0;
virtual gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) = 0; virtual gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) = 0;
virtual gl::Error generateMipmapsUsingD3D(TextureStorage *storage, virtual gl::Error generateMipmapUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState) = 0; const gl::TextureState &textureState) = 0;
virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain) = 0; virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain) = 0;
virtual TextureStorage *createTextureStorageEGLImage(EGLImageD3D *eglImage) = 0; virtual TextureStorage *createTextureStorageEGLImage(EGLImageD3D *eglImage) = 0;
virtual TextureStorage *createTextureStorageExternal( virtual TextureStorage *createTextureStorageExternal(
......
...@@ -115,11 +115,13 @@ gl::Error TextureD3D::getNativeTexture(TextureStorage **outStorage) ...@@ -115,11 +115,13 @@ gl::Error TextureD3D::getNativeTexture(TextureStorage **outStorage)
GLint TextureD3D::getLevelZeroWidth() const GLint TextureD3D::getLevelZeroWidth() const
{ {
ASSERT(gl::CountLeadingZeros(static_cast<uint32_t>(getBaseLevelWidth())) > getBaseLevel());
return getBaseLevelWidth() << mBaseLevel; return getBaseLevelWidth() << mBaseLevel;
} }
GLint TextureD3D::getLevelZeroHeight() const GLint TextureD3D::getLevelZeroHeight() const
{ {
ASSERT(gl::CountLeadingZeros(static_cast<uint32_t>(getBaseLevelHeight())) > getBaseLevel());
return getBaseLevelHeight() << mBaseLevel; return getBaseLevelHeight() << mBaseLevel;
} }
...@@ -374,13 +376,6 @@ GLint TextureD3D::creationLevels(GLsizei width, GLsizei height, GLsizei depth) c ...@@ -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() TextureStorage *TextureD3D::getStorage()
{ {
ASSERT(mTexStorage); ASSERT(mTexStorage);
...@@ -405,14 +400,11 @@ gl::Error TextureD3D::setImageExternal(GLenum target, ...@@ -405,14 +400,11 @@ gl::Error TextureD3D::setImageExternal(GLenum target,
return gl::Error(GL_INVALID_OPERATION); return gl::Error(GL_INVALID_OPERATION);
} }
gl::Error TextureD3D::generateMipmaps() gl::Error TextureD3D::generateMipmap()
{ {
GLint mipCount = mipLevels(); const GLuint baseLevel = mState.getEffectiveBaseLevel();
const GLuint maxLevel = mState.getMipmapMaxLevel();
if (mipCount == 1) ASSERT(maxLevel > baseLevel); // Should be checked before calling this.
{
return gl::Error(GL_NO_ERROR); // no-op
}
if (mTexStorage && mRenderer->getWorkarounds().zeroMaxLodWorkaround) if (mTexStorage && mRenderer->getWorkarounds().zeroMaxLodWorkaround)
{ {
...@@ -432,7 +424,7 @@ gl::Error TextureD3D::generateMipmaps() ...@@ -432,7 +424,7 @@ gl::Error TextureD3D::generateMipmaps()
} }
// Set up proper mipmap chain in our Image array. // Set up proper mipmap chain in our Image array.
initMipmapsImages(); initMipmapImages();
if (mTexStorage && mTexStorage->supportsNativeMipmapFunction()) if (mTexStorage && mTexStorage->supportsNativeMipmapFunction())
{ {
...@@ -443,7 +435,7 @@ gl::Error TextureD3D::generateMipmaps() ...@@ -443,7 +435,7 @@ gl::Error TextureD3D::generateMipmaps()
} }
// Generate the mipmap chain using the ad-hoc DirectX function. // Generate the mipmap chain using the ad-hoc DirectX function.
error = mRenderer->generateMipmapsUsingD3D(mTexStorage, mState); error = mRenderer->generateMipmapUsingD3D(mTexStorage, mState);
if (error.isError()) if (error.isError())
{ {
return error; return error;
...@@ -452,7 +444,7 @@ gl::Error TextureD3D::generateMipmaps() ...@@ -452,7 +444,7 @@ gl::Error TextureD3D::generateMipmaps()
else else
{ {
// Generate the mipmap chain, one level at a time. // Generate the mipmap chain, one level at a time.
gl::Error error = generateMipmapsUsingImages(); gl::Error error = generateMipmapUsingImages(maxLevel);
if (error.isError()) if (error.isError())
{ {
return error; return error;
...@@ -462,12 +454,10 @@ gl::Error TextureD3D::generateMipmaps() ...@@ -462,12 +454,10 @@ gl::Error TextureD3D::generateMipmaps()
return gl::Error(GL_NO_ERROR); 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 // 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 // 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 // the image data already. For non-render-target storage, we have to pull it out into
...@@ -479,7 +469,7 @@ gl::Error TextureD3D::generateMipmapsUsingImages() ...@@ -479,7 +469,7 @@ gl::Error TextureD3D::generateMipmapsUsingImages()
// Copy from the storage mip 0 to Image mip 0 // Copy from the storage mip 0 to Image mip 0
for (GLint layer = 0; layer < layerCount; ++layer) for (GLint layer = 0; layer < layerCount; ++layer)
{ {
gl::ImageIndex srcIndex = getImageIndex(0, layer); gl::ImageIndex srcIndex = getImageIndex(mBaseLevel, layer);
ImageD3D *image = getImage(srcIndex); ImageD3D *image = getImage(srcIndex);
gl::Error error = image->copyFromTexStorage(srcIndex, mTexStorage); gl::Error error = image->copyFromTexStorage(srcIndex, mTexStorage);
...@@ -508,7 +498,7 @@ gl::Error TextureD3D::generateMipmapsUsingImages() ...@@ -508,7 +498,7 @@ gl::Error TextureD3D::generateMipmapsUsingImages()
for (GLint layer = 0; layer < layerCount; ++layer) 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); ASSERT(getLayerCount(mip) == layerCount);
...@@ -1065,11 +1055,13 @@ gl::Error TextureD3D_2D::setEGLImageTarget(GLenum target, egl::Image *image) ...@@ -1065,11 +1055,13 @@ gl::Error TextureD3D_2D::setEGLImageTarget(GLenum target, egl::Image *image)
return gl::Error(GL_NO_ERROR); 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. const GLuint baseLevel = mState.getEffectiveBaseLevel();
int levelCount = mipLevels(); const GLuint maxLevel = mState.getMipmapMaxLevel();
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 + 1; level <= maxLevel; level++)
{ {
gl::Extents levelSize(std::max(getLevelZeroWidth() >> level, 1), gl::Extents levelSize(std::max(getLevelZeroWidth() >> level, 1),
std::max(getLevelZeroHeight() >> level, 1), 1); std::max(getLevelZeroHeight() >> level, 1), 1);
...@@ -1658,17 +1650,19 @@ void TextureD3D_Cube::releaseTexImage() ...@@ -1658,17 +1650,19 @@ void TextureD3D_Cube::releaseTexImage()
UNREACHABLE(); UNREACHABLE();
} }
void TextureD3D_Cube::initMipmapImages()
void TextureD3D_Cube::initMipmapsImages()
{ {
// Purge array levels 1 through q and reset them to represent the generated mipmap levels. const GLuint baseLevel = mState.getEffectiveBaseLevel();
int levelCount = mipLevels(); 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 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)); int faceLevelSize =
redefineImage(faceIndex, level, mImageArray[faceIndex][0]->getInternalFormat(), (std::max(mImageArray[faceIndex][baseLevel]->getWidth() >> (level - baseLevel), 1));
redefineImage(faceIndex, level, mImageArray[faceIndex][baseLevel]->getInternalFormat(),
gl::Extents(faceLevelSize, faceLevelSize, 1)); gl::Extents(faceLevelSize, faceLevelSize, 1));
} }
} }
...@@ -2257,12 +2251,13 @@ void TextureD3D_3D::releaseTexImage() ...@@ -2257,12 +2251,13 @@ void TextureD3D_3D::releaseTexImage()
UNREACHABLE(); UNREACHABLE();
} }
void TextureD3D_3D::initMipmapImages()
void TextureD3D_3D::initMipmapsImages()
{ {
// Purge array levels 1 through q and reset them to represent the generated mipmap levels. const GLuint baseLevel = mState.getEffectiveBaseLevel();
int levelCount = mipLevels(); const GLuint maxLevel = mState.getMipmapMaxLevel();
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 + 1; level <= maxLevel; level++)
{ {
gl::Extents levelSize(std::max(getLevelZeroWidth() >> level, 1), gl::Extents levelSize(std::max(getLevelZeroWidth() >> level, 1),
std::max(getLevelZeroHeight() >> level, 1), std::max(getLevelZeroHeight() >> level, 1),
...@@ -2523,6 +2518,7 @@ void TextureD3D_3D::markAllImagesDirty() ...@@ -2523,6 +2518,7 @@ void TextureD3D_3D::markAllImagesDirty()
GLint TextureD3D_3D::getLevelZeroDepth() const GLint TextureD3D_3D::getLevelZeroDepth() const
{ {
ASSERT(gl::CountLeadingZeros(static_cast<uint32_t>(getBaseLevelDepth())) > getBaseLevel());
return getBaseLevelDepth() << getBaseLevel(); return getBaseLevelDepth() << getBaseLevel();
} }
...@@ -2847,18 +2843,20 @@ void TextureD3D_2DArray::releaseTexImage() ...@@ -2847,18 +2843,20 @@ void TextureD3D_2DArray::releaseTexImage()
UNREACHABLE(); UNREACHABLE();
} }
void TextureD3D_2DArray::initMipmapImages()
void TextureD3D_2DArray::initMipmapsImages()
{ {
const GLuint baseLevel = mState.getEffectiveBaseLevel();
const GLuint maxLevel = mState.getMipmapMaxLevel();
int baseWidth = getLevelZeroWidth(); int baseWidth = getLevelZeroWidth();
int baseHeight = getLevelZeroHeight(); int baseHeight = getLevelZeroHeight();
int baseDepth = getLayerCount(getBaseLevel()); int baseDepth = getLayerCount(getBaseLevel());
GLenum baseFormat = getBaseLevelInternalFormat(); GLenum baseFormat = getBaseLevelInternalFormat();
// Purge array levels 1 through q and reset them to represent the generated mipmap levels. // Purge array levels baseLevel + 1 through q and reset them to represent the generated mipmap
int levelCount = mipLevels(); // levels.
for (int level = 1; level < levelCount; level++) for (GLuint level = baseLevel + 1u; level <= maxLevel; level++)
{ {
ASSERT((baseWidth >> level) > 0 || (baseHeight >> level) > 0);
gl::Extents levelLayerSize(std::max(baseWidth >> level, 1), gl::Extents levelLayerSize(std::max(baseWidth >> level, 1),
std::max(baseHeight >> level, 1), std::max(baseHeight >> level, 1),
baseDepth); baseDepth);
...@@ -3311,7 +3309,7 @@ gl::Error TextureD3D_External::setEGLImageTarget(GLenum target, egl::Image *imag ...@@ -3311,7 +3309,7 @@ gl::Error TextureD3D_External::setEGLImageTarget(GLenum target, egl::Image *imag
return gl::NoError(); return gl::NoError();
} }
void TextureD3D_External::initMipmapsImages() void TextureD3D_External::initMipmapImages()
{ {
UNREACHABLE(); UNREACHABLE();
} }
......
...@@ -60,7 +60,7 @@ class TextureD3D : public TextureImpl ...@@ -60,7 +60,7 @@ class TextureD3D : public TextureImpl
virtual gl::Error setImageExternal(GLenum target, virtual gl::Error setImageExternal(GLenum target,
egl::Stream *stream, egl::Stream *stream,
const egl::Stream::GLTextureDescription &desc) override; const egl::Stream::GLTextureDescription &desc) override;
gl::Error generateMipmaps() override; gl::Error generateMipmap() override;
TextureStorage *getStorage(); TextureStorage *getStorage();
ImageD3D *getBaseLevelImage() const; ImageD3D *getBaseLevelImage() const;
...@@ -92,8 +92,7 @@ class TextureD3D : public TextureImpl ...@@ -92,8 +92,7 @@ class TextureD3D : public TextureImpl
virtual GLint getLevelZeroDepth() const; virtual GLint getLevelZeroDepth() const;
GLint creationLevels(GLsizei width, GLsizei height, GLsizei depth) const; GLint creationLevels(GLsizei width, GLsizei height, GLsizei depth) const;
int mipLevels() const; virtual void initMipmapImages() = 0;
virtual void initMipmapsImages() = 0;
bool isBaseImageZeroSize() const; bool isBaseImageZeroSize() const;
virtual bool isImageComplete(const gl::ImageIndex &index) const = 0; virtual bool isImageComplete(const gl::ImageIndex &index) const = 0;
...@@ -124,7 +123,7 @@ class TextureD3D : public TextureImpl ...@@ -124,7 +123,7 @@ class TextureD3D : public TextureImpl
bool shouldUseSetData(const ImageD3D *image) const; bool shouldUseSetData(const ImageD3D *image) const;
gl::Error generateMipmapsUsingImages(); gl::Error generateMipmapUsingImages(const GLuint maxLevel);
GLuint mBaseLevel; GLuint mBaseLevel;
}; };
...@@ -181,7 +180,7 @@ class TextureD3D_2D : public TextureD3D ...@@ -181,7 +180,7 @@ class TextureD3D_2D : public TextureD3D
virtual gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage); virtual gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage);
virtual gl::Error updateStorage(); virtual gl::Error updateStorage();
virtual void initMipmapsImages(); virtual void initMipmapImages();
bool isValidLevel(int level) const; bool isValidLevel(int level) const;
bool isLevelComplete(int level) const; bool isLevelComplete(int level) const;
...@@ -251,7 +250,7 @@ class TextureD3D_Cube : public TextureD3D ...@@ -251,7 +250,7 @@ class TextureD3D_Cube : public TextureD3D
virtual gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage); virtual gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage);
virtual gl::Error updateStorage(); virtual gl::Error updateStorage();
virtual void initMipmapsImages(); void initMipmapImages() override;
bool isValidFaceLevel(int faceIndex, int level) const; bool isValidFaceLevel(int faceIndex, int level) const;
bool isFaceLevelComplete(int faceIndex, int level) const; bool isFaceLevelComplete(int faceIndex, int level) const;
...@@ -318,7 +317,7 @@ class TextureD3D_3D : public TextureD3D ...@@ -318,7 +317,7 @@ class TextureD3D_3D : public TextureD3D
virtual gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage); virtual gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage);
virtual gl::Error updateStorage(); virtual gl::Error updateStorage();
virtual void initMipmapsImages(); void initMipmapImages() override;
bool isValidLevel(int level) const; bool isValidLevel(int level) const;
bool isLevelComplete(int level) const; bool isLevelComplete(int level) const;
...@@ -382,7 +381,7 @@ class TextureD3D_2DArray : public TextureD3D ...@@ -382,7 +381,7 @@ class TextureD3D_2DArray : public TextureD3D
virtual gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage); virtual gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage);
virtual gl::Error updateStorage(); virtual gl::Error updateStorage();
virtual void initMipmapsImages(); void initMipmapImages() override;
bool isValidLevel(int level) const; bool isValidLevel(int level) const;
bool isLevelComplete(int level) const; bool isLevelComplete(int level) const;
...@@ -481,7 +480,7 @@ class TextureD3D_External : public TextureD3D ...@@ -481,7 +480,7 @@ class TextureD3D_External : public TextureD3D
gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage) override; gl::Error setCompleteTexStorage(TextureStorage *newCompleteTexStorage) override;
gl::Error updateStorage() override; gl::Error updateStorage() override;
void initMipmapsImages() override; void initMipmapImages() override;
bool isImageComplete(const gl::ImageIndex &index) const override; bool isImageComplete(const gl::ImageIndex &index) const override;
}; };
......
...@@ -3598,8 +3598,8 @@ gl::Error Renderer11::generateMipmap(ImageD3D *dest, ImageD3D *src) ...@@ -3598,8 +3598,8 @@ gl::Error Renderer11::generateMipmap(ImageD3D *dest, ImageD3D *src)
return Image11::generateMipmap(dest11, src11, mRenderer11DeviceCaps); return Image11::generateMipmap(dest11, src11, mRenderer11DeviceCaps);
} }
gl::Error Renderer11::generateMipmapsUsingD3D(TextureStorage *storage, gl::Error Renderer11::generateMipmapUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState) const gl::TextureState &textureState)
{ {
TextureStorage11 *storage11 = GetAs<TextureStorage11>(storage); TextureStorage11 *storage11 = GetAs<TextureStorage11>(storage);
...@@ -3607,8 +3607,8 @@ gl::Error Renderer11::generateMipmapsUsingD3D(TextureStorage *storage, ...@@ -3607,8 +3607,8 @@ gl::Error Renderer11::generateMipmapsUsingD3D(TextureStorage *storage,
ASSERT(storage11->supportsNativeMipmapFunction()); ASSERT(storage11->supportsNativeMipmapFunction());
ID3D11ShaderResourceView *srv; ID3D11ShaderResourceView *srv;
gl::Error error = gl::Error error = storage11->getSRVLevels(textureState.getEffectiveBaseLevel(),
storage11->getSRVLevels(textureState.mBaseLevel, textureState.mMaxLevel, &srv); textureState.getEffectiveMaxLevel(), &srv);
if (error.isError()) if (error.isError())
{ {
return error; return error;
......
...@@ -229,8 +229,8 @@ class Renderer11 : public RendererD3D ...@@ -229,8 +229,8 @@ class Renderer11 : public RendererD3D
// Image operations // Image operations
ImageD3D *createImage() override; ImageD3D *createImage() override;
gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override; gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override;
gl::Error generateMipmapsUsingD3D(TextureStorage *storage, gl::Error generateMipmapUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState) override; const gl::TextureState &textureState) override;
TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain) override; TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain) override;
TextureStorage *createTextureStorageEGLImage(EGLImageD3D *eglImage) override; TextureStorage *createTextureStorageEGLImage(EGLImageD3D *eglImage) override;
TextureStorage *createTextureStorageExternal( TextureStorage *createTextureStorageExternal(
......
...@@ -2638,8 +2638,8 @@ gl::Error Renderer9::generateMipmap(ImageD3D *dest, ImageD3D *src) ...@@ -2638,8 +2638,8 @@ gl::Error Renderer9::generateMipmap(ImageD3D *dest, ImageD3D *src)
return Image9::generateMipmap(dst9, src9); return Image9::generateMipmap(dst9, src9);
} }
gl::Error Renderer9::generateMipmapsUsingD3D(TextureStorage *storage, gl::Error Renderer9::generateMipmapUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState) const gl::TextureState &textureState)
{ {
UNREACHABLE(); UNREACHABLE();
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
......
...@@ -226,8 +226,8 @@ class Renderer9 : public RendererD3D ...@@ -226,8 +226,8 @@ class Renderer9 : public RendererD3D
// Image operations // Image operations
ImageD3D *createImage() override; ImageD3D *createImage() override;
gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override; gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override;
gl::Error generateMipmapsUsingD3D(TextureStorage *storage, gl::Error generateMipmapUsingD3D(TextureStorage *storage,
const gl::TextureState &textureState) override; const gl::TextureState &textureState) override;
virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain); virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain);
TextureStorage *createTextureStorageEGLImage(EGLImageD3D *eglImage) override; TextureStorage *createTextureStorageEGLImage(EGLImageD3D *eglImage) override;
TextureStorage *createTextureStorageExternal( TextureStorage *createTextureStorageExternal(
......
...@@ -496,14 +496,21 @@ gl::Error TextureGL::setImageExternal(GLenum target, ...@@ -496,14 +496,21 @@ gl::Error TextureGL::setImageExternal(GLenum target,
return gl::Error(GL_INVALID_OPERATION); 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); mStateManager->bindTexture(mState.mTarget, mTextureID);
mFunctions->generateMipmap(mState.mTarget); 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); return gl::Error(GL_NO_ERROR);
......
...@@ -80,7 +80,7 @@ class TextureGL : public TextureImpl ...@@ -80,7 +80,7 @@ class TextureGL : public TextureImpl
egl::Stream *stream, egl::Stream *stream,
const egl::Stream::GLTextureDescription &desc) override; const egl::Stream::GLTextureDescription &desc) override;
gl::Error generateMipmaps() override; gl::Error generateMipmap() override;
void bindTexImage(egl::Surface *surface) override; void bindTexImage(egl::Surface *surface) override;
void releaseTexImage() override; void releaseTexImage() override;
......
...@@ -135,7 +135,7 @@ egl::Error ValidateCreateImageKHRMipLevelCommon(gl::Context *context, ...@@ -135,7 +135,7 @@ egl::Error ValidateCreateImageKHRMipLevelCommon(gl::Context *context,
const GLuint effectiveBaseLevel = texture->getTextureState().getEffectiveBaseLevel(); const GLuint effectiveBaseLevel = texture->getTextureState().getEffectiveBaseLevel();
if (level > 0 && if (level > 0 &&
(!texture->isMipmapComplete() || static_cast<GLuint>(level) < effectiveBaseLevel || (!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."); return egl::Error(EGL_BAD_PARAMETER, "texture must be complete if level is non-zero.");
} }
......
...@@ -2925,6 +2925,83 @@ bool ValidateFlushMappedBufferRangeBase(Context *context, ...@@ -2925,6 +2925,83 @@ bool ValidateFlushMappedBufferRangeBase(Context *context,
return true; 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 *) bool ValidateGenBuffers(Context *context, GLint n, GLuint *)
{ {
return ValidateGenOrDelete(context, n); return ValidateGenOrDelete(context, n);
......
...@@ -248,6 +248,8 @@ bool ValidateFlushMappedBufferRangeBase(Context *context, ...@@ -248,6 +248,8 @@ bool ValidateFlushMappedBufferRangeBase(Context *context,
GLintptr offset, GLintptr offset,
GLsizeiptr length); GLsizeiptr length);
bool ValidateGenerateMipmap(Context *context, GLenum target);
bool ValidateGenBuffers(Context *context, GLint n, GLuint *buffers); bool ValidateGenBuffers(Context *context, GLint n, GLuint *buffers);
bool ValidateDeleteBuffers(Context *context, GLint n, const GLuint *buffers); bool ValidateDeleteBuffers(Context *context, GLint n, const GLuint *buffers);
bool ValidateGenFramebuffers(Context *context, GLint n, GLuint *framebuffers); bool ValidateGenFramebuffers(Context *context, GLint n, GLuint *framebuffers);
......
...@@ -1242,74 +1242,12 @@ void GL_APIENTRY GenerateMipmap(GLenum target) ...@@ -1242,74 +1242,12 @@ void GL_APIENTRY GenerateMipmap(GLenum target)
Context *context = GetValidGlobalContext(); Context *context = GetValidGlobalContext();
if (context) 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; return;
} }
// GL_EXT_sRGB does not support mipmap generation on sRGB textures context->generateMipmap(target);
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;
}
} }
} }
......
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