[HOpenGL] Re: [Haskell] ANNOUNCE: OpenGLRaw 1.0.0.0
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"... ;-)
Cheers,
S.
More information about the HOpenGL
mailing list