- Added `generatedCodePath` to `FullPassDescriptor` for better shader code organization. - Removed redundant `IID_PPV_ARGS` method and unused `Misaki.HighPerformance.Unsafe` reference. - Refactored `Material` and `MaterialAccessor` to use `CBuffer` and updated buffer size handling. - Renamed command buffer variables in `RenderingContext` for consistency. - Updated `D3D12PipelineLibrary` to cache compiled shader results and added `ShaderPassKey`. - Refactored `D3D12GraphicsEngine` to integrate `_copyCommandBuffer` lifecycle. - Enhanced `D3D12ResourceAllocator` with shader pass creation using constant buffer info. - Simplified `D3D12ShaderCompiler` with `GENERATED_CODE_PATH` support and improved reflection handling. - Introduced `CBufferPropertyInfo` and `CBufferInfo` structs for better encapsulation. - Updated HLSL shaders to use `g_PerMaterialData` and dynamic includes. - Improved error handling in `SDLCompiler` with try-catch blocks and better messages. - Refactored `test.gshader` to use dynamically generated includes. - Fixed typos, improved code readability, and removed unused code.
774 lines
15 KiB
C#
774 lines
15 KiB
C#
using Ghost.Core;
|
|
using Ghost.Core.Graphics;
|
|
using Ghost.Graphics.Core;
|
|
using Ghost.Graphics.D3D12.Utilities;
|
|
using Misaki.HighPerformance.Utilities;
|
|
using System.IO.Hashing;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
using TerraFX.Interop.DirectX;
|
|
|
|
namespace Ghost.Graphics.RHI;
|
|
|
|
public interface IRHIObject
|
|
{
|
|
string Name
|
|
{
|
|
get; set;
|
|
}
|
|
}
|
|
|
|
public readonly struct ShaderPassKey
|
|
{
|
|
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 override int GetHashCode()
|
|
{
|
|
return value.GetHashCode();
|
|
}
|
|
}
|
|
|
|
public readonly struct GraphicsPipelineKey
|
|
{
|
|
public const int KEY_STRING_LENGTH = 17; // 16 chars + null terminator
|
|
|
|
public readonly ulong value;
|
|
|
|
public GraphicsPipelineKey(ulong value)
|
|
{
|
|
this.value = value;
|
|
}
|
|
|
|
public Result GetString(Span<char> destination)
|
|
{
|
|
if (!value.TryFormat(destination, out _, "X16"))
|
|
{
|
|
return Result.Fail("Failed to format GraphicsPipelineKey to string.");
|
|
}
|
|
|
|
destination[16] = '\0';
|
|
return Result.Success();
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return value.ToString("X16");
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return value.GetHashCode();
|
|
}
|
|
}
|
|
|
|
internal struct GraphicsPipelineHash
|
|
{
|
|
[InlineArray(D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT)]
|
|
public struct rtv_array
|
|
{
|
|
public TextureFormat rtvFormats;
|
|
}
|
|
|
|
public ShaderPassKey id;
|
|
public rtv_array rtvFormats;
|
|
public uint rtvCount;
|
|
public TextureFormat dsvFormat;
|
|
// Do we need to store blend state?
|
|
// TODO: Variants
|
|
|
|
public readonly GraphicsPipelineKey GetKey()
|
|
{
|
|
Span<ulong> data = stackalloc ulong[3 + D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT];
|
|
data[0] = id.value;
|
|
data[1] = rtvCount;
|
|
data[2] = (ulong)dsvFormat;
|
|
|
|
for (var i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
|
|
{
|
|
data[3 + i] = (ulong)rtvFormats[i];
|
|
}
|
|
|
|
var bytes = MemoryMarshal.AsBytes(data);
|
|
return new GraphicsPipelineKey(XxHash3.HashToUInt64(bytes));
|
|
}
|
|
}
|
|
|
|
public ref struct GraphicsPSODescriptor
|
|
{
|
|
public ShaderPassKey passId;
|
|
public ZTestOptions zTest;
|
|
public ZWriteOptions zWrite;
|
|
public CullOptions cull;
|
|
public BlendOptions blend;
|
|
public uint colorMask;
|
|
|
|
public ReadOnlySpan<TextureFormat> rtvFormats;
|
|
public TextureFormat dsvFormat;
|
|
}
|
|
|
|
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<CBufferPropertyInfo> Properties
|
|
{
|
|
get; init;
|
|
}
|
|
}
|
|
|
|
public struct ViewportDesc
|
|
{
|
|
public float x;
|
|
public float y;
|
|
public float width;
|
|
public float height;
|
|
public float minDepth;
|
|
public float maxDepth;
|
|
}
|
|
|
|
public struct RectDesc
|
|
{
|
|
public uint left;
|
|
public uint top;
|
|
public uint right;
|
|
public uint bottom;
|
|
}
|
|
|
|
public struct SubResourceData
|
|
{
|
|
public unsafe void* pData;
|
|
public nint rowPitch;
|
|
public nint slicePitch;
|
|
}
|
|
|
|
public struct PassRenderTargetDesc
|
|
{
|
|
public Handle<Texture> texture;
|
|
public Color128 clearColor;
|
|
}
|
|
|
|
public struct PassDepthStencilDesc
|
|
{
|
|
public Handle<Texture> texture;
|
|
public float clearDepth;
|
|
public byte clearStencil;
|
|
}
|
|
|
|
|
|
[StructLayout(LayoutKind.Explicit)]
|
|
public struct ResourceDesc
|
|
{
|
|
[FieldOffset(0)]
|
|
public TextureDesc textureDescription;
|
|
|
|
[FieldOffset(0)]
|
|
public BufferDesc bufferDescription;
|
|
|
|
public static ResourceDesc Buffer(BufferDesc desc)
|
|
{
|
|
return new ResourceDesc
|
|
{
|
|
bufferDescription = desc
|
|
};
|
|
}
|
|
|
|
public static ResourceDesc Texture(TextureDesc desc)
|
|
{
|
|
return new ResourceDesc
|
|
{
|
|
textureDescription = desc
|
|
};
|
|
}
|
|
|
|
internal static ResourceDesc FromD3D12(D3D12_RESOURCE_DESC desc)
|
|
{
|
|
if (desc.Dimension == D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_BUFFER)
|
|
{
|
|
return Buffer(new BufferDesc
|
|
{
|
|
Size = (uint)desc.Width,
|
|
Stride = 0,
|
|
Usage = BufferUsage.None,
|
|
MemoryType = ResourceMemoryType.Default
|
|
});
|
|
}
|
|
else
|
|
{
|
|
return Texture(new TextureDesc
|
|
{
|
|
Width = (uint)desc.Width,
|
|
Height = desc.Height,
|
|
Slice = desc.DepthOrArraySize,
|
|
Format = desc.Format.ToTextureFormat(),
|
|
Dimension = desc.Dimension.ToTextureDimension(),
|
|
MipLevels = desc.MipLevels,
|
|
Usage = TextureUsage.None,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Render target description
|
|
/// Supports either color OR depth rendering, not both
|
|
/// </summary>
|
|
public struct RenderTargetDesc
|
|
{
|
|
/// <summary>
|
|
/// Width of the render target
|
|
/// </summary>
|
|
public uint Width
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Height of the render target
|
|
/// </summary>
|
|
public uint Height
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Slice of the render target
|
|
/// </summary>
|
|
public uint Slice
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Type of render target
|
|
/// </summary>
|
|
public RenderTargetType Type
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Target texture format
|
|
/// </summary>
|
|
public TextureFormat Format
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Texture dimension
|
|
/// </summary>
|
|
public TextureDimension Dimension
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creation flags for the render target
|
|
/// </summary>
|
|
public RenderTargetCreationFlags CreationFlags
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Number of mip levels. 0 to generate full mip chain
|
|
/// </summary>
|
|
public uint MipLevels
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Number of samples for MSAA
|
|
/// </summary>
|
|
public uint SampleCount
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a color render target
|
|
/// </summary>
|
|
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
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a depth render target
|
|
/// </summary>
|
|
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,
|
|
};
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Texture description
|
|
/// </summary>
|
|
public struct TextureDesc
|
|
{
|
|
/// <summary>
|
|
/// Width of the texture
|
|
/// </summary>
|
|
public uint Width
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Height of the texture
|
|
/// </summary>
|
|
public uint Height
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Slice of the texture
|
|
/// </summary>
|
|
public uint Slice
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Texture format
|
|
/// </summary>
|
|
public TextureFormat Format
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Texture dimension
|
|
/// </summary>
|
|
public TextureDimension Dimension
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Number of mip levels. 0 to generate full mip chain
|
|
/// </summary>
|
|
public uint MipLevels
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Texture usage flags
|
|
/// </summary>
|
|
public TextureUsage Usage
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Buffer description
|
|
/// </summary>
|
|
public struct BufferDesc
|
|
{
|
|
/// <summary>
|
|
/// Size of the buffer in bytes
|
|
/// </summary>
|
|
public ulong Size
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public uint Stride
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Buffer usage flags
|
|
/// </summary>
|
|
public BufferUsage Usage
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Memory type for the buffer
|
|
/// </summary>
|
|
public ResourceMemoryType MemoryType
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Swap chain description
|
|
/// </summary>
|
|
public struct SwapChainDesc
|
|
{
|
|
/// <summary>
|
|
/// Width of the swap chain
|
|
/// </summary>
|
|
public uint width;
|
|
|
|
/// <summary>
|
|
/// Height of the swap chain
|
|
/// </summary>
|
|
public uint height;
|
|
|
|
/// <summary>
|
|
/// Back buffer format
|
|
/// </summary>
|
|
public TextureFormat format;
|
|
|
|
/// <summary>
|
|
/// Target for presentation (window handle or composition target)
|
|
/// </summary>
|
|
public SwapChainTarget target;
|
|
|
|
public SwapChainDesc(uint width, uint height, SwapChainTarget target, TextureFormat format = TextureFormat.B8G8R8A8_UNorm, uint bufferCount = 2)
|
|
{
|
|
this.width = width;
|
|
this.height = height;
|
|
this.format = format;
|
|
this.target = target;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Swap chain target (window handle or composition surface)
|
|
/// </summary>
|
|
public struct SwapChainTarget
|
|
{
|
|
/// <summary>
|
|
/// Target type
|
|
/// </summary>
|
|
public SwapChainTargetType type;
|
|
|
|
/// <summary>
|
|
/// Window handle for HWND targets
|
|
/// </summary>
|
|
public nint windowHandle;
|
|
|
|
/// <summary>
|
|
/// Composition surface for UWP/WinUI targets
|
|
/// </summary>
|
|
public object? compositionSurface;
|
|
|
|
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 = nint.Zero,
|
|
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,
|
|
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,
|
|
}
|
|
|
|
// SDL compiler
|
|
|
|
internal ref struct CompilerConfig
|
|
{
|
|
public ReadOnlySpan<string> defines;
|
|
public string? include;
|
|
public string shaderPath;
|
|
public string entryPoint;
|
|
public ShaderStage stage;
|
|
public CompilerTier tier;
|
|
public CompilerOptimizeLevel optimizeLevel;
|
|
public CompilerOption options;
|
|
}
|
|
|
|
internal enum CompilerTier
|
|
{
|
|
Tier0,
|
|
Tier1,
|
|
Tier2
|
|
}
|
|
|
|
internal enum CompilerOptimizeLevel
|
|
{
|
|
O0,
|
|
O1,
|
|
O2,
|
|
O3
|
|
}
|
|
|
|
[Flags]
|
|
internal enum CompilerOption
|
|
{
|
|
None = 0,
|
|
KeepDebugInfo = 1 << 0,
|
|
KeepReflections = 1 << 1,
|
|
WarnAsError = 1 << 2
|
|
}
|
|
|
|
internal enum ShaderStage
|
|
{
|
|
TaskShader,
|
|
MeshShader,
|
|
PixelShader,
|
|
ComputeShader
|
|
}
|