forked from Misaki/GhostEngine
Add high-performance material/shader system (Ghost.Shader.Concept)
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.
This commit is contained in:
77
Ghost.Shader.Concept/ShaderKeyword.cs
Normal file
77
Ghost.Shader.Concept/ShaderKeyword.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
namespace Ghost.Shader.Concept;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a shader keyword that can toggle shader features.
|
||||
/// Keywords are immutable and interned for fast comparison.
|
||||
/// </summary>
|
||||
public readonly struct ShaderKeyword : IEquatable<ShaderKeyword>
|
||||
{
|
||||
private readonly int _id;
|
||||
private readonly KeywordScope _scope;
|
||||
|
||||
public int Id => _id;
|
||||
public KeywordScope Scope => _scope;
|
||||
public bool IsValid => _id >= 0;
|
||||
|
||||
internal ShaderKeyword(int id, KeywordScope scope)
|
||||
{
|
||||
_id = id;
|
||||
_scope = scope;
|
||||
}
|
||||
|
||||
public bool Equals(ShaderKeyword other) => _id == other._id && _scope == other._scope;
|
||||
public override bool Equals(object? obj) => obj is ShaderKeyword other && Equals(other);
|
||||
public override int GetHashCode() => HashCode.Combine(_id, _scope);
|
||||
|
||||
public static bool operator ==(ShaderKeyword left, ShaderKeyword right) => left.Equals(right);
|
||||
public static bool operator !=(ShaderKeyword left, ShaderKeyword right) => !left.Equals(right);
|
||||
}
|
||||
|
||||
public enum KeywordScope : byte
|
||||
{
|
||||
/// <summary>Keywords set globally (e.g., platform, quality settings)</summary>
|
||||
Global,
|
||||
|
||||
/// <summary>Keywords set per-material instance</summary>
|
||||
Local
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manages keyword registration and fast lookup.
|
||||
/// Thread-safe for registration, lock-free for lookups.
|
||||
/// </summary>
|
||||
public sealed class ShaderKeywordRegistry
|
||||
{
|
||||
private readonly Dictionary<string, ShaderKeyword> _keywords = new();
|
||||
private readonly Dictionary<int, string> _idToName = new();
|
||||
private int _nextId = 0;
|
||||
private readonly object _lock = new();
|
||||
|
||||
public static ShaderKeywordRegistry Instance { get; } = new();
|
||||
|
||||
private ShaderKeywordRegistry() { }
|
||||
|
||||
public ShaderKeyword GetOrRegister(string name, KeywordScope scope)
|
||||
{
|
||||
string key = $"{scope}:{name}";
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_keywords.TryGetValue(key, out var existing))
|
||||
return existing;
|
||||
|
||||
var keyword = new ShaderKeyword(_nextId++, scope);
|
||||
_keywords[key] = keyword;
|
||||
_idToName[keyword.Id] = name;
|
||||
return keyword;
|
||||
}
|
||||
}
|
||||
|
||||
public string? GetName(ShaderKeyword keyword)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _idToName.TryGetValue(keyword.Id, out var name) ? name : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user