/// <summary> /// Determines whether the specified <see cref="VariableDesc" />, /// is equal to this instance. /// </summary> /// <param name="obj"> /// The <see cref="VariableDesc" /> to compare with this instance. /// </param> /// <returns> /// <c>true</c> if the specified <see cref="VariableDesc" /> /// is equal to this instance; otherwise, <c>false</c>. /// </returns> public bool Equals(VariableDesc obj) { return(obj.Definition.FullName == Definition.FullName); }
private static int Main(string[] args) { Console.WriteLine("---------------------- CrossSL V1.0 ----------------------"); if (args.Length == 0 || String.IsNullOrEmpty(args[0])) { DebugLog.UsageError("Start CrossSL with a .NET assemly as argument to translate\n" + "shaders from .NET to GLSL, e.g. 'CrossSL.exe Assembly.exe'"); return(-1); } string inputPath = args[0]; var asmName = Path.GetFileName(inputPath); var asmDir = Path.GetDirectoryName(inputPath); if (!File.Exists(inputPath)) { DebugLog.UsageError("Could not find assembly '" + asmName + "' in path:\n\n" + asmDir); return(-1); } // ReSharper disable once AssignNullToNotNullAttribute var metaPath = Path.Combine(asmDir, "CrossSL.Meta.dll"); if (!File.Exists(inputPath) || !File.Exists(metaPath)) { DebugLog.UsageError("Found assembly '" + asmName + "' but meta assembly 'CrossSL.Meta.dll'" + "\nis missing. It needs to be in the same directory:\n\n" + asmDir); return(-1); } Console.WriteLine("\n\nFound assembly '" + asmName + "' and meta assembly 'CrossSL.Meta.dll'."); // check if there is a .pdb file along with the .exe var debugFile = Path.ChangeExtension(inputPath, "pdb"); var debugName = Path.GetFileName(debugFile); DebugLog.Verbose = File.Exists(debugFile); Console.WriteLine(DebugLog.Verbose ? "Found also .pdb file '" + debugName + "'. This allows for better debugging." : "Found no .pdb file '" + debugName + "'. Extended debugging has been disabled."); // read assembly (and symbols) with Mono.Cecil var resolver = new DefaultAssemblyResolver(); resolver.AddSearchDirectory(asmDir); var readParams = new ReaderParameters { ReadSymbols = DebugLog.Verbose, AssemblyResolver = resolver }; var asm = AssemblyDefinition.ReadAssembly(inputPath, readParams); // read meta assembly (without symbols) with Mono.Cecil var metaParams = new ReaderParameters { ReadSymbols = false }; var metaAsm = AssemblyDefinition.ReadAssembly(metaPath, metaParams); // find all types with xSLShader as base type var shaderDescs = new List <ShaderDesc>(); var asmTypes = asm.Modules.SelectMany( asmMod => asmMod.Types.Where(asmType => asmType.BaseType != null)); var asmShaderTypes = asmTypes.Where(asmType => asmType.BaseType.IsType <xSLShader>()); foreach (var asmType in asmShaderTypes) { DebugLog.Reset(); if (DebugLog.Verbose) { // load symbols from pdb file var asmModule = asmType.Module; using (var symbReader = new PdbReaderProvider().GetSymbolReader(asmModule, debugFile)) asmModule.ReadSymbols(symbReader); } var asmTypeName = asmType.Name; Console.WriteLine("\n\nFound a shader called '" + asmTypeName + "':"); var shaderDesc = new ShaderDesc { Name = asmTypeName, Type = asmType }; // check for [xSLDebug] first in case the shader should be ignored var debugAttr = asmType.CustomAttributes.FirstOrDefault( attrType => attrType.AttributeType.IsType <xSLShader.xSLDebugAttribute>()); if (debugAttr != null) { shaderDesc.DebugFlags = (xSLShader.xSLDebug)debugAttr.ConstructorArguments[0].Value; if ((shaderDesc.DebugFlags & xSLShader.xSLDebug.IgnoreShader) != 0) { Console.WriteLine(" => Found [xSLDebug] with 'IgnoreShader' flag. Shader skipped."); continue; } } // check for [xSLTarget] and save settings var targetAttr = asmType.CustomAttributes.FirstOrDefault( attr => attr.AttributeType.IsType <xSLShader.xSLTargetAttribute>()); if (targetAttr == null) { shaderDesc.Target = new ShaderTarget { Envr = SLEnvironment.OpenGL, Version = 110, VersionID = 0 }; DebugLog.Error("Could not find [xSLTarget]. Please specify the targeted shading language"); } else { var typeName = targetAttr.ConstructorArguments[0].Type.Name; var versionID = (int)targetAttr.ConstructorArguments[0].Value; var shaderTarget = new ShaderTarget { VersionID = versionID }; switch (typeName) { case "GLSL": shaderTarget.Envr = SLEnvironment.OpenGL; break; case "GLSLES": shaderTarget.Envr = SLEnvironment.OpenGLES; break; case "GLSLMix": shaderTarget.Envr = SLEnvironment.OpenGLMix; break; } var vStr = SLVersion.VIDs[(int)shaderTarget.Envr][versionID]; shaderTarget.Version = Int32.Parse(vStr); shaderDesc.Target = shaderTarget; if (shaderTarget.Envr == SLEnvironment.OpenGLMix) { typeName = "GLSL 1.10 & GLSLES"; vStr = "100"; } vStr = vStr.Insert(1, "."); Console.WriteLine(" => Found [xSLTarget]. Compiling shader as " + typeName + " " + vStr + "."); } var shaderTranslator = ShaderTranslator.GetTranslator(shaderDesc.Target); shaderTranslator.ShaderDesc = shaderDesc; // save debug settings if (debugAttr == null) { shaderDesc.DebugFlags = xSLShader.xSLDebug.None; Console.WriteLine(" => Could not find [xSLDebug]. Debugging has been disabled."); DebugLog.Disabled = true; } else { if ((shaderDesc.DebugFlags & xSLShader.xSLDebug.None) != 0) { Console.WriteLine(" => Found [xSLDebug] with 'None' flag. Debugging has been disabled."); } else { Console.WriteLine(" => Found [xSLDebug]. Debugging with flags: " + shaderDesc.DebugFlags); } } // check for common mistakes Console.WriteLine("\n 1. Checking shader for obvious mistakes."); // check if vertex or fragment method is missing MethodDefinition vertexMain; if (!MethodExists(asmType, "VertexShader", out vertexMain)) { continue; } MethodDefinition fragmentMain; if (!MethodExists(asmType, "FragmentShader", out fragmentMain)) { continue; } // get their precission attributes var vertPrecAttr = vertexMain.CustomAttributes.FirstOrDefault( attrType => attrType.AttributeType.IsType <xSLShader.xSLPrecisionAttribute>()); var fragPrecAttr = fragmentMain.CustomAttributes.FirstOrDefault( attrType => attrType.AttributeType.IsType <xSLShader.xSLPrecisionAttribute>()); shaderDesc.Precision = new CustomAttribute[2]; shaderDesc.Precision[(int)SLShaderType.VertexShader] = vertPrecAttr; shaderDesc.Precision[(int)SLShaderType.FragmentShader] = fragPrecAttr; // get default file for this shader in case no instructions are available var defaultSeq = vertexMain.Body.Instructions[0].SequencePoint; DebugLog.DefaultFile = defaultSeq != null?Path.GetFileName(defaultSeq.Document.Url) : asmTypeName; // check if there are additional constructors for field/property initialization var ctorMethods = asmType.Methods.Where(asmMethod => asmMethod.IsConstructor); var customCtors = new Collection <MethodDefinition>(); foreach (var ctorMethod in ctorMethods.Where(ctor => ctor.Body.CodeSize > 7)) { // see if there are field initializations (as for "constants") var varInits = ctorMethod.Body.Instructions.Any(instr => instr.OpCode == OpCodes.Stfld); // or property setter calls (as for "constants") var funcCalls = ctorMethod.Body.Instructions.Where(instr => instr.OpCode == OpCodes.Call); var propInits = funcCalls.Any(instr => ((MethodReference)instr.Operand).Resolve().IsSetter); if (varInits || propInits) { customCtors.Add(ctorMethod); } else { var instr = ctorMethod.Body.Instructions[0]; DebugLog.Warning("Found a constructor with no valid content", instr); } } // analyze variables used in shader Console.WriteLine("\n 2. Collecting information about fields and properties."); var variables = new Collection <VariableDesc>(); var varTypes = Enum.GetNames(typeof(SLVariableType)); // read and gather fields and backing fields foreach (var asmField in asmType.Fields) { var varDesc = new VariableDesc { Definition = asmField }; var fdName = asmField.Name; var fdType = asmField.FieldType.ToType(); var fdArray = asmField.FieldType.IsArray; var attrs = asmField.CustomAttributes; var isProp = fdName.Contains("<"); if (isProp) { // ReSharper disable once StringIndexOfIsCultureSpecific.1 fdName = fdName.Remove(0, 1).Remove(fdName.IndexOf('>') - 1); var asmProp = asmType.Properties.First(prop => prop.Name == fdName); varDesc.Definition = asmProp; attrs = asmProp.CustomAttributes; fdType = asmProp.PropertyType.ToType(); fdArray = asmProp.PropertyType.IsArray; } var attrCt = attrs.Count(attr => varTypes.Contains(attr.AttributeType.Name)); var validFd = (asmField.HasConstant || attrCt == 1); var varType = (isProp) ? "Property '" : "Field '"; if (asmField.IsStatic && !asmField.HasConstant) { DebugLog.Error(varType + fdName + "' cannot be static"); } else if (validFd) { var fdAttrName = attrs.First(attr => varTypes.Contains(attr.AttributeType.Name)); var fdAttr = (SLVariableType)Array.IndexOf(varTypes, fdAttrName.AttributeType.Name); if (asmField.HasConstant) { if (fdAttr != SLVariableType.xSLConstAttribute) { DebugLog.Error(varType + "constant " + fdName + "' has an invalid attribute"); } varDesc.Value = asmField.Constant; } varDesc.DataType = fdType; varDesc.Attribute = fdAttr; varDesc.IsArray = fdArray; variables.Add(varDesc); } else { DebugLog.Error(varType + fdName + "' is neither a constant nor has valid attributes"); } } shaderDesc.Variables = variables; shaderTranslator.PreVariableCheck(); // translate main, depending methods and constructors if (shaderDesc.Target.Envr == SLEnvironment.OpenGLMix) { Console.WriteLine("\n 3. Translating shader from C# to GLSL/GLSLES."); } else { Console.WriteLine("\n 3. Translating shader from C# to " + shaderDesc.Target.Envr + "."); } shaderDesc.Funcs = new IEnumerable <FunctionDesc> [2]; var vertexFuncs = shaderTranslator.Translate(shaderDesc.Target, vertexMain); shaderDesc.Funcs[(int)SLShaderType.VertexShader] = vertexFuncs; var fragmentFuncs = shaderTranslator.Translate(shaderDesc.Target, fragmentMain); shaderDesc.Funcs[(int)SLShaderType.FragmentShader] = fragmentFuncs; // check correct use of constants foreach (var ctor in customCtors) { var funcs = shaderTranslator.Translate(shaderDesc.Target, ctor); var allVars = funcs.SelectMany(func => func.Variables).ToList(); var allGlobVars = allVars.Where(variables.Contains).ToList(); var illegalVars = allVars.Except(allGlobVars).ToList(); foreach (var illegalVar in illegalVars) { var name = illegalVar.Definition.Name; var instr = illegalVar.Instruction; DebugLog.Error("Illegal use of '" + name + "' in a constructor", instr); } foreach (var constVar in allGlobVars) { var globVar = shaderDesc.Variables.First(var => var.Equals(constVar)); var index = shaderDesc.Variables.IndexOf(globVar); var name = constVar.Definition.Name; var instr = constVar.Instruction; if (globVar.Attribute != SLVariableType.xSLConstAttribute) { DebugLog.Error("Variable '" + name + "' is used as a constant but not marked as such'", instr); } else if (globVar.Value != null && constVar.Value != null) { DebugLog.Error("Constant '" + name + "' cannot be set more than once", instr); } else if (constVar.Value is String) { DebugLog.Error("Constant '" + name + "' was initialized with an invalid value", instr); } else { shaderDesc.Variables[index].Value = constVar.Value; } } } // build both shaders var vertexResult = shaderTranslator.BuildShader(SLShaderType.VertexShader); var fragmentResult = shaderTranslator.BuildShader(SLShaderType.FragmentShader); shaderDesc = shaderTranslator.ShaderDesc; // see if there are unused fields/properties var unusedVars = shaderDesc.Variables.Where(var => !var.IsReferenced); unusedVars = unusedVars.Where(var => var.Attribute != SLVariableType.xSLConstAttribute); foreach (var unsedVar in unusedVars) { DebugLog.Warning("Variable '" + unsedVar.Definition.Name + "' was declared but not used"); } if (!DebugLog.Abort) { Console.WriteLine("\n 4. Building vertex and fragment shader."); // debugging: save to file first, then precompile Console.WriteLine("\n 5. Applying debugging flags if any."); if (!DebugLog.Abort && (xSLShader.xSLDebug.SaveToFile & shaderDesc.DebugFlags) != 0) { var directory = Path.GetDirectoryName(inputPath); if (directory != null) { var combined = new StringBuilder("---- VertexShader ----").NewLine(2); combined.Append(vertexResult).NewLine(3).Append("---- FragmentShader ----"); combined.NewLine(2).Append(fragmentResult); var filePath = Path.Combine(directory, shaderDesc.Name + ".txt"); File.WriteAllText(filePath, combined.ToString()); Console.WriteLine(" => Saved shader to: '" + filePath + "'"); } } if (!DebugLog.Abort && (xSLShader.xSLDebug.PreCompile & shaderDesc.DebugFlags) != 0) { shaderTranslator.PreCompile(vertexResult, fragmentResult); } } else { Console.WriteLine("\n 4. Shader will not be built due to critical errors."); Console.WriteLine("\n 5. Applying debugging flags if any."); } if (DebugLog.Abort) { if ((xSLShader.xSLDebug.ThrowException & shaderDesc.DebugFlags) != 0) { Console.WriteLine(" => Errors will be thrown when using the shader."); Console.WriteLine("\n 6. Preparing to update meta assembly for this shader."); } } // save shaders or errors into the assembly var genShader = metaAsm.MainModule.Types.First(type => type.ToType() == typeof(xSL <>)); var instShader = new GenericInstanceType(genShader); var asmTypeImport = metaAsm.MainModule.Import(asmType); instShader.GenericArguments.Add(asmTypeImport); var instrList = new List <Instruction>(); if (!DebugLog.Abort) { Console.WriteLine("\n 6. Preparing to update meta assembly for this shader."); var vertField = GenericFieldReference(genShader, instShader, "_vertex"); var fragField = GenericFieldReference(genShader, instShader, "_fragment"); var transField = GenericFieldReference(genShader, instShader, "_translated"); metaAsm.MainModule.Import(vertField); metaAsm.MainModule.Import(fragField); metaAsm.MainModule.Import(transField); instrList.Add(Instruction.Create(OpCodes.Ldstr, vertexResult.ToString())); instrList.Add(Instruction.Create(OpCodes.Stsfld, vertField)); instrList.Add(Instruction.Create(OpCodes.Ldstr, fragmentResult.ToString())); instrList.Add(Instruction.Create(OpCodes.Stsfld, fragField)); instrList.Add(Instruction.Create(OpCodes.Ldc_I4_1)); instrList.Add(Instruction.Create(OpCodes.Stsfld, transField)); } // apply debug mode ThrowException if (DebugLog.Abort && (xSLShader.xSLDebug.ThrowException & shaderDesc.DebugFlags) != 0) { var errors = DebugLog.Errors.ToString().Replace(" =>", "=>"); var errField = GenericFieldReference(genShader, instShader, "_error"); instrList.Add(Instruction.Create(OpCodes.Ldstr, errors)); instrList.Add(Instruction.Create(OpCodes.Stsfld, errField)); } shaderDesc.Instructions = instrList; Console.WriteLine(DebugLog.Abort ? "\n ---- Translation failed ----" : "\n ---- Translation succeeded ----"); shaderDescs.Add(shaderDesc); } // write shaders into assembly var invalidCt = shaderDescs.Count(shader => !shader.Instructions.Any()); if (invalidCt == shaderDescs.Count) { Console.WriteLine("\n\nAssembly will not be updated as no shader was translated successfully."); } else { Console.WriteLine("\n\nUpdating CrossSL meta assembly:"); var asmModule = metaAsm.MainModule; var genShader = asmModule.Types.First(type => type.ToType() == typeof(xSL <>)); var xSLInit = genShader.Methods.First(method => method.Name == "Init"); var ilProc = xSLInit.Body.GetILProcessor(); xSLInit.Body.Instructions.Clear(); foreach (var shaderDesc in shaderDescs) { foreach (var instr in shaderDesc.Instructions) { ilProc.Append(instr); } } var ret = Instruction.Create(OpCodes.Ret); ilProc.Append(ret); try { var writeParams = new WriterParameters { WriteSymbols = false }; asmModule.Write(metaPath, writeParams); foreach (var shaderDesc in shaderDescs) { if (shaderDesc.Instructions.Count() > 2) { Console.WriteLine(" => Added shader '" + shaderDesc.Name + "' to assembly."); } else if (shaderDesc.Instructions.Any()) { Console.WriteLine(" => [ThrowException] mode was applied for shader '" + shaderDesc.Name + "'."); } } Console.WriteLine("\n => Saved assembly as '" + metaPath + "'"); } catch (IOException) { Console.WriteLine(" => Cannot update assembly. File might be missing, read-only or in use."); } } DebugLog.UsageError("\nDone."); return(0); }