Added new RHI abstraction layer;

Added new console debug page to UnitTest;
This commit is contained in:
2025-08-25 10:48:59 +09:00
parent eafbfb2fa1
commit 5385141f14
44 changed files with 3473 additions and 357 deletions

View File

@@ -0,0 +1,147 @@
using Ghost.Graphics.Data;
namespace Ghost.Graphics.RHI;
/// <summary>
/// D3D12-style command buffer interface for recording rendering commands
/// </summary>
public interface ICommandBuffer : IDisposable
{
/// <summary>
/// Begins recording commands into this command buffer
/// </summary>
void Begin();
/// <summary>
/// Ends recording commands and prepares for submission
/// </summary>
void End();
/// <summary>
/// Begins a render pass with the specified render target
/// </summary>
/// <param name="renderTarget">Render target to render into</param>
/// <param name="clearColor">Color to clear the render target with</param>
void BeginRenderPass(IRenderTarget renderTarget, Color128 clearColor);
/// <summary>
/// Ends the current render pass
/// </summary>
void EndRenderPass();
/// <summary>
/// Sets the viewport for rendering
/// </summary>
/// <param name="viewport">Viewport to set</param>
void SetViewport(ViewportDesc viewport);
/// <summary>
/// Sets the scissor rectangle
/// </summary>
/// <param name="rect">Scissor rectangle to set</param>
void SetScissorRect(RectDesc rect);
/// <summary>
/// Inserts a resource barrier for state transitions
/// </summary>
/// <param name="resource">Resource to transition</param>
/// <param name="before">Current resource state</param>
/// <param name="after">Target resource state</param>
void ResourceBarrier(IResource resource, ResourceState before, ResourceState after);
/// <summary>
/// Sets the graphics root signature
/// </summary>
/// <param name="rootSignature">Root signature to set</param>
void SetGraphicsRootSignature(IRootSignature rootSignature);
/// <summary>
/// Sets the pipeline state object
/// </summary>
/// <param name="pipelineState">Pipeline state to set</param>
void SetPipelineState(IPipelineState pipelineState);
/// <summary>
/// Sets descriptor heaps for bindless rendering
/// </summary>
/// <param name="heaps">Descriptor heaps to set</param>
void SetDescriptorHeaps(IDescriptorHeap[] heaps);
/// <summary>
/// Draws indexed geometry
/// </summary>
/// <param name="indexCount">Number of indices to draw</param>
/// <param name="instanceCount">Number of instances to draw</param>
/// <param name="startIndex">Starting index location</param>
/// <param name="baseVertex">Base vertex location</param>
/// <param name="startInstance">Starting instance location</param>
void DrawIndexedInstanced(uint indexCount, uint instanceCount = 1, uint startIndex = 0, int baseVertex = 0, uint startInstance = 0);
/// <summary>
/// Dispatches compute threads
/// </summary>
/// <param name="threadGroupCountX">Thread groups in X dimension</param>
/// <param name="threadGroupCountY">Thread groups in Y dimension</param>
/// <param name="threadGroupCountZ">Thread groups in Z dimension</param>
void Dispatch(uint threadGroupCountX, uint threadGroupCountY = 1, uint threadGroupCountZ = 1);
}
/// <summary>
/// Viewport description
/// </summary>
public struct ViewportDesc
{
public float X;
public float Y;
public float Width;
public float Height;
public float MinDepth;
public float MaxDepth;
public ViewportDesc(float width, float height)
{
X = 0;
Y = 0;
Width = width;
Height = height;
MinDepth = 0.0f;
MaxDepth = 1.0f;
}
}
/// <summary>
/// Rectangle description
/// </summary>
public struct RectDesc
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public RectDesc(int left, int top, int right, int bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
}
/// <summary>
/// D3D12-style resource states
/// </summary>
public enum ResourceState
{
Common = 0,
VertexAndConstantBuffer = 0x1,
IndexBuffer = 0x2,
RenderTarget = 0x4,
UnorderedAccess = 0x8,
DepthWrite = 0x10,
DepthRead = 0x20,
PixelShaderResource = 0x80,
CopyDest = 0x400,
CopySource = 0x800,
Present = 0
}

