There has been some good progress over the past few days.
Now all the Texture2D<T>, Texture2DMS<T>, TextureCube<T>, Buffer<T> etc… have stubs and get converted by the system.
However, most of my work has been focused on implementing a good system for constant buffer. As 3D pipeline becomes more and more shader-centric, I believe string-driven constant buffer system as it exists in OpenGL and D3D is something that should be avoided.
Of course, as part of my effort to write shader in C#, there is already constant buffer definitions lying in the assembly. After giving it some thoughts, I ended up with a system for generating optimized proxy from a Constant Buffer C# interface taken from the shader signature.
As an example, let’s say we have those C# interface being referenced as constant buffer from the shader code in C# (check previous posts for more info):
public interface IStructTest
{
SVector2F Test3 { get; set; }
SVector2F Test4 { get; set; }
}
[ConstantBuffer]
public interface IConstantBuffer1
{
SVector1F Test { get; set; }
SVector1F Test2 { get; set; }
IStructTest Struct { get; }
}
To obtain a constant buffer instance implementing that interface and change its content, user only needs to do something along the line of (this piece of code will probably be simplified later):
var proxyType = ProxyGenerator.GenerateConstantBufferType<IConstantBuffer1>(shader.ConstantBuffers["cb1"]);
var cb = (IConstantBuffer1)Activator.CreateInstance(proxyType);
// Then we can play with our buffer:
testInstance.Test = new SVector1F(32.321f);
testInstance.Test2 = new SVector1F(32.321f);
testInstance.Struct.Test4 = new SVector2F(24.0f, 24.0f);
testInstance.Struct.Test3 = new SVector2F(12.0f, 24.0f);
What the hell is going on? Under the hood, the proxy generator is using Reflection.Emit to generate optimized code for filling a buffer (byte[]) very efficiently through its auto-generated “set” properties Most of the copy is done directly with memcpy (OpCodes.Cpblk) and offset/size are hardcoded directly into the type, without any backing field. On top of that, the root constant buffer object will have its IsDirty flags updated to know if the buffer needs to be reuploaded (might consider range IsDirty support later, not sure if it is worth it as Constant Buffer should be split depending on their upload frequency). Please note that a constant buffer type generated by the proxy generator is done using information only known at runtime (OpenGL and D3D both have different way to layout data and do padding). However, nothing prevents the implementation of statically defined padding (such as std140 layout in OpenGL). The point is, user doesn’t need to worry about that, all the buffer layout is done from the shader reflection when constructing the type.
As a result, this system offers an almost optimal (close to hardcoded C) way to fill constant buffer while not writing a single line of code (except the interface). Also, since it maps to a real type, there is no needs for the error-prone GetVariableByName() or glGetUniformLocation() at all. Overhead happens only first time a constant buffer proxy type is requested (then it is cached for future instantiation anyway).
Overall, as the different part of this shader system are getting in place, I feel very excited about the new possibilities and ease of use for fast shader prototyping (with almost no restriction compared to writing shaders in HLSL). I believe such a system could be the base for writing very powerful 3D middleware. This is still in early stage of development, but I expect to be able to do some rendering with it very soon.