Ejemplo n.º 1
0
        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);
            }
        }
Ejemplo n.º 2
0
 public HlslMethod GetMethod(string name, Platform platform)
 {
     for (int i = 0; i < this.methods.Count; i++)
     {
         if (this.methods[i].Name == name)
         {
             if ((this.methods[i].Platform & platform) == platform)
             {
                 return(this.methods[i]);
             }
         }
     }
     for (int i = 0; i < this.includedSource.Count; i++)
     {
         HlslMethod method = this.includedSource[i].GetMethod(name, platform);
         if (method != null)
         {
             return(method);
         }
     }
     return(null);
 }
		private static string GetTechniqueInvoke(TokenRename rename, StringBuilder sb, HlslMethod method, IEnumerator<string> argsEnum)
		{
			string methodCall = rename.TranslateMethodName(method.Name);

			StringBuilder call = new StringBuilder();

			call.Append(methodCall);
			call.Append("(");

			while (argsEnum.MoveNext())
			{
				call.Append(argsEnum.Current);
			}
			call.Append(")");

			return call.ToString();
		}
		private static void ExtractMethodBody(TokenRename rename, StringBuilder sb, HlslMethod method, string prefix)
		{
			method.HlslShader.ToString(sb, 0, rename, prefix, true, false);
		}
		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();
		}
Ejemplo n.º 6
0
        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());
        }