View File

@@ -0,0 +1,53 @@
namespace Ghost.Graphics.RHI;
/// <summary>
/// D3D12-style command queue interface
/// </summary>
public interface ICommandQueue : IDisposable
{
/// <summary>
/// Type of commands this queue can execute
/// </summary>
CommandQueueType Type { get; }
/// <summary>
/// Submits a single command buffer for execution
/// </summary>
/// <param name="commandBuffer">Command buffer to submit</param>
void Submit(ICommandBuffer commandBuffer);
/// <summary>
/// Submits multiple command buffers for execution
/// </summary>
/// <param name="commandBuffers">Command buffers to submit</param>
void Submit(ICommandBuffer[] commandBuffers);
/// <summary>
/// Signals a fence with the specified value
/// </summary>
/// <param name="value">Value to signal</param>
/// <returns>The fence value that was signaled</returns>
ulong Signal(ulong value);
/// <summary>
/// Waits for the fence to reach the specified value
/// </summary>
/// <param name="value">Value to wait for</param>
void WaitForValue(ulong value);
/// <summary>
/// Gets the last completed fence value
/// </summary>
/// <returns>Last completed fence value</returns>
ulong GetCompletedValue();
}
/// <summary>
/// Command queue types matching D3D12
/// </summary>
public enum CommandQueueType
{
Graphics,
Compute,
Copy
}

View File

@@ -0,0 +1,144 @@
namespace Ghost.Graphics.RHI;
/// <summary>
/// D3D12-style descriptor allocator interface
/// </summary>
public interface IDescriptorAllocator : IDisposable
{
/// <summary>
/// Allocates a render target view descriptor
/// </summary>
/// <returns>RTV descriptor handle</returns>
DescriptorHandle AllocateRTV();
/// <summary>
/// Allocates multiple render target view descriptors
/// </summary>
/// <param name="count">Number of descriptors to allocate</param>
/// <returns>Array of RTV descriptor handles</returns>
DescriptorHandle[] AllocateRTVs(uint count);
/// <summary>
/// Allocates a depth stencil view descriptor
/// </summary>
/// <returns>DSV descriptor handle</returns>
DescriptorHandle AllocateDSV();
/// <summary>
/// Allocates a shader resource view descriptor
/// </summary>
/// <returns>SRV descriptor handle</returns>
DescriptorHandle AllocateSRV();
/// <summary>
/// Allocates a sampler descriptor
/// </summary>
/// <returns>Sampler descriptor handle</returns>
DescriptorHandle AllocateSampler();
/// <summary>
/// Allocates a bindless descriptor for SM 6.6 rendering
/// </summary>
/// <returns>Bindless descriptor handle</returns>
DescriptorHandle AllocateBindless();
/// <summary>
/// Releases a render target view descriptor
/// </summary>
/// <param name="handle">RTV descriptor to release</param>
void ReleaseRTV(DescriptorHandle handle);
/// <summary>
/// Releases a depth stencil view descriptor
/// </summary>
/// <param name="handle">DSV descriptor to release</param>
void ReleaseDSV(DescriptorHandle handle);
/// <summary>
/// Releases a shader resource view descriptor
/// </summary>
/// <param name="handle">SRV descriptor to release</param>
void ReleaseSRV(DescriptorHandle handle);
/// <summary>
/// Releases a sampler descriptor
/// </summary>
/// <param name="handle">Sampler descriptor to release</param>
void ReleaseSampler(DescriptorHandle handle);
/// <summary>
/// Releases a bindless descriptor
/// </summary>
/// <param name="handle">Bindless descriptor to release</param>
void ReleaseBindless(DescriptorHandle handle);
}
/// <summary>
/// D3D12-style descriptor heap interface
/// </summary>
public interface IDescriptorHeap : IDisposable
{
/// <summary>
/// Type of descriptors this heap contains
/// </summary>
DescriptorHeapType Type { get; }
/// <summary>
/// Maximum number of descriptors in this heap
/// </summary>
uint MaxDescriptors { get; }
/// <summary>
/// Whether this heap is shader visible
/// </summary>
bool IsShaderVisible { get; }
/// <summary>
/// Gets a CPU descriptor handle at the specified index
/// </summary>
/// <param name="index">Index of the descriptor</param>
/// <returns>CPU descriptor handle</returns>
DescriptorHandle GetCPUHandle(uint index);
/// <summary>
/// Gets a GPU descriptor handle at the specified index
/// </summary>
/// <param name="index">Index of the descriptor</param>
/// <returns>GPU descriptor handle</returns>
DescriptorHandle GetGPUHandle(uint index);
}
/// <summary>
/// Descriptor handle for D3D12-style descriptor management
/// </summary>
public struct DescriptorHandle : IEquatable<DescriptorHandle>
{
public uint Index;
public bool IsValid;
public DescriptorHandle(uint index)
{
Index = index;
IsValid = true;
}
public static DescriptorHandle Invalid => new() { Index = uint.MaxValue, IsValid = false };
public bool Equals(DescriptorHandle other) => Index == other.Index && IsValid == other.IsValid;
public override bool Equals(object? obj) => obj is DescriptorHandle other && Equals(other);
public override int GetHashCode() => HashCode.Combine(Index, IsValid);
public static bool operator ==(DescriptorHandle left, DescriptorHandle right) => left.Equals(right);
public static bool operator !=(DescriptorHandle left, DescriptorHandle right) => !left.Equals(right);
}
/// <summary>
/// D3D12 descriptor heap types
/// </summary>
public enum DescriptorHeapType
{
CBV_SRV_UAV,
Sampler,
RTV,
DSV
}

