public VFetchXboxMethodExtractor(SourceShader shader, HlslTechnique technique) { this.source = shader; HlslMethod method = shader.GetMethod(technique.VertexShaderMethodName, Platform.Xbox); if (method != null && method.UsesVFetch) { vsCode = ProcessMethod(method, technique.VertexShaderArgs, ShaderProfile.VS_3_0); } method = shader.GetMethod(technique.PixelShaderVersion, Platform.Xbox); //pixel shader using vfetch?! if (method != null && method.UsesVFetch) { psCode = ProcessMethod(method, technique.PixelShaderArgs, ShaderProfile.PS_3_0); } }
public static byte[] Generate(SourceShader source, HlslTechnique technique, Platform platform) { // ShaderExtensionGenerator exGen = new ShaderExtensionGenerator(source, platform); TokenRename vsRename, psRename; TokenRename vsBlendRename, vsInstanceRename; vsRename = new TokenRename(source, platform, "VS"); psRename = new TokenRename(source, platform, "PS"); vsBlendRename = new TokenRename(source, platform, "VS_B"); vsInstanceRename = new TokenRename(source, platform, "VS_I"); HlslMethod methodVS = source.GetMethod(technique.VertexShaderMethodName, platform); HlslMethod methodPS = source.GetMethod(technique.PixelShaderMethodName, platform); HlslMethod methodBlending = source.GetMethod(technique.BlendingShaderMethodName, platform); HlslMethod methodInstancing = source.GetMethod(technique.InstancingShaderMethodName, platform); if (methodPS == null || methodVS == null) return null; string vsPrefix, psPrefix; AsmTechnique asmTechnique = source.GetAsmTechnique(technique.Name, platform); vsPrefix = BuildMapping(asmTechnique, "vs", asmTechnique.VertexShader.RegisterSet); psPrefix = BuildMapping(asmTechnique, "ps", asmTechnique.PixelShader.RegisterSet); string blendingPrefix = null, instancingPrefix = null; if (asmTechnique.BlendingShader != null) blendingPrefix = BuildMapping(asmTechnique, "vsb", asmTechnique.BlendingShader.RegisterSet); if (asmTechnique.InstancingShader != null) instancingPrefix = BuildMapping(asmTechnique, "vsi", asmTechnique.InstancingShader.RegisterSet); //build the method bodies for the technique //process through all the methods used. vsRename.methodList.Add(methodVS); psRename.methodList.Add(methodPS); if (methodBlending != null) vsBlendRename.methodList.Add(methodBlending); if (methodInstancing != null) vsInstanceRename.methodList.Add(methodInstancing); //setup remapping for registers used MapRegistersToRenamer(vsRename, asmTechnique.VertexShader.RegisterSet, "vs"); MapRegistersToRenamer(psRename, asmTechnique.PixelShader.RegisterSet, "ps"); if (asmTechnique.BlendingShader != null) { MapRegistersToRenamer(vsBlendRename, asmTechnique.VertexShader.RegisterSet, "vs"); MapRegistersToRenamer(vsBlendRename, asmTechnique.BlendingShader.RegisterSet, "vsb"); } if (asmTechnique.InstancingShader != null) { MapRegistersToRenamer(vsInstanceRename, asmTechnique.VertexShader.RegisterSet, "vs"); MapRegistersToRenamer(vsInstanceRename, asmTechnique.InstancingShader.RegisterSet, "vsi"); } //finally, parse all the registers used by the Effect. Dump them in as 'unused' RegisterSet decompiledEffectRegisters = source.GetDecompiledEffect(platform).EffectRegisters; string unusedSamplerName = "__unused_sampler"; string unusedFloatName = "__unused_"; string unusedMatrixName = "__unused4x4_"; string unusedArrayName = "__unused_array"; foreach (Register reg in decompiledEffectRegisters) { if (reg.Name == null || reg.Category == RegisterCategory.Texture) continue; if (reg.Category == RegisterCategory.Sampler) { vsRename.unusedRemapping.Add(reg.Name, unusedSamplerName); psRename.unusedRemapping.Add(reg.Name, unusedSamplerName); vsBlendRename.unusedRemapping.Add(reg.Name, unusedSamplerName); vsInstanceRename.unusedRemapping.Add(reg.Name, unusedSamplerName); } else { vsRename.unusedRemapping.Add(reg.Name, reg.ArraySize > 0 ? unusedArrayName : (reg.Size > 1 ? unusedMatrixName : unusedFloatName)); psRename.unusedRemapping.Add(reg.Name, reg.ArraySize > 0 ? unusedArrayName : (reg.Size > 1 ? unusedMatrixName : unusedFloatName)); vsBlendRename.unusedRemapping.Add(reg.Name, reg.ArraySize > 0 ? unusedArrayName : (reg.Size > 1 ? unusedMatrixName : unusedFloatName)); vsInstanceRename.unusedRemapping.Add(reg.Name, reg.ArraySize > 0 ? unusedArrayName : (reg.Size > 1 ? unusedMatrixName : unusedFloatName)); } } //will write booleans down if the values are used. vsRename.unusedAccessed = new Dictionary<string, bool>(); psRename.unusedAccessed = vsRename.unusedAccessed; vsBlendRename.unusedAccessed = vsRename.unusedAccessed; vsInstanceRename.unusedAccessed = vsRename.unusedAccessed; //note, the method list will be added to as new methods are needed by the base method. StringBuilder sb = new StringBuilder(); //extract PS methods ExtractMethodBodies(sb, psRename); //extract VS methods ExtractMethodBodies(sb, vsRename); ExtractMethodBodies(sb, vsBlendRename); ExtractMethodBodies(sb, vsInstanceRename); string techniqueVsName = GetTechniqueInvoke(vsRename, sb, methodVS, technique.VertexShaderArgs); string techniquePsName = GetTechniqueInvoke(psRename, sb, methodPS, technique.PixelShaderArgs); string techniqueBlendingName = null, techniqueInstancingName = null; if (asmTechnique.BlendingShader != null) techniqueBlendingName = GetTechniqueInvoke(vsBlendRename, sb, methodBlending, technique.BlendingShaderArgs); if (asmTechnique.InstancingShader != null) techniqueInstancingName = GetTechniqueInvoke(vsInstanceRename, sb, methodInstancing, technique.InstancingShaderArgs); StringBuilder constants = new StringBuilder(); //work out if any unused values were touched... //if so, add them. bool addUnusedSampler = false; bool addUnusedFloat = false; foreach (KeyValuePair<string,bool> item in vsRename.unusedAccessed) { if (item.Value) { //something has been used unexpectadly. Register reg; if (decompiledEffectRegisters.TryGetRegister(item.Key,out reg)) { if (reg.Category == RegisterCategory.Sampler) addUnusedSampler = true; else addUnusedFloat = true; } } } if (addUnusedSampler) constants.AppendLine("sampler " + unusedSamplerName + ";"); if (addUnusedFloat) { constants.AppendLine("const static float4 " + unusedFloatName + " = 0;"); constants.AppendLine("const static float4x4 " + unusedMatrixName + " = 0;"); constants.AppendLine("#define " + unusedArrayName + "(__INDEX__) " + unusedFloatName); } //setup the VS/PS constants asmTechnique.ConstructTechniqueConstants(constants, true); constants.Append(vsPrefix); constants.Append(psPrefix); if (blendingPrefix != null) constants.Append(blendingPrefix); if (instancingPrefix != null) constants.Append(instancingPrefix); constants.AppendLine(ExtractFixedDeclarations(source.HlslShader)); //finally, build the technique delcaration string techniqueStructure = @"{2} {3} technique Shader {0} pass {0} VertexShader = compile {4} {5}; PixelShader = compile {6} {7}; {1} {8}{9}{1}"; string extensionPass = @" pass {2} {0} VertexShader = compile {3} {4}; PixelShader = compile {5} {6}; {1} "; string blendExtension = "", instanceExtension = ""; if (asmTechnique.BlendingShader != null) { blendExtension = string.Format(extensionPass, "{", "}", "Blending", technique.GetVertexShaderVersion(platform), techniqueBlendingName, technique.GetPixelShaderVersion(platform), techniquePsName); } if (asmTechnique.InstancingShader != null) { instanceExtension = string.Format(extensionPass, "{", "}", "Instancing", "vs_3_0", techniqueInstancingName, "ps_3_0", techniquePsName); } string techniqueSource = string.Format(techniqueStructure, "{", "}", constants, sb, technique.GetVertexShaderVersion(platform), techniqueVsName, technique.GetPixelShaderVersion(platform), techniquePsName, blendExtension,instanceExtension); var types = typeof(EffectCompiler).Assembly.GetType("Xen.Graphics.ShaderSystem.EffectCompiler"); var method = types.GetMethod("CompileEffect"); CompileEffect effect = Delegate.CreateDelegate(typeof(CompileEffect), types.GetMethod("CompileEffect")) as CompileEffect; string compileErrors; byte[] compiledEffect = EffectCompiler.CompileEffect(techniqueSource, source.FileName, platform == Platform.Xbox, out compileErrors); if (compileErrors != null) { string[] errors = SanitizeConversionErrorMessage(compileErrors, techniqueSource, source.ShaderSource, source); if (source.ManualExtensions == false) { string errorsLine = ""; foreach (var error in errors) errorsLine += error + Environment.NewLine; //error occured in generated code! ShaderExtensionGenerator.ThrowGeneratorError(errorsLine.Trim(), source.FileName); } else { string header = "XenFX Platform Technique Extraction Failed:"; if (errors == null) Common.ThrowError(header, compileErrors); else Common.ThrowError(header, errors); } } return compiledEffect; }
private void ExtractMethods(string generatedPrefix) { string str = this.hlslShader.ToString(); List<Platform> platformStack = new List<Platform>(); //pull out methods and techniques, but only from the root level... foreach (HlslStructure hs in this.hlslShader.Children) { //could be an #if, #else, #etc if (hs.Children.Length == 0 && hs.Elements.Length > 1 && hs.Elements[0].Statement.Length == 1 && hs.Elements[0].Statement[0] == '#') { //need to account for #if XBOX360 blocks switch (hs.Elements[1].Statement) { case "if": case "ifdef": if (hs.Elements.Length > 2) { if (hs.Elements[2] == "XBOX360" || hs.Elements[2] == "XBOX") platformStack.Add(Platform.Xbox); else if (hs.Elements[2] == "!XBOX360" || hs.Elements[2] == "!XBOX") platformStack.Add(Platform.Windows); else platformStack.Add(Platform.Both); } break; case "ifndef": if (hs.Elements.Length > 2) { if (hs.Elements[2] == "XBOX360" || hs.Elements[2] == "XBOX") platformStack.Add(Platform.Windows); else if (hs.Elements[2] == "!XBOX360" || hs.Elements[2] == "!XBOX") platformStack.Add(Platform.Xbox); else platformStack.Add(Platform.Both); } break; case "else": if (platformStack.Count > 0) { Platform peek = platformStack[platformStack.Count - 1]; platformStack.RemoveAt(platformStack.Count - 1); if (peek == Platform.Xbox) platformStack.Add(Platform.Windows); if (peek == Platform.Windows) platformStack.Add(Platform.Xbox); } break; case "endif": if (platformStack.Count > 0) platformStack.RemoveAt(platformStack.Count - 1); break; } } if (hs.BraceEnclosedChildren) { if (hs.Elements.Length > 0 && hs.Elements[0].Statement.Equals("technique", StringComparison.InvariantCultureIgnoreCase)) { //figure out the platform, based on #if blocks stack. Platform platform = Platform.Both; for (int i = 0; i < platformStack.Count; i++) platform &= platformStack[i]; var technique = new HlslTechnique(hs, platform, generatedPrefix); if (technique.IsGenerated || generatedPrefix == null) this.techniques.Add(technique); } //finding a method is a bit trickier if (hs.Elements.Length > 2) { //should have a (...) block in it to be a method... int openDepth = 0; for (int i = 2; i < hs.Elements.Length; i++) { if (hs.Elements[i] == "(") openDepth++; if (hs.Elements[i] == ")") { if (--openDepth == 0) { //figure out the platform, based on #if blocks stack. Platform platform = Platform.Both; for (int p = 0; p < platformStack.Count; p++) platform &= platformStack[p]; //found the method. this.methods.Add(new HlslMethod(hs, platform)); break; } } } } } } foreach (SourceShader child in this.includedSource) child.ExtractMethods(generatedPrefix); }
private string ProcessTechniqueVS(SourceShader shader, HlslTechnique technique, AsmTechnique asmTechnique, ShaderExtension extension, Platform platform) { HlslMethod vs = shader.GetMethod(technique.VertexShaderMethodName, platform); HlslMethod ps = shader.GetMethod(technique.PixelShaderMethodName, platform); if (vs == null) throw new CompileException(string.Format("Unabled to find declaration for vertex shader method '{0}'", technique.VertexShaderMethodName)); if (ps == null) throw new CompileException(string.Format("Unabled to find declaration for pixel shader method '{0}'", technique.PixelShaderMethodName)); string nameAppend = "_" + Guid.NewGuid().ToString("N") + "_" + extension.ToString(); string blendName = "__blend_weights__GEN"; string indicesName = "__blend_indices__GEN"; //generated merged matrices StringBuilder matrixGen = new StringBuilder(); foreach (var remap in matrixRemap.Values) { if (remap.BaseName != null) // this will be null for the world matrix { //find the matching register in the technique string matrixType; string matrixExpansion = GetMatrixRankExpansion(remap.Name, asmTechnique, out matrixType); if (extension == ShaderExtension.Instancing) matrixGen.AppendFormat("\tfloat4x4 {0} = float4x4(({4})mul({1},{2}){3});", remap.RemapTo, worldMatrixName, remap.BaseName, matrixExpansion, matrixType); if (extension == ShaderExtension.Blending) matrixGen.AppendFormat("\tfloat4x4 {0} = float4x4(({4})mul({1},{2}){3});", remap.RemapTo, worldMatrixName, remap.Name, matrixExpansion, matrixType); matrixGen.AppendLine(); } } //create the method signatures footer.AppendLine(); bool multiArg = false; StringBuilder argList = new StringBuilder(); int argIndex = -1; bool transposeWorldMatrix = false; foreach (var element in vs.HlslShader.Elements) { if (element.Statement == vs.Name) { footer.Append(vs.Name); footer.Append(nameAppend); } else { if (element.Statement == "(" || element.Statement == ",") argIndex = 0; else { argIndex++; if (element.Statement == "out" || element.Statement == "in" || element.Statement == "inout" || element.Statement == "uniform" || element.Statement == "const") argIndex--; if (argIndex != -1 && argIndex == 2) { if (argList.Length > 0) argList.Append(", "); argList.Append(element.Statement); } } if (element.Statement == ")") { argIndex = -1; if (methodNames.TryGetValue(vs.Name, out multiArg) && multiArg) footer.Append(", "); if (extension == ShaderExtension.Instancing) { footer.Append("float4x4 " + worldMatrixName + "_transpose : POSITION12"); transposeWorldMatrix = true; } if (extension == ShaderExtension.Blending) { footer.AppendFormat("float4 {0} : BLENDWEIGHT, int4 {1} : BLENDINDICES", blendName, indicesName); } } footer.Append(element); footer.Append(' '); } } footer.AppendLine(); footer.AppendLine("{"); if (transposeWorldMatrix) { footer.AppendLine("\tfloat4x4 " + worldMatrixName + " = transpose(" + worldMatrixName + "_transpose);"); } if (extension == ShaderExtension.Blending) { string calculation = @" float4x4 {3} = transpose(float4x4( {0}[{1}.x * 3 + 0] * {2}.x + {0}[{1}.y * 3 + 0] * {2}.y + {0}[{1}.z * 3 + 0] * {2}.z + {0}[{1}.w * 3 + 0] * {2}.w, {0}[{1}.x * 3 + 1] * {2}.x + {0}[{1}.y * 3 + 1] * {2}.y + {0}[{1}.z * 3 + 1] * {2}.z + {0}[{1}.w * 3 + 1] * {2}.w, {0}[{1}.x * 3 + 2] * {2}.x + {0}[{1}.y * 3 + 2] * {2}.y + {0}[{1}.z * 3 + 2] * {2}.z + {0}[{1}.w * 3 + 2] * {2}.w, float4(0,0,0,1)));"; footer.AppendFormat(calculation, blendMatricesName, indicesName, blendName, worldMatrixName); footer.AppendLine(); } footer.Append(matrixGen); footer.AppendFormat("\t{4}{0}({1}{2}{3});", ToInternalMethodName(vs.Name, extension), methodCallAppend, multiArg ? ", " : "", argList, vs.HasReturnValue ? "return " : ""); footer.AppendLine(); footer.AppendLine("}"); return vs.Name + nameAppend; }
private void GenerateTechnique(SourceShader shader, HlslTechnique technique, AsmTechnique asmTechnique, Platform platform) { string blendVS = ProcessTechniqueVS(shader, technique, asmTechnique, ShaderExtension.Blending, platform); string instVS = ProcessTechniqueVS(shader, technique, asmTechnique, ShaderExtension.Instancing, platform); footer.AppendLine(); ////append the new technique string techniqueName = technique.Name + techniquePostfix; string code = @" technique {0} {11} {9} pass {9} VertexShader = compile {1} {5}({6}); PixelShader = compile {2} {3}({4}); {10} pass Blending {9} VertexShader = compile {1} {7}({6}); {10} pass Instancing {9} VertexShader = compile {1} {8}({6}); {10} {10} "; var annotation = new StringBuilder(); var asm = shader.GetAsmTechnique(technique.Name, platform); if (asm != null && asm.TechniqueExtraData != null && asm.TechniqueExtraData.ClassBaseTypes != null && asm.TechniqueExtraData.ClassBaseTypes.Length > 0) { annotation.Append("< string BaseTypes = \""); bool first = true; foreach (var baseType in asm.TechniqueExtraData.ClassBaseTypes) { if (!first) annotation.Append(", "); first = false; annotation.Append(baseType); } annotation.Append("\"; >"); } footer.AppendFormat( code, techniqueName, technique.GetVertexShaderVersion(platform), technique.GetPixelShaderVersion(platform), technique.PixelShaderMethodName, CombineArgs(technique.PixelShaderArgs), technique.VertexShaderMethodName, CombineArgs(technique.VertexShaderArgs), blendVS, instVS, "{","}", annotation); }