Refactor rendering projects
This commit is contained in:
1285
src/Runtime/Ghost.Graphics.RHI/Common.cs
Normal file
1285
src/Runtime/Ghost.Graphics.RHI/Common.cs
Normal file
File diff suppressed because it is too large
Load Diff
22
src/Runtime/Ghost.Graphics.RHI/Ghost.Graphics.RHI.csproj
Normal file
22
src/Runtime/Ghost.Graphics.RHI/Ghost.Graphics.RHI.csproj
Normal file
@@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ghost.Core\Ghost.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
6
src/Runtime/Ghost.Graphics.RHI/ICommandAllocator.cs
Normal file
6
src/Runtime/Ghost.Graphics.RHI/ICommandAllocator.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public interface ICommandAllocator : IDisposable
|
||||
{
|
||||
void Reset();
|
||||
}
|
||||
204
src/Runtime/Ghost.Graphics.RHI/ICommandBuffer.cs
Normal file
204
src/Runtime/Ghost.Graphics.RHI/ICommandBuffer.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
// TODO: Add ICommandAllocator support for thread local command buffers. We often use one allocator for multiple command buffers in a single frame.
|
||||
|
||||
/// <summary>
|
||||
/// D3D12-style command buffer interface for recording rendering commands
|
||||
/// </summary>
|
||||
public interface ICommandBuffer : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the space of the command buffer.
|
||||
/// </summary>
|
||||
CommandBufferType Type
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the command buffer contains any recorded commands.
|
||||
/// </summary>
|
||||
bool IsEmpty
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
string Name
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begins recording commands into this command buffer
|
||||
/// </summary>
|
||||
void Begin(ICommandAllocator allocator);
|
||||
|
||||
/// <summary>
|
||||
/// Ends recording commands and prepares for submission
|
||||
/// </summary>
|
||||
Result End();
|
||||
|
||||
/// <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>
|
||||
/// Sets the optional render targets and optional depth Target for subsequent rendering operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To specify no render targets, provide an empty span for <paramref name="renderTargets"/>.
|
||||
/// Use <see cref="Handle{Texture}.Invalid"/> for <paramref name="depthTarget"/> if no depth Target is required.
|
||||
/// </remarks>
|
||||
/// <param name="renderTargets">A read-only span of handles to textures that will be used as render targets.
|
||||
/// The order of handles determines the order in which render targets are bound.</param>
|
||||
/// <param name="depthTarget">A handle to the texture to be used as the depth Target. Specify a invalid handle if no depth Target is required.</param>
|
||||
void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget);
|
||||
|
||||
void ClearRenderTargetView(Handle<Texture> renderTarget, Color128 clearColor);
|
||||
|
||||
void ClearDepthStencilView(Handle<Texture> depthStencil, bool inlcludeDepth, bool includeStencil, float clearDepth = 1.0f, byte clearStencil = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Begins a render pass with the specified render Target
|
||||
/// </summary>
|
||||
/// <param name="rtDescs">Render Target descriptions</param>
|
||||
/// <param name="depthDesc">Depth stencil description</param>
|
||||
/// <param name="allowUAVWrites">Whether UAV writes are allowed during the render pass</param>
|
||||
void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false);
|
||||
|
||||
/// <summary>
|
||||
/// Ends the current render pass
|
||||
/// </summary>
|
||||
void EndRenderPass();
|
||||
|
||||
// TODO: Enhanced barriers.
|
||||
|
||||
/// <summary>
|
||||
/// Inserts multiple resource barriers.
|
||||
/// </summary>
|
||||
/// <param name="barrierDescs">Resource barrier descriptions</param>
|
||||
void ResourceBarrier(params ReadOnlySpan<BarrierDesc> barrierDescs);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the pipeline state object
|
||||
/// </summary>
|
||||
/// <param name="pipelineKey">Pipeline state to set</param>
|
||||
void SetPipelineState(Key128<GraphicsPipeline> pipelineKey);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the constant buffer view for the specified slot in the graphics pipeline.
|
||||
/// </summary>
|
||||
/// <param name="slot">The zero-based index of the slot to bind the constant buffer view to.</param>
|
||||
/// <param name="buffer">A graphics buffer to use as the constant buffer view.</param>
|
||||
void SetConstantBufferView(uint slot, Handle<GraphicsBuffer> buffer);
|
||||
|
||||
/// <summary>
|
||||
/// Binds a vertex buffer to the specified slot for subsequent draw calls.
|
||||
/// </summary>
|
||||
/// <param name="slot">The vertex buffer slot to bind to.</param>
|
||||
/// <param name="buffer">The handle to the graphics buffer containing vertex data.</param>
|
||||
/// <param name="offset">The Offset in bytes from the start of the buffer.</param>
|
||||
void SetVertexBuffer(uint slot, Handle<GraphicsBuffer> buffer, ulong offset = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Binds an index buffer for indexed drawing.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The handle to the graphics buffer containing index data.</param>
|
||||
/// <param name="type">The space of indices (e.g., 16-bit or 32-bit).</param>
|
||||
/// <param name="offset">The Offset in bytes from the start of the buffer.</param>
|
||||
void SetIndexBuffer(Handle<GraphicsBuffer> buffer, IndexType type, ulong offset = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the primitive topology to be used for subsequent drawing operations.
|
||||
/// </summary>
|
||||
/// <param name="topology">The primitive topology that determines how the input vertices are interpreted during rendering.</param>
|
||||
void SetPrimitiveTopology(PrimitiveTopology topology);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a 32-bit constant value in the graphics root signature at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="rootIndex">The zero-based index of the root parameter in the graphics root signature to set the constant for.</param>
|
||||
/// <param name="constantBuffer">A read-only span containing the 32-bit constant values to set.</param>
|
||||
/// <param name="offsetIn32Bits">The Offset, in 32-bit values, from the start of the root parameter where the constants will be set.</param>
|
||||
void SetGraphicsRoot32Constants(uint rootIndex, ReadOnlySpan<uint> constantBuffer, uint offsetIn32Bits = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Issues a non-indexed draw call.
|
||||
/// </summary>
|
||||
/// <param name="vertexCount">Number of vertices to draw.</param>
|
||||
/// <param name="instanceCount">Number of instances to draw.</param>
|
||||
/// <param name="startVertex">Index of the first vertex to draw.</param>
|
||||
/// <param name="startInstance">Index of the first instance to draw.</param>
|
||||
void Draw(uint vertexCount, uint instanceCount = 1, uint startVertex = 0, uint startInstance = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Issues an indexed draw call.
|
||||
/// </summary>
|
||||
/// <param name="indexCount">Number of indices to draw.</param>
|
||||
/// <param name="instanceCount">Number of instances to draw.</param>
|
||||
/// <param name="startIndex">Index of the first index to draw.</param>
|
||||
/// <param name="baseVertex">Value added to each index before indexing the vertex buffer.</param>
|
||||
/// <param name="startInstance">Index of the first instance to draw.</param>
|
||||
void DrawIndexed(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 DispatchCompute(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ);
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches mesh shader 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 DispatchMesh(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ);
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches ray tracing threads
|
||||
/// </summary>
|
||||
// TODO: This method is not supported yet.
|
||||
void DispatchRay();
|
||||
|
||||
/// <summary>
|
||||
/// Uploads the specified data to the buffer represented by the given handle.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The unmanaged Value space of the elements to upload to the buffer.</typeparam>
|
||||
/// <param name="buffer">A handle to the buffer that will receive the uploaded data.</param>
|
||||
/// <param name="data">A read-only span containing the data to upload to the buffer. The span must contain elements of space
|
||||
/// <typeparamref name="T"/>.</param>
|
||||
void UploadBuffer<T>(Handle<GraphicsBuffer> buffer, params ReadOnlySpan<T> data)
|
||||
where T : unmanaged;
|
||||
|
||||
/// <summary>
|
||||
/// Uploads texture data to the specified texture resource starting at the given subresource index.
|
||||
/// </summary>
|
||||
/// <param name="texture">The texture resource to which the subresource data will be uploaded. Must be a valid, initialized texture handle.</param>
|
||||
/// <param name="subresources">A reference to the structure containing the subresource data to upload. The data must match the Format and layout expected by the texture.</param>
|
||||
/// Must be greater than zero and not exceed the remaining subresources in the texture.</param>
|
||||
void UploadTexture(Handle<Texture> texture, params ReadOnlySpan<SubResourceData> subresources);
|
||||
|
||||
/// <summary>
|
||||
/// Copies a specified number of bytes from the source graphics buffer to the destination graphics buffer.
|
||||
/// </summary>
|
||||
/// <param name="dest">The handle to the destination graphics buffer where data will be written.</param>
|
||||
/// <param name="src">The handle to the source graphics buffer from which data will be read.</param>
|
||||
/// <param name="destOffset">The byte Offset in the destination buffer at which to begin writing. Must be zero or greater.</param>
|
||||
/// <param name="srcOffset">The byte Offset in the source buffer at which to begin reading. Must be zero or greater.</param>
|
||||
/// <param name="numBytes">The number of bytes to copy. If zero, copies the remaining bytes from the source buffer starting at <paramref name="srcOffset"/>.</param>
|
||||
void CopyBuffer(Handle<GraphicsBuffer> dest, Handle<GraphicsBuffer> src, ulong destOffset = 0, ulong srcOffset = 0, ulong numBytes = 0);
|
||||
}
|
||||
51
src/Runtime/Ghost.Graphics.RHI/ICommandQueue.cs
Normal file
51
src/Runtime/Ghost.Graphics.RHI/ICommandQueue.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
/// <summary>
|
||||
/// Command queue interface
|
||||
/// </summary>
|
||||
public interface ICommandQueue : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of commands this queue can execute
|
||||
/// </summary>
|
||||
public CommandQueueType Type
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Submits a single command buffer for execution
|
||||
/// </summary>
|
||||
/// <param name="commandBuffer">Command buffer to submit</param>
|
||||
public void Submit(ICommandBuffer commandBuffer);
|
||||
|
||||
/// <summary>
|
||||
/// Submits multiple command buffers for execution
|
||||
/// </summary>
|
||||
/// <param name="commandBuffers">Command buffers to submit</param>
|
||||
public void Submit(params ReadOnlySpan<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>
|
||||
public ulong Signal(ulong value);
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the fence to reach the specified Value
|
||||
/// </summary>
|
||||
/// <param name="value">Value to wait for</param>
|
||||
public void WaitForValue(ulong value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last completed fence Value
|
||||
/// </summary>
|
||||
/// <returns>Last completed fence Value</returns>
|
||||
public ulong GetCompletedValue();
|
||||
|
||||
/// <summary>
|
||||
/// Waits until all submitted commands have finished executing
|
||||
/// </summary>
|
||||
public void WaitIdle();
|
||||
}
|
||||
78
src/Runtime/Ghost.Graphics.RHI/IGraphicsEngine.cs
Normal file
78
src/Runtime/Ghost.Graphics.RHI/IGraphicsEngine.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public interface IGraphicsEngine : IDisposable
|
||||
{
|
||||
IRenderDevice Device
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
IShaderCompiler ShaderCompiler
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
IPipelineLibrary PipelineLibrary
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
IResourceDatabase ResourceDatabase
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
IResourceAllocator ResourceAllocator
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a renderer for drawing graphical content.
|
||||
/// </summary>
|
||||
/// <returns>An object that implements the IRenderer interface, which can be used to render graphics.</returns>
|
||||
IRenderer CreateRenderer();
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified renderer from the collection of active renderers.
|
||||
/// </summary>
|
||||
/// <param name="renderer">The renderer instance to remove.</param>
|
||||
void RemoveRenderer(IRenderer renderer);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all registered renderers from the collection.
|
||||
/// </summary>
|
||||
/// <remarks>Call this method to reset the renderer collection to an empty state. After calling this
|
||||
/// method, no renderers will be available until new ones are added.</remarks>
|
||||
void ClearRenderers();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new command allocator for the specified command buffer space.
|
||||
/// </summary>
|
||||
/// <param name="type">The space of command buffer for which to create the allocator. The default is CommandBufferType.Graphics.</param>
|
||||
/// <returns>An <see cref="ICommandAllocator"/> instance configured for the specified command buffer space.</returns>
|
||||
ICommandAllocator CreateCommandAllocator(CommandBufferType type = CommandBufferType.Graphics);
|
||||
|
||||
/// <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>
|
||||
/// Renders the current frame.
|
||||
/// </summary>
|
||||
/// <param name="commandAllocator">Command allocator to use for rendering</param>
|
||||
/// <returns>Result of the rendering operation</returns>
|
||||
Result RenderFrame(ICommandAllocator commandAllocator);
|
||||
}
|
||||
15
src/Runtime/Ghost.Graphics.RHI/IPipelineLibrary.cs
Normal file
15
src/Runtime/Ghost.Graphics.RHI/IPipelineLibrary.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public interface IPipelineLibrary : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Load pipeline library from disk.
|
||||
/// </summary>
|
||||
/// <param name="filePath">File path. If null, load default library.</param>
|
||||
void InitializeLibrary(string? filePath);
|
||||
void SaveLibraryToDisk(string filePath);
|
||||
bool HasPipeline(Key128<GraphicsPipeline> key);
|
||||
Result<Key128<GraphicsPipeline>> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled);
|
||||
}
|
||||
49
src/Runtime/Ghost.Graphics.RHI/IRenderDevice.cs
Normal file
49
src/Runtime/Ghost.Graphics.RHI/IRenderDevice.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
[Flags]
|
||||
public enum FeatureSupport
|
||||
{
|
||||
None = 0,
|
||||
RayTracing = 1 << 0,
|
||||
VariableRateShading = 1 << 1,
|
||||
MeshShaders = 1 << 2,
|
||||
SamplerFeedback = 1 << 3,
|
||||
BindlessResources = 1 << 4,
|
||||
WorkGraphs = 1 << 5,
|
||||
AliasBuffersAndTextures = 1 << 6,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// D3D12-native render device interface for creating graphics resources
|
||||
/// </summary>
|
||||
public interface IRenderDevice : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Graphics command queue for rendering operations
|
||||
/// </summary>
|
||||
public ICommandQueue GraphicsQueue
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute command queue for compute shader operations
|
||||
/// </summary>
|
||||
public ICommandQueue ComputeQueue
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy command queue for data transfer operations
|
||||
/// </summary>
|
||||
public ICommandQueue CopyQueue
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public FeatureSupport FeatureSupport
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
41
src/Runtime/Ghost.Graphics.RHI/IRenderOutput.cs
Normal file
41
src/Runtime/Ghost.Graphics.RHI/IRenderOutput.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public interface IRenderOutput
|
||||
{
|
||||
ViewportDesc Viewport
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
RectDesc Scissor
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a handle to the current render target texture.
|
||||
/// </summary>
|
||||
/// <returns>A handle to the texture that is currently set as the render target.</returns>
|
||||
Handle<Texture> GetRenderTarget();
|
||||
|
||||
/// <summary>
|
||||
/// Begins a rendering operation using the specified command buffer. Typically this will include resource barriers,
|
||||
/// </summary>
|
||||
/// <param name="cmd">The command buffer that records rendering commands.</param>
|
||||
///
|
||||
void BeginRender(ICommandBuffer cmd);
|
||||
/// <summary>
|
||||
/// Finalizes the rendering process using the specified command buffer.
|
||||
/// </summary>
|
||||
/// <param name="cmd">The command buffer that contains the rendering commands to be finalized.</param>
|
||||
void EndRender(ICommandBuffer cmd);
|
||||
|
||||
/// <summary>
|
||||
/// Displays the current frame to the output device or screen.
|
||||
/// </summary>
|
||||
/// <remarks>Call this method after rendering operations to present the rendered content. The exact
|
||||
/// behavior may depend on the underlying graphics implementation or device.</remarks>
|
||||
void Present();
|
||||
}
|
||||
47
src/Runtime/Ghost.Graphics.RHI/IRenderSystem.cs
Normal file
47
src/Runtime/Ghost.Graphics.RHI/IRenderSystem.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public interface IFenceSynchronizer
|
||||
{
|
||||
uint CPUFenceValue
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
uint GPUFenceValue
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
uint FrameIndex
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
uint MaxFrameLatency
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
bool WaitForGPUReady(int timeOut = -1);
|
||||
void SignalCPUReady();
|
||||
void WaitIdle();
|
||||
}
|
||||
|
||||
public interface IRenderSystem : IFenceSynchronizer, IDisposable
|
||||
{
|
||||
IGraphicsEngine GraphicsEngine
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
bool IsRunning
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize);
|
||||
}
|
||||
37
src/Runtime/Ghost.Graphics.RHI/IRenderer.cs
Normal file
37
src/Runtime/Ghost.Graphics.RHI/IRenderer.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public readonly struct RenderContext
|
||||
{
|
||||
public ICommandBuffer CommandBuffer { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// High-level renderer interface that uses RHI abstractions
|
||||
/// </summary>
|
||||
public interface IRenderer : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the render output target for this renderer.
|
||||
/// </summary>
|
||||
IRenderOutput? RenderOutput
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The function that performs the actual rendering operations. Skip rendering if this is null.
|
||||
/// </summary>
|
||||
Func<RenderContext, Error>? RenderFunc
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders a frame
|
||||
/// </summary>
|
||||
/// <param name="commandAllocator">Command allocator to use for rendering</param>
|
||||
/// <returns>Result of the rendering operation</returns>
|
||||
Result Render(ICommandAllocator commandAllocator);
|
||||
}
|
||||
140
src/Runtime/Ghost.Graphics.RHI/IResourceAllocator.cs
Normal file
140
src/Runtime/Ghost.Graphics.RHI/IResourceAllocator.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public enum ResourceAllocationType
|
||||
{
|
||||
Default,
|
||||
Temporary,
|
||||
Suballocation,
|
||||
}
|
||||
|
||||
public struct CreationOptions
|
||||
{
|
||||
public ResourceAllocationType AllocationType
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public Handle<GPUResource> Heap
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public ulong Offset
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
public enum HeapType
|
||||
{
|
||||
Default,
|
||||
Upload,
|
||||
Readback
|
||||
}
|
||||
|
||||
public enum HeapFlags
|
||||
{
|
||||
None = 0,
|
||||
AllowBuffers,
|
||||
AllowTextures,
|
||||
AllowRTAndDS,
|
||||
AlowBufferAndTexture,
|
||||
}
|
||||
|
||||
public struct AllocationDesc
|
||||
{
|
||||
public ulong Size
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public ulong Alignment
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public HeapType HeapType
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public HeapFlags HeapFlags
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct ResourceSizeInfo
|
||||
{
|
||||
public ulong Size
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public ulong Alignment
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IResourceAllocator : IDisposable
|
||||
{
|
||||
ResourceSizeInfo GetSizeInfo(ResourceDesc desc);
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a block of memory on the GPU
|
||||
/// </summary>
|
||||
/// <param name="desc">Allocation description</param>
|
||||
/// <param name="name">Debug name of the allocation</param>
|
||||
/// <returns>An <see cref="Handle{GPUResource}"/> point to the allocated memory</returns>
|
||||
Handle<GPUResource> Allocate(ref readonly AllocationDesc desc, string name);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a texture resource
|
||||
/// </summary>
|
||||
/// <param name="desc">Texture description</param>
|
||||
/// <param name="name">Debug name of the resource</param>
|
||||
/// <param name="options">Additional options of the resource allocation</param>
|
||||
/// <returns>An <see cref="Handle{Texture}"/> point to the resource</returns>
|
||||
Handle<Texture> CreateTexture(ref readonly TextureDesc desc, string name, CreationOptions options = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a render Target for off-screen rendering
|
||||
/// </summary>
|
||||
/// <param name="desc">Render Target description</param>
|
||||
/// <param name="name">Debug name of the resource</param>
|
||||
/// <param name="options">Additional options of the resource allocation</param>
|
||||
/// <returns>An <see cref="Handle{Texture}"/> point to the resource</returns>
|
||||
Handle<Texture> CreateRenderTarget(ref readonly RenderTargetDesc desc, string name, CreationOptions options = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a buffer resource
|
||||
/// </summary>
|
||||
/// <param name="desc">Buffer description</param>
|
||||
/// <param name="name">Debug name of the resource</param>
|
||||
/// <param name="options">Additional options of the resource allocation</param>
|
||||
/// <returns>An <see cref="Handle{GraphicsBuffer}"/> point to the resource</returns>
|
||||
Handle<GraphicsBuffer> CreateBuffer(ref readonly BufferDesc desc, string name, CreationOptions options = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a temporary upload buffer of the specified size in bytes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method has been optimized for frequent calls during frame updates. It efficiently manages memory to minimize fragmentation and overhead.
|
||||
/// </remarks>
|
||||
/// <param name="sizeInBytes">The size of the upload buffer to create, in bytes.</param>
|
||||
/// <param name="offset">The offset within the upload buffer where the allocation begins.</param>
|
||||
/// <returns>An <see cref="Handle{GraphicsBuffer}"/> pointing to the created upload buffer.</returns>
|
||||
Handle<GraphicsBuffer> CreateTempUploadBuffer(ulong sizeInBytes, out ulong offset);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new sampler object using the specified sampler description.
|
||||
/// </summary>
|
||||
/// <param name="desc">A read-only reference to a <see cref="SamplerDesc"/> structure that defines the properties of the sampler to be created.</param>
|
||||
/// <returns>An <see cref="Identifier{Sampler}"/> that uniquely identifies the created sampler object.</returns>
|
||||
Identifier<Sampler> CreateSampler(ref readonly SamplerDesc desc);
|
||||
}
|
||||
131
src/Runtime/Ghost.Graphics.RHI/IResourceDatabase.cs
Normal file
131
src/Runtime/Ghost.Graphics.RHI/IResourceDatabase.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public interface IResourceReleasable
|
||||
{
|
||||
/// <summary>
|
||||
/// A method to release GPU resources.
|
||||
/// </summary>
|
||||
void ReleaseResource(IResourceDatabase database);
|
||||
}
|
||||
|
||||
public struct ResourceBarrierData
|
||||
{
|
||||
public BarrierLayout layout;
|
||||
public BarrierAccess access;
|
||||
public BarrierSync sync;
|
||||
|
||||
public ResourceBarrierData(BarrierLayout layout, BarrierAccess access, BarrierSync sync)
|
||||
{
|
||||
this.layout = layout;
|
||||
this.access = access;
|
||||
this.sync = sync;
|
||||
}
|
||||
}
|
||||
|
||||
public enum BindlessAccess
|
||||
{
|
||||
ShaderResource,
|
||||
ConstantBuffer,
|
||||
UnorderedAccess,
|
||||
}
|
||||
|
||||
// TODO: Consider adding methods for resource enumeration, statistics, and bulk operations.
|
||||
// TODO: Consider adding async resource loading and streaming support.
|
||||
// TODO: Mesh, Material, Shader management could be separated into their own interfaces for better modularity because they are not bound to specific graphics API.
|
||||
public interface IResourceDatabase : IDisposable
|
||||
{
|
||||
/*
|
||||
/// <summary>
|
||||
/// Imports an external unmanaged resource and returns a handle for use within the resource management system.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The space of the unmanaged resource pointer to import.</typeparam>
|
||||
/// <param name="resourcePtr">A pointer to the external unmanaged resource to be imported. Must remain valid for the duration of the resource's usage.</param>
|
||||
/// <param name="initialState">The initial state to assign to the imported resource.</param>
|
||||
/// <returns>A handle representing the imported resource, which can be used for subsequent operations.</returns>
|
||||
unsafe Handle<GPUResource> ImportExternalResource<T>(T resourcePtr, ResourceState initialState, string? name = null)
|
||||
where T : unmanaged;
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a resource with the specified handle exists in the database.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the resource to check for existence.</param>
|
||||
bool HasResource(Handle<GPUResource> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the current barrier data of the specified resource.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle that uniquely identifies the resource.</param>
|
||||
/// <returns>A ResourceBarrierData value representing the current barrier state.</returns>
|
||||
Result<ResourceBarrierData, Error> GetResourceBarrierData(Handle<GPUResource> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the barrier data of the specified resource handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle that identifies the resource.</param>
|
||||
/// <param name="data">The new barrier data.</param>
|
||||
/// <returns>An Error indicating the success or failure of the operation.</returns>
|
||||
Error SetResourceBarrierData(Handle<GPUResource> handle, ResourceBarrierData data);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the description of a GPU resource associated with the specified handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">A handle that identifies the GPU resource for which to obtain the description. Must reference a valid resource.</param>
|
||||
/// <returns>A ResourceDesc structure containing details about the specified GPU resource.</returns>
|
||||
Result<ResourceDesc, Error> GetResourceDescription(Handle<GPUResource> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the bindless index associated with the specified GPU resource handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">A handle to the GPU resource for which to obtain the bindless index. Must reference a valid, currently registered resource.</param>
|
||||
/// <param name="access">The type of bindless access for which to obtain the index.</param>
|
||||
/// <returns>The bindless index corresponding to the specified GPU resource handle. ~0 if the resource does not support bindless access or is not found.</returns>
|
||||
uint GetBindlessIndex(Handle<GPUResource> handle, BindlessAccess access = BindlessAccess.ShaderResource);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the name of the GPU resource associated with the specified handle.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You should only use this method in debug builds or inside engine editor.
|
||||
/// </remarks>
|
||||
/// <param name="handle">A handle to the GPU resource for which to obtain the name. Must reference a valid resource.</param>
|
||||
/// <returns>The name of the GPU resource associated with the specified handle, or null if the resource does not have a name.</returns>
|
||||
string? GetResourceName(Handle<GPUResource> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the GPU resource associated with the specified handle, freeing any resources allocated to it.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the resource to be removed.</param>
|
||||
void ScheduleReleaseResource(Handle<GPUResource> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the GPU resource associated with the specified handle immediately, freeing any resources allocated to it.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the resource to be removed.</param>
|
||||
void ReleaseResourceImmediately(Handle<GPUResource> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an existing sampler identifier that matches the specified description, or creates a new one if none
|
||||
/// exists.
|
||||
/// </summary>
|
||||
/// <param name="desc">A read-only reference to a <see cref="SamplerDesc"/> structure that defines the properties of the sampler to retrieve or create.</param>
|
||||
/// <param name="id">An integer identifier to associate with the sampler.</param>
|
||||
/// <returns>An <see cref="Identifier{Sampler}"/> representing the sampler that matches the specified description.
|
||||
/// If a matching sampler does not exist, a new sampler is created and its identifier is returned.</returns>
|
||||
Identifier<Sampler> CreateSampler(ref readonly SamplerDesc desc, int id);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a sampler with the specified identifier exists.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the sampler to check for existence.</param>
|
||||
/// <returns>true if a sampler with the given identifier exists; otherwise, false.</returns>
|
||||
bool TryGetSampler(ref readonly SamplerDesc desc, out Identifier<Sampler> id);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the sampler associated with the specified identifier and frees any resources allocated to it.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the sampler to release. Must reference a valid, existing sampler.</param>
|
||||
void ReleaseSampler(Identifier<Sampler> id);
|
||||
}
|
||||
149
src/Runtime/Ghost.Graphics.RHI/IShaderCompiler.cs
Normal file
149
src/Runtime/Ghost.Graphics.RHI/IShaderCompiler.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public struct ShaderCompileResult : IDisposable
|
||||
{
|
||||
public UnsafeArray<byte> bytecode;
|
||||
public ShaderReflectionData reflectionData;
|
||||
|
||||
public readonly bool IsCreated => bytecode.IsCreated;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
bytecode.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public struct GraphicsCompiledResult : IDisposable
|
||||
{
|
||||
public ShaderCompileResult tsResult;
|
||||
public ShaderCompileResult msResult;
|
||||
public ShaderCompileResult psResult;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
tsResult.Dispose();
|
||||
msResult.Dispose();
|
||||
psResult.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public ref struct ShaderCompilationConfig
|
||||
{
|
||||
public ReadOnlySpan<string> defines;
|
||||
public ReadOnlySpan<string> includes;
|
||||
public string shaderPath;
|
||||
public string entryPoint;
|
||||
public string? injectedCode;
|
||||
public ShaderStage stage;
|
||||
public CompilerTier tier;
|
||||
public CompilerOptimizeLevel optimizeLevel;
|
||||
public CompilerOption options;
|
||||
}
|
||||
|
||||
public enum CompilerTier
|
||||
{
|
||||
Tier0,
|
||||
Tier1,
|
||||
Tier2
|
||||
}
|
||||
|
||||
public enum CompilerOptimizeLevel
|
||||
{
|
||||
O0,
|
||||
O1,
|
||||
O2,
|
||||
O3
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum CompilerOption
|
||||
{
|
||||
None = 0,
|
||||
KeepDebugInfo = 1 << 0,
|
||||
KeepReflections = 1 << 1,
|
||||
WarnAsError = 1 << 2,
|
||||
SpirvCrossCompile = 1 << 3
|
||||
}
|
||||
|
||||
public enum ShaderStage
|
||||
{
|
||||
TaskShader,
|
||||
MeshShader,
|
||||
PixelShader,
|
||||
ComputeShader
|
||||
}
|
||||
|
||||
public enum ShaderInputType
|
||||
{
|
||||
ConstantBuffer,
|
||||
Texture,
|
||||
Sampler,
|
||||
UAV,
|
||||
StructuredBuffer,
|
||||
ByteAddressBuffer,
|
||||
RWStructuredBuffer,
|
||||
RWByteAddressBuffer
|
||||
}
|
||||
|
||||
public struct ResourceBindingInfo
|
||||
{
|
||||
public string Name
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public ShaderInputType Type
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public uint BindPoint
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public uint BindCount
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public uint Space
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public uint Size
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public IReadOnlyList<CBufferPropertyInfo>? Properties
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct ShaderReflectionData
|
||||
{
|
||||
public List<ResourceBindingInfo> ResourcesBindings
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ShaderReflectionData()
|
||||
{
|
||||
ResourcesBindings = new List<ResourceBindingInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
public interface IShaderCompiler : IDisposable
|
||||
{
|
||||
Result<ShaderCompileResult> Compile(ref readonly ShaderCompilationConfig config, Allocator allocator);
|
||||
Result<GraphicsCompiledResult> CompilePass(ref readonly PassDescriptor descriptor, ref readonly ShaderCompilationConfig additionalConfig, Key64<ShaderVariant> key);
|
||||
Result<GraphicsCompiledResult, Error> LoadCompiledCache(Key64<ShaderVariant> key);
|
||||
}
|
||||
73
src/Runtime/Ghost.Graphics.RHI/ISwapChain.cs
Normal file
73
src/Runtime/Ghost.Graphics.RHI/ISwapChain.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using Ghost.Core;
|
||||
|
||||
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>
|
||||
/// Gets the horizontal scaling factor applied to the object. This is used for DPI scaling.
|
||||
/// </summary>
|
||||
float ScaleX
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vertical scale factor applied to the object. This is used for DPI scaling.
|
||||
/// </summary>
|
||||
float ScaleY
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current back buffer texture
|
||||
/// </summary>
|
||||
/// <returns>Current back buffer texture</returns>
|
||||
Handle<Texture> GetCurrentBackBuffer();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all back buffer textures
|
||||
/// </summary>
|
||||
/// <returns>AlowBufferAndTexture back buffer textures</returns>
|
||||
ReadOnlySpan<Handle<Texture>> GetBackBuffers();
|
||||
|
||||
/// <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>
|
||||
/// Sets the horizontal and vertical scaling factors for the object.
|
||||
/// </summary>
|
||||
/// <param name="scaleX">The factor by which to scale the object along the X-axis.</param>
|
||||
/// <param name="scaleY">The factor by which to scale the object along the Y-axis.</param>
|
||||
void SetScale(float scaleX, float scaleY);
|
||||
}
|
||||
127
src/Runtime/Ghost.Graphics.RHI/Keyword.cs
Normal file
127
src/Runtime/Ghost.Graphics.RHI/Keyword.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System.Runtime.Intrinsics;
|
||||
using ElementType = uint;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public unsafe struct LocalKeywordSet
|
||||
{
|
||||
private const int _DATA_ARRAY_LENGTH = 4; // 4 * 32 = 128 bits
|
||||
private const int _BITS_PER_ELEMENT = sizeof(ElementType) * 8;
|
||||
|
||||
private fixed ElementType _data[_DATA_ARRAY_LENGTH];
|
||||
|
||||
public void SetKeyword(int localIndex, bool enabled)
|
||||
{
|
||||
var index = localIndex / _BITS_PER_ELEMENT;
|
||||
var bit = localIndex % _BITS_PER_ELEMENT;
|
||||
if (enabled)
|
||||
{
|
||||
_data[index] |= (uint)(1 << bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
_data[index] &= ~(uint)(1 << bit);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsKeywordEnabled(int localIndex)
|
||||
{
|
||||
var index = localIndex / _BITS_PER_ELEMENT;
|
||||
var bit = localIndex % _BITS_PER_ELEMENT;
|
||||
return (_data[index] & (uint)(1 << bit)) != 0;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
for (var i = 0; i < _DATA_ARRAY_LENGTH; i++)
|
||||
{
|
||||
_data[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public ulong GetHash64()
|
||||
{
|
||||
var hash = 14695981039346656037ul; // FNV Offset basis
|
||||
|
||||
for (var i = 0; i < _DATA_ARRAY_LENGTH; i++)
|
||||
{
|
||||
hash ^= _data[i];
|
||||
hash *= 1099511628211ul; // FNV prime
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = 17;
|
||||
for (var i = 0; i < _DATA_ARRAY_LENGTH; i++)
|
||||
{
|
||||
hash = hash * 31 + _data[i].GetHashCode();
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
public static LocalKeywordSet operator |(in LocalKeywordSet a, in LocalKeywordSet b)
|
||||
{
|
||||
var result = default(LocalKeywordSet);
|
||||
|
||||
if (Vector128<ElementType>.IsSupported)
|
||||
{
|
||||
fixed (ElementType* pDataA = a._data)
|
||||
fixed (ElementType* pDataB = b._data)
|
||||
{
|
||||
for (var i = 0; i < _DATA_ARRAY_LENGTH; i += Vector128<ElementType>.Count)
|
||||
{
|
||||
var elementOffset = (nuint)i;
|
||||
var vecA = Vector128.LoadUnsafe(ref *pDataA, elementOffset);
|
||||
var vecB = Vector128.LoadUnsafe(ref *pDataB, elementOffset);
|
||||
var vecResult = Vector128.BitwiseOr(vecA, vecB);
|
||||
vecResult.StoreUnsafe(ref result._data[0], elementOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < _DATA_ARRAY_LENGTH; i++)
|
||||
{
|
||||
result._data[i] = a._data[i] | b._data[i];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static LocalKeywordSet operator &(in LocalKeywordSet a, in LocalKeywordSet b)
|
||||
{
|
||||
var result = default(LocalKeywordSet);
|
||||
|
||||
if (Vector128<ElementType>.IsSupported)
|
||||
{
|
||||
fixed (ElementType* pDataA = a._data)
|
||||
fixed (ElementType* pDataB = b._data)
|
||||
{
|
||||
for (var i = 0; i < _DATA_ARRAY_LENGTH; i += Vector128<ElementType>.Count)
|
||||
{
|
||||
var elementOffset = (nuint)i;
|
||||
var vecA = Vector128.LoadUnsafe(ref *pDataA, elementOffset);
|
||||
var vecB = Vector128.LoadUnsafe(ref *pDataB, elementOffset);
|
||||
var vecResult = Vector128.BitwiseAnd(vecA, vecB);
|
||||
vecResult.StoreUnsafe(ref result._data[0], elementOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
for (var i = 0; i < _DATA_ARRAY_LENGTH; i++)
|
||||
{
|
||||
result._data[i] = a._data[i] & b._data[i];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
183
src/Runtime/Ghost.Graphics.RHI/RHIUtility.cs
Normal file
183
src/Runtime/Ghost.Graphics.RHI/RHIUtility.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Core.Utilities;
|
||||
using System.IO.Hashing;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public static class RHIUtility
|
||||
{
|
||||
public const int MAX_RENDER_TARGETS = 8;
|
||||
|
||||
public static uint GetBytesPerPixel(this TextureFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
TextureFormat.R8G8B8A8_UNorm => 4,
|
||||
TextureFormat.B8G8R8A8_UNorm => 4,
|
||||
TextureFormat.R16G16B16A16_Float => 8,
|
||||
TextureFormat.R32G32B32A32_Float => 16,
|
||||
TextureFormat.D24_UNorm_S8_UInt => 4,
|
||||
TextureFormat.D32_Float => 4,
|
||||
_ => throw new NotSupportedException($"Texture format {format} is not supported."),
|
||||
};
|
||||
}
|
||||
|
||||
public static uint GetTotalBytes(this TextureDesc desc)
|
||||
{
|
||||
return desc.Format.GetBytesPerPixel() * desc.Width * desc.Height * desc.Slice;
|
||||
}
|
||||
|
||||
public static void GetSurfaceInfo(this TextureFormat format, uint width, uint height, out uint rowPitch, out uint slicePitch, out uint rowCount)
|
||||
{
|
||||
var bc = false;
|
||||
var packed = false;
|
||||
var planar = false;
|
||||
var bpe = 0u;
|
||||
|
||||
//switch (Format)
|
||||
//{
|
||||
// case Format.BC1Typeless:
|
||||
// case Format.BC1Unorm:
|
||||
// case Format.BC1UnormSrgb:
|
||||
// case Format.BC4Typeless:
|
||||
// case Format.BC4Unorm:
|
||||
// case Format.BC4Snorm:
|
||||
// bc = true;
|
||||
// bpe = 8;
|
||||
// break;
|
||||
|
||||
// case Format.BC2Typeless:
|
||||
// case Format.BC2Unorm:
|
||||
// case Format.BC2UnormSrgb:
|
||||
// case Format.BC3Typeless:
|
||||
// case Format.BC3Unorm:
|
||||
// case Format.BC3UnormSrgb:
|
||||
// case Format.BC5Typeless:
|
||||
// case Format.BC5Unorm:
|
||||
// case Format.BC5Snorm:
|
||||
// case Format.BC6HTypeless:
|
||||
// case Format.BC6HUF16:
|
||||
// case Format.BC6HSF16:
|
||||
// case Format.BC7Typeless:
|
||||
// case Format.BC7Unorm:
|
||||
// case Format.BC7UnormSrgb:
|
||||
// bc = true;
|
||||
// bpe = 16;
|
||||
// break;
|
||||
|
||||
// case Format.R8G8_B8G8Unorm:
|
||||
// case Format.G8R8_G8B8Unorm:
|
||||
// case Format.YUY2:
|
||||
// packed = true;
|
||||
// bpe = 4;
|
||||
// break;
|
||||
|
||||
// case Format.Y210:
|
||||
// case Format.Y216:
|
||||
// packed = true;
|
||||
// bpe = 8;
|
||||
// break;
|
||||
|
||||
// case Format.NV12:
|
||||
// case Format.Opaque420:
|
||||
// case Format.P208:
|
||||
// planar = true;
|
||||
// bpe = 2;
|
||||
// break;
|
||||
|
||||
// case Format.P010:
|
||||
// case Format.P016:
|
||||
// planar = true;
|
||||
// bpe = 4;
|
||||
// break;
|
||||
|
||||
// default:
|
||||
// break;
|
||||
//}
|
||||
|
||||
if (bc)
|
||||
{
|
||||
var numBlocksWide = 0u;
|
||||
if (width > 0)
|
||||
{
|
||||
numBlocksWide = Math.Max(1u, (width + 3) / 4u);
|
||||
}
|
||||
|
||||
var numBlocksHigh = 0u;
|
||||
if (height > 0)
|
||||
{
|
||||
numBlocksHigh = Math.Max(1u, (height + 3) / 4u);
|
||||
}
|
||||
|
||||
rowPitch = numBlocksWide * bpe;
|
||||
rowCount = numBlocksHigh;
|
||||
slicePitch = rowPitch * numBlocksHigh;
|
||||
}
|
||||
else if (packed)
|
||||
{
|
||||
rowPitch = ((width + 1u) >> 1) * bpe;
|
||||
rowCount = height;
|
||||
slicePitch = rowPitch * height;
|
||||
}
|
||||
else if (planar)
|
||||
{
|
||||
rowPitch = ((width + 1u) >> 1) * bpe;
|
||||
slicePitch = (rowPitch * height) + ((rowPitch * height + 1) >> 1);
|
||||
rowCount = height + ((height + 1u) >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
var bpp = GetBytesPerPixel(format) * 8;
|
||||
rowPitch = (width * bpp + 7) / 8; // round up to nearest byte
|
||||
rowCount = height;
|
||||
slicePitch = rowPitch * height;
|
||||
}
|
||||
}
|
||||
|
||||
public static Key64<ShaderPass> CreateShaderPassKey(string passID)
|
||||
{
|
||||
var passIdSpan = passID.AsSpan();
|
||||
return new Key64<ShaderPass>(XxHash3.HashToUInt64(MemoryMarshal.AsBytes(passIdSpan)));
|
||||
}
|
||||
|
||||
public static Key64<ShaderVariant> CreateShaderVariantKey(Key64<ShaderPass> passKey, ref readonly LocalKeywordSet keywords)
|
||||
{
|
||||
var passHash = passKey.Value;
|
||||
var keywordHash = keywords.GetHash64();
|
||||
return new Key64<ShaderVariant>(Hash.Hash64(passHash, keywordHash));
|
||||
}
|
||||
|
||||
public static unsafe Key128<GraphicsPipeline> CreateGraphicsPipelineKey(Key64<ShaderVariant> shaderVariantKey, PipelineState pipelineState, PassPipelineHash 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;
|
||||
}
|
||||
|
||||
var mLo = shaderVariantKey.Value;
|
||||
var mHi = pipelineState.GetHashCode64();
|
||||
|
||||
var pPasskey = (ulong*)&passKey.value;
|
||||
var pLo = pPasskey[0];
|
||||
var pHi = pPasskey[1];
|
||||
|
||||
// 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 Key128<GraphicsPipeline>(new UInt128(lo, hi));
|
||||
}
|
||||
|
||||
public static bool TryGetString(this Key128<GraphicsPipeline> key, Span<char> destination)
|
||||
{
|
||||
return key.Value.TryFormat(destination, out var _, "X16");
|
||||
}
|
||||
}
|
||||
32
src/Runtime/Ghost.Graphics.RHI/ResourceHandle.cs
Normal file
32
src/Runtime/Ghost.Graphics.RHI/ResourceHandle.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public readonly struct GPUResource;
|
||||
public readonly struct Texture;
|
||||
public readonly struct GraphicsBuffer;
|
||||
|
||||
public readonly struct Sampler;
|
||||
|
||||
public static class ResourceHandleExtensions
|
||||
{
|
||||
public static Handle<GPUResource> AsResource(this Handle<Texture> texture)
|
||||
{
|
||||
return new Handle<GPUResource>(texture.ID, texture.Generation);
|
||||
}
|
||||
|
||||
public static Handle<GPUResource> AsResource(this Handle<GraphicsBuffer> buffer)
|
||||
{
|
||||
return new Handle<GPUResource>(buffer.ID, buffer.Generation);
|
||||
}
|
||||
|
||||
public static Handle<Texture> AsTexture(this Handle<GPUResource> resource)
|
||||
{
|
||||
return new Handle<Texture>(resource.ID, resource.Generation);
|
||||
}
|
||||
|
||||
public static Handle<GraphicsBuffer> AsGraphicsBuffer(this Handle<GPUResource> resource)
|
||||
{
|
||||
return new Handle<GraphicsBuffer>(resource.ID, resource.Generation);
|
||||
}
|
||||
}
|
||||
75
src/Runtime/Ghost.Graphics.RHI/RootSignatureLayout.cs
Normal file
75
src/Runtime/Ghost.Graphics.RHI/RootSignatureLayout.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
/// <summary>
|
||||
/// The layout of the root signature is:
|
||||
/// <list space="bullet">
|
||||
/// <item>
|
||||
/// Global buffer (b0)
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// Per-view buffer (b1)
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// Per-object buffer (b2)
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// Per-material buffer (b3)
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// Descriptor table for bindless textures (t0)
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// Descriptor table for bindless samplers (s0)
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public static class RootSignatureLayout
|
||||
{
|
||||
// public const int GLOBAL_BUFFER_SLOT = 0;
|
||||
// public const int PER_VIEW_BUFFER_SLOT = 1;
|
||||
// public const int PER_OBJECT_BUFFER_SLOT = 2;
|
||||
// public const int PER_MATERIAL_BUFFER_SLOT = 3;
|
||||
|
||||
// public const int TEXTURE_HEAP_SLOT = 0;
|
||||
// public const int SAMPLER_HEAP_SLOT = 0;
|
||||
|
||||
public const int PUSH_CONSTANT_SLOT = 0;
|
||||
|
||||
public const int ROOT_PARAMETER_COUNT = 1;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 16)]
|
||||
public struct PushConstantsData
|
||||
{
|
||||
public uint globalIndex;
|
||||
public uint viewIndex;
|
||||
public uint objectIndex;
|
||||
public uint materialIndex;
|
||||
}
|
||||
|
||||
// The size should be 176 bytes (16-byte aligned)
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct PerViewData
|
||||
{
|
||||
public float4x4 viewMatrix;
|
||||
public float4x4 projectionMatrix;
|
||||
public float3 cameraPosition;
|
||||
public float nearClip;
|
||||
public float3 cameraDirection;
|
||||
public float farClip;
|
||||
public float4 screenSize; // xy: size, zw: 1/size
|
||||
};
|
||||
|
||||
// The size should be 96 bytes (16-byte aligned)
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct PerObjectData
|
||||
{
|
||||
public float4x4 localToWorld;
|
||||
public float3 worldBoundsMin;
|
||||
public uint vertexBuffer;
|
||||
public float3 worldBoundsMax;
|
||||
public uint indexBuffer;
|
||||
};
|
||||
Reference in New Issue
Block a user