Page MenuHome

Canonical OpenGL Procedure Binding
AbandonedPublic

Authored by Jason Wilkins (jwilkins) on Aug 1 2014, 8:04 PM.

Details

Summary

This applies to https://developer.blender.org/D643

This is a binding of OpenGL that is simpler than GLEW because it combines equivalent OpenGL versions and extensions into a single interface.

I'll write a more detailed justification later, since it is late.

Diff Detail

Event Timeline

small optimization of the MX_profile_* macros

Did a review of my own code to make a todo list.

intern/cycles/device/device_cuda.cpp
190

This would result in a silent failure if the features are not available.

intern/glew-mx/glew-mx.h
37

This edit is to remove gl prefix so I can use my glreport.py tool and get clean results. Reminding myself here to back port this edit to the context branch.

intern/glew-mx/intern/glew-mx.c
87

Initialization is redundant here since it's static? There probably isnot a preferred style though.

intern/glew-mx/intern/proc-binding.c
44

Need ifdef for compatibility profile

84

Also here

161

Maybe add comments here to highlight how shader objects differ from gl 2.0

339

Comment to note that this extension did not have suffixes functions

382

Double check if this extension also didnt have suffixes

529

Comment to note this is taken from gpu_extensions.c for detecting ATI cards

intern/glew-mx/intern/proc-binding.h
38

These gl prefixes will pollute the gl report.

223

Undefine GLEW_NV_pixel_buffer_object

223

Undefine texture rectangle extensions

304

A comment here might help somebody who is confused about what function to use.

306

A comment here might help somebody who is confused about which function to use.

350

A comment here might help somebody who is confused about which function to use

source/blender/gpu/intern/gpu_buffers.c
116

For now all uses of vbo in blender assume mapping is available which means it won't work in ES without extensions. This can be fixed, but not sure if this patch is the right place since the interface for it would not be standard OpenGL.

1000–1006

Tempted to clean up formatting here since all the lines change anyway

1458

Another place that requires mapbuffer when we could get away with an ES compatible interface

1935

I didn't mess up these back slashes but the temptation to fix them is high.

2055

Mapbuffer again

source/blender/gpu/intern/gpu_extensions.c
131

gl_TexCoord will pollute gl report.

157

Here too

184

Off topic, but: Is the proxy texture target viable here to replace this?

186–190

As it is this substitutes for doing more precise checks closer to the code that uses these extensions. I do wonder though if that might be better. Need to consider that.

328

This case would report unknown even though it should say no error.

Also, this should be move into gpu_debug.c, but that patch comes after this one.

1424

Would be better to make this interface take a char** and a count.

Or, this is dead code and it should be deleted.

1506–1511

Another place where I itch to reformat

1654

This code is also dead, but I updated it anyway.

source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp
474

Checking for gl 1.1 seems silly, at least on windows.

I'd actually lIke to make the code completely bullet proof, but making things work on 1.0 would be way more work than 1.1

source/gameengine/GamePlayer/ghost/GPG_Application.cpp
572

Another 1.1 check

source/gameengine/Ketsji/BL_Shader.cpp
445

These checks at such a low level seem to be very likely to be redundant. See about pushing them up some.

461

Another low level check

477

Another low level check

597

Another low level check

605

Another low level check

629

You get the idea by now...

848–849

This validation is done by the game engine but not the blender application. Maybe it should be?

source/gameengine/Ketsji/BL_Texture.cpp
404–405

Is thid doing the same error that was fixed in gpu_extensions where we get the multitude texture environment limits and not glsl?

417–418

Possible redundant low level extension check. More follow.

source/gameengine/Ketsji/KX_BlenderMaterial.cpp
273

Asserting that an extension is supported is probably wrong unless we mean to doublecheck a higher level guard.

In fact the other low level checks should be replaced with assertions.

source/gameengine/Ketsji/KX_PythonInit.cpp
33

Not sure if this file shouldn't be rewritten to make this unneeded.

I think it depends on if we create a canonical binding for python

source/gameengine/Rasterizer/RAS_2DFilterManager.cpp
222

It seems like uniform locations should be cached

source/gameengine/Rasterizer/RAS_2DFilterManager.h
51

