Skip to content

Commit

Permalink
More groundwork for HLSL generation.
Browse files Browse the repository at this point in the history
  • Loading branch information
mellinoe committed Sep 7, 2017
1 parent 07d51fe commit ea1d095
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 85 deletions.
7 changes: 7 additions & 0 deletions NuGet.Config
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="dotnet-myget dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
<add key="dotnet-myget dotnet-corefxlab" value="https://dotnet.myget.org/F/dotnet-corefxlab/api/v3/index.json" />
</packageSources>
</configuration>
23 changes: 13 additions & 10 deletions src/ShaderGen/DuplicateWithSuffixAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@
using System.Diagnostics;
using Validation;

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
[CodeGenerationAttribute(typeof(DuplicateWithSuffixGenerator))]
[Conditional("CodeGeneration")]
public class DuplicateWithSuffixAttribute : Attribute
namespace ShaderGen
{
public DuplicateWithSuffixAttribute(string suffix)
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
[CodeGenerationAttribute(typeof(DuplicateWithSuffixGenerator))]
[Conditional("CodeGeneration")]
public class DuplicateWithSuffixAttribute : Attribute
{
Requires.NotNullOrEmpty(suffix, nameof(suffix));
this.Suffix = suffix;
}
public DuplicateWithSuffixAttribute(string suffix)
{
Requires.NotNullOrEmpty(suffix, nameof(suffix));
this.Suffix = suffix;
}

public string Suffix { get; }
}
public string Suffix { get; }
}
}
44 changes: 23 additions & 21 deletions src/ShaderGen/DuplicateWithSuffixGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,35 @@
using System.Threading.Tasks;
using Validation;

public class DuplicateWithSuffixGenerator : ICodeGenerator
namespace ShaderGen
{
private readonly string suffix;

public DuplicateWithSuffixGenerator(AttributeData attributeData)
public class DuplicateWithSuffixGenerator : ICodeGenerator
{
Requires.NotNull(attributeData, nameof(attributeData));
this.suffix = (string)attributeData.ConstructorArguments[0].Value;
}
private readonly string suffix;

public Task<SyntaxList<MemberDeclarationSyntax>> GenerateAsync(TransformationContext context, IProgress<Diagnostic> progress, CancellationToken cancellationToken)
{
var results = SyntaxFactory.List<MemberDeclarationSyntax>();
public DuplicateWithSuffixGenerator(AttributeData attributeData)
{
Requires.NotNull(attributeData, nameof(attributeData));
this.suffix = (string)attributeData.ConstructorArguments[0].Value;
}

// Our generator is applied to any class that our attribute is applied to.
var applyToClass = (ClassDeclarationSyntax)context.ProcessingMember;
public Task<SyntaxList<MemberDeclarationSyntax>> GenerateAsync(TransformationContext context, IProgress<Diagnostic> progress, CancellationToken cancellationToken)
{
var results = SyntaxFactory.List<MemberDeclarationSyntax>();

// Apply a suffix to the name of a copy of the class.
var copy = applyToClass
.WithIdentifier(SyntaxFactory.Identifier(applyToClass.Identifier.ValueText + this.suffix));
// Our generator is applied to any class that our attribute is applied to.
var applyToClass = (ClassDeclarationSyntax)context.ProcessingMember;

ShaderSyntaxWalker walker = new ShaderSyntaxWalker();
walker.Visit(context.SemanticModel.SyntaxTree.GetRoot());
walker.WriteToFile(@"E:\outputtext.txt");
// Apply a suffix to the name of a copy of the class.
var copy = applyToClass
.WithIdentifier(SyntaxFactory.Identifier(applyToClass.Identifier.ValueText + this.suffix));

// Return our modified copy. It will be added to the user's project for compilation.
results = results.Add(copy);
return Task.FromResult<SyntaxList<MemberDeclarationSyntax>>(results);
ShaderSyntaxWalker walker = new ShaderSyntaxWalker(context);
walker.Visit(context.SemanticModel.SyntaxTree.GetRoot());
walker.WriteToFile("outputtext.hlsl");
// Return our modified copy. It will be added to the user's project for compilation.
results = results.Add(copy);
return Task.FromResult<SyntaxList<MemberDeclarationSyntax>>(results);
}
}
}
27 changes: 27 additions & 0 deletions src/ShaderGen/HlslKnownTypes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Collections.Generic;

