Reputation: 640
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:
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..
Upvotes: 1
Views: 1286
Reputation: 253
I find Qt may change the bind location during program. For example, you generate a textureID, and bind it to GL_TEXTURE1
, but later you can't use it through setting sampler to 1
by glUniform1i(loc,1)
,because it's not there already, you have to use glActiveTexture
and glBindTexture
every time when you want to use the texture in shaders. This is just my observation, I don't know why either.
Upvotes: 0
Reputation: 16794
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.
Upvotes: 2