From d9b25c605790b076c6f8cded7dabaffe8f2f366f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 10 Jan 2016 23:00:49 +0100 Subject: [PATCH] Implemented ARB_shader_image_load_store. --- doc/opengl-mapping.dox | 2 +- doc/opengl-support.dox | 4 +- src/Magnum/AbstractShaderProgram.h | 13 +- src/Magnum/AbstractTexture.cpp | 56 ++++ src/Magnum/AbstractTexture.h | 92 ++++- src/Magnum/BufferTexture.h | 22 ++ src/Magnum/CMakeLists.txt | 1 + src/Magnum/CubeMapTexture.h | 49 +++ src/Magnum/CubeMapTextureArray.h | 47 +++ src/Magnum/ImageFormat.h | 315 ++++++++++++++++++ src/Magnum/Implementation/TextureState.cpp | 17 + src/Magnum/Implementation/TextureState.h | 4 + src/Magnum/Magnum.h | 5 + src/Magnum/MultisampleTexture.h | 76 +++++ src/Magnum/RectangleTexture.h | 19 ++ src/Magnum/Test/BufferTextureGLTest.cpp | 42 +++ src/Magnum/Test/CubeMapTextureArrayGLTest.cpp | 40 +++ src/Magnum/Test/CubeMapTextureGLTest.cpp | 44 +++ src/Magnum/Test/MultisampleTextureGLTest.cpp | 78 +++++ src/Magnum/Test/RectangleTextureGLTest.cpp | 28 ++ src/Magnum/Test/TextureArrayGLTest.cpp | 78 +++++ src/Magnum/Test/TextureGLTest.cpp | 109 ++++++ src/Magnum/Texture.h | 84 +++++ src/Magnum/TextureArray.h | 49 +++ 24 files changed, 1256 insertions(+), 18 deletions(-) create mode 100644 src/Magnum/ImageFormat.h diff --git a/doc/opengl-mapping.dox b/doc/opengl-mapping.dox index 78b4f62ea..998984a54 100644 --- a/doc/opengl-mapping.dox +++ b/doc/opengl-mapping.dox @@ -60,7 +60,7 @@ OpenGL function | Matching API @fn_gl{BindFragDataLocation} | @ref AbstractShaderProgram::bindFragmentDataLocation() @fn_gl{BindFragDataLocationIndexed} | @ref AbstractShaderProgram::bindFragmentDataLocationIndexed() @fn_gl{BindFramebuffer} | @ref Framebuffer::bind() -@fn_gl{BindImageTexture}, \n @fn_gl{BindImageTextures} | | +@fn_gl{BindImageTexture}, \n @fn_gl{BindImageTextures} | @ref AbstractTexture::unbindImage(), \n @ref AbstractTexture::unbindImages(), \n @ref AbstractTexture::bindImages(), \n @ref Texture::bindImage(), \n @ref Texture::bindImageLayered(), \n @ref TextureArray::bindImage(), \n @ref TextureArray::bindImageLayered(), \n @ref CubeMapTexture::bindImage(), \n @ref CubeMapTexture::bindImageLayered(), \n @ref CubeMapTextureArray::bindImage(), \n @ref CubeMapTextureArray::bindImageLayered(), \n @ref MultisampleTexture::bindImage(), \n @ref MultisampleTexture::bindImageLayered(), \n @ref RectangleTexture::bindImage(), \n @ref BufferTexture::bindImage() @fn_gl{BindProgramPipeline} | | @fn_gl{BindRenderbuffer} | not needed, handled internally in @ref Renderbuffer @fn_gl{BindSampler}, \n @fn_gl{BindSamplers} | | diff --git a/doc/opengl-support.dox b/doc/opengl-support.dox index decd80180..a82eb683e 100644 --- a/doc/opengl-support.dox +++ b/doc/opengl-support.dox @@ -161,7 +161,7 @@ GLSL 4.20 | done @extension{ARB,internalformat_query} | | @extension{ARB,map_buffer_alignment} | done @extension{ARB,shader_atomic_counters} | done -@extension{ARB,shader_image_load_store} | | +@extension{ARB,shader_image_load_store} | done @extension{ARB,shading_language_packing} | done (shading language only) @extension{ARB,texture_storage} | done @@ -205,7 +205,7 @@ GLSL 4.40 | done @extension{ARB,buffer_storage} | | @extension{ARB,clear_texture} | | @extension{ARB,enhanced_layouts} | done (shading language only) -@extension{ARB,multi_bind} | only texture and buffer binding +@extension{ARB,multi_bind} | missing sampler and vertex buffer binding @extension{ARB,query_buffer_object} | | @extension{ARB,texture_mirror_clamp_to_edge} | done @extension{ARB,texture_stencil8} | done diff --git a/src/Magnum/AbstractShaderProgram.h b/src/Magnum/AbstractShaderProgram.h index 4769aa735..9bad054c3 100644 --- a/src/Magnum/AbstractShaderProgram.h +++ b/src/Magnum/AbstractShaderProgram.h @@ -99,9 +99,10 @@ MyShader& setNormalMatrix(const Matrix3x3& matrix) { return *this; } @endcode -- **Texture setting functions** in which you bind the textures - to particular texture units using @ref Texture::bind() "*Texture::bind()" - and equivalents, for example: +- **Texture and texture image setting functions** in which you bind the + textures/images to particular texture/image units using + @ref Texture::bind() "*Texture::bind()" / + @ref Texture::bindImage() "*Texture::bindImage()" and similar, for example: @code MyShader& setDiffuseTexture(Texture2D& texture) { texture.bind(0); @@ -311,9 +312,9 @@ layout(std430, binding = 1) buffer normals { @requires_gles Shader storage is not available in WebGL. @anchor AbstractShaderProgram-texture-units -### Specifying texture binding units +### Specifying texture and image binding units -The preferred workflow is to specify texture binding unit directly in the +The preferred workflow is to specify texture/image binding unit directly in the shader code, e.g.: @code // GLSL 4.20, GLSL ES 3.10 or @@ -335,7 +336,7 @@ setUniform(uniformLocation("diffuseTexture"), 0); setUniform(uniformLocation("specularTexture"), 1); @endcode -@see @ref Shader::maxTextureImageUnits() +@see @ref Shader::maxTextureImageUnits(), @ref maxImageUnits() @requires_gl42 Extension @extension{ARB,shading_language_420pack} for explicit texture binding unit instead of using @ref setUniform(Int, const T&) "setUniform(Int, Int)". diff --git a/src/Magnum/AbstractTexture.cpp b/src/Magnum/AbstractTexture.cpp index 16cdd7c81..8e1acf5be 100644 --- a/src/Magnum/AbstractTexture.cpp +++ b/src/Magnum/AbstractTexture.cpp @@ -269,6 +269,62 @@ AbstractTexture& AbstractTexture::setLabelInternal(const Containers::ArrayView(textureState.imageBindings[imageUnit]) == 0) return; + + /* Update state tracker, bind the texture to the unit */ + std::get<0>(textureState.imageBindings[imageUnit]) = 0; + glBindImageTexture(imageUnit, 0, 0, false, 0, GL_READ_ONLY, GL_R8); +} + +#ifndef MAGNUM_TARGET_GLES +/** @todoc const Containers::ArrayView makes Doxygen grumpy */ +void AbstractTexture::bindImagesInternal(const Int firstImageUnit, Containers::ArrayView textures) { + Implementation::TextureState& textureState = *Context::current().state().texture; + + /* Create array of IDs and also update bindings in state tracker */ + Containers::Array ids{textures ? textures.size() : 0}; + bool different = false; + for(std::size_t i = 0; i != textures.size(); ++i) { + const std::tuple state = textures && textures[i] ? + std::tuple(textures[i]->_id, 0, true, 0, GL_READ_WRITE) : + std::tuple(0, 0, false, 0, GL_READ_ONLY); + + if(textures) { + if(textures[i]) { + textures[i]->createIfNotAlready(); + } + ids[i] = std::get<0>(state); + } + + if(textureState.imageBindings[firstImageUnit + i] != state) { + different = true; + textureState.imageBindings[firstImageUnit + i] = state; + } + } + + /* Avoid doing the binding if there is nothing different */ + if(different) glBindImageTextures(firstImageUnit, textures.size(), ids); +} +#endif + +void AbstractTexture::bindImageInternal(const Int imageUnit, const Int level, const bool layered, const Int layer, const ImageAccess access, const ImageFormat format) { + Implementation::TextureState& textureState = *Context::current().state().texture; + const std::tuple state{_id, level, layered, layer, GLenum(access)}; + + /* If already bound in given texture unit, nothing to do */ + if(textureState.imageBindings[imageUnit] == state) return; + + /* Update state tracker, bind the texture to the unit */ + textureState.imageBindings[imageUnit] = state; + glBindImageTexture(imageUnit, _id, level, layered, layer, GLenum(access), GLenum(format)); +} +#endif + void AbstractTexture::bind(Int textureUnit) { Implementation::TextureState& textureState = *Context::current().state().texture; diff --git a/src/Magnum/AbstractTexture.h b/src/Magnum/AbstractTexture.h index eea1ea3d4..72072a38d 100644 --- a/src/Magnum/AbstractTexture.h +++ b/src/Magnum/AbstractTexture.h @@ -69,13 +69,13 @@ documentation for details. @anchor AbstractTexture-performance-optimization ## Performance optimizations and security -The engine tracks currently bound textures in all available texture units to -avoid unnecessary calls to @fn_gl{ActiveTexture} and @fn_gl{BindTexture}. -Texture configuration functions use dedicated highest available texture unit -to not affect active bindings in user units. Texture limits and -implementation-defined values (such as @ref maxColorSamples()) are cached, so -repeated queries don't result in repeated @fn_gl{Get} calls. See also -@ref Context::resetState() and @ref Context::State::Textures. +The engine tracks currently bound textures and images in all available texture +units to avoid unnecessary calls to @fn_gl{ActiveTexture}, @fn_gl{BindTexture} +and @fn_gl{BindImageTexture}. Texture configuration functions use dedicated +highest available texture unit to not affect active bindings in user units. +Texture limits and implementation-defined values (such as @ref maxColorSamples()) +are cached, so repeated queries don't result in repeated @fn_gl{Get} calls. See +also @ref Context::resetState() and @ref Context::State::Textures. If @extension{ARB,direct_state_access} (part of OpenGL 4.5) is available, @ref bind(Int) and @ref unbind(Int) use @fn_gl{BindTextureUnit}. Otherwise, if @@ -209,13 +209,14 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { /** * @brief Unbind textures in given range of texture units * - * Unbinds all texture in the range @f$ [ firstTextureUnit ; firstTextureUnit + count ) @f$. + * Unbinds all textures in the range @f$ [ firstTextureUnit ; firstTextureUnit + count ) @f$. * If @extension{ARB,multi_bind} (part of OpenGL 4.4) is not available, * the feature is emulated with sequence of @ref unbind(Int) calls. * @note This function is meant to be used only internally from * @ref AbstractShaderProgram subclasses. See its documentation * for more information. - * @see @ref Shader::maxCombinedTextureImageUnits(), @fn_gl{BindTextures} + * @see @ref bind(), @ref Shader::maxCombinedTextureImageUnits(), + * @fn_gl{BindTextures} */ static void unbind(Int firstTextureUnit, std::size_t count); @@ -234,6 +235,71 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { */ static void bind(Int firstTextureUnit, std::initializer_list textures); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + /** + * @brief Unbind any image from given image unit + * + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref Texture::bindImage() "*Texture::bindImage()", + * @ref Texture::bindImageLayered() "*Texture::bindImageLayered()", + * @ref unbindImages(), @ref bindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_gles31 Shader image load/store is not available in OpenGL + * ES 3.0 and older. + * @requires_gles Shader image load/store is not available in WebGL. + */ + static void unbindImage(Int imageUnit); + #endif + + #ifndef MAGNUM_TARGET_GLES + /** + * @brief Unbind images in given range of image units + * + * Unbinds all texture in the range @f$ [ firstImageUnit ; firstImageUnit + count ) @f$. + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref Texture::bindImage() "*Texture::bindImage()", + * @ref Texture::bindImageLayered() "*Texture::bindImageLayered()", + * @ref unbindImage(), @ref bindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTextures} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_gl44 Extension @extension{ARB,multi_bind} + * @requires_gl Multi bind is not available in OpenGL ES and WebGL. + */ + static void unbindImages(Int firstImageUnit, std::size_t count) { + bindImagesInternal(firstImageUnit, {nullptr, count}); + } + + /** + * @brief Bind textures to given range of texture units + * + * Binds first level of given texture in the list to @p firstImageUnit, + * second to `firstTextureUnit + 1` etc. 3D, cube map and array + * textures are bound as layered targets. If any texture is `nullptr`, + * given image unit is unbound. + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref Texture::bindImage() "*Texture::bindImage()", + * @ref Texture::bindImageLayered() "*Texture::bindImageLayered()", + * @ref unbindImages(), @ref unbindImage(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTextures} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_gl44 Extension @extension{ARB,multi_bind} + * @requires_gl Multi bind is not available in OpenGL ES and WebGL. + */ + static void bindImages(Int firstImageUnit, std::initializer_list textures) { + bindImagesInternal(firstImageUnit, {textures.begin(), textures.size()}); + } + #endif + /** @brief Copying is not allowed */ AbstractTexture(const AbstractTexture&) = delete; @@ -342,6 +408,10 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { static Int compressedBlockDataSize(GLenum target, TextureFormat format); #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + static void bindImagesInternal(Int firstImageUnit, Containers::ArrayView textures); + #endif + explicit AbstractTexture(GLenum target); explicit AbstractTexture(NoCreateT, GLenum target) noexcept: _target{target}, _id{0}, _flags{ObjectFlag::DeleteOnDestruction} {} explicit AbstractTexture(GLuint id, GLenum target, ObjectFlags flags) noexcept: _target{target}, _id{id}, _flags{flags} {} @@ -352,6 +422,10 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { void MAGNUM_LOCAL createIfNotAlready(); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + void bindImageInternal(Int imageUnit, Int level, bool layered, Int layer, ImageAccess access, ImageFormat format); + #endif + /* Unlike bind() this also sets the texture binding unit as active */ void MAGNUM_LOCAL bindInternal(); diff --git a/src/Magnum/BufferTexture.h b/src/Magnum/BufferTexture.h index 41980de01..21fa4b645 100644 --- a/src/Magnum/BufferTexture.h +++ b/src/Magnum/BufferTexture.h @@ -157,6 +157,28 @@ class MAGNUM_EXPORT BufferTexture: public AbstractTexture { AbstractTexture{NoCreate, GL_TEXTURE_BUFFER_EXT} {} #endif + /** + * @brief Bind texture to given image unit + * @param imageUnit Image unit + * @param access Image access + * @param format Image format + * + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bindImages(Int, std::initializer_list), + * @ref unbindImage(), @ref unbindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_gles31 Shader image load/store is not available in OpenGL + * ES 3.0 and older. + * @requires_gles Shader image load/store is not available in WebGL. + */ + void bindImage(Int imageUnit, ImageAccess access, ImageFormat format) { + bindImageInternal(imageUnit, 0, false, 0, access, format); + } + /** * @brief Set texture buffer * @param internalFormat Internal format diff --git a/src/Magnum/CMakeLists.txt b/src/Magnum/CMakeLists.txt index 95e8f6f2f..e8afacd08 100644 --- a/src/Magnum/CMakeLists.txt +++ b/src/Magnum/CMakeLists.txt @@ -187,6 +187,7 @@ if(NOT TARGET_WEBGL) BufferTexture.h BufferTextureFormat.h CubeMapTextureArray.h + ImageFormat.h MultisampleTexture.h) endif() diff --git a/src/Magnum/CubeMapTexture.h b/src/Magnum/CubeMapTexture.h index 2ee70927a..ef1935657 100644 --- a/src/Magnum/CubeMapTexture.h +++ b/src/Magnum/CubeMapTexture.h @@ -166,6 +166,55 @@ class MAGNUM_EXPORT CubeMapTexture: public AbstractTexture { */ explicit CubeMapTexture(NoCreateT) noexcept: AbstractTexture{NoCreate, GL_TEXTURE_CUBE_MAP} {} + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + /** + * @brief Bind level of given cube map texture coordinate to given image unit + * @param imageUnit Image unit + * @param level Texture level + * @param coordinate Cube map coordinate + * @param access Image access + * @param format Image format + * + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bindImages(Int, std::initializer_list), + * @ref bindImageLayered(), @ref unbindImage(), @ref unbindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_gles31 Shader image load/store is not available in OpenGL + * ES 3.0 and older. + * @requires_gles Shader image load/store is not available in WebGL. + */ + void bindImage(Int imageUnit, Int level, Coordinate coordinate, ImageAccess access, ImageFormat format) { + bindImageInternal(imageUnit, level, false, GLenum(coordinate) - GL_TEXTURE_CUBE_MAP_POSITIVE_X, access, format); + } + + /** + * @brief Bind level of layered cube map texture to given image unit + * @param imageUnit Image unit + * @param level Texture level + * @param access Image access + * @param format Image format + * + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bindImages(Int, std::initializer_list), + * @ref bindImage(), @ref unbindImage(), @ref unbindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_gles31 Shader image load/store is not available in OpenGL + * ES 3.0 and older. + * @requires_gles Shader image load/store is not available in WebGL. + */ + void bindImageLayered(Int imageUnit, Int level, ImageAccess access, ImageFormat format) { + bindImageInternal(imageUnit, level, true, 0, access, format); + } + #endif + #ifndef MAGNUM_TARGET_GLES2 /** * @copybrief Texture::setBaseLevel() diff --git a/src/Magnum/CubeMapTextureArray.h b/src/Magnum/CubeMapTextureArray.h index a78abb21b..a3e02aefd 100644 --- a/src/Magnum/CubeMapTextureArray.h +++ b/src/Magnum/CubeMapTextureArray.h @@ -173,6 +173,53 @@ class MAGNUM_EXPORT CubeMapTextureArray: public AbstractTexture { AbstractTexture{NoCreate, GL_TEXTURE_CUBE_MAP_ARRAY_EXT} {} #endif + /** + * @brief Bind level of given texture layer to given image unit + * @param imageUnit Image unit + * @param level Texture level + * @param layer Texture layer + * @param access Image access + * @param format Image format + * + * Layer is equivalent to layer * 6 + number of texture face, i.e. +X + * is `0` and so on, in order of (+X, -X, +Y, -Y, +Z, -Z). + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bindImages(Int, std::initializer_list), + * @ref bindImageLayered(), @ref unbindImage(), @ref unbindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_gles31 Shader image load/store is not available in OpenGL + * ES 3.0 and older. + */ + void bindImage(Int imageUnit, Int level, Int layer, ImageAccess access, ImageFormat format) { + bindImageInternal(imageUnit, level, false, layer, access, format); + } + + /** + * @brief Bind level of layered texture to given image unit + * @param imageUnit Image unit + * @param level Texture level + * @param access Image access + * @param format Image format + * + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bindImages(Int, std::initializer_list), + * @ref bindImage(), @ref unbindImage(), @ref unbindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_gles31 Shader image load/store is not available in OpenGL + * ES 3.0 and older. + */ + void bindImageLayered(Int imageUnit, Int level, ImageAccess access, ImageFormat format) { + bindImageInternal(imageUnit, level, true, 0, access, format); + } + /** * @copybrief Texture::setBaseLevel() * @return Reference to self (for method chaining) diff --git a/src/Magnum/ImageFormat.h b/src/Magnum/ImageFormat.h new file mode 100644 index 000000000..dd8b0f5a6 --- /dev/null +++ b/src/Magnum/ImageFormat.h @@ -0,0 +1,315 @@ +#ifndef Magnum_ImageFormat_h +#define Magnum_ImageFormat_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) +/** @file + * @brief Enum @ref Magnum::ImageAccess, @ref Magnum::ImageFormat + */ +#endif + +#include + +#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) +namespace Magnum { + +/** +@brief Image access + +@see @ref Texture::bindImage() "*Texture::bindImage()", + @ref Texture::bindImageLayered() "*Texture::bindImageLayered()" +@requires_gl42 Extension @extension{ARB,shader_image_load_store} +@requires_gles31 Shader image load/store is not available in OpenGL ES 3.0 and + older. +@requires_gles Shader image load/store is not available in WebGL. +*/ +enum class ImageAccess: GLenum { + ReadOnly = GL_READ_ONLY, + WriteOnly = GL_WRITE_ONLY, + ReadWrite = GL_READ_WRITE +}; + +/** +@brief Image format + +@see @ref Texture::bindImage() "*Texture::bindImage()", + @ref Texture::bindImageLayered() "*Texture::bindImageLayered()" +@requires_gl42 Extension @extension{ARB,shader_image_load_store} +@requires_gles31 Shader image load/store is not available in OpenGL ES 3.0 and + older. +@requires_gles Shader image load/store is not available in WebGL. +*/ +enum class ImageFormat: GLenum { + #ifndef MAGNUM_TARGET_GLES + /** + * Red component, normalized unsigned byte. + * @requires_gl Only four-component byte image formats are available in + * OpenGL ES. + */ + R8 = GL_R8, + + /** + * Red and green component, each normalized unsigned byte. + * @requires_gl Only four-component byte image formats are available in + * OpenGL ES. + */ + RG8 = GL_RG8, + #endif + + /** RGBA, each component normalized unsigned byte. */ + RGBA8 = GL_RGBA8, + + #ifndef MAGNUM_TARGET_GLES + /** + * Red component, normalized signed byte. + * @requires_gl Only four-component byte image formats are available in + * OpenGL ES. + */ + R8Snorm = GL_R8_SNORM, + + /** + * Red and green component, each normalized signed byte. + * @requires_gl Only four-component byte image formats are available in + * OpenGL ES. + */ + RG8Snorm = GL_RG8_SNORM, + #endif + + /** RGBA, each component normalized unsigned byte. */ + RGBA8Snorm = GL_RGBA8_SNORM, + + #ifndef MAGNUM_TARGET_GLES + /** + * Red component, normalized unsigned short. + * @requires_gl Only byte-size normalized image formats are available in + * OpenGL ES. + */ + R16 = GL_R16, + + /** + * Red and green component, each normalized unsigned short. + * @requires_gl Only byte-size normalized image formats are available in + * OpenGL ES. + */ + RG16 = GL_RG16, + + /** + * RGBA, each component normalized unsigned short. + * @requires_gl Only byte-size normalized image formats are available in + * OpenGL ES. + */ + RGBA16 = GL_RGBA16, + + /** + * Red component, normalized signed short. + * @requires_gl Only byte-size normalized image formats are available in + * OpenGL ES. + */ + R16Snorm = GL_R16_SNORM, + + /** + * Red and green component, each normalized signed short. + * @requires_gl Only byte-size normalized image formats are available in + * OpenGL ES. + */ + RG16Snorm = GL_RG16_SNORM, + + /** + * RGBA, each component normalized unsigned short. + * @requires_gl Only byte-size normalized image formats are available in + * OpenGL ES. + */ + RGBA16Snorm = GL_RGBA16_SNORM, + #endif + + #ifndef MAGNUM_TARGET_GLES + /** + * Red component, non-normalized unsigned byte. + * @requires_gl Only four-component byte image formats are available in + * OpenGL ES. + */ + R8UI = GL_R8UI, + + /** + * Red and green component, each non-normalized unsigned byte. + * @requires_gl Only four-component byte image formats are available in + * OpenGL ES. + */ + RG8UI = GL_RG8UI, + #endif + + /** RGBA, each component non-normalized unsigned byte. */ + RGBA8UI = GL_RGBA8UI, + + #ifndef MAGNUM_TARGET_GLES + /** + * Red component, non-normalized signed byte. + * @requires_gl Only four-component byte image formats are available in + * OpenGL ES. + */ + R8I = GL_R8I, + + /** + * Red and green component, each non-normalized signed byte. + * @requires_gl Only four-component byte image formats are available in + * OpenGL ES. + */ + RG8I = GL_RG8I, + #endif + + /** RGBA, each component non-normalized signed byte. */ + RGBA8I = GL_RGBA8I, + + #ifndef MAGNUM_TARGET_GLES + /** + * Red component, non-normalized unsigned short. + * @requires_gl Only four-component short image formats are available in + * OpenGL ES. + */ + R16UI = GL_R16UI, + + /** + * Red and green component, each non-normalized unsigned short. + * @requires_gl Only four-component short image formats are available in + * OpenGL ES. + */ + RG16UI = GL_RG16UI, + #endif + + /** RGBA, each component non-normalized unsigned short. */ + RGBA16UI = GL_RGBA16UI, + + #ifndef MAGNUM_TARGET_GLES + /** + * Red component, non-normalized signed short. + * @requires_gl Only four-component short image formats are available in + * OpenGL ES. + */ + R16I = GL_R16I, + + /** + * Red and green component, each non-normalized signed short. + * @requires_gl Only four-component short image formats are available in + * OpenGL ES. + */ + RG16I = GL_RG16I, + #endif + + /** RGBA, each component non-normalized signed short. */ + RGBA16I = GL_RGBA16I, + + /** Red component, non-normalized unsigned int. */ + R32UI = GL_R32UI, + + #ifndef MAGNUM_TARGET_GLES + /** + * Red and green component, each non-normalized unsigned int. + * @requires_gl Only one- or four-component int image formats are available + * in OpenGL ES. + */ + RG32UI = GL_RG32UI, + #endif + + /** RGBA, each component non-normalized unsigned int. */ + RGBA32UI = GL_RGBA32UI, + + /** Red component, non-normalized signed int. */ + R32I = GL_R32I, + + #ifndef MAGNUM_TARGET_GLES + /** + * Red and green component, each non-normalized signed int. + * @requires_gl Only one- or four-component int image formats are available + * in OpenGL ES. + */ + RG32I = GL_RG32I, + #endif + + /** RGBA, each component non-normalized signed int. */ + RGBA32I = GL_RGBA32I, + + #ifndef MAGNUM_TARGET_GLES + /** + * Red component, half float. + * @requires_gl Only four-component half float image formats are available + * in OpenGL ES. + */ + R16F = GL_R16F, + + /** + * Red and green component, each half float. + * @requires_gl Only four-component half float image formats are available + * in OpenGL ES. + */ + RG16F = GL_RG16F, + #endif + + /** RGBA, each component half float. */ + RGBA16F = GL_RGBA16F, + + /** Red component, float. */ + R32F = GL_R32F, + + #ifndef MAGNUM_TARGET_GLES + /** + * Red and green component, each float. + * @requires_gl Only one- or four-component float image formats are + * available in OpenGL ES. + */ + RG32F = GL_RG32F, + #endif + + /** RGBA, each component float. */ + RGBA32F = GL_RGBA32F, + + #ifndef MAGNUM_TARGET_GLES + /** + * RGB, float, red and green component 11bit, blue 10bit. + * @requires_gl Packed image formats are not available in OpenGL ES. + */ + R11FG11FB10F = GL_R11F_G11F_B10F, + + /** + * RGBA, normalized unsigned, each RGB component 10bit, alpha 2bit. + * @requires_gl Packed image formats are not available in OpenGL ES. + */ + RGB10A2 = GL_RGB10_A2, + + /** + * RGBA, non-normalized unsigned, each RGB component 10bit, alpha 2bit. + * @requires_gl33 Extension @extension{ARB,texture_rgb10_a2ui} + * @requires_gl Packed image formats are not available in OpenGL ES. + */ + RGB10A2UI = GL_RGB10_A2UI, + #endif +}; + +} +#else +#error this header is not available in OpenGL ES 2.0 and WebGL build +#endif + +#endif diff --git a/src/Magnum/Implementation/TextureState.cpp b/src/Magnum/Implementation/TextureState.cpp index bc813149c..663e70771 100644 --- a/src/Magnum/Implementation/TextureState.cpp +++ b/src/Magnum/Implementation/TextureState.cpp @@ -387,12 +387,29 @@ TextureState::TextureState(Context& context, std::vector& extension glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); CORRADE_INTERNAL_ASSERT(maxTextureUnits > 0); bindings = Containers::Array>{Containers::ValueInit, std::size_t(maxTextureUnits)}; + + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + /* Allocate image bindings array to hold all possible image units */ + #ifndef MAGNUM_TARGET_GLES + if(context.isExtensionSupported()) + #else + if(context.isVersionSupported(Version::GLES310)) + #endif + { + GLint maxImageUnits; + glGetIntegerv(GL_MAX_IMAGE_UNITS, &maxImageUnits); + imageBindings = Containers::Array>{Containers::ValueInit, std::size_t(maxImageUnits)}; + } + #endif } TextureState::~TextureState() = default; void TextureState::reset() { std::fill_n(bindings.begin(), bindings.size(), std::pair{{}, State::DisengagedBinding}); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + std::fill_n(imageBindings.begin(), imageBindings.size(), std::tuple{State::DisengagedBinding, 0, false, 0, 0}); + #endif } }} diff --git a/src/Magnum/Implementation/TextureState.h b/src/Magnum/Implementation/TextureState.h index 10c0fcfc0..d6e637ec2 100644 --- a/src/Magnum/Implementation/TextureState.h +++ b/src/Magnum/Implementation/TextureState.h @@ -27,6 +27,7 @@ #include #include +#include #include "Magnum/CubeMapTexture.h" @@ -140,6 +141,9 @@ struct TextureState { #endif Containers::Array> bindings; + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + Containers::Array> imageBindings; + #endif }; }} diff --git a/src/Magnum/Magnum.h b/src/Magnum/Magnum.h index 2648e0f7f..fdd7e1ab0 100644 --- a/src/Magnum/Magnum.h +++ b/src/Magnum/Magnum.h @@ -486,6 +486,11 @@ class CubeMapTextureArray; class Extension; class Framebuffer; +#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) +enum class ImageFormat: GLenum; +enum class ImageAccess: GLenum; +#endif + template class Image; typedef Image<1> Image1D; typedef Image<2> Image2D; diff --git a/src/Magnum/MultisampleTexture.h b/src/Magnum/MultisampleTexture.h index d9d42e3d8..6375a9da4 100644 --- a/src/Magnum/MultisampleTexture.h +++ b/src/Magnum/MultisampleTexture.h @@ -160,6 +160,82 @@ template class MultisampleTexture: public AbstractTextur */ explicit MultisampleTexture(NoCreateT) noexcept: AbstractTexture{NoCreate, Implementation::multisampleTextureTarget()} {} + /** + * @brief Bind texture to given image unit + * @param imageUnit Image unit + * @param access Image access + * @param format Image format + * + * Available only on 2D multisample textures. + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bindImages(Int, std::initializer_list), + * @ref bindImageLayered(), @ref unbindImage(), @ref unbindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template::type> + #endif + void bindImage(Int imageUnit, ImageAccess access, ImageFormat format) { + bindImageInternal(imageUnit, 0, false, 0, access, format); + } + + /** + * @brief Bind texture layer to given image unit + * @param imageUnit Image unit + * @param layer Texture layer + * @param access Image access + * @param format Image format + * + * Available only on 2D multisample texture arrays. + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bindImages(Int, std::initializer_list), + * @ref bindImageLayered(), @ref unbindImage(), @ref unbindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_es_extension Extension @es_extension{ANDROID,extension_pack_es31a}/ + * @es_extension{OES,texture_storage_multisample_2d_array} for + * multisample 2D array textures. + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template::type> + #endif + void bindImage(Int imageUnit, Int layer, ImageAccess access, ImageFormat format) { + bindImageInternal(imageUnit, 0, false, layer, access, format); + } + + /** + * @brief Bind layered texture to given image unit + * @param imageUnit Image unit + * @param access Image access + * @param format Image format + * + * Available only on 2D multisample texture arrays. + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bindImages(Int, std::initializer_list), + * @ref bindImage(), @ref unbindImages(), @ref unbindImage(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_es_extension Extension @es_extension{ANDROID,extension_pack_es31a}/ + * @es_extension{OES,texture_storage_multisample_2d_array} for + * multisample 2D array textures. + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template::type> + #endif + void bindImageLayered(Int imageUnit, ImageAccess access, ImageFormat format) { + bindImageInternal(imageUnit, 0, true, 0, access, format); + } + /** * @brief Set storage * @param samples Sample count diff --git a/src/Magnum/RectangleTexture.h b/src/Magnum/RectangleTexture.h index 11db9875e..94544818d 100644 --- a/src/Magnum/RectangleTexture.h +++ b/src/Magnum/RectangleTexture.h @@ -139,6 +139,25 @@ class MAGNUM_EXPORT RectangleTexture: public AbstractTexture { */ explicit RectangleTexture(NoCreateT) noexcept: AbstractTexture{NoCreate, GL_TEXTURE_RECTANGLE} {} + /** + * @brief Bind texture to given image unit + * @param imageUnit Image unit + * @param access Image access + * @param format Image format + * + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bindImages(Int, std::initializer_list), + * @ref unbindImage(), @ref unbindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + */ + void bindImage(Int imageUnit, ImageAccess access, ImageFormat format) { + bindImageInternal(imageUnit, 0, false, 0, access, format); + } + /** * @copybrief Texture::setMinificationFilter() * @return Reference to self (for method chaining) diff --git a/src/Magnum/Test/BufferTextureGLTest.cpp b/src/Magnum/Test/BufferTextureGLTest.cpp index 16d7484a8..c4eda943d 100644 --- a/src/Magnum/Test/BufferTextureGLTest.cpp +++ b/src/Magnum/Test/BufferTextureGLTest.cpp @@ -28,6 +28,7 @@ #include "Magnum/Buffer.h" #include "Magnum/BufferTexture.h" #include "Magnum/BufferTextureFormat.h" +#include "Magnum/ImageFormat.h" #include "Magnum/Test/AbstractOpenGLTester.h" namespace Magnum { namespace Test { @@ -40,6 +41,8 @@ struct BufferTextureGLTest: AbstractOpenGLTester { void wrap(); void bind(); + void bindImage(); + void setBuffer(); void setBufferOffset(); }; @@ -50,6 +53,8 @@ BufferTextureGLTest::BufferTextureGLTest() { &BufferTextureGLTest::wrap, &BufferTextureGLTest::bind, + &BufferTextureGLTest::bindImage, + &BufferTextureGLTest::setBuffer, &BufferTextureGLTest::setBufferOffset}); } @@ -134,6 +139,43 @@ void BufferTextureGLTest::bind() { MAGNUM_VERIFY_NO_ERROR(); } +void BufferTextureGLTest::bindImage() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::texture_buffer_object::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::shader_image_load_store::string() + std::string(" is not supported.")); + #else + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::texture_buffer::string() + std::string(" is not supported.")); + if(!Context::current().isVersionSupported(Version::GLES310)) + CORRADE_SKIP("OpenGL ES 3.1 is not supported."); + #endif + + Buffer buffer; + buffer.setData({nullptr, 32}, BufferUsage::StaticDraw); + + BufferTexture texture; + texture.setBuffer(BufferTextureFormat::RGBA8, buffer) + .bindImage(2, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImage(2); + + MAGNUM_VERIFY_NO_ERROR(); + + #ifndef MAGNUM_TARGET_GLES + AbstractTexture::bindImages(1, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImages(1, 3); + + MAGNUM_VERIFY_NO_ERROR(); + #endif +} + void BufferTextureGLTest::setBuffer() { #ifndef MAGNUM_TARGET_GLES if(!Context::current().isExtensionSupported()) diff --git a/src/Magnum/Test/CubeMapTextureArrayGLTest.cpp b/src/Magnum/Test/CubeMapTextureArrayGLTest.cpp index e449cc465..589a799c9 100644 --- a/src/Magnum/Test/CubeMapTextureArrayGLTest.cpp +++ b/src/Magnum/Test/CubeMapTextureArrayGLTest.cpp @@ -28,6 +28,7 @@ #include "Magnum/BufferImage.h" #include "Magnum/CubeMapTextureArray.h" #include "Magnum/Image.h" +#include "Magnum/ImageFormat.h" #include "Magnum/PixelFormat.h" #include "Magnum/TextureFormat.h" #include "Magnum/Math/Color.h" @@ -44,6 +45,7 @@ struct CubeMapTextureArrayGLTest: AbstractOpenGLTester { void wrap(); void bind(); + void bindImage(); void sampling(); void samplingSRGBDecode(); @@ -89,6 +91,7 @@ CubeMapTextureArrayGLTest::CubeMapTextureArrayGLTest() { &CubeMapTextureArrayGLTest::wrap, &CubeMapTextureArrayGLTest::bind, + &CubeMapTextureArrayGLTest::bindImage, &CubeMapTextureArrayGLTest::sampling, &CubeMapTextureArrayGLTest::samplingSRGBDecode, @@ -226,6 +229,43 @@ void CubeMapTextureArrayGLTest::bind() { MAGNUM_VERIFY_NO_ERROR(); } +void CubeMapTextureArrayGLTest::bindImage() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::texture_cube_map_array::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::shader_image_load_store::string() + std::string(" is not supported.")); + #else + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::texture_cube_map_array::string() + std::string(" is not supported.")); + if(!Context::current().isVersionSupported(Version::GLES310)) + CORRADE_SKIP("OpenGL ES 3.1 is not supported."); + #endif + + CubeMapTextureArray texture; + texture.setStorage(1, TextureFormat::RGBA8, {32, 32, 12}) + .bindImage(2, 0, 1, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + MAGNUM_VERIFY_NO_ERROR(); + + texture.bindImageLayered(3, 0, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + AbstractTexture::unbindImage(2); + AbstractTexture::unbindImage(3); + + MAGNUM_VERIFY_NO_ERROR(); + + #ifndef MAGNUM_TARGET_GLES + AbstractTexture::bindImages(1, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImages(1, 3); + + MAGNUM_VERIFY_NO_ERROR(); + #endif +} + void CubeMapTextureArrayGLTest::sampling() { #ifndef MAGNUM_TARGET_GLES if(!Context::current().isExtensionSupported()) diff --git a/src/Magnum/Test/CubeMapTextureGLTest.cpp b/src/Magnum/Test/CubeMapTextureGLTest.cpp index 6b3b68fac..b142621b8 100644 --- a/src/Magnum/Test/CubeMapTextureGLTest.cpp +++ b/src/Magnum/Test/CubeMapTextureGLTest.cpp @@ -31,6 +31,9 @@ #endif #include "Magnum/CubeMapTexture.h" #include "Magnum/Image.h" +#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) +#include "Magnum/ImageFormat.h" +#endif #include "Magnum/PixelFormat.h" #include "Magnum/TextureFormat.h" #include "Magnum/Math/Color.h" @@ -47,6 +50,9 @@ struct CubeMapTextureGLTest: AbstractOpenGLTester { void wrap(); void bind(); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + void bindImage(); + #endif void sampling(); void samplingSRGBDecode(); @@ -116,6 +122,9 @@ CubeMapTextureGLTest::CubeMapTextureGLTest() { &CubeMapTextureGLTest::wrap, &CubeMapTextureGLTest::bind, + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + &CubeMapTextureGLTest::bindImage, + #endif &CubeMapTextureGLTest::sampling, &CubeMapTextureGLTest::samplingSRGBDecode, @@ -268,6 +277,41 @@ void CubeMapTextureGLTest::bind() { MAGNUM_VERIFY_NO_ERROR(); } +#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) +void CubeMapTextureGLTest::bindImage() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::shader_image_load_store::string() + std::string(" is not supported.")); + #else + if(!Context::current().isVersionSupported(Version::GLES310)) + CORRADE_SKIP("OpenGL ES 3.1 is not supported."); + #endif + + CubeMapTexture texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{32}) + .bindImage(2, 0, CubeMapTexture::Coordinate::NegativeX, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + MAGNUM_VERIFY_NO_ERROR(); + + texture.bindImageLayered(3, 0, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + AbstractTexture::unbindImage(2); + AbstractTexture::unbindImage(3); + + MAGNUM_VERIFY_NO_ERROR(); + + #ifndef MAGNUM_TARGET_GLES + AbstractTexture::bindImages(1, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImages(1, 3); + + MAGNUM_VERIFY_NO_ERROR(); + #endif +} +#endif + void CubeMapTextureGLTest::sampling() { CubeMapTexture texture; texture.setMinificationFilter(Sampler::Filter::Linear, Sampler::Mipmap::Linear) diff --git a/src/Magnum/Test/MultisampleTextureGLTest.cpp b/src/Magnum/Test/MultisampleTextureGLTest.cpp index 1439c2306..131b0397e 100644 --- a/src/Magnum/Test/MultisampleTextureGLTest.cpp +++ b/src/Magnum/Test/MultisampleTextureGLTest.cpp @@ -26,6 +26,7 @@ #include #include "Magnum/MultisampleTexture.h" +#include "Magnum/ImageFormat.h" #include "Magnum/TextureFormat.h" #include "Magnum/Math/Vector3.h" #include "Magnum/Test/AbstractOpenGLTester.h" @@ -47,6 +48,9 @@ struct MultisampleTextureGLTest: AbstractOpenGLTester { void bind2D(); void bind2DArray(); + void bindImage2D(); + void bindImage2DArray(); + void storage2D(); void storage2DArray(); @@ -70,6 +74,9 @@ MultisampleTextureGLTest::MultisampleTextureGLTest() { &MultisampleTextureGLTest::bind2D, &MultisampleTextureGLTest::bind2DArray, + &MultisampleTextureGLTest::bindImage2D, + &MultisampleTextureGLTest::bindImage2DArray, + &MultisampleTextureGLTest::storage2D, &MultisampleTextureGLTest::storage2DArray, @@ -240,6 +247,77 @@ void MultisampleTextureGLTest::bind2DArray() { MAGNUM_VERIFY_NO_ERROR(); } +void MultisampleTextureGLTest::bindImage2D() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::shader_image_load_store::string() + std::string(" is not supported.")); + #else + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::OES::texture_storage_multisample_2d_array::string() + std::string(" is not supported.")); + if(!Context::current().isVersionSupported(Version::GLES310)) + CORRADE_SKIP("OpenGL ES 3.1 is not supported."); + #endif + + MultisampleTexture2D texture; + texture.setStorage(4, TextureFormat::RGBA8, Vector2i{32}) + .bindImage(2, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImage(2); + + MAGNUM_VERIFY_NO_ERROR(); + + #ifndef MAGNUM_TARGET_GLES + AbstractTexture::bindImages(1, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImages(1, 3); + + MAGNUM_VERIFY_NO_ERROR(); + #endif +} + +void MultisampleTextureGLTest::bindImage2DArray() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::shader_image_load_store::string() + std::string(" is not supported.")); + #else + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::OES::texture_storage_multisample_2d_array::string() + std::string(" is not supported.")); + if(!Context::current().isVersionSupported(Version::GLES310)) + CORRADE_SKIP("OpenGL ES 3.1 is not supported."); + #endif + + MultisampleTexture2DArray texture; + texture.setStorage(4, TextureFormat::RGBA8, {32, 32, 4}) + .bindImage(2, 1, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + MAGNUM_VERIFY_NO_ERROR(); + + texture.bindImageLayered(3, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + AbstractTexture::unbindImage(2); + AbstractTexture::unbindImage(3); + + MAGNUM_VERIFY_NO_ERROR(); + + #ifndef MAGNUM_TARGET_GLES + AbstractTexture::bindImages(1, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImages(1, 3); + + MAGNUM_VERIFY_NO_ERROR(); + #endif +} + void MultisampleTextureGLTest::storage2D() { #ifndef MAGNUM_TARGET_GLES if(!Context::current().isExtensionSupported()) diff --git a/src/Magnum/Test/RectangleTextureGLTest.cpp b/src/Magnum/Test/RectangleTextureGLTest.cpp index 51ecff200..453197b12 100644 --- a/src/Magnum/Test/RectangleTextureGLTest.cpp +++ b/src/Magnum/Test/RectangleTextureGLTest.cpp @@ -29,6 +29,7 @@ #include "Magnum/configure.h" #include "Magnum/BufferImage.h" #include "Magnum/Image.h" +#include "Magnum/ImageFormat.h" #include "Magnum/PixelFormat.h" #include "Magnum/RectangleTexture.h" #include "Magnum/TextureFormat.h" @@ -46,6 +47,7 @@ struct RectangleTextureGLTest: AbstractOpenGLTester { void wrap(); void bind(); + void bindImage(); void sampling(); void samplingSRGBDecode(); @@ -78,6 +80,7 @@ RectangleTextureGLTest::RectangleTextureGLTest() { &RectangleTextureGLTest::wrap, &RectangleTextureGLTest::bind, + &RectangleTextureGLTest::bindImage, &RectangleTextureGLTest::sampling, &RectangleTextureGLTest::samplingSRGBDecode, @@ -177,6 +180,31 @@ void RectangleTextureGLTest::bind() { MAGNUM_VERIFY_NO_ERROR(); } +void RectangleTextureGLTest::bindImage() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::texture_rectangle::string() + std::string(" is not supported.")); + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::shader_image_load_store::string() + std::string(" is not supported.")); + + RectangleTexture texture; + texture.setStorage(TextureFormat::RGBA8, Vector2i{32}) + .bindImage(2, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImage(2); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::bindImages(1, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImages(1, 3); + + MAGNUM_VERIFY_NO_ERROR(); +} + void RectangleTextureGLTest::sampling() { if(!Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_rectangle::string() + std::string(" is not supported.")); diff --git a/src/Magnum/Test/TextureArrayGLTest.cpp b/src/Magnum/Test/TextureArrayGLTest.cpp index b9e170ba9..99785898b 100644 --- a/src/Magnum/Test/TextureArrayGLTest.cpp +++ b/src/Magnum/Test/TextureArrayGLTest.cpp @@ -28,6 +28,7 @@ #include "Magnum/configure.h" #include "Magnum/BufferImage.h" #include "Magnum/Image.h" +#include "Magnum/ImageFormat.h" #include "Magnum/PixelFormat.h" #include "Magnum/TextureArray.h" #include "Magnum/TextureFormat.h" @@ -57,6 +58,13 @@ struct TextureArrayGLTest: AbstractOpenGLTester { #endif void bind2D(); + #ifndef MAGNUM_TARGET_WEBGL + #ifndef MAGNUM_TARGET_GLES + void bindImage1D(); + #endif + void bindImage2D(); + #endif + #ifndef MAGNUM_TARGET_GLES void sampling1D(); #endif @@ -172,6 +180,13 @@ TextureArrayGLTest::TextureArrayGLTest() { #endif &TextureArrayGLTest::bind2D, + #ifndef MAGNUM_TARGET_WEBGL + #ifndef MAGNUM_TARGET_GLES + &TextureArrayGLTest::bindImage1D, + #endif + &TextureArrayGLTest::bindImage2D, + #endif + #ifndef MAGNUM_TARGET_GLES &TextureArrayGLTest::sampling1D, #endif @@ -424,6 +439,69 @@ void TextureArrayGLTest::bind2D() { MAGNUM_VERIFY_NO_ERROR(); } +#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) +#ifndef MAGNUM_TARGET_GLES +void TextureArrayGLTest::bindImage1D() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::shader_image_load_store::string() + std::string(" is not supported.")); + + Texture1DArray texture; + texture.setStorage(1, TextureFormat::RGBA8, {32, 4}) + .bindImage(2, 0, 1, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + MAGNUM_VERIFY_NO_ERROR(); + + texture.bindImageLayered(3, 0, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + AbstractTexture::unbindImage(2); + AbstractTexture::unbindImage(3); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::bindImages(1, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImages(1, 3); + + MAGNUM_VERIFY_NO_ERROR(); +} +#endif + +void TextureArrayGLTest::bindImage2D() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::shader_image_load_store::string() + std::string(" is not supported.")); + #else + if(!Context::current().isVersionSupported(Version::GLES310)) + CORRADE_SKIP("OpenGL ES 3.1 is not supported."); + #endif + + Texture2DArray texture; + texture.setStorage(1, TextureFormat::RGBA8, {32, 32, 4}) + .bindImage(2, 0, 1, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + MAGNUM_VERIFY_NO_ERROR(); + + texture.bindImageLayered(3, 0, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + AbstractTexture::unbindImage(2); + AbstractTexture::unbindImage(3); + + MAGNUM_VERIFY_NO_ERROR(); + + #ifndef MAGNUM_TARGET_GLES + AbstractTexture::bindImages(1, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImages(1, 3); + + MAGNUM_VERIFY_NO_ERROR(); + #endif +} +#endif + #ifndef MAGNUM_TARGET_GLES void TextureArrayGLTest::sampling1D() { if(!Context::current().isExtensionSupported()) diff --git a/src/Magnum/Test/TextureGLTest.cpp b/src/Magnum/Test/TextureGLTest.cpp index c74e5eacb..3cc63e669 100644 --- a/src/Magnum/Test/TextureGLTest.cpp +++ b/src/Magnum/Test/TextureGLTest.cpp @@ -30,6 +30,9 @@ #include "Magnum/BufferImage.h" #endif #include "Magnum/Image.h" +#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) +#include "Magnum/ImageFormat.h" +#endif #include "Magnum/PixelFormat.h" #include "Magnum/Texture.h" #include "Magnum/TextureFormat.h" @@ -63,6 +66,14 @@ struct TextureGLTest: AbstractOpenGLTester { void bind2D(); void bind3D(); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + #ifndef MAGNUM_TARGET_GLES + void bindImage1D(); + #endif + void bindImage2D(); + void bindImage3D(); + #endif + #ifndef MAGNUM_TARGET_GLES void sampling1D(); #endif @@ -218,6 +229,14 @@ TextureGLTest::TextureGLTest() { &TextureGLTest::bind2D, &TextureGLTest::bind3D, + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + #ifndef MAGNUM_TARGET_GLES + &TextureGLTest::bindImage1D, + #endif + &TextureGLTest::bindImage2D, + &TextureGLTest::bindImage3D, + #endif + #ifndef MAGNUM_TARGET_GLES &TextureGLTest::sampling1D, #endif @@ -573,6 +592,96 @@ void TextureGLTest::bind3D() { MAGNUM_VERIFY_NO_ERROR(); } +#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) +#ifndef MAGNUM_TARGET_GLES +void TextureGLTest::bindImage1D() { + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::shader_image_load_store::string() + std::string(" is not supported.")); + + Texture1D texture; + texture.setStorage(1, TextureFormat::RGBA8, 32) + .bindImage(2, 0, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImage(2); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::bindImages(1, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImages(1, 3); + + MAGNUM_VERIFY_NO_ERROR(); +} +#endif + +void TextureGLTest::bindImage2D() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::shader_image_load_store::string() + std::string(" is not supported.")); + #else + if(!Context::current().isVersionSupported(Version::GLES310)) + CORRADE_SKIP("OpenGL ES 3.1 is not supported."); + #endif + + Texture2D texture; + texture.setStorage(1, TextureFormat::RGBA8, Vector2i{32}) + .bindImage(2, 0, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImage(2); + + MAGNUM_VERIFY_NO_ERROR(); + + #ifndef MAGNUM_TARGET_GLES + AbstractTexture::bindImages(1, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImages(1, 3); + + MAGNUM_VERIFY_NO_ERROR(); + #endif +} + +void TextureGLTest::bindImage3D() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current().isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::shader_image_load_store::string() + std::string(" is not supported.")); + #else + if(!Context::current().isVersionSupported(Version::GLES310)) + CORRADE_SKIP("OpenGL ES 3.1 is not supported."); + #endif + + Texture3D texture; + texture.setStorage(1, TextureFormat::RGBA8, {32, 32, 4}) + .bindImage(2, 0, 1, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + MAGNUM_VERIFY_NO_ERROR(); + + texture.bindImageLayered(3, 0, ImageAccess::ReadWrite, ImageFormat::RGBA8); + + AbstractTexture::unbindImage(2); + AbstractTexture::unbindImage(3); + + MAGNUM_VERIFY_NO_ERROR(); + + #ifndef MAGNUM_TARGET_GLES + AbstractTexture::bindImages(1, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbindImages(1, 3); + + MAGNUM_VERIFY_NO_ERROR(); + #endif +} +#endif + #ifndef MAGNUM_TARGET_GLES void TextureGLTest::sampling1D() { Texture1D texture; diff --git a/src/Magnum/Texture.h b/src/Magnum/Texture.h index c99773547..6718681bd 100644 --- a/src/Magnum/Texture.h +++ b/src/Magnum/Texture.h @@ -194,6 +194,90 @@ template class Texture: public AbstractTexture { */ explicit Texture(NoCreateT) noexcept: AbstractTexture{NoCreate, Implementation::textureTarget()} {} + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + /** + * @brief Bind level of texture to given image unit + * @param imageUnit Image unit + * @param level Texture level + * @param access Image access + * @param format Image format + * + * Available only on 1D and 2D textures. + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bindImages(Int, std::initializer_list), + * @ref bindImageLayered(), @ref unbindImage(), @ref unbindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_gles31 Shader image load/store is not available in OpenGL + * ES 3.0 and older. + * @requires_gles Shader image load/store is not available in WebGL. + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template::type> + #endif + void bindImage(Int imageUnit, Int level, ImageAccess access, ImageFormat format) { + bindImageInternal(imageUnit, level, false, 0, access, format); + } + + /** + * @brief Bind level of given texture layer to given image unit + * @param imageUnit Image unit + * @param level Texture level + * @param layer Texture layer + * @param access Image access + * @param format Image format + * + * Available only on 3D textures. + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bindImages(Int, std::initializer_list), + * @ref bindImageLayered(), @ref unbindImage(), @ref unbindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_gles31 Shader image load/store is not available in OpenGL + * ES 3.0 and older. + * @requires_gles Shader image load/store is not available in WebGL. + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template::type> + #endif + void bindImage(Int imageUnit, Int level, Int layer, ImageAccess access, ImageFormat format) { + bindImageInternal(imageUnit, level, false, layer, access, format); + } + + /** + * @brief Bind level of layered texture to given image unit + * @param imageUnit Image unit + * @param level Texture level + * @param access Image access + * @param format Image format + * + * Available only on 3D textures. + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bindImages(Int, std::initializer_list), + * @ref bindImage(), @ref unbindImage(), @ref unbindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_gles31 Shader image load/store is not available in OpenGL + * ES 3.0 and older. + * @requires_gles Shader image load/store is not available in WebGL. + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template::type> + #endif + void bindImageLayered(Int imageUnit, Int level, ImageAccess access, ImageFormat format) { + bindImageInternal(imageUnit, level, true, 0, access, format); + } + #endif + #ifndef MAGNUM_TARGET_GLES2 /** * @brief Set base mip level diff --git a/src/Magnum/TextureArray.h b/src/Magnum/TextureArray.h index d0f7498f2..7cf37eb20 100644 --- a/src/Magnum/TextureArray.h +++ b/src/Magnum/TextureArray.h @@ -177,6 +177,55 @@ template class TextureArray: public AbstractTexture { */ explicit TextureArray(NoCreateT) noexcept: AbstractTexture{NoCreate, Implementation::textureArrayTarget()} {} + #ifndef MAGNUM_TARGET_WEBGL + /** + * @brief Bind level of given texture layer to given image unit + * @param imageUnit Image unit + * @param level Texture level + * @param layer Texture layer + * @param access Image access + * @param format Image format + * + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bindImages(Int, std::initializer_list), + * @ref bindImageLayered(), @ref unbindImage(), @ref unbindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_gles31 Shader image load/store is not available in OpenGL + * ES 3.0 and older. + * @requires_gles Shader image load/store is not available in WebGL. + */ + void bindImage(Int imageUnit, Int level, Int layer, ImageAccess access, ImageFormat format) { + bindImageInternal(imageUnit, level, false, layer, access, format); + } + + /** + * @brief Bind level of layered texture to given image unit + * @param imageUnit Image unit + * @param level Texture level + * @param access Image access + * @param format Image format + * + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bindImages(Int, std::initializer_list), + * @ref bindImage(), @ref unbindImage(), @ref unbindImages(), + * @ref AbstractShaderProgram::maxImageUnits(), + * @fn_gl{BindImageTexture} + * @requires_gl42 Extension @extension{ARB,shader_image_load_store} + * @requires_gles31 Shader image load/store is not available in OpenGL + * ES 3.0 and older. + * @requires_gles Shader image load/store is not available in WebGL. + */ + void bindImageLayered(Int imageUnit, Int level, ImageAccess access, ImageFormat format) { + bindImageInternal(imageUnit, level, true, 0, access, format); + } + #endif + /** * @copybrief Texture::setBaseLevel() * @return Reference to self (for method chaining)