glDrawArrays: when has it finished?

Started by
8 comments, last by Brother Bob 12 years, 11 months ago
Pseudocode:

[source]
void draw()
{
Vertex* vertices = scene.GetVertexArray();
glEnableClientState(...);
glVertexPointer(..., vertices);
glDrawArrays(...);
glDisableClientState(...);
delete vertices;
}
[/source]
I'm not using VBO since I want to support older OpenGL implementations.

After calling glDrawArrays, I may want to:

  • deallocate my vertex array ("delete vertices;")
  • perhaps modify some of the vertices

However, GL is free to perform the glDrawArrays asynchronously, and it's not safe to deallocate or modify my array until it has finished.

I could do a glFinish to ensure that, but it'd slow down the app.

So at what moment am I free to deallocate/modify my vertex array?

I'd appreciate if you have some reliable reference for your answer. Thanks.
Advertisement
From your application's point of view, glDrawArrays is finished as soon as it returns. You are free to delete the array as soon as you want; any buffering, which is likely to happen, is all OpenGL's internal concern. If it decides to buffer your array, it has to do so with an internal copy of your buffer and not with your buffer.

You can never change the outcome of any function once it has returned. Once called, the outcome is required to be as if no buffering has happened. This applies not only to vertex arrays, but to any function that relies on indirect data, such as glTexImage, glVertex3fv, and other functions that essentially take pointers to their actual arguments.
Thanks Bob.

Now that I've got that cleared up, I'll take the question one step further.

Can glDrawArrays get messed up if I pass it an array from within a GC'd language? I'm talking about C#/.NET, in which I'm using the OpenTK bindings for OpenGL. See this discussion: page 1, page 2.

Quotes from "The Fiddler" (OpenTK maintainer) from that page:

The vertex arrays are stored in managed memory and may be moved by the Garbage Collector. While this can be worked around (either by allocating unmanaged memory, or by pinning the objects in question), doing this will cause a performance hit.[/quote]
The code sample above will work correctly as long as any of the following is true:

  1. All GL.DrawElements calls finish within the fixed section. or
  2. GL.DrawElements spill outside the fixed section, but the GC does not touch m_vertices because it is placed in the LOH. or
  3. GL.DrawElements spill outside the fixed section, m_vertices are not in the LOH, but no GC happens while GL.DrawElements are executing.

You can ensure that (1) will always happen, by placing a call to GL.Finish just before leaving the fixed statement, which will block until all GL.DrawElements calls are complete. Without this, your graphics drivers will determine whether the calls will finish on the spot, or they will be queued up and executed later on.
[/quote]

Also, from the OpenTK docs:

Due to the asynchronous nature of OpenGL, GL.Finish() must be used to ensure that rendering is complete before the arrays are unpinned. However, this call introduces a sync point between the CPU and GPU, which can significantly degrade performance.
[/quote]

So, the guy says the DrawArrays call should be in a fixed{} section - I can agree with that. If you're unfamiliar with .NET, it means the GC won't relocate the array memory until the end of the fixed{} section.

But he says that's not enough. He says even after the end of fixed{}, DrawArrays may still access the array due to GL's async nature. Which necessitates the GL.Finish().

Thoughts?
So basically, C# or OpenTK can move around the array between the call to GL.DrawArrays and the point where the OpenGL implementation takes over, and OpenTK won't handle such a situation to ensure that the correct pointer is passed to OpenGL? Personally, and completely without experience of either C# or OpenTK, this sounds like a really poor binding library. If this is a common thing with C#, that arrays can be relocated the way you imply, how can any unmanaged code assume anything about arrays being passed to it, if the arrays can relocate suddenly?

My thoughts on the comments is that the author believes glDrawArrays won't internally copy the array if it decides to buffer, and incorrectly states that you have to manually flush the command buffer to ensure that the garbage collector doesn't interfere with OpenGL.
glDrawArrays is not an async operation, therefore their docs is wrong. If the docs say that you should pin an array, then I guess you have because the GL library itself is unmanaged code. It is a bit odd since in VB6 you don't need to pin an array and there is no such feature and it works fine. I did at one point use VB.net and opentk. After a week of struggling, it turned out that the problem was with opentk. It wasn't a problem with my arrays at all. Pinned didnt help. It would simply not render. The same code in vb6 and C++ was fine.

I can suggest that bite the bullet and pin your arrays. Dont worry about performance. If perf is issue, then switch to C++.
Sig: http://glhlib.sourceforge.net
an open source GLU replacement library. Much more modern than GLU.
float matrix[16], inverse_matrix[16];
glhLoadIdentityf2(matrix);
glhTranslatef2(matrix, 0.0, 0.0, 5.0);
glhRotateAboutXf2(matrix, angleInRadians);
glhScalef2(matrix, 1.0, 1.0, -1.0);
glhQuickInvertMatrixf2(matrix, inverse_matrix);
glUniformMatrix4fv(uniformLocation1, 1, FALSE, matrix);
glUniformMatrix4fv(uniformLocation2, 1, FALSE, inverse_matrix);

So basically, C# or OpenTK can move around the array between the call to GL.DrawArrays and the point where the OpenGL implementation takes over


No, that can't happen. The problematic sequence is:

  1. I pin my array in place so C# can't move it
  2. I call GL.DrawArrays and it returns. But it hasn't actually done any work or copying yet.
  3. I unpin my array since it's bad for performance to keep it pinned for long (and it can't get garbage collected if it's pinned)
  4. C# moves the array
  5. GL gets around to executing my GL.DrawArrays call and fails.


If you're right and GL.DrawArrays internally copies the array before returning, then the failure can't happen. Do you have any reference on that, though?
If OpenTK can take the pointer to the underlaying memory without making a copy of the original data, return before making the call to OpenGL, and delay the call to a point where the actual array may have been destroyed or moved outside the control of the application, then that is some really shitty behavior. I really cannot see any reason why it would do that. OpenGL is already synchronous, and that would be a really poor attempt at making it asynchronous, something the internal buffer is likely already doing behind you back, and doing so correctly.

I simply don't believe that 5 may happen after 3 and 4.

If OpenTK can take the pointer to the underlaying memory without making a copy of the original data, return before making the call to OpenGL...

No, that's not what I meant. OpenTK doesn't do that, it calls the function immediately. But, in OpenGL, how do you know glDrawArrays is guaranteed to copy the array before returning? I don't see that in the spec. The spec just defines glDrawArrays to be equivalent to "glBegin; loop over all vertices and send them; glEnd;". Is that where you infer the copying guarantee from?
Yes, the key part in your description is "send them".
I should say, the copying itself is not inferred from the description, but the synchronous behavior is. It is not necessary that a copy has to be made, although that is probably what has to be made to ensure that the buffer can safely be released immediately after the call returns, which is what is important.

This topic is closed to new replies.

Advertisement