LWJGL Forum

Programming => Lightweight Java Gaming Library => Topic started by: princec on June 10, 2003, 15:01:25

Title: LWJGL discussion forum officially migrates here today!
Post by: princec on June 10, 2003, 15:01:25
In the abscence of the only other worthwhile forum about LWJGL, it seems fitting that today be the Grand Opening of the LWJGL forums here!

First up:

think long and hard about the fact that I want to remove int pointers from the entire API.

Implications:
1) All code is broken. Hurrah! Including my own. So I must have a good reason.
2) Performance. Having to unwrap a native buffer will take about 300 nanoseconds on a 1.2GHz machine. Woo, big deal. I've pretty much proved it to myself that this is totally irrelevant even in the most complex of games. Brian seems to think we should do it from Java. I'm still pondering. Your thoughts are welcome.
3) Credibility, security, and stability. Yes, int pointers bit my arse a few times by pointing at the wrong places. Well whaddya know :D? And we'll never be officially endorsed by anyone in the Java community so long as we've got pointers. And indeed, we'll never have a secure API.

Your time starts... NOW!

Cas :)
Title: LWJGL discussion forum officially migrates here today!
Post by: elias on June 10, 2003, 15:16:14
I'm all for it. However I'm very strong in favour of the java unpacking. I simply hate the way JNIEnv works so I like to pass *only* native types to native code, not objects.

Other implications:
1. We need to ba able to incorporate offsets in a buffer as a start address without slicing. Interleaved vertex formats are an example of this.
2. The gain in stability and security is not that big after all. You can still easily wander off the end of a buffer.
3. Packing buffers in yet another MemoryBuffer (or something) with a cached address will only make the overhead of a buffer even higher. I might want to sacrifice a little performance for the ability to use java buffers directly and not through a proxy.

- elias
Title: LWJGL discussion forum officially migrates here today!
Post by: elias on June 10, 2003, 15:56:16
... And because this is a fairly important and large part of the LWJGL api, please post your best decision here before implementing it.

- elias
Title: LWJGL discussion forum officially migrates here today!
Post by: bedelf on June 10, 2003, 16:24:03
Short and sweet as follows:

1. small price to pay if you improve LWJGL

2. I really don't know enough to say either way. My project hasn't gotten large enough to run into any problems. All and all I trust you guys will make the right decision in the end.

3. all agreed here, wouldn't suck
Title: LWJGL discussion forum officially migrates here today!
Post by: Mojomonkey on June 10, 2003, 16:37:08
I'm all for the change if you feel strongly enough that it will improve the library. All I ask is for a easing period perhaps just deprecate the affected classes for a release or so?
Title: LWJGL discussion forum officially migrates here today!
Post by: cfmdobbie on June 10, 2003, 16:52:19
Agreed, good idea.

All code broken?  Meh.  In my opinion it's fair game for the entire API to be changed up to the point it goes to 1.0.

Performance - the eternal question of trade-off.  The Slashdot-heads are currently slagging off the Quake tech demo that was posted yesterday - "Runs a non-interactive demo slower than the seven year old full game" etc.  How much will that extra 300ns actually affect the application, in real terms?  Probably not a lot.
Title: LWJGL discussion forum officially migrates here today!
Post by: princec on June 10, 2003, 17:02:12
The 300ns won't affect performance *at* *all*.

Cas :)
Title: LWJGL discussion forum officially migrates here today!
Post by: Mojomonkey on June 10, 2003, 18:05:06
No, it will effect perfomance by 300ns per call... *wink* *wink*
Title: LWJGL discussion forum officially migrates here today!
Post by: princec on June 10, 2003, 18:41:00
:roll:

Cas :)
Title: LWJGL discussion forum officially migrates here today!
Post by: rgrzywinski on June 10, 2003, 23:07:37
Kudos to you guys on your release!

As for the whole pointer thing .... I was going to tell you guys that I had *ZERO* performance increase between GL4Java and LWJGL (the former does not use pointers) but you seemed so hell bent on them.   :D

As you said, after you factor in all of the application code, the pointers are in the noise.  That's the problem with granular testing.  Sure, it make take 300ns to make one call and you extrapolate that to 3000 calls and you think "Uugh!  There goes performance!" but how does that factor into the entire equation?  The bottom line is that you can gain more by coarse tuning than you can by fine tuning.  What if a code change dropped that 3000 calls down to 300 and removed 30ms of latency in the total app?

