From 07b3253714fef2af4febbd5a9e3e361d3d6cbc63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 21 Jul 2019 00:57:52 +0200 Subject: [PATCH] GL: make it possible to read pixel data into mutable image views. UGH I hate cube maps and all the driver bugs that make them so freaking annoying to implement. --- doc/changelog.dox | 6 + src/Magnum/GL/AbstractFramebuffer.cpp | 29 +- src/Magnum/GL/AbstractFramebuffer.h | 20 +- src/Magnum/GL/AbstractTexture.cpp | 132 ++++- src/Magnum/GL/AbstractTexture.h | 4 + src/Magnum/GL/CMakeLists.txt | 6 +- src/Magnum/GL/CubeMapTexture.cpp | 133 +++++ src/Magnum/GL/CubeMapTexture.h | 61 ++ src/Magnum/GL/CubeMapTextureArray.h | 47 ++ src/Magnum/GL/RectangleTexture.h | 46 ++ src/Magnum/GL/Test/AbstractTextureGLTest.cpp | 258 +++++++++ src/Magnum/GL/Test/CMakeLists.txt | 8 +- .../GL/Test/CubeMapTextureArrayGLTest.cpp | 147 ++++- src/Magnum/GL/Test/CubeMapTextureGLTest.cpp | 542 ++++++++++++++++++ src/Magnum/GL/Test/FramebufferGLTest.cpp | 104 ++++ src/Magnum/GL/Test/RectangleTextureGLTest.cpp | 70 +++ src/Magnum/GL/Test/TextureArrayGLTest.cpp | 211 +++++++ src/Magnum/GL/Test/TextureGLTest.cpp | 338 +++++++++++ src/Magnum/GL/Texture.h | 70 ++- src/Magnum/GL/TextureArray.h | 47 ++ 20 files changed, 2241 insertions(+), 38 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 7323b0e58..c669b3154 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -131,6 +131,12 @@ See also: - Added an ability to use Magnum with multiple OpenGL contexts using @ref GL::Context::makeCurrent(), see @ref GL-Context-multiple for more information +- @ref GL::AbstractFramebuffer::read(), @ref GL::Texture::image(), + @ref GL::Texture::subImage(), @ref GL::Texture::compressedImage(), + @ref GL::Texture::setCompressedSubImage() and equivalent APIs in other + texture classes now can read the pixels into + @ref BasicMutableImageView "MutableImageView*D" and + @ref BasicMutableCompressedImageView "MutableCompressedImageView*D" as well @subsubsection changelog-latest-new-math Math library diff --git a/src/Magnum/GL/AbstractFramebuffer.cpp b/src/Magnum/GL/AbstractFramebuffer.cpp index c8d5e4bff..13947f26e 100644 --- a/src/Magnum/GL/AbstractFramebuffer.cpp +++ b/src/Magnum/GL/AbstractFramebuffer.cpp @@ -26,6 +26,7 @@ #include "AbstractFramebuffer.h" #include "Magnum/Image.h" +#include "Magnum/ImageView.h" #ifndef MAGNUM_TARGET_GLES2 #include "Magnum/GL/BufferImage.h" #endif @@ -334,25 +335,35 @@ AbstractFramebuffer& AbstractFramebuffer::clearDepthStencil(const Float depth, c } #endif -void AbstractFramebuffer::read(const Range2Di& rectangle, Image2D& image) { - bindInternal(FramebufferTarget::Read); - - /* Reallocate only if needed */ - const std::size_t dataSize = Magnum::Implementation::imageDataSizeFor(image, rectangle.size()); - Containers::Array data{image.release()}; - if(data.size() < dataSize) - data = Containers::Array{dataSize}; +void AbstractFramebuffer::read(const Range2Di& rectangle, MutableImageView2D& image) { + CORRADE_ASSERT(image.data().data() != nullptr, + "GL::AbstractFramebuffer::read(): image view is nullptr", ); + CORRADE_ASSERT(image.size() == rectangle.size(), + "GL::AbstractFramebuffer::read(): expected image view size" << rectangle.size() << "but got" << image.size(), ); + bindInternal(FramebufferTarget::Read); #ifndef MAGNUM_TARGET_GLES2 Buffer::unbindInternal(Buffer::TargetHint::PixelPack); #endif Context::current().state().renderer->applyPixelStoragePack(image.storage()); - (Context::current().state().framebuffer->readImplementation)(rectangle, pixelFormat(image.format()), pixelType(image.format(), image.formatExtra()), data.size(), data + (Context::current().state().framebuffer->readImplementation)(rectangle, pixelFormat(image.format()), pixelType(image.format(), image.formatExtra()), image.data().size(), image.data() #ifdef MAGNUM_TARGET_GLES2 + Magnum::Implementation::pixelStorageSkipOffsetFor(image, rectangle.size()) #endif ); +} + +void AbstractFramebuffer::read(const Range2Di& rectangle, Image2D& image) { + /* Reallocate only if needed */ + const std::size_t dataSize = Magnum::Implementation::imageDataSizeFor(image, rectangle.size()); + Containers::Array data{image.release()}; + if(data.size() < dataSize) + data = Containers::Array{dataSize}; + + /* Replace the storage, proxy to the function taking a view */ image = Image2D{image.storage(), image.format(), image.formatExtra(), image.pixelSize(), rectangle.size(), std::move(data)}; + MutableImageView2D view(image); + read(rectangle, view); } Image2D AbstractFramebuffer::read(const Range2Di& rectangle, Image2D&& image) { diff --git a/src/Magnum/GL/AbstractFramebuffer.h b/src/Magnum/GL/AbstractFramebuffer.h index 35de2a3ee..08bce42bc 100644 --- a/src/Magnum/GL/AbstractFramebuffer.h +++ b/src/Magnum/GL/AbstractFramebuffer.h @@ -380,11 +380,13 @@ class MAGNUM_GL_EXPORT AbstractFramebuffer { * * Image parameters like format and type of pixel data are taken from * given image. The storage is not reallocated if it is large enough to - * contain the new data. On OpenGL ES 2.0 and WebGL 1.0, if - * @ref PixelStorage::skip() is set, the functionality is emulated by - * adjusting the data pointer. + * contain the new data --- however if you want to read into existing + * memory or *ensure* a reallocation does not happen, use + * @ref read(const Range2Di&, MutableImageView2D&) instead. * - * If @gl_extension{ARB,robustness} is available, the operation is + * On OpenGL ES 2.0 and WebGL 1.0, if @ref PixelStorage::skip() is set, + * the functionality is emulated by adjusting the data pointer. If + * @gl_extension{ARB,robustness} is available, the operation is * protected from buffer overflow. * @see @fn_gl{BindFramebuffer}, then @fn_gl{PixelStore} and * @fn_gl_keyword{ReadPixels} or @@ -405,6 +407,16 @@ class MAGNUM_GL_EXPORT AbstractFramebuffer { */ Image2D read(const Range2Di& rectangle, Image2D&& image); + /** + * @brief Read block of pixels from framebuffer to image + * + * Compared to @ref read(const Range2Di&, Image2D&) the function + * reads the pixels into the memory provided by @p image, expecting + * it's not @cpp nullptr @ce and its size is the same as @p rectangle + * size. + */ + void read(const Range2Di& rectangle, MutableImageView2D& image); + #ifndef MAGNUM_TARGET_GLES2 /** * @brief Read block of pixels from framebuffer to buffer image diff --git a/src/Magnum/GL/AbstractTexture.cpp b/src/Magnum/GL/AbstractTexture.cpp index 3afe7e4e3..3c5047354 100644 --- a/src/Magnum/GL/AbstractTexture.cpp +++ b/src/Magnum/GL/AbstractTexture.cpp @@ -1602,6 +1602,24 @@ template void MAGNUM_GL_EXPORT AbstractTexture::image<1>(GLint, Image<1>&); template void MAGNUM_GL_EXPORT AbstractTexture::image<2>(GLint, Image<2>&); template void MAGNUM_GL_EXPORT AbstractTexture::image<3>(GLint, Image<3>&); +template void AbstractTexture::image(GLint level, BasicMutableImageView& image) { + #ifndef CORRADE_NO_ASSERT + const Math::Vector size = DataHelper::imageSize(*this, level); + CORRADE_ASSERT(image.data().data() != nullptr, + "GL::AbstractTexture::image(): image view is nullptr", ); + CORRADE_ASSERT(image.size() == size, + "GL::AbstractTexture::image(): expected image view size" << size << "but got" << image.size(), ); + #endif + + Buffer::unbindInternal(Buffer::TargetHint::PixelPack); + Context::current().state().renderer->applyPixelStoragePack(image.storage()); + (this->*Context::current().state().texture->getImageImplementation)(level, pixelFormat(image.format()), pixelType(image.format(), image.formatExtra()), image.data().size(), image.data()); +} + +template void MAGNUM_GL_EXPORT AbstractTexture::image<1>(GLint, BasicMutableImageView<1>&); +template void MAGNUM_GL_EXPORT AbstractTexture::image<2>(GLint, BasicMutableImageView<2>&); +template void MAGNUM_GL_EXPORT AbstractTexture::image<3>(GLint, BasicMutableImageView<3>&); + template void AbstractTexture::image(GLint level, BufferImage& image, BufferUsage usage) { const Math::Vector size = DataHelper::imageSize(*this, level); const std::size_t dataSize = Magnum::Implementation::imageDataSizeFor(image, size); @@ -1652,6 +1670,45 @@ template void MAGNUM_GL_EXPORT AbstractTexture::compressedImage<1>(GLint, Compre template void MAGNUM_GL_EXPORT AbstractTexture::compressedImage<2>(GLint, CompressedImage<2>&); template void MAGNUM_GL_EXPORT AbstractTexture::compressedImage<3>(GLint, CompressedImage<3>&); +template void AbstractTexture::compressedImage(const GLint level, BasicMutableCompressedImageView& image) { + #ifndef CORRADE_NO_ASSERT + CORRADE_ASSERT(image.data().data() != nullptr, + "GL::AbstractTexture::compressedImage(): image view is nullptr", ); + + const Math::Vector size = DataHelper::imageSize(*this, level); + + CORRADE_ASSERT(image.size() == size, + "GL::AbstractTexture::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::size_t dataSize; + if(!image.storage().compressedBlockSize().product() || !image.storage().compressedBlockDataSize()) { + GLint textureDataSize; + (this->*Context::current().state().texture->getLevelParameterivImplementation)(level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &textureDataSize); + dataSize = textureDataSize; + } else dataSize = Magnum::Implementation::compressedImageDataSizeFor(image, size); + + CORRADE_ASSERT(image.data().size() == dataSize, + "GL::AbstractTexture::compressedImage(): expected image view data size" << dataSize << "bytes but got" << image.data().size(), ); + + /* Internal texture format */ + GLint format; + (this->*Context::current().state().texture->getLevelParameterivImplementation)(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()); + (this->*Context::current().state().texture->getCompressedImageImplementation)(level, image.data().size(), image.data()); +} + +template void MAGNUM_GL_EXPORT AbstractTexture::compressedImage<1>(GLint, BasicMutableCompressedImageView<1>&); +template void MAGNUM_GL_EXPORT AbstractTexture::compressedImage<2>(GLint, BasicMutableCompressedImageView<2>&); +template void MAGNUM_GL_EXPORT AbstractTexture::compressedImage<3>(GLint, BasicMutableCompressedImageView<3>&); + template void AbstractTexture::compressedImage(const GLint level, CompressedBufferImage& image, BufferUsage usage) { const Math::Vector size = DataHelper::imageSize(*this, level); @@ -1684,28 +1741,43 @@ template void MAGNUM_GL_EXPORT AbstractTexture::compressedImage<2>(GLint, Compre template void MAGNUM_GL_EXPORT AbstractTexture::compressedImage<3>(GLint, CompressedBufferImage<3>&, BufferUsage); template void AbstractTexture::subImage(const GLint level, const RangeTypeFor& range, Image& image) { - createIfNotAlready(); - + /* Reallocate only if needed */ const Math::Vector size = range.size(); const std::size_t dataSize = Magnum::Implementation::imageDataSizeFor(image, size); - const Vector3i paddedOffset = Vector3i::pad(range.min()); - const Vector3i paddedSize = Vector3i::pad(size, 1); - - /* Reallocate only if needed */ Containers::Array data{image.release()}; if(data.size() < dataSize) data = Containers::Array{dataSize}; - Buffer::unbindInternal(Buffer::TargetHint::PixelPack); - Context::current().state().renderer->applyPixelStoragePack(image.storage()); - glGetTextureSubImage(_id, level, paddedOffset.x(), paddedOffset.y(), paddedOffset.z(), paddedSize.x(), paddedSize.y(), paddedSize.z(), GLenum(pixelFormat(image.format())), GLenum(pixelType(image.format(), image.formatExtra())), data.size(), data); image = Image{image.storage(), image.format(), image.formatExtra(), image.pixelSize(), size, std::move(data)}; + BasicMutableImageView view(image); + subImage(level, range, view); } template void MAGNUM_GL_EXPORT AbstractTexture::subImage<1>(GLint, const Range1Di&, Image<1>&); template void MAGNUM_GL_EXPORT AbstractTexture::subImage<2>(GLint, const Range2Di&, Image<2>&); template void MAGNUM_GL_EXPORT AbstractTexture::subImage<3>(GLint, const Range3Di&, Image<3>&); +template void AbstractTexture::subImage(const GLint level, const RangeTypeFor& range, BasicMutableImageView& image) { + CORRADE_ASSERT(image.data().data() != nullptr, + "GL::AbstractTexture::subImage(): image view is nullptr", ); + CORRADE_ASSERT(image.size() == range.size(), + "GL::AbstractTexture::subImage(): expected image view size" << range.size() << "but got" << image.size(), ); + + createIfNotAlready(); + + const Math::Vector size = range.size(); + const Vector3i paddedOffset = Vector3i::pad(range.min()); + const Vector3i paddedSize = Vector3i::pad(size, 1); + + Buffer::unbindInternal(Buffer::TargetHint::PixelPack); + Context::current().state().renderer->applyPixelStoragePack(image.storage()); + glGetTextureSubImage(_id, level, paddedOffset.x(), paddedOffset.y(), paddedOffset.z(), paddedSize.x(), paddedSize.y(), paddedSize.z(), GLenum(pixelFormat(image.format())), GLenum(pixelType(image.format(), image.formatExtra())), image.data().size(), image.data()); +} + +template void MAGNUM_GL_EXPORT AbstractTexture::subImage<1>(GLint, const Range1Di&, BasicMutableImageView<1>&); +template void MAGNUM_GL_EXPORT AbstractTexture::subImage<2>(GLint, const Range2Di&, BasicMutableImageView<2>&); +template void MAGNUM_GL_EXPORT AbstractTexture::subImage<3>(GLint, const Range3Di&, BasicMutableImageView<3>&); + template void AbstractTexture::subImage(const GLint level, const RangeTypeFor& range, BufferImage& image, const BufferUsage usage) { createIfNotAlready(); @@ -1775,6 +1847,48 @@ template void MAGNUM_GL_EXPORT AbstractTexture::compressedSubImage<1>(GLint, con template void MAGNUM_GL_EXPORT AbstractTexture::compressedSubImage<2>(GLint, const Range2Di&, CompressedImage<2>&); template void MAGNUM_GL_EXPORT AbstractTexture::compressedSubImage<3>(GLint, const Range3Di&, CompressedImage<3>&); +template void AbstractTexture::compressedSubImage(const GLint level, const RangeTypeFor& range, BasicMutableCompressedImageView& image) { + #ifndef CORRADE_NO_ASSERT + CORRADE_ASSERT(image.data().data() != nullptr, + "GL::AbstractTexture::compressedSubImage(): image view is nullptr", ); + CORRADE_ASSERT(image.size() == range.size(), + "GL::AbstractTexture::compressedSubImage(): expected image view size" << range.size() << "but got" << image.size(), ); + + createIfNotAlready(); + + const Math::Vector size = range.size(); + + /* Internal texture format */ + GLint format; + (this->*Context::current().state().texture->getLevelParameterivImplementation)(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()), ); + + /* 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); + + CORRADE_ASSERT(image.data().size() == dataSize, + "GL::AbstractTexture::compressedSubImage(): expected image view data size" << dataSize << "bytes but got" << image.data().size(), ); + #endif + + const Vector3i paddedOffset = Vector3i::pad(range.min()); + const Vector3i paddedSize = Vector3i::pad(size, 1); + + Buffer::unbindInternal(Buffer::TargetHint::PixelPack); + Context::current().state().renderer->applyPixelStoragePack(image.storage()); + glGetCompressedTextureSubImage(_id, level, paddedOffset.x(), paddedOffset.y(), paddedOffset.z(), paddedSize.x(), paddedSize.y(), paddedSize.z(), image.data().size(), image.data()); +} + +template void MAGNUM_GL_EXPORT AbstractTexture::compressedSubImage<1>(GLint, const Range1Di&, BasicMutableCompressedImageView<1>&); +template void MAGNUM_GL_EXPORT AbstractTexture::compressedSubImage<2>(GLint, const Range2Di&, BasicMutableCompressedImageView<2>&); +template void MAGNUM_GL_EXPORT AbstractTexture::compressedSubImage<3>(GLint, const Range3Di&, BasicMutableCompressedImageView<3>&); + template void AbstractTexture::compressedSubImage(const GLint level, const RangeTypeFor& range, CompressedBufferImage& image, const BufferUsage usage) { createIfNotAlready(); diff --git a/src/Magnum/GL/AbstractTexture.h b/src/Magnum/GL/AbstractTexture.h index f72b09b39..6ef2c4488 100644 --- a/src/Magnum/GL/AbstractTexture.h +++ b/src/Magnum/GL/AbstractTexture.h @@ -489,12 +489,16 @@ class MAGNUM_GL_EXPORT AbstractTexture: public AbstractObject { void generateMipmap(); #ifndef MAGNUM_TARGET_GLES + template void image(GLint level, BasicMutableImageView& image); template void image(GLint level, Image& image); template void image(GLint level, BufferImage& image, BufferUsage usage); + template void compressedImage(GLint level, BasicMutableCompressedImageView& image); template void compressedImage(GLint level, CompressedImage& image); template void compressedImage(GLint level, CompressedBufferImage& image, BufferUsage usage); + template void subImage(GLint level, const RangeTypeFor& range, BasicMutableImageView& image); template void subImage(GLint level, const RangeTypeFor& range, Image& image); template void subImage(GLint level, const RangeTypeFor& range, BufferImage& image, BufferUsage usage); + template void compressedSubImage(GLint level, const RangeTypeFor& range, BasicMutableCompressedImageView& image); template void compressedSubImage(GLint level, const RangeTypeFor& range, CompressedImage& image); template void compressedSubImage(GLint level, const RangeTypeFor& range, CompressedBufferImage& image, BufferUsage usage); #endif diff --git a/src/Magnum/GL/CMakeLists.txt b/src/Magnum/GL/CMakeLists.txt index 2bbee9ff9..35c287de3 100644 --- a/src/Magnum/GL/CMakeLists.txt +++ b/src/Magnum/GL/CMakeLists.txt @@ -24,13 +24,10 @@ # set(MagnumGL_SRCS - AbstractFramebuffer.cpp AbstractObject.cpp - AbstractTexture.cpp AbstractShaderProgram.cpp Attribute.cpp Buffer.cpp - CubeMapTexture.cpp Context.cpp DefaultFramebuffer.cpp Framebuffer.cpp @@ -54,6 +51,9 @@ set(MagnumGL_SRCS Implementation/maxTextureSize.cpp) set(MagnumGL_GracefulAssert_SRCS + AbstractFramebuffer.cpp + AbstractTexture.cpp + CubeMapTexture.cpp Mesh.cpp MeshView.cpp PixelFormat.cpp diff --git a/src/Magnum/GL/CubeMapTexture.cpp b/src/Magnum/GL/CubeMapTexture.cpp index 7f7cb670b..430047c02 100644 --- a/src/Magnum/GL/CubeMapTexture.cpp +++ b/src/Magnum/GL/CubeMapTexture.cpp @@ -85,6 +85,20 @@ Image3D CubeMapTexture::image(const Int level, Image3D&& image) { return std::move(image); } +void CubeMapTexture::image(const Int level, MutableImageView3D& image) { + #ifndef CORRADE_NO_ASSERT + const Vector3i size{imageSize(level), 6}; + CORRADE_ASSERT(image.data().data() != nullptr, + "GL::CubeMapTexture::image(): image view is nullptr", ); + CORRADE_ASSERT(image.size() == size, + "GL::CubeMapTexture::image(): expected image view size" << size << "but got" << image.size(), ); + #endif + + Buffer::unbindInternal(Buffer::TargetHint::PixelPack); + Context::current().state().renderer->applyPixelStoragePack(image.storage()); + glGetTextureImage(_id, level, GLenum(pixelFormat(image.format())), GLenum(pixelType(image.format(), image.formatExtra())), image.data().size(), image.data()); +} + void CubeMapTexture::image(const Int level, BufferImage3D& image, const BufferUsage usage) { createIfNotAlready(); @@ -140,6 +154,40 @@ CompressedImage3D CubeMapTexture::compressedImage(const Int level, CompressedIma return std::move(image); } +void CubeMapTexture::compressedImage(const Int level, MutableCompressedImageView3D& image) { + #ifndef CORRADE_NO_ASSERT + CORRADE_ASSERT(image.data().data() != nullptr, + "GL::CubeMapTexture::compressedImage(): image view is nullptr", ); + + const Vector3i size{imageSize(level), 6}; + + 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()) { + dataOffsetSize.first = 0; + dataOffsetSize.second = (this->*Context::current().state().texture->getCubeLevelCompressedImageSizeImplementation)(level)*6; + } else dataOffsetSize = Magnum::Implementation::compressedImageDataOffsetSizeFor(image, size); + + CORRADE_ASSERT(image.data().size() == dataOffsetSize.first + dataOffsetSize.second, + "GL::CubeMapTexture::compressedImage(): expected image view data size" << dataOffsetSize.first + dataOffsetSize.second << "bytes but got" << image.data().size(), ); + + /* Internal texture format */ + GLint format; + (this->*Context::current().state().texture->getCubeLevelParameterivImplementation)(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()); + (this->*Context::current().state().texture->getFullCompressedCubeImageImplementation)(level, size.xy(), dataOffsetSize.first, dataOffsetSize.second, image.data()); +} + void CubeMapTexture::compressedImage(const Int level, CompressedBufferImage3D& image, const BufferUsage usage) { createIfNotAlready(); @@ -193,6 +241,20 @@ Image2D CubeMapTexture::image(const CubeMapCoordinate coordinate, const Int leve return std::move(image); } +void CubeMapTexture::image(const CubeMapCoordinate coordinate, const Int level, MutableImageView2D& image) { + #ifndef CORRADE_NO_ASSERT + const Vector2i size = imageSize(level); + CORRADE_ASSERT(image.data().data() != nullptr, + "GL::CubeMapTexture::image(): image view is nullptr", ); + CORRADE_ASSERT(image.size() == size, + "GL::CubeMapTexture::image(): expected image view size" << size << "but got" << image.size(), ); + #endif + + Buffer::unbindInternal(Buffer::TargetHint::PixelPack); + Context::current().state().renderer->applyPixelStoragePack(image.storage()); + (this->*Context::current().state().texture->getCubeImageImplementation)(coordinate, level, size, pixelFormat(image.format()), pixelType(image.format(), image.formatExtra()), image.data().size(), image.data()); +} + void CubeMapTexture::image(const CubeMapCoordinate coordinate, const Int level, BufferImage2D& image, const BufferUsage usage) { const Vector2i size = imageSize(level); const std::size_t dataSize = Magnum::Implementation::imageDataSizeFor(image, size); @@ -246,6 +308,42 @@ CompressedImage2D CubeMapTexture::compressedImage(const CubeMapCoordinate coordi return std::move(image); } +void CubeMapTexture::compressedImage(const CubeMapCoordinate coordinate, const Int level, MutableCompressedImageView2D& image) { + #ifndef CORRADE_NO_ASSERT + CORRADE_ASSERT(image.data().data() != nullptr, + "GL::CubeMapTexture::compressedImage(): image view is nullptr", ); + + const Vector2i size = imageSize(level); + + 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::size_t dataSize; + if(!image.storage().compressedBlockSize().product() || !image.storage().compressedBlockDataSize()) + dataSize = (this->*Context::current().state().texture->getCubeLevelCompressedImageSizeImplementation)(level); + else + dataSize = Magnum::Implementation::compressedImageDataSizeFor(image, size); + + CORRADE_ASSERT(image.data().size() == dataSize, + "GL::CubeMapTexture::compressedImage(): expected image view data size" << dataSize << "bytes but got" << image.data().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). */ + GLint format{}; + (this->*Context::current().state().texture->getCubeLevelParameterivImplementation)(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()); + (this->*Context::current().state().texture->getCompressedCubeImageImplementation)(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); @@ -322,6 +420,41 @@ CompressedImage3D CubeMapTexture::compressedSubImage(const Int level, const Rang return std::move(image); } +void CubeMapTexture::compressedSubImage(const Int level, const Range3Di& range, MutableCompressedImageView3D& image) { + #ifndef CORRADE_NO_ASSERT + CORRADE_ASSERT(image.data().data() != nullptr, + "GL::CubeMapTexture::compressedSubImage(): image view is nullptr", ); + CORRADE_ASSERT(image.size() == range.size(), + "GL::CubeMapTexture::compressedSubImage(): expected image view size" << range.size() << "but got" << image.size(), ); + + 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). */ + GLint format{}; + (this->*Context::current().state().texture->getCubeLevelParameterivImplementation)(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()), ); + + /* 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()); + + CORRADE_ASSERT(image.data().size() == dataSize, + "GL::CubeMapTexture::compressedSubImage(): expected image view data size" << dataSize << "bytes but got" << image.data().size(), ); + #endif + + Buffer::unbindInternal(Buffer::TargetHint::PixelPack); + Context::current().state().renderer->applyPixelStoragePack(image.storage()); + 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()); +} + void CubeMapTexture::compressedSubImage(const Int level, const Range3Di& range, CompressedBufferImage3D& image, const BufferUsage usage) { createIfNotAlready(); diff --git a/src/Magnum/GL/CubeMapTexture.h b/src/Magnum/GL/CubeMapTexture.h index 0a643402e..96690e016 100644 --- a/src/Magnum/GL/CubeMapTexture.h +++ b/src/Magnum/GL/CubeMapTexture.h @@ -565,6 +565,15 @@ class MAGNUM_GL_EXPORT CubeMapTexture: public AbstractTexture { */ Image3D image(Int level, Image3D&& image); + /** + * @brief Read given texture mip level to an image view + * + * Compared to @ref image(Int, Image3D&) the function reads the pixels + * into the memory provided by @p image, expecting it's not + * @cpp nullptr @ce and its size is the same as size of given @p level. + */ + void image(Int level, MutableImageView3D& image); + /** * @brief Read given texture mip level to a buffer image * @@ -614,6 +623,16 @@ class MAGNUM_GL_EXPORT CubeMapTexture: public AbstractTexture { */ CompressedImage3D compressedImage(Int level, CompressedImage3D&& image); + /** + * @brief Read given compressed texture mip level to an image view + * + * Compared to @ref compressedImage(Int, CompressedImage3D&) the + * function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce, its format is the same as + * texture format and its size is the same as size of given @p level. + */ + void compressedImage(Int level, MutableCompressedImageView3D& image); + /** * @brief Read given compressed texture mip level to a buffer image * @@ -671,6 +690,16 @@ class MAGNUM_GL_EXPORT CubeMapTexture: public AbstractTexture { */ Image2D image(CubeMapCoordinate coordinate, Int level, Image2D&& image); + /** + * @brief Read given texture mip level and coordinate to an image view + * + * Compared to @ref image(CubeMapCoordinate, Int, Image2D&) the + * function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce and its size is the same as size + * of given @p level. + */ + void image(CubeMapCoordinate coordinate, Int level, MutableImageView2D& image); + /** * @brief Read given texture mip level and coordinate to a buffer image * @@ -727,6 +756,16 @@ class MAGNUM_GL_EXPORT CubeMapTexture: public AbstractTexture { */ CompressedImage2D compressedImage(CubeMapCoordinate coordinate, Int level, CompressedImage2D&& image); + /** + * @brief Read given compressed texture mip level to an image view + * + * Compared to @ref compressedImage(CubeMapCoordinate, Int, CompressedImage2D&) + * the function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce, its format is the same as + * texture format and its size is the same as size of given @p level. + */ + void compressedImage(CubeMapCoordinate coordinate, Int level, MutableCompressedImageView2D& image); + /** * @brief Read given compressed texture mip level and coordinate to a buffer image * @@ -772,6 +811,18 @@ class MAGNUM_GL_EXPORT CubeMapTexture: public AbstractTexture { */ Image3D subImage(Int level, const Range3Di& range, Image3D&& image); + /** + * @brief Read a range of given texture mip level to an image view + * + * Compared to @ref subImage(Int, const Range3Di&, Image3D&) the + * function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce and its size is the same as + * @p range size. + */ + void subImage(Int level, const Range3Di& range, MutableImageView3D& image) { + AbstractTexture::subImage<3>(level, range, image); + } + /** * @brief Read a range of given texture mip level to a buffer image * @@ -820,6 +871,16 @@ class MAGNUM_GL_EXPORT CubeMapTexture: public AbstractTexture { */ CompressedImage3D compressedSubImage(Int level, const Range3Di& range, CompressedImage3D&& image); + /** + * @brief Read a range of given compressed texture mip level to an image view + * + * Compared to @ref compressedSubImage(Int, const Range3Di&, CompressedImage3D&) + * the function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce, its format is the same as + * texture format and its size is the same as @p range size. + */ + void compressedSubImage(Int level, const Range3Di& range, MutableCompressedImageView3D& image); + /** * @brief Read a range of given compressed texture mip level to a buffer image * diff --git a/src/Magnum/GL/CubeMapTextureArray.h b/src/Magnum/GL/CubeMapTextureArray.h index 33769e82f..747cdc330 100644 --- a/src/Magnum/GL/CubeMapTextureArray.h +++ b/src/Magnum/GL/CubeMapTextureArray.h @@ -485,6 +485,17 @@ class MAGNUM_GL_EXPORT CubeMapTextureArray: public AbstractTexture { */ Image3D image(Int level, Image3D&& image); + /** + * @brief Read given texture mip level to an image view + * + * Compared to @ref image(Int, Image3D&) the function reads the pixels + * into the memory provided by @p image, expecting it's not + * @cpp nullptr @ce and its size is the same as size of given @p level. + */ + void image(Int level, MutableImageView3D& image) { + AbstractTexture::image<3>(level, image); + } + /** * @brief Read given texture mip level to a buffer image * @@ -529,6 +540,18 @@ class MAGNUM_GL_EXPORT CubeMapTextureArray: public AbstractTexture { */ CompressedImage3D compressedImage(Int level, CompressedImage3D&& image); + /** + * @brief Read given compressed texture mip level to an image view + * + * Compared to @ref compressedImage(Int, CompressedImage3D&) the + * function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce, its format is the same as + * texture format and its size is the same as size of given @p level. + */ + void compressedImage(Int level, MutableCompressedImageView3D& image) { + AbstractTexture::compressedImage<3>(level, image); + } + /** * @brief Read given compressed texture mip level to a buffer image * @@ -574,6 +597,18 @@ class MAGNUM_GL_EXPORT CubeMapTextureArray: public AbstractTexture { */ Image3D subImage(Int level, const Range3Di& range, Image3D&& image); + /** + * @brief Read a range of given texture mip level to an image view + * + * Compared to @ref subImage(Int, const Range3Di&, Image3D&) the + * function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce and its size is the same as + * @p range size. + */ + void subImage(Int level, const Range3Di& range, MutableImageView3D& image) { + AbstractTexture::subImage<3>(level, range, image); + } + /** * @brief Read a range of given texture mip level to a buffer image * @@ -624,6 +659,18 @@ class MAGNUM_GL_EXPORT CubeMapTextureArray: public AbstractTexture { */ CompressedImage3D compressedSubImage(Int level, const Range3Di& range, CompressedImage3D&& image); + /** + * @brief Read a range of given compressed texture mip level to an image view + * + * Compared to @ref compressedSubImage(Int, const Range3Di&, CompressedImage3D&) + * the function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce, its format is the same as + * texture format and its size is the same as @p range size. + */ + void compressedSubImage(Int level, const Range3Di& range, MutableCompressedImageView3D& image) { + AbstractTexture::compressedSubImage<3>(level, range, image); + } + /** * @brief Read a range of given compressed texture mip level to a buffer image * diff --git a/src/Magnum/GL/RectangleTexture.h b/src/Magnum/GL/RectangleTexture.h index e8149a624..019b24988 100644 --- a/src/Magnum/GL/RectangleTexture.h +++ b/src/Magnum/GL/RectangleTexture.h @@ -369,6 +369,17 @@ class MAGNUM_GL_EXPORT RectangleTexture: public AbstractTexture { */ Image2D image(Image2D&& image); + /** + * @brief Read texture to an image view + * + * Compared to @ref image(Image2D&) the function reads the pixels into + * the memory provided by @p image, expecting it's not @cpp nullptr @ce + * and its size is the same as texture size. + */ + void image(MutableImageView2D& image) { + AbstractTexture::image<2>(0, image); + } + /** * @brief Read texture to a buffer image * @@ -407,6 +418,18 @@ class MAGNUM_GL_EXPORT RectangleTexture: public AbstractTexture { */ CompressedImage2D compressedImage(CompressedImage2D&& image); + /** + * @brief Read compressed texture to an image view + * + * Compared to @ref compressedImage(CompressedImage2D&) the function + * reads the pixels into the memory provided by @p image, expecting + * it's not @cpp nullptr @ce, its format is the same as texture format + * and its size is the same as texture size. + */ + void compressedImage(MutableCompressedImageView2D& image) { + AbstractTexture::compressedImage<2>(0, image); + } + /** * @brief Read compressed texture to a buffer image * @@ -446,6 +469,17 @@ class MAGNUM_GL_EXPORT RectangleTexture: public AbstractTexture { */ Image2D subImage(const Range2Di& range, Image2D&& image); + /** + * @brief Read a range of given texture mip level to an image view + * + * Compared to @ref subImage(const Range2Di&, Image2D&) the function + * reads the pixels into the memory provided by @p image, expecting + * it's not @cpp nullptr @ce and its size is the same as @p range size. + */ + void subImage(const Range2Di& range, MutableImageView2D& image) { + AbstractTexture::subImage<2>(0, range, image); + } + /** * @brief Read a texture range to a buffer image * @@ -490,6 +524,18 @@ class MAGNUM_GL_EXPORT RectangleTexture: public AbstractTexture { */ CompressedImage2D compressedSubImage(const Range2Di& range, CompressedImage2D&& image); + /** + * @brief Read a compressed texture range to an image view + * + * Compared to @ref compressedSubImage(const Range2Di&, CompressedImage2D&) + * the function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce, its format is the same as + * texture format and its size is the same as @p range size. + */ + void compressedSubImage(const Range2Di& range, MutableCompressedImageView2D& image) { + AbstractTexture::compressedSubImage<2>(0, range, image); + } + /** * @brief Read a compressed texture range to a buffer image * diff --git a/src/Magnum/GL/Test/AbstractTextureGLTest.cpp b/src/Magnum/GL/Test/AbstractTextureGLTest.cpp index 4693316f1..4944ca7c4 100644 --- a/src/Magnum/GL/Test/AbstractTextureGLTest.cpp +++ b/src/Magnum/GL/Test/AbstractTextureGLTest.cpp @@ -23,12 +23,17 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include "Magnum/ImageView.h" #include "Magnum/GL/Context.h" #include "Magnum/GL/Extensions.h" +#include "Magnum/GL/PixelFormat.h" #include "Magnum/GL/Texture.h" +#include "Magnum/GL/TextureFormat.h" #include "Magnum/GL/OpenGLTester.h" +#include "Magnum/Math/Range.h" namespace Magnum { namespace GL { namespace Test { namespace { @@ -38,6 +43,22 @@ struct AbstractTextureGLTest: OpenGLTester { void construct(); void constructMove(); + #ifndef MAGNUM_TARGET_GLES + void imageQueryViewNullptr(); + void imageQueryViewBadSize(); + void subImageQueryViewNullptr(); + void subImageQueryViewBadSize(); + + void compressedImageQueryViewNullptr(); + void compressedImageQueryViewBadSize(); + void compressedImageQueryViewBadDataSize(); + void compressedImageQueryViewBadFormat(); + void compressedSubImageQueryViewNullptr(); + void compressedSubImageQueryViewBadSize(); + void compressedSubImageQueryViewBadDataSize(); + void compressedSubImageQueryViewBadFormat(); + #endif + #ifndef MAGNUM_TARGET_WEBGL void label(); #endif @@ -47,6 +68,22 @@ AbstractTextureGLTest::AbstractTextureGLTest() { addTests({&AbstractTextureGLTest::construct, &AbstractTextureGLTest::constructMove, + #ifndef MAGNUM_TARGET_GLES + &AbstractTextureGLTest::imageQueryViewNullptr, + &AbstractTextureGLTest::imageQueryViewBadSize, + &AbstractTextureGLTest::subImageQueryViewNullptr, + &AbstractTextureGLTest::subImageQueryViewBadSize, + + &AbstractTextureGLTest::compressedImageQueryViewNullptr, + &AbstractTextureGLTest::compressedImageQueryViewBadSize, + &AbstractTextureGLTest::compressedImageQueryViewBadDataSize, + &AbstractTextureGLTest::compressedImageQueryViewBadFormat, + &AbstractTextureGLTest::compressedSubImageQueryViewNullptr, + &AbstractTextureGLTest::compressedSubImageQueryViewBadSize, + &AbstractTextureGLTest::compressedSubImageQueryViewBadDataSize, + &AbstractTextureGLTest::compressedSubImageQueryViewBadFormat, + #endif + #ifndef MAGNUM_TARGET_WEBGL &AbstractTextureGLTest::label #endif @@ -86,6 +123,227 @@ void AbstractTextureGLTest::constructMove() { CORRADE_COMPARE(c.id(), id); } +#ifndef MAGNUM_TARGET_GLES +void AbstractTextureGLTest::imageQueryViewNullptr() { + Texture2D texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{2}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + MutableImageView2D image{PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i{2}, {nullptr, 2*2*4}}; + + std::ostringstream out; + Error redirectError{&out}; + texture.image(0, image); + CORRADE_COMPARE(out.str(), "GL::AbstractTexture::image(): image view is nullptr\n"); +} + +void AbstractTextureGLTest::imageQueryViewBadSize() { + Texture2D texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{2}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[2*4]; + MutableImageView2D image{PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i{2, 1}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.image(0, image); + CORRADE_COMPARE(out.str(), "GL::AbstractTexture::image(): expected image view size Vector(2, 2) but got Vector(2, 1)\n"); +} + +void AbstractTextureGLTest::subImageQueryViewNullptr() { + Texture2D texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{2}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + MutableImageView2D image{PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i{2}, {nullptr, 2*2*4}}; + + std::ostringstream out; + Error redirectError{&out}; + texture.subImage(0, {{}, Vector2i{2}}, image); + CORRADE_COMPARE(out.str(), "GL::AbstractTexture::subImage(): image view is nullptr\n"); +} + +void AbstractTextureGLTest::subImageQueryViewBadSize() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + + Texture2D texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{2}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[2*4]; + MutableImageView2D image{PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i{2, 1}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.subImage(0, {{}, Vector2i{2}}, image); + CORRADE_COMPARE(out.str(), "GL::AbstractTexture::subImage(): expected image view size Vector(2, 2) but got Vector(2, 1)\n"); +} + +void AbstractTextureGLTest::compressedImageQueryViewNullptr() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + Texture2D texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + MutableCompressedImageView2D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector2i{4}, {nullptr, 16}}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedImage(0, image); + CORRADE_COMPARE(out.str(), "GL::AbstractTexture::compressedImage(): image view is nullptr\n"); +} + +void AbstractTextureGLTest::compressedImageQueryViewBadSize() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + Texture2D texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[2*16]; + MutableCompressedImageView2D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector2i{4, 8}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedImage(0, image); + CORRADE_COMPARE(out.str(), "GL::AbstractTexture::compressedImage(): expected image view size Vector(4, 4) but got Vector(4, 8)\n"); +} + +void AbstractTextureGLTest::compressedImageQueryViewBadDataSize() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + Texture2D texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[16 - 1]; + MutableCompressedImageView2D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector2i{4}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedImage(0, image); + CORRADE_COMPARE(out.str(), "GL::AbstractTexture::compressedImage(): expected image view data size 16 bytes but got 15\n"); +} + +void AbstractTextureGLTest::compressedImageQueryViewBadFormat() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + Texture2D texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[16]; + MutableCompressedImageView2D image{CompressedPixelFormat::RGBAS3tcDxt1, Vector2i{4}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedImage(0, image); + CORRADE_COMPARE(out.str(), "GL::AbstractTexture::compressedImage(): expected image view format GL::CompressedPixelFormat::RGBAS3tcDxt3 but got GL::CompressedPixelFormat::RGBAS3tcDxt1\n"); +} + +void AbstractTextureGLTest::compressedSubImageQueryViewNullptr() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() + std::string(" is not supported.")); + + Texture2D texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + MutableCompressedImageView2D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector2i{4}, {nullptr, 16}}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedSubImage(0, {{}, Vector2i{4}}, image); + CORRADE_COMPARE(out.str(), "GL::AbstractTexture::compressedSubImage(): image view is nullptr\n"); +} + +void AbstractTextureGLTest::compressedSubImageQueryViewBadSize() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() + std::string(" is not supported.")); + + Texture2D texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[2*16]; + MutableCompressedImageView2D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector2i{4, 8}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedSubImage(0, {{}, Vector2i{4}}, image); + CORRADE_COMPARE(out.str(), "GL::AbstractTexture::compressedSubImage(): expected image view size Vector(4, 4) but got Vector(4, 8)\n"); +} + +void AbstractTextureGLTest::compressedSubImageQueryViewBadDataSize() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() + std::string(" is not supported.")); + + Texture2D texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[16 - 1]; + MutableCompressedImageView2D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector2i{4}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedSubImage(0, {{}, Vector2i{4}}, image); + CORRADE_COMPARE(out.str(), "GL::AbstractTexture::compressedSubImage(): expected image view data size 16 bytes but got 15\n"); +} + +void AbstractTextureGLTest::compressedSubImageQueryViewBadFormat() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() + std::string(" is not supported.")); + + Texture2D texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[16]; + MutableCompressedImageView2D image{CompressedPixelFormat::RGBAS3tcDxt1, Vector2i{4}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedSubImage(0, {{}, Vector2i{4}}, image); + CORRADE_COMPARE(out.str(), "GL::AbstractTexture::compressedSubImage(): expected image view format GL::CompressedPixelFormat::RGBAS3tcDxt3 but got GL::CompressedPixelFormat::RGBAS3tcDxt1\n"); +} +#endif + #ifndef MAGNUM_TARGET_WEBGL void AbstractTextureGLTest::label() { /* No-Op version is tested in AbstractObjectGLTest */ diff --git a/src/Magnum/GL/Test/CMakeLists.txt b/src/Magnum/GL/Test/CMakeLists.txt index 08cdff37c..c1d312819 100644 --- a/src/Magnum/GL/Test/CMakeLists.txt +++ b/src/Magnum/GL/Test/CMakeLists.txt @@ -104,14 +104,14 @@ if(NOT MAGNUM_TARGET_GLES) endif() if(BUILD_GL_TESTS) - corrade_add_test(GLAbstractTextureGLTest AbstractTextureGLTest.cpp LIBRARIES MagnumOpenGLTester) + corrade_add_test(GLAbstractTextureGLTest AbstractTextureGLTest.cpp LIBRARIES MagnumOpenGLTesterTestLib) corrade_add_test(GLBufferGLTest BufferGLTest.cpp LIBRARIES MagnumOpenGLTester) - corrade_add_test(GLCubeMapTextureGLTest CubeMapTextureGLTest.cpp LIBRARIES MagnumOpenGLTester) - corrade_add_test(GLFramebufferGLTest FramebufferGLTest.cpp LIBRARIES MagnumOpenGLTester) + corrade_add_test(GLCubeMapTextureGLTest CubeMapTextureGLTest.cpp LIBRARIES MagnumOpenGLTesterTestLib) + corrade_add_test(GLFramebufferGLTest FramebufferGLTest.cpp LIBRARIES MagnumOpenGLTesterTestLib) corrade_add_test(GLMeshGLTest MeshGLTest.cpp LIBRARIES MagnumOpenGLTesterTestLib) corrade_add_test(GLRendererGLTest RendererGLTest.cpp LIBRARIES MagnumOpenGLTester) corrade_add_test(GLRenderbufferGLTest RenderbufferGLTest.cpp LIBRARIES MagnumOpenGLTester) - corrade_add_test(GLTextureGLTest TextureGLTest.cpp LIBRARIES MagnumOpenGLTester) + corrade_add_test(GLTextureGLTest TextureGLTest.cpp LIBRARIES MagnumOpenGLTesterTestLib) corrade_add_resource(GLAbstractShaderProgramGLTest_RES AbstractShaderProgramGLTestFiles/resources.conf) corrade_add_test(GLAbstractShaderProgramGLTest diff --git a/src/Magnum/GL/Test/CubeMapTextureArrayGLTest.cpp b/src/Magnum/GL/Test/CubeMapTextureArrayGLTest.cpp index c99c22a66..46536324d 100644 --- a/src/Magnum/GL/Test/CubeMapTextureArrayGLTest.cpp +++ b/src/Magnum/GL/Test/CubeMapTextureArrayGLTest.cpp @@ -62,20 +62,30 @@ struct CubeMapTextureArrayGLTest: OpenGLTester { void image(); void imageBuffer(); + #ifndef MAGNUM_TARGET_GLES + void imageQueryView(); + #endif void subImage(); void subImageBuffer(); #ifndef MAGNUM_TARGET_GLES void subImageQuery(); + void subImageQueryView(); void subImageQueryBuffer(); + /* View query assertions tested in AbstractTextureGLTest */ #endif void compressedImage(); void compressedImageBuffer(); + #ifndef MAGNUM_TARGET_GLES + void compressedImageQueryView(); + #endif void compressedSubImage(); void compressedSubImageBuffer(); #ifndef MAGNUM_TARGET_GLES void compressedSubImageQuery(); + void compressedSubImageQueryView(); void compressedSubImageQueryBuffer(); + /* View query assertions tested in AbstractTextureGLTest */ #endif void generateMipmap(); @@ -274,28 +284,36 @@ CubeMapTextureArrayGLTest::CubeMapTextureArrayGLTest() { addInstancedTests({ &CubeMapTextureArrayGLTest::image, - &CubeMapTextureArrayGLTest::imageBuffer}, - Containers::arraySize(PixelStorageData)); + &CubeMapTextureArrayGLTest::imageBuffer, + #ifndef MAGNUM_TARGET_GLES + &CubeMapTextureArrayGLTest::imageQueryView + #endif + }, Containers::arraySize(PixelStorageData)); addInstancedTests({ &CubeMapTextureArrayGLTest::subImage, &CubeMapTextureArrayGLTest::subImageBuffer, #ifndef MAGNUM_TARGET_GLES &CubeMapTextureArrayGLTest::subImageQuery, + &CubeMapTextureArrayGLTest::subImageQueryView, &CubeMapTextureArrayGLTest::subImageQueryBuffer #endif }, Containers::arraySize(SubPixelStorageData)); addInstancedTests({ &CubeMapTextureArrayGLTest::compressedImage, - &CubeMapTextureArrayGLTest::compressedImageBuffer}, - Containers::arraySize(CompressedPixelStorageData)); + &CubeMapTextureArrayGLTest::compressedImageBuffer, + #ifndef MAGNUM_TARGET_GLES + &CubeMapTextureArrayGLTest::compressedImageQueryView + #endif + }, Containers::arraySize(CompressedPixelStorageData)); addInstancedTests({ &CubeMapTextureArrayGLTest::compressedSubImage, &CubeMapTextureArrayGLTest::compressedSubImageBuffer, #ifndef MAGNUM_TARGET_GLES &CubeMapTextureArrayGLTest::compressedSubImageQuery, + &CubeMapTextureArrayGLTest::compressedSubImageQueryView, &CubeMapTextureArrayGLTest::compressedSubImageQueryBuffer, #endif }, Containers::arraySize(CompressedSubPixelStorageData)); @@ -627,6 +645,34 @@ void CubeMapTextureArrayGLTest::imageBuffer() { #endif } +#ifndef MAGNUM_TARGET_GLES +void CubeMapTextureArrayGLTest::imageQueryView() { + setTestCaseDescription(PixelStorageData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::texture_cube_map_array::string() + std::string(" is not supported.")); + + CubeMapTextureArray texture; + texture.setImage(0, TextureFormat::RGBA8, ImageView3D{ + PixelStorageData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, {2, 2, 6}, + PixelStorageData[testCaseInstanceId()].dataSparse}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorageData[testCaseInstanceId()].offset + 2*2*6*4}; + MutableImageView3D image{PixelStorageData[testCaseInstanceId()].storage, PixelFormat::RGBA, PixelType::UnsignedByte, {2, 2, 6}, data}; + texture.image(0, image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector3i(2, 2, 6)); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorageData[testCaseInstanceId()].offset), + PixelStorageData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} +#endif + void CubeMapTextureArrayGLTest::compressedImage() { setTestCaseDescription(CompressedPixelStorageData[testCaseInstanceId()].name); @@ -710,6 +756,41 @@ void CubeMapTextureArrayGLTest::compressedImageBuffer() { #endif } +#ifndef MAGNUM_TARGET_GLES +void CubeMapTextureArrayGLTest::compressedImageQueryView() { + setTestCaseDescription(CompressedPixelStorageData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::texture_cube_map_array::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + if(CompressedPixelStorageData[testCaseInstanceId()].storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() + std::string(" is not supported.")); + + CubeMapTextureArray texture; + texture.setCompressedImage(0, CompressedImageView3D{ + #ifndef MAGNUM_TARGET_GLES + CompressedPixelStorageData[testCaseInstanceId()].storage, + #endif + CompressedPixelFormat::RGBAS3tcDxt3, {4, 4, 6}, + CompressedPixelStorageData[testCaseInstanceId()].dataSparse}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{CompressedPixelStorageData[testCaseInstanceId()].offset + 6*16}; + MutableCompressedImageView3D image{CompressedPixelStorageData[testCaseInstanceId()].storage, CompressedPixelFormat::RGBAS3tcDxt3, {4, 4, 6}, data}; + texture.compressedImage(0, image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), (Vector3i{4, 4, 6})); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(CompressedPixelStorageData[testCaseInstanceId()].offset), + CompressedPixelStorageData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} +#endif + constexpr UnsignedByte Zero[4*4*4*6]{}; #ifndef MAGNUM_TARGET_GLES constexpr UnsignedByte SubDataComplete[]{ @@ -841,6 +922,32 @@ void CubeMapTextureArrayGLTest::subImageQuery() { TestSuite::Compare::Container); } +void CubeMapTextureArrayGLTest::subImageQueryView() { + setTestCaseDescription(SubPixelStorageData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::texture_cube_map_array::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + + CubeMapTextureArray texture; + texture.setStorage(1, TextureFormat::RGBA8, {4, 4, 6}) + .setSubImage(0, {}, ImageView3D{PixelFormat::RGBA, PixelType::UnsignedByte, {4, 4, 6}, SubDataComplete}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{SubPixelStorageData[testCaseInstanceId()].offset + 2*2*4*4}; + MutableImageView3D image{PixelStorageData[testCaseInstanceId()].storage, PixelFormat::RGBA, PixelType::UnsignedByte, {2, 2, 4}, data}; + texture.subImage(0, Range3Di::fromSize(Vector3i{1}, {2, 2, 4}), image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector3i(2, 2, 4)); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(SubPixelStorageData[testCaseInstanceId()].offset), + SubPixelStorageData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + void CubeMapTextureArrayGLTest::subImageQueryBuffer() { setTestCaseDescription(SubPixelStorageData[testCaseInstanceId()].name); @@ -1058,6 +1165,38 @@ void CubeMapTextureArrayGLTest::compressedSubImageQuery() { TestSuite::Compare::Container); } +void CubeMapTextureArrayGLTest::compressedSubImageQueryView() { + setTestCaseDescription(CompressedSubPixelStorageData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::texture_cube_map_array::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + if(CompressedSubPixelStorageData[testCaseInstanceId()].storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() + std::string(" is not supported.")); + if(CompressedSubPixelStorageData[testCaseInstanceId()].storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() + std::string(" is not supported.")); + + CubeMapTextureArray texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, {12, 12, 6}) + .setCompressedSubImage(0, {}, CompressedImageView3D{CompressedPixelFormat::RGBAS3tcDxt3, {12, 12, 6}, CompressedSubDataComplete}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{CompressedSubPixelStorageData[testCaseInstanceId()].offset + 4*16}; + MutableCompressedImageView3D image{CompressedPixelStorageData[testCaseInstanceId()].storage, CompressedPixelFormat::RGBAS3tcDxt3, {4, 4, 4}, data}; + texture.compressedSubImage(0, Range3Di::fromSize({4, 4, 1}, Vector3i{4}), image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector3i{4}); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(CompressedSubPixelStorageData[testCaseInstanceId()].offset), + CompressedSubPixelStorageData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + void CubeMapTextureArrayGLTest::compressedSubImageQueryBuffer() { setTestCaseDescription(CompressedSubPixelStorageData[testCaseInstanceId()].name); diff --git a/src/Magnum/GL/Test/CubeMapTextureGLTest.cpp b/src/Magnum/GL/Test/CubeMapTextureGLTest.cpp index fd126e302..00225eff1 100644 --- a/src/Magnum/GL/Test/CubeMapTextureGLTest.cpp +++ b/src/Magnum/GL/Test/CubeMapTextureGLTest.cpp @@ -23,7 +23,9 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include "Magnum/Image.h" #include "Magnum/ImageView.h" @@ -80,12 +82,20 @@ struct CubeMapTextureGLTest: OpenGLTester { #ifndef MAGNUM_TARGET_GLES2 void imageBuffer(); #endif + #ifndef MAGNUM_TARGET_GLES + void imageQueryView(); + void imageQueryViewNullptr(); + void imageQueryViewBadSize(); + #endif void subImage(); #ifndef MAGNUM_TARGET_GLES2 void subImageBuffer(); #endif #ifndef MAGNUM_TARGET_GLES void subImageQuery(); + void subImageQueryView(); + /* unlike all others, subImage() simply calls into AbstractTexture, so + all assertions are already tested in AbstractTextureGLTest */ void subImageQueryBuffer(); #endif @@ -93,6 +103,13 @@ struct CubeMapTextureGLTest: OpenGLTester { #ifndef MAGNUM_TARGET_GLES2 void compressedImageBuffer(); #endif + #ifndef MAGNUM_TARGET_GLES + void compressedImageQueryView(); + void compressedImageQueryViewNullptr(); + void compressedImageQueryViewBadSize(); + void compressedImageQueryViewBadDataSize(); + void compressedImageQueryViewBadFormat(); + #endif #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) void immutableCompressedImage(); #endif @@ -102,14 +119,27 @@ struct CubeMapTextureGLTest: OpenGLTester { #endif #ifndef MAGNUM_TARGET_GLES void compressedSubImageQuery(); + void compressedSubImageQueryView(); + void compressedSubImageQueryViewNullptr(); + void compressedSubImageQueryViewBadSize(); + void compressedSubImageQueryViewBadDataSize(); + void compressedSubImageQueryViewBadFormat(); void compressedSubImageQueryBuffer(); #endif #ifndef MAGNUM_TARGET_GLES void fullImageQuery(); + void fullImageQueryView(); + void fullImageQueryViewNullptr(); + void fullImageQueryViewBadSize(); void fullImageQueryBuffer(); void compressedFullImageQuery(); + void compressedFullImageQueryView(); + void compressedFullImageQueryViewNullptr(); + void compressedFullImageQueryViewBadSize(); + void compressedFullImageQueryViewBadDataSize(); + void compressedFullImageQueryViewBadFormat(); void compressedFullImageQueryBuffer(); #endif @@ -306,13 +336,25 @@ CubeMapTextureGLTest::CubeMapTextureGLTest() { #ifndef MAGNUM_TARGET_GLES2 &CubeMapTextureGLTest::imageBuffer, #endif + #ifndef MAGNUM_TARGET_GLES + &CubeMapTextureGLTest::imageQueryView, + #endif }, Containers::arraySize(PixelStorageData)); + #ifndef MAGNUM_TARGET_GLES + addTests({&CubeMapTextureGLTest::imageQueryViewNullptr, + &CubeMapTextureGLTest::imageQueryViewBadSize}); + #endif + #ifndef MAGNUM_TARGET_GLES addInstancedTests({ &CubeMapTextureGLTest::fullImageQuery, + &CubeMapTextureGLTest::fullImageQueryView, &CubeMapTextureGLTest::fullImageQueryBuffer}, Containers::arraySize(FullPixelStorageData)); + + addTests({&CubeMapTextureGLTest::fullImageQueryViewNullptr, + &CubeMapTextureGLTest::fullImageQueryViewBadSize}); #endif addInstancedTests({ @@ -322,6 +364,7 @@ CubeMapTextureGLTest::CubeMapTextureGLTest() { #endif #ifndef MAGNUM_TARGET_GLES &CubeMapTextureGLTest::subImageQuery, + &CubeMapTextureGLTest::subImageQueryView, &CubeMapTextureGLTest::subImageQueryBuffer #endif }, Containers::arraySize(PixelStorageData)); @@ -331,16 +374,30 @@ CubeMapTextureGLTest::CubeMapTextureGLTest() { #ifndef MAGNUM_TARGET_GLES2 &CubeMapTextureGLTest::compressedImageBuffer, #endif + #ifndef MAGNUM_TARGET_GLES + &CubeMapTextureGLTest::compressedImageQueryView, + #endif #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) &CubeMapTextureGLTest::immutableCompressedImage, #endif }, Containers::arraySize(CompressedPixelStorageData)); #ifndef MAGNUM_TARGET_GLES + addTests({&CubeMapTextureGLTest::compressedImageQueryViewNullptr, + &CubeMapTextureGLTest::compressedImageQueryViewBadSize, + &CubeMapTextureGLTest::compressedImageQueryViewBadDataSize, + &CubeMapTextureGLTest::compressedImageQueryViewBadFormat}); + addInstancedTests({ &CubeMapTextureGLTest::compressedFullImageQuery, + &CubeMapTextureGLTest::compressedFullImageQueryView, &CubeMapTextureGLTest::compressedFullImageQueryBuffer}, Containers::arraySize(CompressedFullPixelStorageData)); + + addTests({&CubeMapTextureGLTest::compressedFullImageQueryViewNullptr, + &CubeMapTextureGLTest::compressedFullImageQueryViewBadSize, + &CubeMapTextureGLTest::compressedFullImageQueryViewBadDataSize, + &CubeMapTextureGLTest::compressedFullImageQueryViewBadFormat}); #endif addInstancedTests({ @@ -350,10 +407,18 @@ CubeMapTextureGLTest::CubeMapTextureGLTest() { #endif #ifndef MAGNUM_TARGET_GLES &CubeMapTextureGLTest::compressedSubImageQuery, + &CubeMapTextureGLTest::compressedSubImageQueryView, &CubeMapTextureGLTest::compressedSubImageQueryBuffer #endif }, Containers::arraySize(CompressedPixelStorageData)); + #ifndef MAGNUM_TARGET_GLES + addTests({&CubeMapTextureGLTest::compressedSubImageQueryViewNullptr, + &CubeMapTextureGLTest::compressedSubImageQueryViewBadSize, + &CubeMapTextureGLTest::compressedSubImageQueryViewBadDataSize, + &CubeMapTextureGLTest::compressedSubImageQueryViewBadFormat}); + #endif + addTests({&CubeMapTextureGLTest::generateMipmap, &CubeMapTextureGLTest::invalidateImage, @@ -807,6 +872,64 @@ void CubeMapTextureGLTest::imageBuffer() { } #endif +#ifndef MAGNUM_TARGET_GLES +void CubeMapTextureGLTest::imageQueryView() { + setTestCaseDescription(PixelStorageData[testCaseInstanceId()].name); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{2}) + .setSubImage(CubeMapCoordinate::PositiveY, 0, {}, + ImageView2D{PixelStorageData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i(2), + PixelStorageData[testCaseInstanceId()].dataSparse}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorageData[testCaseInstanceId()].offset + 2*2*4}; + MutableImageView2D image{PixelStorageData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i{2}, data}; + texture.image(CubeMapCoordinate::PositiveY, 0, image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector2i(2)); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorageData[testCaseInstanceId()].offset), + PixelStorageData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + +void CubeMapTextureGLTest::imageQueryViewNullptr() { + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{2}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + MutableImageView2D image{PixelFormat::RGBA, PixelType::UnsignedByte, + Vector2i{2}, {nullptr, 2*2*4}}; + + std::ostringstream out; + Error redirectError{&out}; + texture.image(CubeMapCoordinate::PositiveY, 0, image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::image(): image view is nullptr\n"); +} + +void CubeMapTextureGLTest::imageQueryViewBadSize() { + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{2}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[2*4]; + MutableImageView2D image{PixelFormat::RGBA, PixelType::UnsignedByte, + {2, 1}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.image(CubeMapCoordinate::PositiveY, 0, image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::image(): expected image view size Vector(2, 2) but got Vector(2, 1)\n"); +} +#endif + #ifndef MAGNUM_TARGET_GLES constexpr UnsignedByte SubDataComplete[]{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -932,6 +1055,31 @@ void CubeMapTextureGLTest::subImageQuery() { TestSuite::Compare::Container); } +void CubeMapTextureGLTest::subImageQueryView() { + setTestCaseDescription(PixelStorageData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{4}) + .setSubImage(CubeMapCoordinate::PositiveX, 0, {}, ImageView2D{PixelFormat::RGBA, PixelType::UnsignedByte, {4, 4}, SubDataComplete}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorageData[testCaseInstanceId()].offset + 2*2*4}; + MutableImageView3D image{PixelStorageData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, {2, 2, 1}, data}; + texture.subImage(0, Range3Di::fromSize({1, 1, 0}, {2, 2, 1}), image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector3i(2, 2, 1)); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorageData[testCaseInstanceId()].offset), + PixelStorageData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + void CubeMapTextureGLTest::subImageQueryBuffer() { setTestCaseDescription(PixelStorageData[testCaseInstanceId()].name); @@ -1053,6 +1201,111 @@ void CubeMapTextureGLTest::compressedImageBuffer() { } #endif +#ifndef MAGNUM_TARGET_GLES +void CubeMapTextureGLTest::compressedImageQueryView() { + setTestCaseDescription(CompressedPixelStorageData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + if(CompressedPixelStorageData[testCaseInstanceId()].storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() + std::string(" is not supported.")); + + const CompressedImageView2D view{ + CompressedPixelStorageData[testCaseInstanceId()].storage, + CompressedPixelFormat::RGBAS3tcDxt3, Vector2i{4}, + CompressedPixelStorageData[testCaseInstanceId()].dataSparse}; + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}) + .setCompressedSubImage(CubeMapCoordinate::PositiveZ, 0, {}, view); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{CompressedPixelStorageData[testCaseInstanceId()].offset + 16}; + MutableCompressedImageView2D image{CompressedPixelStorageData[testCaseInstanceId()].storage, CompressedPixelFormat::RGBAS3tcDxt3, Vector2i{4}, data}; + texture.compressedImage(CubeMapCoordinate::PositiveZ, 0, image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector2i{4}); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(CompressedPixelStorageData[testCaseInstanceId()].offset), + CompressedPixelStorageData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + +void CubeMapTextureGLTest::compressedImageQueryViewNullptr() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + MutableCompressedImageView2D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector2i{4}, {nullptr, 16}}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedImage(CubeMapCoordinate::PositiveX, 0, image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::compressedImage(): image view is nullptr\n"); +} + +void CubeMapTextureGLTest::compressedImageQueryViewBadSize() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[2*16]; + MutableCompressedImageView2D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector2i{4, 8}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedImage(CubeMapCoordinate::PositiveX, 0, image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::compressedImage(): expected image view size Vector(4, 4) but got Vector(4, 8)\n"); +} + +void CubeMapTextureGLTest::compressedImageQueryViewBadDataSize() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[16 - 1]; + MutableCompressedImageView2D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector2i{4}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedImage(CubeMapCoordinate::PositiveX, 0, image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::compressedImage(): expected image view data size 16 bytes but got 15\n"); +} + +void CubeMapTextureGLTest::compressedImageQueryViewBadFormat() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[16]; + MutableCompressedImageView2D image{CompressedPixelFormat::RGBAS3tcDxt1, Vector2i{4}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedImage(CubeMapCoordinate::PositiveX, 0, image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::compressedImage(): expected image view format GL::CompressedPixelFormat::RGBAS3tcDxt3 but got GL::CompressedPixelFormat::RGBAS3tcDxt1\n"); +} +#endif + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) void CubeMapTextureGLTest::immutableCompressedImage() { setTestCaseDescription(CompressedPixelStorageData[testCaseInstanceId()].name); @@ -1284,6 +1537,107 @@ void CubeMapTextureGLTest::compressedSubImageQuery() { TestSuite::Compare::Container); } +void CubeMapTextureGLTest::compressedSubImageQueryView() { + setTestCaseDescription(CompressedPixelStorageData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + if(CompressedPixelStorageData[testCaseInstanceId()].storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() + std::string(" is not supported.")); + if(CompressedPixelStorageData[testCaseInstanceId()].storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{12}) + .setCompressedSubImage(CubeMapCoordinate::PositiveX, 0, {}, CompressedImageView2D{CompressedPixelFormat::RGBAS3tcDxt3, {12, 12}, CompressedSubDataComplete}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{CompressedPixelStorageData[testCaseInstanceId()].offset + 16}; + MutableCompressedImageView3D image{CompressedPixelStorageData[testCaseInstanceId()].storage, CompressedPixelFormat::RGBAS3tcDxt3, {4, 4, 1}, data}; + texture.compressedSubImage(0, Range3Di::fromSize({4, 4, 0}, {4, 4, 1}), image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(CompressedPixelStorageData[testCaseInstanceId()].offset), + CompressedPixelStorageData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + +void CubeMapTextureGLTest::compressedSubImageQueryViewNullptr() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{12}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + MutableCompressedImageView3D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector3i{4, 4, 1}, {nullptr, 16}}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedSubImage(0, Range3Di::fromSize({4, 4, 0}, {4, 4, 1}), image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::compressedSubImage(): image view is nullptr\n"); +} + +void CubeMapTextureGLTest::compressedSubImageQueryViewBadSize() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{12}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[2*16]; + MutableCompressedImageView3D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector3i{4, 4, 2}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedSubImage(0, Range3Di::fromSize({4, 4, 0}, {4, 4, 1}), image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::compressedSubImage(): expected image view size Vector(4, 4, 1) but got Vector(4, 4, 2)\n"); +} + +void CubeMapTextureGLTest::compressedSubImageQueryViewBadDataSize() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{12}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[16 - 1]; + MutableCompressedImageView3D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector3i{4, 4, 1}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedSubImage(0, Range3Di::fromSize({4, 4, 0}, {4, 4, 1}), image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::compressedSubImage(): expected image view data size 16 bytes but got 15\n"); +} + +void CubeMapTextureGLTest::compressedSubImageQueryViewBadFormat() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{12}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[16]; + MutableCompressedImageView3D image{CompressedPixelFormat::RGBAS3tcDxt1, Vector3i{4, 4, 1}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedSubImage(0, Range3Di::fromSize({4, 4, 0}, {4, 4, 1}), image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::compressedSubImage(): expected image view format GL::CompressedPixelFormat::RGBAS3tcDxt3 but got GL::CompressedPixelFormat::RGBAS3tcDxt1\n"); +} + void CubeMapTextureGLTest::compressedSubImageQueryBuffer() { setTestCaseDescription(CompressedPixelStorageData[testCaseInstanceId()].name); @@ -1356,6 +1710,78 @@ void CubeMapTextureGLTest::fullImageQuery() { } } +void CubeMapTextureGLTest::fullImageQueryView() { + setTestCaseDescription(FullPixelStorageData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::direct_state_access::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{2, 2}) + .setSubImage(0, {}, ImageView3D{ + PixelFormat::RGBA, PixelType::UnsignedByte, {2, 2, 6}, + FullPixelStorageData[testCaseInstanceId()].data}); + + { + #ifdef CORRADE_TARGET_WINDOWS + bool fails(Context::current().detectedDriver() & Context::DetectedDriver::IntelWindows); + CORRADE_EXPECT_FAIL_IF(fails, + "ARB_DSA cubemap APIs are broken on Intel Windows drivers."); + #endif + + MAGNUM_VERIFY_NO_GL_ERROR(); + #ifdef CORRADE_TARGET_WINDOWS + if(fails) CORRADE_SKIP("Skipping the rest of the test"); + #endif + } + + Containers::Array data{FullPixelStorageData[testCaseInstanceId()].offset + 2*2*6*4}; + MutableImageView3D image{FullPixelStorageData[testCaseInstanceId()].storage, PixelFormat::RGBA, PixelType::UnsignedByte, {2, 2, 6}, data}; + texture.image(0, image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector3i(2, 2, 6)); + { + CORRADE_EXPECT_FAIL_IF((Context::current().detectedDriver() & Context::DetectedDriver::Mesa) && FullPixelStorageData[testCaseInstanceId()].storage != PixelStorage{}, + "Mesa drivers can't handle non-default pixel storage for full cubemap image queries."); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(FullPixelStorageData[testCaseInstanceId()].offset), + FullPixelStorageData[testCaseInstanceId()].data, + TestSuite::Compare::Container); + } +} + +void CubeMapTextureGLTest::fullImageQueryViewNullptr() { + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{2}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + MutableImageView3D image{PixelFormat::RGBA, PixelType::UnsignedByte, + {2, 2, 6}, {nullptr, 2*2*6*4}}; + + std::ostringstream out; + Error redirectError{&out}; + texture.image(0, image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::image(): image view is nullptr\n"); +} + +void CubeMapTextureGLTest::fullImageQueryViewBadSize() { + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{2}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[2*4*6]; + MutableImageView3D image{PixelFormat::RGBA, PixelType::UnsignedByte, + {2, 1, 6}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.image(0, image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::image(): expected image view size Vector(2, 2, 6) but got Vector(2, 1, 6)\n"); +} + void CubeMapTextureGLTest::fullImageQueryBuffer() { setTestCaseDescription(FullPixelStorageData[testCaseInstanceId()].name); @@ -1441,6 +1867,122 @@ void CubeMapTextureGLTest::compressedFullImageQuery() { } } +void CubeMapTextureGLTest::compressedFullImageQueryView() { + setTestCaseDescription(CompressedFullPixelStorageData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::direct_state_access::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + if(CompressedPixelStorageData[testCaseInstanceId()].storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}) + .setCompressedSubImage(0, {}, CompressedImageView3D{ + CompressedPixelFormat::RGBAS3tcDxt3, {4, 4, 6}, + CompressedFullPixelStorageData[testCaseInstanceId()].data}); + + { + #ifdef CORRADE_TARGET_WINDOWS + bool fails(Context::current().detectedDriver() & Context::DetectedDriver::IntelWindows); + CORRADE_EXPECT_FAIL_IF(fails, + "ARB_DSA cubemap APIs are broken on Intel Windows drivers."); + #endif + + MAGNUM_VERIFY_NO_GL_ERROR(); + #ifdef CORRADE_TARGET_WINDOWS + if(fails) CORRADE_SKIP("Skipping the rest of the test"); + #endif + } + + Containers::Array data{CompressedFullPixelStorageData[testCaseInstanceId()].offset + 16*6}; + MutableCompressedImageView3D image{CompressedFullPixelStorageData[testCaseInstanceId()].storage, CompressedPixelFormat::RGBAS3tcDxt3, {4, 4, 6}, data}; + texture.compressedImage(0, image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), (Vector3i{4, 4, 6})); + { + CORRADE_EXPECT_FAIL_IF((Context::current().detectedDriver() & Context::DetectedDriver::Mesa) && CompressedFullPixelStorageData[testCaseInstanceId()].storage != CompressedPixelStorage{}, + "Mesa drivers can't handle non-default pixel storage for full cubemap image queries."); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(CompressedFullPixelStorageData[testCaseInstanceId()].offset), + CompressedFullPixelStorageData[testCaseInstanceId()].data, + TestSuite::Compare::Container); + } +} + +void CubeMapTextureGLTest::compressedFullImageQueryViewNullptr() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + MutableCompressedImageView3D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector3i{4, 4, 6}, {nullptr, 16*6}}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedImage(0, image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::compressedImage(): image view is nullptr\n"); +} + +void CubeMapTextureGLTest::compressedFullImageQueryViewBadSize() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[2*6*16]; + MutableCompressedImageView3D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector3i{4, 8, 6}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedImage(0, image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::compressedImage(): expected image view size Vector(4, 4, 6) but got Vector(4, 8, 6)\n"); +} + +void CubeMapTextureGLTest::compressedFullImageQueryViewBadDataSize() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[16*6 - 1]; + MutableCompressedImageView3D image{CompressedPixelFormat::RGBAS3tcDxt3, Vector3i{4, 4, 6}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedImage(0, image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::compressedImage(): expected image view data size 96 bytes but got 95\n"); +} + +void CubeMapTextureGLTest::compressedFullImageQueryViewBadFormat() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, Vector2i{4}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + char data[6*16]; + MutableCompressedImageView3D image{CompressedPixelFormat::RGBAS3tcDxt1, Vector3i{4, 4, 6}, data}; + + std::ostringstream out; + Error redirectError{&out}; + texture.compressedImage(0, image); + CORRADE_COMPARE(out.str(), "GL::CubeMapTexture::compressedImage(): expected image view format GL::CompressedPixelFormat::RGBAS3tcDxt3 but got GL::CompressedPixelFormat::RGBAS3tcDxt1\n"); +} + void CubeMapTextureGLTest::compressedFullImageQueryBuffer() { setTestCaseDescription(CompressedFullPixelStorageData[testCaseInstanceId()].name); diff --git a/src/Magnum/GL/Test/FramebufferGLTest.cpp b/src/Magnum/GL/Test/FramebufferGLTest.cpp index e60fd6ae8..6a8e11a36 100644 --- a/src/Magnum/GL/Test/FramebufferGLTest.cpp +++ b/src/Magnum/GL/Test/FramebufferGLTest.cpp @@ -23,6 +23,7 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include @@ -124,6 +125,9 @@ struct FramebufferGLTest: OpenGLTester { void invalidateSub(); #endif void read(); + void readView(); + void readViewNullptr(); + void readViewBadSize(); #ifndef MAGNUM_TARGET_GLES2 void readBuffer(); #endif @@ -263,6 +267,9 @@ FramebufferGLTest::FramebufferGLTest() { &FramebufferGLTest::invalidateSub, #endif &FramebufferGLTest::read, + &FramebufferGLTest::readView, + &FramebufferGLTest::readViewNullptr, + &FramebufferGLTest::readViewBadSize, #ifndef MAGNUM_TARGET_GLES2 &FramebufferGLTest::readBuffer, #endif @@ -1528,6 +1535,103 @@ void FramebufferGLTest::read() { #endif } +void FramebufferGLTest::readView() { + using namespace Math::Literals; + + #ifndef MAGNUM_TARGET_GLES + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::framebuffer_object::string() + std::string(" is not available.")); + #endif + + Renderbuffer color; + #ifndef MAGNUM_TARGET_GLES2 + color.setStorage(RenderbufferFormat::RGBA8, Vector2i(128)); + #else + color.setStorage(RenderbufferFormat::RGBA4, Vector2i(128)); + #endif + + Framebuffer framebuffer({{}, Vector2i(128)}); + framebuffer.attachRenderbuffer(Framebuffer::ColorAttachment(0), color); + + MAGNUM_VERIFY_NO_GL_ERROR(); + CORRADE_COMPARE(framebuffer.checkStatus(FramebufferTarget::Read), Framebuffer::Status::Complete); + CORRADE_COMPARE(framebuffer.checkStatus(FramebufferTarget::Draw), Framebuffer::Status::Complete); + + #ifndef MAGNUM_TARGET_GLES2 + Renderer::setClearColor(0x80402011_rgbaf); + #else + /* Using only RGBA4, supply less precision. This has to be one on the input + because SwiftShader stores RGBA4 as RGBA8 internally, thus preserving + the full precision of the input. */ + Renderer::setClearColor(0x88442211_rgbaf); + #endif + Renderer::setClearDepth(Math::unpack(48352)); + Renderer::setClearStencil(67); + framebuffer.clear(FramebufferClear::Color); + + char data[(DataOffset + 8*16)*sizeof(Color4ub)]{}; + MutableImageView2D view{DataStorage, PixelFormat::RGBA, PixelType::UnsignedByte, {8, 16}, data}; + framebuffer.read(Range2Di::fromSize({16, 8}, {8, 16}), view); + + MAGNUM_VERIFY_NO_GL_ERROR(); + CORRADE_COMPARE(view.size(), Vector2i(8, 16)); + CORRADE_COMPARE(view.data().size(), (DataOffset + 8*16)*sizeof(Color4ub)); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_COMPARE(Containers::arrayCast(view.data())[DataOffset], 0x80402011_rgba); + #else /* using only RGBA4, less precision */ + CORRADE_COMPARE(Containers::arrayCast(view.data())[DataOffset], 0x88442211_rgba); + #endif +} + +void FramebufferGLTest::readViewNullptr() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::framebuffer_object::string() + std::string(" is not available.")); + #endif + + Renderbuffer color; + #ifndef MAGNUM_TARGET_GLES2 + color.setStorage(RenderbufferFormat::RGBA8, Vector2i(128)); + #else + color.setStorage(RenderbufferFormat::RGBA4, Vector2i(128)); + #endif + + Framebuffer framebuffer({{}, Vector2i(128)}); + framebuffer.attachRenderbuffer(Framebuffer::ColorAttachment(0), color); + + MutableImageView2D view{DataStorage, PixelFormat::RGBA, PixelType::UnsignedByte, {8, 16}, {nullptr, (DataOffset + 8*15)*sizeof(Color4ub)}}; + + std::ostringstream out; + Error redirectError{&out}; + framebuffer.read({{}, {8, 16}}, view); + CORRADE_COMPARE(out.str(), "GL::AbstractFramebuffer::read(): image view is nullptr\n"); +} + +void FramebufferGLTest::readViewBadSize() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::framebuffer_object::string() + std::string(" is not available.")); + #endif + + Renderbuffer color; + #ifndef MAGNUM_TARGET_GLES2 + color.setStorage(RenderbufferFormat::RGBA8, Vector2i(128)); + #else + color.setStorage(RenderbufferFormat::RGBA4, Vector2i(128)); + #endif + + Framebuffer framebuffer({{}, Vector2i(128)}); + framebuffer.attachRenderbuffer(Framebuffer::ColorAttachment(0), color); + + char data[(DataOffset + 8*15)*sizeof(Color4ub)]{}; + MutableImageView2D view{DataStorage, PixelFormat::RGBA, PixelType::UnsignedByte, {8, 15}, data}; + + std::ostringstream out; + Error redirectError{&out}; + framebuffer.read({{}, {8, 16}}, view); + CORRADE_COMPARE(out.str(), "GL::AbstractFramebuffer::read(): expected image view size Vector(8, 16) but got Vector(8, 15)\n"); +} + #ifndef MAGNUM_TARGET_GLES2 void FramebufferGLTest::readBuffer() { #ifndef MAGNUM_TARGET_GLES diff --git a/src/Magnum/GL/Test/RectangleTextureGLTest.cpp b/src/Magnum/GL/Test/RectangleTextureGLTest.cpp index 9b3559f6f..3f2fe782c 100644 --- a/src/Magnum/GL/Test/RectangleTextureGLTest.cpp +++ b/src/Magnum/GL/Test/RectangleTextureGLTest.cpp @@ -60,17 +60,23 @@ struct RectangleTextureGLTest: OpenGLTester { void image(); void imageBuffer(); + void imageQueryView(); void subImage(); void subImageBuffer(); void subImageQuery(); + void subImageQueryView(); void subImageQueryBuffer(); + /* View query assertions tested in AbstractTextureGLTest */ void compressedImage(); void compressedImageBuffer(); + void compressedImageQueryView(); void compressedSubImage(); void compressedSubImageBuffer(); void compressedSubImageQuery(); + void compressedSubImageQueryView(); void compressedSubImageQueryBuffer(); + /* View query assertions tested in AbstractTextureGLTest */ void invalidateImage(); void invalidateSubImage(); @@ -126,17 +132,21 @@ RectangleTextureGLTest::RectangleTextureGLTest() { addInstancedTests({ &RectangleTextureGLTest::image, &RectangleTextureGLTest::imageBuffer, + &RectangleTextureGLTest::imageQueryView, &RectangleTextureGLTest::subImage, &RectangleTextureGLTest::subImageBuffer, &RectangleTextureGLTest::subImageQuery, + &RectangleTextureGLTest::subImageQueryView, &RectangleTextureGLTest::subImageQueryBuffer}, Containers::arraySize(PixelStorageData)); addTests({&RectangleTextureGLTest::compressedImage, &RectangleTextureGLTest::compressedImageBuffer, + &RectangleTextureGLTest::compressedImageQueryView, &RectangleTextureGLTest::compressedSubImage, &RectangleTextureGLTest::compressedSubImageBuffer, &RectangleTextureGLTest::compressedSubImageQuery, + &RectangleTextureGLTest::compressedSubImageQueryView, &RectangleTextureGLTest::compressedSubImageQueryBuffer, &RectangleTextureGLTest::invalidateImage, @@ -359,6 +369,32 @@ void RectangleTextureGLTest::imageBuffer() { TestSuite::Compare::Container); } +void RectangleTextureGLTest::imageQueryView() { + setTestCaseDescription(PixelStorageData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::texture_rectangle::string() + std::string(" is not supported.")); + + RectangleTexture texture; + texture.setImage(TextureFormat::RGBA8, ImageView2D{ + PixelStorageData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i(2), + PixelStorageData[testCaseInstanceId()].dataSparse}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorageData[testCaseInstanceId()].offset + 2*2*4}; + MutableImageView2D image{PixelStorageData[testCaseInstanceId()].storage, PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i{2}, data}; + texture.image(image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector2i(2)); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorageData[testCaseInstanceId()].offset), + PixelStorageData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + constexpr UnsignedByte Zero[4*4*4]{}; constexpr UnsignedByte SubDataComplete[]{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -446,6 +482,32 @@ void RectangleTextureGLTest::subImageQuery() { TestSuite::Compare::Container); } +void RectangleTextureGLTest::subImageQueryView() { + setTestCaseDescription(PixelStorageData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::texture_rectangle::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + + RectangleTexture texture; + texture.setStorage(TextureFormat::RGBA8, Vector2i{4}) + .setSubImage({}, ImageView2D{PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i{4}, SubDataComplete}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorageData[testCaseInstanceId()].offset + 2*2*4}; + MutableImageView2D image{PixelStorageData[testCaseInstanceId()].storage, PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i{2}, data}; + texture.subImage(Range2Di::fromSize(Vector2i{1}, Vector2i{2}), image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector2i{2}); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorageData[testCaseInstanceId()].offset), + PixelStorageData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + void RectangleTextureGLTest::subImageQueryBuffer() { setTestCaseDescription(PixelStorageData[testCaseInstanceId()].name); @@ -481,6 +543,10 @@ void RectangleTextureGLTest::compressedImageBuffer() { CORRADE_SKIP("No rectangle texture compression format exists."); } +void RectangleTextureGLTest::compressedImageQueryView() { + CORRADE_SKIP("No rectangle texture compression format exists."); +} + void RectangleTextureGLTest::compressedSubImage() { CORRADE_SKIP("No rectangle texture compression format exists."); } @@ -493,6 +559,10 @@ void RectangleTextureGLTest::compressedSubImageQuery() { CORRADE_SKIP("No rectangle texture compression format exists."); } +void RectangleTextureGLTest::compressedSubImageQueryView() { + CORRADE_SKIP("No rectangle texture compression format exists."); +} + void RectangleTextureGLTest::compressedSubImageQueryBuffer() { CORRADE_SKIP("No rectangle texture compression format exists."); } diff --git a/src/Magnum/GL/Test/TextureArrayGLTest.cpp b/src/Magnum/GL/Test/TextureArrayGLTest.cpp index e9464634d..927a091dd 100644 --- a/src/Magnum/GL/Test/TextureArrayGLTest.cpp +++ b/src/Magnum/GL/Test/TextureArrayGLTest.cpp @@ -111,35 +111,51 @@ struct TextureArrayGLTest: OpenGLTester { #ifndef MAGNUM_TARGET_GLES void image1D(); void image1DBuffer(); + void image1DQueryView(); void subImage1D(); void subImage1DBuffer(); void subImage1DQuery(); + void subImage1DQueryView(); void subImage1DQueryBuffer(); + /* View query assertions tested in AbstractTextureGLTest */ void compressedImage1D(); void compressedImage1DBuffer(); + void compressedImage1DQueryView(); void compressedSubImage1D(); void compressedSubImage1DBuffer(); void compressedSubImage1DQuery(); + void compressedSubImage1DQueryView(); void compressedSubImage1DQueryBuffer(); + /* View query assertions tested in AbstractTextureGLTest */ #endif void image2D(); void image2DBuffer(); + #ifndef MAGNUM_TARGET_GLES + void image2DQueryView(); + #endif void subImage2D(); void subImage2DBuffer(); #ifndef MAGNUM_TARGET_GLES void subImage2DQuery(); + void subImage2DQueryView(); void subImage2DQueryBuffer(); + /* View query assertions tested in AbstractTextureGLTest */ #endif void compressedImage2D(); void compressedImage2DBuffer(); + #ifndef MAGNUM_TARGET_GLES + void compressedImage2DQueryView(); + #endif void compressedSubImage2D(); void compressedSubImage2DBuffer(); #ifndef MAGNUM_TARGET_GLES void compressedSubImage2DQuery(); + void compressedSubImage2DQueryView(); void compressedSubImage2DQueryBuffer(); + /* View query assertions tested in AbstractTextureGLTest */ #endif #ifndef MAGNUM_TARGET_GLES @@ -320,27 +336,35 @@ TextureArrayGLTest::TextureArrayGLTest() { addInstancedTests({ &TextureArrayGLTest::image1D, &TextureArrayGLTest::image1DBuffer, + &TextureArrayGLTest::image1DQueryView, &TextureArrayGLTest::subImage1D, &TextureArrayGLTest::subImage1DBuffer, &TextureArrayGLTest::subImage1DQuery, + &TextureArrayGLTest::subImage1DQueryView, &TextureArrayGLTest::subImage1DQueryBuffer}, Containers::arraySize(PixelStorage1DData)); addTests({&TextureArrayGLTest::compressedImage1D, &TextureArrayGLTest::compressedImage1DBuffer, + &TextureArrayGLTest::compressedImage1DQueryView, &TextureArrayGLTest::compressedSubImage1D, &TextureArrayGLTest::compressedSubImage1DBuffer, &TextureArrayGLTest::compressedSubImage1DQuery, + &TextureArrayGLTest::compressedSubImage1DQueryView, &TextureArrayGLTest::compressedSubImage1DQueryBuffer}); #endif addInstancedTests({ &TextureArrayGLTest::image2D, &TextureArrayGLTest::image2DBuffer, + #ifndef MAGNUM_TARGET_GLES + &TextureArrayGLTest::image2DQueryView, + #endif &TextureArrayGLTest::subImage2D, &TextureArrayGLTest::subImage2DBuffer, #ifndef MAGNUM_TARGET_GLES &TextureArrayGLTest::subImage2DQuery, + &TextureArrayGLTest::subImage2DQueryView, &TextureArrayGLTest::subImage2DQueryBuffer #endif }, Containers::arraySize(PixelStorage2DData)); @@ -348,10 +372,14 @@ TextureArrayGLTest::TextureArrayGLTest() { addInstancedTests({ &TextureArrayGLTest::compressedImage2D, &TextureArrayGLTest::compressedImage2DBuffer, + #ifndef MAGNUM_TARGET_GLES + &TextureArrayGLTest::compressedImage2DQueryView, + #endif &TextureArrayGLTest::compressedSubImage2D, &TextureArrayGLTest::compressedSubImage2DBuffer, #ifndef MAGNUM_TARGET_GLES &TextureArrayGLTest::compressedSubImage2DQuery, + &TextureArrayGLTest::compressedSubImage2DQueryView, &TextureArrayGLTest::compressedSubImage2DQueryBuffer #endif }, Containers::arraySize(CompressedPixelStorage2DData)); @@ -882,6 +910,32 @@ void TextureArrayGLTest::image1DBuffer() { TestSuite::Compare::Container); } +void TextureArrayGLTest::image1DQueryView() { + setTestCaseDescription(PixelStorage1DData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_array::string() + std::string(" is not supported.")); + + Texture1DArray texture; + texture.setImage(0, TextureFormat::RGBA8, ImageView2D{ + PixelStorage1DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i(2), PixelStorage1DData[testCaseInstanceId()].dataSparse}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorage1DData[testCaseInstanceId()].offset + 2*2*4}; + MutableImageView2D image{PixelStorage1DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i{2}, data}; + texture.image(0, image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector2i(2)); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorage1DData[testCaseInstanceId()].offset), + PixelStorage1DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + constexpr UnsignedByte Zero1D[4*4*4] = {}; constexpr UnsignedByte SubData1DComplete[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -969,6 +1023,33 @@ void TextureArrayGLTest::subImage1DQuery() { TestSuite::Compare::Container); } +void TextureArrayGLTest::subImage1DQueryView() { + setTestCaseDescription(PixelStorage1DData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_array::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + + Texture1DArray texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{4}) + .setSubImage(0, {}, ImageView2D{PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i{4}, SubData1DComplete}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorage1DData[testCaseInstanceId()].offset + 2*2*4}; + MutableImageView2D image{PixelStorage1DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i{2}, data}; + texture.subImage(0, Range2Di::fromSize(Vector2i{1}, Vector2i{2}), image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector2i{2}); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorage1DData[testCaseInstanceId()].offset), + PixelStorage1DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + void TextureArrayGLTest::subImage1DQueryBuffer() { setTestCaseDescription(PixelStorage1DData[testCaseInstanceId()].name); @@ -1006,6 +1087,10 @@ void TextureArrayGLTest::compressedImage1DBuffer() { CORRADE_SKIP("No 1D texture compression format exists."); } +void TextureArrayGLTest::compressedImage1DQueryView() { + CORRADE_SKIP("No 1D texture compression format exists."); +} + void TextureArrayGLTest::compressedSubImage1D() { CORRADE_SKIP("No 1D texture compression format exists."); } @@ -1018,6 +1103,10 @@ void TextureArrayGLTest::compressedSubImage1DQuery() { CORRADE_SKIP("No 1D texture compression format exists."); } +void TextureArrayGLTest::compressedSubImage1DQueryView() { + CORRADE_SKIP("No 1D texture compression format exists."); +} + void TextureArrayGLTest::compressedSubImage1DQueryBuffer() { CORRADE_SKIP("No 1D texture compression format exists."); } @@ -1085,6 +1174,35 @@ void TextureArrayGLTest::image2DBuffer() { #endif } +#ifndef MAGNUM_TARGET_GLES +void TextureArrayGLTest::image2DQueryView() { + setTestCaseDescription(PixelStorage2DData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_array::string() + std::string(" is not supported.")); + + Texture2DArray texture; + texture.setImage(0, TextureFormat::RGBA8, ImageView3D{ + PixelStorage2DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector3i(2), + PixelStorage2DData[testCaseInstanceId()].dataSparse}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorage2DData[testCaseInstanceId()].offset + 2*2*2*4}; + MutableImageView3D image{PixelStorage2DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector3i{2}, data}; + texture.image(0, image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector3i(2)); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorage2DData[testCaseInstanceId()].offset), + PixelStorage2DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} +#endif + constexpr UnsignedByte Zero2D[4*4*4*4]{}; #ifndef MAGNUM_TARGET_GLES @@ -1202,6 +1320,33 @@ void TextureArrayGLTest::subImage2DQuery() { TestSuite::Compare::Container); } +void TextureArrayGLTest::subImage2DQueryView() { + setTestCaseDescription(PixelStorage2DData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_array::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + + Texture2DArray texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector3i{4}) + .setSubImage(0, {}, ImageView3D{PixelFormat::RGBA, PixelType::UnsignedByte, Vector3i{4}, SubData2DComplete}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorage2DData[testCaseInstanceId()].offset + 2*2*2*4}; + MutableImageView3D image{PixelStorage2DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector3i{2}, data}; + texture.subImage(0, Range3Di::fromSize(Vector3i{1}, Vector3i{2}), image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector3i{2}); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorage2DData[testCaseInstanceId()].offset), + PixelStorage2DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + void TextureArrayGLTest::subImage2DQueryBuffer() { setTestCaseDescription(PixelStorage2DData[testCaseInstanceId()].name); @@ -1316,6 +1461,39 @@ void TextureArrayGLTest::compressedImage2DBuffer() { #endif } +#ifndef MAGNUM_TARGET_GLES +void TextureArrayGLTest::compressedImage2DQueryView() { + setTestCaseDescription(CompressedPixelStorage2DData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_array::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + if(CompressedPixelStorage2DData[testCaseInstanceId()].storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() + std::string(" is not supported.")); + + Texture2DArray texture; + texture.setCompressedImage(0, CompressedImageView3D{ + CompressedPixelStorage2DData[testCaseInstanceId()].storage, + CompressedPixelFormat::RGBAS3tcDxt3, {4, 4, 2}, + CompressedPixelStorage2DData[testCaseInstanceId()].dataSparse}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{CompressedPixelStorage2DData[testCaseInstanceId()].offset + 2*16}; + MutableCompressedImageView3D image{CompressedPixelStorage2DData[testCaseInstanceId()].storage, CompressedPixelFormat::RGBAS3tcDxt3, {4, 4, 2}, data}; + texture.compressedImage(0, image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), (Vector3i{4, 4, 2})); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(CompressedPixelStorage2DData[testCaseInstanceId()].offset), + CompressedPixelStorage2DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} +#endif + /* Just 12x4x4 zeros compressed using RGBA DXT3 by the driver */ constexpr UnsignedByte CompressedZero2D[3*4*16]{}; @@ -1480,6 +1658,39 @@ void TextureArrayGLTest::compressedSubImage2DQuery() { TestSuite::Compare::Container); } +void TextureArrayGLTest::compressedSubImage2DQueryView() { + setTestCaseDescription(CompressedPixelStorage2DData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_array::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + if(CompressedPixelStorage2DData[testCaseInstanceId()].storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() + std::string(" is not supported.")); + if(CompressedPixelStorage2DData[testCaseInstanceId()].storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() + std::string(" is not supported.")); + + Texture2DArray texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, {12, 4, 4}) + .setCompressedSubImage(0, {}, CompressedImageView3D{CompressedPixelFormat::RGBAS3tcDxt3, + {12, 4, 4}, CompressedSubData2DComplete}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{CompressedPixelStorage2DData[testCaseInstanceId()].offset + 2*16}; + MutableCompressedImageView3D image{CompressedPixelStorage2DData[testCaseInstanceId()].storage, CompressedPixelFormat::RGBAS3tcDxt3, {4, 4, 2}, data}; + texture.compressedSubImage(0, Range3Di::fromSize({4, 0, 1}, {4, 4, 2}), image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), (Vector3i{4, 4, 2})); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(CompressedPixelStorage2DData[testCaseInstanceId()].offset), + CompressedPixelStorage2DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + void TextureArrayGLTest::compressedSubImage2DQueryBuffer() { setTestCaseDescription(CompressedPixelStorage2DData[testCaseInstanceId()].name); diff --git a/src/Magnum/GL/Test/TextureGLTest.cpp b/src/Magnum/GL/Test/TextureGLTest.cpp index c5668c7a8..ade7356a8 100644 --- a/src/Magnum/GL/Test/TextureGLTest.cpp +++ b/src/Magnum/GL/Test/TextureGLTest.cpp @@ -140,43 +140,59 @@ struct TextureGLTest: OpenGLTester { #ifndef MAGNUM_TARGET_GLES void image1D(); void image1DBuffer(); + void image1DQueryView(); void subImage1D(); void subImage1DBuffer(); void subImage1DQuery(); + void subImage1DQueryView(); void subImage1DQueryBuffer(); + /* View query assertions tested in AbstractTextureGLTest */ void compressedImage1D(); void compressedImage1DBuffer(); + void compressedImage1DQueryView(); void compressedSubImage1D(); void compressedSubImage1DBuffer(); void compressedSubImage1DQuery(); + void compressedSubImage1DQueryView(); void compressedSubImage1DQueryBuffer(); + /* View query assertions tested in AbstractTextureGLTest */ #endif void image2D(); #ifndef MAGNUM_TARGET_GLES2 void image2DBuffer(); #endif + #ifndef MAGNUM_TARGET_GLES + void image2DQueryView(); + #endif void subImage2D(); #ifndef MAGNUM_TARGET_GLES2 void subImage2DBuffer(); #endif #ifndef MAGNUM_TARGET_GLES void subImage2DQuery(); + void subImage2DQueryView(); void subImage2DQueryBuffer(); + /* View query assertions tested in AbstractTextureGLTest */ #endif void compressedImage2D(); #ifndef MAGNUM_TARGET_GLES2 void compressedImage2DBuffer(); #endif + #ifndef MAGNUM_TARGET_GLES + void compressedImage2DQueryView(); + #endif void compressedSubImage2D(); #ifndef MAGNUM_TARGET_GLES2 void compressedSubImage2DBuffer(); #endif #ifndef MAGNUM_TARGET_GLES void compressedSubImage2DQuery(); + void compressedSubImage2DQueryView(); void compressedSubImage2DQueryBuffer(); + /* View query assertions tested in AbstractTextureGLTest */ #endif #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) @@ -184,26 +200,36 @@ struct TextureGLTest: OpenGLTester { #ifndef MAGNUM_TARGET_GLES2 void image3DBuffer(); #endif + #ifndef MAGNUM_TARGET_GLES + void image3DQueryView(); + #endif void subImage3D(); #ifndef MAGNUM_TARGET_GLES2 void subImage3DBuffer(); #endif #ifndef MAGNUM_TARGET_GLES void subImage3DQuery(); + void subImage3DQueryView(); void subImage3DQueryBuffer(); + /* View query assertions tested in AbstractTextureGLTest */ #endif void compressedImage3D(); #ifndef MAGNUM_TARGET_GLES2 void compressedImage3DBuffer(); #endif + #ifndef MAGNUM_TARGET_GLES + void compressedImage3DQueryView(); + #endif void compressedSubImage3D(); #ifndef MAGNUM_TARGET_GLES2 void compressedSubImage3DBuffer(); #endif #ifndef MAGNUM_TARGET_GLES void compressedSubImage3DQuery(); + void compressedSubImage3DQueryView(); void compressedSubImage3DQueryBuffer(); + /* View query assertions tested in AbstractTextureGLTest */ #endif #endif @@ -501,17 +527,21 @@ TextureGLTest::TextureGLTest() { addInstancedTests({ &TextureGLTest::image1D, &TextureGLTest::image1DBuffer, + &TextureGLTest::image1DQueryView, &TextureGLTest::subImage1D, &TextureGLTest::subImage1DBuffer, &TextureGLTest::subImage1DQuery, + &TextureGLTest::subImage1DQueryView, &TextureGLTest::subImage1DQueryBuffer}, Containers::arraySize(PixelStorage1DData)); addTests({&TextureGLTest::compressedImage1D, &TextureGLTest::compressedImage1DBuffer, + &TextureGLTest::compressedImage1DQueryView, &TextureGLTest::compressedSubImage1D, &TextureGLTest::compressedSubImage1DBuffer, &TextureGLTest::compressedSubImage1DQuery, + &TextureGLTest::compressedSubImage1DQueryView, &TextureGLTest::compressedSubImage1DQueryBuffer}); #endif @@ -520,12 +550,16 @@ TextureGLTest::TextureGLTest() { #ifndef MAGNUM_TARGET_GLES2 &TextureGLTest::image2DBuffer, #endif + #ifndef MAGNUM_TARGET_GLES + &TextureGLTest::image2DQueryView, + #endif &TextureGLTest::subImage2D, #ifndef MAGNUM_TARGET_GLES2 &TextureGLTest::subImage2DBuffer, #endif #ifndef MAGNUM_TARGET_GLES &TextureGLTest::subImage2DQuery, + &TextureGLTest::subImage2DQueryView, &TextureGLTest::subImage2DQueryBuffer, #endif }, Containers::arraySize(PixelStorage2DData)); @@ -535,12 +569,16 @@ TextureGLTest::TextureGLTest() { #ifndef MAGNUM_TARGET_GLES2 &TextureGLTest::compressedImage2DBuffer, #endif + #ifndef MAGNUM_TARGET_GLES + &TextureGLTest::compressedImage2DQueryView, + #endif &TextureGLTest::compressedSubImage2D, #ifndef MAGNUM_TARGET_GLES2 &TextureGLTest::compressedSubImage2DBuffer, #endif #ifndef MAGNUM_TARGET_GLES &TextureGLTest::compressedSubImage2DQuery, + &TextureGLTest::compressedSubImage2DQueryView, &TextureGLTest::compressedSubImage2DQueryBuffer #endif }, Containers::arraySize(CompressedPixelStorage2DData)); @@ -551,12 +589,16 @@ TextureGLTest::TextureGLTest() { #ifndef MAGNUM_TARGET_GLES2 &TextureGLTest::image3DBuffer, #endif + #ifndef MAGNUM_TARGET_GLES + &TextureGLTest::image3DQueryView, + #endif &TextureGLTest::subImage3D, #ifndef MAGNUM_TARGET_GLES2 &TextureGLTest::subImage3DBuffer, #endif #ifndef MAGNUM_TARGET_GLES &TextureGLTest::subImage3DQuery, + &TextureGLTest::subImage3DQueryView, &TextureGLTest::subImage3DQueryBuffer, #endif }, Containers::arraySize(PixelStorage3DData)); @@ -566,12 +608,16 @@ TextureGLTest::TextureGLTest() { #ifndef MAGNUM_TARGET_GLES2 &TextureGLTest::compressedImage3DBuffer, #endif + #ifndef MAGNUM_TARGET_GLES + &TextureGLTest::compressedImage3DQueryView, + #endif &TextureGLTest::compressedSubImage3D, #ifndef MAGNUM_TARGET_GLES2 &TextureGLTest::compressedSubImage3DBuffer, #endif #ifndef MAGNUM_TARGET_GLES &TextureGLTest::compressedSubImage3DQuery, + &TextureGLTest::compressedSubImage3DQueryView, &TextureGLTest::compressedSubImage3DQueryBuffer #endif }, Containers::arraySize(CompressedPixelStorage3DData)); @@ -1319,6 +1365,30 @@ void TextureGLTest::image1DBuffer() { TestSuite::Compare::Container); } +void TextureGLTest::image1DQueryView() { + setTestCaseDescription(PixelStorage1DData[testCaseInstanceId()].name); + + Texture1D texture; + texture.setImage(0, TextureFormat::RGBA8, ImageView1D{ + PixelStorage1DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, 2, + PixelStorage1DData[testCaseInstanceId()].dataSparse}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorage1DData[testCaseInstanceId()].offset + 2*4}; + MutableImageView1D image{PixelStorage1DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, 2, data}; + texture.image(0, image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), 2); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorage1DData[testCaseInstanceId()].offset), + PixelStorage1DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + constexpr UnsignedByte Zero1D[4*4] = {}; constexpr UnsignedByte SubData1DComplete[]{ 0, 0, 0, 0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0, 0, 0, 0 @@ -1395,6 +1465,31 @@ void TextureGLTest::subImage1DQuery() { TestSuite::Compare::Container); } +void TextureGLTest::subImage1DQueryView() { + setTestCaseDescription(PixelStorage1DData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + + Texture1D texture; + texture.setStorage(1, TextureFormat::RGBA8, 4) + .setSubImage(0, {}, ImageView1D{PixelFormat::RGBA, PixelType::UnsignedByte, 4, SubData1DComplete}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorage1DData[testCaseInstanceId()].offset + 2*4}; + MutableImageView1D image{PixelStorage1DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, 2, data}; + texture.subImage(0, Range1Di::fromSize(1, 2), image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), 2); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorage1DData[testCaseInstanceId()].offset), + PixelStorage1DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + void TextureGLTest::subImage1DQueryBuffer() { setTestCaseDescription(PixelStorage1DData[testCaseInstanceId()].name); @@ -1428,6 +1523,10 @@ void TextureGLTest::compressedImage1DBuffer() { CORRADE_SKIP("No 1D texture compression format exists."); } +void TextureGLTest::compressedImage1DQueryView() { + CORRADE_SKIP("No 1D texture compression format exists."); +} + void TextureGLTest::compressedSubImage1D() { CORRADE_SKIP("No 1D texture compression format exists."); } @@ -1440,6 +1539,10 @@ void TextureGLTest::compressedSubImage1DQuery() { CORRADE_SKIP("No 1D texture compression format exists."); } +void TextureGLTest::compressedSubImage1DQueryView() { + CORRADE_SKIP("No 1D texture compression format exists."); +} + void TextureGLTest::compressedSubImage1DQueryBuffer() { CORRADE_SKIP("No 1D texture compression format exists."); } @@ -1516,6 +1619,32 @@ void TextureGLTest::image2DBuffer() { } #endif +#ifndef MAGNUM_TARGET_GLES +void TextureGLTest::image2DQueryView() { + setTestCaseDescription(PixelStorage2DData[testCaseInstanceId()].name); + + Texture2D texture; + texture.setImage(0, TextureFormat::RGBA8, ImageView2D{ + PixelStorage2DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i(2), + PixelStorage2DData[testCaseInstanceId()].dataSparse}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorage2DData[testCaseInstanceId()].offset + 2*2*4}; + MutableImageView2D image{PixelStorage2DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i{2}, data}; + texture.image(0, image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector2i(2)); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorage2DData[testCaseInstanceId()].offset), + PixelStorage2DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} +#endif + constexpr UnsignedByte Zero2D[4*4*4]{}; #ifndef MAGNUM_TARGET_GLES @@ -1620,6 +1749,31 @@ void TextureGLTest::subImage2DQuery() { TestSuite::Compare::Container); } +void TextureGLTest::subImage2DQueryView() { + setTestCaseDescription(PixelStorage2DData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + + Texture2D texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{4}) + .setSubImage(0, {}, ImageView2D{PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i{4}, SubData2DComplete}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorage2DData[testCaseInstanceId()].offset + 2*2*4}; + MutableImageView2D image{PixelStorage2DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector2i{2}, data}; + texture.subImage(0, Range2Di::fromSize(Vector2i{1}, Vector2i{2}), image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector2i{2}); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorage2DData[testCaseInstanceId()].offset), + PixelStorage2DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + void TextureGLTest::subImage2DQueryBuffer() { setTestCaseDescription(PixelStorage2DData[testCaseInstanceId()].name); @@ -1727,6 +1881,38 @@ void TextureGLTest::compressedImage2DBuffer() { } #endif +#ifndef MAGNUM_TARGET_GLES +void TextureGLTest::compressedImage2DQueryView() { + setTestCaseDescription(CompressedPixelStorage2DData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + + if(CompressedPixelStorage2DData[testCaseInstanceId()].storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() + std::string(" is not supported.")); + + Texture2D texture; + texture.setCompressedImage(0, CompressedImageView2D{ + CompressedPixelStorage2DData[testCaseInstanceId()].storage, + CompressedPixelFormat::RGBAS3tcDxt3, Vector2i{4}, + CompressedPixelStorage2DData[testCaseInstanceId()].dataSparse}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{CompressedPixelStorage2DData[testCaseInstanceId()].offset + 1*16}; + MutableCompressedImageView2D image{CompressedPixelStorage2DData[testCaseInstanceId()].storage, CompressedPixelFormat::RGBAS3tcDxt3, Vector2i{4}, data}; + texture.compressedImage(0, image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector2i{4}); + + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(CompressedPixelStorage2DData[testCaseInstanceId()].offset), + CompressedPixelStorage2DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} +#endif + /* Just 12x4 zeros compressed using RGBA DXT3 by the driver */ constexpr UnsignedByte CompressedZero2D[3*16]{}; @@ -1856,6 +2042,36 @@ void TextureGLTest::compressedSubImage2DQuery() { TestSuite::Compare::Container); } +void TextureGLTest::compressedSubImage2DQueryView() { + setTestCaseDescription(CompressedPixelStorage2DData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::EXT::texture_compression_s3tc::string() + std::string(" is not supported.")); + if(CompressedPixelStorage2DData[testCaseInstanceId()].storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() + std::string(" is not supported.")); + if(CompressedPixelStorage2DData[testCaseInstanceId()].storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() + std::string(" is not supported.")); + + Texture2D texture; + texture.setStorage(1, TextureFormat::CompressedRGBAS3tcDxt3, {12, 4}) + .setCompressedSubImage(0, {}, CompressedImageView2D{CompressedPixelFormat::RGBAS3tcDxt3, {12, 4}, CompressedSubData2DComplete}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{CompressedPixelStorage2DData[testCaseInstanceId()].offset + 1*16}; + MutableCompressedImageView2D image{CompressedPixelStorage2DData[testCaseInstanceId()].storage, CompressedPixelFormat::RGBAS3tcDxt3, Vector2i{4}, data}; + texture.compressedSubImage(0, Range2Di::fromSize({4, 0}, Vector2i{4}), image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector2i{4}); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(CompressedPixelStorage2DData[testCaseInstanceId()].offset), + CompressedPixelStorage2DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + void TextureGLTest::compressedSubImage2DQueryBuffer() { setTestCaseDescription(CompressedPixelStorage2DData[testCaseInstanceId()].name); @@ -1950,6 +2166,32 @@ void TextureGLTest::image3DBuffer() { } #endif +#ifndef MAGNUM_TARGET_GLES +void TextureGLTest::image3DQueryView() { + setTestCaseDescription(PixelStorage3DData[testCaseInstanceId()].name); + + Texture3D texture; + texture.setImage(0, TextureFormat::RGBA8, ImageView3D{ + PixelStorage3DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector3i(2), + PixelStorage3DData[testCaseInstanceId()].dataSparse}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorage3DData[testCaseInstanceId()].offset + 2*2*2*4}; + MutableImageView3D image{PixelStorage3DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector3i{2}, data}; + texture.image(0, image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector3i(2)); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorage3DData[testCaseInstanceId()].offset), + PixelStorage3DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} +#endif + constexpr UnsignedByte Zero3D[4*4*4*4]{}; #ifndef MAGNUM_TARGET_GLES @@ -2063,6 +2305,31 @@ void TextureGLTest::subImage3DQuery() { TestSuite::Compare::Container); } +void TextureGLTest::subImage3DQueryView() { + setTestCaseDescription(PixelStorage3DData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + + Texture3D texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector3i{4}) + .setSubImage(0, {}, ImageView3D{PixelFormat::RGBA, PixelType::UnsignedByte, Vector3i{4}, SubData3DComplete}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{PixelStorage3DData[testCaseInstanceId()].offset + 2*2*2*4}; + MutableImageView3D image{PixelStorage3DData[testCaseInstanceId()].storage, + PixelFormat::RGBA, PixelType::UnsignedByte, Vector3i{2}, data}; + texture.subImage(0, Range3Di::fromSize(Vector3i{1}, Vector3i{2}), image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector3i{2}); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(PixelStorage3DData[testCaseInstanceId()].offset), + PixelStorage3DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); +} + void TextureGLTest::subImage3DQueryBuffer() { setTestCaseDescription(PixelStorage3DData[testCaseInstanceId()].name); @@ -2163,6 +2430,40 @@ void TextureGLTest::compressedImage3DBuffer() { } #endif +#ifndef MAGNUM_TARGET_GLES +void TextureGLTest::compressedImage3DQueryView() { + setTestCaseDescription(CompressedPixelStorage3DData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::texture_compression_bptc::string() + std::string(" is not supported.")); + if(CompressedPixelStorage3DData[testCaseInstanceId()].storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() + std::string(" is not supported.")); + + Texture3D texture; + texture.setCompressedImage(0, CompressedImageView3D{ + CompressedPixelStorage3DData[testCaseInstanceId()].storage, + CompressedPixelFormat::RGBABptcUnorm, Vector3i{4}, + CompressedPixelStorage3DData[testCaseInstanceId()].dataSparse}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{CompressedPixelStorage2DData[testCaseInstanceId()].offset + 64}; + MutableCompressedImageView3D image{CompressedPixelStorage2DData[testCaseInstanceId()].storage, CompressedPixelFormat::RGBABptcUnorm, Vector3i{4}, data}; + texture.compressedImage(0, image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), Vector3i{4}); + { + CORRADE_EXPECT_FAIL_IF((Context::current().detectedDriver() & Context::DetectedDriver::Mesa) && CompressedPixelStorage3DData[testCaseInstanceId()].storage != CompressedPixelStorage{}, + "Mesa drivers can't handle non-default compressed 3D pixel storage."); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(CompressedPixelStorage3DData[testCaseInstanceId()].offset), + CompressedPixelStorage3DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); + } +} +#endif + #ifndef MAGNUM_TARGET_GLES /* Just 12x4x4 zeros compressed using RGBA BPTC Unorm by the driver */ constexpr UnsignedByte CompressedZero3D[3*4*16]{ @@ -2325,6 +2626,43 @@ void TextureGLTest::compressedSubImage3DQuery() { } } +void TextureGLTest::compressedSubImage3DQueryView() { + setTestCaseDescription(CompressedPixelStorage3DData[testCaseInstanceId()].name); + + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::get_texture_sub_image::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::texture_compression_bptc::string() + std::string(" is not supported.")); + if(CompressedPixelStorage3DData[testCaseInstanceId()].storage != CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::compressed_texture_pixel_storage::string() + std::string(" is not supported.")); + if(CompressedPixelStorage3DData[testCaseInstanceId()].storage == CompressedPixelStorage{} && !Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::ARB::internalformat_query2::string() + std::string(" is not supported.")); + + Texture3D texture; + texture.setStorage(1, TextureFormat::CompressedRGBABptcUnorm, {12, 4, 4}) + .setCompressedSubImage(0, {}, CompressedImageView3D{CompressedPixelFormat::RGBABptcUnorm, {12, 4, 4}, CompressedSubData3DComplete}); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + Containers::Array data{CompressedPixelStorage2DData[testCaseInstanceId()].offset + 64}; + MutableCompressedImageView3D image{CompressedPixelStorage2DData[testCaseInstanceId()].storage, CompressedPixelFormat::RGBABptcUnorm, Vector3i{4}, data}; + texture.compressedSubImage(0, Range3Di::fromSize({4, 0, 0}, Vector3i{4}), image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(image.size(), (Vector3i{4})); + + { + CORRADE_EXPECT_FAIL_IF(CompressedPixelStorage3DData[testCaseInstanceId()].storage == CompressedPixelStorage{} && (Context::current().detectedDriver() & Context::DetectedDriver::NVidia), + "Default compressed pixel storage behaves weirdly with BPTC compression on NVidia."); + CORRADE_EXPECT_FAIL_IF((Context::current().detectedDriver() & Context::DetectedDriver::Mesa), + "Mesa drivers can't handle compressed 3D pixel storage for subimages."); + CORRADE_COMPARE_AS(Containers::arrayCast(image.data()).suffix(CompressedPixelStorage3DData[testCaseInstanceId()].offset), + CompressedPixelStorage3DData[testCaseInstanceId()].data, + TestSuite::Compare::Container); + } +} + void TextureGLTest::compressedSubImage3DQueryBuffer() { setTestCaseDescription(CompressedPixelStorage3DData[testCaseInstanceId()].name); diff --git a/src/Magnum/GL/Texture.h b/src/Magnum/GL/Texture.h index 5f165f4c7..594590912 100644 --- a/src/Magnum/GL/Texture.h +++ b/src/Magnum/GL/Texture.h @@ -754,8 +754,10 @@ template class Texture: public AbstractTexture { * * Image parameters like format and type of pixel data are taken from * given image, image size is taken from the texture using - * @ref imageSize(). The storage is not reallocated if it is large - * enough to contain the new data. + * @ref imageSize(). 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 image(Int, BasicMutableImageView&) instead. * * If @gl_extension{ARB,direct_state_access} (part of OpenGL 4.5) is * not available, the texture is bound before the operation (if not @@ -785,6 +787,17 @@ template class Texture: public AbstractTexture { */ Image image(Int level, Image&& image); + /** + * @brief Read given texture mip level to an image view + * + * Compared to @ref image(Int, Image&) the function reads + * the pixels into the memory provided by @p image, expecting it's not + * @cpp nullptr @ce and its size is the same as size of given @p level. + */ + void image(Int level, BasicMutableImageView& image) { + AbstractTexture::image(level, image); + } + /** * @brief Read given texture mip level to a buffer image * @param level Mip level @@ -819,7 +832,10 @@ template class Texture: public AbstractTexture { * * Compression format and data size are taken from the texture, image * size is taken using @ref imageSize(). The storage is not reallocated - * if it is large enough to contain the new data. + * 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, BasicMutableCompressedImageView&) + * instead. * * If @gl_extension{ARB,direct_state_access} (part of OpenGL 4.5) is * not available, the texture is bound before the operation (if not @@ -853,6 +869,18 @@ template class Texture: public AbstractTexture { */ CompressedImage compressedImage(Int level, CompressedImage&& image); + /** + * @brief Read given compressed texture mip level to an image view + * + * Compared to @ref compressedImage(Int, CompressedImage&) + * the function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce, its format is the same as + * texture format and its size is the same as size of given @p level. + */ + void compressedImage(Int level, BasicMutableCompressedImageView& image) { + AbstractTexture::compressedImage(level, image); + } + /** * @brief Read given compressed texture mip level to a buffer image * @param level Mip level @@ -890,7 +918,10 @@ template class Texture: public AbstractTexture { * * Image parameters like format and type of pixel data are taken from * given image. The storage is not reallocated if it is large enough to - * contain the new data. + * contain the new data --- however if you want to read into existing + * memory or *ensure* a reallocation does not happen, use + * @ref subImage(Int, const RangeTypeFor&, BasicMutableImageView&) + * instead. * * The operation is protected from buffer overflow. * @see @fn_gl_keyword{GetTextureSubImage} @@ -910,6 +941,18 @@ template class Texture: public AbstractTexture { */ Image subImage(Int level, const RangeTypeFor& range, Image&& image); + /** + * @brief Read a range of given texture mip level to an image view + * + * Compared to @ref subImage(Int, const RangeTypeFor&, Image&) + * the function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce and its size is the same as + * @p range size. + */ + void subImage(Int level, const RangeTypeFor& range, BasicMutableImageView& image) { + AbstractTexture::subImage(level, range, image); + } + /** * @brief Read a range of given texture mip level to a buffer image * @param level Mip level @@ -943,7 +986,12 @@ template class 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. + * Compression format and data size are taken from the texture. 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&, BasicMutableCompressedImageView&) + * instead. * @see @fn_gl2{GetTextureLevelParameter,GetTexLevelParameter}, * eventually @fn_gl{GetTexLevelParameter} with * @def_gl{TEXTURE_INTERNAL_FORMAT}, then possibly @@ -974,6 +1022,18 @@ template class Texture: public AbstractTexture { */ CompressedImage compressedSubImage(Int level, const RangeTypeFor& range, CompressedImage&& image); + /** + * @brief Read a range of given compressed texture mip level to an image view + * + * Compared to @ref compressedSubImage(Int, const RangeTypeFor&, CompressedImage&) + * the function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce, its format is the same as + * texture format and its size is the same as @p range size. + */ + void compressedSubImage(Int level, const RangeTypeFor& range, BasicMutableCompressedImageView& image) { + AbstractTexture::compressedSubImage(level, range, image); + } + /** * @brief Read a range of given compressed texture mip level to a buffer image * @param level Mip level diff --git a/src/Magnum/GL/TextureArray.h b/src/Magnum/GL/TextureArray.h index faa48b7be..1cd461ce4 100644 --- a/src/Magnum/GL/TextureArray.h +++ b/src/Magnum/GL/TextureArray.h @@ -522,6 +522,17 @@ template class TextureArray: public AbstractTexture { */ Image image(Int level, Image&& image); + /** + * @brief Read given texture mip level to an image view + * + * Compared to @ref image(Int, Image&) the function reads + * the pixels into the memory provided by @p image, expecting it's not + * @cpp nullptr @ce and its size is the same as size of given @p level. + */ + void image(Int level, BasicMutableImageView& image) { + AbstractTexture::image(level, image); + } + /** * @brief Read given texture mip level to a buffer image * @@ -566,6 +577,18 @@ template class TextureArray: public AbstractTexture { */ CompressedImage compressedImage(Int level, CompressedImage&& image); + /** + * @brief Read given compressed texture mip level to an image view + * + * Compared to @ref compressedImage(Int, CompressedImage&) + * the function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce, its format is the same as + * texture format and its size is the same as size of given @p level. + */ + void compressedImage(Int level, BasicMutableCompressedImageView& image) { + AbstractTexture::compressedImage(level, image); + } + /** * @brief Read given compressed texture mip level to a buffer image * @@ -611,6 +634,18 @@ template class TextureArray: public AbstractTexture { */ Image subImage(Int level, const RangeTypeFor& range, Image&& image); + /** + * @brief Read a range of given texture mip level to an image view + * + * Compared to @ref subImage(Int, const RangeTypeFor&, Image&) + * the function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce and its size is the same as + * @p range size. + */ + void subImage(Int level, const RangeTypeFor& range, BasicMutableImageView& image) { + AbstractTexture::subImage(level, range, image); + } + /** * @brief Read a range of given texture mip level to a buffer image * @@ -661,6 +696,18 @@ template class TextureArray: public AbstractTexture { */ CompressedImage compressedSubImage(Int level, const RangeTypeFor& range, CompressedImage&& image); + /** + * @brief Read a range of given compressed texture mip level to an image view + * + * Compared to @ref compressedSubImage(Int, const RangeTypeFor&, CompressedImage&) + * the function reads the pixels into the memory provided by @p image, + * expecting it's not @cpp nullptr @ce, its format is the same as + * texture format and its size is the same as @p range size. + */ + void compressedSubImage(Int level, const RangeTypeFor& range, BasicMutableCompressedImageView& image) { + AbstractTexture::compressedSubImage(level, range, image); + } + /** * @brief Read a range of given compressed texture mip level to a buffer image *