syr1111's picture

AccessViolationException when using GL.DrawArrays()

I have a simple function that draws a set of Quads (just rectangles) and a set of lines using a series of X,Y points that are part of a 2D graph. This normally works fine. However on two occassions I've had a very rare error condition that I've only run accross if I let the app run over several days straight. It's set up to cycle the same type of data at about 30fps continuously and render this graph using code like the example below. What happens is that eventually I get a SystemAccessViolationException on the GL.DrawArrays call.

            PointF[] points = new PointF[mostNumPointsNeeded]; // worst-case size needed
            PointF[] quadPoints = new PointF[mostQuadPointsNeeded]; // worst-case size needed
 
            // Fills in X,Y points in arrays from source data
 
            unsafe
            {
               GL.VertexPointer(2, VertexPointerType.Float, sizeof(PointF), quadPoints);
 
               // Draw the filled quads
               GL.DrawArrays(BeginMode.Quads, 0, numQuadPoints);
 
               // Draw outlines of quads
               GL.PolygonMode(MaterialFace.Front, PolygonMode.Line);
               GL.DrawArrays(BeginMode.Quads, 0, numQuadPoints);
               GL.PolygonMode(MaterialFace.Front, PolygonMode.Fill);
 
               // Draw lines
               GL.VertexPointer(2, VertexPointerType.Float, sizeof(PointF), points);
               GL.DrawArrays(BeginMode.LineStrip, 0, numPoints);
            }

Could there be any problem with doing two DrawArrays calls using the same vertex array with only 1 call to VertexPointer? I see that the OpenTK VertexPointer function pins the pointer for the array down when passing to Delegates.glVertexPointer(), but do I also need to pin the array after creation until I'm done using it? Unfortunately since this works 99.99% of the time I don't have any good ideas why this could be a problem. Has anyone experienced AccesViolation problems with Vertex arrays?


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

As long as your vertex data is stored in GC-enabled memory (i.e. a .Net array), you need to keep it pinned for the lifetime of the application. OpenTK will only pin it until the VertexPointer call returns, which is enough for modern vertex buffer objects (which are copied into server memory) but not for vertex arrays (which are stored in client memory).

What happens is that the GC may collect or relocate your vertex array between the GL.VertexPointer() and GL.DrawArrays() calls, invalidating the vertex pointer and causing an access violation. The larger the distance between the two calls and the higher the memory pressure, the sooner this error condition will show up. In contrived test cases this can show up in mere seconds.

Possible workarounds:

  1. Use vertex buffer objects. Pros: can be several orders of magnitude faster, no need to worry about the GC. Cons: require GL 1.5 or ARB_vertex_buffer_object extension.
  2. Pin the vertex array after allocation: Pros: takes care of the GC issue. Cons: harms GC performance, increases memory consumption.
  3. Store vertex arrays in unmanaged memory, using Marshal.AllocHGlobal(). Pros: takes care of the GC issue, no performance/memory hit. Cons: harder to work with (need to use Marshal.Read*/Write* or unsafe code).
  4. Pin the vertex array and call GL.VertexPointer(), GL.DrawArrays() and GL.Finish() before unpinning it. Pros: doesn't impact GC efficiency like #2. Cons: stalls rendering and hurts throughput.

My preferred solution is to use VBOs, falling back to display lists & immediate mode when they are not available. Vertex arrays are not worth the trouble (if the drivers don't support VBOs, the card is probably too old/slow anyway. Immediate mode won't really hurt in that case.)

syr1111's picture

Thanks, I thought it might be related to that, but did not know the details of the behavior. It explains the rarity of the exception (only when the GC decides to move the memory in between those calls).

I just used vertex arrays instead of VBOs because the data is constantly changing and my impression is that VBOs have no performance benefit if you always need to send a new set of data to the card's memory. I do use one of the arrays twice in this example, but I do similar work in a number of other spots and for most of them each array is only used once. Is there still a performance benefit to using VBOs if the length of my array and the data in it changes every time?

I'll weigh the options you presented and give it a shot.

Again, thanks a lot for the great, thorough answer.

the Fiddler's picture

There are ways to improve streaming performance with VBOs. I don't have much experience in that area, but the opengl wiki has a page dedicated to buffer object streaming. I have seen acceptable performance with the draw, discard (GL.BufferData(..., null)), update pattern, and there's a good chance that fences/unsynchronized maps could be even faster (maybe treating a single, large VBO as a ring buffer, drawing and updating different parts in parallel).

However, even plain VBOs should be fast enough. Since the implementation is so similar to VARs (4-5 lines difference), I'd try that first and see how it works out.

(Off-topic: there's no need to place all OpenGL calls inside an unsafe region. This is only necessary if you use an actual pointer.)

syr1111's picture

OK, thanks.

Actually it's the sizeof() calls that required the unsafe code, so this was an "odd" example in that sense, the rest of the OpenTK calls I don't have in unsafe blocks.