glVertexPointer and Buffer

I am wondering about the strategy of using direct buffers with glVertexPointer. With GL4Java, I had code like


gl.glVertexPointer(2, gl.GL_FLOAT, 0, series.getArray());

where “series” was an instance of my DataSeries class, which basically wraps a float array and provides getArray() and setArray() along with other functionality.

JOGL uses a Buffer with glVertexPointer but, apparently, it requires it to be direct. Therefore, FloatBuffer.wrap(array) does not work, as it creates a non-direct buffer. I found that the following code works as a functional replacement


float[] array = series.getArray();
ByteBuffer bBuf = ByteBuffer.allocateDirect(4 * array.length);
bBuf.order(ByteOrder.nativeOrder());
FloatBuffer fBuf = bBuf.asFloatBuffer();
fBuf.put(array);
gl.glVertexPointer(2, gl.GL_FLOAT, 0, fBuf);

The problem is that the array can be any size. On occasion, the application is memory bound because of it. I’m not sure I like being forced to take an existing array, allocate new space for it, and copy it in, when space is already tight.

The only alternative I see is to rewrite the DataSeries class to be based around a Buffer instead of an array. Any code that allocates an array and creates a DataSeries from it would need to be modified to allocate a Buffer instead. This would be a considerable effort. Is there any alternative to this?

Is there a reason that JOGL can’t work with a non-direct buffer? According to the Java documentation for ByteBuffer, “In general it is best to allocate direct buffers only when they yield a measureable gain in program performance.” Would giving the option of a non-direct wrapped array be a big drain on performance or be harder to implement?

The reason JOGL requires a direct buffer is due to the semantics of glVertexPointer and the other vertex array routines, which require a persistent pointer unaffected by garbage collection. There are details about this in the JavaOne 2002 slides on OpenGL for Java which are linked from the JOGL home page. If your DataSeries class is intended to interoperate with glVertexPointer then I would srongly recommend you convert it to use direct buffers. You can achieve very close to C speed by doing so; see the VertexArrayRange and VertexBufferObject demos in the jogl-demos workspace.

There maybe a third way to do this.

You don’t need to use a single huge buffer to contain all vertices. Allocate a buffer for say 5000 vertices and then rendern your vertices in chunks.

If your data changes on each pass this is with probably faster than copying all data to a huge buffer and then render it with one call. A small buffer will fit into the processors caches, a large one must be in main memory.

[quote]The only alternative I see is to rewrite the DataSeries class to be based around a Buffer instead of an array. Any code that allocates an array and creates a DataSeries from it would need to be modified to allocate a Buffer instead. This would be a considerable effort. Is there any alternative to this?
[/quote]
Perhaps it would be best to hide the implementation of the DataSeries class - that is, if the user does not need to know of the contents of the class.

Is there a reason not to allocate the buffer/array inside the DataSeries constructor, for example? So, for example, now you have:

float[] array = new float[100];
DataSeries ds = new DataSeries(array);

could this be transformed into:

// Hides implementation
DataSeries ds = new DataSeries(100);

Then again, you might need to access the specific array out side the class and I’m totally wrong… :slight_smile:

I do frequently access the array from outside the DataSeries object. A quick count shows 26 classes that modify the array on their own. The pattern I was using was for a file-parser class to allocate an array, read vertex data into the array, and then create a new DataSeries from the array. Later, the user might add a vertex so an editor class would retrieve the array, allocate a new array that is one vertex larger, and copy the existing vertices. The editor class would then call setArray(newArray) on the DataSeries to replace the old array.

I have a number of editor classes for different types of objects. Some only allow modifications. Others allow adding or deleting. The DataSeries array is interleaved and holds any number of dimensions. For example, it could hold a 2D Time-Value plot, a 3D X-Y-Z surface, a 5D Time-X-Y-Z-Scalar animation of an object that varies in location and color.

The DataSeries getArray() method can be changed easily from “return array” to “return buffer.array()”. But the DataSeries.setArray(float[]) method will have to be changed to setBuffer(Buffer), which means the parsers and editor classes will have to create a buffer for the DataSeries instead of an array.

I do handle some edit operations within the DataSeries class, but I try to keep them general like shift, multiply, resample, etc. Some operations, such as frequency filtering, are more complex and should be in a separate class that operates on the DataSeries.

I’ve begun recoding them. I’m about half way right now.

The DataSeries getArray() method can be changed easily from “return
array” to “return buffer.array()”

This won’t work. Direct buffers have no backing array.

Well, I went on vacation for the holidays, then fought with upgrading to a new computer, and did some other work. I finally got back to my “convert from GL4Java to JOGL” project last week. I put in over a day switching the syntax from allocating arrays to allocating buffers. Then I was finally able to compile the app without errors and I found that the optional Buffer array() method doesn’t work with direct buffers and I apparently have to use get() and put(). Then I logged in to the JOGL forums and saw the previous post, which stated the same. Great.

There are so many array accesses in my code. Things like array1[i] = array2[j] + array3[k]. There is no way I’m changing all that to get() and put() on buffers. It’s time prohibitive, not to mention the significant chance of introducing errors. My app isn’t just an OpenGL app. It does a lot of other things, which users relay on. I can’t drop everything and completely restructure my design, rewrite thousands of lines of code, and risk introducing bugs just to take advantage of slightly faster buffers. I couldn’t justify it for 10x faster buffers.

For compatability, I suggest offering a lower performance alternative that uses arrays with glVertexPointer the way GL4Java did. For some people, such as myself, there isn’t time for big rewrites like this. The only time-effective alternative I see now is converting to for-loops with glVertex. I may be able to convert the most performance critical code to Buffer and glVertexPointer as I get time later.

[quote]For compatability, I suggest offering a lower performance alternative that uses arrays with glVertexPointer the way GL4Java did.
[/quote]
GL4Java’s glVertexPointer variants taking arrays weren’t slower; they were unsafe, storing pointers in to the Java heap, and a random GC could cause either data corruption or crashes. This is completely unacceptable.

You can implement a glVertexPointer wrapper in your code which would behave exactly as efficiently (or maybe even more so) as any wrapper we could provide in JOGL; allocate a direct buffer in your code and put() your entire array in to it. Keep the direct buffer around from frame to frame.

I converted it as you said with the exception of keeping the buffer around from frame to frame. I allocate a new one with every redraw and I quickly crash with an OutOfMemoryError.

I can change the code to reuse the buffer as long as the underlying data don’t change, but if the user adds points to the underlying array, I will have to allocate a new, larger direct buffer. How do I deallocate the old one to avoid a memory leak?

[quote]I converted it as you said with the exception of keeping the buffer around from frame to frame. I allocate a new one with every redraw and I quickly crash with an OutOfMemoryError.
[/quote]
This is happening because when you allocate a direct buffer you are allocating C storage which only gets cleaned up when the direct buffer is GC’d. You’re putting enormous pressure on the system by allocating tons of C heap memory referenced by tiny Java objects that aren’t being GC’d since the heap isn’t full. If you want to do this strategy you are pretty much going to have to hold on to the direct buffer from frame to frame.

[quote]I can change the code to reuse the buffer as long as the underlying data don’t change, but if the user adds points to the underlying array, I will have to allocate a new, larger direct buffer. How do I deallocate the old one to avoid a memory leak?
[/quote]
Just lose the reference to it and it will be reclaimed. You should probably make the direct buffer somewhat larger than the underlying array and grow the direct buffer exponentially (i.e., multiply its size by 2 when expanding) to prevent needing to expand it every time the user adds a point.