Thanks for activating this forum here guys!!!  I miss JavaGaming.org.
Title: LWJGL discussion forum officially migrates here today!
Post by: PlanetMongo on June 11, 2003, 02:39:20
Well, whatever you do, hurry up and do it before I commit to learning this stuff.  :P
Title: LWJGL discussion forum officially migrates here today!
Post by: Fuseboy2 on June 12, 2003, 02:05:35
I'll throw in my support too.  What I like about LWJGL is that it's so close to OpenGL that other OpenGL resources (books, tutorials, etc.) are easily adapted to it, which is an enormous advantage.

That being said, there are some elements that aren't very idiomatic to Java.  Removing buffer addresses from the API is a good step.

Two things that I've found myself wanting while learning the API (I'm as new to OpenGL as I am to LWJGL) are type safety for the truly mind-bending number of constants that are passed as 'int', and a light-weight OO wrapper around OpenGL.  Textures, shaders, and other aspects of OpenGL seem obvious candidates for representation as objects (that's how they're referred to in the docs, for instance).

That's a lot to ask for, given the size of the API; I think this would make it so much more explorable, however, if you take my meaning.

Michael
Title: LWJGL discussion forum officially migrates here today!
Post by: princec on June 12, 2003, 09:20:45
That's the kind of thing we want people to write that lives on top of the LWJGL ;) The SPGL (which will be explained a bit more soon) does a lot of this kind of tedious stuff. It's just one solution of many of course.

Cas :)
Title: LWJGL discussion forum officially migrates here today!
Post by: spasi on June 12, 2003, 13:44:51
Quote
1. We need to ba able to incorporate offsets in a buffer as a start address without slicing. Interleaved vertex formats are an example of this.

Agreed. That's a problem and we need a clean solution, that doesn't create any new objects (slicing)...

Quote
2. The gain in stability and security is not that big after all. You can still easily wander off the end of a buffer.
3. Packing buffers in yet another MemoryBuffer (or something) with a cached address will only make the overhead of a buffer even higher. I might want to sacrifice a little performance for the ability to use java buffers directly and not through a proxy.

We sure need some kind of wrapping. Bounds checking can be easily added this way, for even higher security. Besides, I believe most of us use some kind of custom object that holds a ByteBuffer and its address. I use such an object all the time and found it really easy to use.

Quote
... And because this is a fairly important and large part of the LWJGL api, please post your best decision here before implementing it.

Totally agreed. We should get a preview of the new API, before releasing anything, so that we can comment on it. We should make the desicion together.

About performance: Even if the overhead was higher (which isn't), GL methods that take a pointer as an argument are called only a few times per frame (usually). So I also believe that performance won't be a problem.

A couple of questions:

- How is ByteBuffer createDirectBuffer(int address, int length) going to be used? This takes a pointer (address) no matter what. I believe it should be left as is.

- From native code: (jint) env->GetDirectBufferAddress(buf);
This is what adds the 300ns overhead? This would be called everytime we pass a ByteBuffer to a GL method?

OK, my suggestion is to go for a proxy/wrapper, an object from inside the LWJGL API that wraps a ByteBuffer and caches it's address. Make anything that has to do with pointers "LWJGL private", add some bounds check and then some people may stop whining about pointers. And we need a solution about the offsets (as elias pointed out). Anyway, whatever you decide to do, post it here first!
Title: LWJGL discussion forum officially migrates here today!
Post by: spasi on June 12, 2003, 13:48:15
Quote
1) All code is broken. Hurrah! Including my own. So I must have a good reason.

Not a problem. We are used to refactoring our entire project...  :lol:
Title: LWJGL discussion forum officially migrates here today!
Post by: elias on June 12, 2003, 14:05:15
the createDirectBuffer needs to go too. It is only used by calls like wglAllocateMemoryNV which need to return a ByteBuffer directly.

- elias
Title: LWJGL discussion forum officially migrates here today!
Post by: William on June 12, 2003, 16:20:29
Cas, this is such a great decision that I felt compelled to register just to compliment you on making it. I have not really used or been involved in LWJGL, even when I have coded directly to the OpenGL layer I have just used GL4Java, and the use of int pointers was one major reason for that. The API is definitely moving in the right direction.
Title: LWJGL discussion forum officially migrates here today!
Post by: princec on June 12, 2003, 16:39:13
Oh goody! It seems to be a pretty popular decision.

