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;
}
}
}