From 4dcb5df21c0f9fec21aa6ae632df9c35db75f43b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 19 Jun 2012 02:34:54 +0200 Subject: [PATCH] Reworked binding of textures to shaders. According to OpenGL specification the sampler uniform takes only texture layer and not any particular texture. In OpenGL 4.2 it is possible to explicitly specify desired texture layer in the uniform, so I suppose it's desired to have shader with fixed texture layers and on the other hand be able to bind texture to any layer, not only one -- the exact opposite of how it was in Magnum before. The shader should now specify in its public API which texture is on which layer and explicitly set the layer uniforms upon construction. All setUniform() functions taking textures as argument were thus removed, as they confuse things a lot. The textures now have bind() function which takes layer as argument, so it's now sufficient to only bind the texture before drawing the mesh without messing with sampler uniform. --- src/AbstractShaderProgram.h | 88 ++++++++++++++++++++++++------------- src/AbstractTexture.cpp | 18 +++----- src/AbstractTexture.h | 46 ++++++++++++------- src/BufferedTexture.h | 23 +++++----- src/CubeMapTexture.h | 3 +- src/CubeMapTextureArray.h | 3 +- src/Texture.h | 3 +- 7 files changed, 108 insertions(+), 76 deletions(-) diff --git a/src/AbstractShaderProgram.h b/src/AbstractShaderProgram.h index 2a4a875ff..7b28acdc8 100644 --- a/src/AbstractShaderProgram.h +++ b/src/AbstractShaderProgram.h @@ -22,8 +22,6 @@ #include #include "Shader.h" -#include "AbstractTexture.h" -#include "BufferedTexture.h" namespace Magnum { @@ -34,34 +32,45 @@ namespace Magnum { This class is designed to be used via subclassing. Subclasses define these functions and properties: - - **Attribute location** typedefs defining locations and types for attribute - binding with Mesh::bindAttribute(), for example: + - %Attribute location typedefs defining locations and types + for attribute binding with Mesh::bindAttribute(), for example: @code typedef Attribute<0, Vector4> Vertex; typedef Attribute<1, Vector3> Normal; typedef Attribute<2, Vector2> TextureCoords; +@endcode + - **Layers for texture uniforms** to which the textures will be bound before + rendering, for example: +@code +static const GLint DiffuseTextureLayer = 0; +static const GLint SpecularTextureLayer = 1; @endcode - **Constructor**, which attaches particular shaders, links the program and gets uniform locations, for example: @code -// Load shaders from file and attach them to the program -attachShader(Shader::fromFile(Shader::Vertex, "PhongShader.vert")); -attachShader(Shader::fromFile(Shader::Fragment, "PhongShader.frag")); - -// Link -link(); - -// Get locations of uniforms -transformationMatrixUniform = uniformLocation("transformationMatrix"); -projectionMatrixUniform = uniformLocation("projectionMatrix"); -// more uniforms like light location, colors etc. +MyShader() { + // Load shaders from file and attach them to the program + attachShader(Shader::fromFile(Shader::Vertex, "PhongShader.vert")); + attachShader(Shader::fromFile(Shader::Fragment, "PhongShader.frag")); + + // Link + link(); + + // Get locations of uniforms + transformationMatrixUniform = uniformLocation("transformationMatrix"); + projectionMatrixUniform = uniformLocation("projectionMatrix"); + // more uniforms like light location, colors etc. +} @endcode - - **Uniform binding functions**, which set shader uniforms using - setUniform() functions. Example: + - **Uniform setting functions**, which will provide public interface for + protected setUniform() functions. Example: @code void setTransformationMatrixUniform(const Matrix4& matrix) { setUniform(transformationMatrixUniform, matrix); } +void setProjectionMatrixUniform(const Matrix4& matrix) { + setUniform(projectionMatrixUniform, matrix); +} @endcode @subsection AbstractShaderProgramAttributeLocation Binding attribute location @@ -89,11 +98,40 @@ bindAttributeLocation(TextureCoords::Location, "textureCoords"); // Link... @endcode +@subsection AbstractShaderProgramTextureLayer Binding texture layer uniforms +The preferred workflow is to specify texture layers directly in the shader +code, e.g.: +@code +layout(binding = 0) uniform sampler2D diffuseTexture; +layout(binding = 1) uniform sampler2D specularTexture; +@endcode +@requires_gl42 Extension @extension{ARB,shading_language_420pack} (for explicit + texture layer binding instead of using setUniform(GLint, GLint)) + +If you don't have the required extension (or if you want to change the layer +later), you can set the texture layer uniform using setUniform(GLint, GLint): +@code +use(); +setUniform(uniformLocation("diffuseTexture"), DiffuseTextureLayer); +setUniform(uniformLocation("specularTexture"), SpecularTextureLayer); +@endcode + @section AbstractShaderProgramRenderingWorkflow Rendering workflow -Basic workflow with AbstractShaderProgram subclasses is: instancing the class -(once at the beginning), then in every frame calling use(), setting uniforms -and calling Mesh::draw() (see its documentation for more). +Basic workflow with %AbstractShaderProgram subclasses is: instancing the class +(once at the beginning), then in Object::draw() reimplementation calling +use(), setting uniforms, binding required textures to their respective layers +using AbstractTexture::bind(GLint) and calling Mesh::draw(). For example: +@code +void draw(const Magnum::Matrix4& transformationMatrix, Magnum::Camera* camera) { + shader.use(); + shader.setTransformationMatrixUniform(transformationMatrix); + shader.setProjectionMatrixUniform(camera->projectionMatrix()); + diffuseTexture.bind(MyShader::DiffuseTextureLayer); + specularTexture.bind(MyShader::SpecularTextureLayer); + mesh.draw(); +} +@endcode @todo Uniform arrays support @@ -297,16 +335,6 @@ class MAGNUM_EXPORT AbstractShaderProgram { glUniformMatrix4fv(location, 1, GL_FALSE, value.data()); } - /** @copydoc setUniform(GLint, GLint) */ - inline void setUniform(GLint location, const AbstractTexture* value) { - setUniform(location, value->layer()); - } - - /** @copydoc setUniform(GLint, GLint) */ - inline void setUniform(GLint location, const BufferedTexture* value) { - setUniform(location, value->layer()); - } - private: enum State { Initialized, diff --git a/src/AbstractTexture.cpp b/src/AbstractTexture.cpp index 3135dd4ac..a0ba88f96 100644 --- a/src/AbstractTexture.cpp +++ b/src/AbstractTexture.cpp @@ -18,18 +18,6 @@ namespace Magnum { #ifndef DOXYGEN_GENERATING_OUTPUT -/* Check corretness of GL_TEXTURE0 + N == GL_TEXTUREN */ -#define texture_assert(num) static_assert(GL_TEXTURE0 + num == GL_TEXTURE##num,\ - "Unsupported constants for GL texture layers") -texture_assert(0); texture_assert(8); texture_assert(16); texture_assert(24); -texture_assert(1); texture_assert(9); texture_assert(17); texture_assert(25); -texture_assert(2); texture_assert(10); texture_assert(18); texture_assert(26); -texture_assert(3); texture_assert(11); texture_assert(19); texture_assert(27); -texture_assert(4); texture_assert(12); texture_assert(20); texture_assert(28); -texture_assert(5); texture_assert(13); texture_assert(21); texture_assert(29); -texture_assert(6); texture_assert(14); texture_assert(22); texture_assert(30); -texture_assert(7); texture_assert(15); texture_assert(23); texture_assert(31); -#undef texture_assert /* Check correctness of binary OR in setMinificationFilter(). If nobody fucks anything up, this assert should produce the same results on all dimensions, @@ -46,6 +34,12 @@ static_assert((filter_or(NearestNeighbor, BaseLevel) == GL_NEAREST) && #undef filter_or #endif +GLint AbstractTexture::maxSupportedLayerCount() { + GLint value; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &value); + return value; +} + GLfloat AbstractTexture::maxSupportedAnisotropy() { GLfloat value; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &value); diff --git a/src/AbstractTexture.h b/src/AbstractTexture.h index 1942ee780..27ae65fa5 100644 --- a/src/AbstractTexture.h +++ b/src/AbstractTexture.h @@ -32,8 +32,9 @@ texture, otherwise the texture will be incomplete. If you specified mipmap filtering in setMinificationFilter(), be sure to also either explicitly set all mip levels or call generateMipmap(). -The texture is bound via bind() and setting texture uniform on the shader to the -texture (see AbstractShaderProgram::setUniform(GLint, const AbstractTexture*)). +The texture is bound to shader via bind(). Texture uniform on the shader must +also be set to particular texture layer using +AbstractShaderProgram::setUniform(GLint, GLint). See Texture, CubeMapTexture and CubeMapTextureArray documentation for more information. @@ -468,6 +469,14 @@ class MAGNUM_EXPORT AbstractTexture { /*@}*/ + /** + * @brief Max supported layer count + * + * At least 48. + * @see bind(GLint) + */ + static GLint maxSupportedLayerCount(); + /** * @brief Max supported anisotropy * @@ -478,13 +487,11 @@ class MAGNUM_EXPORT AbstractTexture { /** * @brief Constructor - * @param layer %Texture layer (number between 0 and 31) * @param target Target, e.g. `GL_TEXTURE_2D`. * * Creates one OpenGL texture. */ - inline AbstractTexture(GLint layer, GLenum target): _target(target), _layer(layer) { - glActiveTexture(GL_TEXTURE0 + layer); + inline AbstractTexture(GLenum target): _target(target) { glGenTextures(1, &texture); } @@ -495,23 +502,19 @@ class MAGNUM_EXPORT AbstractTexture { */ virtual ~AbstractTexture() = 0; - /** @brief %Texture layer */ - inline GLint layer() const { return _layer; } - /** @brief OpenGL internal texture ID */ inline GLuint id() const { return texture; } /** - * @brief Bind texture for usage / rendering - * - * Sets current texture as active in its texture layer. Note that - * only one texture can be bound to given layer. + * @brief Bind texture for rendering * - * @see layer() + * 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. */ - inline void bind() { - glActiveTexture(GL_TEXTURE0 + _layer); - glBindTexture(_target, texture); + inline void bind(GLint layer) { + glActiveTexture(GL_TEXTURE0 + layer); + bind(); } /** @@ -582,8 +585,17 @@ class MAGNUM_EXPORT AbstractTexture { const GLenum _target; /**< @brief Target */ + /** + * @brief Bind texture for parameter modification + * + * Unlike bind(GLint) doesn't bind the texture to any particular + * layer, thus unusable for binding for rendering. + */ + inline void bind() { + glBindTexture(_target, texture); + } + private: - const GLint _layer; GLuint texture; }; diff --git a/src/BufferedTexture.h b/src/BufferedTexture.h index 5710e8399..5d69f7b8d 100644 --- a/src/BufferedTexture.h +++ b/src/BufferedTexture.h @@ -108,10 +108,10 @@ class BufferedTexture { /** * @brief Constructor - * @param layer %Texture layer (number between 0 and 31) + * + * Creates one OpenGL texture. */ - inline BufferedTexture(GLint layer = 0): _layer(layer) { - glActiveTexture(GL_TEXTURE0 + layer); + inline BufferedTexture() { glGenTextures(1, &texture); } @@ -120,13 +120,10 @@ class BufferedTexture { glDeleteTextures(1, &texture); } - /** @copydoc AbstractTexture::layer() */ - inline GLint layer() const { return _layer; } - - /** @copydoc AbstractTexture::bind() */ - inline void bind() const { - glActiveTexture(GL_TEXTURE0 + _layer); - glBindTexture(GL_TEXTURE_BUFFER, texture); + /** @copydoc AbstractTexture::bind(GLint) */ + inline void bind(GLint layer) { + glActiveTexture(GL_TEXTURE0 + layer); + bind(); } /** @@ -144,8 +141,12 @@ class BufferedTexture { } private: - GLint _layer; GLuint texture; + + /** @copydoc AbstractTexture::bind() */ + inline void bind() { + glBindTexture(GL_TEXTURE_BUFFER, texture); + } }; /** @relates BufferedTexture diff --git a/src/CubeMapTexture.h b/src/CubeMapTexture.h index ddb455c14..1ca43134a 100644 --- a/src/CubeMapTexture.h +++ b/src/CubeMapTexture.h @@ -70,11 +70,10 @@ class CubeMapTexture: public AbstractTexture { /** * @brief Constructor - * @param layer Texture layer (number between 0 and 31) * * Creates one cube map OpenGL texture. */ - inline CubeMapTexture(GLint layer = 0): AbstractTexture(layer, GL_TEXTURE_CUBE_MAP) {} + inline CubeMapTexture(): AbstractTexture(GL_TEXTURE_CUBE_MAP) {} /** * @copydoc Texture::setWrapping() diff --git a/src/CubeMapTextureArray.h b/src/CubeMapTextureArray.h index af6cdcb23..22d7e1cde 100644 --- a/src/CubeMapTextureArray.h +++ b/src/CubeMapTextureArray.h @@ -58,11 +58,10 @@ class CubeMapTextureArray: public AbstractTexture { /** * @brief Constructor - * @param layer Texture layer (number between 0 and 31) * * Creates one cube map OpenGL texture. */ - inline CubeMapTextureArray(GLint layer = 0): AbstractTexture(layer, GL_TEXTURE_CUBE_MAP_ARRAY) {} + inline CubeMapTextureArray(): AbstractTexture(GL_TEXTURE_CUBE_MAP_ARRAY) {} /** * @copydoc Texture::setWrapping() diff --git a/src/Texture.h b/src/Texture.h index 60d9c6ade..b3447976c 100644 --- a/src/Texture.h +++ b/src/Texture.h @@ -93,14 +93,13 @@ template class Texture: public AbstractTexture { /** * @brief Constructor - * @param layer %Texture layer (number between 0 and 31) * @param target %Texture target. If not set, default value * is `Target::Texture1D`, `Target::Texture2D` or * `Target::Texture3D` based on dimension count. * * Creates one OpenGL texture. */ - inline Texture(GLint layer = 0, Target target = DataHelper::target()): AbstractTexture(layer, static_cast(target)) {} + inline Texture(Target target = DataHelper::target()): AbstractTexture(static_cast(target)) {} /** @brief %Texture target */ inline constexpr Target target() const { return static_cast(_target); }