
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"... ;-) Cheers, S.