您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
324 行
12 KiB
324 行
12 KiB
using System;
|
|
using ICSharpCode.NRefactory;
|
|
using ICSharpCode.NRefactory.Visitors;
|
|
using ICSharpCode.NRefactory.Ast;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using UnityEditor;
|
|
|
|
namespace UnityEngine.ScriptableRenderLoop
|
|
{
|
|
public class CSharpToHLSL
|
|
{
|
|
public static bool GenerateHLSL(System.Type type, GenerateHLSL attribute, out string shaderSource)
|
|
{
|
|
List<string> errors;
|
|
return GenerateHLSL(type, attribute, out shaderSource, out errors);
|
|
}
|
|
|
|
public static bool GenerateHLSL(System.Type type, GenerateHLSL attribute, out string shaderSource, out List<string> errors)
|
|
{
|
|
ShaderTypeGenerator gen = new ShaderTypeGenerator(type, attribute);
|
|
bool success = gen.Generate();
|
|
|
|
if (success)
|
|
{
|
|
shaderSource = gen.Emit();
|
|
}
|
|
else
|
|
{
|
|
shaderSource = null;
|
|
}
|
|
|
|
errors = gen.errors;
|
|
return success;
|
|
}
|
|
|
|
public static void GenerateAll()
|
|
{
|
|
m_typeName = new Dictionary<string, ShaderTypeGenerator>();
|
|
|
|
// Iterate over assemblyList, discover all applicable types with fully qualified names
|
|
var assemblyList = AssemblyEnumerator.EnumerateReferencedAssemblies(Assembly.GetCallingAssembly());
|
|
|
|
foreach (var assembly in assemblyList)
|
|
{
|
|
Type[] types = assembly.GetExportedTypes();
|
|
|
|
foreach (var type in types)
|
|
{
|
|
object[] attributes = type.GetCustomAttributes(true);
|
|
|
|
foreach (var attr in attributes)
|
|
{
|
|
if (attr is GenerateHLSL)
|
|
{
|
|
Type parent = type.DeclaringType;
|
|
if (parent != null)
|
|
{
|
|
Debug.LogError("The GenerateHLSL attribute not supported on nested classes (" + type.FullName + "), skipping.");
|
|
}
|
|
else
|
|
{
|
|
ShaderTypeGenerator gen;
|
|
if (m_typeName.TryGetValue(type.FullName, out gen))
|
|
{
|
|
Debug.LogError("Duplicate typename with the GenerateHLSL attribute detected: " + type.FullName +
|
|
" declared in both " + gen.type.Assembly.FullName + " and " + type.Assembly.FullName + ". Skipping the second instance.");
|
|
}
|
|
m_typeName[type.FullName] = new ShaderTypeGenerator(type, attr as GenerateHLSL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Now that we have extracted all the typenames that we care about, parse all .cs files in all asset
|
|
// paths and figure out in which files those types are actually declared.
|
|
m_sourceGenerators = new Dictionary<string, List<ShaderTypeGenerator>>();
|
|
|
|
var assetPaths = AssetDatabase.GetAllAssetPaths().Where(s => s.EndsWith(".cs")).ToList();
|
|
foreach (var assetPath in assetPaths)
|
|
{
|
|
LoadTypes(assetPath);
|
|
}
|
|
|
|
// Finally, write out the generated code
|
|
foreach (var it in m_sourceGenerators)
|
|
{
|
|
string fileName = it.Key + ".hlsl";
|
|
bool skipFile = false;
|
|
foreach (var gen in it.Value)
|
|
{
|
|
if (!gen.Generate())
|
|
{
|
|
// Error reporting will be done by the generator. Skip this file.
|
|
gen.PrintErrors();
|
|
skipFile = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!skipFile)
|
|
{
|
|
using (System.IO.StreamWriter writer = File.CreateText(fileName))
|
|
{
|
|
writer.Write("//\n");
|
|
writer.Write("// This file was automatically generated from " + it.Key + ". Please don't edit by hand.\n");
|
|
writer.Write("//\n\n");
|
|
|
|
foreach (var gen in it.Value)
|
|
{
|
|
if (gen.hasStatics)
|
|
{
|
|
writer.Write(gen.EmitDefines() + "\n");
|
|
}
|
|
}
|
|
|
|
foreach (var gen in it.Value)
|
|
{
|
|
if (gen.hasFields)
|
|
{
|
|
writer.Write(gen.EmitTypeDecl() + "\n");
|
|
}
|
|
}
|
|
|
|
foreach (var gen in it.Value)
|
|
{
|
|
if (gen.hasFields && !gen.IsSimple())
|
|
{
|
|
writer.Write(gen.EmitAccessors() + "\n");
|
|
}
|
|
}
|
|
|
|
writer.Write("\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static Dictionary<string, ShaderTypeGenerator> m_typeName;
|
|
|
|
static void LoadTypes(string fileName)
|
|
{
|
|
using (var parser = ParserFactory.CreateParser(fileName))
|
|
{
|
|
// @TODO any standard preprocessor symbols we need?
|
|
|
|
/*var uniqueSymbols = new HashSet<string>(definedSymbols.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
|
|
foreach (var symbol in uniqueSymbols)
|
|
{
|
|
parser.Lexer.ConditionalCompilationSymbols.Add(symbol, string.Empty);
|
|
}*/
|
|
parser.Lexer.EvaluateConditionalCompilation = true;
|
|
|
|
parser.Parse();
|
|
try
|
|
{
|
|
var visitor = new NamespaceVisitor();
|
|
VisitorData data = new VisitorData();
|
|
data.m_typeName = m_typeName;
|
|
parser.CompilationUnit.AcceptVisitor(visitor, data);
|
|
|
|
if (data.generators.Count > 0)
|
|
m_sourceGenerators[fileName] = data.generators;
|
|
}
|
|
catch
|
|
{
|
|
// does NRefactory throw anything we can handle here?
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
static Dictionary<string, List<ShaderTypeGenerator>> m_sourceGenerators;
|
|
|
|
class VisitorData
|
|
{
|
|
public VisitorData()
|
|
{
|
|
currentNamespaces = new Stack<string>();
|
|
currentClasses = new Stack<string>();
|
|
generators = new List<ShaderTypeGenerator>();
|
|
}
|
|
|
|
public string GetTypePrefix()
|
|
{
|
|
string fullNamespace = string.Empty;
|
|
|
|
string separator = "";
|
|
foreach (string ns in currentClasses)
|
|
{
|
|
fullNamespace = ns + "+" + fullNamespace;
|
|
}
|
|
|
|
foreach (string ns in currentNamespaces)
|
|
{
|
|
if (fullNamespace == string.Empty)
|
|
{
|
|
separator = ".";
|
|
fullNamespace = ns;
|
|
}
|
|
else
|
|
fullNamespace = ns + "." + fullNamespace;
|
|
}
|
|
|
|
string name = "";
|
|
if (fullNamespace != string.Empty)
|
|
{
|
|
name = fullNamespace + separator + name;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
public Stack<string> currentNamespaces;
|
|
public Stack<string> currentClasses;
|
|
public List<ShaderTypeGenerator> generators;
|
|
public Dictionary<string, ShaderTypeGenerator> m_typeName;
|
|
}
|
|
|
|
class NamespaceVisitor : AbstractAstVisitor
|
|
{
|
|
public override object VisitNamespaceDeclaration(ICSharpCode.NRefactory.Ast.NamespaceDeclaration namespaceDeclaration, object data)
|
|
{
|
|
VisitorData visitorData = (VisitorData)data;
|
|
visitorData.currentNamespaces.Push(namespaceDeclaration.Name);
|
|
namespaceDeclaration.AcceptChildren(this, visitorData);
|
|
visitorData.currentNamespaces.Pop();
|
|
|
|
return null;
|
|
}
|
|
|
|
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
|
|
{
|
|
// Structured types only
|
|
if (typeDeclaration.Type == ClassType.Class || typeDeclaration.Type == ClassType.Struct || typeDeclaration.Type == ClassType.Enum)
|
|
{
|
|
VisitorData visitorData = (VisitorData)data;
|
|
|
|
string name = visitorData.GetTypePrefix() + typeDeclaration.Name;
|
|
|
|
ShaderTypeGenerator gen;
|
|
if (visitorData.m_typeName.TryGetValue(name, out gen))
|
|
{
|
|
visitorData.generators.Add(gen);
|
|
}
|
|
|
|
visitorData.currentClasses.Push(typeDeclaration.Name);
|
|
typeDeclaration.AcceptChildren(this, visitorData);
|
|
visitorData.currentClasses.Pop();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper class to recursively enumerate assemblies referenced by the calling assembly, including unloaded ones
|
|
static class AssemblyEnumerator
|
|
{
|
|
public static List<Assembly> EnumerateReferencedAssemblies(Assembly assembly)
|
|
{
|
|
Dictionary<string, Assembly> assemblies = assembly.GetReferencedAssembliesRecursive();
|
|
assemblies[GetName(assembly.FullName)] = assembly;
|
|
return assemblies.Values.ToList();
|
|
}
|
|
|
|
public static Dictionary<string, Assembly> GetReferencedAssembliesRecursive(this Assembly assembly)
|
|
{
|
|
assemblies = new Dictionary<string, Assembly>();
|
|
InternalGetDependentAssembliesRecursive(assembly);
|
|
|
|
// Skip assemblies from GAC (@TODO: any reason we'd want to include them?)
|
|
var keysToRemove = assemblies.Values.Where(
|
|
o => o.GlobalAssemblyCache == true).ToList();
|
|
|
|
foreach (var k in keysToRemove)
|
|
{
|
|
assemblies.Remove(GetName(k.FullName));
|
|
}
|
|
|
|
return assemblies;
|
|
}
|
|
|
|
private static void InternalGetDependentAssembliesRecursive(Assembly assembly)
|
|
{
|
|
// Load assemblies with newest versions first.
|
|
var referencedAssemblies = assembly.GetReferencedAssemblies()
|
|
.OrderByDescending(o => o.Version);
|
|
|
|
foreach (var r in referencedAssemblies)
|
|
{
|
|
if (String.IsNullOrEmpty(assembly.FullName))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (assemblies.ContainsKey(GetName(r.FullName)) == false)
|
|
{
|
|
try
|
|
{
|
|
// Ensure that the assembly is loaded
|
|
var a = Assembly.Load(r.FullName);
|
|
assemblies[GetName(a.FullName)] = a;
|
|
InternalGetDependentAssembliesRecursive(a);
|
|
}
|
|
catch
|
|
{
|
|
// Missing dll, ignore.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static string GetName(string name)
|
|
{
|
|
return name.Split(',')[0];
|
|
}
|
|
|
|
static Dictionary<string, Assembly> assemblies;
|
|
}
|
|
}
|