Discussion: To prefix or Not To Prefix

Started by rgrzywinski, June 16, 2003, 19:29:27

Previous topic - Next topic

rgrzywinski

There has been a lot of discussion going on surrounding whether or not the JOGL and LWJGL GL calls should be static or not.  I have seen a large number of posts in favor of removing the prefixes so I will present why it is a Bad Thing(tm).

o  Statics do not have a virtual table.  This means that a class composed of static members (aka a function library) cannot be effectively subclassed (no, I didn't say that it couldn't be subclassed -- I said that it cannot be subclassed effectively).  Why would you want to subclass GL or GLU?  Thinking long term, it would make the most sense to have GL be a hierarchy based on the various GL versions.  The subclasses would be vendor specific.  This would allow OO techniques to be used and different render paths set up based on the available hardware.  How many times have you been bitten by an extension not being available on a particular video card?  If GLContext.getGL() was smart enough to detect the hardware and return a correct instance (such as NV30GL as an example) then you could use OO techniques to ensure that you are using the correct rendering path in your code.  Yes, you can do a lookup in JOGL to see if a particular extension is available on the available hardware but then what are you going to do?  Have a huge conditional block to determine what to do next?!?  This is Java people.  Use OO.  It's your friend.

o  You lose the ability to have composable pipelines.  "What about a static facade?"  This will be touched on more in the "multiple GL contexts" part below but the bad thing here is that you have messy interface for switching the implementation at runtime with the static facade.  Also, the point of the composable pipeline is that you can potentially create your own GL subclass that takes care of common or performance enhancing issues transparently (a common one that I like to use is to check local state and only send deltas to the hardware).  You can swap in and out these GL subclasses at runtime.  Statics severely limit this (see "statics" above).

o  Not all implementations use a single GL context.  If you have an application with more than one context then you cannot have different functionality for each context.  If one context is in debug mode (with DebugGL) then all contexts are in debug mode if statics are used.  I would recommend that both GL and GLU get a:

    GLContext getGLContext();

placed on them to further enhance the fact that you have an instance of an object.  This would allow you be deep into the mix and call getGLContext().makeCurrent().  If fact, with the non-static model it is possible for this makeCurrent() call to be made transparent.

o  "It resembles the C API"  This is one of my personal favorites.  Typically if your code looks like C then you're doing something wrong.  Sure, you can write Java that is procedural but then you can write bad Fortran in any language (as the saying goes).  OO is a Good Thing(tm).  Don't remove it.

o  "1.5 will introduce static imports".  Oh come on.  You can't type three (or four) more characters to gain the benefit of all of the above?  If you're using a modern IDE with name completion then you should be eager to back some of those lost key strokes  :D

I only added the last two since people have brought them up.  I don't want to derail the issues with flame wars on To OO or Not To OO.

Fuseboy2

Let's not make the API any less Java-like than it needs to be.  If you can only have one instance, then enforce that through a private constructor and a factory method.

An all-static API would make it much more difficult to wrap the GL and GLU objects for unit testing purposes (e.g. using a Mock Object-like approach), in addition to the reasons rgrzywinski mentioned.

swpalmer

I agree with all the above.

But, I just thought of something else...  The "resembles C" argument does go against OO principals, and therefore Java principals... but it would serve as a stepping stone to get some developers to try the Java platform.. once they are there they will be exposed to the wonders of a good OO language (sure it isn't perfect, so what).   Maybe that is a way that LWJGL and JOGL would be different.. if LWJGL is more C-like it may attract the C folks, if JOGL remains much more true to Java OO principals it would be the next step..

In general I don't like the fragmentation that I think having both APIs will cause.. but perhaps it will work out OK.  I mean there is nothing wrong iwth healthy competition, but I think it woudl be best if the Java gaming community isn't split over it.  We are a relatively small group.. In a sense we need to unite to be strong.

rgrzywinski

That is an interesting observation swpalmer.

One of the main differentiators between JOGL and LWJGL is that LWJGL is tight and very specific to games.  I doubt that princec and the gang are going to make multiple contexts available as 99.999% of games will never use them.  This still doesn't make it a good thing to use statics (specifically for the no virtual table issues) but statics do provide the illusion of simplicity especially when coupled with the static imports proposed for 1.5.

If princec goes ahead and does the changes that he's proposing (specifically adding back in the gl prefexes (i.e.  glTexImage2D from texImage2D) and removes the dreaded pointers) then the barrier to migration between LWJGL and JOGL is small and well defined.

princec

This can only descend into an OO flame war :)
I stand by the principle of creating software to do a job well, and our job is to enable people to write AAA games that run on Java runtimes easily. To this end it's a certainty we won't be adding multiple contexts etc.

