Skip to content

Commit

Permalink
Refactor ShaderProfile to allow for pipeline extensibility (MonoGame#…
Browse files Browse the repository at this point in the history
…4992)

* Refactored the ShaderProfile enum into extensible class.

* Added Enumeration<T> for easier creation of enumeration types.
Refactored shader creation into a single function call.

* Helper for deleting files without exceptions.

* Missed in previous commit.

* Added XB1 and Vita to platforms.

* Added new LoadedTypeCollection<T> helper.
Refactor ShaderProfile to not try to mimic an Enum.
  • Loading branch information
tomspilman authored and harry-cpp committed Jul 23, 2016
1 parent c21c33d commit 65a4a74
Show file tree
Hide file tree
Showing 22 changed files with 433 additions and 179 deletions.
13 changes: 12 additions & 1 deletion Build/Projects/2MGFX.definition
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,15 @@
<Compile Include="..\..\MonoGame.Framework\Utilities\Hash.cs">
<Link>MonoGame.Framework\Utilities\Hash.cs</Link>
</Compile>


<!-- We also use a few types from the content pipeline. -->
<Compile Include="..\..\MonoGame.Framework.Content.Pipeline\ExternalTool.cs">
<Link>MonoGame.Framework.Content.Pipeline\ExternalTool.cs</Link>
</Compile>
<Compile Include="..\..\MonoGame.Framework.Content.Pipeline\LoadedTypeCollection.cs">
<Link>MonoGame.Framework.Content.Pipeline\LoadedTypeCollection.c</Link>
</Compile>

<!-- A fake GraphicsDevice to satisfy some of the framework classes above! -->
<Compile Include="MonoGame.Framework\GraphicsDevice.cs" />

Expand All @@ -184,6 +192,8 @@
<Compile Include="SamplerStateInfo.cs" />
<Compile Include="ShaderInfo.cs" />
<Compile Include="ShaderProfile.cs" />
<Compile Include="ShaderProfile.OpenGL.cs" />
<Compile Include="ShaderProfile.DirectX.cs" />
<Compile Include="TechniqueInfo.cs" />
<Compile Include="TextureFilterType.cs" />

Expand All @@ -200,5 +210,6 @@

<!-- The MGFX preprocessor -->
<Compile Include="Preprocessor.cs" />

</Files>
</Project>
9 changes: 9 additions & 0 deletions Build/Projects/MonoGame.Framework.Content.Pipeline.definition
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,14 @@
<Platforms>Windows</Platforms>
<Link>Processors\MGFX\ShaderProfile.cs</Link>
</Compile>
<Compile Include="..\Tools\2MGFX\ShaderProfile.OpenGL.cs">
<Platforms>Windows</Platforms>
<Link>Processors\MGFX\ShaderProfile.OpenGL.cs</Link>
</Compile>
<Compile Include="..\Tools\2MGFX\ShaderProfile.DirectX.cs">
<Platforms>Windows</Platforms>
<Link>Processors\MGFX\ShaderProfile.DirectX.cs</Link>
</Compile>
<Compile Include="..\Tools\2MGFX\TechniqueInfo.cs">
<Platforms>Windows</Platforms>
<Link>Processors\MGFX\TechniqueInfo.cs</Link>
Expand Down Expand Up @@ -419,6 +427,7 @@
<Compile Include="IContentImporter.cs" />
<Compile Include="IContentProcessor.cs" />
<Compile Include="InvalidContentException.cs" />
<Compile Include="LoadedTypeCollection.cs" />
<Compile Include="Mp3Importer.cs" />
<Compile Include="NamedValueDictionary.cs" />
<Compile Include="OggImporter.cs" />
Expand Down
15 changes: 15 additions & 0 deletions MonoGame.Framework.Content.Pipeline/ExternalTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,20 @@ private static string FindCommand(string command)

return null;
}

/// <summary>
/// Safely deletes the file if it exists.
/// </summary>
/// <param name="filePath">The path to the file to delete.</param>
public static void DeleteFile(string filePath)
{
try
{
File.Delete(filePath);
}
catch (Exception)
{
}
}
}
}
73 changes: 73 additions & 0 deletions MonoGame.Framework.Content.Pipeline/LoadedTypeCollection.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// A helper for collecting instances of a particular type
/// by scanning the types in loaded assemblies.
/// </summary>
public class LoadedTypeCollection<T> : IEnumerable<T>
{
private static List<T> _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<T>(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<T> GetEnumerator()
{
if (_all == null)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var ass in assemblies)
ScanAssembly(ass);
}

return _all.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
20 changes: 3 additions & 17 deletions MonoGame.Framework.Content.Pipeline/Processors/EffectProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public sealed class ContentWriter : BinaryWriter
'M', // WindowsPhone8
'r', // RaspberryPi
'P', // PlayStation4
'v', // PSVita
'O', // XboxOne
};

/// <summary>
Expand Down
10 changes: 10 additions & 0 deletions MonoGame.Framework.Content.Pipeline/TargetPlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ public enum TargetPlatform
/// Sony PlayStation4
/// </summary>
PlayStation4,

/// <summary>
/// PlayStation Vita
/// </summary>
PSVita,

/// <summary>
/// Xbox One
/// </summary>
XboxOne,
}


Expand Down
2 changes: 2 additions & 0 deletions MonoGame.Framework/Content/ContentManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 14 additions & 3 deletions Tools/2MGFX/CommandLineParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
using System.ComponentModel;


namespace Utilities
namespace TwoMGFX
{
// Reusable, reflection based helper for parsing commandline options.
//
Expand Down Expand Up @@ -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)
{
Expand All @@ -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);
}
}
}
}
42 changes: 3 additions & 39 deletions Tools/2MGFX/EffectObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
{
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion Tools/2MGFX/EffectObject.hlsl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Tools/2MGFX/EffectObject.writer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
16 changes: 10 additions & 6 deletions Tools/2MGFX/Options.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

0 comments on commit 65a4a74

Please sign in to comment.