public static void BuildPackedType(System.Type unpacked, HashSet <string> activeFields, ShaderGenerator result) { // for each interpolator, the number of components used (up to 4 for a float4 interpolator) List <int> packedCounts = new List <int>(); ShaderGenerator packer = new ShaderGenerator(); ShaderGenerator unpacker = new ShaderGenerator(); ShaderGenerator structEnd = new ShaderGenerator(); string unpackedStruct = unpacked.Name.ToString(); string packedStruct = "Packed" + unpacked.Name; string packerFunction = "Pack" + unpacked.Name; string unpackerFunction = "Unpack" + unpacked.Name; // declare struct header: // struct packedStruct { result.AddShaderChunk("struct " + packedStruct + " {"); result.Indent(); // declare function headers: // packedStruct packerFunction(unpackedStruct input) // { // packedStruct output; packer.AddShaderChunk(packedStruct + " " + packerFunction + "(" + unpackedStruct + " input)"); packer.AddShaderChunk("{"); packer.Indent(); packer.AddShaderChunk(packedStruct + " output;"); // unpackedStruct unpackerFunction(packedStruct input) // { // unpackedStruct output; unpacker.AddShaderChunk(unpackedStruct + " " + unpackerFunction + "(" + packedStruct + " input)"); unpacker.AddShaderChunk("{"); unpacker.Indent(); unpacker.AddShaderChunk(unpackedStruct + " output;"); // TODO: this could do a better job packing // especially if we allowed breaking up fields across multiple interpolators (to pack them into remaining space...) // though we would want to only do this if it improves final interpolator count, and is worth it on the target machine foreach (FieldInfo field in unpacked.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) { if (field.MemberType == MemberTypes.Field) { bool isOptional; if (ShouldSpliceField(unpacked, field, activeFields, out isOptional)) { string semanticString = GetFieldSemantic(field); int floatVectorCount; string fieldType = GetFieldType(field, out floatVectorCount); string conditional = GetFieldConditional(field); if ((semanticString != null) || (conditional != null) || (floatVectorCount == 0)) { // not a packed value if (conditional != null) { structEnd.AddShaderChunk("#if " + conditional); packer.AddShaderChunk("#if " + conditional); unpacker.AddShaderChunk("#if " + conditional); } structEnd.AddShaderChunk(fieldType + " " + field.Name + semanticString + "; // unpacked"); packer.AddShaderChunk("output." + field.Name + " = input." + field.Name + ";"); unpacker.AddShaderChunk("output." + field.Name + " = input." + field.Name + ";"); if (conditional != null) { structEnd.AddShaderChunk("#endif // " + conditional); packer.AddShaderChunk("#endif // " + conditional); unpacker.AddShaderChunk("#endif // " + conditional); } } else { // pack float field // super simple packing: use the first interpolator that has room for the whole value int interpIndex = packedCounts.FindIndex(x => (x + floatVectorCount <= 4)); int firstChannel; if (interpIndex < 0) { // allocate a new interpolator interpIndex = packedCounts.Count; firstChannel = 0; packedCounts.Add(floatVectorCount); } else { // pack into existing interpolator firstChannel = packedCounts[interpIndex]; packedCounts[interpIndex] += floatVectorCount; } // add code to packer and unpacker -- packed data declaration is handled later string packedChannels = GetChannelSwizzle(firstChannel, floatVectorCount); packer.AddShaderChunk(string.Format("output.interp{0:00}.{1} = input.{2};", interpIndex, packedChannels, field.Name)); unpacker.AddShaderChunk(string.Format("output.{0} = input.interp{1:00}.{2};", field.Name, interpIndex, packedChannels)); } } } } // add packed data declarations to struct, using the packedCounts for (int index = 0; index < packedCounts.Count; index++) { int count = packedCounts[index]; result.AddShaderChunk(string.Format("{0} interp{1:00} : TEXCOORD{1}; // auto-packed", vectorTypeNames[count], index)); } // add unpacked data declarations to struct (must be at end) result.AddGenerator(structEnd); // close declarations result.Deindent(); result.AddShaderChunk("};"); packer.AddShaderChunk("return output;"); packer.Deindent(); packer.AddShaderChunk("}"); unpacker.AddShaderChunk("return output;"); unpacker.Deindent(); unpacker.AddShaderChunk("}"); // combine all of the code into the result result.AddGenerator(packer); result.AddGenerator(unpacker); }