Anonymous's picture

MultiDrawElements() problem

It appears, MultiDrawElements requires an type parameter as Version14 enum. Is this expected behaviour? How is the type for the indices specified?


Comments

Comment viewing options

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

You should update to OpenTK 0.9.0 (frontpage has a huge button), which removes the VersionXY enums. You can then use e.g DrawElementsType.UnsignedInt

the Fiddler's picture

Edit: Oops, beaten :)

If updating is not an option, you'll have to cast from OpenTK.OpenGL.Enums.All.

Anonymous's picture

ok,Thanks! After updating to 0.9 the enums are fixed.
However I have not been able to provide the indices parameter for the MultiDrawElements. This is supposed to be a uint** in the c-World. I would expect it to correspond to an uint[][] in C#? Did you figure out, how to cast this to an IntPtr? Thanks again

the Fiddler's picture

I just checked the specs, and MultiDrawElements takes a void* in the C world, which translates into a IntPtr in managed code. OpenTK (and Tao) provide an object overload that hides the complexity of dealing with pointers; just create your uint[] or uint[][] array normally and pass it directly.

There's a slight possibility that the 2d array will not work (would appreciate some feedback here!), but the uint[] one is supported 100%.

Alternatively, you can "pin" the array manually, to obtain the necessary pointer:

unsafe
{
    fixed (uint* ptr = data)
    {
        GL.MultiDrawElements(..., (IntPtr)data, ...);
    }
}
haymo's picture

The problem arise, because .NET does not allow for arrays of reference types to be pinned. Pinning would only pin the container array of pointers, not the objects pointed to. Therefore one of the references could have been cleaned up in the meantime.
One workaround could be, to manually create an array of pinned pointers and pass this to OpenGL, but this is pretty much nonsense, I think.

So I'm using DrawElements in a loop now. May be I'll switch to using degenerated triangles some time...

By the way, the spec I retrieved from the official OpenGL site, declares indices as void** ? Did I miss something here? anyway, nice lib! ;)

the Fiddler's picture

Ah, of course you are correct. Just rechecked the function spec (.spec file) and it is an *array* of void pointers, hence void**. My mistake.

Looping over DrawElements is the way to go, then. I remember reading that MultiDrawElements doesn't offer any performance advantage over a DrawElements loop (can't find the reference right now), so this shouldn't be a big disadvantage. Can't think of any other clean solution right now, I'll add a warning on MultiDrawElements, just in case.

This also uncovered a small bug in MultiDrawArrays (the parameters are incorrectly marked as "out" in the .spec) - just fixed that :)

Edit: I've been thinking about this. Technically, void** would map to IntPtr[]. One could use Marshal.AllocHGlobal (or unsafe stackalloc) to allocate the necessary storage and call the function that way, but I'm not sure how useful that would be in practice. I'll run some tests and see if anything better or more intuitive shows up.

haymo's picture

I guess there IS a measurable overhead by simulating MultiDrawElements via DrawElements in a loop. Since we are in .NET (and yes, we do want to! ;), switching the context + pinning the array(s) to call unmanaged code this often will demand its price. Also, using AllocHGlobal seems somehow "from behind in the back" (how is this called in english?) One could just as well store & manage the vertex arrays in unmanaged code than... But did you archieve some progress in testing on this?

the Fiddler's picture

Not yet, I've been pretty busy the past few days.

On a modern Core 2 machine, the overhead typically is around 20ns for each OpenGL function call, compared to native calls (this is about the best one can hope for, without hacking the p/invoke code, which I plan to do at some point in the future). I'm fairly certain that drivers don't perform any specific optimization on MultiDrawElements (i.e. they probably loop over DrawElements), so the savings are not that significant (depending on the number of arrays, of course).

We are currently ramping up for the 0.9.1 release, so I might not find the time to test until then, but it's on the todo list. What's important right now, is that we find an interface to MultiDrawElements that is actually usable (void** and IntPtr[] aren't very nice, unless you plan to use unmanaged memory only). I was thinking of adding an object[] overload, which would handle the necessary pinning internally. This would be a little more usable, as well as faster if object is an array (but just as slow if it isn't).

If you have any other ideas of how to handle this function, I'm open to suggestions :)

Inertia's picture

I'm aware this is not a real answer to the original question, but I'd like to point out that strips aren't really an advantage on modern hardware. The memory cost is neglectible, no matter if you use ushort or uint, assuming you have a static mesh which can be stored in a VBO|DL.

object[] is probably the way to go for MultiDrawElements.

the Fiddler's picture

I've been trying to think of another way, but object[] does seem to be the way. The problem lies in pinning the references effectively: if object is a System.Array, we could avoid allocating GCHandles by using the fixed statement, but I can't see how to do that for a varying number of elements.

There probably is a way though, it's just a matter of finding out how.