[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Scheme OpenGL API



> "Robert Kooima" writes:
>For the past few weeks I've been investigating the possibility of
>adding OpenGL bindings as an extension to MzScheme.  This is not a
>small task, and I'm aware of a few previous aborted attempts.

I'm not on this list, but Shriram forwarded the message to me and
asked my opinion.  I've implemented a number of OpenGL programs in C++
and Java and have designed many graphics API's, including the
OpenGL bindings and high level 2D/3D API for Curl
(http://www.curl.com).  So, I'm very familiar with the design choices
you are facing.  I wish I could say I also know the right answers;
instead all I can do is offer some opinions born of my own experience
and mistakes I've tried to learn from.

> (gl-matrix-mode gl-projection)

You've Scheme-ified the API.  For those not familiar with OpenGL, in
C this would look like:

  glMatrixMode(GL_PROJECTION);

I suggest you consider using syntax that looks as much like the C
spec as possible.  There are two reasons for this:

1. People already familiar with OpenGL will have an easier time using
   it within Scheme.  For those not familiar with OpenGL, provide a
   higher level API that maps more naturally to Scheme.

2. It makes the conversion process mechanical.  Adopting an existing
   standard in its entirety avoids all of the places where people can
   disagree.  You'll see wider acceptance and spend less time worrying
   about the correct punctuation.  In general, don't try to improve GL
   in any way while mapping it into Scheme... that job is for a Scheme
   specific library built on top of your OpenGL bindings.

3. It makes it easier to compare Scheme and C code.  There is an awful
   lot of OpenGL code on the web and it is nice to be able to look at
   and borrow from it without performing excessive syntax gymnastics.


>	 (define (on-paint)
>	   (send this with-gl-context (lambda () (draw)))
>	   (send this swap-gl-buffers))

Very nice.

>	     (gl-frustum (- a) a -1.0 1.0 2.0 100.0)

Don't forget to port GLU... a lot of handy functions (like glViewport)
are in there and are inconvenient to work without.  I'd skip GLUT
entirely; Scheme already has GUI stuff. 

While you're at it, I've found that porting a subset of OpenGL is a
good way to lose friends.  People start using your bindings then find
out you haven't implemented some particular function they depended
on... consider supporting the full OpenGL 1.3 spec.  You should be able
to machine generate almost all of it by running over a set of GL
headers, anyway.

> This takes a very C-like view of GL usage.  As GL hides its state, GL
> code ends up being somewhat un-scheme-like lists of gl-do-something
> invocations.

That's ok... GL is a state-machine API; trying to make it more
functional would just make it harder to use.  Again, a high-level
wrapper built on top of your bindings is the place to make a real
Scheme 3D API.

> The on-char handler calls the draw function directly, rather than
> invoking refresh on the window in order to generate a paint event.
> This is because the on-paint handler clears the window to its
> background color before doing the rendering, resulting in serious
> flickering.

With regard to the canvasses... you will lose huge amounts of
performance if extra page copies start happening.  I'm not sure if
you're doing it optimally, but make sure that the page flipping is
working correctly (i.e. not clearing the background extra times or
switching contexts unnecessarily).  There is no reason your
performance should not be the same as C for rendering a few hundred
thousand polygons.

In order to use certain pieces of functionality, it is really
important to let the user control how the context is instantiated.
Color, alpha, depth, stencil bit depth and antialiasing have to be
specified on initialization, so make sure there's a way of doing that.


> GL makes heavy use of blocks of data for things such as vertex arrays
> and textures.  It will be necessary to create vector types for each
of
> the GL types: gl-float-vec, gl-uint-vec, gl-ubyte-vec, etc.  These
> would support make-, -length, -ref, and -set! vector operations, and
> would hopefully be as unsurprising as possible.

I think that is the right approach.  Most of these are accessed via
dynamically loaded extensions.  It is *essential* to have a solid
extension loading mechanism.  There is no standard for this, so you
can be slick about it, using macros to make the syntax nice.  

Since there is a lot of interest in

> Given that many modern GL applications are bus-bound,
transform-bound,
> or fill-bound, the ability to define vertex arrays using such vectors
> would allow for scheme GL code that performs every bit as well as the
> equivalent C code.

Agreed.  I find that fill-bound is the problem for most applications
now, since interesting effects take multiple rendering passes and high
resolutions require ridiculous amounts of fill rate.  Processors sit
around waiting on graphics cards in my experience.

> I'm considering creating a few wrappers.  For instance, there are a
> wide variety of vertex commands for different argument types and
> counts: gl-vertex-3f, gl-vertex-4f, gl-vertex-2i, etc.  It might be
> convenient to have a gl-vertex function that counts the arguments and
> chooses which gl-vertex-* to call, possibly converting all arguments
> up to a consistent type.  While this would hurt performance in
> immediate mode, any overhead would magically disappear when using
> display lists.

Good idea.  I suggest sending args over as floats since that's the
precision most cards use anyway and doubles just waste bandwidth (my
fill-rate argument notwithstanding).


> Similarly, I'm of the opinion that this is fairly ugly:
>
>        (gl-begin gl-triangles)
>  	(gl-vertex-2i 0 0)
>	(gl-vertex-2i 1 0)
>	(gl-vertex-2i 0 1)
>	(gl-end)
>
> Something like this is a little less ugly:
>
>	 (gl-begin-triangles
>	   (gl-vertex-2i 0 0)
>	   (gl-vertex-2i 1 0)
>	   (gl-vertex-2i 0 1))
>

This is where I think you're designing too far ahead.  Your instincts
are dead on-- the GL API is full of (sometimes dangerous) warts like
this, and you want to remove them.  I've found that this is a bad
place to tweak API's, however.  You're taking on the constraints of
trying to make the API safe and elegant while still bearing the
constraints of trying to follow the GL spec.  

Follow GL faithfully, then make a high level API on top of these
bindings that is much cleaner.  This divides up the constraints as
well as the APIs and the development process... divide and conquer is
a strategy for design/implementation success.

> How can the use of a GL extension be made more scheme-ish?  SHOULD
the
> use of a GL extension be more scheme-ish?

No.

> The C functions all return scheme_void (with the exception of some
> unimplemented state queries).  Does this make sense?

Yes.


> A very large majority of GL functions have side effects.  So, naming
> the procedures as gl-do-something! might be in order.  However, the
> state being changed is external to the environment.  A ! on every GL
> call begins to look cluttered anyway.  Thoughts on this?

Don't bother.  We don't make a DISPLAY! function for printing, so 
why make 3D side effects different from text side effects?

> Should the GL extension be packed into a unit somehow?

Yes.

> An alternative implementation would have the C functions assume their
> arguments are perfect.  Scheme wrappers would check type and count.
> Is there any advantage or disadvantage to this approach?

It is safer to have everything check arguments, but slower.  Also, you
want as little C code as possible.  I think having the C code make 
assumptions is reasonable (if you write some unit tests).

> How's the naming?  Would a different choice of symbols be better?
> It would seem that gl-vertex-3f is more scheme-ish than glVertex3f.

Use glVertex3f.

-morgan

__________________________________________________
Do You Yahoo!?
Send your FREE holiday greetings online!
http://greetings.yahoo.com