PDA

View Full Version : Compatibility with OO-languages



H. Guijt
11-14-2014, 02:10 AM
It would be ideal if we could directly represent OpenGL objects by C++ (or other OO-language of choice) objects, without needing to jump through hoops for tracking (and if necessary modifying) contexts and existing state.

Ideally we'd be able to do stuff like:

myFBO1->glClear(color);
myFBO2->glClear(color); // no need to "bind" that second FBO first
myBackBuffer->DrawArrays(someVAO);

i.e. none of this "on my next call to glClear, I want to have the following color...". If it is necessary to store state in some OpenGL context (like a VAO or FBO or whatever), make that context explicit as a parameter. Add the ability to copy and save/restore contexts so libraries can at least do some local work, and then leave their objects as if nothing had happened.

Essentially I'm asking for the ability to build a one-to-one translation between C++ member function calls, and the underlying C-based OpenGL calls, without the need to track any state on the C++ level, and without requiring the programmer to mentally track the active state either.

On a similar note, it would be fantastic if such a C++ layer had proper type-safety. GLenum is all nice and well, but with type-safety the compiler would warn you if you accidentally passed an incorrect flag to a function.

kRogue
11-14-2014, 08:14 AM
One can always write the layer oneself, it is not exactly rocket science.

However, there are some serious issues with a "direct" approach for some commands with respect to performance. Lets start with taking as argument the framebuffer to which to render. Firstly, for tiled based renderers changing the buffer to which to write is a BIG, expensive thing. It is not a cheap thing with respect to immediate based renderers either. Going a little further, the most recent version of OpenGL has within it DSA (Direct State Access) which is for the setting of object properties without the need for binding. However, there are some good reasons for binding when it comes to setting shader input sources and output destinations. As hardware features change and advance the binding model also changes (for example NVIDIA's bindless extensions remove the need for binding). To get a taste of what happens underneath, I strongly encourage one to look at the open source GL drivers embodied in the Mesa project. For easier to read, the Gallium drivers are a good place to start. For a high performance driver, examine the non-Gallium driver for Intel hardware, i965. This will allow one to see what is good for direct commands and what is not. Again, reading some driver source code will give one a handle on the situation.

As for graphics contexts, that is a more subtle. Currently, changing GL contexts is a very expensive operation for many OpenGL/OpenGL ES implementations. For tiledbased renderers, it implies a framebuffer switch as well which is very expensive. For immediate based renderers, it still is not cheap[there are some wierd expensive things that happen for some drivers on context switch]. My personal opinion is that the concept of a context is now a historical artefact that should go away. Indeed, the Metal API from Apple on iOS kills it and changes the story to building command buffers to be sent. With that change of approach, all the junk of multiple contexts goes away and yet multi-threaded rendering works fine. The important case of building a command buffer in another thread to be submitted later is trivial in Metal. In my opinion, the future for GPU APIs is the notion of explicitly building a command buffer to be later placed on a queue. It is simple to understand, easier to implement and more flexible.

GClements
11-14-2014, 12:35 PM
It would be ideal if we could directly represent OpenGL objects by C++ (or other OO-language of choice) objects, without needing to jump through hoops for tracking (and if necessary modifying) contexts and existing state.

Ideally we'd be able to do stuff like:

myFBO1->glClear(color);
myFBO2->glClear(color); // no need to "bind" that second FBO first

glClear() clears multiple buffers concurrently. Different buffers typically have different formats. Clearing is affected by the scissor rectangle and write masks. And clearing is one of the simpler operations.



i.e. none of this "on my next call to glClear, I want to have the following color...". If it is necessary to store state in some OpenGL context (like a VAO or FBO or whatever), make that context explicit as a parameter.

There's really no point in requesting architectural features on the assumption that the designers have a free hand. They don't. Many of OpenGL's features are dictated by hardware considerations.

A stateless design assumes that the parameters can be used directly. Often, there's non trivial processing involved in converting the data supplied by the application into something the GPU can use. In that situation, there's a significant difference between calling the same function repeatedly with the same parameter, and calling it with a different parameter every time. So it may be easier (read: more efficient) for the interface to explicitly require specific parameter combinations to be "registered" in advance. The alternative is that many functions end up using the parameters as a key to perform a lookup in a database of cached parameter combinations, and adding a new combination if it's not found.

Also, I'm not sure you're aware just how many parameters many of the functions would take if they didn't use state. E.g. what would be the signature of the equivalent of glDrawElements()?

To the extent that some of the state is unnecessary, much of that has already been removed by the direct state access (API) functions (the ones with "Named" or "Texture" in the function name). So entities which have a "name" (textures, buffers, framebuffers, renderbuffers) can now be manipulated directly rather than having to bind them to a binding point then manipulating them via the binding point.


Add the ability to copy and save/restore contexts so libraries can at least do some local work, and then leave their objects as if nothing had happened.
This ability is provided via the ability to create multiple contexts. The main problem with partial save/restore (like with glPushAttrib() and glPopAttrib()) is that most of the time you actually needed to save, initialise and restore practically the entire context. Code which tried to set only the parts it needed tended to make assumptions about which parts would differ from their initial state, and hand to be constantly updated whenever the rest of the program used additional state.


Essentially I'm asking for the ability to build a one-to-one translation between C++ member function calls, and the underlying C-based OpenGL calls, without the need to track any state on the C++ level, and without requiring the programmer to mentally track the active state either.
Essentially, you're asking for the hardware to be stateless. It isn't. Modern hardware is less stateful than the hardware for which OpenGL 1.1 was designed, but most things are still not just a matter of passing a different pointer.

H. Guijt
11-18-2014, 09:32 AM
There's really no point in requesting architectural features on the assumption that the designers have a free hand. They don't. Many of OpenGL's features are dictated by hardware considerations.

Is this actually still true on modern programmable hardware? Surely, as programmable capabilities have increased, have special-purpose circuits slowly been phased out?


This ability is provided via the ability to create multiple contexts. The main problem with partial save/restore (like with glPushAttrib() and glPopAttrib()) is that most of the time you actually needed to save, initialise and restore practically the entire context. Code which tried to set only the parts it needed tended to make assumptions about which parts would differ from their initial state, and hand to be constantly updated whenever the rest of the program used additional state.

How does that work? As I understand it, the context is an integral part of the window, and cannot be cloned or directly operated on in any way.


Essentially, you're asking for the hardware to be stateless. It isn't. Modern hardware is less stateful than the hardware for which OpenGL 1.1 was designed, but most things are still not just a matter of passing a different pointer.

I suppose what I'm asking for is greater clarity on what affects what. It is not at all clear to me what command is going to have what effect. For example, take this sequence:

glBindFramebuffer (GL_DRAW_FRAMEBUFFER, FBO1);
glClearColor (red);
glBindFramebuffer (GL_DRAW_FRAMEBUFFER, FBO2);
glClearColor (green);
glBindFramebuffer (GL_DRAW_FRAMEBUFFER, FBO1);
glClear ();

Will FBO1 end up being red or green? If glClearColor respects the FBO binding, it will be red. If it is a global state, it will be green. How do I know which one it actually is? It's certainly not written anywhere in the OpenGL Programming Manual or here in the reference pages. And this is true for just about everything in OpenGL - it is totally unclear which namespace a command operates in.

Of course, documenting all this would be a major step forwards, but good API design can go a long way to removing such questions - by making the namespace ("the thing being affected") an explicit parameter. Hence my original suggestion.

Even worse is how glActiveTexture() and glBindTexture() interact - If I do this:

glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, mytexture);
glActiveTexture (GL_TEXTURE1);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