Might want to add the nickname argument as well?

This and the shader error routine in gpu_extensions.c could be combined?

Should note that the differences in shader_objects and gl 2.0 were enough that I rewrote these routines more than others in this patch. I didn't want to create a minimal change cause I planned on improving them already.

source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_GLExtensionManager.cpp
35

This file should probably report festures, not extentions.

Maybe MXContext should cache which extensions were used so they can be printed by these kinds of routines.

source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_StorageIM.cpp
84

Is there a space here or is that the Web software?

source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_StorageVA.cpp
195

Hmm, this should also be guarded by MX_profile_compatibility.

271

I guess the game enine actually uses old school multitexture. Checking for that extension was almost entirely wrong in Blender proper.

source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_StorageVBO.cpp
103–104

Added this check, but it may be unneeded if it's checked at a higher level.

Sergey Sharybin (sergey) requested changes to this revision.Sep 5 2014, 10:16 AM
Sergey Sharybin (sergey) edited edge metadata.

Only did quick glance. not really happy with Cycles being dependeing on the particular OGL binding code from blender. Plus seems you've got rather long list of sto be addressed before final review it seems?

intern/cycles/device/device_cuda.cpp
190

I don't think this is a proper place for checks, plus making Cycles dependeing on particular GL code sued in blender is not the best way to go.

Why exactly it's needed?

intern/glew-mx/intern/proc-binding.h
54

Who maintains the list of symbols to be undefined, redefined and so? What's gonna to happen when, say, mesa supports new ogl stdandard and adds more defines in it's side?

This revision now requires changes to proceed.Sep 5 2014, 10:16 AM

Did a quick skim through the code, attached some notes.

When I first saw this patch I was worried that abstracting the interfaces in such a hardcoded way might not be such a good idea because sometimes different OpenGL extensions have subtle differences that need a different coding model (for instance, ARB_shader_objects is quite different from core OpenGL). Also, one cannot easily check at runtime what function is really called in the code. For instance, what would happen if two extensions interact in different ways and the abstraction code chose one over the other?

Also, if we really are going to use so many levels of extension loading code (GLEW, multi-context glew, unloading functions we do not need) it gets a bit too complicated just to do one thing: Check for OpenGL support based on platform. I start thinking if having a dedicated extension loader here might be better. Essentially different OpenGL are different renderers. Maybe we should design them like that?

If we really want to abstract OpenGL calls in the GPU module, I wonder if such abstractions will really help us in the end, since the GPU code will be more or less concentrated in one place and we can abstract platform differences "easily" anyway.

Generally I'm still not convinced if this patch should be used.

intern/cycles/device/device_cuda.cpp
190

Hmm...same issue exists in the context patch where cycles depends on headers from blender. Arguably it makes cycles less standalone. On the other hand I'm not sure how we would safely integrate in blender...maybe it's just a matter of separating GL functions in cycles in their own module as well.

intern/glew-mx/intern/glew-mx.c
87

Nope, it's OK, leave as is.

intern/glew-mx/intern/proc-binding.h
54

I am also a bit concerned on that. We should at least have a patch with changes or attempt to push upstream.

source/blender/gpu/intern/gpu_buffers.c
1935

Can always be done as a cleanup commit in master

source/blender/gpu/intern/gpu_extensions.c
186–190

It might be a concern if multiple contexts have different capabilities (ie different GPUs?) since those are stored in a global variable. I don't think that's a common use case we should design for though.

source/gameengine/Ketsji/BL_Shader.cpp
848–849

I think it would only make sense for debugging (-d flag).

source/gameengine/Ketsji/BL_Texture.cpp
404–405

Yes

source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_GLExtensionManager.cpp
35

I'm slightly confused here. Does the manager take care of extensions on its own? Does it use multicontext glew functions?

I am now slowly reading through the whole viewport branch, and given that there is already a level of abstraction in shim_init, I would be interested in hearing the detailed rationale for this actually.

OK, there's a reason to prefer defines rather than shims actually. If we use multiple contexts, then the shim will only have the pointer of the first context applied.

I am now slowly reading through the whole viewport branch, and given that there is already a level of abstraction in shim_init, I would be interested in hearing the detailed rationale for this actually.

