public StructureDefinition(string name, FieldDefinition[] fields, AlignmentInfo size) { Name = name; Fields = fields; Alignment = size; CSharpMatchesShaderAlignment = GetCSharpMatchesShaderAlignment(); }
public static bool TryGetStructDefinition(SemanticModel model, StructDeclarationSyntax node, out StructureDefinition sd) { string fullNestedTypePrefix = Utilities.GetFullNestedTypePrefix(node, out bool nested); string structName = node.Identifier.ToFullString().Trim(); if (!string.IsNullOrEmpty(fullNestedTypePrefix)) { string joiner = nested ? "+" : "."; structName = fullNestedTypePrefix + joiner + structName; } int structCSharpSize = 0; int structShaderSize = 0; int structCSharpAlignment = 0; int structShaderAlignment = 0; List <FieldDefinition> fields = new List <FieldDefinition>(); foreach (MemberDeclarationSyntax member in node.Members) { if (member is FieldDeclarationSyntax fds && !fds.Modifiers.Any(x => x.IsKind(SyntaxKind.ConstKeyword))) { VariableDeclarationSyntax varDecl = fds.Declaration; foreach (VariableDeclaratorSyntax vds in varDecl.Variables) { string fieldName = vds.Identifier.Text.Trim(); string typeName = model.GetFullTypeName(varDecl.Type, out bool isArray); int arrayElementCount = 0; if (isArray) { arrayElementCount = GetArrayCountValue(vds, model); } TypeInfo typeInfo = model.GetTypeInfo(varDecl.Type); AlignmentInfo fieldSizeAndAlignment; if (typeInfo.Type.Kind == SymbolKind.ArrayType) { ITypeSymbol elementType = ((IArrayTypeSymbol)typeInfo.Type).ElementType; AlignmentInfo elementSizeAndAlignment = TypeSizeCache.Get(elementType); fieldSizeAndAlignment = new AlignmentInfo( elementSizeAndAlignment.CSharpSize * arrayElementCount, elementSizeAndAlignment.ShaderSize * arrayElementCount, elementSizeAndAlignment.CSharpAlignment, elementSizeAndAlignment.ShaderAlignment); } else { fieldSizeAndAlignment = TypeSizeCache.Get(typeInfo.Type); } structCSharpSize += structCSharpSize % fieldSizeAndAlignment.CSharpAlignment; structCSharpSize += fieldSizeAndAlignment.CSharpSize; structCSharpAlignment = Math.Max(structCSharpAlignment, fieldSizeAndAlignment.CSharpAlignment); structShaderSize += structShaderSize % fieldSizeAndAlignment.ShaderAlignment; structShaderSize += fieldSizeAndAlignment.ShaderSize; structShaderAlignment = Math.Max(structShaderAlignment, fieldSizeAndAlignment.ShaderAlignment); TypeReference tr = new TypeReference(typeName, model.GetTypeInfo(varDecl.Type).Type); SemanticType semanticType = GetSemanticType(vds); fields.Add(new FieldDefinition(fieldName, tr, semanticType, arrayElementCount, fieldSizeAndAlignment)); } } } sd = new StructureDefinition( structName.Trim(), fields.ToArray(), new AlignmentInfo(structCSharpSize, structShaderSize, structCSharpAlignment, structShaderAlignment)); return(true); }
public static bool TryGetStructDefinition(SemanticModel model, StructDeclarationSyntax node, out StructureDefinition sd) { //string fullNestedTypePrefix = Utilities.GetFullNestedTypePrefix(node, out bool nested); //string structName = node.Identifier.ToFullString().Trim(); //if (!string.IsNullOrEmpty(fullNestedTypePrefix)) //{ // string joiner = nested ? "+" : "."; // structName = fullNestedTypePrefix + joiner + structName; //} int structCSharpSize = 0; int structShaderSize = 0; int structCSharpAlignment = 0; int structShaderAlignment = 0; List <FieldDefinition> fields = new List <FieldDefinition>(); foreach (MemberDeclarationSyntax member in node.Members) { if (member is FieldDeclarationSyntax fds && !fds.Modifiers.Any(x => x.IsKind(SyntaxKind.ConstKeyword))) { VariableDeclarationSyntax varDecl = fds.Declaration; foreach (VariableDeclaratorSyntax vds in varDecl.Variables) { string fieldName = vds.Identifier.Text.Trim(); string typeName = model.GetFullTypeName(varDecl.Type, out bool isArray); int arrayElementCount = 0; if (isArray) { arrayElementCount = GetArrayCountValue(vds, model); } TypeInfo typeInfo = model.GetTypeInfo(varDecl.Type); AlignmentInfo fieldSizeAndAlignment; int fixedSize = 1; if (typeInfo.Type.Kind == SymbolKind.ArrayType) { var arraySize = fds.DescendantNodes().OfType <AttributeSyntax>().FirstOrDefault( attrSyntax => attrSyntax.Name.ToString().EndsWith("ArraySize")); fixedSize = (int)model.GetConstantValue(arraySize.ArgumentList.Arguments.First().Expression).Value; ITypeSymbol elementType = ((IArrayTypeSymbol)typeInfo.Type).ElementType; AlignmentInfo elementSizeAndAlignment = TypeSizeCache.Get(elementType); fieldSizeAndAlignment = new AlignmentInfo( elementSizeAndAlignment.CSharpSize * arrayElementCount, elementSizeAndAlignment.ShaderSize * arrayElementCount, elementSizeAndAlignment.CSharpAlignment, elementSizeAndAlignment.ShaderAlignment); } else { fieldSizeAndAlignment = TypeSizeCache.Get(typeInfo.Type); } structCSharpSize += structCSharpSize % fieldSizeAndAlignment.CSharpAlignment; structCSharpSize += fieldSizeAndAlignment.CSharpSize; structCSharpAlignment = Math.Max(structCSharpAlignment, fieldSizeAndAlignment.CSharpAlignment); structShaderSize += structShaderSize % fieldSizeAndAlignment.ShaderAlignment; structShaderSize += fieldSizeAndAlignment.ShaderSize; structShaderAlignment = Math.Max(structShaderAlignment, fieldSizeAndAlignment.ShaderAlignment); TypeReference tr = new TypeReference(typeName, model.GetTypeInfo(varDecl.Type).Type, fixedSize); SemanticType semanticType = GetSemanticType(vds); if (semanticType == SemanticType.None) { var geometrySemantic = GetGeometrySemantic(vds); if (geometrySemantic != GeometrySemantic.None) { fields.Add(new FieldDefinition(fieldName, tr, geometrySemantic, arrayElementCount, fieldSizeAndAlignment)); continue; } } fields.Add(new FieldDefinition(fieldName, tr, semanticType, arrayElementCount, fieldSizeAndAlignment)); } } } var type = model.GetDeclaredSymbol(node); sd = new StructureDefinition( type, fields.ToArray(), new AlignmentInfo(structCSharpSize, structShaderSize, structCSharpAlignment, structShaderAlignment)); return(true); }
private static AlignmentInfo Analyze(ITypeSymbol typeSymbol) { // Check if we already know this type if (s_cachedSizes.TryGetValue(typeSymbol, out AlignmentInfo alignmentInfo)) { return(alignmentInfo); } string symbolFullName = typeSymbol.GetFullMetadataName(); // Get any specific shader alignment int?specificShaderAlignment = s_shaderAlignments.TryGetValue(symbolFullName, out int sa) ? (int?)sa : null; // Check if this in our list of known sizes if (s_knownSizes.TryGetValue(symbolFullName, out int knownSize)) { alignmentInfo = new AlignmentInfo(knownSize, knownSize, knownSize, specificShaderAlignment ?? knownSize); s_cachedSizes.TryAdd(typeSymbol, alignmentInfo); return(alignmentInfo); } // Check if enum if (typeSymbol.TypeKind == TypeKind.Enum) { string enumBaseType = ((INamedTypeSymbol)typeSymbol).EnumUnderlyingType.GetFullMetadataName(); if (!s_knownSizes.TryGetValue(enumBaseType, out int enumSize)) { throw new ShaderGenerationException($"Unknown enum base type: {enumBaseType}"); } alignmentInfo = new AlignmentInfo(enumSize, enumSize, enumSize, specificShaderAlignment ?? enumSize); s_cachedSizes.TryAdd(typeSymbol, alignmentInfo); return(alignmentInfo); } // NOTE This check only works for known types accessible to ShaderGen, but it will pick up most non-blittable types. if (BlittableHelper.IsBlittable(symbolFullName) == false) { throw new ShaderGenerationException($"Cannot use the {symbolFullName} type in a shader as it is not a blittable type."); } // Unknown type, get the instance fields. var fields = typeSymbol.GetMembers() .Where(symb => symb.Kind == SymbolKind.Field && !symb.IsStatic) .Select(symb => (IFieldSymbol)symb) .ToArray(); if (fields.Length == 0) { throw new ShaderGenerationException($"No fields on type {symbolFullName}, cannot assess size of structure."); } int csharpSize = 0; int shaderSize = 0; int csharpAlignment = 0; int shaderAlignment = 0; // Calculate size of struct from its fields alignment infos foreach (IFieldSymbol field in fields) { // Determine if type is blittable if (field.Type is IArrayTypeSymbol arrayType) { // We can only analyze array size as fields since the field has the attribute, not the type var elementSizeAndAlignment = Get(arrayType.ElementType); var arraySizeAttribute = field.GetAttributes().Single(a => a.AttributeClass.Name == nameof(ArraySizeAttribute)); var arraySize = (int)arraySizeAttribute.ConstructorArguments[0].Value; var shaderAlign = Align(elementSizeAndAlignment.ShaderAlignment, 16); alignmentInfo = new AlignmentInfo( elementSizeAndAlignment.CSharpSize * arraySize, Align(elementSizeAndAlignment.ShaderSize * arraySize, shaderAlign), elementSizeAndAlignment.CSharpAlignment, shaderAlign); } else { alignmentInfo = Analyze(field.Type); } // If specified, the field offset dictates the size of the csharp structure at the point where the field starts csharpAlignment = Math.Max(csharpAlignment, alignmentInfo.CSharpAlignment); var offset = GetOffset(field); if (offset > 0) { csharpSize = offset; } else { csharpSize = Align(csharpSize, alignmentInfo.CSharpAlignment); } csharpSize += alignmentInfo.CSharpSize; shaderAlignment = Math.Max(shaderAlignment, alignmentInfo.ShaderAlignment); shaderSize = Align(shaderSize, alignmentInfo.ShaderAlignment); shaderSize += alignmentInfo.ShaderSize; } // Structures always align to a multiple of 16 if (typeSymbol.TypeKind == TypeKind.Struct) { shaderAlignment = Align(specificShaderAlignment ?? shaderAlignment, 16); } else { shaderAlignment = specificShaderAlignment ?? shaderAlignment; } // The overall shader object size is always a multiple of its alignment shaderSize = Align(shaderSize, shaderAlignment); // If specified, the overall csharp object size is fixed var sizeOf = GetSize(typeSymbol); if (sizeOf > 0) { csharpSize = sizeOf; } // Return new alignment info after adding into cache. alignmentInfo = new AlignmentInfo(csharpSize, shaderSize, csharpAlignment, shaderAlignment); s_cachedSizes.TryAdd(typeSymbol, alignmentInfo); return(alignmentInfo); }
private static AlignmentInfo Analyze(ITypeSymbol typeSymbol) { // Check if we already know this type if (s_cachedSizes.TryGetValue(typeSymbol, out AlignmentInfo alignmentInfo)) { return(alignmentInfo); } string symbolFullName = typeSymbol.GetFullMetadataName(); // Get any specific shader alignment int?specificShaderAlignment = s_shaderAlignments.TryGetValue(symbolFullName, out int sa) ? (int?)sa : null; // Check if this in our list of known sizes if (s_knownSizes.TryGetValue(symbolFullName, out int knownSize)) { alignmentInfo = new AlignmentInfo(knownSize, knownSize, knownSize, specificShaderAlignment ?? knownSize); s_cachedSizes.TryAdd(typeSymbol, alignmentInfo); return(alignmentInfo); } // Check if enum if (typeSymbol.TypeKind == TypeKind.Enum) { string enumBaseType = ((INamedTypeSymbol)typeSymbol).EnumUnderlyingType.GetFullMetadataName(); if (!s_knownSizes.TryGetValue(enumBaseType, out int enumSize)) { throw new ShaderGenerationException($"Unknown enum base type: {enumBaseType}"); } alignmentInfo = new AlignmentInfo(enumSize, enumSize, enumSize, specificShaderAlignment ?? enumSize); s_cachedSizes.TryAdd(typeSymbol, alignmentInfo); return(alignmentInfo); } // NOTE This check only works for known types accessible to ShaderGen, but it will pick up most non-blittable types. if (BlittableHelper.IsBlittable(symbolFullName) == false) { throw new ShaderGenerationException($"Cannot use the {symbolFullName} type in a shader as it is not a blittable type."); } // Unknown type, get the instance fields. ITypeSymbol[] fields = typeSymbol.GetMembers() .Where(symb => symb.Kind == SymbolKind.Field && !symb.IsStatic) .Select(symb => ((IFieldSymbol)symb).Type) .ToArray(); if (fields.Length == 0) { throw new ShaderGenerationException($"No fields on type {symbolFullName}, cannot assess size of structure."); } int csharpSize = 0; int shaderSize = 0; int csharpAlignment = 0; int shaderAlignment = 0; // Calculate size of struct from its fields alignment infos foreach (ITypeSymbol fieldType in fields) { // Determine if type is blittable alignmentInfo = Analyze(fieldType); csharpAlignment = Math.Max(csharpAlignment, alignmentInfo.CSharpAlignment); csharpSize += alignmentInfo.CSharpSize + csharpSize % alignmentInfo.CSharpAlignment; shaderAlignment = Math.Max(shaderAlignment, alignmentInfo.ShaderAlignment); shaderSize += alignmentInfo.ShaderSize + shaderSize % alignmentInfo.ShaderAlignment; } // Return new alignment info after adding into cache. alignmentInfo = new AlignmentInfo(csharpSize, shaderSize, csharpAlignment, specificShaderAlignment ?? shaderAlignment); s_cachedSizes.TryAdd(typeSymbol, alignmentInfo); return(alignmentInfo); }
private FieldDefinition ReflectField(IFieldSymbol field) { int fixedSize; AlignmentInfo fieldSizeAndAlignment; if (field.Type is IArrayTypeSymbol arrayType) { var elementSizeAndAlignment = TypeSizeCache.Get(arrayType.ElementType); var arraySizeAttribute = field.GetAttributes().Single(a => a.AttributeClass.Name == nameof(ArraySizeAttribute)); fixedSize = (int)arraySizeAttribute.ConstructorArguments[0].Value; fieldSizeAndAlignment = new AlignmentInfo( elementSizeAndAlignment.CSharpSize * fixedSize, elementSizeAndAlignment.ShaderSize * fixedSize, elementSizeAndAlignment.CSharpAlignment, elementSizeAndAlignment.ShaderAlignment); } else { fixedSize = 0; fieldSizeAndAlignment = TypeSizeCache.Get(field.Type); } SemanticType semantic = SemanticType.None; var semanticAttribute = field.GetAttributes().SingleOrDefault(a => a.AttributeClass.Name.Contains("Semantic")); if (semanticAttribute != null) { switch (semanticAttribute.AttributeClass.Name) { case nameof(PositionSemanticAttribute): semantic = SemanticType.Position; break; case nameof(SystemPositionSemanticAttribute): semantic = SemanticType.SystemPosition; break; case nameof(ColorSemanticAttribute): semantic = SemanticType.Color; break; case nameof(ColorTargetSemanticAttribute): semantic = SemanticType.ColorTarget; break; case nameof(NormalSemanticAttribute): semantic = SemanticType.Normal; break; case nameof(TangentSemanticAttribute): semantic = SemanticType.Tangent; break; case nameof(TextureCoordinateSemanticAttribute): semantic = SemanticType.TextureCoordinate; break; case nameof(GeometrySemanticAttribute): var geometrySemantic = semanticAttribute.ConstructorArguments.First(); return(new FieldDefinition(field.Name, new TypeReference(field.Type.GetFullMetadataName(), field.Type, fixedSize), (GeometrySemantic)Enum.ToObject(typeof(GeometrySemantic), geometrySemantic.Value), fixedSize, fieldSizeAndAlignment)); default: throw new NotSupportedException(); } } return(new FieldDefinition(field.Name, new TypeReference(field.Type.GetFullMetadataName(), field.Type, fixedSize), semantic, fixedSize, fieldSizeAndAlignment)); }