1
votes

I am developing a Qt 5.4 based application in Android system. I am generating several OES textures and rendering in a Qt FBO TEXTURE_2D by a shader. Each OES texture has its own eglcontext, and everyone has the same sharedContext. OES textures are used one by one, I mean, I am not trying to render more than one at the same time but I need to create more than one because of I have to be able to render more than one at the same time in the future.

This is the code I am using when I have to update:

m_program->bind();
glActiveTexture(GL_TEXTURE0 + textureId);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId);
/* my code to fill shader attr and glDraw*() */
glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
m_program->release();

The problem is, for some reason, I can just render the first one OES texture created because I get a W/Adreno-ES20(28468): <core_glActiveTexture:348>: GL_INVALID_ENUM when trying to glActiveTexture the second one. So, target texture is filled black.

I have checked all of this and is correct:

  • textureId
  • sharedContext when calling eglCreateContext.
  • current eglContext when rendering, I mean, when calling to above code.
  • Second OES texture has is filled
  • GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 16
  • GL_MAX_TEXTURE_IMAGE_UNITS 16
  • GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 32

I am a little lost and I am not sure if I this is a limitation or an error in my code.

Any thoughts?

EDIT: Trying in other device, application works properly, so I consider the issue is regarding a device limitation..

1
Since you say the second call is already reporting an error could you try to just hardcode the GL_TEXTURE1 for the second one. If this works then either your textureId has a strange value or GL_TEXTURE1 does not equal to GL_TEXTURE0+1 in your case. And if these work you may create a workaround by wrapping a texture ID, create another custom auto-incremental ID which is then used on some access table containing GL_TEXTUREX enumerations. - Matic Oblak
@MaticOblak for some reason, generated textureIds are not one by one, I am checking it for 3 texture to render and the numbers are: (oesTextureId 23) (oesTextureId 45) (oesTextureId 61) - Miguel Angel
So this seems to be the issue then. You may never assume the ids will be sequential or even begin with 0 or any other low value. Are you still stuck on what to do or do you understand the concept of generating your own auto-incremental active texture ids? - Matic Oblak
@MaticOblak, yes I am still stuck on what to do and I am reading about to generate my own auto-incremental texture ids as I have never used, any help is welcome. - Miguel Angel

1 Answers

2
votes

When generating textures you have no guarantee the ids will start with zero or increment by 1. So you may not expect the values will be [0, 1, 2,...] and can not use the active texture as GL_TEXTURE0 + textureID. You will need to create your own auto-incremental system which is best by creating some kind of texture object which will be able to generate the texture, assign both of the IDs and so forth.

To increment the internal ID all you need to do is use a static integer value which increases for every texture you generate. This is the most basic system where you will only load a few textures at some time and then reuse them assuming none of them should be freed and reused. Other systems would need to include some sort of tracing for the released textures and be able to reuse an empty slot.

So for the basics you would have an object/class such as:

static GLint currentActiveTextureID = GL_TEXTURE0;

class ActiveTextureObject {
    GLuint textureID;
    GLenum activeTextureID;

    void generateNew() {
        activeTextureID = currentActiveTextureID++;
        useActive();
        glGenTextures(1, &activeTextureID);
    }

    void useActive() {
        glActiveTexture(activeTextureID);
    }
    void bind() {
        glBindTexture(GL_TEXTURE_2D, textureID);
    }
    void bindAndUseActive() {
        useActive();
        bind();
    }
};

This now holds all the functionality you posted in your snippet. A method generateNew can also be called in constructor if you so wish. As you can see the object binds together the texture and the active texture so you may simply call bindAndUseActive to do both.

For a more complex system you may use an array of elements which would represent the slots for the active textures. You may then loop through the array to find an empty slot.

class ActiveTexturePool {

    static const GLint maximumActiveTextures = 16; // number of maximum units
    GLint currentActiveTextureID[maximumActiveTextures]; // have the container

    ActiveTexturePool() { // a constructor is needed to reset the data
        memset(currentActiveTextureID, 0, sizeof(currentActiveTextureID)); // sets all to zero
    }

    class ActiveTextureObject {
    public:
        GLuint textureID;
        GLenum activeTextureID;

        void generateNew() {
            useActive();
            glGenTextures(1, &activeTextureID);
        }

        void useActive() {
            glActiveTexture(activeTextureID);
        }
        void bind() {
            glBindTexture(GL_TEXTURE_2D, textureID);
        }
        void bindAndUseActive() {
            useActive();
            bind();
        }
    };

    ActiveTextureObject getNewTextureObject() {
        ActiveTextureObject toReturn;

        for(GLint i=0; i<maximumActiveTextures; i++) {
            if(currentActiveTextureID[i] == 0) {
                GLenum activeTexture = GL_TEXTURE0 + i;

                currentActiveTextureID[i] = activeTexture;
                toReturn.activeTextureID = activeTexture;

                return toReturn;;
            }
        }
        return NULL; // the pool is full, you may not create another active texture!
    }

    void recycleTexture(ActiveTextureObject texture) { // remove from the pool
        for(GLint i=0; i<maximumActiveTextures; i++) {
            if(currentActiveTextureID[i] == texture.activeTextureID) {
                currentActiveTextureID[i] = 0;
                texture.activeTextureID = 0;
                // you may also delete the data here but that should most likely be handled by the ActiveTextureObject
                break;
            }
        }
    }
};

Now in this case you would rather create a pool somewhere to handle the textures. You may easily have one or more pools which can be useful for multiple contexts. In the end it might be best to create a class that holds your main context and has a texture pool. The context object may then also create shared context for each of the texture you are generating and also be responsible for removing, recycling the textures and contexts.