Finding the best way to handle structure constructors with overlapping fields

I created a custom structure for processing RGBA values ​​that will be bound to the GPU.

In my type, I hold the individual components R, G, B, and A as byte values ​​and overlap an unsigned 32-bit integer (Uint32) to easily transfer and assign a packed value. I know the concept is obvious, but here is a sample structure for a good measure:

[StructLayout(LayoutKind.Explicit, Size = 4)] public struct RGBA { [FieldOffset(0)] public uint32 PackedValue; [FieldOffset(0)] public byte R; [FieldOffset(1)] public byte G; [FieldOffset(2)] public byte B; [FieldOffset(3)] public byte A; } 

Due to the way C # handles structures, each field must be explicitly specified in any constructor. In my case, this means that I have to assign values ​​twice in any constructors due to overlapping fields.

I could use:

 public RGBA(uint packed value) { R = G = B = A = 0; // initialize all to defaults before assigning packed value PackedValue = packedValue; } public RGBA(byte r, byte g, byte b, byte a) { PackedValue = 0; // initialize to default before assigning components R = r; G = g; B = b; A = a; } 

or I could first call the base constructor for each constructor as follows:

 public RGBA(uint packedValue) : this() { PackedValue = packedValue; } public RGBA(byte r, byte g, byte b, byte a) : this() { R = r; G = g; B = b; A = a; } 

Since this is used for graphical code, performance is critical, and I'm trying to find the most optimal way to handle building in this scenario. Using the first example seems to be the least cost of the two examples, because although it involves assigning all fields twice (once for PackedValue and once for fields R, G, B, and A), another example involves assigning all values ​​3 times (twice in default constructor and once in a specific constructor).

Is there a way to make the compiler recognize that these fields overlap and does not require the direct assignment of R, G, B, and A if PackedValue is assigned and vice versa? I suppose this can be done by manually setting up IL, but I'm wondering if there is a way to handle this more optimally directly in C #.

Any ideas?

+8
constructor c # memory data-structures
source share
2 answers

From here :

Struct elements are automatically initialized to their default values. Thus, you do not need to initialize any of them to the default values ​​in any constructor.

However, this does not apply to your case. It works only for nonoverlapping fields and only when using the default constructor. Anyway, look at the last part of the answer to an alternative based on this.

Looking at the IL code for a constructor with one parameter, we see that the compiler does nothing (without optimization, this is the release mode with default settings):

 .method public hidebysig specialname rtspecialname instance void .ctor(uint32 packedValue) cil managed { // Code size 42 (0x2a) .maxstack 6 .locals init ([0] uint8 CS$0$0000, [1] uint8 CS$0$0001, [2] uint8 CS$0$0002) IL_0000: ldarg.0 IL_0001: ldarg.0 IL_0002: ldarg.0 IL_0003: ldarg.0 IL_0004: ldc.i4.0 IL_0005: dup IL_0006: stloc.0 IL_0007: stfld uint8 ConsoleApplication2.Program/RGBA::A IL_000c: ldloc.0 IL_000d: dup IL_000e: stloc.1 IL_000f: stfld uint8 ConsoleApplication2.Program/RGBA::B IL_0014: ldloc.1 IL_0015: dup IL_0016: stloc.2 IL_0017: stfld uint8 ConsoleApplication2.Program/RGBA::G IL_001c: ldloc.2 IL_001d: stfld uint8 ConsoleApplication2.Program/RGBA::R IL_0022: ldarg.0 IL_0023: ldarg.1 IL_0024: stfld uint32 ConsoleApplication2.Program/RGBA::PackedValue IL_0029: ret } // end of method RGBA::.ctor 

After the @usr sentence after jitting, it looks the same (this is also a release mode, members are assigned individually):

 007400B5 call 750586FE 007400BA mov eax,dword ptr [ebp-8] 007400BD mov byte ptr [eax],0 G = 0; 007400C0 mov eax,dword ptr [ebp-8] 007400C3 mov byte ptr [eax+1],0 B = 0; 007400C7 mov eax,dword ptr [ebp-8] 007400CA mov byte ptr [eax+2],0 A = 0; 007400CE mov eax,dword ptr [ebp-8] 007400D1 mov byte ptr [eax+3],0 PackedValue = packedValue; 007400D5 mov eax,dword ptr [ebp-8] 007400D8 mov edx,dword ptr [ebp-4] 007400DB mov dword ptr [eax],edx 

Benchmarking is probably the best way. Or use the default constructor and assign PackedValue manually after you have an instance of the structure. In this case, the default behavior described in the article is applied.

 var rgba = new RGBA { PackedValue = 2556 }; 

OR

 var rgba = new RGBA(); rgba.PackedValue = 2556; 
+3
source share

I ran into this problem and ended up using unsafe code to convert my Color structure to a 32-bit integer and vice versa. Not sure if this is the option for you.

+1
source share

All Articles