diff --git a/Build/Projects/2MGFX.definition b/Build/Projects/2MGFX.definition index d2348c38da7..aeb7753421a 100644 --- a/Build/Projects/2MGFX.definition +++ b/Build/Projects/2MGFX.definition @@ -160,7 +160,15 @@ MonoGame.Framework\Utilities\Hash.cs - + + + + MonoGame.Framework.Content.Pipeline\ExternalTool.cs + + + MonoGame.Framework.Content.Pipeline\LoadedTypeCollection.c + + @@ -184,6 +192,8 @@ + + @@ -200,5 +210,6 @@ + diff --git a/Build/Projects/MonoGame.Framework.Content.Pipeline.definition b/Build/Projects/MonoGame.Framework.Content.Pipeline.definition index 07bd7682faa..db732479bb9 100644 --- a/Build/Projects/MonoGame.Framework.Content.Pipeline.definition +++ b/Build/Projects/MonoGame.Framework.Content.Pipeline.definition @@ -280,6 +280,14 @@ Windows Processors\MGFX\ShaderProfile.cs + + Windows + Processors\MGFX\ShaderProfile.OpenGL.cs + + + Windows + Processors\MGFX\ShaderProfile.DirectX.cs + Windows Processors\MGFX\TechniqueInfo.cs @@ -419,6 +427,7 @@ + diff --git a/MonoGame.Framework.Content.Pipeline/ExternalTool.cs b/MonoGame.Framework.Content.Pipeline/ExternalTool.cs index d9ba2015e6e..7ac355df334 100644 --- a/MonoGame.Framework.Content.Pipeline/ExternalTool.cs +++ b/MonoGame.Framework.Content.Pipeline/ExternalTool.cs @@ -139,5 +139,20 @@ private static string FindCommand(string command) return null; } + + /// + /// Safely deletes the file if it exists. + /// + /// The path to the file to delete. + public static void DeleteFile(string filePath) + { + try + { + File.Delete(filePath); + } + catch (Exception) + { + } + } } } diff --git a/MonoGame.Framework.Content.Pipeline/LoadedTypeCollection.cs b/MonoGame.Framework.Content.Pipeline/LoadedTypeCollection.cs new file mode 100644 index 00000000000..ef3009a1b26 --- /dev/null +++ b/MonoGame.Framework.Content.Pipeline/LoadedTypeCollection.cs @@ -0,0 +1,73 @@ +// MonoGame - Copyright (C) The MonoGame Team +// This file is subject to the terms and conditions defined in +// file 'LICENSE.txt', which is part of this source code package. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + + +namespace Microsoft.Xna.Framework.Content.Pipeline +{ + /// + /// A helper for collecting instances of a particular type + /// by scanning the types in loaded assemblies. + /// + public class LoadedTypeCollection : IEnumerable + { + private static List _all; + + public LoadedTypeCollection() + { + // Hook into assembly loading events to gather any new + // enumeration types that are found. + AppDomain.CurrentDomain.AssemblyLoad += (sender, args) => ScanAssembly(args.LoadedAssembly); + } + + private static void ScanAssembly(Assembly ass) + { + // Initialize the list on first use. + if (_all == null) + _all = new List(24); + + var thisAss = typeof(T).Assembly; + + // If the assembly doesn't reference our assembly then it + // cannot contain this type... so skip scanning it. + var refAss = ass.GetReferencedAssemblies(); + if (thisAss.FullName != ass.FullName && refAss.All(r => r.FullName != thisAss.FullName)) + return; + + var definedTypes = ass.DefinedTypes; + + foreach (var type in definedTypes) + { + if (!type.IsSubclassOf(typeof(T)) || type.IsAbstract) + continue; + + // Create an instance of the type and add it to our list. + var ttype = (T)Activator.CreateInstance(type); + _all.Add(ttype); + } + } + + public IEnumerator GetEnumerator() + { + if (_all == null) + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (var ass in assemblies) + ScanAssembly(ass); + } + + return _all.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/MonoGame.Framework.Content.Pipeline/Processors/EffectProcessor.cs b/MonoGame.Framework.Content.Pipeline/Processors/EffectProcessor.cs index 695c5594ead..088b934d882 100644 --- a/MonoGame.Framework.Content.Pipeline/Processors/EffectProcessor.cs +++ b/MonoGame.Framework.Content.Pipeline/Processors/EffectProcessor.cs @@ -54,23 +54,9 @@ public override CompiledEffectContent Process(EffectContent input, ContentProces var options = new Options(); options.SourceFile = input.Identity.SourceFilename; - switch (context.TargetPlatform) - { - case TargetPlatform.Windows: - case TargetPlatform.WindowsPhone8: - case TargetPlatform.WindowsStoreApp: - options.Profile = ShaderProfile.DirectX_11; - break; - case TargetPlatform.iOS: - case TargetPlatform.Android: - case TargetPlatform.DesktopGL: - case TargetPlatform.MacOSX: - case TargetPlatform.RaspberryPi: - options.Profile = ShaderProfile.OpenGL; - break; - default: - throw new InvalidContentException(string.Format("{0} effects are not supported.", context.TargetPlatform), input.Identity); - } + options.Profile = ShaderProfile.ForPlatform(context.TargetPlatform.ToString()); + if (options.Profile == null) + throw new InvalidContentException(string.Format("{0} effects are not supported.", context.TargetPlatform), input.Identity); options.Debug = DebugMode == EffectProcessorDebugMode.Debug; options.Defines = Defines; diff --git a/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/ContentWriter.cs b/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/ContentWriter.cs index 678e12518a8..c69cd1d6472 100644 --- a/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/ContentWriter.cs +++ b/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/ContentWriter.cs @@ -54,6 +54,8 @@ public sealed class ContentWriter : BinaryWriter 'M', // WindowsPhone8 'r', // RaspberryPi 'P', // PlayStation4 + 'v', // PSVita + 'O', // XboxOne }; /// diff --git a/MonoGame.Framework.Content.Pipeline/TargetPlatform.cs b/MonoGame.Framework.Content.Pipeline/TargetPlatform.cs index 60bfd09df4b..36ab710b273 100644 --- a/MonoGame.Framework.Content.Pipeline/TargetPlatform.cs +++ b/MonoGame.Framework.Content.Pipeline/TargetPlatform.cs @@ -90,6 +90,16 @@ public enum TargetPlatform /// Sony PlayStation4 /// PlayStation4, + + /// + /// PlayStation Vita + /// + PSVita, + + /// + /// Xbox One + /// + XboxOne, } diff --git a/MonoGame.Framework/Content/ContentManager.cs b/MonoGame.Framework/Content/ContentManager.cs index fe7b0d8aa41..541f88bc9d1 100644 --- a/MonoGame.Framework/Content/ContentManager.cs +++ b/MonoGame.Framework/Content/ContentManager.cs @@ -47,6 +47,8 @@ public partial class ContentManager : IDisposable 'M', // WindowsPhone8 'r', // RaspberryPi 'P', // PlayStation4 + 'v', // PSVita + 'O', // XboxOne // NOTE: There are additional idenfiers for consoles that // are not defined in this repository. Be sure to ask the diff --git a/Tools/2MGFX/CommandLineParser.cs b/Tools/2MGFX/CommandLineParser.cs index 11d8b144817..30e82b22bfe 100644 --- a/Tools/2MGFX/CommandLineParser.cs +++ b/Tools/2MGFX/CommandLineParser.cs @@ -12,7 +12,7 @@ using System.ComponentModel; -namespace Utilities +namespace TwoMGFX { // Reusable, reflection based helper for parsing commandline options. // @@ -248,7 +248,7 @@ public sealed class RequiredAttribute : Attribute // Used on an optionsObject field to rename the corresponding commandline option. [AttributeUsage(AttributeTargets.Field)] - public sealed class NameAttribute : Attribute + public class NameAttribute : Attribute { public NameAttribute(string name) { @@ -263,7 +263,18 @@ public NameAttribute(string name, string description) } public string Name { get; private set; } - public string Description { get; private set; } + public string Description { get; protected set; } + } + + [AttributeUsage(AttributeTargets.Field)] + public sealed class ProfileNameAttribute : NameAttribute + { + public ProfileNameAttribute() + : base("Profile") + { + var names = ShaderProfile.All.Select(p => p.Name); + Description = "\t - Must be one of the following: " + string.Join(", ", names); + } } } } diff --git a/Tools/2MGFX/EffectObject.cs b/Tools/2MGFX/EffectObject.cs index 00612e62f15..02cb2b347ea 100644 --- a/Tools/2MGFX/EffectObject.cs +++ b/Tools/2MGFX/EffectObject.cs @@ -683,7 +683,7 @@ static public EffectObject CompileEffect(ShaderInfo shaderInfo, out string error pass.state_count = 0; var tempstate = new d3dx_state[2]; - pinfo.ValidateShaderModels(shaderInfo.Profile); + shaderInfo.Profile.ValidateShaderModels(pinfo); if (!string.IsNullOrEmpty(pinfo.psFunction)) { @@ -794,44 +794,8 @@ static public EffectObject CompileEffect(ShaderInfo shaderInfo, out string error private d3dx_state CreateShader(ShaderInfo shaderInfo, string shaderFunction, string shaderProfile, bool isVertexShader, ref string errorsAndWarnings) { - // Compile the shader. - byte[] bytecode; - if (shaderInfo.Profile == ShaderProfile.DirectX_11 || shaderInfo.Profile == ShaderProfile.OpenGL) - { - // For now GLSL is only supported via translation - // using MojoShader which works from HLSL bytecode. - bytecode = CompileHLSL(shaderInfo, shaderFunction, shaderProfile, ref errorsAndWarnings); - } - else if (shaderInfo.Profile == ShaderProfile.PlayStation4) - bytecode = CompilePSSL(shaderInfo, shaderFunction, shaderProfile, ref errorsAndWarnings); - else - throw new NotSupportedException("Unknown shader profile!"); - - // First look to see if we already created this same shader. - ShaderData shaderData = null; - foreach (var shader in Shaders) - { - if (bytecode.SequenceEqual(shader.Bytecode)) - { - shaderData = shader; - break; - } - } - - // Create a new shader. - if (shaderData == null) - { - if (shaderInfo.Profile == ShaderProfile.DirectX_11) - shaderData = ShaderData.CreateHLSL(bytecode, isVertexShader, ConstantBuffers, Shaders.Count, shaderInfo.SamplerStates, shaderInfo.Debug); - else if (shaderInfo.Profile == ShaderProfile.OpenGL) - shaderData = ShaderData.CreateGLSL(bytecode, isVertexShader, ConstantBuffers, Shaders.Count, shaderInfo.SamplerStates, shaderInfo.Debug); - else if (shaderInfo.Profile == ShaderProfile.PlayStation4) - shaderData = ShaderData.CreatePSSL(bytecode, isVertexShader, ConstantBuffers, Shaders.Count, shaderInfo.SamplerStates, shaderInfo.Debug); - else - throw new NotSupportedException("Unknown shader profile!"); - - Shaders.Add(shaderData); - } + // Compile and create the shader. + var shaderData = shaderInfo.Profile.CreateShader(shaderInfo, shaderFunction, shaderProfile, isVertexShader, this, ref errorsAndWarnings); var state = new d3dx_state(); state.index = 0; diff --git a/Tools/2MGFX/EffectObject.hlsl.cs b/Tools/2MGFX/EffectObject.hlsl.cs index 0afb2a55587..8eb3fb20de1 100644 --- a/Tools/2MGFX/EffectObject.hlsl.cs +++ b/Tools/2MGFX/EffectObject.hlsl.cs @@ -5,7 +5,7 @@ namespace TwoMGFX { partial class EffectObject { - private static byte[] CompileHLSL(ShaderInfo shaderInfo, string shaderFunction, string shaderProfile, ref string errorsAndWarnings) + public static byte[] CompileHLSL(ShaderInfo shaderInfo, string shaderFunction, string shaderProfile, ref string errorsAndWarnings) { SharpDX.D3DCompiler.ShaderBytecode shaderByteCode; try diff --git a/Tools/2MGFX/EffectObject.writer.cs b/Tools/2MGFX/EffectObject.writer.cs index 330088b0856..7463fbb3a60 100644 --- a/Tools/2MGFX/EffectObject.writer.cs +++ b/Tools/2MGFX/EffectObject.writer.cs @@ -25,7 +25,7 @@ public void Write(BinaryWriter writer, Options options) // Write an simple identifier for DX11 vs GLSL // so we can easily detect the correct shader type. - var profile = (byte)options.Profile; + var profile = (byte)options.Profile.FormatId; writer.Write(profile); // Write the rest to a memory stream. diff --git a/Tools/2MGFX/Options.cs b/Tools/2MGFX/Options.cs index 60695ca3167..59507a6f3a9 100644 --- a/Tools/2MGFX/Options.cs +++ b/Tools/2MGFX/Options.cs @@ -1,20 +1,24 @@ -namespace TwoMGFX +// MonoGame - Copyright (C) The MonoGame Team +// This file is subject to the terms and conditions defined in +// file 'LICENSE.txt', which is part of this source code package. + +namespace TwoMGFX { public class Options { - [Utilities.CommandLineParser.Required] + [CommandLineParser.Required] public string SourceFile; - [Utilities.CommandLineParser.Required] + [CommandLineParser.Required] public string OutputFile = string.Empty; - [Utilities.CommandLineParser.Name("Profile", "\t - Must be either DirectX_11, OpenGL, or PlayStation4")] + [CommandLineParser.ProfileName] public ShaderProfile Profile = ShaderProfile.OpenGL; - [Utilities.CommandLineParser.Name("DEBUG")] + [CommandLineParser.Name("Debug", "\t\t - Include extra debug information in the compiled effect.")] public bool Debug; - [Utilities.CommandLineParser.Name("Defines", "\t - Semicolon-delimited define assignments")] + [CommandLineParser.Name("Defines", "\t - Semicolon-delimited define assignments")] public string Defines; } } diff --git a/Tools/2MGFX/PassInfo.cs b/Tools/2MGFX/PassInfo.cs index 645b2d03085..228ac2529d1 100644 --- a/Tools/2MGFX/PassInfo.cs +++ b/Tools/2MGFX/PassInfo.cs @@ -19,70 +19,6 @@ public class PassInfo public RasterizerState rasterizerState; public DepthStencilState depthStencilState; - private static readonly Regex _hlslPixelShaderRegex = new Regex(@"^ps_(?1|2|3|4|5)_(?0|1|)(_level_(9_1|9_2|9_3))?$", RegexOptions.Compiled); - private static readonly Regex _hlslVertexShaderRegex = new Regex(@"^vs_(?1|2|3|4|5)_(?0|1|)(_level_(9_1|9_2|9_3))?$", RegexOptions.Compiled); - - private static readonly Regex _glslPixelShaderRegex = new Regex(@"^ps_(?1|2|3|4|5)_(?0|1|)$", RegexOptions.Compiled); - private static readonly Regex _glslVertexShaderRegex = new Regex(@"^vs_(?1|2|3|4|5)_(?0|1|)$", RegexOptions.Compiled); - - - public static void ParseShaderModel(string text, Regex regex, out int major, out int minor) - { - var match = regex.Match(text); - if (!match.Success) - { - major = 0; - minor = 0; - return; - } - - major = int.Parse(match.Groups["major"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture); - minor = int.Parse(match.Groups["minor"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture); - } - - public void ValidateShaderModels(ShaderProfile profile) - { - int major, minor; - - if (!string.IsNullOrEmpty(vsFunction)) - { - switch (profile) - { - case ShaderProfile.DirectX_11: - ParseShaderModel(vsModel, _hlslVertexShaderRegex, out major, out minor); - if (major <= 3) - throw new Exception(String.Format("Invalid profile '{0}'. Vertex shader '{1}' must be SM 4.0 level 9.1 or higher!", vsModel, vsFunction)); - break; - case ShaderProfile.OpenGL: - ParseShaderModel(vsModel, _glslVertexShaderRegex, out major, out minor); - if (major > 3) - throw new Exception(String.Format("Invalid profile '{0}'. Vertex shader '{1}' must be SM 3.0 or lower!", vsModel, vsFunction)); - break; - case ShaderProfile.PlayStation4: - throw new NotSupportedException("PlayStation 4 support isn't available in this build."); - } - } - - if (!string.IsNullOrEmpty(psFunction)) - { - switch (profile) - { - case ShaderProfile.DirectX_11: - ParseShaderModel(psModel, _hlslPixelShaderRegex, out major, out minor); - if (major <= 3) - throw new Exception(String.Format("Invalid profile '{0}'. Pixel shader '{1}' must be SM 4.0 level 9.1 or higher!", vsModel, psFunction)); - break; - case ShaderProfile.OpenGL: - ParseShaderModel(psModel, _glslPixelShaderRegex, out major, out minor); - if (major > 3) - throw new Exception(String.Format("Invalid profile '{0}'. Pixel shader '{1}' must be SM 3.0 or lower!", vsModel, psFunction)); - break; - case ShaderProfile.PlayStation4: - throw new NotSupportedException("PlayStation 4 support isn't available in this build."); - } - } - } - private static Blend ToAlphaBlend(Blend blend) { switch (blend) diff --git a/Tools/2MGFX/Program.cs b/Tools/2MGFX/Program.cs index ca7ac4cd98e..9bcfdfd9350 100644 --- a/Tools/2MGFX/Program.cs +++ b/Tools/2MGFX/Program.cs @@ -1,4 +1,8 @@ -using System; +// MonoGame - Copyright (C) The MonoGame Team +// This file is subject to the terms and conditions defined in +// file 'LICENSE.txt', which is part of this source code package. + +using System; using System.IO; namespace TwoMGFX @@ -14,8 +18,8 @@ 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."; + var parser = new CommandLineParser(options); + parser.Title = "2MGFX - The MonoGame Effect compiler."; if (!parser.ParseCommandLine(args)) return 1; diff --git a/Tools/2MGFX/ShaderData.cs b/Tools/2MGFX/ShaderData.cs index 8a973b6eda9..ae778f4cf64 100644 --- a/Tools/2MGFX/ShaderData.cs +++ b/Tools/2MGFX/ShaderData.cs @@ -4,7 +4,14 @@ namespace TwoMGFX { internal partial class ShaderData { - public bool IsVertexShader; + public ShaderData(bool isVertexShader, int sharedIndex, byte[] bytecode) + { + IsVertexShader = isVertexShader; + SharedIndex = sharedIndex; + Bytecode = (byte[])bytecode.Clone(); + } + + public bool IsVertexShader { get; private set; } public struct Sampler { @@ -35,7 +42,7 @@ public struct Attribute public Attribute[] _attributes; - public byte[] ShaderCode { get; private set; } + public byte[] ShaderCode { get; set; } #region Non-Serialized Stuff diff --git a/Tools/2MGFX/ShaderData.mojo.cs b/Tools/2MGFX/ShaderData.mojo.cs index 62ee069b364..019e36a7c32 100644 --- a/Tools/2MGFX/ShaderData.mojo.cs +++ b/Tools/2MGFX/ShaderData.mojo.cs @@ -9,9 +9,7 @@ internal partial class ShaderData { public static ShaderData CreateGLSL(byte[] byteCode, bool isVertexShader, List cbuffers, int sharedIndex, Dictionary samplerStates, bool debug) { - var dxshader = new ShaderData (); - dxshader.SharedIndex = sharedIndex; - dxshader.Bytecode = (byte[])byteCode.Clone (); + var dxshader = new ShaderData(isVertexShader, sharedIndex, byteCode); // Use MojoShader to convert the HLSL bytecode to GLSL. @@ -36,18 +34,6 @@ public static ShaderData CreateGLSL(byte[] byteCode, bool isVertexShader, List cbuffers, int sharedIndex, Dictionary samplerStates, bool debug) { - var dxshader = new ShaderData(); - dxshader.IsVertexShader = isVertexShader; - dxshader.SharedIndex = sharedIndex; - dxshader.Bytecode = (byte[])byteCode.Clone(); + var dxshader = new ShaderData(isVertexShader, sharedIndex, byteCode); dxshader._attributes = new Attribute[0]; // Strip the bytecode we're gonna save! diff --git a/Tools/2MGFX/ShaderInfo.cs b/Tools/2MGFX/ShaderInfo.cs index b71d6535402..19236e7d48e 100644 --- a/Tools/2MGFX/ShaderInfo.cs +++ b/Tools/2MGFX/ShaderInfo.cs @@ -1,4 +1,8 @@ -using System; +// MonoGame - Copyright (C) The MonoGame Team +// This file is subject to the terms and conditions defined in +// file 'LICENSE.txt', which is part of this source code package. + +using System; using System.Collections.Generic; using System.IO; @@ -17,6 +21,7 @@ public class ShaderInfo public bool Debug { get; private set; } public List Techniques = new List(); + public Dictionary SamplerStates = new Dictionary(); public List Dependencies { get; private set; } @@ -34,21 +39,7 @@ static public ShaderInfo FromString(string effectSource, string filePath, Option var macros = new Dictionary(); macros.Add("MGFX", "1"); - // Under the DX11 profile we pass a few more macros. - if (options.Profile == ShaderProfile.DirectX_11) - { - macros.Add("HLSL", "1"); - macros.Add("SM4", "1"); - } - else if (options.Profile == ShaderProfile.OpenGL) - { - macros.Add("GLSL", "1"); - macros.Add("OPENGL", "1"); - } - else if (options.Profile == ShaderProfile.PlayStation4) - { - throw new NotSupportedException("PlayStation 4 support isn't available in this build."); - } + options.Profile.AddMacros(macros); // If we're building shaders for debug set that flag too. if (options.Debug) diff --git a/Tools/2MGFX/ShaderProfile.DirectX.cs b/Tools/2MGFX/ShaderProfile.DirectX.cs new file mode 100644 index 00000000000..978801a503a --- /dev/null +++ b/Tools/2MGFX/ShaderProfile.DirectX.cs @@ -0,0 +1,73 @@ +// MonoGame - Copyright (C) The MonoGame Team +// This file is subject to the terms and conditions defined in +// file 'LICENSE.txt', which is part of this source code package. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace TwoMGFX +{ + class DirectX11ShaderProfile : ShaderProfile + { + private static readonly Regex HlslPixelShaderRegex = new Regex(@"^ps_(?1|2|3|4|5)_(?0|1|)(_level_(9_1|9_2|9_3))?$", RegexOptions.Compiled); + private static readonly Regex HlslVertexShaderRegex = new Regex(@"^vs_(?1|2|3|4|5)_(?0|1|)(_level_(9_1|9_2|9_3))?$", RegexOptions.Compiled); + + public DirectX11ShaderProfile() + : base("DirectX_11", 1) + { + } + + internal override void AddMacros(Dictionary macros) + { + macros.Add("HLSL", "1"); + macros.Add("SM4", "1"); + } + + internal override void ValidateShaderModels(PassInfo pass) + { + int major, minor; + + if (!string.IsNullOrEmpty(pass.vsFunction)) + { + ParseShaderModel(pass.vsModel, HlslVertexShaderRegex, out major, out minor); + if (major <= 3) + throw new Exception(String.Format("Invalid profile '{0}'. Vertex shader '{1}' must be SM 4.0 level 9.1 or higher!", pass.vsModel, pass.vsFunction)); + } + + if (!string.IsNullOrEmpty(pass.psFunction)) + { + ParseShaderModel(pass.psModel, HlslPixelShaderRegex, out major, out minor); + if (major <= 3) + throw new Exception(String.Format("Invalid profile '{0}'. Pixel shader '{1}' must be SM 4.0 level 9.1 or higher!", pass.vsModel, pass.psFunction)); + } + } + + internal override ShaderData CreateShader(ShaderInfo shaderInfo, string shaderFunction, string shaderProfile, bool isVertexShader, EffectObject effect, ref string errorsAndWarnings) + { + var bytecode = EffectObject.CompileHLSL(shaderInfo, shaderFunction, shaderProfile, ref errorsAndWarnings); + + // First look to see if we already created this same shader. + foreach (var shader in effect.Shaders) + { + if (bytecode.SequenceEqual(shader.Bytecode)) + return shader; + } + + var shaderData = ShaderData.CreateHLSL(bytecode, isVertexShader, effect.ConstantBuffers, effect.Shaders.Count, shaderInfo.SamplerStates, shaderInfo.Debug); + effect.Shaders.Add(shaderData); + return shaderData; + } + + internal override bool Supports(string platform) + { + if (platform == "Windows" || + platform == "WindowsPhone8" || + platform == "WindowsStoreApp") + return true; + + return false; + } + } +} \ No newline at end of file diff --git a/Tools/2MGFX/ShaderProfile.OpenGL.cs b/Tools/2MGFX/ShaderProfile.OpenGL.cs new file mode 100644 index 00000000000..a3c723e825a --- /dev/null +++ b/Tools/2MGFX/ShaderProfile.OpenGL.cs @@ -0,0 +1,78 @@ +// MonoGame - Copyright (C) The MonoGame Team +// This file is subject to the terms and conditions defined in +// file 'LICENSE.txt', which is part of this source code package. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace TwoMGFX +{ + class OpenGLShaderProfile : ShaderProfile + { + private static readonly Regex GlslPixelShaderRegex = new Regex(@"^ps_(?1|2|3|4|5)_(?0|1|)$", RegexOptions.Compiled); + private static readonly Regex GlslVertexShaderRegex = new Regex(@"^vs_(?1|2|3|4|5)_(?0|1|)$", RegexOptions.Compiled); + + public OpenGLShaderProfile() + : base("OpenGL", 0) + { + } + + internal override void AddMacros(Dictionary macros) + { + macros.Add("GLSL", "1"); + macros.Add("OPENGL", "1"); + } + + internal override void ValidateShaderModels(PassInfo pass) + { + int major, minor; + + if (!string.IsNullOrEmpty(pass.vsFunction)) + { + ParseShaderModel(pass.vsModel, GlslVertexShaderRegex, out major, out minor); + if (major > 3) + throw new Exception(String.Format("Invalid profile '{0}'. Vertex shader '{1}' must be SM 3.0 or lower!", pass.vsModel, pass.vsFunction)); + } + + if (!string.IsNullOrEmpty(pass.psFunction)) + { + ParseShaderModel(pass.psModel, GlslPixelShaderRegex, out major, out minor); + if (major > 3) + throw new Exception(String.Format("Invalid profile '{0}'. Pixel shader '{1}' must be SM 3.0 or lower!", pass.vsModel, pass.psFunction)); + } + } + + internal override ShaderData CreateShader(ShaderInfo shaderInfo, string shaderFunction, string shaderProfile, bool isVertexShader, EffectObject effect, ref string errorsAndWarnings) + { + // For now GLSL is only supported via translation + // using MojoShader which works from HLSL bytecode. + var bytecode = EffectObject.CompileHLSL(shaderInfo, shaderFunction, shaderProfile, ref errorsAndWarnings); + + // First look to see if we already created this same shader. + foreach (var shader in effect.Shaders) + { + if (bytecode.SequenceEqual(shader.Bytecode)) + return shader; + } + + var shaderData = ShaderData.CreateGLSL(bytecode, isVertexShader, effect.ConstantBuffers, effect.Shaders.Count, shaderInfo.SamplerStates, shaderInfo.Debug); + effect.Shaders.Add(shaderData); + + return shaderData; + } + + internal override bool Supports(string platform) + { + if (platform == "iOS" || + platform == "Android" || + platform == "DesktopGL" || + platform == "MacOSX" || + platform == "RaspberryPi") + return true; + + return false; + } + } +} \ No newline at end of file diff --git a/Tools/2MGFX/ShaderProfile.cs b/Tools/2MGFX/ShaderProfile.cs index fbd9270f091..8c7bb595a21 100644 --- a/Tools/2MGFX/ShaderProfile.cs +++ b/Tools/2MGFX/ShaderProfile.cs @@ -1,11 +1,106 @@ -namespace TwoMGFX +// MonoGame - Copyright (C) The MonoGame Team +// This file is subject to the terms and conditions defined in +// file 'LICENSE.txt', which is part of this source code package. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.Xna.Framework.Content.Pipeline; + +namespace TwoMGFX { - public enum ShaderProfile + [TypeConverter(typeof(StringConverter))] + public abstract class ShaderProfile { - // NOTE: This order matters and is used as part - // of the file format... don't change it. - OpenGL = 0, - DirectX_11 = 1, - PlayStation4 = 2, + private static readonly LoadedTypeCollection _profiles = new LoadedTypeCollection(); + + protected ShaderProfile(string name, byte formatId) + { + Name = name; + FormatId = formatId; + } + + public static readonly ShaderProfile OpenGL = FromName("OpenGL"); + + public static readonly ShaderProfile DirectX_11 = FromName("DirectX_11"); + + /// + /// Returns all the loaded shader profiles. + /// + public static IEnumerable All + { + get { return _profiles; } + } + + /// + /// Returns the name of the shader profile. + /// + public string Name { get; private set; } + + /// + /// Returns the format identifier used in the MGFX file format. + /// + public byte FormatId { get; private set; } + + /// + /// Returns the correct profile for the named platform or + /// null if no supporting profile is found. + /// + public static ShaderProfile ForPlatform(string platform) + { + return _profiles.FirstOrDefault(p => p.Supports(platform)); + } + + /// + /// Returns the profile by name or null if no match is found. + /// + public static ShaderProfile FromName(string name) + { + return _profiles.FirstOrDefault(p => p.Name == name); + } + + internal abstract void AddMacros(Dictionary macros); + + internal abstract void ValidateShaderModels(PassInfo pass); + + internal abstract ShaderData CreateShader(ShaderInfo shaderInfo, string shaderFunction, string shaderProfile, bool isVertexShader, EffectObject effect, ref string errorsAndWarnings); + + internal abstract bool Supports(string platform); + + protected static void ParseShaderModel(string text, Regex regex, out int major, out int minor) + { + var match = regex.Match(text); + if (!match.Success) + { + major = 0; + minor = 0; + return; + } + + major = int.Parse(match.Groups["major"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture); + minor = int.Parse(match.Groups["minor"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture); + } + + private class StringConverter : TypeConverter + { + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string) + { + var name = value as string; + + foreach (var e in All) + { + if (e.Name == name) + return e; + } + } + + return base.ConvertFrom(context, culture, value); + } + } } } \ No newline at end of file