using Ghost.Core; using Ghost.Core.Graphics; using Ghost.Graphics.Core; using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Utilities; using System.IO.Hashing; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Ghost.Graphics.RHI; public readonly struct ShaderPassKey : IEquatable { public readonly ulong value; public ShaderPassKey(ulong value) { this.value = value; } public ShaderPassKey(string passId) { var passIdSpan = passId.AsSpan(); value = XxHash3.HashToUInt64(MemoryMarshal.AsBytes(passIdSpan)); } public override string ToString() { return value.ToString("X16"); } public bool Equals(ShaderPassKey other) { return value == other.value; } public override int GetHashCode() { return value.GetHashCode(); } public override bool Equals(object? obj) { return obj is ShaderPassKey key && Equals(key); } public static bool operator ==(ShaderPassKey left, ShaderPassKey right) { return left.Equals(right); } public static bool operator !=(ShaderPassKey left, ShaderPassKey right) { return !(left == right); } } public readonly struct GraphicsPipelineKey { public const int KEY_STRING_LENGTH = 33; // 32 chars + null terminator public readonly UInt128 value; public GraphicsPipelineKey(UInt128 value) { this.value = value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static GraphicsPipelineKey Combine(MaterialPipelineKey materialKey, PassPipelineKey passKey) { // Order-sensitive 128-bit mix. Cheap and stable, avoids span hashing. static ulong Mix64(ulong x) { x ^= x >> 30; x *= 0xBF58476D1CE4E5B9ul; x ^= x >> 27; x *= 0x94D049BB133111EBul; x ^= x >> 31; return x; } unsafe static ulong GetLow(UInt128 value) => ((ulong*)&value)[0]; unsafe static ulong GetHigh(UInt128 value) => ((ulong*)&value)[1]; var mLo = GetLow(materialKey.value); var mHi = GetHigh(materialKey.value); var pLo = GetLow(passKey.value); var pHi = GetHigh(passKey.value); // Distinct constants + cross-feeding to reduce structural collisions. var lo = Mix64(mLo ^ (pLo + 0x9E3779B97F4A7C15ul) ^ (mHi * 0xD6E8FEB86659FD93ul)); var hi = Mix64(mHi ^ (pHi + 0xC2B2AE3D27D4EB4Ful) ^ (pLo * 0x165667B19E3779F9ul)); return new GraphicsPipelineKey(new UInt128(lo, hi)); } public Result GetString(Span destination) { if (!value.TryFormat(destination, out var num, "X16")) { return Result.Failure("Failed to format GraphicsPipelineKey to string."); } // Just in case. destination[num] = '\0'; return Result.Success(); } public override string ToString() { return value.ToString("X16"); } public override int GetHashCode() { return value.GetHashCode(); } } public readonly struct MaterialPipelineKey : IEquatable { public readonly UInt128 value; // TODO: Variants public MaterialPipelineKey(ShaderPassKey passKey, PipelineState psoOptions) { // 32-bit packed key for states controlled by material / overrides. // layout: // 0..3 Blend (4 bits) // 4..6 Cull (3 bits) // 7..10 DeafaultState (4 bits) // 11 ZWrite (1 bit) // 12..15 ColorMask (4 bits) var key = 0u; key |= ((uint)psoOptions.Blend & 0xFu) << 0; key |= ((uint)psoOptions.Cull & 0x7u) << 4; key |= ((uint)psoOptions.ZTest & 0xFu) << 7; key |= ((uint)psoOptions.ZWrite & 0x1u) << 11; key |= ((uint)psoOptions.ColorMask & 0xFu) << 12; value = new UInt128(passKey.value, key); } public bool Equals(MaterialPipelineKey other) => value == other.value; public override bool Equals(object? obj) => obj is MaterialPipelineKey other && Equals(other); public override int GetHashCode() => value.GetHashCode(); public static bool operator ==(MaterialPipelineKey left, MaterialPipelineKey right) => left.Equals(right); public static bool operator !=(MaterialPipelineKey left, MaterialPipelineKey right) => !(left == right); } public readonly struct PassPipelineKey : IEquatable { public readonly UInt128 value; public PassPipelineKey(ReadOnlySpan rtvFormats, TextureFormat dsvFormat) { if (rtvFormats.Length > 8) { throw new ArgumentException($"RTV formats length exceeds maximum supported count of {8}."); } // layout: // 0..64 8 RTV formats (8 bits each) // 64..72 DSV format (8 bits) var rtvPart = 0UL; for (var i = 0; i < rtvFormats.Length; i++) { rtvPart |= ((ulong)(byte)rtvFormats[i]) << (i * 8); } value = new UInt128(rtvPart, (ulong)dsvFormat); } public bool Equals(PassPipelineKey other) => value == other.value; public override bool Equals(object? obj) => obj is PassPipelineKey other && Equals(other); public override int GetHashCode() => value.GetHashCode(); public static bool operator ==(PassPipelineKey left, PassPipelineKey right) => left.Equals(right); public static bool operator !=(PassPipelineKey left, PassPipelineKey right) => !(left == right); } public ref struct GraphicsPSODescriptor { public ShaderPassKey PassId { get; set; } public PipelineState PipelineOption { get; set; } public ReadOnlySpan RtvFormats { get; set; } public TextureFormat DsvFormat { get; set; } } public readonly struct CBufferPropertyInfo { public string Name { get; init; } public uint StartOffset { get; init; } public uint Size { get; init; } } public readonly struct CBufferInfo { public string Name { get; init; } public uint RegisterSlot { get; init; } public uint RegisterSpace { get; init; } public uint SizeInBytes { get; init; } public IReadOnlyList Properties { get; init; } } public struct RenderDesc { public float4x4 ViewMatrix { get; set; } public float4x4 ProjectionMatrix { get; set; } public float4 CameraPosition { get; set; } // The "Target" (Where to write pixels) public Handle Target { get; set; } public Handle DepthTarget { get; set; } public RectDesc Viewport { get; set; } //public RenderPathID RenderPath; //public LayerMask CullingMask; } public struct ViewportDesc { public float X { get; set; } public float Y { get; set; } public float Width { get; set; } public float Height { get; set; } public float MinDepth { get; set; } public float MaxDepth { get; set; } } public struct RectDesc { public uint Left { get; set; } public uint Top { get; set; } public uint Right { get; set; } public uint Bottom { get; set; } } public struct SubResourceData { public unsafe void* pData; public uint rowPitch; public uint slicePitch; } public struct PassRenderTargetDesc { public Handle Texture { get; set; } public Color128 ClearColor { get; set; } } public struct PassDepthStencilDesc { public Handle Texture { get; set; } public float ClearDepth { get; set; } public byte ClearStencil { get; set; } } public struct BarrierDesc { public Handle Resource { get; set; } public ResourceState StateBefore { get; set; } public ResourceState StateAfter { get; set; } } public struct ResourceDesc { [StructLayout(LayoutKind.Explicit)] private struct resource_union { [FieldOffset(0)] public TextureDesc textureDescription; [FieldOffset(0)] public BufferDesc bufferDescription; } private resource_union _desc; public TextureDesc TextureDescription { readonly get => _desc.textureDescription; set => _desc.textureDescription = value; } public BufferDesc BufferDescription { readonly get => _desc.bufferDescription; set => _desc.bufferDescription = value; } public static ResourceDesc Buffer(BufferDesc desc) { return new ResourceDesc { BufferDescription = desc }; } public static ResourceDesc Texture(TextureDesc desc) { return new ResourceDesc { TextureDescription = desc }; } } /// /// Render Target description /// Supports either color OR depth rendering, not both /// public struct RenderTargetDesc { /// /// Width of the render Target /// public uint Width { get; set; } /// /// Height of the render Target /// public uint Height { get; set; } /// /// Slice of the render Target /// public uint Slice { get; set; } /// /// Type of render Target /// public RenderTargetType Type { get; set; } /// /// Target texture Format /// public TextureFormat Format { get; set; } /// /// Texture dimension /// public TextureDimension Dimension { get; set; } /// /// Creation flags for the render Target /// public RenderTargetCreationFlags CreationFlags { get; set; } /// /// Number of mip levels. 0 to generate full mip chain /// public uint MipLevels { get; set; } /// /// Number of samples for MSAA /// public uint SampleCount { get; set; } /// /// Creates a color render Target /// public static RenderTargetDesc Color(uint width, uint height, uint slice = 1, TextureFormat format = TextureFormat.R8G8B8A8_UNorm, TextureDimension dimension = TextureDimension.Texture2D, RenderTargetCreationFlags creationFlags = RenderTargetCreationFlags.AllowUAV | RenderTargetCreationFlags.DynamicallyResolution | RenderTargetCreationFlags.GenerateMips, uint mipLevels = 0u, uint sampleCount = 1) { return new RenderTargetDesc { Width = width, Height = height, Slice = slice, Type = RenderTargetType.Color, Format = format, Dimension = dimension, CreationFlags = creationFlags, MipLevels = mipLevels, SampleCount = sampleCount }; } /// /// Creates a depth render Target /// public static RenderTargetDesc Depth(uint width, uint height, uint slice = 1, TextureFormat format = TextureFormat.D24_UNorm_S8_UInt, TextureDimension dimension = TextureDimension.Texture2D, RenderTargetCreationFlags creationFlags = RenderTargetCreationFlags.AllowUAV | RenderTargetCreationFlags.DynamicallyResolution, uint mipLevels = 0u, uint sampleCount = 1) { return new RenderTargetDesc { Width = width, Height = height, Slice = slice, Type = RenderTargetType.Depth, Format = format, Dimension = dimension, CreationFlags = creationFlags, MipLevels = mipLevels, SampleCount = sampleCount }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TextureDesc ToTextureDescripton() { var usage = Type == RenderTargetType.Color ? TextureUsage.RenderTarget : TextureUsage.DepthStencil; if (CreationFlags.HasFlag(RenderTargetCreationFlags.AllowUAV)) { usage |= TextureUsage.UnorderedAccess; } return new TextureDesc { Width = Width, Height = Height, Slice = Slice, Format = Format, Dimension = Dimension, MipLevels = MipLevels, Usage = usage, }; } } /// /// Texture description /// public struct TextureDesc { /// /// Width of the texture /// public uint Width { get; set; } /// /// Height of the texture /// public uint Height { get; set; } /// /// Slice of the texture /// public uint Slice { get; set; } /// /// Texture Format /// public TextureFormat Format { get; set; } /// /// Texture dimension /// public TextureDimension Dimension { get; set; } /// /// Number of mip levels. 0 to generate full mip chain /// public uint MipLevels { get; set; } /// /// Texture usage flags /// public TextureUsage Usage { get; set; } } /// /// Describes the parameters used to configure a texture sampler for graphics rendering operations. /// public record struct SamplerDesc { public TextureFilterMode FilterMode { get; set; } public TextureAddressMode AddressU { get; set; } public TextureAddressMode AddressV { get; set; } public TextureAddressMode AddressW { get; set; } public ComparisonFunction ComparisonFunc { get; set; } public float MipLODBias { get; set; } public uint MaxAnisotropy { get; set; } public float MinLOD { get; set; } public float MaxLOD { get; set; } } /// /// Buffer description /// public struct BufferDesc { /// /// Size of the buffer in bytes /// public ulong Size { get; set; } public uint Stride { get; set; } /// /// Buffer usage flags /// public BufferUsage Usage { get; set; } /// /// Memory space for the buffer /// public ResourceMemoryType MemoryType { get; set; } } public struct CommandError { public int CommandIndex { get; set; } public string CommandName { get; set; } public ErrorStatus Status { get; set; } } /// /// Swap chain description /// public struct SwapChainDesc { /// /// Width of the swap chain /// public uint Width { get; set; } /// /// Height of the swap chain /// public uint Height { get; set; } public float ScaleX { get; set; } public float ScaleY { get; set; } /// /// Back buffer Format /// public TextureFormat Format { get; set; } /// /// Target for presentation (window handle or composition Target) /// public SwapChainTarget Target { get; set; } } /// /// Swap chain Target (window handle or composition surface) /// public struct SwapChainTarget { /// /// Target space /// public SwapChainTargetType Type { get; set; } /// /// Window handle for HWND targets /// public nint WindowHandle { get; set; } /// /// Composition surface for UWP/WinUI targets /// public object? CompositionSurface { get; set; } public static SwapChainTarget FromWindowHandle(nint hwnd) { return new SwapChainTarget { Type = SwapChainTargetType.WindowHandle, WindowHandle = hwnd, CompositionSurface = null }; } public static SwapChainTarget FromCompositionSurface(object surface) { return new SwapChainTarget { Type = SwapChainTargetType.Composition, WindowHandle = 0, CompositionSurface = surface }; } } public enum SwapChainTargetType { WindowHandle, Composition } [Flags] public enum ResourceState { Common = 0, VertexAndConstantBuffer = 1 << 0, IndexBuffer = 1 << 1, RenderTarget = 1 << 2, UnorderedAccess = 1 << 3, DepthWrite = 1 << 4, DepthRead = 1 << 5, PixelShaderResource = 1 << 6, CopyDest = 1 << 7, CopySource = 1 << 8, GenericRead = 1 << 9, IndirectArgument = 1 << 10, NonPixelShaderResource = 1 << 11, Present = 0, } public enum CommandQueueType { Graphics, Compute, Copy } public enum CommandBufferType { Graphics, Compute, Copy } public enum PipelineType { Graphics, Compute } [Flags] public enum RenderTargetCreationFlags { None = 0, AllowUAV = 1 << 0, AllowMSAA = 1 << 1, DynamicallyResolution = 1 << 2, GenerateMips = 1 << 3 } public enum ResourceMemoryType { Default, // GPU memory Upload, // CPU-to-GPU memory Readback // GPU-to-CPU memory } [Flags] public enum TextureUsage { None = 0, ShaderResource = 1 << 0, RenderTarget = 1 << 1, DepthStencil = 1 << 2, UnorderedAccess = 1 << 3 } public enum TextureDimension { Unknown = -1, None = 0, Texture2D = 1, Texture3D = 2, TextureCube = 3, Texture2DArray = 4, TextureCubeArray = 5 } public enum RenderTargetType { Color, Depth } // TODO: Support compressed formats (BCn, ASTC, ETC2, etc) public enum TextureFormat { Unknown, R8G8B8A8_UNorm, B8G8R8A8_UNorm, R16G16B16A16_Float, R32G32B32A32_Float, D24_UNorm_S8_UInt, D32_Float } [Flags] public enum BufferUsage { None = 0, Vertex = 1 << 0, Index = 1 << 1, IndirectArgument = 1 << 7, Constant = 1 << 2, ShaderResource = 1 << 3, UnorderedAccess = 1 << 4, Structured = 1 << 5, Raw = 1 << 6, Upload = 1 << 8, Readback = 1 << 9, } public enum IndexType { UInt16, UInt32 } public enum PrimitiveTopology { Point, Line, Triangle, } public enum TextureFilterMode { Point, Bilinear, Trilinear, Anisotropic } public enum TextureAddressMode { Repeat, Mirror, Clamp, Border, MirrorOnce } public enum ComparisonFunction { Never, Less, Equal, LessEqual, Greater, NotEqual, GreaterEqual, Always }