From d779e837f7c2bc14066f9b8c22349cc8147d15f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 5 Jul 2025 14:47:34 +0200 Subject: [PATCH] GL: use known compressed format properties instead of querying them. With this change, neither GL is queried for compressed block size properties nor they're taken from CompressedPixelStorage anymore. For image upload they're taken directly from the passed image and set to GL's pixel pack state, for image download they're taken from known GL::CompressedPixelFormat properties set to GL's pixel unpack state and saved to the image. Besides removing a bunch of checks from tests, there isn't anything new to *add* there -- everything should work just as before, or (assuming shitty drivers with broken format queries) better. Also, in some cases the internal format queries were made into a zero-initialized output variable to ensure consistent behavior with broken drivers. That's now done in all cases, with a lengthy comment to ensure this doesn't get "cleaned up" by accident. --- src/Magnum/GL/AbstractTexture.cpp | 201 ++++++----- src/Magnum/GL/AbstractTexture.h | 4 - src/Magnum/GL/CubeMapTexture.cpp | 314 +++++++++++------- src/Magnum/GL/CubeMapTexture.h | 48 +-- src/Magnum/GL/CubeMapTextureArray.h | 8 - .../GL/Implementation/RendererState.cpp | 25 +- src/Magnum/GL/Implementation/RendererState.h | 23 +- src/Magnum/GL/RectangleTexture.h | 8 - src/Magnum/GL/Test/AbstractTextureGLTest.cpp | 6 - .../GL/Test/CubeMapTextureArrayGLTest.cpp | 6 - src/Magnum/GL/Test/CubeMapTextureGLTest.cpp | 6 - src/Magnum/GL/Test/TextureArrayGLTest.cpp | 6 - src/Magnum/GL/Test/TextureGLTest.cpp | 12 - src/Magnum/GL/Texture.h | 53 +-- src/Magnum/GL/TextureArray.h | 8 - src/Magnum/Implementation/ImageProperties.h | 6 + 16 files changed, 406 insertions(+), 328 deletions(-) diff --git a/src/Magnum/GL/AbstractTexture.cpp b/src/Magnum/GL/AbstractTexture.cpp index 520f4a5cf..28ef5c8d2 100644 --- a/src/Magnum/GL/AbstractTexture.cpp +++ b/src/Magnum/GL/AbstractTexture.cpp @@ -1794,18 +1794,23 @@ template void MAGNUM_GL_EXPORT AbstractTexture::image<3>(GLint, BufferImage<3>&, template void AbstractTexture::compressedImage(const GLint level, CompressedImage& image, const ImageFlags flags) { const Math::Vector size = DataHelper::imageSize(*this, level); - /* If the user-provided pixel storage doesn't tell us all properties about - the compression, we need to ask GL for it */ - std::size_t dataSize; - if(!image.storage().compressedBlockSize().product() || !image.storage().compressedBlockDataSize()) { - GLint textureDataSize; - Context::current().state().texture.getLevelParameterivImplementation(*this, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &textureDataSize); - dataSize = textureDataSize; - } else dataSize = Magnum::Implementation::compressedImageDataSizeFor(image, size); - - /* Internal texture format */ - GLint format; + /* Get internal texture format, determine its properties (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat) and + calculate data size for those. Yes, if the format is unknown to Magnum, + this will blow up. But that's likely a very rare scenario that isn't + worth implementing (and is rather impossible to test), and the user can + always query into a view with block properties specified in that case. + + The format is zero-init to have it deterministically assert inside + compressedPixelFormatBlockSize() if the drivers are extra shitty and + don't implement this query (Intel Windows drivers, I'm talking about + you), otherwise it could give back a value that could randomly work, or + cause OOMs, crashes and such. */ + GLint format{}; Context::current().state().texture.getLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); + const Vector3i blockSize = compressedPixelFormatBlockSize(CompressedPixelFormat(format)); + const UnsignedInt blockDataSize = compressedPixelFormatBlockDataSize(CompressedPixelFormat(format)); + const std::size_t dataSize = Magnum::Implementation::compressedImageDataSizeFor(image.storage(), blockSize, blockDataSize, size); /* Reallocate only if needed */ Containers::Array data{image.release()}; @@ -1813,9 +1818,9 @@ template void AbstractTexture::compressedImage(const GLi data = Containers::Array{dataSize}; Buffer::unbindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image.storage(), blockSize, blockDataSize); Context::current().state().texture.getCompressedImageImplementation(*this, level, data.size(), data); - image = CompressedImage{image.storage(), CompressedPixelFormat(format), size, Utility::move(data), flags}; + image = CompressedImage{image.storage(), UnsignedInt(format), blockSize, blockDataSize, size, Utility::move(data), flags}; } template void MAGNUM_GL_EXPORT AbstractTexture::compressedImage<1>(GLint, CompressedImage<1>&, ImageFlags1D); @@ -1831,16 +1836,21 @@ template void AbstractTexture::compressedImage(const GLi CORRADE_ASSERT(image.size() == size, "GL::AbstractTexture::compressedImage(): expected image view size" << size << "but got" << image.size(), ); - /* Internal texture format */ - GLint format; - Context::current().state().texture.getLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); - - CORRADE_ASSERT(compressedPixelFormat(image.format()) == CompressedPixelFormat(format), - "GL::AbstractTexture::compressedImage(): expected image view format" << CompressedPixelFormat(format) << "but got" << compressedPixelFormat(image.format()), ); + /* Check that the internal texture format matches (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat). + Zero-init to avoid a non-deterministic message in the assert below if + the drivers are extra shitty and don't implement this query (Intel + Windows drivers, I'm talking about you). */ + { + GLint format{}; + Context::current().state().texture.getLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); + CORRADE_ASSERT(compressedPixelFormat(image.format()) == CompressedPixelFormat(format), + "GL::AbstractTexture::compressedImage(): expected image view format" << CompressedPixelFormat(format) << "but got" << compressedPixelFormat(image.format()), ); + } #endif Buffer::unbindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image); Context::current().state().texture.getCompressedImageImplementation(*this, level, image.data().size(), image.data()); } @@ -1851,27 +1861,36 @@ template void MAGNUM_GL_EXPORT AbstractTexture::compressedImage<3>(GLint, const template void AbstractTexture::compressedImage(const GLint level, CompressedBufferImage& image, BufferUsage usage) { const Math::Vector size = DataHelper::imageSize(*this, level); - /* If the user-provided pixel storage doesn't tell us all properties about - the compression, we need to ask GL for it */ - std::size_t dataSize; - if(!image.storage().compressedBlockSize().product() || !image.storage().compressedBlockDataSize()) { - GLint textureDataSize; - Context::current().state().texture.getLevelParameterivImplementation(*this, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &textureDataSize); - dataSize = textureDataSize; - } else dataSize = Magnum::Implementation::compressedImageDataSizeFor(image, size); - - /* Internal texture format */ - GLint format; + /* Get internal texture format, determine its properties (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat) and + calculate data size for those. Yes, if the format is unknown to Magnum, + this will blow up. But that's likely a very rare scenario that isn't + worth implementing (and is rather impossible to test), and the user can + always query into a view with block properties specified in that case. + + The format is zero-init to have it deterministically assert inside + compressedPixelFormatBlockSize() if the drivers are extra shitty and + don't implement this query (Intel Windows drivers, I'm talking about + you), otherwise it could give back a value that could randomly work, or + cause OOMs, crashes and such. */ + GLint format{}; Context::current().state().texture.getLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); + const Vector3i blockSize = compressedPixelFormatBlockSize(CompressedPixelFormat(format)); + const UnsignedInt blockDataSize = compressedPixelFormatBlockDataSize(CompressedPixelFormat(format)); + const std::size_t dataSize = Magnum::Implementation::compressedImageDataSizeFor(image.storage(), blockSize, blockDataSize, size); /* Reallocate only if needed */ if(image.dataSize() < dataSize) image.setData(image.storage(), CompressedPixelFormat(format), size, {nullptr, dataSize}, usage); else image.setData(image.storage(), CompressedPixelFormat(format), size, nullptr, usage); + /* The setData() call above updates the block properties, so just verify + they're the same as the ones used here as the ones from the umage get + used in applyCompressedPixelStoragePack() below */ + CORRADE_INTERNAL_ASSERT(blockSize == image.blockSize() && blockDataSize == image.blockDataSize()); image.buffer().bindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image); Context::current().state().texture.getCompressedImageImplementation(*this, level, dataSize, nullptr); } @@ -1943,18 +1962,6 @@ template void MAGNUM_GL_EXPORT AbstractTexture::subImage<1>(GLint, const Range1D template void MAGNUM_GL_EXPORT AbstractTexture::subImage<2>(GLint, const Range2Di&, BufferImage<2>&, BufferUsage); template void MAGNUM_GL_EXPORT AbstractTexture::subImage<3>(GLint, const Range3Di&, BufferImage<3>&, BufferUsage); -template std::size_t AbstractTexture::compressedSubImageSize(TextureFormat format, const Math::Vector& size) { - /* Amount of blocks in given range (rounded up) multiplied by block - data size. The user is responsible for proper block-aligned size. */ - const Math::Vector blockSize = DataHelper::compressedBlockSize(_target, format); - return ((size + blockSize - Math::Vector{1})/blockSize).product()* - compressedBlockDataSize(_target, format); -} - -template std::size_t MAGNUM_GL_EXPORT AbstractTexture::compressedSubImageSize<1>(TextureFormat format, const Math::Vector<1, Int>& size); -template std::size_t MAGNUM_GL_EXPORT AbstractTexture::compressedSubImageSize<2>(TextureFormat format, const Math::Vector<2, Int>& size); -template std::size_t MAGNUM_GL_EXPORT AbstractTexture::compressedSubImageSize<3>(TextureFormat format, const Math::Vector<3, Int>& size); - template void AbstractTexture::compressedSubImage(const GLint level, const RangeTypeFor& range, CompressedImage& image, const ImageFlags flags) { /* Explicitly create if not already because the texture might have been created w/ the DSA extension disabled but below a DSA API is used */ @@ -1964,17 +1971,23 @@ template void AbstractTexture::compressedSubImage(const const Vector3i paddedOffset = Vector3i::pad(range.min()); const Vector3i paddedSize = Vector3i::pad(size, 1); - /* Internal texture format */ - GLint format; + /* Get internal texture format, determine its properties (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat) and + calculate data size for those. Yes, if the format is unknown to Magnum, + this will blow up. But that's likely a very rare scenario that isn't + worth implementing (and is rather impossible to test), and the user can + always query into a view with block properties specified in that case. + + The format is zero-init to have it deterministically assert inside + compressedPixelFormatBlockSize() if the drivers are extra shitty and + don't implement this query (Intel Windows drivers, I'm talking about + you), otherwise it could give back a value that could randomly work, or + cause OOMs, crashes and such. */ + GLint format{}; Context::current().state().texture.getLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); - - /* Calculate compressed subimage size. If the user-provided pixel storage - doesn't tell us all properties about the compression, we need to ask GL - for it. That requires GL_ARB_internalformat_query2. */ - std::size_t dataSize; - if(!image.storage().compressedBlockSize().product() || !image.storage().compressedBlockDataSize()) - dataSize = compressedSubImageSize(TextureFormat(format), size); - else dataSize = Magnum::Implementation::compressedImageDataSizeFor(image, size); + const Vector3i blockSize = compressedPixelFormatBlockSize(CompressedPixelFormat(format)); + const UnsignedInt blockDataSize = compressedPixelFormatBlockDataSize(CompressedPixelFormat(format)); + const std::size_t dataSize = Magnum::Implementation::compressedImageDataSizeFor(image.storage(), blockSize, blockDataSize, size); /* Reallocate only if needed */ Containers::Array data{image.release()}; @@ -1982,7 +1995,7 @@ template void AbstractTexture::compressedSubImage(const data = Containers::Array{dataSize}; Buffer::unbindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image.storage(), blockSize, blockDataSize); glGetCompressedTextureSubImage(_id, level, paddedOffset.x(), paddedOffset.y(), paddedOffset.z(), paddedSize.x(), paddedSize.y(), paddedSize.z(), data.size(), data); image = CompressedImage{CompressedPixelFormat(format), size, Utility::move(data), flags}; } @@ -2001,22 +2014,26 @@ template void AbstractTexture::compressedSubImage(const created w/ the DSA extension disabled but below a DSA API is used */ createIfNotAlready(); - const Math::Vector size = range.size(); - #ifndef CORRADE_NO_ASSERT - /* Internal texture format */ - GLint format; - Context::current().state().texture.getLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); + /* Check that the internal texture format matches (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat). + Zero-init to avoid a non-deterministic message in the assert below if + the drivers are extra shitty and don't implement this query (Intel + Windows drivers, I'm talking about you). */ + { + GLint format{}; + Context::current().state().texture.getLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); - CORRADE_ASSERT(compressedPixelFormat(image.format()) == CompressedPixelFormat(format), - "GL::AbstractTexture::compressedSubImage(): expected image view format" << CompressedPixelFormat(format) << "but got" << compressedPixelFormat(image.format()), ); + CORRADE_ASSERT(compressedPixelFormat(image.format()) == CompressedPixelFormat(format), + "GL::AbstractTexture::compressedSubImage(): expected image view format" << CompressedPixelFormat(format) << "but got" << compressedPixelFormat(image.format()), ); + } #endif const Vector3i paddedOffset = Vector3i::pad(range.min()); - const Vector3i paddedSize = Vector3i::pad(size, 1); + const Vector3i paddedSize = Vector3i::pad(range.size(), 1); Buffer::unbindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image); glGetCompressedTextureSubImage(_id, level, paddedOffset.x(), paddedOffset.y(), paddedOffset.z(), paddedSize.x(), paddedSize.y(), paddedSize.z(), image.data().size(), image.data()); } @@ -2033,26 +2050,36 @@ template void AbstractTexture::compressedSubImage(const const Vector3i paddedOffset = Vector3i::pad(range.min()); const Vector3i paddedSize = Vector3i::pad(size, 1); - /* Internal texture format */ - GLint format; + /* Get internal texture format, determine its properties (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat) and + calculate data size for those. Yes, if the format is unknown to Magnum, + this will blow up. But that's likely a very rare scenario that isn't + worth implementing (and is rather impossible to test), and the user can + always query into a view with block properties specified in that case. + + The format is zero-init to have it deterministically assert inside + compressedPixelFormatBlockSize() if the drivers are extra shitty and + don't implement this query (Intel Windows drivers, I'm talking about + you), otherwise it could give back a value that could randomly work, or + cause OOMs, crashes and such. */ + GLint format{}; Context::current().state().texture.getLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); - - /* Calculate compressed subimage size. If the user-provided pixel storage - doesn't tell us all properties about the compression, we need to ask GL - for it. That requires GL_ARB_internalformat_query2. */ - std::size_t dataSize; - if(!image.storage().compressedBlockSize().product() || !image.storage().compressedBlockDataSize()) - dataSize = compressedSubImageSize(TextureFormat(format), size); - else dataSize = Magnum::Implementation::compressedImageDataSizeFor(image, size); + const Vector3i blockSize = compressedPixelFormatBlockSize(CompressedPixelFormat(format)); + const UnsignedInt blockDataSize = compressedPixelFormatBlockDataSize(CompressedPixelFormat(format)); + const std::size_t dataSize = Magnum::Implementation::compressedImageDataSizeFor(image.storage(), blockSize, blockDataSize, size); /* Reallocate only if needed */ if(image.dataSize() < dataSize) image.setData(image.storage(), CompressedPixelFormat(format), size, {nullptr, dataSize}, usage); else image.setData(image.storage(), CompressedPixelFormat(format), size, nullptr, usage); + /* The setData() call above updates the block properties, so just verify + they're the same as the ones used here as the ones from the umage get + used in applyCompressedPixelStoragePack() below */ + CORRADE_INTERNAL_ASSERT(blockSize == image.blockSize() && blockDataSize == image.blockDataSize()); image.buffer().bindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image); glGetCompressedTextureSubImage(_id, level, paddedOffset.x(), paddedOffset.y(), paddedOffset.z(), paddedSize.x(), paddedSize.y(), paddedSize.z(), dataSize, nullptr); } @@ -2149,7 +2176,7 @@ void AbstractTexture::DataHelper<1>::setImage(AbstractTexture& texture, const GL void AbstractTexture::DataHelper<1>::setCompressedImage(AbstractTexture& texture, const GLint level, const CompressedImageView1D& image) { Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); texture.bindInternal(); glCompressedTexImage1D(texture._target, level, GLenum(compressedPixelFormat(image.format())), image.size()[0], 0, Implementation::occupiedCompressedImageDataSize(image), image.data()); } @@ -2163,7 +2190,7 @@ void AbstractTexture::DataHelper<1>::setImage(AbstractTexture& texture, const GL void AbstractTexture::DataHelper<1>::setCompressedImage(AbstractTexture& texture, const GLint level, CompressedBufferImage1D& image) { image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); texture.bindInternal(); glCompressedTexImage1D(texture._target, level, GLenum(image.format()), image.size()[0], 0, Implementation::occupiedCompressedImageDataSize(image), nullptr); } @@ -2176,7 +2203,7 @@ void AbstractTexture::DataHelper<1>::setSubImage(AbstractTexture& texture, const void AbstractTexture::DataHelper<1>::setCompressedSubImage(AbstractTexture& texture, const GLint level, const Math::Vector<1, GLint>& offset, const CompressedImageView1D& image) { Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); Context::current().state().texture.compressedSubImage1DImplementation(texture, level, offset, image.size(), compressedPixelFormat(image.format()), image.data(), Implementation::occupiedCompressedImageDataSize(image)); } @@ -2188,7 +2215,7 @@ void AbstractTexture::DataHelper<1>::setSubImage(AbstractTexture& texture, const void AbstractTexture::DataHelper<1>::setCompressedSubImage(AbstractTexture& texture, const GLint level, const Math::Vector<1, GLint>& offset, CompressedBufferImage1D& image) { image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); Context::current().state().texture.compressedSubImage1DImplementation(texture, level, offset, image.size(), image.format(), nullptr, Implementation::occupiedCompressedImageDataSize(image)); } #endif @@ -2209,7 +2236,7 @@ void AbstractTexture::DataHelper<2>::setCompressedImage(AbstractTexture& texture #ifndef MAGNUM_TARGET_GLES2 Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); #endif - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); texture.bindInternal(); glCompressedTexImage2D(target, level, GLenum(compressedPixelFormat(image.format())), image.size().x(), image.size().y(), 0, Implementation::occupiedCompressedImageDataSize(image), image.data()); } @@ -2224,7 +2251,7 @@ void AbstractTexture::DataHelper<2>::setImage(AbstractTexture& texture, const GL void AbstractTexture::DataHelper<2>::setCompressedImage(AbstractTexture& texture, const GLenum target, const GLint level, CompressedBufferImage2D& image) { image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); texture.bindInternal(); glCompressedTexImage2D(target, level, GLenum(image.format()), image.size().x(), image.size().y(), 0, Implementation::occupiedCompressedImageDataSize(image), nullptr); } @@ -2246,7 +2273,7 @@ void AbstractTexture::DataHelper<2>::setCompressedSubImage(AbstractTexture& text #ifndef MAGNUM_TARGET_GLES2 Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); #endif - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); Context::current().state().texture.compressedSubImage2DImplementation(texture, level, offset, image.size(), compressedPixelFormat(image.format()), image.data(), Implementation::occupiedCompressedImageDataSize(image)); } @@ -2259,7 +2286,7 @@ void AbstractTexture::DataHelper<2>::setSubImage(AbstractTexture& texture, const void AbstractTexture::DataHelper<2>::setCompressedSubImage(AbstractTexture& texture, const GLint level, const Vector2i& offset, CompressedBufferImage2D& image) { image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); Context::current().state().texture.compressedSubImage2DImplementation(texture, level, offset, image.size(), image.format(), nullptr, Implementation::occupiedCompressedImageDataSize(image)); } #endif @@ -2281,7 +2308,7 @@ void AbstractTexture::DataHelper<3>::setCompressedImage(AbstractTexture& texture #ifndef MAGNUM_TARGET_GLES2 Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); #endif - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); texture.bindInternal(); #ifndef MAGNUM_TARGET_GLES2 glCompressedTexImage3D(texture._target, level, GLenum(compressedPixelFormat(image.format())), image.size().x(), image.size().y(), image.size().z(), 0, Implementation::occupiedCompressedImageDataSize(image), image.data()); @@ -2301,7 +2328,7 @@ void AbstractTexture::DataHelper<3>::setImage(AbstractTexture& texture, const GL void AbstractTexture::DataHelper<3>::setCompressedImage(AbstractTexture& texture, const GLint level, CompressedBufferImage3D& image) { image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); texture.bindInternal(); glCompressedTexImage3D(texture._target, level, GLenum(image.format()), image.size().x(), image.size().y(), image.size().z(), 0, Implementation::occupiedCompressedImageDataSize(image), nullptr); } @@ -2324,7 +2351,7 @@ void AbstractTexture::DataHelper<3>::setCompressedSubImage(AbstractTexture& text #ifndef MAGNUM_TARGET_GLES2 Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); #endif - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); Context::current().state().texture.compressedSubImage3DImplementation(texture, level, offset, image.size(), compressedPixelFormat(image.format()), image.data(), Implementation::occupiedCompressedImageDataSize(image)); } #endif @@ -2338,7 +2365,7 @@ void AbstractTexture::DataHelper<3>::setSubImage(AbstractTexture& texture, const void AbstractTexture::DataHelper<3>::setCompressedSubImage(AbstractTexture& texture, const GLint level, const Vector3i& offset, CompressedBufferImage3D& image) { image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); Context::current().state().texture.compressedSubImage3DImplementation(texture, level, offset, image.size(), image.format(), nullptr, Implementation::occupiedCompressedImageDataSize(image)); } #endif diff --git a/src/Magnum/GL/AbstractTexture.h b/src/Magnum/GL/AbstractTexture.h index 8768342af..f18c50e90 100644 --- a/src/Magnum/GL/AbstractTexture.h +++ b/src/Magnum/GL/AbstractTexture.h @@ -544,10 +544,6 @@ class MAGNUM_GL_EXPORT AbstractTexture: public AbstractObject { static void MAGNUM_GL_LOCAL createImplementationDSA(AbstractTexture& self); #endif - #ifndef MAGNUM_TARGET_GLES - template std::size_t MAGNUM_GL_LOCAL compressedSubImageSize(TextureFormat format, const Math::Vector& size); - #endif - static void MAGNUM_GL_LOCAL bindImplementationDefault(AbstractTexture& self, GLint textureUnit); #ifndef MAGNUM_TARGET_GLES static void MAGNUM_GL_LOCAL bindImplementationMulti(AbstractTexture& self, GLint textureUnit); diff --git a/src/Magnum/GL/CubeMapTexture.cpp b/src/Magnum/GL/CubeMapTexture.cpp index 554980a8c..d9851a43d 100644 --- a/src/Magnum/GL/CubeMapTexture.cpp +++ b/src/Magnum/GL/CubeMapTexture.cpp @@ -153,21 +153,30 @@ BufferImage3D CubeMapTexture::image(const Int level, BufferImage3D&& image, cons void CubeMapTexture::compressedImage(const Int level, CompressedImage3D& image) { const Vector3i size{imageSize(level), 6}; - /* If the user-provided pixel storage doesn't tell us all properties about - the compression, we need to ask GL for it */ - std::pair dataOffsetSize; - if(!image.storage().compressedBlockSize().product() || !image.storage().compressedBlockDataSize()) { - /* Unlike in AbstractTexture::compressedImage(), here we have a - separate offset and size because of the - nv-cubemap-broken-full-compressed-image-query workaround, where it - needs to go slice-by-slice, advancing the offset each time */ - dataOffsetSize.first = 0; - dataOffsetSize.second = Context::current().state().texture.getCubeLevelCompressedImageSizeImplementation(*this, level)*6; - } else dataOffsetSize = Magnum::Implementation::compressedImageDataOffsetSizeFor(image, size); - - /* Internal texture format */ - GLint format; + /* Get internal texture format, determine its properties (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat) and + calculate data size for those. Yes, if the format is unknown to Magnum, + this will blow up. But that's likely a very rare scenario that isn't + worth implementing (and is rather impossible to test), and the user can + always query into a view with block properties specified in that case. + + The format is zero-init to have it deterministically assert inside + compressedPixelFormatBlockSize() if the drivers are extra shitty and + don't implement this query (Intel Windows drivers, I'm talking about + you), otherwise it could give back a value that could randomly work, or + cause OOMs, crashes and such. */ + GLint format{}; + /* Note that this has to call getCubeLevelParameterivImplementation(), not + getLevelParameterivImplementation(), to supply a concrete coordinate in + non-DSA codepaths */ Context::current().state().texture.getCubeLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); + const Vector3i blockSize = compressedPixelFormatBlockSize(CompressedPixelFormat(format)); + const UnsignedInt blockDataSize = compressedPixelFormatBlockDataSize(CompressedPixelFormat(format)); + /* Unlike in AbstractTexture::compressedImage(), here we have a separate + offset and size because of the nv-cubemap-broken-full-compressed-image-query + workaround, where it needs to go slice-by-slice, advancing the offset + each time */ + const std::pair dataOffsetSize = Magnum::Implementation::compressedImageDataOffsetSizeFor(image.storage(), blockSize, blockDataSize, size); /* Reallocate only if needed */ Containers::Array data{image.release()}; @@ -175,7 +184,7 @@ void CubeMapTexture::compressedImage(const Int level, CompressedImage3D& image) data = Containers::Array{dataOffsetSize.first + dataOffsetSize.second}; Buffer::unbindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image.storage(), blockSize, blockDataSize); Context::current().state().texture.getCompressedCubeImage3DImplementation(*this, level, size.xy(), dataOffsetSize.first, dataOffsetSize.second, data); image = CompressedImage3D{image.storage(), CompressedPixelFormat(format), size, Utility::move(data), ImageFlag3D::CubeMap}; } @@ -193,59 +202,77 @@ void CubeMapTexture::compressedImage(const Int level, const MutableCompressedIma CORRADE_ASSERT(image.size() == size, "GL::CubeMapTexture::compressedImage(): expected image view size" << size << "but got" << image.size(), ); - /* If the user-provided pixel storage doesn't tell us all properties about - the compression, we need to ask GL for it */ - std::pair dataOffsetSize; - if(!image.storage().compressedBlockSize().product() || !image.storage().compressedBlockDataSize()) { - /* Unlike in AbstractTexture::compressedImage(), here we have a - separate offset and size because of the - nv-cubemap-broken-full-compressed-image-query workaround, where it - needs to go slice-by-slice, advancing the offset each time */ - dataOffsetSize.first = 0; - dataOffsetSize.second = Context::current().state().texture.getCubeLevelCompressedImageSizeImplementation(*this, level)*6; - } else dataOffsetSize = Magnum::Implementation::compressedImageDataOffsetSizeFor(image, size); - #ifndef CORRADE_NO_ASSERT - /* Internal texture format */ - GLint format; - Context::current().state().texture.getCubeLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); - - CORRADE_ASSERT(compressedPixelFormat(image.format()) == CompressedPixelFormat(format), - "GL::CubeMapTexture::compressedImage(): expected image view format" << CompressedPixelFormat(format) << "but got" << compressedPixelFormat(image.format()), ); + /* Check that the internal texture format matches (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat). + Zero-init to avoid a non-deterministic message in the assert below if + the drivers are extra shitty and don't implement this query (Intel + Windows drivers, I'm talking about you). */ + { + GLint format{}; + /* Note that this has to call getCubeLevelParameterivImplementation(), + not getLevelParameterivImplementation(), to supply a concrete + coordinate in non-DSA codepaths */ + Context::current().state().texture.getCubeLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); + CORRADE_ASSERT(compressedPixelFormat(image.format()) == CompressedPixelFormat(format), + "GL::CubeMapTexture::compressedImage(): expected image view format" << CompressedPixelFormat(format) << "but got" << compressedPixelFormat(image.format()), ); + } #endif + /* Unlike in AbstractTexture::compressedImage(), here we have a separate + offset and size because of the nv-cubemap-broken-full-compressed-image-query + workaround, where it needs to go slice-by-slice, advancing the offset + each time */ + const CompressedPixelFormat format = compressedPixelFormat(image.format()); + const Vector3i blockSize = compressedPixelFormatBlockSize(format); + const UnsignedInt blockDataSize = compressedPixelFormatBlockDataSize(format); + const std::pair dataOffsetSize = Magnum::Implementation::compressedImageDataOffsetSizeFor(image.storage(), blockSize, blockDataSize, size); + Buffer::unbindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image); Context::current().state().texture.getCompressedCubeImage3DImplementation(*this, level, size.xy(), dataOffsetSize.first, dataOffsetSize.second, image.data()); } void CubeMapTexture::compressedImage(const Int level, CompressedBufferImage3D& image, const BufferUsage usage) { const Vector3i size{imageSize(level), 6}; - /* If the user-provided pixel storage doesn't tell us all properties about - the compression, we need to ask GL for it */ - std::pair dataOffsetSize; - if(!image.storage().compressedBlockSize().product() || !image.storage().compressedBlockDataSize()) { - /* Unlike in AbstractTexture::compressedImage(), here we have a - separate offset and size because of the - nv-cubemap-broken-full-compressed-image-query workaround, where it - needs to go slice-by-slice, advancing the offset each time */ - dataOffsetSize.first = 0; - dataOffsetSize.second = Context::current().state().texture.getCubeLevelCompressedImageSizeImplementation(*this, level)*6; - } else dataOffsetSize = Magnum::Implementation::compressedImageDataOffsetSizeFor(image, size); - - /* Internal texture format */ - GLint format; + /* Get internal texture format, determine its properties (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat) and + calculate data size for those. Yes, if the format is unknown to Magnum, + this will blow up. But that's likely a very rare scenario that isn't + worth implementing (and is rather impossible to test), and the user can + always query into a view with block properties specified in that case. + + The format is zero-init to have it deterministically assert inside + compressedPixelFormatBlockSize() if the drivers are extra shitty and + don't implement this query (Intel Windows drivers, I'm talking about + you), otherwise it could give back a value that could randomly work, or + cause OOMs, crashes and such. */ + GLint format{}; + /* Note that this has to call getCubeLevelParameterivImplementation(), not + getLevelParameterivImplementation(), to supply a concrete coordinate in + non-DSA codepaths */ Context::current().state().texture.getCubeLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); + const Vector3i blockSize = compressedPixelFormatBlockSize(CompressedPixelFormat(format)); + const UnsignedInt blockDataSize = compressedPixelFormatBlockDataSize(CompressedPixelFormat(format)); + /* Unlike in AbstractTexture::compressedImage(), here we have a separate + offset and size because of the nv-cubemap-broken-full-compressed-image-query + workaround, where it needs to go slice-by-slice, advancing the offset + each time */ + const std::pair dataOffsetSize = Magnum::Implementation::compressedImageDataOffsetSizeFor(image.storage(), blockSize, blockDataSize, size); /* Reallocate only if needed */ if(image.dataSize() < dataOffsetSize.first + dataOffsetSize.second) image.setData(image.storage(), CompressedPixelFormat(format), size, {nullptr, dataOffsetSize.first + dataOffsetSize.second}, usage); else image.setData(image.storage(), CompressedPixelFormat(format), size, nullptr, usage); + /* The setData() call above updates the block properties, so just verify + they're the same as the ones used here as the ones from the umage get + used in applyCompressedPixelStoragePack() below */ + CORRADE_INTERNAL_ASSERT(blockSize == image.blockSize() && blockDataSize == image.blockDataSize()); image.buffer().bindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image); Context::current().state().texture.getCompressedCubeImage3DImplementation(*this, level, size.xy(), dataOffsetSize.first, dataOffsetSize.second, nullptr); } @@ -309,19 +336,27 @@ BufferImage2D CubeMapTexture::image(const CubeMapCoordinate coordinate, const In void CubeMapTexture::compressedImage(const CubeMapCoordinate coordinate, const Int level, CompressedImage2D& image) { const Vector2i size = imageSize(level); - /* If the user-provided pixel storage doesn't tell us all properties about - the compression, we need to ask GL for it */ - std::size_t dataSize; - if(!image.storage().compressedBlockSize().product() || !image.storage().compressedBlockDataSize()) - dataSize = Context::current().state().texture.getCubeLevelCompressedImageSizeImplementation(*this, level); - else - dataSize = Magnum::Implementation::compressedImageDataSizeFor(image, size); - - /* Internal texture format. Zero-init to avoid an assert about value - already wrapped in compressedPixelFormatWrap() later if the drivers are - extra shitty (Intel Windows drivers, I'm talking about you). */ + /* Get internal texture format, determine its properties (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat) and + calculate data size for those. Yes, if the format is unknown to Magnum, + this will blow up. But that's likely a very rare scenario that isn't + worth implementing (and is rather impossible to test), and the user can + always query into a view with block properties specified in that + case. + + The format is zero-init to have it deterministically assert inside + compressedPixelFormatBlockSize() if the drivers are extra shitty and + don't implement this query (Intel Windows drivers, I'm talking about + you), otherwise it could give back a value that could randomly work, or + cause OOMs, crashes and such. */ GLint format{}; + /* Note that this has to call getCubeLevelParameterivImplementation(), not + getLevelParameterivImplementation(), to supply a concrete coordinate in + non-DSA codepaths */ Context::current().state().texture.getCubeLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); + const Vector3i blockSize = compressedPixelFormatBlockSize(CompressedPixelFormat(format)); + const UnsignedInt blockDataSize = compressedPixelFormatBlockDataSize(CompressedPixelFormat(format)); + const std::size_t dataSize = Magnum::Implementation::compressedImageDataSizeFor(image.storage(), blockSize, blockDataSize, size); /* Reallocate only if needed */ Containers::Array data{image.release()}; @@ -329,7 +364,7 @@ void CubeMapTexture::compressedImage(const CubeMapCoordinate coordinate, const I data = Containers::Array{dataSize}; Buffer::unbindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image.storage(), blockSize, blockDataSize); Context::current().state().texture.getCompressedCubeImageImplementation(*this, coordinate, level, size, data.size(), data); image = CompressedImage2D{image.storage(), CompressedPixelFormat(format), size, Utility::move(data), ImageFlags2D{}}; } @@ -348,46 +383,63 @@ void CubeMapTexture::compressedImage(const CubeMapCoordinate coordinate, const I "GL::CubeMapTexture::compressedImage(): expected image view size" << size << "but got" << image.size(), ); #ifndef CORRADE_NO_ASSERT - /* Internal texture format. Zero-init to avoid an assert about value - already wrapped in compressedPixelFormatWrap() later if the drivers are - extra shitty (Intel Windows drivers, I'm talking about you). */ - GLint format{}; - Context::current().state().texture.getCubeLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); - - CORRADE_ASSERT(compressedPixelFormat(image.format()) == CompressedPixelFormat(format), - "GL::CubeMapTexture::compressedImage(): expected image view format" << CompressedPixelFormat(format) << "but got" << compressedPixelFormat(image.format()), ); + /* Check that the internal texture format matches (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat). + Zero-init to avoid a non-deterministic message in the assert below if + the drivers are extra shitty and don't implement this query (Intel + Windows drivers, I'm talking about you). */ + { + GLint format{}; + /* Note that this has to call getCubeLevelParameterivImplementation(), + not getLevelParameterivImplementation(), to supply a concrete + coordinate in non-DSA codepaths */ + Context::current().state().texture.getCubeLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); + CORRADE_ASSERT(compressedPixelFormat(image.format()) == CompressedPixelFormat(format), + "GL::CubeMapTexture::compressedImage(): expected image view format" << CompressedPixelFormat(format) << "but got" << compressedPixelFormat(image.format()), ); + } #endif Buffer::unbindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image); Context::current().state().texture.getCompressedCubeImageImplementation(*this, coordinate, level, size, image.data().size(), image.data()); } void CubeMapTexture::compressedImage(const CubeMapCoordinate coordinate, const Int level, CompressedBufferImage2D& image, const BufferUsage usage) { const Vector2i size = imageSize(level); - /* If the user-provided pixel storage doesn't tell us all properties about - the compression, we need to ask GL for it */ - std::size_t dataSize; - if(!image.storage().compressedBlockSize().product() || !image.storage().compressedBlockDataSize()) - dataSize = Context::current().state().texture.getCubeLevelCompressedImageSizeImplementation(*this, level); - else - dataSize = Magnum::Implementation::compressedImageDataSizeFor(image, size); - - /* Internal texture format. Zero-init to avoid an assert about value - already wrapped in compressedPixelFormatWrap() later if the drivers are - extra shitty (Intel Windows drivers, I'm talking about you). */ + /* Get internal texture format, determine its properties (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat) and + calculate data size for those. Yes, if the format is unknown to Magnum, + this will blow up. But that's likely a very rare scenario that isn't + worth implementing (and is rather impossible to test), and the user can + always query into a view with block properties specified in that case. + + The format is zero-init to have it deterministically assert inside + compressedPixelFormatBlockSize() if the drivers are extra shitty and + don't implement this query (Intel Windows drivers, I'm talking about + you), otherwise it could give back a value that could randomly work, or + cause OOMs, crashes and such. */ GLint format{}; + /* Note that this has to call getCubeLevelParameterivImplementation(), not + getLevelParameterivImplementation(), to supply a concrete coordinate in + non-DSA codepaths */ Context::current().state().texture.getCubeLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); + const Vector3i blockSize = compressedPixelFormatBlockSize(CompressedPixelFormat(format)); + const UnsignedInt blockDataSize = compressedPixelFormatBlockDataSize(CompressedPixelFormat(format)); + const std::size_t dataSize = Magnum::Implementation::compressedImageDataSizeFor(image.storage(), blockSize, blockDataSize, size); /* Reallocate only if needed */ if(image.dataSize() < dataSize) image.setData(image.storage(), CompressedPixelFormat(format), size, {nullptr, dataSize}, usage); else image.setData(image.storage(), CompressedPixelFormat(format), size, nullptr, usage); + /* The setData() call above updates the block properties, so just verify + they're the same as the ones used here as the ones from the umage get + used in applyCompressedPixelStoragePack() below */ + CORRADE_INTERNAL_ASSERT(blockSize == image.blockSize() && blockDataSize == image.blockDataSize()); image.buffer().bindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image); Context::current().state().texture.getCompressedCubeImageImplementation(*this, coordinate, level, size, dataSize, nullptr); } @@ -411,19 +463,26 @@ void CubeMapTexture::compressedSubImage(const Int level, const Range3Di& range, created w/ the DSA extension disabled but below a DSA API is used */ createIfNotAlready(); - /* Internal texture format. Zero-init to avoid an assert about value - already wrapped in compressedPixelFormatWrap() later if the drivers are - extra shitty (Intel Windows drivers, I'm talking about you). */ + /* Get internal texture format, determine its properties (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat) and + calculate data size for those. Yes, if the format is unknown to Magnum, + this will blow up. But that's likely a very rare scenario that isn't + worth implementing (and is rather impossible to test), and the user can + always query into a view with block properties specified in that case. + + The format is zero-init to have it deterministically assert inside + compressedPixelFormatBlockSize() if the drivers are extra shitty and + don't implement this query (Intel Windows drivers, I'm talking about + you), otherwise it could give back a value that could randomly work, or + cause OOMs, crashes and such. */ GLint format{}; + /* Note that this has to call getCubeLevelParameterivImplementation(), not + getLevelParameterivImplementation(), to supply a concrete coordinate in + non-DSA codepaths */ Context::current().state().texture.getCubeLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); - - /* Calculate compressed subimage size. If the user-provided pixel storage - doesn't tell us all properties about the compression, we need to ask GL - for it. That requires GL_ARB_internalformat_query2. */ - std::size_t dataSize; - if(!image.storage().compressedBlockSize().product() || !image.storage().compressedBlockDataSize()) - dataSize = compressedSubImageSize<3>(TextureFormat(format), range.size()); - else dataSize = Magnum::Implementation::compressedImageDataSizeFor(image, range.size()); + const Vector3i blockSize = compressedPixelFormatBlockSize(CompressedPixelFormat(format)); + const UnsignedInt blockDataSize = compressedPixelFormatBlockDataSize(CompressedPixelFormat(format)); + const std::size_t dataSize = Magnum::Implementation::compressedImageDataSizeFor(image.storage(), blockSize, blockDataSize, range.size()); /* Reallocate only if needed */ Containers::Array data{image.release()}; @@ -431,7 +490,7 @@ void CubeMapTexture::compressedSubImage(const Int level, const Range3Di& range, data = Containers::Array{dataSize}; Buffer::unbindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image.storage(), blockSize, blockDataSize); glGetCompressedTextureSubImage(_id, level, range.min().x(), range.min().y(), range.min().z(), range.size().x(), range.size().y(), range.size().z(), data.size(), data); /* Would be CubeMap if the whole image was queried, but then we'd have to query the size and compare, which is extra work. So it's Array @@ -455,18 +514,24 @@ void CubeMapTexture::compressedSubImage(const Int level, const Range3Di& range, createIfNotAlready(); #ifndef CORRADE_NO_ASSERT - /* Internal texture format. Zero-init to avoid an assert about value - already wrapped in compressedPixelFormatWrap() later if the drivers are - extra shitty (Intel Windows drivers, I'm talking about you). */ - GLint format{}; - Context::current().state().texture.getCubeLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); - - CORRADE_ASSERT(compressedPixelFormat(image.format()) == CompressedPixelFormat(format), - "GL::CubeMapTexture::compressedSubImage(): expected image view format" << CompressedPixelFormat(format) << "but got" << compressedPixelFormat(image.format()), ); + /* Check that the internal texture format matches (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat). + Zero-init to avoid a non-deterministic message in the assert below if + the drivers are extra shitty and don't implement this query (Intel + Windows drivers, I'm talking about you). */ + { + GLint format{}; + /* Note that this has to call getCubeLevelParameterivImplementation(), + not getLevelParameterivImplementation(), to supply a concrete + coordinate in non-DSA codepaths */ + Context::current().state().texture.getCubeLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); + CORRADE_ASSERT(compressedPixelFormat(image.format()) == CompressedPixelFormat(format), + "GL::CubeMapTexture::compressedSubImage(): expected image view format" << CompressedPixelFormat(format) << "but got" << compressedPixelFormat(image.format()), ); + } #endif Buffer::unbindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image); glGetCompressedTextureSubImage(_id, level, range.min().x(), range.min().y(), range.min().z(), range.size().x(), range.size().y(), range.size().z(), image.data().size(), image.data()); } @@ -475,28 +540,39 @@ void CubeMapTexture::compressedSubImage(const Int level, const Range3Di& range, created w/ the DSA extension disabled but below a DSA API is used */ createIfNotAlready(); - /* Internal texture format. Zero-init to avoid an assert about value - already wrapped in compressedPixelFormatWrap() later if the drivers are - extra shitty (Intel Windows drivers, I'm talking about you). */ + /* Get internal texture format, determine its properties (compressed + GL::TextureFormat values are same as GL::CompressedPixelFormat) and + calculate data size for those. Yes, if the format is unknown to Magnum, + this will blow up. But that's likely a very rare scenario that isn't + worth implementing (and is rather impossible to test), and the user can + always query into a view with block properties specified in that case. + + The format is zero-init to have it deterministically assert inside + compressedPixelFormatBlockSize() if the drivers are extra shitty and + don't implement this query (Intel Windows drivers, I'm talking about + you), otherwise it could give back a value that could randomly work, or + cause OOMs, crashes and such. */ GLint format{}; + /* Note that this has to call getCubeLevelParameterivImplementation(), not + getLevelParameterivImplementation(), to supply a concrete coordinate in + non-DSA codepaths */ Context::current().state().texture.getCubeLevelParameterivImplementation(*this, level, GL_TEXTURE_INTERNAL_FORMAT, &format); - - /* Calculate compressed subimage size. If the user-provided pixel storage - doesn't tell us all properties about the compression, we need to ask GL - for it. That requires GL_ARB_internalformat_query2. */ - std::size_t dataSize; - if(!image.storage().compressedBlockSize().product() || !image.storage().compressedBlockDataSize()) - dataSize = compressedSubImageSize<3>(TextureFormat(format), range.size()); - else dataSize = Magnum::Implementation::compressedImageDataSizeFor(image, range.size()); + const Vector3i blockSize = compressedPixelFormatBlockSize(CompressedPixelFormat(format)); + const UnsignedInt blockDataSize = compressedPixelFormatBlockDataSize(CompressedPixelFormat(format)); + const std::size_t dataSize = Magnum::Implementation::compressedImageDataSizeFor(image.storage(), blockSize, blockDataSize, range.size()); /* Reallocate only if needed */ if(image.dataSize() < dataSize) image.setData(image.storage(), CompressedPixelFormat(format), range.size(), {nullptr, dataSize}, usage); else image.setData(image.storage(), CompressedPixelFormat(format), range.size(), nullptr, usage); + /* The setData() call above updates the block properties, so just verify + they're the same as the ones used here as the ones from the umage get + used in applyCompressedPixelStoragePack() below */ + CORRADE_INTERNAL_ASSERT(blockSize == image.blockSize() && blockDataSize == image.blockDataSize()); image.buffer().bindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer.applyPixelStoragePack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStoragePack(image); glGetCompressedTextureSubImage(_id, level, range.min().x(), range.min().y(), range.min().z(), range.size().x(), range.size().y(), range.size().z(), dataSize, nullptr); } @@ -535,7 +611,7 @@ CubeMapTexture& CubeMapTexture::setCompressedSubImage(const Int level, const Vec createIfNotAlready(); Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); glCompressedTextureSubImage3D(_id, level, offset.x(), offset.y(), offset.z(), image.size().x(), image.size().y(), image.size().z(), GLenum(compressedPixelFormat(image.format())), Implementation::occupiedCompressedImageDataSize(image), image.data()); return *this; } @@ -546,7 +622,7 @@ CubeMapTexture& CubeMapTexture::setCompressedSubImage(const Int level, const Vec createIfNotAlready(); image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); glCompressedTextureSubImage3D(_id, level, offset.x(), offset.y(), offset.z(), image.size().x(), image.size().y(), image.size().z(), GLenum(image.format()), Implementation::occupiedCompressedImageDataSize(image), nullptr); return *this; } @@ -578,7 +654,7 @@ CubeMapTexture& CubeMapTexture::setCompressedSubImage(const CubeMapCoordinate co #ifndef MAGNUM_TARGET_GLES2 Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); #endif - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); Context::current().state().texture.cubeCompressedSubImageImplementation(*this, coordinate, level, offset, image.size(), compressedPixelFormat(image.format()), image.data(), Implementation::occupiedCompressedImageDataSize(image)); return *this; } @@ -586,7 +662,7 @@ CubeMapTexture& CubeMapTexture::setCompressedSubImage(const CubeMapCoordinate co #ifndef MAGNUM_TARGET_GLES2 CubeMapTexture& CubeMapTexture::setCompressedSubImage(const CubeMapCoordinate coordinate, const Int level, const Vector2i& offset, CompressedBufferImage2D& image) { image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); - Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); + Context::current().state().renderer.applyCompressedPixelStorageUnpack(image); Context::current().state().texture.cubeCompressedSubImageImplementation(*this, coordinate, level, offset, image.size(), image.format(), nullptr, Implementation::occupiedCompressedImageDataSize(image)); return *this; } diff --git a/src/Magnum/GL/CubeMapTexture.h b/src/Magnum/GL/CubeMapTexture.h index ce0b97fa5..e7b221a5f 100644 --- a/src/Magnum/GL/CubeMapTexture.h +++ b/src/Magnum/GL/CubeMapTexture.h @@ -663,15 +663,24 @@ class MAGNUM_GL_EXPORT CubeMapTexture: public AbstractTexture { /** * @brief Read given mip level of a compressed texture to an image * - * Compression format and data size are taken from the texture, image - * size is taken using @ref imageSize(). Flags of @p image get reset to + * Compression format is taken from the texture, image size is taken + * using @ref imageSize(). Flags of @p image get reset to * @ref ImageFlag3D::CubeMap. The storage is not reallocated if it is * large enough to contain the new data --- however if you want to read * into existing memory or *ensure* a reallocation does not happen, use * @ref compressedImage(Int, const MutableCompressedImageView3D&) * instead. + * + * The function assumes that the internal texture format is a known + * @ref CompressedPixelFormat in order to correctly size the output + * array using @ref compressedPixelFormatBlockSize() and + * @ref compressedPixelFormatBlockDataSize() properties queried on it. + * If it's not, use @ref compressedImage(Int, const MutableCompressedImageView3D&) + * instead and specify the format along with explicit block properties. + * You can also query the texture itself using + * @ref compressedBlockSize() and @ref compressedBlockDataSize() in + * that case, if available on given platform. * @see @fn_gl2{GetTextureLevelParameter,GetTexLevelParameter} with - * @def_gl{TEXTURE_COMPRESSED_IMAGE_SIZE}, * @def_gl{TEXTURE_INTERNAL_FORMAT}, @def_gl{TEXTURE_WIDTH}, * @def_gl{TEXTURE_HEIGHT}, then * @fn_gl2_keyword{GetCompressedTextureImage,GetCompressedTexImage} @@ -802,16 +811,26 @@ class MAGNUM_GL_EXPORT CubeMapTexture: public AbstractTexture { /** * @brief Read given compressed texture mip level and coordinate to an image * - * Compression format and data size are taken from the texture, image - * size is taken using @ref imageSize(). Flags of @p image get cleared - * --- use @ref compressedImage(Int, CompressedImage3D&) instead if you - * want to get an image annotated with @ref ImageFlag3D::CubeMap. The - * storage is not reallocated if it is large enough to contain the new - * data --- however if you want to read into existing memory or - * *ensure* a reallocation does not happen, use + * Compression format is taken from the texture, image size is taken + * using @ref imageSize(). Flags of @p image get cleared --- use + * @ref compressedImage(Int, CompressedImage3D&) instead if you want to + * get an image annotated with @ref ImageFlag3D::CubeMap. The storage + * is not reallocated if it is large enough to contain the new data --- + * however if you want to read into existing memory or *ensure* a + * reallocation does not happen, use * @ref compressedImage(CubeMapCoordinate, Int, const MutableCompressedImageView2D&) * instead. * + * The function assumes that the internal texture format is a known + * @ref CompressedPixelFormat in order to correctly size the output + * array using @ref compressedPixelFormatBlockSize() and + * @ref compressedPixelFormatBlockDataSize() properties queried on it. + * If it's not, use @ref compressedImage(CubeMapCoordinate, Int, const MutableCompressedImageView2D&) + * instead and specify the format along with explicit block properties. + * You can also query the texture itself using + * @ref compressedBlockSize() and @ref compressedBlockDataSize() in + * that case, if available on given platform. + * * If @gl_extension{ARB,get_texture_sub_image} (part of OpenGL 4.5) is * not available, the texture is bound before the operation (if not * already). If either @gl_extension{ARB,get_texture_sub_image} or @@ -819,7 +838,6 @@ class MAGNUM_GL_EXPORT CubeMapTexture: public AbstractTexture { * protected from buffer overflow. * @see @fn_gl2{GetTextureLevelParameter,GetTexLevelParameter}, * eventually @fn_gl{GetTexLevelParameter} with - * @def_gl{TEXTURE_COMPRESSED_IMAGE_SIZE}, * @def_gl{TEXTURE_INTERNAL_FORMAT}, @def_gl{TEXTURE_WIDTH}, * @def_gl{TEXTURE_HEIGHT}, then @fn_gl{PixelStore}, then * @fn_gl_keyword{GetCompressedTextureSubImage}, @@ -956,10 +974,6 @@ class MAGNUM_GL_EXPORT CubeMapTexture: public AbstractTexture { * @requires_gl45 Extension @gl_extension{ARB,get_texture_sub_image} * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage} * for non-default @ref CompressedPixelStorage - * @requires_gl43 Extension @gl_extension{ARB,internalformat_query2} if - * @ref CompressedPixelStorage::compressedBlockSize() and - * @ref CompressedPixelStorage::compressedBlockDataSize() are not - * set to non-zero values * @requires_gl Texture image queries are not available in OpenGL ES or * WebGL. See @ref Framebuffer::read() or @ref DebugTools::textureSubImage() * for possible workarounds. @@ -995,10 +1009,6 @@ class MAGNUM_GL_EXPORT CubeMapTexture: public AbstractTexture { * @requires_gl45 Extension @gl_extension{ARB,get_texture_sub_image} * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage} * for non-default @ref CompressedPixelStorage - * @requires_gl43 Extension @gl_extension{ARB,internalformat_query2} if - * @ref CompressedPixelStorage::compressedBlockSize() and - * @ref CompressedPixelStorage::compressedBlockDataSize() are not - * set to non-zero values * @requires_gl Texture image queries are not available in OpenGL ES or * WebGL. See @ref Framebuffer::read() or @ref DebugTools::textureSubImage() * for possible workarounds. diff --git a/src/Magnum/GL/CubeMapTextureArray.h b/src/Magnum/GL/CubeMapTextureArray.h index 750799424..f1a7fe6fb 100644 --- a/src/Magnum/GL/CubeMapTextureArray.h +++ b/src/Magnum/GL/CubeMapTextureArray.h @@ -715,10 +715,6 @@ class MAGNUM_GL_EXPORT CubeMapTextureArray: public AbstractTexture { * @requires_gl45 Extension @gl_extension{ARB,get_texture_sub_image} * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage} * for non-default @ref CompressedPixelStorage - * @requires_gl43 Extension @gl_extension{ARB,internalformat_query2} if - * @ref CompressedPixelStorage::compressedBlockSize() and - * @ref CompressedPixelStorage::compressedBlockDataSize() are not - * set to non-zero values * @requires_gl Texture image queries are not available in OpenGL ES. * See @ref Framebuffer::read() or @ref DebugTools::textureSubImage() * for possible workarounds. @@ -761,10 +757,6 @@ class MAGNUM_GL_EXPORT CubeMapTextureArray: public AbstractTexture { * @requires_gl45 Extension @gl_extension{ARB,get_texture_sub_image} * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage} * for non-default @ref CompressedPixelStorage - * @requires_gl43 Extension @gl_extension{ARB,internalformat_query2} if - * @ref CompressedPixelStorage::compressedBlockSize() and - * @ref CompressedPixelStorage::compressedBlockDataSize() are not - * set to non-zero values * @requires_gl Texture image queries are not available in OpenGL ES. * See @ref Framebuffer::read() or @ref DebugTools::textureSubImage() * for possible workarounds. diff --git a/src/Magnum/GL/Implementation/RendererState.cpp b/src/Magnum/GL/Implementation/RendererState.cpp index cc6107d30..fcf90e276 100644 --- a/src/Magnum/GL/Implementation/RendererState.cpp +++ b/src/Magnum/GL/Implementation/RendererState.cpp @@ -359,39 +359,46 @@ void RendererState::applyPixelStorageInternal(const Magnum::PixelStorage& storag #endif } -void RendererState::applyPixelStorageInternal(const CompressedPixelStorage& storage, const bool isUnpack) { +void RendererState::applyCompressedPixelStorageInternal(const CompressedPixelStorage& storage, const Vector3i& blockSize, const Int blockDataSize, const bool isUnpack) { #ifdef MAGNUM_TARGET_GLES CORRADE_ASSERT(storage == CompressedPixelStorage{}, "GL: non-default CompressedPixelStorage parameters are not supported in OpenGL ES or WebGL", ); + static_cast(blockSize); + static_cast(blockDataSize); static_cast(isUnpack); #else + /* The block properties should always be non-zero, either coming from an + Image(View) constructed with a particular format or from properties for + a format that was queried from GL */ + CORRADE_INTERNAL_ASSERT(blockSize != Vector3i{} && blockDataSize != 0); + applyPixelStorageInternal(static_cast(storage), isUnpack); PixelStorage& state = isUnpack ? unpackPixelStorage : packPixelStorage; /* Compressed block width */ if(state.compressedBlockSize.x() == PixelStorage::DisengagedValue || - state.compressedBlockSize.x() != storage.compressedBlockSize().x()) + state.compressedBlockSize.x() != blockSize.x()) glPixelStorei(isUnpack ? GL_UNPACK_COMPRESSED_BLOCK_WIDTH : GL_PACK_COMPRESSED_BLOCK_WIDTH, - state.compressedBlockSize.x() = storage.compressedBlockSize().x()); + state.compressedBlockSize.x() = blockSize.x()); /* Compressed block height */ if(state.compressedBlockSize.y() == PixelStorage::DisengagedValue || - state.compressedBlockSize.y() != storage.compressedBlockSize().y()) + state.compressedBlockSize.y() != blockSize.y()) glPixelStorei(isUnpack ? GL_UNPACK_COMPRESSED_BLOCK_HEIGHT : GL_PACK_COMPRESSED_BLOCK_HEIGHT, - state.compressedBlockSize.y() = storage.compressedBlockSize().y()); + state.compressedBlockSize.y() = blockSize.y()); /* Compressed block depth */ if(state.compressedBlockSize.z() == PixelStorage::DisengagedValue || - state.compressedBlockSize.z() != storage.compressedBlockSize().z()) + state.compressedBlockSize.z() != blockSize.z()) glPixelStorei(isUnpack ? GL_UNPACK_COMPRESSED_BLOCK_DEPTH : GL_PACK_COMPRESSED_BLOCK_DEPTH, - state.compressedBlockSize.z() = storage.compressedBlockSize().z()); + state.compressedBlockSize.z() = blockSize.z()); /* Compressed block size */ if(state.compressedBlockDataSize == PixelStorage::DisengagedValue || - state.compressedBlockDataSize != storage.compressedBlockDataSize()) + state.compressedBlockDataSize != blockDataSize) glPixelStorei(isUnpack ? GL_UNPACK_COMPRESSED_BLOCK_SIZE : GL_PACK_COMPRESSED_BLOCK_SIZE, - state.compressedBlockDataSize = storage.compressedBlockDataSize()); + state.compressedBlockDataSize = blockDataSize); #endif } diff --git a/src/Magnum/GL/Implementation/RendererState.h b/src/Magnum/GL/Implementation/RendererState.h index 850fb2a19..ef475c1ea 100644 --- a/src/Magnum/GL/Implementation/RendererState.h +++ b/src/Magnum/GL/Implementation/RendererState.h @@ -117,17 +117,26 @@ struct RendererState { void applyPixelStorageUnpack(const Magnum::PixelStorage& storage) { applyPixelStorageInternal(storage, true); } + /* Deleted to avoid accidents -- use applyCompressedPixelStorage*() */ + void applyPixelStoragePack(const Magnum::CompressedPixelStorage&) = delete; + void applyPixelStorageUnpack(const Magnum::CompressedPixelStorage&) = delete; /* Bool parameter is ugly, but this is implementation detail of internal API so who cares */ - void applyPixelStorageInternal(const CompressedPixelStorage& storage, bool unpack); - /* Used internally in *Texture::compressedImage(), *Texture::compressedSubImage(), - *Texture::setCompressedImage() and *Texture::setCompressedSubImage() */ - void applyPixelStoragePack(const CompressedPixelStorage& storage) { - applyPixelStorageInternal(storage, false); + void applyCompressedPixelStorageInternal(const CompressedPixelStorage& storage, const Vector3i& blockSize, Int blockDataSize, bool unpack); + /* Used internally in *Texture::compressedImage(), + *Texture::compressedSubImage(), *Texture::setCompressedImage() and + *Texture::setCompressedSubImage(). The overload with explicit block + properties is used in APIs that take an Image& and which replace it with + am image of a new format along with its properties. */ + void applyCompressedPixelStoragePack(const CompressedPixelStorage& storage, const Vector3i& blockSize, Int blockDataSize) { + applyCompressedPixelStorageInternal(storage, blockSize, blockDataSize, false); } - void applyPixelStorageUnpack(const CompressedPixelStorage& storage) { - applyPixelStorageInternal(storage, true); + template void applyCompressedPixelStoragePack(const T& image) { + applyCompressedPixelStoragePack(image.storage(), image.blockSize(), image.blockDataSize()); + } + template void applyCompressedPixelStorageUnpack(const T& image) { + applyCompressedPixelStorageInternal(image.storage(), image.blockSize(), image.blockDataSize(), true); } }; diff --git a/src/Magnum/GL/RectangleTexture.h b/src/Magnum/GL/RectangleTexture.h index 22c7403a9..d9d66439a 100644 --- a/src/Magnum/GL/RectangleTexture.h +++ b/src/Magnum/GL/RectangleTexture.h @@ -545,10 +545,6 @@ class MAGNUM_GL_EXPORT RectangleTexture: public AbstractTexture { * @requires_gl45 Extension @gl_extension{ARB,get_texture_sub_image} * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage} * for non-default @ref CompressedPixelStorage - * @requires_gl43 Extension @gl_extension{ARB,internalformat_query2} if - * @ref CompressedPixelStorage::compressedBlockSize() and - * @ref CompressedPixelStorage::compressedBlockDataSize() are not - * set to non-zero values */ void compressedSubImage(const Range2Di& range, CompressedImage2D& image) { AbstractTexture::compressedSubImage<2>(0, range, image, ImageFlags2D{}); @@ -585,10 +581,6 @@ class MAGNUM_GL_EXPORT RectangleTexture: public AbstractTexture { * @requires_gl45 Extension @gl_extension{ARB,get_texture_sub_image} * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage} * for non-default @ref CompressedPixelStorage - * @requires_gl43 Extension @gl_extension{ARB,internalformat_query2} if - * @ref CompressedPixelStorage::compressedBlockSize() and - * @ref CompressedPixelStorage::compressedBlockDataSize() are not - * set to non-zero values */ void compressedSubImage(const Range2Di& range, CompressedBufferImage2D& image, BufferUsage usage) { AbstractTexture::compressedSubImage<2>(0, range, image, usage); diff --git a/src/Magnum/GL/Test/AbstractTextureGLTest.cpp b/src/Magnum/GL/Test/AbstractTextureGLTest.cpp index 1ac0c5bbf..17e2a9c18 100644 --- a/src/Magnum/GL/Test/AbstractTextureGLTest.cpp +++ b/src/Magnum/GL/Test/AbstractTextureGLTest.cpp @@ -252,8 +252,6 @@ void AbstractTextureGLTest::compressedSubImageQueryViewNullptr() { CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() << "is not supported."); if(!Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); - if(!Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); Texture2D texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); @@ -275,8 +273,6 @@ void AbstractTextureGLTest::compressedSubImageQueryViewBadSize() { CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() << "is not supported."); if(!Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); - if(!Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); Texture2D texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); @@ -299,8 +295,6 @@ void AbstractTextureGLTest::compressedSubImageQueryViewBadFormat() { CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() << "is not supported."); if(!Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); - if(!Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); Texture2D texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); diff --git a/src/Magnum/GL/Test/CubeMapTextureArrayGLTest.cpp b/src/Magnum/GL/Test/CubeMapTextureArrayGLTest.cpp index 4a82e662d..b4689c4fd 100644 --- a/src/Magnum/GL/Test/CubeMapTextureArrayGLTest.cpp +++ b/src/Magnum/GL/Test/CubeMapTextureArrayGLTest.cpp @@ -1305,8 +1305,6 @@ void CubeMapTextureArrayGLTest::compressedSubImageQuery() { CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); CubeMapTextureArray texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, {12, 12, 6}) @@ -1338,8 +1336,6 @@ void CubeMapTextureArrayGLTest::compressedSubImageQueryView() { CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); CubeMapTextureArray texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, {12, 12, 6}) @@ -1373,8 +1369,6 @@ void CubeMapTextureArrayGLTest::compressedSubImageQueryBuffer() { CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); CubeMapTextureArray texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, {12, 12, 6}) diff --git a/src/Magnum/GL/Test/CubeMapTextureGLTest.cpp b/src/Magnum/GL/Test/CubeMapTextureGLTest.cpp index a85bf6538..005242ddb 100644 --- a/src/Magnum/GL/Test/CubeMapTextureGLTest.cpp +++ b/src/Magnum/GL/Test/CubeMapTextureGLTest.cpp @@ -1698,8 +1698,6 @@ void CubeMapTextureGLTest::compressedSubImageQuery() { CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); CubeMapTexture texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{12}) @@ -1728,8 +1726,6 @@ void CubeMapTextureGLTest::compressedSubImageQueryView() { CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); CubeMapTexture texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{12}) @@ -1820,8 +1816,6 @@ void CubeMapTextureGLTest::compressedSubImageQueryBuffer() { CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); CubeMapTexture texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{12}) diff --git a/src/Magnum/GL/Test/TextureArrayGLTest.cpp b/src/Magnum/GL/Test/TextureArrayGLTest.cpp index d3e4d8d3f..7b56f0494 100644 --- a/src/Magnum/GL/Test/TextureArrayGLTest.cpp +++ b/src/Magnum/GL/Test/TextureArrayGLTest.cpp @@ -1940,8 +1940,6 @@ void TextureArrayGLTest::compressedSubImage2DQuery() { CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); Texture2DArray texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, {12, 4, 4}) @@ -1974,8 +1972,6 @@ void TextureArrayGLTest::compressedSubImage2DQueryView() { CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); Texture2DArray texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, {12, 4, 4}) @@ -2010,8 +2006,6 @@ void TextureArrayGLTest::compressedSubImage2DQueryBuffer() { CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); Texture2DArray texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, {12, 4, 4}) diff --git a/src/Magnum/GL/Test/TextureGLTest.cpp b/src/Magnum/GL/Test/TextureGLTest.cpp index 21cc05e91..f091e85a1 100644 --- a/src/Magnum/GL/Test/TextureGLTest.cpp +++ b/src/Magnum/GL/Test/TextureGLTest.cpp @@ -2389,8 +2389,6 @@ void TextureGLTest::compressedSubImage2DQuery() { CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); Texture2D texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, {12, 4}) @@ -2420,8 +2418,6 @@ void TextureGLTest::compressedSubImage2DQueryView() { CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); Texture2D texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, {12, 4}) @@ -2453,8 +2449,6 @@ void TextureGLTest::compressedSubImage2DQueryBuffer() { CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); Texture2D texture; texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, {12, 4}) @@ -3010,8 +3004,6 @@ void TextureGLTest::compressedSubImage3DQuery() { CORRADE_SKIP(Extensions::ARB::texture_compression_bptc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); Texture3D texture; texture.setStorage(1, TextureFormat::CompressedRGBABptcUnorm, {12, 4, 4}) @@ -3044,8 +3036,6 @@ void TextureGLTest::compressedSubImage3DQueryView() { CORRADE_SKIP(Extensions::ARB::texture_compression_bptc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); Texture3D texture; texture.setStorage(1, TextureFormat::CompressedRGBABptcUnorm, {12, 4, 4}) @@ -3080,8 +3070,6 @@ void TextureGLTest::compressedSubImage3DQueryBuffer() { CORRADE_SKIP(Extensions::ARB::texture_compression_bptc::string() << "is not supported."); if(data.storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() << "is not supported."); - if(data.storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) - CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() << "is not supported."); Texture3D texture; texture.setStorage(1, TextureFormat::CompressedRGBABptcUnorm, {12, 4, 4}) diff --git a/src/Magnum/GL/Texture.h b/src/Magnum/GL/Texture.h index 82a52be97..6d5e7d58f 100644 --- a/src/Magnum/GL/Texture.h +++ b/src/Magnum/GL/Texture.h @@ -943,14 +943,24 @@ Texture: public AbstractTexture { * @param level Mip level * @param image Image where to put the compressed data * - * Compression format and data size are taken from the texture, image - * size is taken using @ref imageSize(). @ref ImageFlags of @p image - * get cleared. The storage is not reallocated if it is large enough to - * contain the new data --- however if you want to read into existing - * memory or *ensure* a reallocation does not happen, use + * Compression format is taken from the texture, image size is taken + * using @ref imageSize(). @ref ImageFlags of @p image get cleared. The + * storage is not reallocated if it is large enough to contain the new + * data --- however if you want to read into existing memory or + * *ensure* a reallocation does not happen, use * @ref compressedImage(Int, const BasicMutableCompressedImageView&) * instead. * + * The function assumes that the internal texture format is a known + * @ref CompressedPixelFormat in order to correctly size the output + * array using @ref compressedPixelFormatBlockSize() and + * @ref compressedPixelFormatBlockDataSize() properties queried on it. + * If it's not, use @ref compressedImage(Int, const BasicMutableCompressedImageView&) + * instead and specify the format along with explicit block properties. + * You can also query the texture itself using + * @ref compressedBlockSize() and @ref compressedBlockDataSize() in + * that case, if available on given platform. + * * If @gl_extension{ARB,direct_state_access} (part of OpenGL 4.5) is * not available, the texture is bound before the operation (if not * already). If either @gl_extension{ARB,direct_state_access} or @@ -958,7 +968,6 @@ Texture: public AbstractTexture { * protected from buffer overflow. * @see @fn_gl2{GetTextureLevelParameter,GetTexLevelParameter}, * eventually @fn_gl{GetTexLevelParameter} with - * @def_gl{TEXTURE_COMPRESSED_IMAGE_SIZE}, * @def_gl{TEXTURE_INTERNAL_FORMAT}, @def_gl{TEXTURE_WIDTH}, * @def_gl{TEXTURE_HEIGHT}, @def_gl{TEXTURE_DEPTH}, then * @fn_gl{PixelStore}, then @@ -1107,27 +1116,29 @@ Texture: public AbstractTexture { * @param range Range to read * @param image Image where to put the compressed data * - * Compression format and data size are taken from the texture. - * @ref ImageFlags of @p image get cleared. The storage is not - * reallocated if it is large enough to contain the new data --- - * however if you want to read into existing memory or *ensure* a - * reallocation does not happen, use + * Compression format is taken from the texture. @ref ImageFlags of + * @p image get cleared. The storage is not reallocated if it is large + * enough to contain the new data --- however if you want to read into + * existing memory or *ensure* a reallocation does not happen, use * @ref compressedSubImage(Int, const RangeTypeFor&, const BasicMutableCompressedImageView&) * instead. + * + * The function assumes that the internal texture format is a known + * @ref CompressedPixelFormat in order to correctly size the output + * array using @ref compressedPixelFormatBlockSize() and + * @ref compressedPixelFormatBlockDataSize() properties queried on it. + * If it's not, use@ref compressedSubImage(Int, const RangeTypeFor&, const BasicMutableCompressedImageView&) + * instead and specify the format along with explicit block properties. + * You can also query the texture itself using + * @ref compressedBlockSize() and @ref compressedBlockDataSize() in + * that case, if available on given platform. * @see @fn_gl2{GetTextureLevelParameter,GetTexLevelParameter}, * eventually @fn_gl{GetTexLevelParameter} with - * @def_gl{TEXTURE_INTERNAL_FORMAT}, then possibly - * @fn_gl{GetInternalformat} with @def_gl{TEXTURE_COMPRESSED_BLOCK_SIZE}, - * @def_gl{TEXTURE_COMPRESSED_BLOCK_WIDTH} and - * @def_gl{TEXTURE_COMPRESSED_BLOCK_HEIGHT}, then + * @def_gl{TEXTURE_INTERNAL_FORMAT}, then * @fn_gl_keyword{GetCompressedTextureSubImage} * @requires_gl45 Extension @gl_extension{ARB,get_texture_sub_image} * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage} * for non-default @ref CompressedPixelStorage - * @requires_gl43 Extension @gl_extension{ARB,internalformat_query2} if - * @ref CompressedPixelStorage::compressedBlockSize() and - * @ref CompressedPixelStorage::compressedBlockDataSize() are not - * set to non-zero values * @requires_gl Texture image queries are not available in OpenGL ES or * WebGL. See @ref Framebuffer::read() or @ref DebugTools::textureSubImage() * for possible workarounds. @@ -1171,10 +1182,6 @@ Texture: public AbstractTexture { * @requires_gl45 Extension @gl_extension{ARB,get_texture_sub_image} * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage} * for non-default @ref CompressedPixelStorage - * @requires_gl43 Extension @gl_extension{ARB,internalformat_query2} if - * @ref CompressedPixelStorage::compressedBlockSize() and - * @ref CompressedPixelStorage::compressedBlockDataSize() are not - * set to non-zero values * @requires_gl Texture image queries are not available in OpenGL ES or * WebGL. See @ref Framebuffer::read() or @ref DebugTools::textureSubImage() * for possible workarounds. diff --git a/src/Magnum/GL/TextureArray.h b/src/Magnum/GL/TextureArray.h index 8219e82ca..8a19fdadc 100644 --- a/src/Magnum/GL/TextureArray.h +++ b/src/Magnum/GL/TextureArray.h @@ -796,10 +796,6 @@ TextureArray: public AbstractTexture { * @requires_gl45 Extension @gl_extension{ARB,get_texture_sub_image} * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage} * for non-default @ref CompressedPixelStorage - * @requires_gl43 Extension @gl_extension{ARB,internalformat_query2} if - * @ref CompressedPixelStorage::compressedBlockSize() and - * @ref CompressedPixelStorage::compressedBlockDataSize() are not - * set to non-zero values * @requires_gl Texture image queries are not available in OpenGL ES or * WebGL. See @ref Framebuffer::read() or @ref DebugTools::textureSubImage() * for possible workarounds. @@ -839,10 +835,6 @@ TextureArray: public AbstractTexture { * @requires_gl45 Extension @gl_extension{ARB,get_texture_sub_image} * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage} * for non-default @ref CompressedPixelStorage - * @requires_gl43 Extension @gl_extension{ARB,internalformat_query2} if - * @ref CompressedPixelStorage::compressedBlockSize() and - * @ref CompressedPixelStorage::compressedBlockDataSize() are not - * set to non-zero values * @requires_gl Texture image queries are not available in OpenGL ES or * WebGL. See @ref Framebuffer::read() or @ref DebugTools::textureSubImage() * for possible workarounds. diff --git a/src/Magnum/Implementation/ImageProperties.h b/src/Magnum/Implementation/ImageProperties.h index 0a3755bf3..5ddb1f50b 100644 --- a/src/Magnum/Implementation/ImageProperties.h +++ b/src/Magnum/Implementation/ImageProperties.h @@ -190,6 +190,12 @@ template std::size_t compressedImageDataSizeFor return r.first + r.second; } +/* Used in image query functions */ +template std::size_t compressedImageDataSizeFor(const CompressedPixelStorage& storage, const Vector3i& blockSize, const UnsignedInt blockDataSize, const Math::Vector& size) { + auto r = compressedImageDataOffsetSizeFor(storage, blockSize, blockDataSize, size); + return r.first + r.second; +} + /* Used in data size assertions */ template inline std::size_t compressedImageDataSize(const T& image) { auto r = compressedImageDataOffsetSizeFor(image, image.size());