I'm not sure where the fear of slices() comes from - they're tiny little objects really. To create a vertex buffer you'd have, what, maybe 3 slices? Vertices, normals, texcoords? Not a big deal really. You should see all the nasty static final int offsets I use with the current system.

Remember, we're not setting up these buffers every frame or anything - probably just once at init. We've got to let go our hangups about creating objects, because they're important!

Cas :)
Title: LWJGL discussion forum officially migrates here today!
Post by: jbanes on June 12, 2003, 16:44:43
Actually, creating objects isn't so bad. It's the deleting part that'll get you. :)
Title: LWJGL discussion forum officially migrates here today!
Post by: spasi on June 12, 2003, 19:35:25
Quote
the createDirectBuffer needs to go too. It is only used by calls like wglAllocateMemoryNV which need to return a ByteBuffer directly.

From the ARB_vertex_buffer_object extension specification:


The entire data store of a buffer object can be mapped into the client's address space by calling

void *MapBufferARB(enum target, enum access);

with <target> set to ARRAY_BUFFER_ARB.  If the GL is able to map the buffer object's data store into the client's address space, MapBufferARB returns the pointer value to the data store.


And more functions like this one are coming soon (see pixel_buffer_object extension, GL2 will have more...)

We HAVE to be able to create a ByteBuffer from a pointer address.

Cas, you're right that a few slices won't be problem (for performance/memory), but having to keep 3 ByteBuffers around, instead of a single one...I think it's probably gonna get ugly. We'll support any decision you guys make, but please consider all the possible solutions first.
Title: LWJGL discussion forum officially migrates here today!
Post by: princec on June 12, 2003, 19:38:29
Worry not - instead of returning pointers we will be returning DirectByteBuffers, set with appropriate limits, too :)

And sure it'll be a bit ugly but that's Java for you. Until we get Doug Twlleager to do structs for us.

Cas :)
Title: LWJGL discussion forum officially migrates here today!
Post by: elias on June 12, 2003, 22:02:43
I think what worries spasi is the VBO case which could potentially create a new buffer at every mapping, that is, every frame. But those kind of problems could probably be solved by only returning a new buffer if the address changes (which it probably won't in the general case)

- elias
Title: LWJGL discussion forum officially migrates here today!
Post by: princec on June 12, 2003, 22:59:58
Hm, you've got a point there. And you won't be able to do that clever trick because that means keeping a hash map of buffer configurations to addresses lying around doesn't it? And that's asking for trouble.

Having said that - creating a buffer every frame won't tax the gc at all. Even creating 100 buffers every frame won't I don't think.

Cas :)
Title: LWJGL discussion forum officially migrates here today!
Post by: elias on June 13, 2003, 06:50:20
Well you could always pass the old buffer to the map method and then it would only return a new one if the old is null or has different address. A litle ugly but better than addresses I think. The check is going to be performed by the app anyway to avoid new buffers anyway.

- elias
Title: LWJGL discussion forum officially migrates here today!
Post by: elias on June 14, 2003, 12:21:06
To move the discussion here where it belongs from "nVIDIA's Cg" thread:

We're to determine a good solution to the int pointers -> buffers convertion problem. My personal favorite right now is to use ByteBuffers directly and fetching the address from C native code. To get insight on just how costly this is, I created a test, using glVertexPointer as my testing point. It will not compile other places than here, because I added a buffer version of glVertexPointer to my private library.

Here's the prototype for the new glVertexPointer:


public void vertexPointer2(int size, int type, int stride, ByteBuffer buffer);


Here's the code:


package org.lwjgl.test;

import org.lwjgl.*;
import org.lwjgl.opengl.GL;
import java.nio.*;

/**
* @author Elias Naur
*/
public class NativeCallTimingTest {
       private final static int WARMUP_ITERATIONS = 5;
       private final static int ITERATIONS = 10000000;