View File

@@ -0,0 +1,72 @@
namespace Ghost.Graphics.RHI;
/// <summary>
/// D3D12-native render device interface for creating graphics resources
/// </summary>
public interface IRenderDevice : IDisposable
{
/// <summary>
/// Graphics command queue for rendering operations
/// </summary>
ICommandQueue GraphicsQueue { get; }
/// <summary>
/// Compute command queue for compute shader operations
/// </summary>
ICommandQueue ComputeQueue { get; }
/// <summary>
/// Copy command queue for data transfer operations
/// </summary>
ICommandQueue CopyQueue { get; }
/// <summary>
/// Creates a command buffer for recording rendering commands
/// </summary>
/// <param name="type">Type of command buffer to create</param>
/// <returns>A new command buffer instance</returns>
ICommandBuffer CreateCommandBuffer(CommandBufferType type = CommandBufferType.Graphics);
/// <summary>
/// Creates a swap chain for presentation
/// </summary>
/// <param name="desc">Swap chain description</param>
/// <returns>A new swap chain instance</returns>
ISwapChain CreateSwapChain(SwapChainDesc desc);
/// <summary>
/// Creates a render target for off-screen rendering
/// </summary>
/// <param name="desc">Render target description</param>
/// <returns>A new render target instance</returns>
IRenderTarget CreateRenderTarget(RenderTargetDesc desc);
/// <summary>
/// Creates a texture resource
/// </summary>
/// <param name="desc">Texture description</param>
/// <returns>A new texture instance</returns>
ITexture CreateTexture(TextureDesc desc);
/// <summary>
/// Creates a buffer resource
/// </summary>
/// <param name="desc">Buffer description</param>
/// <returns>A new buffer instance</returns>
IBuffer CreateBuffer(BufferDesc desc);
/// <summary>
/// Gets the descriptor allocator for managing descriptors
/// </summary>
IDescriptorAllocator DescriptorAllocator { get; }
}
/// <summary>
/// Command buffer types matching D3D12 command list types
/// </summary>
public enum CommandBufferType
{
Graphics,
Compute,
Copy
}

View File

