SIMD support in Mono

by Miguel de Icaza

The other day Cody Russell was asking on IRC what we could do in the Mono universe to take advantage of some special instruction sets like SIMD or how to use C# to generate code for pixel shaders or vertex shaders.

One idea that we discussed was the creation of a library that would be able to expose this kind of functionality. Take for example the following Cg example:

// input vertex
struct VertIn {
    float4 pos   : POSITION;
    float4 color : COLOR0;
};

// output vertex
struct VertOut {
    float4 pos   : POSITION;
    float4 color : COLOR0;
};

// vertex shader main entry
VertOut main(VertIn IN, uniform float4x4 modelViewProj) {
    VertOut OUT;
    OUT.pos     = mul(modelViewProj, IN.pos); // calculate output coords
    OUT.color   = IN.color; // copy input color to output
    OUT.color.z = 1.0f; // blue component of color = 1.0f
    return OUT;
}
	

A C# rendering of the above would be:

class MyShader {
	struct VertIn {
	    [Position] float pos;
	    [Color]    float color;
	};
	
	// output vertex
	struct VertOut {
	    [Position] float pos;
	    [Color]    float color;
	};
	
	// vertex shader main entry
	public VertOut main(VertIn IN, [Uniform] float4x4 modelViewProj) {
	    VertOut OUT;
	    OUT.pos     = mul(modelViewProj, IN.pos); // calculate output coords
	    OUT.color   = IN.color; // copy input color to output
	    OUT.color.z = 1.0f; // blue component of color = 1.0f
	    return OUT;
	}
}
	

The above is a direct translation. Now, the value of the library would be basically a method that generates a delegate with the proper signature:


	Delegate Compile (MethodInfo method)
	
	

It would be used like this:

	delegate VertOut main_delegate (VertIn In, [UniForm][float4x4] model);
	...
	main_delegate shader;
	shader = (main_delegate) Compile (typeof (MyShader).GetMethod ("main"));
	...
	shader (my_in_data, model);
	

The Compile method would use the Cecil library to decompile the code, perform flow analysis and ensure that the code in the method passed (in this case main) meets the restrictions imposed by the target (in this case Cg's target).

In some cases (SSE instructions) you might want a delegate back, or a method token that you could then Reflection.Emit call. In some other cases you might want the code to give you some handle back that you can pass to your graphics hardware.

(Cecil has a very nice flowanalysis engine, the one used by db4objects to provide the LINQ-like functionality with today's C# compilers).

The above model can be extended to other operations, and in some cases the return value from Compile could be just a delegate to the original method (if the hardware is not supported).

Today a student interested in doing something very similar for the Summer of Code emailed me, so I decided to dump here some of the ideas.

Posted on 15 Mar 2007