From f9099a86bf68c10e5364ad3ff5b63c8b1ee3455d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 13 Oct 2012 21:11:23 +0200 Subject: [PATCH] Tracking texture state - bindings and limits. --- src/AbstractTexture.cpp | 70 +++++++++++++++++++++++++++---- src/AbstractTexture.h | 53 ++++++++++++----------- src/BufferedTexture.h | 2 +- src/Context.cpp | 2 + src/CubeMapTexture.h | 6 +-- src/CubeMapTextureArray.h | 8 ++-- src/Implementation/State.cpp | 4 +- src/Implementation/State.h | 2 + src/Implementation/TextureState.h | 36 ++++++++++++++++ src/Texture.h | 6 +-- 10 files changed, 146 insertions(+), 43 deletions(-) create mode 100644 src/Implementation/TextureState.h diff --git a/src/AbstractTexture.cpp b/src/AbstractTexture.cpp index 87af0d7b0..a2b5f3e1f 100644 --- a/src/AbstractTexture.cpp +++ b/src/AbstractTexture.cpp @@ -15,6 +15,10 @@ #include "AbstractTexture.h" +#include "Context.h" +#include "Implementation/State.h" +#include "Implementation/TextureState.h" + namespace Magnum { #ifndef DOXYGEN_GENERATING_OUTPUT @@ -35,25 +39,49 @@ static_assert((filter_or(NearestNeighbor, BaseLevel) == GL_NEAREST) && #endif GLint AbstractTexture::maxSupportedLayerCount() { - GLint value; - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &value); - return value; + return Context::current()->state()->texture->maxSupportedLayerCount; } #ifndef MAGNUM_TARGET_GLES GLfloat AbstractTexture::maxSupportedAnisotropy() { - GLfloat value; - glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &value); + GLfloat& value = Context::current()->state()->texture->maxSupportedAnisotropy; + + /* Get the value, if not already cached */ + if(value == 0.0f) + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &value); + return value; } #endif +AbstractTexture::~AbstractTexture() { + /* Remove all bindings */ + for(GLuint& binding: Context::current()->state()->texture->bindings) + if(binding == _id) binding = 0; + + glDeleteTextures(1, &_id); +} + +void AbstractTexture::bind(GLint layer) { + Implementation::TextureState* const textureState = Context::current()->state()->texture; + + /* If already bound in given layer, nothing to do */ + if(textureState->bindings[layer] == _id) return; + + /* Change to given layer, if not already there */ + if(textureState->currentLayer != layer) + glActiveTexture(GL_TEXTURE0 + (textureState->currentLayer = layer)); + + /* Bind the texture to the layer */ + glBindTexture(_target, (textureState->bindings[layer] = _id)); +} + AbstractTexture* AbstractTexture::setMinificationFilter(Filter filter, Mipmap mipmap) { #ifndef MAGNUM_TARGET_GLES CORRADE_ASSERT(_target != GL_TEXTURE_RECTANGLE || mipmap == Mipmap::BaseLevel, "AbstractTexture: rectangle textures cannot have mipmaps", this); #endif - bind(); + bindInternal(); glTexParameteri(_target, GL_TEXTURE_MIN_FILTER, static_cast(filter)|static_cast(mipmap)); return this; @@ -64,11 +92,39 @@ AbstractTexture* AbstractTexture::generateMipmap() { CORRADE_ASSERT(_target != GL_TEXTURE_RECTANGLE, "AbstractTexture: rectangle textures cannot have mipmaps", this); #endif - bind(); + bindInternal(); glGenerateMipmap(_target); return this; } +#ifndef DOXYGEN_GENERATING_OUTPUT +void AbstractTexture::bindInternal() { + Implementation::TextureState* const textureState = Context::current()->state()->texture; + + /* If the texture is already bound in current layer, nothing to do */ + if(textureState->bindings[textureState->currentLayer] == _id) + return; + + /* Set internal layer as active if not already */ + const GLint internalLayer = textureState->maxSupportedLayerCount-1; + if(textureState->currentLayer != internalLayer) + glActiveTexture(GL_TEXTURE0 + (textureState->currentLayer = internalLayer)); + + /* Bind the texture to internal layer, if not already */ + if(textureState->bindings[internalLayer] != _id) + glBindTexture(_target, (textureState->bindings[internalLayer] = _id)); +} +#endif + +void AbstractTexture::initializeContextBasedFunctionality(Context* context) { + Implementation::TextureState* const textureState = context->state()->texture; + GLint& value = textureState->maxSupportedLayerCount; + + /* Get the value and resize bindings array */ + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &value); + textureState->bindings.resize(value); +} + AbstractTexture::InternalFormat::InternalFormat(AbstractTexture::Components components, AbstractTexture::ComponentType type) { #ifndef MAGNUM_TARGET_GLES #define internalFormatSwitch(c) switch(type) { \ diff --git a/src/AbstractTexture.h b/src/AbstractTexture.h index fc0b0f818..905618642 100644 --- a/src/AbstractTexture.h +++ b/src/AbstractTexture.h @@ -26,6 +26,8 @@ namespace Magnum { +class Context; + /** @brief Base for textures @@ -42,10 +44,24 @@ AbstractShaderProgram::setUniform(GLint, GLint). See Texture, CubeMapTexture and CubeMapTextureArray documentation for more information. +@section AbstractTexture-performance-optimization Performance optimizations +The engine tracks currently bound textures in all available layers to avoid +unnecessary calls to @fn_gl{ActiveTexture} and @fn_gl{BindTexture}. %Texture +configuration functions use dedicated highest available texture layer to not +affect active bindings in user layers. %Texture limits (such as +maxSupportedLayerCount()) are cached, so repeated queries don't result in +repeated @fn_gl{Get} calls. + +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. + @todo Add glPixelStore encapsulation @todo Texture copying */ class MAGNUM_EXPORT AbstractTexture { + friend class Context; + AbstractTexture(const AbstractTexture& other) = delete; AbstractTexture(AbstractTexture&& other) = delete; AbstractTexture& operator=(const AbstractTexture& other) = delete; @@ -533,8 +549,8 @@ class MAGNUM_EXPORT AbstractTexture { /** * @brief Max supported layer count * - * At least 48. - * @see bind(GLint), @fn_gl{Get} with @def_gl{MAX_COMBINED_TEXTURE_IMAGE_UNITS} + * @see bind(GLint), @fn_gl{Get} with @def_gl{MAX_COMBINED_TEXTURE_IMAGE_UNITS}, + * @fn_gl{ActiveTexture} */ static GLint maxSupportedLayerCount(); @@ -577,12 +593,9 @@ class MAGNUM_EXPORT AbstractTexture { * Sets current texture as active in given layer. The layer must be * between 0 and maxSupportedLayerCount(). Note that only one texture * can be bound to given layer. - * @see bind(), @fn_gl{ActiveTexture} + * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} */ - inline void bind(GLint layer) { - glActiveTexture(GL_TEXTURE0 + layer); - bind(); - } + void bind(GLint layer); /** * @brief Set minification filter @@ -612,7 +625,7 @@ class MAGNUM_EXPORT AbstractTexture { * @see bind(), @fn_gl{TexParameter} with @def_gl{TEXTURE_MAG_FILTER} */ inline AbstractTexture* setMagnificationFilter(Filter filter) { - bind(); + bindInternal(); glTexParameteri(_target, GL_TEXTURE_MAG_FILTER, static_cast(filter)); return this; } @@ -628,7 +641,7 @@ class MAGNUM_EXPORT AbstractTexture { * @requires_gl */ inline AbstractTexture* setBorderColor(const Color4& color) { - bind(); + bindInternal(); glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR, color.data()); return this; } @@ -644,7 +657,7 @@ class MAGNUM_EXPORT AbstractTexture { * @requires_extension @extension{EXT,texture_filter_anisotropic} */ inline AbstractTexture* setMaxAnisotropy(GLfloat anisotropy) { - bind(); + bindInternal(); glTexParameterf(_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy); return this; } @@ -663,27 +676,19 @@ class MAGNUM_EXPORT AbstractTexture { protected: #ifndef DOXYGEN_GENERATING_OUTPUT template struct DataHelper {}; - #endif - const GLenum _target; /**< @brief Target */ + /* Unlike bind() this also sets the binding layer as active */ + void bindInternal(); - /** - * @brief Bind texture for parameter modification - * - * Unlike bind(GLint) doesn't bind the texture to any particular - * layer, thus unusable for binding for rendering. - * @see @fn_gl{BindTexture} - */ - inline void bind() { - glBindTexture(_target, _id); - } + const GLenum _target; + #endif private: + static void initializeContextBasedFunctionality(Context* context); + GLuint _id; }; -inline AbstractTexture::~AbstractTexture() { glDeleteTextures(1, &_id); } - /** @relates AbstractTexture @brief Convertor of component count and data type to InternalFormat diff --git a/src/BufferedTexture.h b/src/BufferedTexture.h index dabe50c67..be3e6f8b8 100644 --- a/src/BufferedTexture.h +++ b/src/BufferedTexture.h @@ -133,7 +133,7 @@ class BufferedTexture: private AbstractTexture { * @see @fn_gl{BindTexture}, @fn_gl{TexBuffer} */ void setBuffer(InternalFormat internalFormat, Buffer* buffer) { - AbstractTexture::bind(); + bindInternal(); glTexBuffer(GL_TEXTURE_BUFFER, internalFormat, buffer->id()); } }; diff --git a/src/Context.cpp b/src/Context.cpp index d3361f8a5..70c9e391b 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -20,6 +20,7 @@ #include #include "AbstractShaderProgram.h" +#include "AbstractTexture.h" #include "Buffer.h" #include "Extensions.h" #include "Implementation/State.h" @@ -201,6 +202,7 @@ Context::Context() { /* Initialize functionality based on current OpenGL version and extensions */ AbstractShaderProgram::initializeContextBasedFunctionality(this); Buffer::initializeContextBasedFunctionality(this); + AbstractTexture::initializeContextBasedFunctionality(this); } Context::~Context() { diff --git a/src/CubeMapTexture.h b/src/CubeMapTexture.h index a537eaec8..a6d1c170f 100644 --- a/src/CubeMapTexture.h +++ b/src/CubeMapTexture.h @@ -86,7 +86,7 @@ class CubeMapTexture: public AbstractTexture { * @copydoc Texture::setWrapping() */ inline CubeMapTexture* setWrapping(const Math::Vector<3, Wrapping>& wrapping) { - bind(); + bindInternal(); DataHelper<3>::setWrapping(GL_TEXTURE_CUBE_MAP, wrapping); return this; } @@ -97,7 +97,7 @@ class CubeMapTexture: public AbstractTexture { * @return Pointer to self (for method chaining) */ template inline CubeMapTexture* setData(Coordinate coordinate, GLint mipLevel, InternalFormat internalFormat, Image* image) { - bind(); + bindInternal(); DataHelper<2>::set(static_cast(coordinate), mipLevel, internalFormat, image); return this; } @@ -108,7 +108,7 @@ class CubeMapTexture: public AbstractTexture { * @return Pointer to self (for method chaining) */ template inline CubeMapTexture* setSubData(Coordinate coordinate, GLint mipLevel, const Math::Vector<2, GLint>& offset, const Image* image) { - bind(); + bindInternal(); DataHelper<2>::setSub(static_cast(coordinate), mipLevel, offset, image); return this; } diff --git a/src/CubeMapTextureArray.h b/src/CubeMapTextureArray.h index ea6851aa2..50689e746 100644 --- a/src/CubeMapTextureArray.h +++ b/src/CubeMapTextureArray.h @@ -60,7 +60,7 @@ class CubeMapTextureArray: public AbstractTexture { * @copydoc Texture::setWrapping() */ inline CubeMapTextureArray* setWrapping(const Math::Vector<3, Wrapping>& wrapping) { - bind(); + bindInternal(); DataHelper<3>::setWrapping(GL_TEXTURE_CUBE_MAP_ARRAY, wrapping); return this; } @@ -73,7 +73,7 @@ class CubeMapTextureArray: public AbstractTexture { * The images are ordered the same way as Coordinate enum. */ template inline CubeMapTextureArray* setData(GLint mipLevel, InternalFormat internalFormat, T* image) { - bind(); + bindInternal(); DataHelper<3>::set(GL_TEXTURE_CUBE_MAP_ARRAY, mipLevel, internalFormat, image); return this; } @@ -97,7 +97,7 @@ class CubeMapTextureArray: public AbstractTexture { * @see setSubData(GLsizei, Coordinate, GLint, const Math::Vector<2, GLint>&, const Image*) */ template inline CubeMapTextureArray* setSubData(GLint mipLevel, const Math::Vector<3, GLint>& offset, const Image* image) { - bind(); + bindInternal(); DataHelper<3>::setSub(GL_TEXTURE_CUBE_MAP_ARRAY, mipLevel, offset, image, Math::Vector<3, GLsizei>(Math::Vector())); return this; } @@ -118,7 +118,7 @@ class CubeMapTextureArray: public AbstractTexture { * @see setSubData(GLint, const Math::Vector<3, GLint>&, const Image*) */ template inline CubeMapTextureArray* setSubData(GLsizei layer, Coordinate coordinate, GLint mipLevel, const Math::Vector<2, GLint>& offset, const Image* image) { - bind(); + bindInternal(); DataHelper<3>::setSub(GL_TEXTURE_CUBE_MAP_ARRAY, mipLevel, Math::Vector<3, GLint>(offset, layer*6+static_cast(coordinate)), image, Math::Vector<2, GLsizei>(Math::Vector())); return this; } diff --git a/src/Implementation/State.cpp b/src/Implementation/State.cpp index 5c3ead774..b1cb03dd8 100644 --- a/src/Implementation/State.cpp +++ b/src/Implementation/State.cpp @@ -17,12 +17,14 @@ #include "BufferState.h" #include "ShaderProgramState.h" +#include "TextureState.h" namespace Magnum { namespace Implementation { -State::State(): buffer(new BufferState), shaderProgram(new ShaderProgramState) {} +State::State(): buffer(new BufferState), shaderProgram(new ShaderProgramState), texture(new TextureState) {} State::~State() { + delete texture; delete shaderProgram; delete buffer; } diff --git a/src/Implementation/State.h b/src/Implementation/State.h index 8330fb7bd..d655b3172 100644 --- a/src/Implementation/State.h +++ b/src/Implementation/State.h @@ -21,6 +21,7 @@ namespace Magnum { namespace Implementation { struct BufferState; struct ShaderProgramState; +struct TextureState; struct State { State(); @@ -28,6 +29,7 @@ struct State { BufferState* const buffer; ShaderProgramState* const shaderProgram; + TextureState* const texture; }; }} diff --git a/src/Implementation/TextureState.h b/src/Implementation/TextureState.h new file mode 100644 index 000000000..74388443e --- /dev/null +++ b/src/Implementation/TextureState.h @@ -0,0 +1,36 @@ +#ifndef Magnum_Implementation_TextureState_h +#define Magnum_Implementation_TextureState_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "Magnum.h" + +#include "Buffer.h" + +namespace Magnum { namespace Implementation { + +struct TextureState { + inline TextureState(): maxSupportedLayerCount(0), maxSupportedAnisotropy(0.0f), currentLayer(0) {} + + GLint maxSupportedLayerCount; + GLfloat maxSupportedAnisotropy; + GLint currentLayer; + + std::vector bindings; +}; + +}} + +#endif diff --git a/src/Texture.h b/src/Texture.h index ba57c8d23..ff3454151 100644 --- a/src/Texture.h +++ b/src/Texture.h @@ -130,7 +130,7 @@ template class Texture: public AbstractTexture { * that can easily create all values the same) */ inline Texture* setWrapping(const Math::Vector& wrapping) { - bind(); + bindInternal(); DataHelper::setWrapping(_target, wrapping); return this; } @@ -148,7 +148,7 @@ template class Texture: public AbstractTexture { * @see bind(), @fn_gl{TexImage1D}, @fn_gl{TexImage2D}, @fn_gl{TexImage3D} */ template inline Texture* setData(GLint mipLevel, InternalFormat internalFormat, Image* image) { - bind(); + bindInternal(); DataHelper::set(_target, mipLevel, internalFormat, image); return this; } @@ -172,7 +172,7 @@ template class Texture: public AbstractTexture { * @see bind(), @fn_gl{TexSubImage1D}, @fn_gl{TexSubImage2D}, @fn_gl{TexSubImage3D} */ template inline Texture* setSubData(GLint mipLevel, const typename DimensionTraits::VectorType& offset, Image* image) { - bind(); + bindInternal(); DataHelper::setSub(_target, mipLevel, offset, image); return this; }