using System; using System.IO; using System.Linq; using SharpDX.Direct3D9; namespace Blacklight.SharpDX9.ShaderBuilder { /// /// Compiles a D3D9 effect file to the effect binary the device loads, at build time, so no launch pays for it. /// public static class Program { /// /// Resolves an #include by looking in each directory in turn. /// public class DirectoryIncludeHandler : SharpDX.D3DCompiler.Include { private readonly string[] directories; public DirectoryIncludeHandler(params string[] directories) { this.directories = directories; } public IDisposable Shadow { get; set; } public Stream Open(SharpDX.D3DCompiler.IncludeType type, string fileName, Stream parentStream) { foreach (string directory in directories) { string path = Path.Combine(directory, fileName); if (File.Exists(path)) { return File.OpenRead(path); } } throw new FileNotFoundException( $"The shader includes {fileName}, which isn't in any of: {string.Join(", ", directories)}."); } public void Close(Stream stream) { stream.Dispose(); } public void Dispose() { Shadow?.Dispose(); } } /// /// Returns the effect binary for the effect file at `sourcePath`, finding its includes in `includeDirectories`. /// public static byte[] Compile(string sourcePath, params string[] includeDirectories) { string directory = Path.GetDirectoryName(Path.GetFullPath(sourcePath)); string preprocessed; using (var includes = new DirectoryIncludeHandler(new[] { directory }.Concat(includeDirectories).ToArray())) { preprocessed = SharpDX.D3DCompiler.ShaderBytecode.Preprocess( File.ReadAllText(sourcePath), null, includes, out string _); } var compiler = EffectCompiler.FromMemory( new System.Text.UTF8Encoding().GetBytes(preprocessed), ShaderFlags.None); using (var effect = compiler.CompileEffect(ShaderFlags.None)) { var returnMe = new byte[effect.Length]; effect.Read(returnMe, 0, returnMe.Length); return returnMe; } } public static int Main(string[] args) { if (args.Length < 2) { Console.Error.WriteLine("Usage: ShaderBuilder [include directory...]"); return 2; } try { byte[] effect = Compile(args[0], args.Skip(2).ToArray()); Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(args[1]))); File.WriteAllBytes(args[1], effect); return 0; } catch (Exception exception) when (exception is SharpDX.SharpDXException || exception is IOException) { Console.Error.WriteLine($"{args[0]}: error SHADER: {exception.Message}"); return 1; } } } }