namespace ShaderGen
{
public static class HlslKnownTypes
{
private static readonly Dictionary<string, string> s_knownTypes = new Dictionary<string, string>()
{
{ "System.Numerics.Vector2", "float2" },
{ "System.Numerics.Vector3", "float3" },
{ "System.Numerics.Vector4", "float4" },
{ "System.Numerics.Matrix4x4", "float4x4" },
};

public static string GetMappedName(string name)
{
if (s_knownTypes.TryGetValue(name, out string mapped))
{
return mapped;
}
else
{
return name;
}
}
}
}
45 changes: 45 additions & 0 deletions src/ShaderGen/HlslWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace ShaderGen
{
public class HlslWriter
{
private readonly List<StructDefinition> _structs;
private readonly List<UniformDefinition> _uniforms;

public HlslWriter(List<StructDefinition> structs, List<UniformDefinition> uniforms)
{
_structs = structs;
_uniforms = uniforms;
}

public string GetHlslText()
{
StringBuilder sb = new StringBuilder();
foreach (UniformDefinition ud in _uniforms)
{
sb.AppendLine($"cbuffer {ud.Name}Buffer : register(b{ud.Binding})");
sb.AppendLine("{");
sb.AppendLine($" {HlslKnownTypes.GetMappedName(ud.Type.Name.Trim())} {ud.Name.Trim()};");
sb.AppendLine("}");
sb.AppendLine();
}

foreach (StructDefinition sd in _structs)
{
sb.AppendLine($"struct {sd.Name}");
sb.AppendLine("{");
foreach (FieldDefinition field in sd.Fields)
{
sb.AppendLine($" {HlslKnownTypes.GetMappedName(field.Type.Name.Trim())} {field.Name.Trim()};");
}
sb.AppendLine("};");
sb.AppendLine();
}

return sb.ToString();
}
}
}
173 changes: 119 additions & 54 deletions src/ShaderGen/ShaderSyntaxWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,81 +4,146 @@
using System.Linq;
using System;
using System.IO;
using ShaderGen;
using System.Collections.Generic;
using CodeGeneration.Roslyn;
using Microsoft.CodeAnalysis;
using System.Diagnostics;

