Forum

VL - ToBufferDescription seems to fail inconsistently on custom structs

vl

#1

Not sure if I’m doing something wrong or it’s a problem with the node.

I have some custom structs as below:

[StructLayout(LayoutKind.Sequential)]
    public struct Light
    {
        public float LightType;
        public Color4 Colour;
        public float Intensity;
        public Matrix Transform;
        public Vector2 Params;

        public Light(LightType lightType, Color4 colour, float intensity, Matrix transform, float penumbra, float umbra)
        {
            LightType = (float)lightType;
            Colour = colour;
            Intensity = intensity;
            Transform = transform;
            Params.X = penumbra;
            Params.Y = umbra;
        }
    }

If I upload that to a ToBufferDescription node (by way of a FromValue to spread it), the data coming out of it seems corrupted. I am validating the results by doing a compute buffer readback within VVVV. More confusingly, some structs seem to work perfectly, like this one:

 [StructLayout(LayoutKind.Sequential)]
    public struct Material
    {
        public float MaterialType;
        public float IOR;
        public float Roughness;
        public Color4 Colour;
        public float Intensity;
        public float TextureIndex;
        public Vector2 UVScale;

        public Material(MaterialType materialType, float ior, float roughness, Color4 colour, float intensity,
            int textureIndex, Vector2 uvScale)
        {
            MaterialType = (float)materialType;
            IOR = ior;
            Roughness = roughness;
            Colour = colour;
            Intensity = intensity;
            TextureIndex = textureIndex;
            UVScale = uvScale;
        }
    }

After playing around with this quite extensively, I can’t seem to figure out what the problem is.

Another issue: custom enums in C# won’t work as-is. I have to treat it as a float in the struct, and then cast the enum argument from the constructor into a float to get the buffer to pick it up.

Any suggestions?

P.S. I am aware of the padding issue when it comes to structs being uploaded to GPU, at this stage I’m just trying to get stuff to work.


#2

There should be a c# example in the folder of dynamic buffer without VL… It may not work out of the box as a dynamic plugin, there is some bug with vvvv and it goes red (you have to delete folder called ‘bin’ inside folder with plug, that usually helps), anyways should work ok if you compile it in VS. Also there was a breaking change in dx but I think that code should be ok…


#3

hi @polyrhythm with which vvvversion are you working? there was some problem i remember a while ago…

you can try to pad the structs in c# and hlsl to be 32byte aligned and see whether that works, it will also give you some performance gain: https://developer.nvidia.com/content/understanding-structured-buffer-performance

but in any case, this should work out of the box and i’ve used it several times. do you have a patch to test it?


#4

hi @tonfilm. This is still a problem for me (and blocking development of my renderer), so I’ve set up a sample patch packaged with my C# code and VL node.

If you play with this, you’ll see that the Material seems to work as expected, but neither Primitive or Light do. If I use the manual stride version of the ToBufferDescription node, and set the stride to 4, I can actually get usable data out of it, for the most part, but it of course fails for spreads with more than one slice.

I’ve tried everything from randomly removing fields, manually setting stride, and getting things to align with 32 byte packing. I’m on the latest VVVV beta, 50.38.1.


#5

thanks for the patch, i saw that you define the input buffer as a StructuredBuffer<float> which is not really correct. for primitive data types like float, float2 and so on the correct syntax is Buffer<float>. some drivers still accept that, but it seems to be buggy.

the right way to do it is to define a struct in the shader that is identical to the c# struct in layout and use that as the type argument for the structured buffer:

struct LightSettings
{
	float LightType;
    float4 Colour;
    float Intensity;
    row_major float4x4 Transform;
    float2 Params;
};

RWStructuredBuffer<LightSettings> OutputBuffer : BACKBUFFER;

StructuredBuffer<LightSettings> InBuffer;

[numthreads(32,1,1)]
void CS(uint3 tid : SV_DispatchThreadID)
{
	uint count, stride;
	InBuffer.GetDimensions(count, stride);
	
	if (tid.x > count) return;
	
	OutputBuffer[tid.x] = InBuffer[tid.x];
}

technique11 Apply
{
	pass P0
	{
		SetComputeShader( CompileShader( cs_5_0, CS() ) );
	}
}

if you do it like this it works fine, also with spreads. see patch (add the ‘bin’ folder):
structured buffer (no bin folder).zip (6.9 KB)


#6

Thanks @tonfilm for taking the time to look at my sample and provide a fix. Super appreciate it.


#7

this is interesting, it would help a lot in coordinating dx11 stuff from vl in a very modular way, virtually bypassing vvvv because it is so streamlined.

the patch @tonfilm provided worked in current beta nicely, so I took the liberty to try it for vl Record for myself. Clickwork to mimik the provided class was good ux with control and shift and some shaky handcopying of defaults.

but then this happened:

image

see for yourself, if you want
vlbufferdx11.zip (10.4 KB)


#8

yes, VL records are not structs, you can only upload structs, that have a defined memory layout and passed by value instead of reference, to the GPU.


#9

would it be possible to create a struct from a vl Record, when manually providing such a layout (i guess mostly for meta about sequentiality), and being ready to accept an extra copy in memory?

edit: just asking for a friend, jaja
i feel this question touches a lot of undefined area in terms of vl, and for the meantime I will tell my friend to do a little c#

but gosh, would this be handy


#10

yes, totally agree, the closer we get to GPU and high performance computing, structs will be necessary. we have and issue for this and i think it would rather be a new entry Struct in the patch type drop down. it would need a different behavior, e.g. you need to order the data fields and it can not use process nodes because they would add an internal field.


#11

ah yeah I remember an option like this from node or so.

would be funny to see it comeback solely for marshalling and gpu feeding.