-
Notifications
You must be signed in to change notification settings - Fork 18
Merging OpenGLRaw and gl
Currently there are 2 similar, but different Haskell packages available for the raw access to OpenGL, OpenGLRaw and gl. While competition is good and healthy, especially in the Open Source world, the situation is a bit confusing for users: Which package should they use? And why should 2 different teams duplicate their effort for something functionally equivalent? To improve the situation, this proposal aims to merge those packages, basically making it possible to deprecate gl
afterwards. The result will be a new version of OpenGLRaw
containing "the best of both worlds", at the cost of not being backwards-compatible with neither OpenGLRaw
nor gl
. This is a price one has to pay to get a clean slate after a long time, and it's a small one considering the fact that previous versions are still available on Hackage.
-
OpenGLRaw
exposes OpenGL tokens as plain old Haskell values, e.g.gl_LINES = 0x0001
whilegl
uses bidirectional pattern synonyms, e.g.pattern GL_LINES = 0x0001
. While it is a tiny bit more convenient to use and visually resembles the original OpenGL C API slightly more, thePatternSynonyms
extension is only available in GHC >= 7.8.1 (released April 2014). Proposal: This is a tough call, it’s basically backwards compatibility vs. ease of use. Perhaps we should use pattern synonyms?
[Edward Kmett: the pattern synonyms were a very deliberate choice on the behalf of gl
, because they can be used naked in pattern position. This can generate both more efficient and more readable code.]
[Sven Panne: Using pattern synonyms and dropping support for GHC < 7.8 is fine with me. For people using an older GHC, Hackage still has the old OpenGLRaw versions and should find a build plan for them, so older stuff continues to work.]
- The types of the OpenGL tokes are slightly different, too:
OpenGLRaw
uses the more exact types from the OpenGL registry, i.e. one ofGLbitfield
,GLenum
,GLubyte
,GLuint
, orGLuint64
, whilegl
always uses(Num a, Eq a) => a
. Proposal: Use the more exact types, any kind of conversions should be explicit.
[Edward Kmett: The main concern is where the spec gets sloppy and uses the same synonym at different types.]
[Sven Panne: Looking at my OpenGL package there are not that many places where the spec is sloppy, but I could live with more general types, too. But then I would propose (Integral a, Bits a) => a
for bitmask
enums and (Integral a) => a
for the rest.]
-
OpenGLRaw
has types per OpenGL version (see e.g. Core45),gl
always exposes all of them (see Types).OpenGLRaw
’s approach is following the spec more closely, e.g.GLsizeiptr
is only mentioned in OpenGL >= 1.5. Proposal: UseOpenGLRaw
’s approach.
[Edward Kmett: No objection.]
- The
gl
package exposes OpenGL ES APIs, too, but this is wrong for several reasons (Proposal: Do not expose OpenGL ES stuff, this should live in a separate package):- The token values can be API-dependent, and there is actually one example of this:
GL_ACTIVE_PROGRAM_EXT
has the value 0x8259 under OpenGL ES 2, while it has the value 0x8B8D under OpenGL. While this was actually a specification glitch, it is out in the wild now and ignoring it doesn’t it make go away. - OpenGL versions of extensions can be completely unrelated to their OpenGL ES versions, see e.g.
GL_EXT_separate_shader_objects
. - API entry points for OpenGL ES have to be retrieved via EGL, while OpenGL entry points must be retrieved via GLX/WGL/…, i.e. window-system-specific ways. Perhaps in some bright future EGL will be the one and only way (see e.g. the EGL_KHR_create_context) extension, but this is not the case at the moment.
- The token values can be API-dependent, and there is actually one example of this:
[Edward Kmett: I was actually in the process of going further in gl
and starting to roll in WebGL support! So this may be a bit of a sticking point.]
[Sven Panne: Until EGL unifies everything, offering both WebGL an "normal" OpenGL in a single package won't really work. The current EGL won't give you OpenGL-only entry points, and there is no guarantee that GLX/WGL/... will return the right entry points for WebGL. Annoying, but that's how it is today.]
- Extensions in
OpenGLRaw
come in 2 versions when needed (core and compatibility, see e.g. VertexType2101010RevCore and VertexType2101010RevCompatibility), whilegl
ignores that distinction. Proposal: Do it likeOpenGLRaw
, the OpenGL registry clearly makes it explicit that extensions are profile-dependent in general.
[Edward Kmett: My only objection to being more anal retentive here is that it means that the easy checks mentioned below that you want to remove, that I want to keep become much harder.]
[Sven Panne: Part of the whole effort is to be a bit pedantic, otherwise one could offer Compatibility45
only (basically the union of all things "OpenGL") and be done with it. Furthermore, the simple checks are far too simple IMHO and deny reality. Furthermore, we can still offer the easy checks: Just implement the check in the (e.g.) core version and re-export it in the compatibility version.]
-
OpenGLRaw
exposesGLhalf
as a synonym forCUShort
, whilegl
depends on thehalf
package’sHalf
type. This is very handy, but it will add another dependency. Nevertheless, it’s only a small package with few common language extensions. Proposal: UseHalf
. -
gl
exposes extensions and various related values like gl_EXT_cull_vertex. The problem is that the set of supported extensions is context-dependent in general, so their types are wrong: They must live in theIO
monad. TheOpenGLRaw
package doesn’t have this functionality exposed directly, but theOpenGL
package has, see StringQueries. Proposal: Expose the list of extensions and a query for a specific extension with the right type directly in the raw layer.
[Edward Kmett: This was actually a conscious design decision in gl
forced by actually using the library in the wild. Common code for gl
sets up the OpenGL universe once at the top of the program, but then you often have code that picks what to do based on these flags. Removing these flags or exiling them to IO means that this pattern doesn't work and code that has to vary its behavior on the presence or absence of different flags becomes much much uglier.]
[Sven Panne: Granted, the interface offered by gl
is more convenient, but it is wrong in general. If you have code deciding what to do depending on the available extensions, it has to live in the IO
monad. At some point you have to use IO
anyway (otherwise nothing will be rendered), so I doubt that requiring IO
is a problem in practice. My point is: I don't want to oversimplify things, making general stuff impossible just for the sake of simple programs and demos.]
[Edward Kmett: Let me put it this way. You're making a cut here at the wrong thing. If you want multiple contexts in the same program then the entire enterprise we have here is flawed! The entire package we have right now only works if you only ever load up an OpenGL context once and use it.
To get an environment that works with multiple OpenGL contexts e.g. starting up a core profile, then tearing it down and starting up a compatibility profile, you need entirely separate memoization of all of the functions for every OpenGL context, because if you try to deference ANY opengl operation while in the wrong context we'll memoize an error, and there is no guarantee that if we shutdown the context and start another that we'd get the same thing at all for anything we memoize.
So unless you want the entire API to be IO (.... -> IO ()) you are stuck with some sort of state in your deferencing of OpenGL. We're already thoroughly compromised on this front. There is nothing new about these extensions. If you try to use any of our OpenGL operations before the GL context is up and running you destroy it forever.
The difference is that there is a heck of a lot of utility in being able to write code that looks like
foo | gl_AMD_occlusion_query_event = ...
because once that flag is statically known it can start forwarding through, and we can share the rather convoluted set up of the set of extensions once when it is first forced upon us, and which users otherwise get wrong constantly.
Otherwise you are asking every user of the package to come up with their own ad hoc mechanism to half memoize what extensions are available to them, and forcing them to pass that around as some form of environment. This complicates the user experience and gives no real additional safety in the presence of multiple contexts due to the issues above.]
[Sven Panne: The OpenGL entry points returned by glXGetProcAddress
, eglGetProcAddress
, dlsym
, and NSAddressOfSymbol
are context-independent, only wglGetProcAddress
on Windows (surprise! 😉) is an exception: The entry points retrieved via it are unique per pixel format, but are otherwise context-independent in practice, too (otherwise there would be no way to retrieve wgl
extensions, see the corresponding OpenGL Wiki entry. Hopefully all this insanity will be cleaned up in the future, there is already a new ABI proposal on the way. Perhaps we end up with eglGetProcAddress
only, let's see...
I still think that the convenience argument is bogus. The body of foo
in your example is probably in the IO
monad anyway. Furthermore, threading around some kind of environment through your rendering code is perfectly normal in non-toy programs, even swapping out whole parts of your rendering engine depending on the available extensions. But anyway, I won't fight too hard for this, let's hear the opinions from other people, too. Finally, one could add something like getExtensions :: IO (Set String)
in addition to extensions :: Set String
, too, so the library user can choose.]
- The module prefix used by
OpenGLRaw
isGraphics.Rendering.OpenGL.Raw
, whilegl
uses the shorterGraphics.GL
.OpenGLRaw
’s prefix is overly long and dates back many years, to a time when there was consensus that this might be a good idea. Nowadaysgl
’s choice looks much saner. Nevertheless, it’s desirable to distinguish the raw binding from any higher-level packages building on it, likeOpenGL
. Proposal: UseGraphics.GL.Raw
as the prefix.
[Edward Kmett: We went with the overly terse name because of a limitation in building haddocks on Windows not being able to handle anything longer than the very short names we took. I have no particular objection there.]
[Sven Panne: Hopefully we will get a GHC 7.10.3 out, which will be able to use command files, so thing would be fine again regardless of the name length.]
-
gl
uses an additionalExt
level belowGraphics.GL
for modules containing extensions, but this is superfluous and only makes things more verbose. The extensions are already grouped by their vendor IDs, which form their own sub-namespaces. Proposal: Simply dropExt
.
[Edward Kmett: Ext was chosen to keep the names for Standard, etc. from mixing in between a bunch of extensions]
[Sven Panne: I don't really have a strong opinion here, although I still think that Ext
is superfluous and that Graphics.GL.Ext.EXT.Foo
looks like a hiccup. 😉]
-
OpenGLRaw
uses the module nameVersionXY
for OpenGL versions without profiles, whilegl
usesStandardXY
. Proposal: UseVersionXY
, it’s more consistent with the OpenGL CPP defines. (no strong preference, though) - Although formally OpenGL 3.2 introduced the notion of profiles, the OpenGL registry already does it for 3.0 and 3.1, too.
OpenGLRaw
follows the registry, whilegl
does not. The differences are small, though, onlyGL_INDEX
,GL_TEXTURE_LUMINANCE_TYPE
, andGL_TEXTURE_INTENSITY_TYPE
. Proposal: Follow the registry.
[Edward Kmett: No particular objection]
-
OpenGLRaw
links to all possible man page versions of an API entry, whilegl
only links to the latest one, compare e.g. the documentation forglCreateProgram
in OpenGLRaw and in gl. Note that the latest man page might document stuff which is not supported in previous versions and therefore it can be quite misleading. The documentation for an entity is always the same, regardless of the version. This is caused by the fact that the various modules basically consist of re-exports only, which is necessary to mix versions and extensions. Proposal: UseOpenGLRaw
’s man page links, so the user can choose the right one.
[Edward Kmett: No objection]
-
OpenGLRaw
documents each parameter directly in the signature, whilegl
does it in a more separated way, compare e.g. the documentation forglGetDebugMessageLog
in OpenGLRaw and in gl. The former way avoids the mental matching of parameters and their corresponding documentation and is more direct. Proposal: UseOpenGLRaw
’s way of parameter documentation.
[Edward Kmett: No objection]
-
gl
repeatedly expands enumerant groups in the documentation, whileOpenGLRaw
just mentions the group names, compare e.g. the documentation forglGetTexLevelParameterfv
in [gl] (https://hackage.haskell.org/package/gl-0.7.7/docs/Graphics-GL-Core32.html#v:glGetTexLevelParameterfv) and in OpenGLRaw. The problem with enumerant groups in the registry is that they are highly incomplete, and much more importantly: They should better depend on the OpenGL version, which they don’t, so mentioning the individual enumerants is wrong and misleading in general (OpenGL versions both remove and add tokens/functions). Proposal: Mention only the group names, just likeOpenGLRaw
, the information in the OpenGL registry is relatively useless and broken.
[Edward Kmett: I actually would break the other way on this one. The expansions are one of the things I receive the most happy comments from users about. Right now users are confronted with an opaque enumerant group and have no way to proceed at all unless they are intimiately familiar with the API. This was a very conscious decision on the behalf of gl
.]
[Sven Panne: The problem here is that the enumerant groups are utterly wrong, I'm not sure if there is even a single one which is 100% correct. Using a core profile? Tons of enums are invalid for a lot of entry points. Using an older OpenGL version (e.g. via Mesa)? There are many enums your implementation has never heard of (yet). Using some kind of extension? It's highly probable that your shiny new enum is not listed in the enumerant group. My point is: Wrong documentation is much, much worse than no documentation at all. As a compromise, we could perhaps hyperlink each enumerant group to a separate place with a big, fat warning above each group, this would get rid of the repeated expansion, too.]
[Edward Kmett: I have no objection to linking them off to the side somewhere, just so long as there is some place to go.]
The differences are not really relevant for users of the package, they are just here for reference.
- Both
OpenGLRaw
andgl
generate most of the binding from the XML API description from the OpenGL registry.OpenGLRaw
has the resulting Haskell files checked in, whilegl
dynamically generates them during build time. Having the generated files checked in removes the need to build various XML-related packages, so it’s more convenient and faster for people just wanting to use OpenGL. Furthermore, when the OpenGL registry has changed, one has to look at the generated files anyway: The cabal file needs an update, perhaps needing some kind of version bump. Proposal: Continue to check in the generated files.
[Edward Kmett: I have no real objection there. Note: We technically do ship our build artifacts in gl
, just directly in the autogen folder.]
[Sven Panne: OK, I missed the fact that the autogen folder is shipped, too. Nevertheless, gl
's build system still requires the compilation of 4 hxt
-related packages, which are strictly speaking not necessary anymore when autogen is there. Cutting down the compilation burden on the end-user was part of my motivation to check in the artifacts, and this seems to make sense.]
[Edward Kmett:: Like I said, no real objection. =) ]
-
OpenGLRaw
uses a git submodule for an OpenGL registry mirror to getgl.xml
, whilegl
uses a checked-in version. Proposal: Use a git submodule, it’s a bit more explicit.
[Edward Kmett: I confess I'm not a huge fan of git submodules, I can never remember the workflow, but I don't care enough to fight about it.]
[Sven Panne: Good to hear that I'm not the only one firing up git submodule --help
quite often. 😄]
Thanks to Edward Kmett and Gabríel Arthúr Pétursson for their work on gl
, thereby urging me to do things I would have been too lazy to do otherwise... 😉