The sample Program from the previous post not only demonstrated some OpenGL calls and concepts, but also illustrates that code organization quickly becomes important as soon as the program grows beyond a couple of lines.
So I refactored some of the OpenGL Interface code into classes. I noted that for each draw call I need to setup the OpenGL context by binding some Objects like the shader program, the target framebuffer or the source texture.
I also learned that after you are done using any OpenGL object it is helpful to unbind it. The reason for this is not that they would use too much resources, but rather decoupling. It might be that your program just coincidentally has the right objects bound but as soon as you move any of the code, something will break. Explicitly unbinding helps avoid these dependencies of different parts of your program on what GL objects other parts might have bound last.
So my drawing operations started to look like this
# Bind needed objects framebuffer.bind() render_program.use() texture.bind() gl.glDrawSomething(...) # Unbind Objects texture.unbind() render_program.end_use() framebuffer.unbind()
Just by renaming the matching method pairs
__exit__ the objects become context managers and the code above becomes much cleaner
with framebufer, render_program, texture: gl.glDrawSomething(...)
This style has the advantage of being able to specify the objects used for a draw call in a single
with: line. On the other hand it also removes flexibility on the bind and unbind times of the objects. I think in most cases this is not a disadvantage because most times many objects depend on each other logically (e.g. use this texture with that shader together).
A refactored version of the last posts example which uses the context managers is available on github.