public override CompiledEffectContent Process(EffectContent input, ContentProcessorContext context) { //System.Diagnostics.Debugger.Launch(); // If this isn't a MonoGame platform then do the default processing. var platform = ContentHelper.GetMonoGamePlatform(); if (platform == MonoGamePlatform.None) return base.Process(input, context); var options = new Options(); options.SourceFile = input.Identity.SourceFilename; options.Profile = platform == MonoGamePlatform.Windows8 ? ShaderProfile.DirectX_11 : ShaderProfile.OpenGL; options.Debug = DebugMode == EffectProcessorDebugMode.Debug; options.OutputFile = context.OutputFilename; // Parse the MGFX file expanding includes, macros, and returning the techniques. ShaderInfo shaderInfo; try { shaderInfo = ShaderInfo.FromFile(options.SourceFile, options); foreach (var dep in shaderInfo.Dependencies) context.AddDependency(dep); } catch (Exception ex) { // TODO: Extract good line numbers from mgfx parser! throw new InvalidContentException(ex.Message, input.Identity, ex); } // Create the effect object. EffectObject effect = null; var shaderErrorsAndWarnings = string.Empty; try { effect = EffectObject.CompileEffect(shaderInfo, out shaderErrorsAndWarnings); } catch (ShaderCompilerException) { throw ProcessErrorsAndWarnings(shaderErrorsAndWarnings, input, context); } // Write out the effect to a runtime format. CompiledEffectContent result; try { using (var stream = new MemoryStream()) { using (var writer = new BinaryWriter(stream)) effect.Write(writer, options); result = new CompiledEffectContent(stream.GetBuffer()); } } catch (Exception ex) { throw new InvalidContentException("Failed to serialize the effect!", input.Identity, ex); } return result; }
public void Write(BinaryWriter writer, Options options) { writer.Write(IsVertexShader); writer.Write(ShaderCode.Length); writer.Write(ShaderCode); writer.Write((byte)_samplers.Length); foreach (var sampler in _samplers) { writer.Write((byte)sampler.type); writer.Write((byte)sampler.textureSlot); writer.Write((byte)sampler.samplerSlot); if (sampler.state != null) { writer.Write(true); writer.Write((byte)sampler.state.AddressU); writer.Write((byte)sampler.state.AddressV); writer.Write((byte)sampler.state.AddressW); writer.Write(sampler.state.BorderColor.R); writer.Write(sampler.state.BorderColor.G); writer.Write(sampler.state.BorderColor.B); writer.Write(sampler.state.BorderColor.A); writer.Write((byte)sampler.state.Filter); writer.Write(sampler.state.MaxAnisotropy); writer.Write(sampler.state.MaxMipLevel); writer.Write(sampler.state.MipMapLevelOfDetailBias); } else writer.Write(false); if (options.Profile == ShaderProfile.OpenGL) writer.Write(sampler.samplerName); writer.Write((byte)sampler.parameter); } writer.Write((byte)_cbuffers.Length); foreach (var cb in _cbuffers) writer.Write((byte)cb); if (options.Profile != ShaderProfile.OpenGL) return; // The rest of this is for GL only! writer.Write((byte)_attributes.Length); foreach (var attrib in _attributes) { writer.Write(attrib.name); writer.Write((byte)attrib.usage); writer.Write((byte)attrib.index); writer.Write((short)0); // Unused } }
public override CompiledEffectContent Process(EffectContent input, ContentProcessorContext context) { //System.Diagnostics.Debugger.Launch(); // If this isn't a MonoGame platform then do the default processing. var platform = ContentHelper.GetMonoGamePlatform(); if (platform == MonoGamePlatform.None) return base.Process(input, context); var options = new Options(); options.SourceFile = input.Identity.SourceFilename; options.DX11Profile = platform == MonoGamePlatform.Windows8 ? true : false; options.OutputFile = context.OutputFilename; // Parse the MGFX file expanding includes, macros, and returning the techniques. ShaderInfo shaderInfo; try { shaderInfo = ShaderInfo.FromFile(options.SourceFile, options); } catch (Exception ex) { throw new InvalidContentException("Failed to parse the effect!", ex); } // Create the effect object. DXEffectObject effect; try { effect = DXEffectObject.FromShaderInfo(shaderInfo); } catch (Exception ex) { throw new InvalidContentException("Failed to create the effect!", ex); } // Write out the effect to a runtime format. CompiledEffectContent result; try { using (var stream = new MemoryStream()) { using (var writer = new BinaryWriter(stream)) effect.Write(writer, options); result = new CompiledEffectContent(stream.GetBuffer()); } } catch (Exception ex) { throw new InvalidContentException("Failed to serialize the effect!", ex); } return result; }
public void Write(BinaryWriter writer, Options options) { writer.Write(IsVertexShader); writer.Write((ushort)ShaderCode.Length); writer.Write(ShaderCode); writer.Write((byte)_samplers.Length); foreach (var sampler in _samplers) { writer.Write((byte)sampler.type); writer.Write((byte)sampler.index); if (sampler.state != null) { writer.Write(true); writer.Write((byte)sampler.state.AddressU); writer.Write((byte)sampler.state.AddressV); writer.Write((byte)sampler.state.AddressW); writer.Write((byte)sampler.state.Filter); writer.Write(sampler.state.MaxAnisotropy); writer.Write(sampler.state.MaxMipLevel); writer.Write(sampler.state.MipMapLevelOfDetailBias); } else writer.Write(false); if (!options.DX11Profile) writer.Write(sampler.samplerName); writer.Write((byte)sampler.parameter); } writer.Write((byte)_cbuffers.Length); foreach (var cb in _cbuffers) writer.Write((byte)cb); if (options.DX11Profile) return; // The rest of this is for GL only! writer.Write((byte)_attributes.Length); foreach (var attrib in _attributes) { writer.Write(attrib.name); writer.Write((byte)attrib.usage); writer.Write((byte)attrib.index); writer.Write(attrib.format); } }
public void Write(BinaryWriter writer, Options options) { if (options.Profile == ShaderProfile.OpenGL) writer.Write(Name); writer.Write((ushort)Size); writer.Write((byte)ParameterIndex.Count); for (var i=0; i < ParameterIndex.Count; i++) { writer.Write((byte)ParameterIndex[i]); writer.Write((ushort)ParameterOffset[i]); } }
/// <summary> /// Writes the effect for loading later. /// </summary> public void Write(BinaryWriter writer, Options options) { // Write a very simple header for identification and versioning. writer.Write(Header.ToCharArray()); writer.Write((byte)Version); // Write an simple identifier for DX11 vs GLSL // so we can easily detect the correct shader type. var profile = (byte)( options.DX11Profile ? 1 : 0 ); writer.Write(profile); // Write all the constant buffers. writer.Write((byte)ConstantBuffers.Count); foreach (var cbuffer in ConstantBuffers) cbuffer.Write(writer, options); // Write all the shaders. writer.Write((byte)Shaders.Count); foreach (var shader in Shaders) shader.Write(writer, options); // Write the parameters. WriteParameters(writer, Parameters, Parameters.Length); // Write the techniques. writer.Write((byte)Techniques.Length); foreach (var technique in Techniques) { writer.Write(technique.name); WriteAnnotations(writer, technique.annotation_handles); // Write the passes. writer.Write((byte)technique.pass_count); for (var p = 0; p < technique.pass_count; p++) { var pass = technique.pass_handles[p]; writer.Write(pass.name); WriteAnnotations(writer, pass.annotation_handles); // Write the index for the vertex and pixel shaders. var vertexShader = GetShaderIndex(STATE_CLASS.VERTEXSHADER, pass.states); var pixelShader = GetShaderIndex(STATE_CLASS.PIXELSHADER, pass.states); writer.Write((byte)vertexShader); writer.Write((byte)pixelShader); // Write the state objects too! if (pass.blendState != null) { writer.Write(true); writer.Write((byte)pass.blendState.AlphaBlendFunction); writer.Write((byte)pass.blendState.AlphaDestinationBlend); writer.Write((byte)pass.blendState.AlphaSourceBlend); writer.Write(pass.blendState.BlendFactor.R); writer.Write(pass.blendState.BlendFactor.G); writer.Write(pass.blendState.BlendFactor.B); writer.Write(pass.blendState.BlendFactor.A); writer.Write((byte)pass.blendState.ColorBlendFunction); writer.Write((byte)pass.blendState.ColorDestinationBlend); writer.Write((byte)pass.blendState.ColorSourceBlend); writer.Write((byte)pass.blendState.ColorWriteChannels); writer.Write((byte)pass.blendState.ColorWriteChannels1); writer.Write((byte)pass.blendState.ColorWriteChannels2); writer.Write((byte)pass.blendState.ColorWriteChannels3); writer.Write(pass.blendState.MultiSampleMask); } else writer.Write(false); if (pass.depthStencilState != null) { writer.Write(true); writer.Write((byte)pass.depthStencilState.CounterClockwiseStencilDepthBufferFail); writer.Write((byte)pass.depthStencilState.CounterClockwiseStencilFail); writer.Write((byte)pass.depthStencilState.CounterClockwiseStencilFunction); writer.Write((byte)pass.depthStencilState.CounterClockwiseStencilPass); writer.Write(pass.depthStencilState.DepthBufferEnable); writer.Write((byte)pass.depthStencilState.DepthBufferFunction); writer.Write(pass.depthStencilState.DepthBufferWriteEnable); writer.Write(pass.depthStencilState.ReferenceStencil); writer.Write((byte)pass.depthStencilState.StencilDepthBufferFail); writer.Write(pass.depthStencilState.StencilEnable); writer.Write((byte)pass.depthStencilState.StencilFail); writer.Write((byte)pass.depthStencilState.StencilFunction); writer.Write(pass.depthStencilState.StencilMask); writer.Write((byte)pass.depthStencilState.StencilPass); writer.Write(pass.depthStencilState.StencilWriteMask); writer.Write(pass.depthStencilState.TwoSidedStencilMode); } else writer.Write(false); if (pass.rasterizerState != null) { writer.Write(true); writer.Write((byte)pass.rasterizerState.CullMode); writer.Write(pass.rasterizerState.DepthBias); writer.Write((byte)pass.rasterizerState.FillMode); writer.Write(pass.rasterizerState.MultiSampleAntiAlias); writer.Write(pass.rasterizerState.ScissorTestEnable); writer.Write(pass.rasterizerState.SlopeScaleDepthBias); } else writer.Write(false); } } }
/// <summary> /// Writes the effect for loading later. /// </summary> public void Write(BinaryWriter writer, Options options) { // Write a very simple header for identification and versioning. writer.Write(Header.ToCharArray()); writer.Write((byte)Version); // Write an simple identifier for DX11 vs GLSL // so we can easily detect the correct shader type. var profile = (byte)options.Profile; writer.Write(profile); // Write the rest to a memory stream. using(MemoryStream memStream = new MemoryStream()) using(BinaryWriter memWriter = new BinaryWriter(memStream)) { // Write all the constant buffers. memWriter.Write((byte)ConstantBuffers.Count); foreach (var cbuffer in ConstantBuffers) cbuffer.Write(memWriter, options); // Write all the shaders. memWriter.Write((byte)Shaders.Count); foreach (var shader in Shaders) shader.Write(memWriter, options); // Write the parameters. WriteParameters(memWriter, Parameters, Parameters.Length); // Write the techniques. memWriter.Write((byte)Techniques.Length); foreach (var technique in Techniques) { memWriter.Write(technique.name); WriteAnnotations(memWriter, technique.annotation_handles); // Write the passes. memWriter.Write((byte)technique.pass_count); for (var p = 0; p < technique.pass_count; p++) { var pass = technique.pass_handles[p]; memWriter.Write(pass.name); WriteAnnotations(memWriter, pass.annotation_handles); // Write the index for the vertex and pixel shaders. var vertexShader = GetShaderIndex(STATE_CLASS.VERTEXSHADER, pass.states); var pixelShader = GetShaderIndex(STATE_CLASS.PIXELSHADER, pass.states); memWriter.Write((byte)vertexShader); memWriter.Write((byte)pixelShader); // Write the state objects too! if (pass.blendState != null) { memWriter.Write(true); memWriter.Write((byte)pass.blendState.AlphaBlendFunction); memWriter.Write((byte)pass.blendState.AlphaDestinationBlend); memWriter.Write((byte)pass.blendState.AlphaSourceBlend); memWriter.Write(pass.blendState.BlendFactor.R); memWriter.Write(pass.blendState.BlendFactor.G); memWriter.Write(pass.blendState.BlendFactor.B); memWriter.Write(pass.blendState.BlendFactor.A); memWriter.Write((byte)pass.blendState.ColorBlendFunction); memWriter.Write((byte)pass.blendState.ColorDestinationBlend); memWriter.Write((byte)pass.blendState.ColorSourceBlend); memWriter.Write((byte)pass.blendState.ColorWriteChannels); memWriter.Write((byte)pass.blendState.ColorWriteChannels1); memWriter.Write((byte)pass.blendState.ColorWriteChannels2); memWriter.Write((byte)pass.blendState.ColorWriteChannels3); memWriter.Write(pass.blendState.MultiSampleMask); } else memWriter.Write(false); if (pass.depthStencilState != null) { memWriter.Write(true); memWriter.Write((byte)pass.depthStencilState.CounterClockwiseStencilDepthBufferFail); memWriter.Write((byte)pass.depthStencilState.CounterClockwiseStencilFail); memWriter.Write((byte)pass.depthStencilState.CounterClockwiseStencilFunction); memWriter.Write((byte)pass.depthStencilState.CounterClockwiseStencilPass); memWriter.Write(pass.depthStencilState.DepthBufferEnable); memWriter.Write((byte)pass.depthStencilState.DepthBufferFunction); memWriter.Write(pass.depthStencilState.DepthBufferWriteEnable); memWriter.Write(pass.depthStencilState.ReferenceStencil); memWriter.Write((byte)pass.depthStencilState.StencilDepthBufferFail); memWriter.Write(pass.depthStencilState.StencilEnable); memWriter.Write((byte)pass.depthStencilState.StencilFail); memWriter.Write((byte)pass.depthStencilState.StencilFunction); memWriter.Write(pass.depthStencilState.StencilMask); memWriter.Write((byte)pass.depthStencilState.StencilPass); memWriter.Write(pass.depthStencilState.StencilWriteMask); memWriter.Write(pass.depthStencilState.TwoSidedStencilMode); } else memWriter.Write(false); if (pass.rasterizerState != null) { memWriter.Write(true); memWriter.Write((byte)pass.rasterizerState.CullMode); memWriter.Write(pass.rasterizerState.DepthBias); memWriter.Write((byte)pass.rasterizerState.FillMode); memWriter.Write(pass.rasterizerState.MultiSampleAntiAlias); memWriter.Write(pass.rasterizerState.ScissorTestEnable); memWriter.Write(pass.rasterizerState.SlopeScaleDepthBias); } else memWriter.Write(false); } } // Calculate a hash code from memory stream // and write it to the header. var effectKey = MonoGame.Utilities.Hash.ComputeHash(memStream); writer.Write((Int32)effectKey); //write content from memory stream to final stream. memStream.WriteTo(writer.BaseStream); } }
static public ShaderInfo FromFile(string path, Options options) { var effectSource = File.ReadAllText(path); return FromString(effectSource, path, options); }
static public ShaderInfo FromString(string effectSource, string filePath, Options options) { var macros = new List<SharpDX.Direct3D.ShaderMacro>(); macros.Add(new SharpDX.Direct3D.ShaderMacro("MGFX", 1)); // Under the DX11 profile we pass a few more macros. if (options.DX11Profile) { macros.Add(new SharpDX.Direct3D.ShaderMacro("HLSL", 1)); macros.Add(new SharpDX.Direct3D.ShaderMacro("SM4", 1)); } // If we're building shaders for debug set that flag too. if (options.Debug) macros.Add(new SharpDX.Direct3D.ShaderMacro("DEBUG", 1)); // Use the D3DCompiler to pre-process the file resolving // all #includes and macros.... this even works for GLSL. string newFile; var full = Path.GetFullPath(filePath); var dir = Path.GetDirectoryName(full); using (var includer = new CompilerInclude(Path.GetDirectoryName(Path.GetFullPath(filePath)))) newFile = SharpDX.D3DCompiler.ShaderBytecode.Preprocess(effectSource, macros.ToArray(), includer, Path.GetFullPath(filePath)); // Parse the resulting file for techniques and passes. var tree = new Parser(new Scanner()).Parse(newFile, filePath); if (tree.Errors.Count > 0) { var errors = String.Empty; foreach (var error in tree.Errors) errors += string.Format("{0}({1},{2}) : {3}\r\n", error.File, error.Line, error.Column, error.Message); throw new Exception(errors); } // Evaluate the results of the parse tree. var result = tree.Eval() as ShaderInfo; result.fileName = filePath; result.fileContent = newFile; // Remove empty techniques. for (var i=0; i < result.Techniques.Count; i++) { var tech = result.Techniques[i]; if (tech.Passes.Count <= 0) { result.Techniques.RemoveAt(i); i--; } } // We must have at least one technique. if (result.Techniques.Count <= 0) throw new Exception("The effect must contain at least one technique and pass!"); // Finally remove the techniques from the file. // // TODO: Do we really need to do this, or will the HLSL // compiler just ignore it as we compile shaders? // /* var extra = 2; var offset = 0; foreach (var tech in result.Techniques) { // Remove the technique from the file. newFile = newFile.Remove(tech.startPos + offset, tech.length + extra); offset -= tech.length + extra; techniques.Add(tech); } */ result.DX11Profile = options.DX11Profile; result.Debug = options.Debug; return result; }
bool DoBuild(string _effectSource) { CreateUIParameters(_effectSource); OutputClear(); var options = new Options(); // Parse the MGFX file expanding includes, macros, and returning the techniques. ShaderInfo shaderInfo; try { options.Debug = true; options.Profile = ShaderProfile.DirectX_11; options.SourceFile = string.Empty; options.OutputFile = string.Empty; var strpathApp = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); shaderInfo = ShaderInfo.FromString(_effectSource, strpathApp, options, this); } catch (Exception ex) { OutputAppend(ex.Message); OutputAppend("Failed to parse !"); return false; } // Create the effect object. EffectObject effect; var shaderErrorsAndWarnings = string.Empty; try { effect = EffectObject.CompileEffect(shaderInfo, out shaderErrorsAndWarnings); if (!string.IsNullOrEmpty(shaderErrorsAndWarnings)) OutputAppend(shaderErrorsAndWarnings); } catch (ShaderCompilerException) { // Write the compiler errors and warnings and let the user know what happened. OutputAppend(shaderErrorsAndWarnings); OutputAppend("Failed to compile !"); return false; } catch (Exception ex) { // First write all the compiler errors and warnings. if (!string.IsNullOrEmpty(shaderErrorsAndWarnings)) OutputAppend(shaderErrorsAndWarnings); // If we have an exception message then write that. if (!string.IsNullOrEmpty(ex.Message)) OutputAppend(ex.Message); // Let the user know what happened. OutputAppend("Unexpected error compiling !"); return false; } OutputAppend("Shader successed Compiled !"); //Create Effect for Game View using (MemoryStream stream = new MemoryStream()) { BinaryWriter bw = new BinaryWriter(stream); effect.Write(bw, options); byte[] bytecode = stream.ToArray(); m_game.SetEffectBytesCode(bytecode); } return true; }
public static int Main(string[] args) { if (!Environment.Is64BitProcess && Environment.OSVersion.Platform != PlatformID.Unix) { Console.Error.WriteLine("The MonoGame content tools only work on a 64bit OS."); return -1; } var options = new Options(); var parser = new Utilities.CommandLineParser(options); parser.Title = "2MGFX - Converts Microsoft FX files to a compiled MonoGame Effect."; if (!parser.ParseCommandLine(args)) return 1; // Validate the input file exits. if (!File.Exists(options.SourceFile)) { Console.Error.WriteLine("The input file '{0}' was not found!", options.SourceFile); return 1; } // TODO: This would be where we would decide the user // is trying to convert an FX file to a MGFX glsl file. // // For now we assume we're going right to a compiled MGFXO file. // Parse the MGFX file expanding includes, macros, and returning the techniques. ShaderInfo shaderInfo; try { shaderInfo = ShaderInfo.FromFile(options.SourceFile, options, new ConsoleEffectCompilerOutput()); } catch (Exception ex) { Console.Error.WriteLine(ex.Message); Console.Error.WriteLine("Failed to parse '{0}'!", options.SourceFile); return 1; } // Create the effect object. EffectObject effect; var shaderErrorsAndWarnings = string.Empty; try { effect = EffectObject.CompileEffect(shaderInfo, out shaderErrorsAndWarnings); if (!string.IsNullOrEmpty(shaderErrorsAndWarnings)) Console.Error.WriteLine(shaderErrorsAndWarnings); } catch (ShaderCompilerException) { // Write the compiler errors and warnings and let the user know what happened. Console.Error.WriteLine(shaderErrorsAndWarnings); Console.Error.WriteLine("Failed to compile '{0}'!", options.SourceFile); return 1; } catch (Exception ex) { // First write all the compiler errors and warnings. if (!string.IsNullOrEmpty(shaderErrorsAndWarnings)) Console.Error.WriteLine(shaderErrorsAndWarnings); // If we have an exception message then write that. if (!string.IsNullOrEmpty(ex.Message)) Console.Error.WriteLine(ex.Message); // Let the user know what happened. Console.Error.WriteLine("Unexpected error compiling '{0}'!", options.SourceFile); return 1; } // Get the output file path. if (options.OutputFile == string.Empty) options.OutputFile = Path.GetFileNameWithoutExtension(options.SourceFile) + ".mgfxo"; // Write out the effect to a runtime format. try { using (var stream = new FileStream(options.OutputFile, FileMode.Create, FileAccess.Write)) using (var writer = new BinaryWriter(stream)) effect.Write(writer, options); } catch (Exception ex) { Console.Error.WriteLine(ex.Message); Console.Error.WriteLine("Failed to write '{0}'!", options.OutputFile); return 1; } // We finished succesfully. Console.WriteLine("Compiled '{0}' to '{1}'.", options.SourceFile, options.OutputFile); return 0; }
public static int Main(string[] args) { var options = new Options(); var parser = new Utilities.CommandLineParser(options); parser.Title = "2MGFX - Converts Microsoft FX files to a compiled MonoGame Effect."; if (!parser.ParseCommandLine(args)) return 1; // Validate the input file exits. if (!File.Exists(options.SourceFile)) { Console.Error.WriteLine("The input file '{0}' was not found!", options.SourceFile); return 1; } // TODO: This would be where we would decide the user // is trying to convert an FX file to a MGFX glsl file. // // For now we assume we're going right to a compiled MGFXO file. // Parse the MGFX file expanding includes, macros, and returning the techniques. ShaderInfo shaderInfo; try { shaderInfo = ShaderInfo.FromFile(options.SourceFile, options); } catch (Exception ex) { Console.Error.WriteLine(ex.Message); Console.Error.WriteLine("Failed to parse '{0}'!", options.SourceFile); return 1; } // Create the effect object. EffectObject effect; try { effect = EffectObject.FromShaderInfo(shaderInfo); } catch (Exception ex) { Console.Error.WriteLine(ex.Message); Console.Error.WriteLine("Failed to compile '{0}'!", options.SourceFile); return 1; } // Get the output file path. if (options.OutputFile == string.Empty) options.OutputFile = Path.GetFileNameWithoutExtension(options.SourceFile) + ".mgfxo"; // Write out the effect to a runtime format. try { using (var stream = new FileStream(options.OutputFile, FileMode.Create, FileAccess.Write)) using (var writer = new BinaryWriter(stream)) effect.Write(writer, options); } catch (Exception ex) { Console.Error.WriteLine(ex.Message); Console.Error.WriteLine("Failed to write '{0}'!", options.OutputFile); return 1; } // We finished succesfully. Console.WriteLine("Compiled '{0}' to '{1}'.", options.SourceFile, options.OutputFile); return 0; }
static public ShaderInfo FromString(string effectSource, string filePath, Options options) { var macros = new List<SharpDX.Direct3D.ShaderMacro>(); macros.Add(new SharpDX.Direct3D.ShaderMacro("MGFX", 1)); // Under the DX11 profile we pass a few more macros. if (options.Profile == ShaderProfile.DirectX_11) { macros.Add(new SharpDX.Direct3D.ShaderMacro("HLSL", 1)); macros.Add(new SharpDX.Direct3D.ShaderMacro("SM4", 1)); } else if (options.Profile == ShaderProfile.OpenGL) { macros.Add(new SharpDX.Direct3D.ShaderMacro("GLSL", 1)); macros.Add(new SharpDX.Direct3D.ShaderMacro("OPENGL", 1)); } else if (options.Profile == ShaderProfile.PlayStation4) { throw new NotSupportedException("PlayStation 4 support isn't available in this build."); } // If we're building shaders for debug set that flag too. if (options.Debug) macros.Add(new SharpDX.Direct3D.ShaderMacro("DEBUG", 1)); if (!string.IsNullOrEmpty(options.Defines)) { var defines = options.Defines.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); macros.AddRange(defines.Select(define => new SharpDX.Direct3D.ShaderMacro(define, 1))); } // Use the D3DCompiler to pre-process the file resolving // all #includes and macros.... this even works for GLSL. string newFile; var fullPath = Path.GetFullPath(filePath); var dependencies = new List<string>(); using (var includer = new CompilerInclude(Path.GetDirectoryName(Path.GetFullPath(filePath)), dependencies)) newFile = SharpDX.D3DCompiler.ShaderBytecode.Preprocess(effectSource, macros.ToArray(), includer, fullPath); // Parse the resulting file for techniques and passes. var tree = new Parser(new Scanner()).Parse(newFile, fullPath); if (tree.Errors.Count > 0) { var errors = String.Empty; foreach (var error in tree.Errors) errors += string.Format("{0}({1},{2}) : {3}\r\n", error.File, error.Line, error.Column, error.Message); throw new Exception(errors); } // Evaluate the results of the parse tree. var result = tree.Eval() as ShaderInfo; result.Dependencies = dependencies; result.FilePath = fullPath; result.FileContent = newFile; if (!string.IsNullOrEmpty(options.OutputFile)) result.OutputFilePath = Path.GetFullPath(options.OutputFile); result.AdditionalOutputFiles = new List<string>(); // Remove empty techniques. for (var i=0; i < result.Techniques.Count; i++) { var tech = result.Techniques[i]; if (tech.Passes.Count <= 0) { result.Techniques.RemoveAt(i); i--; } } // We must have at least one technique. if (result.Techniques.Count <= 0) throw new Exception("The effect must contain at least one technique and pass!"); // Finally remove the techniques from the file. // // TODO: Do we really need to do this, or will the HLSL // compiler just ignore it as we compile shaders? // /* var extra = 2; var offset = 0; foreach (var tech in result.Techniques) { // Remove the technique from the file. newFile = newFile.Remove(tech.startPos + offset, tech.length + extra); offset -= tech.length + extra; techniques.Add(tech); } */ result.Profile = options.Profile; result.Debug = options.Debug; return result; }
static public ShaderInfo FromFile(string path, Options options, IEffectCompilerOutput output) { var effectSource = File.ReadAllText(path); return FromString(effectSource, path, options, output); }