public class ShaderSyntaxWalker : CSharpSyntaxWalker
namespace ShaderGen
{
private readonly StringBuilder _sb = new StringBuilder();
private readonly ShaderFunctionWalker _functionWalker;

public ShaderSyntaxWalker() : base(Microsoft.CodeAnalysis.SyntaxWalkerDepth.Token)
public class ShaderSyntaxWalker : CSharpSyntaxWalker
{
_functionWalker = new ShaderFunctionWalker(_sb);
}
private readonly StringBuilder _sb = new StringBuilder();
private readonly TransformationContext _context;
private readonly ShaderFunctionWalker _functionWalker;

public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
{
_sb.AppendLine($"Encountered method declaration: {node.Identifier.Text}");
if (node.AttributeLists.Any(als => als.Attributes.Any(attrSyntax => attrSyntax.GetText().ToString().Contains("EntryFunction"))))
private readonly List<StructDefinition> _structs = new List<StructDefinition>();
private readonly List<UniformDefinition> _uniforms = new List<UniformDefinition>();

public ShaderSyntaxWalker(TransformationContext context) : base(Microsoft.CodeAnalysis.SyntaxWalkerDepth.Token)
{
_sb.AppendLine($" - Is a shader method.");
_context = context;
_functionWalker = new ShaderFunctionWalker(_sb);
}

VisitBlock(node.Body);
}
public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
{
_sb.AppendLine($"Encountered method declaration: {node.Identifier.Text}");
if (node.AttributeLists.Any(als => als.Attributes.Any(attrSyntax => attrSyntax.GetText().ToString().Contains("EntryFunction"))))
{
_sb.AppendLine($" - Is a shader method.");
}

public override void VisitStructDeclaration(StructDeclarationSyntax node)
{
_sb.AppendLine($"Encountered struct declaration: {node.Identifier.Text}");
foreach (MemberDeclarationSyntax member in node.Members)
VisitBlock(node.Body);
}

public override void VisitStructDeclaration(StructDeclarationSyntax node)
{
_sb.AppendLine($" * Member: {member.ToFullString()}");
string structName = node.Identifier.Text;

List<FieldDefinition> fields = new List<FieldDefinition>();
foreach (MemberDeclarationSyntax member in node.Members)
{
if (member is FieldDeclarationSyntax fds)
{
VariableDeclarationSyntax varDecl = fds.Declaration;
foreach (VariableDeclaratorSyntax vds in varDecl.Variables)
{
string fieldName = vds.Identifier.Text;
TypeReference tr = new TypeReference(GetFullTypeName(varDecl.Type));
fields.Add(new FieldDefinition(fieldName, tr));
}
}
}

_structs.Add(new StructDefinition(structName, fields.ToArray()));
}
}

public override void VisitAssignmentExpression(AssignmentExpressionSyntax node)
{
_sb.AppendLine($" * Assignment: [[ {node.ToFullString()} ]]");
}
public override void VisitAssignmentExpression(AssignmentExpressionSyntax node)
{
_sb.AppendLine($" * Assignment: [[ {node.ToFullString()} ]]");
}

public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
{
_sb.AppendLine($" * Member access: [[ {node.ToFullString()} ]]");
}
public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
{
_sb.AppendLine($" * Member access: [[ {node.ToFullString()} ]]");
}

public override void VisitInvocationExpression(InvocationExpressionSyntax node)
{
_sb.AppendLine($" * Invocation: {node.ToFullString()}");
}
public override void VisitInvocationExpression(InvocationExpressionSyntax node)
{
_sb.AppendLine($" * Invocation: {node.ToFullString()}");
}

public override void VisitVariableDeclaration(VariableDeclarationSyntax node)
{
_sb.AppendLine($" * Var decl: [[ {node.ToFullString()} ]] ");
if (GetUniformDecl(node, out AttributeSyntax uniformAttr))
public override void VisitVariableDeclaration(VariableDeclarationSyntax node)
{
_sb.AppendLine($" - This is a uniform: {uniformAttr.ToFullString()}");
if (GetUniformDecl(node, out AttributeSyntax uniformAttr))
{
ExpressionSyntax uniformBindingExpr = uniformAttr.ArgumentList.Arguments[0].Expression;
if (!(uniformBindingExpr is LiteralExpressionSyntax les))
{
throw new InvalidOperationException("Must use a literal parameter in UniformAttribute ctor.");
}

int uniformBinding = int.Parse(les.ToFullString());

foreach (VariableDeclaratorSyntax vds in node.Variables)
{
string uniformName = vds.Identifier.Text;
TypeInfo typeInfo = _context.SemanticModel.GetTypeInfo(node.Type);
string fullTypeName = GetFullTypeName(node.Type);
TypeReference tr = new TypeReference(fullTypeName);
UniformDefinition ud = new UniformDefinition(uniformName, uniformBinding, tr);
_uniforms.Add(ud);
}
}
}

}
private string GetFullTypeName(TypeSyntax type)
{
TypeInfo typeInfo = _context.SemanticModel.GetTypeInfo(type);
string ns = GetFullNamespace(typeInfo.Type.ContainingNamespace);
return ns + "." + typeInfo.Type.Name;
}

private bool GetUniformDecl(VariableDeclarationSyntax node, out AttributeSyntax attr)
{
attr = (node.Parent.DescendantNodes().OfType<AttributeSyntax>().FirstOrDefault(
attrSyntax => attrSyntax.ToString().Contains("Uniform")));
return attr != null;
}
private string GetFullNamespace(INamespaceSymbol ns)
{
Debug.Assert(ns != null);
string currentNamespace = ns.Name;
if (ns.ContainingNamespace != null && !ns.ContainingNamespace.IsGlobalNamespace)
{
return GetFullNamespace(ns.ContainingNamespace) + "." + currentNamespace;
}
else
{
return currentNamespace;
}
}

internal void WriteToFile(string file)
{
File.WriteAllText(file, _sb.ToString());
}
}
private bool GetUniformDecl(VariableDeclarationSyntax node, out AttributeSyntax attr)
{
attr = (node.Parent.DescendantNodes().OfType<AttributeSyntax>().FirstOrDefault(
attrSyntax => attrSyntax.ToString().Contains("Uniform")));
return attr != null;
}

public class ShaderFunctionWalker : CSharpSyntaxWalker
{
private readonly StringBuilder _sb;
internal void WriteToFile(string file)
{
StringBuilder fullText = new StringBuilder();
HlslWriter hlslWriter = new HlslWriter(_structs, _uniforms);
fullText.Append(hlslWriter.GetHlslText());
fullText.Append(_sb);
File.WriteAllText(file, fullText.ToString());
}
}

public ShaderFunctionWalker(StringBuilder sb)
public class ShaderFunctionWalker : CSharpSyntaxWalker
{
_sb = sb;
private readonly StringBuilder _sb;

public ShaderFunctionWalker(StringBuilder sb)
{
_sb = sb;
}
}
}
Loading

0 comments on commit ea1d095

Please sign in to comment.