The code was copied from shim_init into mx when I realized it needed to be part of the context and needed to be separate from Blender.

The only thing left in snim_init should be some code for abstracting the use of vertex buffers.

Here is a more detailed rationale for this code:

If we are using desktop OpenGL then it is very likely, if not guaranteed, that if a feature is available as part of standard OpenGL (glFoo) and it used to be an extension (glFooARB) then you are safe to look up GL_ARB_foo and use glFooARB and it will work fine.

This is thrown out the window when using OpenGL ES!

Since OpenGL ES might have incorporated a feature that was never an ARB extension then looking up GL_ARB_foo will fail and you will not utilize a feature that is actually available.

There are also standard features, such as 3D textures and memory mapping, that are only available to ES as extensions.

Before, in previous GSoCs, we had a set of functions called gpu_glFoo which implemented these 'feature to extension' mappings. But I realized that just using the un-suffixed name was probably cleaner. That is why I added code to remove and redefine the symbols from glew.h

Did a quick skim through the code, attached some notes.

When I first saw this patch I was worried that abstracting the interfaces in such a hardcoded way might not be such a good idea because sometimes different OpenGL extensions have subtle differences that need a different coding model (for instance, ARB_shader_objects is quite different from core OpenGL). Also, one cannot easily check at runtime what function is really called in the code. For instance, what would happen if two extensions interact in different ways and the abstraction code chose one over the other?

Also, if we really are going to use so many levels of extension loading code (GLEW, multi-context glew, unloading functions we do not need) it gets a bit too complicated just to do one thing: Check for OpenGL support based on platform. I start thinking if having a dedicated extension loader here might be better. Essentially different OpenGL are different renderers. Maybe we should design them like that?

If we really want to abstract OpenGL calls in the GPU module, I wonder if such abstractions will really help us in the end, since the GPU code will be more or less concentrated in one place and we can abstract platform differences "easily" anyway.

Generally I'm still not convinced if this patch should be used.

The only real difference I could find was that ARB_shader_objects takes a couple of parameter names that are illegal in OpenGL 2.0 (and not used by Blender). I could check again by reading over the specs and the standard, but I think the possibility of subtle differences causing a problem is unlikely. If there is a problem it would be with ARB_shader_objects, but I think saying it is "quite" different might be an exaggeration. The differences seem simple.

I'm not sure what you mean by losing the ability to check for OpenGL support based on platform. This patch centralizes all of that checking into a single place. Other code simply doesn't need to know, for example, if 3D textures are an extension or not.

If we did have our own custom extension loader it would look a lot like what I've written here except we'd have to duplicate a lot of code from glew. This code just reorganizes what glew found into something less verbose to use.

All this really does is check, once, which entry point to use for a particular feature, and then stores that result in a structure for use later.

intern/cycles/device/device_cuda.cpp
190

Before it did no checking at all and would probably just crashed if the extensions were not available.
The checking probably should be done before the device is created and background_ is not false.

Also, the dependency is not on Blender but on intern/glew-mx.
Not sure if that makes a difference, but it seems important that Cycles and Blender have the same OpenGL binding (glew) when they are linked together. (@Antonis Ryakiotakis (psy-fi) seems to agree)

Does Cycles not have any other dependencies like this?

As for need -- I was adding code to make sure that dependencies are not blindly used without checking first. There are probably several cases where this could be more efficiently done, but even in the worse case the overhead should be tiny compared to how long a gl api call takes.

190

I would say that things are already separated into their own module. I do not see the point of creating something like a glew-mx-cycles module.

intern/glew-mx/intern/proc-binding.h
54

These lists were derived by looking at various standard or extension specification and adding all of the functions listed there.

These entry points are only for those functions that Blender actually uses and are known by multiple names (differing in suffix). It is possible that these often actually have the very same value.

The undefs are to prevent "Redefinition" warnings. An alternative would be to suppress that warning.

The undef of GLEW_* symbols is to prevent a programmer from using those instead of the equivalent MX_*
That is not really needed, but I think helpful for catching errors.

When somebody wants to use a new extension (new to Blender, not necessarily new to OpenGL) and there are multiple equivalent versions of the extension they would simply add the entry points for that extension to this file.

