diff --git a/src/AbstractTexture.cpp b/src/AbstractTexture.cpp index 2dee68180..7a71667fd 100644 --- a/src/AbstractTexture.cpp +++ b/src/AbstractTexture.cpp @@ -28,6 +28,7 @@ #include "BufferImage.h" #include "Context.h" #include "Extensions.h" +#include "Image.h" #include "Implementation/State.h" #include "Implementation/TextureState.h" @@ -56,6 +57,8 @@ AbstractTexture::Storage2DImplementation AbstractTexture::storage2DImplementatio AbstractTexture::Storage3DImplementation AbstractTexture::storage3DImplementation = &AbstractTexture::storageImplementationDefault; #ifndef MAGNUM_TARGET_GLES +AbstractTexture::GetImageImplementation AbstractTexture::getImageImplementation = + &AbstractTexture::getImageImplementationDefault; AbstractTexture::Image1DImplementation AbstractTexture::image1DImplementation = &AbstractTexture::imageImplementationDefault; #endif @@ -233,6 +236,7 @@ void AbstractTexture::initializeContextBasedFunctionality(Context* context) { storage1DImplementation = &AbstractTexture::storageImplementationDSA; storage2DImplementation = &AbstractTexture::storageImplementationDSA; storage3DImplementation = &AbstractTexture::storageImplementationDSA; + getImageImplementation = &AbstractTexture::getImageImplementationDSA; image1DImplementation = &AbstractTexture::imageImplementationDSA; image2DImplementation = &AbstractTexture::imageImplementationDSA; image3DImplementation = &AbstractTexture::imageImplementationDSA; @@ -247,6 +251,13 @@ void AbstractTexture::initializeContextBasedFunctionality(Context* context) { invalidateImageImplementation = &AbstractTexture::invalidateImageImplementationARB; invalidateSubImageImplementation = &AbstractTexture::invalidateSubImageImplementationARB; } + + if(context->isExtensionSupported() && + !context->isExtensionSupported()) { + Debug() << "AbstractTexture: using" << Extensions::GL::ARB::robustness::string() << "features"; + + getImageImplementation = &AbstractTexture::getImageImplementationRobustness; + } #endif } @@ -352,7 +363,35 @@ void AbstractTexture::storageImplementationDefault(GLenum target, GLsizei levels void AbstractTexture::storageImplementationDSA(GLenum target, GLsizei levels, AbstractTexture::InternalFormat internalFormat, const Vector3i& size) { glTextureStorage3DEXT(_id, target, levels, GLenum(internalFormat), size.x(), size.y(), size.z()); } +#endif + +#ifndef MAGNUM_TARGET_GLES +void AbstractTexture::getImageImplementationDefault(const GLenum target, const GLint level, const AbstractImage::Format format, const AbstractImage::Type type, const std::size_t, GLvoid* const data) { + bindInternal(); + glGetTexImage(target, level, GLenum(format), GLenum(type), data); +} + +void AbstractTexture::getImageImplementationDSA(const GLenum target, const GLint level, const AbstractImage::Format format, const AbstractImage::Type type, const std::size_t, GLvoid* const data) { + glGetTextureImageEXT(_id, target, level, GLenum(format), GLenum(type), data); +} + +void AbstractTexture::getImageImplementationRobustness(const GLenum target, const GLint level, const AbstractImage::Format format, const AbstractImage::Type type, const std::size_t dataSize, GLvoid* const data) { + #ifndef MAGNUM_TARGET_GLES + bindInternal(); + glGetnTexImageARB(target, level, GLenum(format), GLenum(type), dataSize, data); + #else + CORRADE_INTERNAL_ASSERT(false); + static_cast(target); + static_cast(level); + static_cast(format); + static_cast(type); + static_cast(dataSize); + static_cast(data); + #endif +} +#endif +#ifndef MAGNUM_TARGET_GLES void AbstractTexture::imageImplementationDefault(GLenum target, GLint level, InternalFormat internalFormat, const Math::Vector<1, GLsizei>& size, AbstractImage::Format format, AbstractImage::Type type, const GLvoid* data) { bindInternal(); glTexImage1D(target, level, static_cast(internalFormat), size[0], 0, static_cast(format), static_cast(type), data); @@ -456,6 +495,36 @@ void AbstractTexture::invalidateSubImageImplementationARB(GLint level, const Vec } #endif +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_TARGET_GLES +template void AbstractTexture::image(GLenum target, GLint level, Image* image) { + const Math::Vector size = DataHelper::imageSize(this, target, level); + const std::size_t dataSize = size.product()*image->pixelSize(); + char* data = new char[dataSize]; + (this->*getImageImplementation)(target, level, image->format(), image->type(), dataSize, data); + image->setData(size, image->format(), image->type(), data); +} + +template void AbstractTexture::image<1>(GLenum, GLint, Image<1>*); +template void AbstractTexture::image<2>(GLenum, GLint, Image<2>*); +template void AbstractTexture::image<3>(GLenum, GLint, Image<3>*); + +template void AbstractTexture::image(GLenum target, GLint level, BufferImage* image, Buffer::Usage usage) { + const Math::Vector size = DataHelper::imageSize(this, target, level); + const std::size_t dataSize = size.product()*image->pixelSize(); + if(image->size() != size) + image->setData(size, image->format(), image->type(), nullptr, usage); + + image->buffer()->bind(Buffer::Target::PixelPack); + (this->*getImageImplementation)(target, level, image->format(), image->type(), dataSize, nullptr); +} + +template void AbstractTexture::image<1>(GLenum, GLint, BufferImage<1>*, Buffer::Usage); +template void AbstractTexture::image<2>(GLenum, GLint, BufferImage<2>*, Buffer::Usage); +template void AbstractTexture::image<3>(GLenum, GLint, BufferImage<3>*, Buffer::Usage); +#endif +#endif + #ifndef DOXYGEN_GENERATING_OUTPUT #ifndef MAGNUM_TARGET_GLES2 namespace Implementation { diff --git a/src/AbstractTexture.h b/src/AbstractTexture.h index 468538fc7..bfa31a563 100644 --- a/src/AbstractTexture.h +++ b/src/AbstractTexture.h @@ -43,7 +43,7 @@ namespace Magnum { See Texture, CubeMapTexture and CubeMapTextureArray documentation for more information and usage examples. -@section AbstractTexture-performance-optimization Performance optimizations +@section AbstractTexture-performance-optimization Performance optimizations and security The engine tracks currently bound textures in all available layers to avoid unnecessary calls to @fn_gl{ActiveTexture} and @fn_gl{BindTexture}. %Texture @@ -58,6 +58,12 @@ configuration and data updating functions use DSA functions to avoid unnecessary calls to @fn_gl{ActiveTexture} and @fn_gl{BindTexture}. See respective function documentation for more information. +If extension @extension{ARB,robustness} is available, image reading operations +(such as Texture::image()) are protected from buffer overflow. However, if both +@extension{EXT,direct_state_access} and @extension{ARB,robustness} are +available, the DSA version is used, because it is better for performance and +there isn't any function combining both features. + To achieve least state changes, fully configure each texture in one run -- method chaining comes in handy -- and try to have often used textures in dedicated layers, not occupied by other textures. First configure the texture @@ -1168,6 +1174,11 @@ class MAGNUM_EXPORT AbstractTexture { /* Unlike bind() this also sets the binding layer as active */ void MAGNUM_LOCAL bindInternal(); + #ifndef MAGNUM_TARGET_GLES + template void MAGNUM_LOCAL image(GLenum target, GLint level, Image* image); + template void MAGNUM_LOCAL image(GLenum target, GLint level, BufferImage* image, Buffer::Usage usage); + #endif + GLenum _target; private: @@ -1236,6 +1247,14 @@ class MAGNUM_EXPORT AbstractTexture { #endif static Storage3DImplementation storage3DImplementation; + #ifndef MAGNUM_TARGET_GLES + typedef void(AbstractTexture::*GetImageImplementation)(GLenum, GLint, AbstractImage::Format, AbstractImage::Type, std::size_t, GLvoid*); + void MAGNUM_LOCAL getImageImplementationDefault(GLenum target, GLint level, AbstractImage::Format format, AbstractImage::Type type, std::size_t dataSize, GLvoid* data); + void MAGNUM_LOCAL getImageImplementationDSA(GLenum target, GLint level, AbstractImage::Format format, AbstractImage::Type type, std::size_t dataSize, GLvoid* data); + void MAGNUM_LOCAL getImageImplementationRobustness(GLenum target, GLint level, AbstractImage::Format format, AbstractImage::Type type, std::size_t dataSize, GLvoid* data); + static MAGNUM_LOCAL GetImageImplementation getImageImplementation; + #endif + #ifndef MAGNUM_TARGET_GLES typedef void(AbstractTexture::*Image1DImplementation)(GLenum, GLint, InternalFormat, const Math::Vector<1, GLsizei>&, AbstractImage::Format, AbstractImage::Type, const GLvoid*); void MAGNUM_LOCAL imageImplementationDefault(GLenum target, GLint level, InternalFormat internalFormat, const Math::Vector<1, GLsizei>& size, AbstractImage::Format format, AbstractImage::Type type, const GLvoid* data); diff --git a/src/CubeMapTexture.h b/src/CubeMapTexture.h index 979ececb9..2aecbb32c 100644 --- a/src/CubeMapTexture.h +++ b/src/CubeMapTexture.h @@ -129,6 +129,36 @@ class CubeMapTexture: public AbstractTexture { return this; } + #ifndef MAGNUM_TARGET_GLES + /** + * @brief Read given mip level of texture to image + * @param coordinate Coordinate + * @param level Mip level + * @param image %Image where to put the data + * + * See Texture::image(Int, Image*) for more information. + * @requires_gl %Texture image queries are not available in OpenGL ES. + */ + inline void image(Coordinate coordinate, Int level, Image2D* image) { + AbstractTexture::image<2>(GLenum(coordinate), level, image); + } + + /** + * @brief Read given mip level of texture to buffer image + * @param coordinate Coordinate + * @param level Mip level + * @param image %Buffer image where to put the data + * @param usage %Buffer usage + * + * See Texture::image(Int, BufferImage*, Buffer::Usage) for more + * information. + * @requires_gl %Texture image queries are not available in OpenGL ES. + */ + inline void image(Coordinate coordinate, Int level, BufferImage2D* image, Buffer::Usage usage) { + AbstractTexture::image<2>(GLenum(coordinate), level, image, usage); + } + #endif + /** * @brief Set image data * @param coordinate Coordinate diff --git a/src/CubeMapTextureArray.h b/src/CubeMapTextureArray.h index 8c439a672..0c0a65c6d 100644 --- a/src/CubeMapTextureArray.h +++ b/src/CubeMapTextureArray.h @@ -129,6 +129,36 @@ class CubeMapTextureArray: public AbstractTexture { return this; } + #ifndef MAGNUM_TARGET_GLES + /** + * @brief Read given mip level of texture to image + * @param coordinate Coordinate + * @param level Mip level + * @param image %Image where to put the data + * + * See Texture::image(Int, Image*) for more information. + * @requires_gl %Texture image queries are not available in OpenGL ES. + */ + inline void image(Coordinate coordinate, Int level, Image3D* image) { + AbstractTexture::image<3>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + GLenum(coordinate), level, image); + } + + /** + * @brief Read given mip level of texture to buffer image + * @param coordinate Coordinate + * @param level Mip level + * @param image %Buffer image where to put the data + * @param usage %Buffer usage + * + * See Texture::image(Int, BufferImage*, Buffer::Usage) for more + * information. + * @requires_gl %Texture image queries are not available in OpenGL ES. + */ + inline void image(Coordinate coordinate, Int level, BufferImage3D* image, Buffer::Usage usage) { + AbstractTexture::image<3>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + GLenum(coordinate), level, image, usage); + } + #endif + /** * @brief Set image data * @param level Mip level diff --git a/src/Texture.h b/src/Texture.h index 7530e0637..49f9be261 100644 --- a/src/Texture.h +++ b/src/Texture.h @@ -250,6 +250,47 @@ template class Texture: public AbstractTexture { return this; } + #ifndef MAGNUM_TARGET_GLES + /** + * @brief Read given mip level of texture to image + * @param level Mip level + * @param image %Image where to put the data + * + * %Image parameters like format and type of pixel data are taken from + * given image, image size is taken from the texture using imageSize(). + * + * If @extension{EXT,direct_state_access} is not available, the + * texture is bound to some layer before the operation. If + * @extension{ARB,robustness} is available, the operation is protected + * from buffer overflow. However, if both @extension{EXT,direct_state_access} + * and @extension{ARB,robustness} are available, the DSA version is + * used, because it is better for performance and there isn't any + * function combining both features. + * @requires_gl %Texture image queries are not available in OpenGL ES. + * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and + * @fn_gl{GetTexLevelParameter} or @fn_gl_extension{GetTextureLevelParameter,EXT,direct_state_access} + * with @def_gl{TEXTURE_WIDTH}, @def_gl{TEXTURE_HEIGHT} or @def_gl{TEXTURE_DEPTH}, + * then @fn_gl{GetTexImage}, @fn_gl_extension{GetTextureImage,EXT,direct_state_access} + * or @fn_gl_extension{GetnTexImage,ARB,robustness} + */ + inline void image(Int level, Image* image) { + AbstractTexture::image(_target, level, image); + } + + /** + * @brief Read given mip level of texture to buffer image + * @param level Mip level + * @param image %Buffer image where to put the data + * @param usage %Buffer usage + * + * See image(Int, Image*) for more information. + * @requires_gl %Texture image queries are not available in OpenGL ES. + */ + inline void image(Int level, BufferImage* image, Buffer::Usage usage) { + AbstractTexture::image(_target, level, image, usage); + } + #endif + /** * @brief Set image data * @param level Mip level