But it's also particularly true that we are following the path of least resistance. There is nothing non-OO about static methods. Why do they exist in an OO language? Because they have their place. Why is the Math object full of static methods? Etc.

It is my belief that static methods in the various GL classes is perfectly object-orientated, in the sense that the class defines the namespace in which the static methods live. And they are static, believe me - they are a direct wrapper for a C library, and no more. As the C library is not object orientated in any way neither can the Java library be. However, we can make proper use of the facilities that Java gives us to accommodate this - which is static methods.

Before in Java you would use GL.vertex() as your namespace qualifier but many developers rightly think this is a) a waste of typing and b) ugly, which is why static import is coming to 1.5. However this causes a namespace problem with a few of the GL commands. This brings us neatly full circle back to having the gl prefixes on all the commands again, so they can be statically imported from a convenient class and without any namespace collision.

Now, it would be great if we could efficiently generate a GL instance which implemented only those interfaces the GL context said it could handle... but right now I don't see an efficient way to do this, nor easy.

So for now what we want to do is capture the existing C programmers' expertise, and make it much easier for them to migrate their game writing to Java. A lot of code will be cut 'n' pastable from C, minus the pointers, which need to be declared as buffers.

We strive always to make life easier for the majority, not more full-featured for the few. This is why Java is such a big bloaty complicated beast and why it takes so long to learn how to do anything useful in it. My total failure to make JSP work the other day illustrated this to me once again. I spent half a day trying to make the hiscore table work before I gave up and installed, learned, and wrote a PHP page to do the same trick in under half an hour. The JOGL/JOAL/JINPUT stuff is exactly the same: it's fully-featured, full-fat, full-complexity, and requires J2SE to boot.

If I really had my way I'd have a version of BASIC instead of Java but that's for another project.

It might be productive to list the pros and cons of what we propose to do:

Pros

  • Easy migration from C and C++
  • Simple to understand
  • No need to keep instances of GL all over the place
  • Extension technique well-understood by existing OpenGL community
  • Eases the path of refactoring with JOGL and GL4Java slightly
Cons

  • Can't do any fancy pipelining stuff
  • Breaks all existing code
  • Deliberately simplified model may mean that a few applications simply aren't feasible. In particular I hear that people would like to be able to write level editors - but this is probably moot as we don't support multiple windows or AWT anyway
Any more?

Cas :)

Orangy Tang

QuoteDeliberately simplified model may mean that a few applications simply aren't feasible. In particular I hear that people would like to be able to write level editors - but this is probably moot as we don't support multiple windows or AWT anyway

Well with some creative use of the viewport and scissor tests, you could have multiple 'windows' withing the single framebuffer pretty easily, and if you started using the stencil buffer you could go one up on AWT by having irregular sub windows..

Personally, not having to keep passing a GL object around would be a plus, but whether thats worth it or not i don't know. But if multiple contexts arn't planned then it does make sense.

princec

It's definitely worth it IMHO. It's a pain in the arse having instances of GL everywhere when we know perfectly well there's only ever 1 context.

I've already got a whole windowed GUI system and so have you - so for us, level editors aren't a problem.

Cas :)

Orangy Tang

By contrast, Jogl also seems to want to go totally static, especially once static imports are brought in with 1.5. How the heck this will work with multiple contexts i have no idea, since i've not got that far into it yet.

As a side note, it'll be interesting to see what the two projects will learn from each other - Jogl's GLCapabilities class makes constructing a window a snap, yet  the game loop seems more awkward to drive. I expect to see lots of changes as it progresses, much like LWJGL is already going though.

princec

It will work the same as it's always done with multiple contexts: the thread that is calling GL methods needs to call makeContextCurrent() or similar, and then it is allowed to issue GL commands.

This is why it doesn't work with Java's object model very well. It would have been terribly convenient if an instance of GL also wrapped its context but this isn't how GL works, and that's that.

I'm not quite sure how the GLCapabilities class actually works, because to my knowledge, we are doing it as simply, effectively and crossplatformly as we can in LWJGL already. The only thing missing is the use of that pixel format extension which we haven't gotten round to using yet.

On a side note - one thing I did once upon a time was have each extension encapsulated in an object which implemented the extension's methods. You could then call gl.NV_vertex_array_range.glVertexArrayRangeNV(...) etc only if you had instances of that object to hand.

However this a) requires an instance of GL b) breaks with multiple contexts c) makes your code huge and even uglier and d) doesn't port so easily from the vast amount of existing C++ code which simply wraps stuff in an if(NV_vertex_array_range) type of structure

Cas :)