@@ -0,0 +1,199 @@
namespace Ghost.Graphics.RHI;
/// <summary>
/// Pipeline state object interface
/// </summary>
public interface IPipelineState : IDisposable
{
/// <summary>
/// Pipeline type (graphics or compute)
/// </summary>
PipelineType Type
{
get;
}
/// <summary>
/// Pipeline name for debugging
/// </summary>
string Name
{
get; set;
}
}
/// <summary>
/// Root signature interface
/// </summary>
public interface IRootSignature : IDisposable
{
/// <summary>
/// Root signature name for debugging
/// </summary>
string Name
{
get; set;
}
}
/// <summary>
/// Pipeline types
/// </summary>
public enum PipelineType
{
Graphics,
Compute
}
/// <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;
/// <summary>
/// Height of the render target
/// </summary>
public uint Height;
/// <summary>
/// Type of render target (color or depth)
/// </summary>
public RenderTargetType Type;
/// <summary>
/// Target texture format
/// </summary>
public TextureFormat Format;
/// <summary>
/// Number of samples for MSAA
/// </summary>
public uint SampleCount;
/// <summary>
/// Creates a color render target
/// </summary>
public static RenderTargetDesc Color(uint width, uint height, TextureFormat format, uint sampleCount = 1)
{
return new RenderTargetDesc
{
Width = width,
Height = height,
Type = RenderTargetType.Color,
Format = format,
SampleCount = sampleCount
};
}
/// <summary>
/// Creates a depth render target
/// </summary>
public static RenderTargetDesc Depth(uint width, uint height, TextureFormat format = TextureFormat.D24_UNorm_S8_UInt, uint sampleCount = 1)
{
return new RenderTargetDesc
{
Width = width,
Height = height,
Type = RenderTargetType.Depth,
Format = format,
SampleCount = sampleCount
};
}
}
/// <summary>
/// Texture description
/// </summary>
public struct TextureDesc
{
/// <summary>
/// Width of the texture
/// </summary>
public uint Width;
/// <summary>
/// Height of the texture
/// </summary>
public uint Height;
/// <summary>
/// Texture format
/// </summary>
public TextureFormat Format;
/// <summary>
/// Number of mip levels
/// </summary>
public uint MipLevels;
/// <summary>
/// Texture usage flags
/// </summary>
public TextureUsage Usage;
public TextureDesc(uint width, uint height, TextureFormat format, uint mipLevels = 1, TextureUsage usage = TextureUsage.ShaderResource)
{
Width = width;
Height = height;
Format = format;
MipLevels = mipLevels;
Usage = usage;
}
}
/// <summary>
/// Buffer description
/// </summary>
public struct BufferDesc
{
/// <summary>
/// Size of the buffer in bytes
/// </summary>
public ulong Size;
/// <summary>
/// Buffer usage flags
/// </summary>
public BufferUsage Usage;
/// <summary>
/// Memory type for the buffer
/// </summary>
public MemoryType MemoryType;
public BufferDesc(ulong size, BufferUsage usage, MemoryType memoryType = MemoryType.Default)
{
Size = size;
Usage = usage;
MemoryType = memoryType;
}
}
/// <summary>
/// Texture usage flags
/// </summary>
[Flags]
public enum TextureUsage
{
None = 0,
ShaderResource = 1 << 0,
RenderTarget = 1 << 1,
DepthStencil = 1 << 2,
UnorderedAccess = 1 << 3
}
/// <summary>
/// Memory types for resources
/// </summary>
public enum MemoryType
{
Default, // GPU memory
Upload, // CPU-to-GPU memory
Readback // GPU-to-CPU memory
}

View File

@@ -0,0 +1,43 @@
using Ghost.Graphics.RHI;
namespace Ghost.Graphics.RHI;
/// <summary>
/// High-level renderer interface that uses RHI abstractions
/// </summary>
public interface IRenderer : IDisposable
{
/// <summary>
/// Sets the render target for this renderer
/// </summary>
/// <param name="renderTarget">Render target to render into</param>
void SetRenderTarget(IRenderTarget? renderTarget);
/// <summary>
/// Sets the swap chain for this renderer
/// </summary>
/// <param name="swapChain">Swap chain for presentation</param>
void SetSwapChain(ISwapChain? swapChain);
/// <summary>
/// Executes any pending resize operations
/// </summary>
void ExecutePendingResize();
/// <summary>
/// Renders a frame
/// </summary>
void Render();
/// <summary>
/// Requests a resize operation
/// </summary>
/// <param name="width">New width</param>
/// <param name="height">New height</param>
void RequestResize(uint width, uint height);
/// <summary>
/// Waits for the GPU to complete all work
/// </summary>
void WaitIdle();
}

