Introduces a new Ghost.Shader.Concept project implementing a modern, data-oriented material and shader system with: - Global/local keyword bitsets (fast O(1) ops, 64 bytes) - Multi-pass shader program and per-pass render state overrides - Thread-safe, 16-byte aligned material property blocks - Material pooling to reduce GC pressure - Batch renderer for efficient PSO grouping and async variant warmup - Full demo (Program.cs) and extensive documentation (ARCHITECTURE.md, README.md, PROJECT_SUMMARY.md) - Minor integration: new enums, doc updates, and keyword handling in existing code No breaking changes to the existing engine; all new code is isolated. This serves as a reference implementation for high-performance, extensible material/shader architectures.
123 lines
3.2 KiB
C#
123 lines
3.2 KiB
C#
namespace Ghost.Shader.Concept;
|
|
|
|
/// <summary>
|
|
/// Represents a single rendering pass within a shader program.
|
|
/// Each pass can have its own render state overrides.
|
|
/// </summary>
|
|
public sealed class ShaderPass
|
|
{
|
|
public string Name { get; }
|
|
public int PassId { get; }
|
|
public RenderState RenderState { get; }
|
|
public string VertexEntryPoint { get; }
|
|
public string PixelEntryPoint { get; }
|
|
|
|
public ShaderPass(
|
|
string name,
|
|
int passId,
|
|
RenderState renderState,
|
|
string vertexEntryPoint = "VSMain",
|
|
string pixelEntryPoint = "PSMain")
|
|
{
|
|
Name = name;
|
|
PassId = passId;
|
|
RenderState = renderState;
|
|
VertexEntryPoint = vertexEntryPoint;
|
|
PixelEntryPoint = pixelEntryPoint;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shader program containing multiple passes and keyword declarations.
|
|
/// Immutable after creation for thread-safety.
|
|
/// </summary>
|
|
public sealed class ShaderProgram
|
|
{
|
|
private static int _nextId = 0;
|
|
|
|
public int Id { get; }
|
|
public string Name { get; }
|
|
public ShaderPass[] Passes { get; }
|
|
public ShaderKeyword[] DeclaredKeywords { get; }
|
|
|
|
private readonly Dictionary<string, int> _passNameToIndex = new();
|
|
|
|
public ShaderProgram(
|
|
string name,
|
|
ShaderPass[] passes,
|
|
ShaderKeyword[] declaredKeywords)
|
|
{
|
|
Id = Interlocked.Increment(ref _nextId);
|
|
Name = name;
|
|
Passes = passes;
|
|
DeclaredKeywords = declaredKeywords;
|
|
|
|
for (int i = 0; i < passes.Length; i++)
|
|
{
|
|
_passNameToIndex[passes[i].Name] = i;
|
|
}
|
|
}
|
|
|
|
public int GetPassIndex(string passName)
|
|
{
|
|
return _passNameToIndex.TryGetValue(passName, out int index) ? index : -1;
|
|
}
|
|
|
|
public ShaderVariantKey CreateVariantKey(in KeywordSet keywords)
|
|
{
|
|
return new ShaderVariantKey(Id, keywords.ComputeHash());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Builder pattern for creating shader programs fluently.
|
|
/// </summary>
|
|
public sealed class ShaderProgramBuilder
|
|
{
|
|
private string _name = "Unnamed";
|
|
private readonly List<ShaderPass> _passes = new();
|
|
private readonly List<ShaderKeyword> _keywords = new();
|
|
|
|
public ShaderProgramBuilder WithName(string name)
|
|
{
|
|
_name = name;
|
|
return this;
|
|
}
|
|
|
|
public ShaderProgramBuilder AddPass(
|
|
string passName,
|
|
RenderState? renderState = null,
|
|
string vertexEntry = "VSMain",
|
|
string pixelEntry = "PSMain")
|
|
{
|
|
var pass = new ShaderPass(
|
|
passName,
|
|
_passes.Count,
|
|
renderState ?? RenderState.Default,
|
|
vertexEntry,
|
|
pixelEntry);
|
|
_passes.Add(pass);
|
|
return this;
|
|
}
|
|
|
|
public ShaderProgramBuilder DeclareKeyword(ShaderKeyword keyword)
|
|
{
|
|
_keywords.Add(keyword);
|
|
return this;
|
|
}
|
|
|
|
public ShaderProgramBuilder DeclareKeywords(params ShaderKeyword[] keywords)
|
|
{
|
|
_keywords.AddRange(keywords);
|
|
return this;
|
|
}
|
|
|
|
public ShaderProgram Build()
|
|
{
|
|
if (_passes.Count == 0)
|
|
throw new InvalidOperationException("Shader program must have at least one pass");
|
|
|
|
return new ShaderProgram(_name, _passes.ToArray(), _keywords.ToArray());
|
|
}
|
|
}
|