From 98f979477c9bf1191909b7d9ed6fdbde9968ca9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 29 Aug 2015 19:21:52 +0200 Subject: [PATCH] Pixel storage support, part 9: reallocate image data only if needed. Makes it possible to update only some portion of an image without breaking the rest. --- src/Magnum/AbstractFramebuffer.cpp | 16 ++++++--- src/Magnum/AbstractFramebuffer.h | 6 ++-- src/Magnum/AbstractTexture.cpp | 40 +++++++++++++++++---- src/Magnum/CubeMapTexture.cpp | 56 +++++++++++++++++++++++++----- src/Magnum/CubeMapTexture.h | 29 +++++++++++----- src/Magnum/Texture.h | 22 ++++++++---- 6 files changed, 134 insertions(+), 35 deletions(-) diff --git a/src/Magnum/AbstractFramebuffer.cpp b/src/Magnum/AbstractFramebuffer.cpp index cccc9ca0f..c939b69e2 100644 --- a/src/Magnum/AbstractFramebuffer.cpp +++ b/src/Magnum/AbstractFramebuffer.cpp @@ -280,7 +280,13 @@ AbstractFramebuffer& AbstractFramebuffer::clear(const FramebufferClearMask mask) void AbstractFramebuffer::read(const Range2Di& rectangle, Image2D& image) { bindInternal(FramebufferTarget::Read); - Containers::Array data{Implementation::imageDataSizeFor(image, rectangle.size())}; + + /* Reallocate only if needed */ + const std::size_t dataSize = Implementation::imageDataSizeFor(image, rectangle.size()); + Containers::Array data{image.release()}; + if(data.size() < dataSize) + data = Containers::Array{dataSize}; + #ifndef MAGNUM_TARGET_GLES2 Buffer::unbindInternal(Buffer::TargetHint::PixelPack); #endif @@ -296,11 +302,13 @@ Image2D AbstractFramebuffer::read(const Range2Di& rectangle, Image2D&& image) { #ifndef MAGNUM_TARGET_GLES2 void AbstractFramebuffer::read(const Range2Di& rectangle, BufferImage2D& image, BufferUsage usage) { bindInternal(FramebufferTarget::Read); - /* If the buffer doesn't have sufficient size, resize it */ - /** @todo Explicitly reset also when buffer usage changes */ + + /* Reallocate only if needed */ const std::size_t dataSize = Implementation::imageDataSizeFor(image, rectangle.size()); - if(image.size() != rectangle.size()) + if(image.dataSize() < dataSize) image.setData(image.storage(), image.format(), image.type(), rectangle.size(), {nullptr, dataSize}, usage); + else + image.setData(image.storage(), image.format(), image.type(), rectangle.size(), nullptr, usage); image.buffer().bindInternal(Buffer::TargetHint::PixelPack); (Context::current()->state().framebuffer->readImplementation)(rectangle, image.format(), image.type(), dataSize, nullptr); diff --git a/src/Magnum/AbstractFramebuffer.h b/src/Magnum/AbstractFramebuffer.h index bfc1107ae..e0ec07038 100644 --- a/src/Magnum/AbstractFramebuffer.h +++ b/src/Magnum/AbstractFramebuffer.h @@ -314,7 +314,8 @@ class MAGNUM_EXPORT AbstractFramebuffer { * @param image Image where to put the data * * Image parameters like format and type of pixel data are taken from - * given image. + * given image. The storage is not reallocated if it is large enough to + * contain the new data. * * If @extension{ARB,robustness} is available, the operation is * protected from buffer overflow. @@ -350,7 +351,8 @@ class MAGNUM_EXPORT AbstractFramebuffer { * @param usage Buffer usage * * See @ref read(const Vector2i&, const Vector2i&, Image2D&) for more - * information. + * information. The storage is not reallocated if it is large enough to + * contain the new data, which means that @p usage might get ignored. * @requires_gles30 Pixel buffer objects are not available in OpenGL ES * 2.0. * @requires_webgl20 Pixel buffer objects are not available in WebGL diff --git a/src/Magnum/AbstractTexture.cpp b/src/Magnum/AbstractTexture.cpp index cac07b17f..2a614d176 100644 --- a/src/Magnum/AbstractTexture.cpp +++ b/src/Magnum/AbstractTexture.cpp @@ -1494,7 +1494,12 @@ void AbstractTexture::invalidateSubImageImplementationARB(GLint level, const Vec #ifndef MAGNUM_TARGET_GLES template void AbstractTexture::image(GLint level, Image& image) { const Math::Vector size = DataHelper::imageSize(*this, level); - Containers::Array data{Implementation::imageDataSizeFor(image, size)}; + const std::size_t dataSize = Implementation::imageDataSizeFor(image, size); + + /* Reallocate only if needed */ + Containers::Array data{image.release()}; + if(data.size() < dataSize) + data = Containers::Array{dataSize}; Buffer::unbindInternal(Buffer::TargetHint::PixelPack); (this->*Context::current()->state().texture->getImageImplementation)(level, image.format(), image.type(), data.size(), data); @@ -1508,8 +1513,12 @@ template void MAGNUM_EXPORT AbstractTexture::image<3>(GLint, Image<3>&); template void AbstractTexture::image(GLint level, BufferImage& image, BufferUsage usage) { const Math::Vector size = DataHelper::imageSize(*this, level); const std::size_t dataSize = Implementation::imageDataSizeFor(image, size); - if(image.size() != size) + + /* Reallocate only if needed */ + if(image.dataSize() < dataSize) image.setData(image.storage(), image.format(), image.type(), size, {nullptr, dataSize}, usage); + else + image.setData(image.storage(), image.format(), image.type(), size, nullptr, usage); image.buffer().bindInternal(Buffer::TargetHint::PixelPack); (this->*Context::current()->state().texture->getImageImplementation)(level, image.format(), image.type(), dataSize, nullptr); @@ -1523,9 +1532,14 @@ template void AbstractTexture::compressedImage(const GLi const Math::Vector size = DataHelper::imageSize(*this, level); GLint textureDataSize; (this->*Context::current()->state().texture->getLevelParameterivImplementation)(level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &textureDataSize); + const std::size_t dataSize = Implementation::compressedImageDataSizeFor(image, size, textureDataSize); GLint format; (this->*Context::current()->state().texture->getLevelParameterivImplementation)(level, GL_TEXTURE_INTERNAL_FORMAT, &format); - Containers::Array data{Implementation::compressedImageDataSizeFor(image, size, textureDataSize)}; + + /* Reallocate only if needed */ + Containers::Array data{image.release()}; + if(data.size() < dataSize) + data = Containers::Array{dataSize}; Buffer::unbindInternal(Buffer::TargetHint::PixelPack); (this->*Context::current()->state().texture->getCompressedImageImplementation)(level, data.size(), data); @@ -1544,7 +1558,12 @@ template void AbstractTexture::compressedImage(const GLi GLint format; (this->*Context::current()->state().texture->getLevelParameterivImplementation)(level, GL_TEXTURE_INTERNAL_FORMAT, &format); - image.setData(image.storage(), CompressedPixelFormat(format), size, {nullptr, dataSize}, usage); + /* Reallocate only if needed */ + if(image.dataSize() < dataSize) + image.setData(image.storage(), CompressedPixelFormat(format), size, {nullptr, dataSize}, usage); + else + image.setData(image.storage(), CompressedPixelFormat(format), size, nullptr, usage); + image.buffer().bindInternal(Buffer::TargetHint::PixelPack); (this->*Context::current()->state().texture->getCompressedImageImplementation)(level, dataSize, nullptr); } @@ -1557,9 +1576,14 @@ template void AbstractTexture::subImage(const GLint leve createIfNotAlready(); const Math::Vector size = range.size(); + const std::size_t dataSize = Implementation::imageDataSizeFor(image, size); const Vector3i paddedOffset = Vector3i::pad(range.min()); const Vector3i paddedSize = Vector3i::pad(size, 1); - Containers::Array data{Implementation::imageDataSizeFor(image, size)}; + + /* Reallocate only if needed */ + Containers::Array data{image.release()}; + if(data.size() < dataSize) + data = Containers::Array{dataSize}; Buffer::unbindInternal(Buffer::TargetHint::PixelPack); glGetTextureSubImage(_id, level, paddedOffset.x(), paddedOffset.y(), paddedOffset.z(), paddedSize.x(), paddedSize.y(), paddedSize.z(), GLenum(image.format()), GLenum(image.type()), data.size(), data); @@ -1577,8 +1601,12 @@ template void AbstractTexture::subImage(const GLint leve const std::size_t dataSize = Implementation::imageDataSizeFor(image, size); const Vector3i paddedOffset = Vector3i::pad(range.min()); const Vector3i paddedSize = Vector3i::pad(size, 1); - if(image.size() != size) + + /* Reallocate only if needed */ + if(image.dataSize() < dataSize) image.setData(image.storage(), image.format(), image.type(), size, {nullptr, dataSize}, usage); + else + image.setData(image.storage(), image.format(), image.type(), size, nullptr, usage); image.buffer().bindInternal(Buffer::TargetHint::PixelPack); glGetTextureSubImage(_id, level, paddedOffset.x(), paddedOffset.y(), paddedOffset.z(), paddedSize.x(), paddedSize.y(), paddedSize.z(), GLenum(image.format()), GLenum(image.type()), dataSize, nullptr); diff --git a/src/Magnum/CubeMapTexture.cpp b/src/Magnum/CubeMapTexture.cpp index 8a5c9cbe7..930be854b 100644 --- a/src/Magnum/CubeMapTexture.cpp +++ b/src/Magnum/CubeMapTexture.cpp @@ -60,7 +60,13 @@ void CubeMapTexture::image(const Int level, Image3D& image) { createIfNotAlready(); const Vector3i size{imageSize(level), 6}; - Containers::Array data{Implementation::imageDataSizeFor(image, size)}; + const std::size_t dataSize = Implementation::imageDataSizeFor(image, size); + + /* Reallocate only if needed */ + Containers::Array data{image.release()}; + if(data.size() < dataSize) + data = Containers::Array{dataSize}; + Buffer::unbindInternal(Buffer::TargetHint::PixelPack); glGetTextureImage(_id, level, GLenum(image.format()), GLenum(image.type()), data.size(), data); image.setData(image.storage(), image.format(), image.type(), size, std::move(data)); @@ -76,8 +82,12 @@ void CubeMapTexture::image(const Int level, BufferImage3D& image, const BufferUs const Vector3i size{imageSize(level), 6}; const std::size_t dataSize = Implementation::imageDataSizeFor(image, size); - if(image.size() != size) + + /* Reallocate only if needed */ + if(image.dataSize() < dataSize) image.setData(image.storage(), image.format(), image.type(), size, {nullptr, dataSize}, usage); + else + image.setData(image.storage(), image.format(), image.type(), size, nullptr, usage); image.buffer().bindInternal(Buffer::TargetHint::PixelPack); glGetTextureImage(_id, level, GLenum(image.format()), GLenum(image.type()), dataSize, nullptr); @@ -97,10 +107,14 @@ void CubeMapTexture::compressedImage(const Int level, CompressedImage3D& image) const std::size_t dataSize = Implementation::compressedImageDataSizeFor(image, size, textureDataSize); GLint format; (this->*Context::current()->state().texture->getLevelParameterivImplementation)(level, GL_TEXTURE_INTERNAL_FORMAT, &format); - Containers::Array data{dataSize}; + + /* Reallocate only if needed */ + Containers::Array data{image.release()}; + if(data.size() < dataSize) + data = Containers::Array{dataSize}; Buffer::unbindInternal(Buffer::TargetHint::PixelPack); - glGetCompressedTextureImage(_id, level, dataSize, data); + glGetCompressedTextureImage(_id, level, data.size(), data); image.setData(image.storage(), CompressedPixelFormat(format), size, std::move(data)); } @@ -119,8 +133,13 @@ void CubeMapTexture::compressedImage(const Int level, CompressedBufferImage3D& i GLint format; (this->*Context::current()->state().texture->getLevelParameterivImplementation)(level, GL_TEXTURE_INTERNAL_FORMAT, &format); + /* Reallocate only if needed */ + if(image.dataSize() < dataSize) + image.setData(image.storage(), CompressedPixelFormat(format), size, {nullptr, dataSize}, usage); + else + image.setData(image.storage(), CompressedPixelFormat(format), size, nullptr, usage); + image.buffer().bindInternal(Buffer::TargetHint::PixelPack); - image.setData(image.storage(), CompressedPixelFormat(format), size, {nullptr, dataSize}, usage); glGetCompressedTextureImage(_id, level, dataSize, nullptr); } @@ -131,7 +150,12 @@ CompressedBufferImage3D CubeMapTexture::compressedImage(const Int level, Compres void CubeMapTexture::image(const Coordinate coordinate, const Int level, Image2D& image) { const Vector2i size = imageSize(level); - Containers::Array data{Implementation::imageDataSizeFor(image, size)}; + const std::size_t dataSize = Implementation::imageDataSizeFor(image, size); + + /* Reallocate only if needed */ + Containers::Array data{image.release()}; + if(data.size() < dataSize) + data = Containers::Array{dataSize}; Buffer::unbindInternal(Buffer::TargetHint::PixelPack); (this->*Context::current()->state().texture->getCubeImageImplementation)(coordinate, level, size, image.format(), image.type(), data.size(), data); @@ -146,8 +170,12 @@ Image2D CubeMapTexture::image(const Coordinate coordinate, const Int level, Imag void CubeMapTexture::image(const Coordinate coordinate, const Int level, BufferImage2D& image, const BufferUsage usage) { const Vector2i size = imageSize(level); const std::size_t dataSize = Implementation::imageDataSizeFor(image, size); - if(image.size() != size) + + /* Reallocate only if needed */ + if(image.dataSize() < dataSize) image.setData(image.storage(), image.format(), image.type(), size, {nullptr, dataSize}, usage); + else + image.setData(image.storage(), image.format(), image.type(), size, nullptr, usage); image.buffer().bindInternal(Buffer::TargetHint::PixelPack); (this->*Context::current()->state().texture->getCubeImageImplementation)(coordinate, level, size, image.format(), image.type(), dataSize, nullptr); @@ -162,9 +190,14 @@ void CubeMapTexture::compressedImage(const Coordinate coordinate, const Int leve const Vector2i size = imageSize(level); GLint textureDataSize; (this->*Context::current()->state().texture->getLevelParameterivImplementation)(level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &textureDataSize); + const std::size_t dataSize = Implementation::compressedImageDataSizeFor(image, size, textureDataSize); GLint format; (this->*Context::current()->state().texture->getLevelParameterivImplementation)(level, GL_TEXTURE_INTERNAL_FORMAT, &format); - Containers::Array data{Implementation::compressedImageDataSizeFor(image, size, textureDataSize)}; + + /* Reallocate only if needed */ + Containers::Array data{image.release()}; + if(data.size() < dataSize) + data = Containers::Array{dataSize}; Buffer::unbindInternal(Buffer::TargetHint::PixelPack); (this->*Context::current()->state().texture->getCompressedCubeImageImplementation)(coordinate, level, size, data.size(), data); @@ -184,7 +217,12 @@ void CubeMapTexture::compressedImage(const Coordinate coordinate, const Int leve GLint format; (this->*Context::current()->state().texture->getLevelParameterivImplementation)(level, GL_TEXTURE_INTERNAL_FORMAT, &format); - image.setData(image.storage(), CompressedPixelFormat(format), size, {nullptr, dataSize}, usage); + /* Reallocate only if needed */ + if(image.dataSize() < dataSize) + image.setData(image.storage(), CompressedPixelFormat(format), size, {nullptr, dataSize}, usage); + else + image.setData(image.storage(), CompressedPixelFormat(format), size, nullptr, usage); + image.buffer().bindInternal(Buffer::TargetHint::PixelPack); (this->*Context::current()->state().texture->getCompressedCubeImageImplementation)(coordinate, level, size, dataSize, nullptr); } diff --git a/src/Magnum/CubeMapTexture.h b/src/Magnum/CubeMapTexture.h index 41363a989..da6152813 100644 --- a/src/Magnum/CubeMapTexture.h +++ b/src/Magnum/CubeMapTexture.h @@ -440,7 +440,10 @@ class MAGNUM_EXPORT CubeMapTexture: 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(). + * @ref imageSize(). The storage is not reallocated if it is large + * enough to contain the new data. + * + * The operation is protected from buffer overflow. * @see @fn_gl2{GetTextureLevelParameter,GetTexLevelParameter} with * @def_gl{TEXTURE_WIDTH}, @def_gl{TEXTURE_HEIGHT}, then * @fn_gl2{GetTextureImage,GetTexImage} @@ -462,7 +465,9 @@ class MAGNUM_EXPORT CubeMapTexture: public AbstractTexture { /** * @brief Read given mip level of texture to buffer image * - * See @ref image(Int, Image3D&) for more information. + * See @ref image(Int, Image3D&) for more information. The storage is + * not reallocated if it is large enough to contain the new data, which + * means that @p usage might get ignored. * @requires_gl45 Extension @extension{ARB,direct_state_access} * @requires_gl Texture image queries are not available in OpenGL ES or * WebGL. See @ref Framebuffer::read() for possible workaround. @@ -482,7 +487,8 @@ class MAGNUM_EXPORT CubeMapTexture: public AbstractTexture { * @brief Read given mip level of compressed texture to image * * Compression format and data size are taken from the texture, image - * size is taken using @ref imageSize(). + * size is taken using @ref imageSize(). The storage is not reallocated + * if it is large enough to contain the new data. * @see @fn_gl2{GetTextureLevelParameter,GetTexLevelParameter} with * @def_gl{TEXTURE_COMPRESSED_IMAGE_SIZE}, * @def_gl{TEXTURE_INTERNAL_FORMAT}, @def_gl{TEXTURE_WIDTH}, @@ -507,7 +513,8 @@ class MAGNUM_EXPORT CubeMapTexture: public AbstractTexture { * @brief Read given mip level of compressed texture to buffer image * * See @ref compressedImage(Int, CompressedImage3D&) for more - * information. + * information. The storage is not reallocated if it is large enough to + * contain the new data, which means that @p usage might get ignored. * @requires_gl45 Extension @extension{ARB,direct_state_access} * @requires_gl Texture image queries are not available in OpenGL ES or * WebGL. See @ref Framebuffer::read() for possible workaround. @@ -528,7 +535,8 @@ class MAGNUM_EXPORT CubeMapTexture: 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(). + * @ref imageSize(). The storage is not reallocated if it is large + * enough to contain the new data. * * If neither @extension{ARB,get_texture_sub_image} (part of OpenGL * 4.5) nor @extension{EXT,direct_state_access} is available, the @@ -564,7 +572,9 @@ class MAGNUM_EXPORT CubeMapTexture: public AbstractTexture { /** * @brief Read given mip level and coordinate of texture to buffer image * - * See @ref image(Coordinate, Int, Image2D&) for more information. + * See @ref image(Coordinate, Int, Image2D&) for more information. The + * storage is not reallocated if it is large enough to contain the new + * data, which means that @p usage might get ignored. * @requires_gl Texture image queries are not available in OpenGL ES or * WebGL. See @ref Framebuffer::read() for possible workaround. */ @@ -583,7 +593,8 @@ class MAGNUM_EXPORT CubeMapTexture: public AbstractTexture { * @brief Read given mip level and coordinate of compressed texture to image * * Compression format and data size are taken from the texture, image - * size is taken using @ref imageSize(). + * size is taken using @ref imageSize(). The storage is not reallocated + * if it is large enough to contain the new data. * * If neither @extension{ARB,get_texture_sub_image} (part of OpenGL * 4.5) nor @extension{EXT,direct_state_access} is available, the @@ -621,7 +632,9 @@ class MAGNUM_EXPORT CubeMapTexture: public AbstractTexture { * @brief Read given mip level and coordinate of compressed texture to buffer image * * See @ref compressedImage(Coordinate, Int, CompressedImage2D&) for - * more information. + * more information. The storage is not reallocated if it is large + * enough to contain the new data, which means that @p usage might get + * ignored. * @requires_gl Texture image queries are not available in OpenGL ES or * WebGL. See @ref Framebuffer::read() for possible workaround. */ diff --git a/src/Magnum/Texture.h b/src/Magnum/Texture.h index f02c5daed..dd0fe0f6d 100644 --- a/src/Magnum/Texture.h +++ b/src/Magnum/Texture.h @@ -637,7 +637,8 @@ 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(). + * @ref imageSize(). The storage is not reallocated if it is large + * enough to contain the new data. * * If neither @extension{ARB,direct_state_access} (part of OpenGL 4.5) * nor @extension{EXT,direct_state_access} is available, the texture is @@ -678,7 +679,9 @@ template class Texture: public AbstractTexture { * @param image Buffer image where to put the data * @param usage Buffer usage * - * See @ref image(Int, Image&) for more information. + * See @ref image(Int, Image&) for more information. The storage is not + * reallocated if it is large enough to contain the new data, which + * means that @p usage might get ignored. * @requires_gl Texture image queries are not available in OpenGL ES or * WebGL. See @ref Framebuffer::read() for possible workaround. * @todo Make it more flexible (usable with @@ -703,7 +706,8 @@ template class Texture: public AbstractTexture { * @param image Image where to put the compressed data * * Compression format and data size are taken from the texture, image - * size is taken using @ref imageSize(). + * size is taken using @ref imageSize(). The storage is not reallocated + * if it is large enough to contain the new data. * * If neither @extension{ARB,direct_state_access} (part of OpenGL 4.5) * nor @extension{EXT,direct_state_access} is available, the texture is @@ -746,7 +750,8 @@ template class Texture: public AbstractTexture { * @param usage Buffer usage * * See @ref compressedImage(Int, CompressedImage&) for more - * information. + * information. The storage is not reallocated if it is large enough to + * contain the new data, which means that @p usage might get ignored. * @requires_gl Texture image queries are not available in OpenGL ES or * WebGL. See @ref Framebuffer::read() for possible workaround. * @todo Make it more flexible (usable with @@ -772,7 +777,10 @@ template class Texture: public AbstractTexture { * @param image Image where to put the data * * Image parameters like format and type of pixel data are taken from - * given image. + * given image. The storage is not reallocated if it is large enough to + * contain the new data. + * + * The operation is protected from buffer overflow. * @see @fn_gl{GetTextureSubImage} * @requires_gl45 Extension @extension{ARB,get_texture_sub_image} * @requires_gl Texture image queries are not available in OpenGL ES or @@ -799,7 +807,9 @@ template class Texture: public AbstractTexture { * @param usage Buffer usage * * See @ref subImage(Int, const RangeTypeFor&, Image&) - * for more information. + * for more information. The storage is not reallocated if it is large + * enough to contain the new data, which means that @p usage might get + * ignored. * @requires_gl45 Extension @extension{ARB,get_texture_sub_image} * @requires_gl Texture image queries are not available in OpenGL ES or * WebGL. See @ref Framebuffer::read() for possible workaround.