View File

@@ -0,0 +1,136 @@
namespace Ghost.Graphics.RHI;
/// <summary>
/// Base interface for all graphics resources
/// </summary>
public interface IResource : IDisposable
{
/// <summary>
/// Current resource state
/// </summary>
ResourceState CurrentState { get; }
/// <summary>
/// Resource name for debugging
/// </summary>
string Name { get; set; }
/// <summary>
/// Size of the resource in bytes
/// </summary>
ulong Size { get; }
}
/// <summary>
/// Texture resource interface
/// </summary>
public interface ITexture : IResource
{
/// <summary>
/// Width of the texture in pixels
/// </summary>
uint Width { get; }
/// <summary>
/// Height of the texture in pixels
/// </summary>
uint Height { get; }
/// <summary>
/// Texture format
/// </summary>
TextureFormat Format { get; }
/// <summary>
/// Number of mip levels
/// </summary>
uint MipLevels { get; }
}
/// <summary>
/// Buffer resource interface
/// </summary>
public interface IBuffer : IResource
{
/// <summary>
/// Buffer usage type
/// </summary>
BufferUsage Usage { get; }
/// <summary>
/// Maps the buffer for CPU access
/// </summary>
/// <returns>Pointer to mapped memory</returns>
unsafe void* Map();
/// <summary>
/// Unmaps the buffer from CPU access
/// </summary>
void Unmap();
}
/// <summary>
/// Render target interface for rendering operations
/// Supports either color OR depth rendering, not both
/// </summary>
public interface IRenderTarget : IDisposable
{
/// <summary>
/// Width of the render target
/// </summary>
uint Width { get; }
/// <summary>
/// Height of the render target
/// </summary>
uint Height { get; }
/// <summary>
/// Type of render target (color or depth)
/// </summary>
RenderTargetType Type { get; }
/// <summary>
/// Gets the target texture (either color or depth based on Type)
/// </summary>
ITexture Target { get; }
}
/// <summary>
/// Type of render target
/// </summary>
public enum RenderTargetType
{
Color,
Depth
}
/// <summary>
/// Texture format enumeration
/// </summary>
public enum TextureFormat
{
Unknown,
R8G8B8A8_UNorm,
B8G8R8A8_UNorm,
R16G16B16A16_Float,
R32G32B32A32_Float,
D24_UNorm_S8_UInt,
D32_Float
}
/// <summary>
/// Buffer usage flags
/// </summary>
[Flags]
public enum BufferUsage
{
None = 0,
Vertex = 1 << 0,
Index = 1 << 1,
Constant = 1 << 2,
Structured = 1 << 3,
Raw = 1 << 4,
Upload = 1 << 5,
Readback = 1 << 6
}

View File

@@ -0,0 +1,133 @@
using Ghost.Graphics.Contracts;
namespace Ghost.Graphics.RHI;
/// <summary>
/// Swap chain interface for presentation
/// </summary>
public interface ISwapChain : IDisposable
{
/// <summary>
/// Width of the swap chain back buffers
/// </summary>
uint Width { get; }
/// <summary>
/// Height of the swap chain back buffers
/// </summary>
uint Height { get; }
/// <summary>
/// Number of back buffers
/// </summary>
uint BufferCount { get; }
/// <summary>
/// Gets the current back buffer texture
/// </summary>
/// <returns>Current back buffer texture</returns>
ITexture GetCurrentBackBuffer();
/// <summary>
/// Presents the rendered frame
/// </summary>
/// <param name="vsync">Enable vertical synchronization</param>
void Present(bool vsync = true);
/// <summary>
/// Resizes the swap chain back buffers
/// </summary>
/// <param name="width">New width</param>
/// <param name="height">New height</param>
void Resize(uint width, uint height);
}
/// <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>
/// Number of back buffers
/// </summary>
public uint BufferCount;
/// <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)
{
Width = width;
Height = height;
Format = format;
BufferCount = bufferCount;
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
};
}
}
/// <summary>
/// Swap chain target types
/// </summary>
public enum SwapChainTargetType
{
WindowHandle,
Composition
}