Will that last function actually operate on the texture I bound on line 2, or was that binding 'erased' on line 3?

Again, documentation would help - but even better would be an API that made at least some of this stuff explicit.

mhagain
11-18-2014, 11:36 AM
even better would be an API that made at least some of this stuff explicit.

There already is one - it's called GL_ARB_direct_state_access (https://www.opengl.org/registry/specs/ARB/direct_state_access.txt):
glBindTextureUnit (0, mytexture);
glTextureParameteri (myothertexture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
There, nice and clear and explicit.

Also see glClearNamedFramebufferfv for your first example.

Seriously, these aren't suggestions for the next version of OpenGL because they already exist in the current version of OpenGL.

Maybe you want to express them as, e.g, FBO1->clear instead? But that's just syntactic sugar; the functionality you're looking for is already there (and it's current expression makes OpenGL usable from non-OO languages too, which is neat. Ever tried using D3D from C code? I have, it's not fun).

GClements
11-18-2014, 04:56 PM
How does that work? As I understand it, the context is an integral part of the window, and cannot be cloned or directly operated on in any way.

Contexts are distinct entities. While "simple" toolkits such as GLUT or GLFW may create them automatically and enforce an association with a particular window, the underlying windowing system functions (glX*, wgl*, agl*) require them to be managed explicitly.



I suppose what I'm asking for is greater clarity on what affects what. It is not at all clear to me what command is going to have what effect. For example, take this sequence:

glBindFramebuffer (GL_DRAW_FRAMEBUFFER, FBO1);
glClearColor (red);
glBindFramebuffer (GL_DRAW_FRAMEBUFFER, FBO2);
glClearColor (green);
glBindFramebuffer (GL_DRAW_FRAMEBUFFER, FBO1);
glClear ();

Will FBO1 end up being red or green?

Green. The clear colour is part of the context, not a specific object. As a rough guide, if a command doesn't take either an object name or a "target" (e.g. binding point) as a parameter, it affects global state. The main exception is VAOs: binding a VAO causes certain state to be stored in the VAO rather than the context.


If glClearColor respects the FBO binding, it will be red. If it is a global state, it will be green. How do I know which one it actually is? It's certainly not written anywhere in the OpenGL Programming Manual or here in the reference pages. And this is true for just about everything in OpenGL - it is totally unclear which namespace a command operates in. .
It's all documented in the specification. Although that's not exactly light reading, there is an entire section devoted to state tables (section 6.2 in the OpenGL 4.x specifications).


Of course, documenting all this would be a major step forwards, but good API design can go a long way to removing such questions - by making the namespace ("the thing being affected") an explicit parameter. Hence my original suggestion.
It has already moved quite a way in this direction. Direct state access functions have the object name passed as a parameter. New features such as samplers tend to use the DSA approach from the outset.


Even worse is how glActiveTexture() and glBindTexture() interact - If I do this:

glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, mytexture);
glActiveTexture (GL_TEXTURE1);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

Will that last function actually operate on the texture I bound on line 2, or was that binding 'erased' on line 3?

The glTexParameteri() call modifies the texture bound to the active texture unit (unit 1). But this is now a legacy API. The DSA equivalent is glTextureParameteri(), which takes the texture name as an argument. Or you could use glSamplerParameteri() to store the parameter in a (named) sampler rather than a texture. Similarly, glBindTexture() is superseded by glBindTextureUnit(), which binds a texture to a specific texture unit rather than to the active texture unit.



Again, documentation would help - but even better would be an API that made at least some of this stuff explicit.
Again, most of what can be done has been.

Older versions were constrained by some of the original implementation features. E.g. OpenGL 1.0 didn't have "textures", it had "the texture", singular. Changing the texture meant uploading different data to the block of memory which stored the texture. Changing texture parameters basically meant changing specific hardware registers related to "the texture". Even when multiple textures (glBindTexture() etc) were added in 1.1, the change was from "the texture" to "the current texture"; it would be a long time before you could use multiple textures at once.