       public static void main(String[] args) {
               GL gl = null;

               try {
                       gl = new GL("WindowCreationTest", 50, 50, 320, 240, 16, 0, 0, 0);
                       gl.create();
               } catch (Exception e) {
                       e.printStackTrace();
               }
               System.out.println("Display created");

               gl.tick();

               long time_taken_nounpack = 0;
               long time_taken_unpack = 0;
               for (int j = 0; j < WARMUP_ITERATIONS; j++) {
                       long before;
                       long after;
                       ByteBuffer buffer = ByteBuffer.allocateDirect(4096).order(ByteOrder.nativeOrder());
                       int address = Sys.getDirectBufferAddress(buffer);
                       before = System.currentTimeMillis();
                       for (int i = 0; i < ITERATIONS; i++)
                               gl.vertexPointer(4, GL.FLOAT, 0, address);
                       after = System.currentTimeMillis();
                       time_taken_nounpack = (after - before);
                       before = System.currentTimeMillis();
                       for (int i = 0; i < ITERATIONS; i++)
                               gl.vertexPointer2(4, GL.FLOAT, 0, buffer);
                       after = System.currentTimeMillis();
                       time_taken_unpack = (after - before);
                       System.out.println("No unpack, time taken: " + time_taken_nounpack + " millis");
                       System.out.println("With unpack, time taken: " + time_taken_unpack + " millis");
               }
               double ratio = (double)time_taken_unpack/time_taken_nounpack;
               System.out.println("FINAL: No unpack, time taken: " + time_taken_nounpack + " millis");
               System.out.println("FINAL: With unpack, time taken: " + time_taken_unpack + " millis");
               System.out.println("FINAL: Ratio unpack/nounpack: " + ratio);

               gl.destroy();
       }
}


And here's the result:


[elias@ip172 tmp]$ java -Djava.library.path=. -cp lwjgl.jar:lwjgl_test.jar org.lwjgl.test.NativeCallTimingTest
Display created
No unpack, time taken: 2979 millis
With unpack, time taken: 7154 millis
No unpack, time taken: 2970 millis
With unpack, time taken: 7075 millis
No unpack, time taken: 2927 millis
With unpack, time taken: 7070 millis
No unpack, time taken: 2909 millis
With unpack, time taken: 7152 millis
No unpack, time taken: 2893 millis
With unpack, time taken: 7126 millis
FINAL: No unpack, time taken: 2893 millis
FINAL: With unpack, time taken: 7126 millis
FINAL: Ratio unpack/nounpack: 2.4631870031109573


So a rough 2.5 times increase in time taken per call with one buffer.

- elias
Title: LWJGL discussion forum officially migrates here today!
Post by: elias on June 14, 2003, 12:33:28
The overhead is roughly 400 nanos if you do the math.

- elias
Title: LWJGL discussion forum officially migrates here today!
Post by: princec on June 14, 2003, 12:44:58
That correlates pretty much with what I discovered - about 300ns on my 1.2ghz machine.

Or, if you extrapolate, about 1us on a low end 350Mhz machine. So you could really get away with a thousand of them and still only use 5% of your frame time. And if you need to do a thousand calls you're almost certainly not going to get performance anyway on something that low-end as you'll be rendering far, far too much for its weedy graphics card.

Cas :)
Title: LWJGL discussion forum officially migrates here today!
Post by: elias on June 14, 2003, 12:49:34
Yes I forgot that, my machine is a 700 MHz athlon.

So this means that this is a viable solution we're all happy with? I'm okay with that, as long as we change the calls to be static at the same time.

Should we poll it or is there not enough good alternatives to warrant one?

- elias
Title: LWJGL discussion forum officially migrates here today!
Post by: princec on June 14, 2003, 12:52:40
There are no real alternatives to it that will give us security and credibility.
The next question is do we implement it natively as macros or a function call? Function call will result in a much smaller library, but with another 100ns overhead... unless it gets inlined of course...

Cas :)
Title: LWJGL discussion forum officially migrates here today!
Post by: elias on June 14, 2003, 15:02:15
I'm not sure what you mean by macro or function - is it the env->GetDirectBufferAddress call you mean?

- elias
Title: LWJGL discussion forum officially migrates here today!
Post by: princec on June 14, 2003, 15:14:19
Well, we can either do

void Java_blah_vertexPointer(int a, int b, int c, jobject vertexBuffer) {
void * buf = GET_BUFFER_ADDRESS(vertexBuffer);
glVertexPointer(a, b, c, buf);
}

or we can do

void Java_blah_vertexPointer(int a, int b, int c, jobject vertexBuffer) {
void * buf = getBufferAddress(vertexBuffer);
glVertexPointer(a, b, c, buf);
}


One will end up with a pretty big dll, one won't. Unless the compiler inlines getBufferAddress for us.

Cas :)
Title: LWJGL discussion forum officially migrates here today!
Post by: elias on June 14, 2003, 15:37:49
The function thing won't work unless you pass the JNIEnv * too, and in that light I'd prefer a macro or simply

void * address = env->GetDirectBufferAddress(buffer);

Any reason not to use the function directly?

- elias