If the extension only has one set of entry points that isn't needed.

The purpose of all of this is to simplify the use of extensions by making the boilerplate code of extension checking at every point unneeded. This becomes especially burdensome when dealing with OpenGL versus OpenGL ES.

We only check if a particular feature is available, without having to be aware of every possible version.

54

I'm not sure what can be pushed upstream.

I have thought about the idea of how to make a universal canonicalization of OpenGL that would be useful for more programmers than just Blender, but that is a large project.

This is just a minimal binding based on how Blender uses different OpenGL versions.

source/blender/gpu/intern/gpu_extensions.c
186–190

I actually do not think it would be too difficult to get rid of these global variables.

A void pointer inside of the _mx_context struct could hold all of these different global GPU data structures.

source/gameengine/Ketsji/BL_Shader.cpp
848–849

The code for compiling shaders in BGE and Blender are both very similar. This can probably be factored into a single set of functions used by both.

source/gameengine/Ketsji/BL_Texture.cpp
404–405

lol, I think I was asking a rhetorical question to my future self ;-)

source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_GLExtensionManager.cpp
35

Honestly not sure what this class does.

I was making a note here that I should probably have the mxcontext initialization function print out what *extensions* it decided to use (as debug function), but then have the BGE print which *features* are available.

For example, no real point is saying that GL_ARB_vertex_program is available if we are using OpenGL >=2.0. Should just say that vertex programs are available (and maybe by what extension/version if one wants to go that far).

The mapping of extensions to features would be handled by the mxcontext init.

OK, there's a reason to prefer defines rather than shims actually. If we use multiple contexts, then the shim will only have the pointer of the first context applied.

My main reason for going with defines is that you do not have to remember to use some weird name like gpu_glFoo or _mx_context->Foo.

Also, glew.h uses defines.

I admit that the list of functions that need to be defined/undefined is long, but it isn't complicated. They are just copied from the specs.

I am checking at this again.

I am feeling that this adds too much complexity with too little benefits at the moment. We have to keep track of various versions of external libraries and undefine certain functions and definitions in our own code. A good question at this point is: Why not write our own wrapper instead of GLEW if we are to do so much work redefining and reloading stuff? It will be much simpler to keep track of, instead of looking through 5 headers at a time trying to figure out what a symbol tracks back to, and we can maintain it better. I think this kind of abstraction, if needed, can certainly wait for later for when we have GLEW support in blender, but I would not go for it now.

I am checking at this again.

I am feeling that this adds too much complexity with too little benefits at the moment. We have to keep track of various versions of external libraries and undefine certain functions and definitions in our own code. A good question at this point is: Why not write our own wrapper instead of GLEW if we are to do so much work redefining and reloading stuff? It will be much simpler to keep track of, instead of looking through 5 headers at a time trying to figure out what a symbol tracks back to, and we can maintain it better. I think this kind of abstraction, if needed, can certainly wait for later for when we have GLEW support in blender, but I would not go for it now.

How is it complex?
There are two types of complexity, incidental and essential.
Incidental complexity is complexity introduced by a bad solution. You could also call it accidental complexity.
Essential complexity is the actual complexity of the problem.

We have a about dozen features that are implemented by about three dozen GL versions and extentions. This solution chooses which version or extention to use once so that the code is not littered with compile time and runtime branches that would be even more complex than this.

This solution centralizes the decision so that it is not spread out through the code and makes it less likely a
maintainer will miss something.

This solution is actually not very complex, just verbose.

What external libraries are we keeping track of? If you mean OpenGL then unfortunately I do not think we can outsource understanding GL's history to somebody else. Using extentions and supporting multiple versions is our job.

The undef's are a convenience to remind programmers not to use extensions directly. They are a lot like the GPU_deprecated.h; if they seem like a nuisance they could be removed.

The definitions for the "canonical" functions are straightforward really. How is it difficult to keep track of? If you wanted to add support for another feature it would be simple.

Writing our own wrapper would be redundant. This is only a small number of functions compared to what GLEW loads. This does not "reload" anything, it just chooses which pointers to use for each feature.

