using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace Blacklight.Bezier.D3D9 { public enum VariantKind { /// /// Computes a distance; also answers the shell question by comparing it with the thickness. /// Distance, /// /// Only answers whether a pixel is inside the shell. /// ShellTest, /// /// Answers whether a pixel is inside its own thickness of a whole piece, ends included. /// PieceShellTest, /// /// Answers whether a pixel is inside its thickness of either of two pieces. /// TwoCurveTest, } /// /// A method under test, as the complete pixel shader source that runs it. /// public sealed class Variant { public readonly string Name; public readonly string Description; public readonly VariantKind Kind; public readonly string Source; public Variant(string name, string description, VariantKind kind, string source) { Name = name; Description = description; Kind = kind; Source = source; } } /// /// Finds variants as files named for their function, with a one-line description as the first comment: /// Shaders\Variants\Name.hlsli defines float Name(x, y, phi, end), Shaders\ShellTests\Name.hlsli defines /// bool Name(x, y, phi, g), Shaders\PieceShellTests\Name.hlsli defines bool Name(float4 piece, thickness), and /// Shaders\TwoCurveTests\Name.hlsli defines bool Name(float4 first, float4 second, float2 thickness). Adding a /// method is adding a file. /// public static class VariantCatalog { public const string BaselineName = "Null"; public static string DescriptionOf(string variantSource) { string firstLine = variantSource.Split('\n')[0].Trim(); if (firstLine.StartsWith("//")) { return firstLine.Substring(2).Trim(); } return ""; } /// /// The shader folder in the source tree, found by walking up from startDirectory. /// public static string FindShaderDirectory(string startDirectory) { // Read in place: an old-style csproj expands wildcards when Visual Studio loads it, so a copy beside the // exe misses variants added since. In place, an added or edited variant also runs without a rebuild. string relative = Path.Combine("Modules", "Blacklight", "Bezier", "Bezier", "Shaders"); for (var directory = new DirectoryInfo(startDirectory); directory != null; directory = directory.Parent) { string candidate = Path.Combine(directory.FullName, relative); if (Directory.Exists(candidate)) { return candidate; } } throw new DirectoryNotFoundException("No " + relative + " above " + startDirectory + "."); } public static IEnumerable VariantFiles(string directory) { return Directory.GetFiles(directory, "*.hlsli") .OrderBy(path => Path.GetFileNameWithoutExtension(path) == BaselineName ? 0 : 1) .ThenBy(path => path, StringComparer.OrdinalIgnoreCase); } /// /// The production distance the renderer ships, found beside this module's shaders in the source tree. /// public static string ProductionDistancePath(string shaderDirectory) { return Path.GetFullPath(Path.Combine(shaderDirectory, "..", "..", "..", "GPUBridge", "GPUBridge", "Shaders", "QuadraticBezierDistance.hlsli")); } public static List Load(string shaderDirectory) { // The variants build on the shipped code, so what's measured here is what the renderer runs. string common = File.ReadAllText(ProductionDistancePath(shaderDirectory)) + "\n" + File.ReadAllText(Path.Combine(shaderDirectory, "Common.hlsli")); string cascade = File.ReadAllText(Path.Combine(shaderDirectory, "Cascade.hlsli")); string harness = File.ReadAllText(Path.Combine(shaderDirectory, "Harness.hlsl")); var returnMe = new List(); foreach (string path in VariantFiles(Path.Combine(shaderDirectory, "Variants"))) { string name = Path.GetFileNameWithoutExtension(path); string variantSource = File.ReadAllText(path); string source = common + "\n" + variantSource + "\n#define VARIANT_DISTANCE " + name + "\n" + harness; returnMe.Add(new Variant(name, DescriptionOf(variantSource), VariantKind.Distance, source)); } foreach (string path in VariantFiles(Path.Combine(shaderDirectory, "ShellTests"))) { string name = Path.GetFileNameWithoutExtension(path); string variantSource = File.ReadAllText(path); string source = common + "\n" + cascade + "\n" + variantSource + "\n#define VARIANT_SHELL " + name + "\n" + harness; returnMe.Add(new Variant(name, DescriptionOf(variantSource), VariantKind.ShellTest, source)); } foreach (string path in VariantFiles(Path.Combine(shaderDirectory, "PieceShellTests"))) { string name = Path.GetFileNameWithoutExtension(path); string variantSource = File.ReadAllText(path); string source = common + "\n" + variantSource + "\n#define VARIANT_PIECE_SHELL " + name + "\n" + harness; returnMe.Add(new Variant(name, DescriptionOf(variantSource), VariantKind.PieceShellTest, source)); } foreach (string path in VariantFiles(Path.Combine(shaderDirectory, "TwoCurveTests"))) { string name = Path.GetFileNameWithoutExtension(path); string variantSource = File.ReadAllText(path); string source = common + "\n" + variantSource + "\n#define VARIANT_TWO_CURVE " + name + "\n" + harness; returnMe.Add(new Variant(name, DescriptionOf(variantSource), VariantKind.TwoCurveTest, source)); } return returnMe; } } }