the Fiddler's picture

Investigate alternatives for the object overloads

Project:The Open Toolkit library
Version:0.9.6
Component:Code
Category:task
Priority:normal
Assigned:the Fiddler
Status:closed
Description

Object overloads wrap native functions that take void* parameters. OpenTK pins the parameter specified by the user and passes the address down to the unmanaged library.

OpenGL and OpenAL interact with value types or arrays of value types (i.e. int, float, Vector3 and similar types, as well as combinations and arrays of those). However, object overloads completely suppress type-checking and allow the user to pass any managed type to unmanaged code. This is unintuitive and potentially dangerous (crash-prone).

It is not possible to specify beforehand every possible structure the user may want to use. As a compromise, it is possible to use generic parameters to allow only T[] and ref T parameters, where T : struct. While this is not a 100% fail-safe solution (the struct constraint allows references to reference types), it should significantly reduce the dangers introduce by the type-less object overloads, with no loss of functionality. As an added advantage, these overloads will be symmetric to existing typed array overloads (i.e. T[] to int[] and ref T to ref int).

Because the pinvoke layer cannot deal with generic parameters, we need to pin and pass a pointer instead. With this implementation, a hack may be employed to avoid using the heavy-weight GCHandle class: a C# union can cast T[] to a byte[], allowing us to obtain a direct pointer to the underlying data.

This trick works on both .Net and Mono, seemingly without side-effects. A proof of concept is attached (the solution is intended for MonoDevelop 2 beta 1 or higher. It will probably require some modifications to work with Visual Studio or SharpDevelop). Note that you might need to copy manually the native library to the same directory as the managed exe.

AttachmentSize
ArrayInteropHack v1.zip9.12 KB

Comments

Comment viewing options

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

#21

This may be somewhat academic as I think most 3d applications are still being written single threaded, but I think the

[StructLayout(LayoutKind.Sequential, Pack=1)]

can break the atomicity guarantee for load and store operations on structure members who become misaligned.

the Fiddler's picture

#22

There are no atomicity guarantees for value types of size greater than the platform bitness, anyway. For example, a field of type Vector3 cannot be updated atomically without using a synchronization primitive.

I can only think of one use-case were you could likely encounter such an issue, namely asynchronous updates of shader uniforms (one thread updates the uniforms and the other uploads them to OpenGL). However atomicity on the field level is not enough in this case: say you have a Vector3 uniform that indicates the position of a light. Do you really want to upload the uniform with a new X, Y but an old Z field?

I may be wrong here (if so, please point it out!), but I don't think you will encounter this issue in action. On the other hand, alignment problems are far more likely to rear their ugly head.

Kamujin's picture

#23

I was referring to the Vector's members, which as float, one would expect to be atomic load / stores if one were not aware of the possibility for misaligned memory.

I agree this is unlikely to be a problem, but I think it is possible.