Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Why does SFML unbind the shader after each draw call?  (Read 2340 times)

0 Members and 1 Guest are viewing this topic.

Jamin Grey

  • Newbie
  • *
  • Posts: 25
    • View Profile
    • Of Stranger Flames
Why does SFML unbind the shader after each draw call?
« on: December 25, 2013, 10:22:21 pm »
In the source of SFML 2.0, in void RenderTarget::draw(...), I see this code:

Code: [Select]
void RenderTarget::draw(const Vertex* vertices, unsigned int vertexCount,
                        PrimitiveType type, const RenderStates& states)
{
    // Nothing to draw?
    if (!vertices || (vertexCount == 0))
        return;

    if (activate(true))
    {
        // First set the persistent OpenGL states if it's the very first call
        if (!m_cache.glStatesSet)
            resetGLStates();

        // Check if the vertex count is low enough so that we can pre-transform them
        bool useVertexCache = (vertexCount <= StatesCache::VertexCacheSize);
        if (useVertexCache)
        {
            // Pre-transform the vertices and store them into the vertex cache
            for (unsigned int i = 0; i < vertexCount; ++i)
            {
                Vertex& vertex = m_cache.vertexCache[i];
                vertex.position = states.transform * vertices[i].position;
                vertex.color = vertices[i].color;
                vertex.texCoords = vertices[i].texCoords;
            }

            // Since vertices are transformed, we must use an identity transform to render them
            if (!m_cache.useVertexCache)
                applyTransform(Transform::Identity);
        }
        else
        {
            applyTransform(states.transform);
        }

        // Apply the view
        if (m_cache.viewChanged)
            applyCurrentView();

        // Apply the blend mode
        if (states.blendMode != m_cache.lastBlendMode)
            applyBlendMode(states.blendMode);

        // Apply the texture
        Uint64 textureId = states.texture ? states.texture->m_cacheId : 0;
        if (textureId != m_cache.lastTextureId)
            applyTexture(states.texture);

        // Apply the shader
        if (states.shader)
            applyShader(states.shader);

        // If we pre-transform the vertices, we must use our internal vertex cache
        if (useVertexCache)
        {
            // ... and if we already used it previously, we don't need to set the pointers again
            if (!m_cache.useVertexCache)
                vertices = m_cache.vertexCache;
            else
                vertices = NULL;
        }

        // Setup the pointers to the vertices' components
        if (vertices)
        {
            const char* data = reinterpret_cast<const char*>(vertices);
            glCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), data + 0));
            glCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), data + 8));
            glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12));
        }

        // Find the OpenGL primitive type
        static const GLenum modes[] = {GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES,
                                       GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS};
        GLenum mode = modes[type];

        // Draw the primitives
        glCheck(glDrawArrays(mode, 0, vertexCount));

        // Unbind the shader, if any
        if (states.shader)
            applyShader(NULL);

        // Update the cache
        m_cache.useVertexCache = useVertexCache;
    }
}

That makes me curious. If it's bad for performance to change shaders and textures, so developers try to reduce those calls as much as possible, why does SFML unbind the shader after every draw call?

Is this a precaution incase the user of SFML lets the sf::Shader go out of scope?
Does the unbinding and then rebinding of the same shader cause a small hit to the performance?

I also noticed the sf::Texture getting bound, but not later unbound. Why is the sf::Shader unbound after the call, but not the sf::Texture?

I use SFML alot, and I'm an OpenGL noob trying to learn it, and I just encountered this in SFML's source and was curious.
If my code is entirely positive that the sf::Shader won't get destructed between two draw calls, do I still have to unbind it between them?
Also, do shaders ever need to be unbound when the program ends, or can I leave the last used shader bound? e.g. is it like calling delete() on what you new(), and important unbinding-processes are occuring, or is 'unbinding' just saying, "use the default shader" and not actually required to clean up when the program is exiting?
Of Stranger Flames - My work-in-progress para-historical (and classically-inspired) 2D rpg.

Jamin Grey

  • Newbie
  • *
  • Posts: 25
    • View Profile
    • Of Stranger Flames
Re: Why does SFML unbind the shader after each draw call?
« Reply #1 on: December 26, 2013, 05:17:10 am »
Ah, at the bottom of that source file is the revealing code comment:
// * Texture
//   Storing the pointer or OpenGL ID of the last used texture
//   is not enough; if the sf::Texture instance is destroyed,
//   both the pointer and the OpenGL ID might be recycled in
//   a new texture instance. We need to use our own unique
//   identifier system to ensure consistent caching.
//
// * Shader
//   Shaders are very hard to optimize, because they have
//   parameters that can be hard (if not impossible) to track,
//   like matrices or textures. The only optimization that we
//   do is that we avoid setting a null shader if there was
//   already none for the previous draw.

Gotcha.
Of Stranger Flames - My work-in-progress para-historical (and classically-inspired) 2D rpg.