From 279317b0673438a3ede3fe38d1c68c7f7ecc5c5f Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Thu, 13 Nov 2014 11:36:33 +0100 Subject: QOpenGLTexture: fix storage allocation Storage allocation was quite wrong in the general case. 1) We can't pass unsized texture formats to immutable storage allocations, so we need to check for that. 2) Second, when allocating mutable storage on GLES, apparently the driver is allowed to peek at the external pixel type. The texture can then only get data in the pixel type passed at storage allocation time. (I guess that, for the sake of simplicity, the drivers are allowed not to perform any pixel conversions.) I'm still not convinced about the wording in the GLES2/3 specifications, but apparently ANGLE behaves this way, so we need to fix this kind of allocation. Unfortunately the only way is to ask, at storage allocation time, what's the external pixel format/type, so we need to introduce an allocateStorage overload taking those as arguments and using them for the glTexImage* calls. Task-number: QTBUG-41822 Task-number: QTBUG-42623 Change-Id: Idf745d549f01c5db3e56e86cf9b1f53a77cfccc6 Reviewed-by: Sean Harmer --- src/gui/opengl/qopengltexture.cpp | 166 +++++++++++++++++++++++++++++++++++--- src/gui/opengl/qopengltexture.h | 1 + src/gui/opengl/qopengltexture_p.h | 4 +- 3 files changed, 159 insertions(+), 12 deletions(-) diff --git a/src/gui/opengl/qopengltexture.cpp b/src/gui/opengl/qopengltexture.cpp index 9c30efe148..6f0eb17397 100644 --- a/src/gui/opengl/qopengltexture.cpp +++ b/src/gui/opengl/qopengltexture.cpp @@ -294,16 +294,124 @@ int QOpenGLTexturePrivate::evaluateMipLevels() const } } -void QOpenGLTexturePrivate::allocateStorage() +static bool isSizedTextureFormat(QOpenGLTexture::TextureFormat internalFormat) +{ + switch (internalFormat) { + case QOpenGLTexture::NoFormat: + return false; + + case QOpenGLTexture::R8_UNorm: + case QOpenGLTexture::RG8_UNorm: + case QOpenGLTexture::RGB8_UNorm: + case QOpenGLTexture::RGBA8_UNorm: + case QOpenGLTexture::R16_UNorm: + case QOpenGLTexture::RG16_UNorm: + case QOpenGLTexture::RGB16_UNorm: + case QOpenGLTexture::RGBA16_UNorm: + case QOpenGLTexture::R8_SNorm: + case QOpenGLTexture::RG8_SNorm: + case QOpenGLTexture::RGB8_SNorm: + case QOpenGLTexture::RGBA8_SNorm: + case QOpenGLTexture::R16_SNorm: + case QOpenGLTexture::RG16_SNorm: + case QOpenGLTexture::RGB16_SNorm: + case QOpenGLTexture::RGBA16_SNorm: + case QOpenGLTexture::R8U: + case QOpenGLTexture::RG8U: + case QOpenGLTexture::RGB8U: + case QOpenGLTexture::RGBA8U: + case QOpenGLTexture::R16U: + case QOpenGLTexture::RG16U: + case QOpenGLTexture::RGB16U: + case QOpenGLTexture::RGBA16U: + case QOpenGLTexture::R32U: + case QOpenGLTexture::RG32U: + case QOpenGLTexture::RGB32U: + case QOpenGLTexture::RGBA32U: + case QOpenGLTexture::R8I: + case QOpenGLTexture::RG8I: + case QOpenGLTexture::RGB8I: + case QOpenGLTexture::RGBA8I: + case QOpenGLTexture::R16I: + case QOpenGLTexture::RG16I: + case QOpenGLTexture::RGB16I: + case QOpenGLTexture::RGBA16I: + case QOpenGLTexture::R32I: + case QOpenGLTexture::RG32I: + case QOpenGLTexture::RGB32I: + case QOpenGLTexture::RGBA32I: + case QOpenGLTexture::R16F: + case QOpenGLTexture::RG16F: + case QOpenGLTexture::RGB16F: + case QOpenGLTexture::RGBA16F: + case QOpenGLTexture::R32F: + case QOpenGLTexture::RG32F: + case QOpenGLTexture::RGB32F: + case QOpenGLTexture::RGBA32F: + case QOpenGLTexture::RGB9E5: + case QOpenGLTexture::RG11B10F: + case QOpenGLTexture::RG3B2: + case QOpenGLTexture::R5G6B5: + case QOpenGLTexture::RGB5A1: + case QOpenGLTexture::RGBA4: + case QOpenGLTexture::RGB10A2: + + case QOpenGLTexture::D16: + case QOpenGLTexture::D24: + case QOpenGLTexture::D32: + case QOpenGLTexture::D32F: + + case QOpenGLTexture::D24S8: + case QOpenGLTexture::D32FS8X24: + + case QOpenGLTexture::S8: + + case QOpenGLTexture::RGB_DXT1: + case QOpenGLTexture::RGBA_DXT1: + case QOpenGLTexture::RGBA_DXT3: + case QOpenGLTexture::RGBA_DXT5: + case QOpenGLTexture::R_ATI1N_UNorm: + case QOpenGLTexture::R_ATI1N_SNorm: + case QOpenGLTexture::RG_ATI2N_UNorm: + case QOpenGLTexture::RG_ATI2N_SNorm: + case QOpenGLTexture::RGB_BP_UNSIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_SIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_UNorm: + case QOpenGLTexture::SRGB8: + case QOpenGLTexture::SRGB8_Alpha8: + case QOpenGLTexture::SRGB_DXT1: + case QOpenGLTexture::SRGB_Alpha_DXT1: + case QOpenGLTexture::SRGB_Alpha_DXT3: + case QOpenGLTexture::SRGB_Alpha_DXT5: + case QOpenGLTexture::SRGB_BP_UNorm: + return true; + + case QOpenGLTexture::DepthFormat: + case QOpenGLTexture::AlphaFormat: + + case QOpenGLTexture::RGBFormat: + case QOpenGLTexture::RGBAFormat: + + case QOpenGLTexture::LuminanceFormat: + + case QOpenGLTexture::LuminanceAlphaFormat: + return false; + } + + Q_UNREACHABLE(); + return false; +} + +void QOpenGLTexturePrivate::allocateStorage(QOpenGLTexture::PixelFormat pixelFormat, QOpenGLTexture::PixelType pixelType) { // Resolve the actual number of mipmap levels we can use mipLevels = evaluateMipLevels(); - // Use immutable storage whenever possible, falling back to mutable when not available - if (features.testFlag(QOpenGLTexture::ImmutableStorage)) + // Use immutable storage whenever possible, falling back to mutable + if (features.testFlag(QOpenGLTexture::ImmutableStorage) && isSizedTextureFormat(format)) allocateImmutableStorage(); else - allocateMutableStorage(); + allocateMutableStorage(pixelFormat, pixelType); } static QOpenGLTexture::PixelFormat pixelFormatCompatibleWithInternalFormat(QOpenGLTexture::TextureFormat internalFormat) @@ -534,11 +642,8 @@ static QOpenGLTexture::PixelType pixelTypeCompatibleWithInternalFormat(QOpenGLTe return QOpenGLTexture::NoPixelType; } -void QOpenGLTexturePrivate::allocateMutableStorage() +void QOpenGLTexturePrivate::allocateMutableStorage(QOpenGLTexture::PixelFormat pixelFormat, QOpenGLTexture::PixelType pixelType) { - const QOpenGLTexture::PixelFormat pixelFormat = pixelFormatCompatibleWithInternalFormat(format); - const QOpenGLTexture::PixelType pixelType = pixelTypeCompatibleWithInternalFormat(format); - switch (target) { case QOpenGLTexture::TargetBuffer: // Buffer textures get their storage from an external OpenGL buffer @@ -2436,16 +2541,57 @@ bool QOpenGLTexture::isFixedSamplePositions() const Once storage has been allocated for the texture then pixel data can be uploaded via one of the setData() overloads. + \note If immutable texture storage is not available, + then a default pixel format and pixel type will be used to + create the mutable storage. You can use the other + allocateStorage() overload to specify exactly the pixel format + and the pixel type to use when allocating mutable storage; + this is particulary useful under certain OpenGL ES implementations + (notably, OpenGL ES 2), where the pixel format and the pixel type + used at allocation time must perfectly match the format + and the type passed to any subsequent setData() call. + \sa isStorageAllocated(), setData() */ void QOpenGLTexture::allocateStorage() { Q_D(QOpenGLTexture); if (d->create()) { - d->allocateStorage(); + const QOpenGLTexture::PixelFormat pixelFormat = pixelFormatCompatibleWithInternalFormat(d->format); + const QOpenGLTexture::PixelType pixelType = pixelTypeCompatibleWithInternalFormat(d->format); + d->allocateStorage(pixelFormat, pixelType); } } +/*! + \since 5.5 + + Allocates server-side storage for this texture object taking + into account, the format, dimensions, mipmap levels, array + layers and cubemap faces. + + Once storage has been allocated it is no longer possible to change + these properties. + + If supported QOpenGLTexture makes use of immutable texture + storage. However, if immutable texture storage is not available, + then the specified \a pixelFormat and \a pixelType will be used + to allocate mutable storage; note that in certain OpenGL implementations + (notably, OpenGL ES 2) they must perfectly match the format + and the type passed to any subsequent setData() call. + + Once storage has been allocated for the texture then pixel data + can be uploaded via one of the setData() overloads. + + \sa isStorageAllocated(), setData() +*/ +void QOpenGLTexture::allocateStorage(QOpenGLTexture::PixelFormat pixelFormat, QOpenGLTexture::PixelType pixelType) +{ + Q_D(QOpenGLTexture); + if (d->create()) + d->allocateStorage(pixelFormat, pixelType); +} + /*! Returns \c true if server-side storage for this texture as been allocated. @@ -2670,7 +2816,7 @@ void QOpenGLTexture::setData(const QImage& image, MipMapGeneration genMipMaps) setSize(image.width(), image.height()); setMipLevels(genMipMaps == GenerateMipMaps ? maximumMipLevels() : 1); - allocateStorage(); + allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); // Upload pixel data and generate mipmaps QImage glImage = image.convertToFormat(QImage::Format_RGBA8888); diff --git a/src/gui/opengl/qopengltexture.h b/src/gui/opengl/qopengltexture.h index 9fee298b90..578f3389a3 100644 --- a/src/gui/opengl/qopengltexture.h +++ b/src/gui/opengl/qopengltexture.h @@ -361,6 +361,7 @@ public: void setFixedSamplePositions(bool fixed); bool isFixedSamplePositions() const; void allocateStorage(); + void allocateStorage(PixelFormat pixelFormat, PixelType pixelType); bool isStorageAllocated() const; QOpenGLTexture *createTextureView(Target target, diff --git a/src/gui/opengl/qopengltexture_p.h b/src/gui/opengl/qopengltexture_p.h index b584d3ec8f..3b70010231 100644 --- a/src/gui/opengl/qopengltexture_p.h +++ b/src/gui/opengl/qopengltexture_p.h @@ -87,8 +87,8 @@ public: bool isBound() const; bool isBound(uint unit) const; - void allocateStorage(); - void allocateMutableStorage(); + void allocateStorage(QOpenGLTexture::PixelFormat pixelFormat, QOpenGLTexture::PixelType pixelType); + void allocateMutableStorage(QOpenGLTexture::PixelFormat pixelFormat, QOpenGLTexture::PixelType pixelType); void allocateImmutableStorage(); void setData(int mipLevel, int layer, QOpenGLTexture::CubeMapFace cubeFace, QOpenGLTexture::PixelFormat sourceFormat, QOpenGLTexture::PixelType sourceType, -- cgit v1.2.3