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); }