Also, this is building on top of GLEW by using it to load the extention pointers and determine what is available. All this code does is decide in a single place which entry points to use.

These decisions have to be made anyway! CentralIzing it makes sense. This complexity is not going away, it is essential. I've just concentrated it in one place and you are only balking because you couldn't see it before.

(Admittedly supporting core and es adds some more essential complexity)

A remake of GLEW would be just as complicated as GLEW. I'd rather build a custom GLEW that only contains the exact stuff we need (which is possible), but making our own would be odd, since we don't have to in order to get the benefits.

Looking through 5 headers is unfortunate. MSVC takes me straight to the correct symbol. My question would be why do you need to? Anybody working on Blender GL directly should know which functions have been canonicallized if they are working that deeply.

I'm not sure what you mean by "GLEW support in Blender".

One thing about this code is that it is based on OpenGL's history. History does not change.

Finally, I do not have a solution to the problem this code solves except lots of conditionals at compile and runtime. But that would be a discusting mess.

When I read Blender's OpenGL code I find a lot of mistakes made because somebody did not look up exactly where an entry point is defined. There is a failure to check for the right version or extention or to even check at all.

There is also a failure to make sure that all possibilities have been exhausted for accessing a feature.

With adding support for OpenGL core and es this task gets even more complex. This code simplifies maintainance for most contributors because they no longer need to worry about being an expert in OpenGL's 25 year history.

This code is also moderately more efficient because it directly calls the required function instead of choosing between alternatives every time.

Aaargh, I meant GLES, not GLEW, sorry.

Centralization is desirable and should be done (everything will indeed be done in the GPU module).

When I say external libraries I am referring to GLEW itself - different platforms or even distributions may use different versions of the library. I don't think it will be such an issue in this case because the abstracted functionality is old, but for GLES the GLEW headers can change as far as I understood?

What is of concern is what information you hide too. Not everyone uses MSVC and you require programmers to also become familiar with how blender uses OpenGL in addition to just know how OpenGL works "out of the box". Having one code path instead of many is good, but with the possible exception of GLES, we can just agree to use one set of extensions without hiding it behind a second layer. Besides I'd say it's good time to define that OpenGL 2.1 at least for desktop should be minimum for blender soon so most if cases should go away anyway. So does adding the code here really solve something crucial?

What I find peculiar, is the following chain:

  • GLEW
  • Add extra wrapper on top of GLEW for multiple contexts (essential if we want correctness)
  • Add extra wrapper on top of the wrapper to disallow functions in the rest of blender (nice but not essential)
  • Add extra wrapper to abstract functions that share same functionality (not essential and makes GL interface different than default)
  • Add missing definitions for different GLEW versions. (not essential and not desirable)
  • Use OpenGL

I am sure this can be simplified. If we really have to add this much code in blender redefining stuff just so we don't drop away GLEW, I seriously suggest we write a loader of our own. There are so many lines redefining things that it's almost certain that those could be used for a custom loader. And in that case, things are even simpler. Undefined functions don't even exist, that is, you get a compile time error unless you define them, and you can define them in any way you want using just one abstraction layer. And we get rid of GLEW as a dependency. For new functions, again, anyone who wants to add new functionality can do so, and the system is much more transparent to people who have working knowledge of standard OpenGL.

In any case, this is just my opinion, if others think that this is a good solution then I will agree to this. If this is truly blocking vertex streaming we should get more eyes here and get a to final conclusion.

Getting rid of GLEW would actually be pretty cool.

I just wanted to avoid it because it seemed like reinventing the wheel, but at the same time writing a Python script to grab the spec from opengl.org and only generate exactly what Blender needs to a "GPU_gl.h" does seem like a similar amount of effort as "patching" GLEW at runtime.

Mainly, it has the conceptual advantage of doing everything in one place instead of spread between extern/glew, intern/glew-mx, and source/blender/gpu.

So, I guess I see your point.

Lots of good discussion here. Now that we've moved to OpenGL 2.1 minimum — and are using 3.2 core profile in the very near future — our GL code has less need for a helpful layer like this. I'd also like to remove the GLEW dependency and make something simpler & specific to Blender's needs. Probably *after* Blender 2.8 ships.