Merging the OpenGLRaw and gl packages

Currently there are 2 similar packages on Hackage to access raw OpenGL functionality: OpenGLRaw and gl. Taking a closer look at them, it turns out that they are only superficially different and have a lot of things in common. This is a bit confusing for users, so it might make sense to merge those packages. Therefore I'd like to start a discussion how to do this in detail, collecting user feedback and opinions. I think that haskell-cafe is the right place for this discussion, while an accompanying wiki page is more suitable to collect the results. As a basis I started a page at https://github.com/haskell-opengl/OpenGLRaw/wiki/Merging-OpenGLRaw-and-gl, which is necessarily biased at the moment. ;-) It would be nice to hear your opinions about how the package should look like, what might be missing, what could be done better, which differences I forgot, etc. Cheers, S.

After some discussions via email and via the Wiki ( https://github.com/haskell-opengl/OpenGLRaw/wiki/Merging-OpenGLRaw-and-gl), I've already made a few changes to OpenGLRaw, bringing it closer to 'gl'. These are fully backwards compatible, because they mainly consist of improvements in the generated documentation and the addition of various extension-related retrieval functions. Before I move on and introduce breaking changes, I'd like to hear some opinions about a few items which might need some more discussion: * Should OpenGLRaw use pattern synonyms? (Probably yes, but note that this implies GHC >= 7.8.1) * Should OpenGLRaw use the 'Half' type from the 'half' package? (Probably yes, but again this implies GHC 7.8.1) * Should the incredibly long 'Graphics.Rendering.OpenGL.Raw' module prefix be replaced by the more palatable 'Graphics.GL.Raw'? (Probably yes) More details and other already resolved and/or less controversial items can be found on the Wiki. The plan is to release a new OpenGLRaw version soon (3.0), after which Edward and Gabríel Arthúr can deprecate the 'gl' package (if they like).

On Wed, Sep 30, 2015 at 10:29 AM, Sven Panne
* Should OpenGLRaw use pattern synonyms? (Probably yes, but note that this implies GHC >= 7.8.1)
* Should OpenGLRaw use the 'Half' type from the 'half' package? (Probably yes, but again this implies GHC 7.8.1)
7.8.1 is, what, a year and a half old? I'm all for bleeding edge personally but not everyone feels that way, and that seems pretty severe relative to the benefits. That said, I don't actually have any specific arguments against requiring GHC >= 7.8.1 since I'm fine with it myself and, as mentioned on the wiki, it's not like the older package versions will disappear. As an aside regarding the wiki discussion, I don't really see how there's a sane way to have a combined low-level API for WebGL and full OpenGL. At least as of last time I did anything with it, there are a huge number of minor differences in the API that I don't know how you'd paper over. If anything, WebGL might be a good role model to imitate for a slightly higher level, less fiddly but still directly mapped to the underlying layer, API for GL. I'm sure Edward had some plan for how to add WebGL support, but short of having a completely separate set of modules for it I can't imagine how it'd work. - C.

2015-09-30 17:02 GMT+02:00 Casey McCann
* Should OpenGLRaw use pattern synonyms? (Probably yes, but note that
On Wed, Sep 30, 2015 at 10:29 AM, Sven Panne
wrote: this implies GHC >= 7.8.1)
* Should OpenGLRaw use the 'Half' type from the 'half' package? (Probably yes, but again this implies GHC 7.8.1)
7.8.1 is, what, a year and a half old?
I'm all for bleeding edge personally but not everyone feels that way, and that seems pretty severe relative to the benefits.
That's my biggest concern, too, and that's why I wanted to hear other people's opinions: IMHO using pattern synonyms vs. plain old Haskell values is to a large part just bikeshedding, at least in the trivial case at hand where we talk about simple integral values. It basically boils down to the question: Is foo x = case x of GL_BAR -> expr1 GL_BAZ -> expr2 _ -> expr3 really so much better than foo x | x == GL_BAR = expr1 | x == GL_BAZ = expr2 | otherwise = expr3 that we want to drop support for GHC < 7.8.1? Personally, I'm not convinced, but if most other people think that it's OK, I'm willing to pay the price for the sake of merging the packages. Edward claims that using pattern synonyms will result in more efficient code, too, but I haven't checked that. And even if it was: Perhaps GHC can be tweaked to treat both versions above in the same way (I can see no reason why this shouldn't be possible, but perhaps I'm wrong). Regarding readability: The version with pattern synonyms *is* slightly more readable, but not so much that it would warrant dropping slightly outdated GHCs. Regarding the 'half' package: I've just seen that it should work with any GHC 7.x now, so this decision can be decoupled from the pattern synonym issue. Perhaps I should release a new, only slightly incompatible OpenGLRaw version using 'half'? Although GLhalf is part of OpenGL core since 3.0, it is only used in very few non-central places (GL_ARB_half_float_pixel, GL_ARB_half_float_vertex, and GL_NV_half_float). So the resulting breakage will probably be very low. Regarding the module prefix: I'm not sure if it's worth making this breaking change alone, probably this should only be done in conjunction with the introduction of pattern synonyms. But other opinions would be valuable here, too. [...] As an aside regarding the wiki discussion, I don't really see how
there's a sane way to have a combined low-level API for WebGL and full OpenGL. [...]
As already mentioned in more detail on the Wiki, the plan to combine OpenGL and WebGL in a single package is doomed: Different values for the same token, different contents of the extensions, different ways to retrieve the entry points. It's a pity, but that's how it is...

On Wed, Sep 30, 2015 at 6:38 PM Sven Panne
IMHO using pattern synonyms vs. plain old Haskell values is to a large part just bikeshedding, at least in the trivial case at hand where we talk about simple integral values. It basically boils down to the question: Is
foo x = case x of GL_BAR -> expr1 GL_BAZ -> expr2 _ -> expr3
really so much better than
foo x | x == GL_BAR = expr1 | x == GL_BAZ = expr2 | otherwise = expr3
Yes, because the first approach is an expression that you can use anywhere. Sure, there's little difference when you do it at the "top" of a function definition, as you're doing. However, it's a lot more significant if you want to do something like: foo x = doSomething (case x of ...) Without pattern synonyms, you either have foo x = doSomething x' where x' | x == ... = ... or {-# LANGUAGE MultiWayIf #-} foo x = doSomething (if | x == ... -> ...)

2015-10-01 11:01 GMT+02:00 Oliver Charles
[...] However, it's a lot more significant if you want to do something like:
foo x = doSomething (case x of ...)
Without pattern synonyms, you either have
foo x = doSomething x' where x' | x == ... = ...
or
{-# LANGUAGE MultiWayIf #-}
foo x = doSomething (if | x == ... -> ...)
Or simply in plain old Haskell (basically the desugaring of the multi-way-if): foo x = doSomething (case () of _ | x == gl_BAR -> expr1 | x == gl_BAZ -> expr2 | otherwise -> expr3) Compared to: foo x = doSomething (case x of GL_BAR -> expr1 GL_BAZ -> expr2 _ .> expr3) it doesn't really look *that* much different IMHO, given the high price one has to pay for a tiny improvement in readability. But that's my personal, more conservative view of things, and I'm here to see what other people prefer.Alas, up to now, this is not very conclusive... :-/

FYI: I've released a new OpenGLRaw version 3.0.0.0 which is now quite close to the gl package. The changes: * Use pattern synonyms for OpenGL enums. * Changed module name prefix from Graphics.Rendering.OpenGL.Raw to Graphics.GL. * Use slightly different type synonyms for GL type (introducing Fixed on the way): * CDouble => Double (for GLclampd, GLdouble) * CFloat => Float (for GLclampf, GLfloat) * CInt => Fixed (for GLclampx, GLfixed) * CInt => Int32 (for GLint, GLsizei) * CSChar => Int8 (for GLbyte) * CShort => Int16 (for GLshort) * CUChar => Word8 (for GLboolean, GLubyte) * CUInt => Word32 (for GLbitfield, GLenum, GLhandleARB, GLuint) * CUShort => Word16 (for GLushort) There are still a few minor differences between OpenGLRaw and gl (see https://github.com/haskell-opengl/OpenGLRaw/wiki/Merging-OpenGLRaw-and-gl), but nothing serious: As a test, I modified the luminance package to make it compatible with the new OpenGLRaw, and the diff is really small (see https://github.com/phaazon/luminance/pull/39). So I think that the gl package can be retired, but that's of course totally up to Edward and Gabríel. A few remarks: * Using pattern synonyms means losing support for GHC < 7.8, which I consider OK now that 8.0 is coming soon. But to be sure, there is a branch ("classic") for the previous OpenGLRaw API if the need for minor changes/bug fixes arises. * To stay consistent, GLURaw has been changed in a similar way. * The OpenGL package has been adapted to use the new APIs internally, but its external API is still the same. Cheers, S.

After some discussions and looking at the diffs needed to make the `luminance` package and Oliver Charles' SSAO-example use OpenGLRaw instead of gl, I decided to change the types of GL_TRUE and GL_FALSE from GLenum to GLboolean. When these enums are used as parameters, their type is almost always GLboolean, with glClampColor being the only exception. Some general retrieval functions like glProgramiv return boolean values as GLint, but that seems to be the rarer use case. OpenGL is very loosely typed, so you will have to use some fromIntegral calls, even if the enum patterns were more polymorphic. After several decades of computer science and having seen tons of bugs caused by them, I have a strong aversion to implicit conversions, so I'm still convinced that the monomorphic enums are the right thing. :-) I made a new release of OpenGLRaw ( https://github.com/haskell-opengl/OpenGLRaw/releases/tag/v3.1.0.0), which in addition to this typing change contains some "mkFoo" synonyms for the "makeFoo" functions, too, a difference between OpenGLRaw and gl I didn't notice earlier. Cheers, S.

I'm not really convinced by this. This change introduced an inconsistency
and duplication, but doesn't really solve the problem. I already found
another enum that has this problem (GL_LINEAR), and I hardly suggest
introducing GL_LINEAR to work around that.
While I agree that OpenGL is barely typed *statically*, there is a lot of
runtime type checking. In practice o always develop with KHR debug as an
extension or replay via apitrace, and this always checks ebum values for
validity.
I think OpenGLRaw would be more practical with gl-style polymorphic
patterns
On Sun, 10 Jan 2016 7:43 pm Sven Panne
After some discussions and looking at the diffs needed to make the `luminance` package and Oliver Charles' SSAO-example use OpenGLRaw instead of gl, I decided to change the types of GL_TRUE and GL_FALSE from GLenum to GLboolean. When these enums are used as parameters, their type is almost always GLboolean, with glClampColor being the only exception. Some general retrieval functions like glProgramiv return boolean values as GLint, but that seems to be the rarer use case. OpenGL is very loosely typed, so you will have to use some fromIntegral calls, even if the enum patterns were more polymorphic. After several decades of computer science and having seen tons of bugs caused by them, I have a strong aversion to implicit conversions, so I'm still convinced that the monomorphic enums are the right thing. :-)
I made a new release of OpenGLRaw ( https://github.com/haskell-opengl/OpenGLRaw/releases/tag/v3.1.0.0), which in addition to this typing change contains some "mkFoo" synonyms for the "makeFoo" functions, too, a difference between OpenGLRaw and gl I didn't notice earlier.
Cheers, S. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

2016-01-10 21:56 GMT+01:00 Oliver Charles
I'm not really convinced by this. This change introduced an inconsistency and duplication, but doesn't really solve the problem. I already found another enum that has this problem (GL_LINEAR), and I hardly suggest introducing GL_LINEAR to work around that.
GL_LINEAR as a parameter is sometimes used as a GLenum (see e.g. glBlitFramebuffer) and sometimes as a GLint (see e.g. glGetTextureParameteriv), and there is no clear winner.
While I agree that OpenGL is barely typed *statically*, there is a lot of runtime type checking. In practice o always develop with KHR debug as an extension or replay via apitrace, and this always checks ebum values for validity.
Yes, using a debug context + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS) + glDebugMessageCallback during development is always a good idea. Apart from the stateful nature of the API ("this and that is only allowed when we are in state foobar etc."), the whole notion of profiles and extensions makes it fundamentally impossible to have a 100% type-safe API. You can't even e.g. statically tell which set of enums is allowed as a parameter for a given function.
I think OpenGLRaw would be more practical with gl-style polymorphic patterns
As I said in my previous email: Whenever you use the OpenGL API directly (be it via OpenGLRaw or gl), you *will* have lots of 'fromIntegral's, and the patterns don't make much of a difference. A quick grep showed that your SSAO-example project has 33 fromIntegral calls, and only 2 are caused by the patterns being monomorphic. The luminance package is even more extreme in this respect: It contains 188 fromIntegral calls, and only 2 are caused by the monomorphic patterns. (I may be off by small amount, but that doesn't really change the fact.) So in a nutshell: This is a non-issue in practice and mostly a bikeshedding discussion, and I won't change that aspect of the binding. Cheers, S.
participants (3)
-
Casey McCann
-
Oliver Charles
-
Sven Panne