[Haskell] ANNOUNCE: OpenGLRaw

Sven Panne Sven.Panne at aedion.de
Fri Jun 12 06:50:35 EDT 2009

Am Donnerstag, 11. Juni 2009 20:56:46 schrieb Isaac Dupree:
> Reminds me of Mauricio's "bindings" efforts, though with some different
> design decisions.  Considering that OpenGL has many many functions that
> may not be supported, in no linear pattern (EXT, or just binding to an
> OpenGL 2.x implementation, etc.), dynamic loading might be a good
> choice.  Is it fast enough?

Of course the API entries are not dynamically looked up every time they are 
called, this would kill performance, even when they are not called very often. 
Let's look at e.g. glCreateShader. The entry is defined via a macro:

   EXTENSION_ENTRY(glCreateShader,GLenum -> IO GLuint)

This expands into (slightly reformatted):

glCreateShader :: GLenum -> IO GLuint
glCreateShader = dyn_glCreateShader ptr_glCreateShader

foreign import ccall unsafe "dynamic"
   dyn_glCreateShader :: Invoker (GLenum -> IO GLuint)

ptr_glCreateShader :: FunPtr a
ptr_glCreateShader =
   unsafePerformIO (getExtensionEntry extensionNameString "glCreateShader")
{-# NOINLINE ptr_glCreateShader #-}

Invoke is just a handy shortcut:

   type Invoker a = FunPtr a -> a

As you can see, ptr_glCreateShader is a CAF and therefore evaluated at most 
once. After the first call of glCreateShader, further calls basically boil 
down to an indirect jump, having a very low overhead. The only downside here 
is that the addresses of the OpenGL API entries *might* depend on the pixel 
format on Windows, which could lead to problems when using multiple rendering 
contexts with different pixel formats. But this issue is ignored by e.g. GLee 
an GLEW, too, at least if they are built in their default way, so this doesn't 
seem to be a real problem in practice. As usual, Windows is the only platform 
having chosen the most silly way of doing things, *nices don't have this 
problem, even in theory (see e.g. then OpenGL Linux ABI).

Dynamically loading the whole OpenGL API has a long tradition, it was e.g. 
done in Quake 2, and what was good enough for John Carmack in 1997 should be 
good enough for us in 2009.

The macro approach has the advantage that you have a single place to control 
how API entries are retrieved and called. It e.g. shouldn't be too hard to add 
logging to every call, just by changing the EXTENSION_ENTRY macro, probably 
using some Template Haskell (or Oleg comes up with some insane use of a type 
system extension for the same task... ;-).

> (Any amount of performance penalty is probably too much for OpenGL -- I
> remember one time I resorted to some C in my OpenGL haskell program
> because I couldn't figure out how to unbox enough things in my Haskell
> code or maybe GHC 6.4 just wasn't as good as low-level
> optimizations/code generation.)

Nowadays the API call overhead is completely irrelevant for performance. The 
immediate mode is deprecated and you can (and should!) throw tens of thousands 
of textured, lit polygons to OpenGL with a handful of API calls. Or as the 
OpenGL Super Bible puts it: "This is not your father's OpenGL"... ;-)


More information about the Libraries mailing list