Question

I am currently building an image processing app for Android using OpenGL ES 2.0 and I was wondering about the use of shader programs in the app.

I am not sure if there is a max limit of shader programs (was not able to turn up anything with google) that can be loaded at once, but at this current time I am looking at between 10 and 20 shader programs within the app. The app is not a game, is for graphical editing of an image. I imagine a game would use a lot more shades than that so I am not sure if this is too much overhead for the app or if it means that I am currently wasting time optimizing code that does not need to be optimized.

The unfortunate thing is that I'd only ever need to use anywhere between 2-6 of these shaders at any one time. I had originaly tried to design the code so it'll only load the shader programs that it needs at that specific time and delete the ones it does not, but so far it appears that I can not load a shader program on the click of a button.

Shader calls should be within a GL thread that is onSurfaceChanged(), onSurfaceCreated() or onDrawFrame()

The glCreateShader method returns 0 when I try to create a shader outside any of the above methods (on button click). I have tried calling it within a queueEvent which in theory should put it on the GL thread but it still returns 0.

So am I right in thinking at the moment that I need to load all 10-20 shader programs when I create my GL Surface (or put them in my draw method with other ifs around it to determine if any shader programs do need to be loaded). Is there anything wrong with doing that even though I'll only use very few of them at once?

Was it helpful?

Solution

You can create shaders anytime. There's no hard limit to how many shader objects you can have. They obviously use memory, but they're not very large, and having a couple of dozen should not be a problem at all.

The main tradeoff with building a lot of shaders at startup is normally startup time. Shader compilation is a fairly expensive process. For high-end games with thousands of shaders, that's a serious problem. Again, with the numbers you're talking about, I don't think it will be significant.

If you want to continue with deferred shader compilation, you are on the right track with delegating the compilation to the rendering thread. You need to have your OpenGL context current, and when using GLSurfaceView in Android, it's the rendering thread that operates on the context. Not totally sure why your attempt with queueEvent did not work. It's possible that while you ended up in the rendering thread, the context was not current. If that's the problem, it could most likely be fixed.

What I would try instead is have the user thread set a member variable in the GLSurfaceView when it wants to change shaders, specifying the new requested shader (watch out for thread safety on the member variable). Then the rendering thread can check in onDrawFrame() if the requested shader is different from the current shader. If it's different, check if the requested shader is already compiled, by for example keeping a map of shader names to shader objects. If it's not compiled yet, compile it, and store it in the map. Make the new shader current, then render.

There's another approach, but I don't think it's simpler. You can create another EGLContext in your GUI thread, in the same share group as the rendering context. Then you can compile the shaders in the UI thread. You'll still need synchronization with the rendering thread to let it know when a new shader is ready to be used. You also risk running into bugs because object sharing between contexts isn't used much on Android, and I wouldn't be surprised at all if it doesn't work properly on all devices.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top