private HlslStructure(ShaderStatement[] el, HlslStructure[] ch, bool braceEnclosed, bool bracketEnclosedChildren) { this.Elements = el; this.Children = ch; this.BraceEnclosedChildren = braceEnclosed; this.BracketEnclosedChildren = bracketEnclosedChildren; }
//extracts the techniques internal HlslMethod(HlslStructure hs, Platform platform) { this.hs = hs; this.platform = platform; for (int i = 1; i < hs.Elements.Length; i++) { if (hs.Elements[i] == "(") { name = hs.Elements[i - 1]; } } foreach (HlslStructure child in hs.GetEnumerator()) { if (child != hs) { for (int i = 0; i < child.Elements.Length - 1; i++) { if (child.Elements[i] == VFetchIncludeHandler.IncludeSymbol && child.Elements[i + 1] == "(") { usesVFetch = true; } } if (usesVFetch) { break; } } } }
public SourceShader(string shaderSource, string filename, bool extractAsmTechniques) { this.shaderSource = shaderSource; this.filename = filename; this.sourceLines = shaderSource.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); this.shaderExtensions = new Dictionary <string, string>(); this.techniques = new List <HlslTechnique>(); this.methods = new List <HlslMethod>(); this.hlslShader = new HlslStructure(shaderSource); this.includedSource = new List <SourceShader>(); if (sourceLines.Length > 0) { this.compilerOptions = ComputeCompilerOps(sourceLines[0], out internalClass, out poolShaderBytes, out useParentNamespace, out mixedMode, out debugHlslProcessXboxShader, out skipConstantValidation); ComputeClassExtensions(sourceLines[0], this.shaderExtensions); } if (sourceLines.Length > 1) { ComputeClassExtensions(sourceLines[1], this.shaderExtensions); } if (poolShaderBytes) { this.bytePoolPc = new Dom.BytePool(); this.bytePoolXbox = new Dom.BytePool(); } this.ExtractIncludeSource(ref this.useVfetch); this.mixedMode &= !this.useVfetch; this.ExtractMethods(); asmTechniques = new List <AsmTechnique>(); if (!mixedMode) { xboxAsmTechniques = new List <AsmTechnique>(); } if (extractAsmTechniques) { this.ExtractAsmTechniques(); } }
//extracts the techniques internal HlslTechnique(HlslStructure hs, Platform platform) { this.hs = hs; this.platform = platform; this.name = hs.Elements[1]; //first child should be the first pass for (int i = 0; i < hs.Children.Length; i++) { if (hs.Children[i].Elements.Length > 0 && hs.Children[i].Elements[0] == "pass") { //good. ExtractPass(hs.Children[i], out pixelShaderMethodName, out vertexShaderMethodName, out psVersion, out vsVersion, out psArgs, out vsArgs); break; } } }
private SourceShader(string shaderSource, string filename, string generatedAppend) { this.sourceLines = shaderSource.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); if (sourceLines.Length > 0) { ComputeCompilerOps(sourceLines[0], out internalClass, out useParentNamespace, out mixedMode, out debugHlslProcessXboxShader, out skipConstantValidation, out exposeRegisters, out manualExtensions); } this.shaderSource = shaderSource; this.filename = filename; this.techniques = new List<HlslTechnique>(); this.methods = new List<HlslMethod>(); this.hlslShader = new HlslStructure(shaderSource); this.includedSource = new List<SourceShader>(); this.ExtractIncludeSource(); this.ExtractMethods(generatedAppend); asmTechniques = new List<AsmTechnique>(); if (!mixedMode) xboxAsmTechniques = new List<AsmTechnique>(); }
void TranslateMatrices(HlslStructure.ShaderStatement[] elements, int callDepth, ShaderExtension extension) { bool isInstancing = extension == ShaderExtension.Instancing; List<int> skipFinalRemappingIndices = new List<int>(); int processingMul = 0, depth = 0, arg = 0; int argStart = 0, argEnd = 0; int remapArg = -1; int remapIndex = 0; MatrixMapping remapping = new MatrixMapping(); Stack<int> mulStart = new Stack<int>(); Stack<int> mulDepth = new Stack<int>(); Stack<int> mulArg = new Stack<int>(); Stack<int> mulArgStart = new Stack<int>(); Stack<int> mulArgEnd = new Stack<int>(); Stack<int> mulRemap = new Stack<int>(); Stack<int> mulRemapIdx = new Stack<int>(); Stack<MatrixMapping> mulRemapping = new Stack<MatrixMapping>(); for (int i = 0; i < elements.Length; i++) { string line = elements[i].Statement; if (line == "mul") { processingMul++; mulStart.Push(i); mulDepth.Push(depth); mulArg.Push(arg); mulArgEnd.Push(argEnd); mulArgStart.Push(argStart); mulRemap.Push(remapArg); mulRemapping.Push(remapping); mulRemapIdx.Push(remapIndex); depth = 0; arg = 0; argStart = 0; argEnd = 0; remapArg = -1; remapIndex = 0; remapping = new MatrixMapping(); } MatrixMapping mapping; if (processingMul > 0 && depth == 1 && matrixRemap.TryGetValue(line, out mapping) && (mapping.BaseName != null || !isInstancing)) { remapArg = arg; remapping = mapping; remapIndex = i; } if (processingMul > 0 && line == "(") { if (depth == 0) argStart = i; depth++; } if (processingMul > 0 && depth == 1 && line == ",") { if (depth == 1) argEnd = i; arg++; } if (processingMul > 0 && line == ")") { depth--; if (depth == 0) { string baseMatrix = remapping.Name; if (isInstancing) baseMatrix = remapping.BaseName; string prefix = ""; int startPrefix = -1; if (remapArg == 1) startPrefix = argEnd; if (remapArg == 0) startPrefix = argStart; if (remapIndex > startPrefix && startPrefix != -1) { for (int p = startPrefix + 1; p < remapIndex; p++) prefix += elements[p].Statement + " "; } //right, see if the args need modifying. if (remapArg == 1) { //a known remapping matrix was used. var element = elements[remapIndex]; elements[remapIndex] = new HlslStructure.ShaderStatement(baseMatrix, element.Line); element = elements[argStart]; elements[argStart] = new HlslStructure.ShaderStatement("(mul(", element.Line); element = elements[argEnd]; elements[argEnd] = new HlslStructure.ShaderStatement("," + prefix + worldMatrixName + "),", element.Line); if (!isInstancing) skipFinalRemappingIndices.Add(remapIndex); } if (remapArg == 0) // transpose { //a known remapping matrix was used. var element = elements[remapIndex]; elements[remapIndex] = new HlslStructure.ShaderStatement(baseMatrix, element.Line); element = elements[argEnd]; elements[argEnd] = new HlslStructure.ShaderStatement(",mul(" + prefix + worldMatrixName + ",", element.Line); element = elements[i]; elements[i] = new HlslStructure.ShaderStatement("))", element.Line); if (!isInstancing) skipFinalRemappingIndices.Add(remapIndex); } processingMul--; mulStart.Pop(); depth = mulDepth.Pop(); arg = mulArg.Pop(); argEnd = mulArgEnd.Pop(); argStart = mulArgStart.Pop(); remapArg = mulRemap.Pop(); remapping = mulRemapping.Pop(); remapIndex = mulRemapIdx.Pop(); } } } //find unremapped matrices if (callDepth > 0) { for (int i = 0; i < elements.Length; i++) { //it is intended that this matrix reference is not modified. if (!isInstancing && skipFinalRemappingIndices.Contains(i)) continue; MatrixMapping mapping; if (matrixRemap.TryGetValue(elements[i].Statement, out mapping)) elements[i] = new HlslStructure.ShaderStatement(mapping.RemapTo, elements[i].Line); } } }
bool HlslStructure.ITokenTranslator.IncludeBlock(HlslStructure block, int depth) { return true; }
private static string ExtractFixedDeclarations(HlslStructure shader) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < shader.Children.Length; i++) { if (shader.Children[i].Elements.Length > 0 && (shader.Children[i].Elements[0] == "struct" || shader.Children[i].Elements[0] == "static")) { sb.Append(shader.Children[i].ToString()); sb.AppendLine(";"); } } return sb.ToString(); }
public bool IncludeBlock(HlslStructure block, int depth) { return true; }
private static void ExtractPass(HlslStructure pass, out string ps, out string vs, out string psVersion, out string vsVersion, out string[] psArgs, out string[] vsArgs) { ps = vs = vsVersion = psVersion = null; psArgs = vsArgs = null; //a bit nasty... foreach (HlslStructure hs in pass.Children) { //should be: // //VertexShader = compile vs_2_0 Zomg(true!); // //or similar int type = -1; string target = null, method = null; List<string> args = new List<string>(); int paranethDepth = 0; for (int i = 0; i < hs.Elements.Length; i++) { if (hs.Elements[i].Statement.Equals("VertexShader", StringComparison.InvariantCultureIgnoreCase)) type = 1; if (hs.Elements[i].Statement.Equals("PixelShader", StringComparison.InvariantCultureIgnoreCase)) type = 2; if (hs.Elements[i].Statement.Equals("compile", StringComparison.InvariantCultureIgnoreCase)) target = hs.Elements[i + 1]; if (hs.Elements[i] == ")") { paranethDepth--; } if (paranethDepth > 0) args.Add(hs.Elements[i]); if (hs.Elements[i] == "(") { if (paranethDepth == 0) method = hs.Elements[i - 1]; paranethDepth++; } } if (type == 1) { vs = method; vsArgs = args.ToArray(); vsVersion = target; } if (type == 2) { ps = method; psArgs = args.ToArray(); psVersion = target; } } }
//extracts the techniques internal HlslTechnique(HlslStructure hs, Platform platform, string generatedPrefix) { this.hs = hs; this.platform = platform; this.name = hs.Elements[1]; bool primaryPassSet = false; if (generatedPrefix != null) { if (name.EndsWith(generatedPrefix)) { name = name.Substring(0, name.Length - generatedPrefix.Length); IsGenerated = true; } } //first child should be the first pass for (int i = 0; i < hs.Children.Length; i++) { if (hs.Children[i].Elements.Length > 0 && hs.Children[i].Elements[0] == "pass") { //good. //this could be an animation or blending pass bool isAnimation = false, isInstancing = false; if (hs.Children[i].Elements.Length > 1) AsmTechnique.ExtractPassType(hs.Children[i].Elements[1], out isAnimation, out isInstancing); if (isAnimation || isInstancing) { string ignored, vs_version, name; string[] args_ignored, args; ExtractPass(hs.Children[i], out ignored, out name, out ignored, out vs_version, out args_ignored, out args); // if (vs_version != vsVersion) // throw new CompileException(string.Format("Technique Extension Pass '{0}' cannot use a different vertex shader version", hs.Children[i].Elements[1])); if (isInstancing) { instancingShaderMethodName = name; instancingArgs = args; } else { blendingShaderMethodName = name; blendArgs = args; } } else if (!primaryPassSet) { ExtractPass(hs.Children[i], out pixelShaderMethodName, out vertexShaderMethodName, out psVersion, out vsVersion, out psArgs, out vsArgs); primaryPassSet = true; } } } }
//extracts the techniques internal HlslMethod(HlslStructure hs, Platform platform) { this.hs = hs; this.platform = platform; for (int i = 1; i < hs.Elements.Length; i++) { if (hs.Elements[i] == "(") name = hs.Elements[i - 1]; } }
private static void ExtractPass(HlslStructure pass, out string ps, out string vs, out string psVersion, out string vsVersion, out string[] psArgs, out string[] vsArgs) { ps = vs = vsVersion = psVersion = null; psArgs = vsArgs = null; //a bit nasty... foreach (HlslStructure hs in pass.Children) { //should be: // //VertexShader = compile vs_2_0 Zomg(true!); // //or similar int type = -1; string target = null, method = null; List <string> args = new List <string>(); int paranethDepth = 0; for (int i = 0; i < hs.Elements.Length; i++) { if (hs.Elements[i].Equals("VertexShader", StringComparison.InvariantCultureIgnoreCase)) { type = 1; } if (hs.Elements[i].Equals("PixelShader", StringComparison.InvariantCultureIgnoreCase)) { type = 2; } if (hs.Elements[i].Equals("compile", StringComparison.InvariantCultureIgnoreCase)) { target = hs.Elements[i + 1]; } if (hs.Elements[i] == ")") { paranethDepth--; } if (paranethDepth > 0) { args.Add(hs.Elements[i]); } if (hs.Elements[i] == "(") { if (paranethDepth == 0) { method = hs.Elements[i - 1]; } paranethDepth++; } } if (type == 1) { vs = method; vsArgs = args.ToArray(); vsVersion = target; } if (type == 2) { ps = method; psArgs = args.ToArray(); psVersion = target; } } }
void TranslateMethodNames(HlslStructure.ShaderStatement[] elements, int callDepth, ShaderExtension extension) { for (int i = 0; i < elements.Length; i++) { string methodName = elements[i].Statement; bool multArg; if (methodNames.TryGetValue(methodName, out multArg)) { BuildMethod(methodName, extension); //find the method start for (int n = i+1; n < elements.Length; n++) { if (elements[n] == "(") { string argAppend = multArg ? ", " : ""; //append the method signatures if (callDepth == 0) elements[n] = new HlslStructure.ShaderStatement("(" + this.methodSignatureAppend + argAppend, elements[n].Line); else elements[n] = new HlslStructure.ShaderStatement("(" + this.methodCallAppend + argAppend, elements[n].Line); elements[i] = new HlslStructure.ShaderStatement(ToInternalMethodName(methodName,extension), elements[i].Line); break; } } } } }
bool HlslStructure.ITokenTranslator.IncludeBlock(HlslStructure block, int depth) { TranslateMatrices(block.Elements, depth, methodExtractionExtension); TranslateMethodNames(block.Elements, depth, methodExtractionExtension); return true; }
private void UnwindBackup(HlslStructure hlslStructure) { HlslStructure.ShaderStatement[] elements = structureBackup[hlslStructure]; for (int i = 0; i < hlslStructure.Elements.Length; i++) hlslStructure.Elements[i] = elements[i]; foreach (var child in hlslStructure.Children) UnwindBackup(child); }
private void BackupStructure(HlslStructure hlslStructure) { structureBackup.Add(hlslStructure, hlslStructure.Elements.Clone() as HlslStructure.ShaderStatement[]); foreach (var child in hlslStructure.Children) BackupStructure(child); }
public bool IncludeBlock(HlslStructure block, int depth) { return !techniques.Contains(block); }
private byte[] ProcessMethod(HlslMethod method, IEnumerator <string> argsEnum, ShaderProfile shaderProfile) { //this is where things get silly... :-) //the method uses vfetch, and for various reasons, the shader code cannot be extracted from an Effect //the shader also cannot be decompiled. //However, a single shader can be compiled from HLSL. Which is great.... except... //the technique may define the method to use, and may pass in arguements.. // like so: // // VertexShader = compile vs_2_0 zomg(true); // //In such a case, the method 'zomg' can't be directly compiled using ShaderCompiler. //When this is the case, things get stupid. // //The way to compile the method is to create a stub that calls the real method, //with values for the arguements. Modifying the method declaration with default arguements //wouldn't always work... //this is, of course, assuming the method has uniform arguements being passed in. //if it isn't, then no problem. List <string> rawArgs = new List <string>(); while (argsEnum.MoveNext()) { rawArgs.Add(argsEnum.Current); } CompiledShader compiledShader; string workingDirectory = null; if (rawArgs.Count == 0) { //just compile the method directly (easy)... try { //set the working directory, since it's using CompileFromFile workingDirectory = Directory.GetCurrentDirectory(); string path = Path.GetDirectoryName(source.FileName); if (path != null && path.Length > 0) { Directory.SetCurrentDirectory(Path.GetDirectoryName(source.FileName)); } compiledShader = ShaderCompiler.CompileFromFile( Path.GetFileName(source.FileName), DecompiledEffect.XboxCompileMacros, new VFetchIncludeHandler(source.FileName, false), source.CompilerOptions, method.Name, shaderProfile, Microsoft.Xna.Framework.TargetPlatform.Xbox360); } finally { if (workingDirectory != null) { Directory.SetCurrentDirectory(workingDirectory); } } } else { //this gets much trickier.. //have to extract the args, and importantly, put them in the right place /* * * eg, a method and it's technique may look like this: * * float4 Test(float4 pos : POSITION, uniform float scale) : POSITION * { * ... vfetch ... * } * * technique * { * VertexShader = compile vs_2_0 Test(5.0); * ... * } * * * The stub that is generated must pull the '5.0' from the technique * and pass it into the real method, like so: * * * float4 Test_STUB(float4 pos : POSITION) : POSITION * { * return Test(pos, 5.0); * } * * * Note: the uniform was removed from the stub input declaration * */ //the actual arg values (passed into the real method) List <string> args = new List <string>(); StringBuilder arg = new StringBuilder(); int depth = 0; //break the args list up for (int i = 0; i < rawArgs.Count; i++) { if (rawArgs[i].Length == 1 && (rawArgs[i][0] == '(' || rawArgs[i][0] == '{' || rawArgs[i][0] == '[')) { depth++; } if (rawArgs[i].Length == 1 && (rawArgs[i][0] == ')' || rawArgs[i][0] == '}' || rawArgs[i][0] == ']')) { depth--; } if (depth == 0 && rawArgs[i] == ",") { args.Add(arg.ToString()); arg.Length = 0; } else { arg.Append(rawArgs[i]); arg.Append(' '); } } args.Add(arg.ToString()); //the input args that are being replaced must be declared as 'uniform' //parse the method declaration... depth = 0; HlslStructure hs = method.HlslShader; bool parsingArgs = false; int argIndex = 0; StringBuilder stubMethodDeclaration = new StringBuilder(); StringBuilder stubMethodInvoke = new StringBuilder(); int stubMethodInvokeCount = 0; //random name string stubName = "_STUB" + Guid.NewGuid().ToString("N"); bool includingArg = true; int replacedArgIndex = 0; int parseArgIndex = 0; int parseArgNameIndex = 0; bool parseCheckForName = true; bool stubReturnsValue = false; for (int i = 0; i < hs.Elements.Length; i++) { if (hs.Elements[i].Length == 1 && (hs.Elements[i][0] == ')' || hs.Elements[i][0] == '}' || hs.Elements[i][0] == ']')) { depth--; } if (hs.Elements[i].Length == 1 && (hs.Elements[i][0] == '(' || hs.Elements[i][0] == '{' || hs.Elements[i][0] == '[')) { depth++; if (depth == 1 && hs.Elements[i][0] == '(' && !parsingArgs) { //about to begin the method args parsingArgs = true; //append the stub name, so to not conflict with the original method stubMethodDeclaration.Append(stubName); stubMethodDeclaration.Append('('); continue; } } //actually parsing the args within the (,,,,,) block if (parsingArgs) { if (argIndex == 0) { //check for uniform. if (hs.Elements[i] == "uniform" && replacedArgIndex != args.Count) { //replace this arg if possible //add technique value to the invoke if (stubMethodInvokeCount != 0) { stubMethodInvoke.Append(','); } stubMethodInvoke.Append(args[replacedArgIndex]); stubMethodInvokeCount++; replacedArgIndex++; //remove the last written character (a ,) from the stub method, //but only if it's not the first parsed arg if (parseArgIndex != 0 && includingArg) { stubMethodDeclaration.Length--; } //skip it in the stub declaration includingArg = false; } else { includingArg = true; } } if (depth == 1 && (hs.Elements[i].Length == 1 && (hs.Elements[i][0] == ',' || hs.Elements[i][0] == ')'))) { argIndex = 0; parseArgIndex++; //write the element name into the arg invoke list if (includingArg) { if (stubMethodInvokeCount > 0) { stubMethodInvoke.Append(','); } stubMethodInvoke.Append(hs.Elements[parseArgNameIndex]); stubMethodInvokeCount++; } parseCheckForName = true; parseArgNameIndex = 0; } else { if (includingArg) { //want to include the name of the arg in the invoke list. if (hs.Elements[i].Length == 1 && (hs.Elements[i][0] == ':' || hs.Elements[i][0] == '=')) { //the arg is declared, now it's being given a default or semantic parseCheckForName = false; } if (parseCheckForName && depth == 1) { //last value written should be the name of the arg parseArgNameIndex = i; } } argIndex++; } } if (includingArg || depth == 0) { if (stubMethodDeclaration.Length > 0 && i > 0 && Tokenizer.IsIdentifierToken(hs.Elements[i]) && Tokenizer.IsIdentifierToken(hs.Elements[i - 1])) { stubMethodDeclaration.Append(' '); } stubMethodDeclaration.Append(hs.Elements[i]); if (depth == 0 && hs.Elements[i].Length == 1 && hs.Elements[i][0] == ':') { //method returns a value that is important somehow... //ie, //float4 Method() : POSITION stubReturnsValue = true; } } } //yikes. //at this point, //stubMethodDeclaration will have the declaration of the method, without the uniform parametres //stubMethodInvoke will have the list of arguements to pass into the real method, from the stub method. //so construct the full stub string fullStub = string.Format("{0}{1}{5}{1}\t{2}{3}({4});{1}{6}", stubMethodDeclaration, Environment.NewLine, stubReturnsValue ? "return " : "", method.Name, stubMethodInvoke, "{", "}"); //append it to the end of the real shader StringBuilder fullShader = new StringBuilder(); fullShader.Append(source.ShaderSource); fullShader.AppendLine(); fullShader.Append(fullStub); //now compile the bugger... try { //set the working directory, since it's using CompileFromSource workingDirectory = Directory.GetCurrentDirectory(); string path = Path.GetDirectoryName(source.FileName); if (path != null && path.Length > 0) { Directory.SetCurrentDirectory(Path.GetDirectoryName(source.FileName)); } //not all compiler options apply when compiling a single shader (instead of an effect) CompilerOptions options = source.CompilerOptions & (CompilerOptions.AvoidFlowControl | CompilerOptions.PreferFlowControl); //compile it... finally... compiledShader = ShaderCompiler.CompileFromSource( fullShader.ToString(), DecompiledEffect.XboxCompileMacros, new VFetchIncludeHandler(source.FileName, false), options, method.Name + stubName, shaderProfile, Microsoft.Xna.Framework.TargetPlatform.Xbox360); } finally { if (workingDirectory != null) { Directory.SetCurrentDirectory(workingDirectory); } } } if (!compiledShader.Success) { Common.ThrowError(compiledShader.ErrorsAndWarnings, source.ShaderSource); } return(compiledShader.GetShaderCode()); }