feat(graphics): improve rendering pipeline and docs
- Refactor D3D12 backend and RenderGraph module - Update graphics RHI and core rendering components - Add Random.hlsl shader include - Regenerate API documentation and update user guides
This commit is contained in:
@@ -265,7 +265,7 @@ public struct Material : IResourceReleasable
|
||||
barrierData.access,
|
||||
BarrierAccess.CopyDest);
|
||||
|
||||
cmd.ResourceBarrier(desc);
|
||||
cmd.Barrier(desc);
|
||||
cmd.UploadBuffer(_cBufferCache.GpuResource, _cBufferCache.CpuData.AsSpan());
|
||||
|
||||
desc = BarrierDesc.Buffer(
|
||||
@@ -275,7 +275,7 @@ public struct Material : IResourceReleasable
|
||||
BarrierAccess.CopyDest,
|
||||
BarrierAccess.ShaderResource);
|
||||
|
||||
cmd.ResourceBarrier(desc);
|
||||
cmd.Barrier(desc);
|
||||
}
|
||||
|
||||
public void ReleaseResource(IResourceDatabase database)
|
||||
|
||||
@@ -55,6 +55,7 @@ public struct MeshletMeshData : IDisposable
|
||||
public UnsafeList<MeshletHierarchyNode> hierarchyNodes;
|
||||
public UnsafeList<uint> meshletVertices;
|
||||
public UnsafeList<uint> meshletTriangles;
|
||||
public int meshletCount;
|
||||
public int lodLevelCount;
|
||||
public int materialSlotCount;
|
||||
|
||||
@@ -236,6 +237,8 @@ public struct Mesh : IResourceReleasable
|
||||
|
||||
// 3. Build
|
||||
MeshletUtility.Build(config, clodMesh, Unsafe.AsPointer(ref this), MeshletOutputCallback);
|
||||
|
||||
_meshletData.meshletCount = _meshletData.meshlets.Count;
|
||||
}
|
||||
|
||||
private static unsafe int MeshletOutputCallback(void* context, ClodGroup group, ReadOnlyUnsafeCollection<ClodCluster> clusters)
|
||||
|
||||
@@ -12,7 +12,7 @@ internal class SwapChainRenderOutput : IRenderOutput
|
||||
get; set;
|
||||
}
|
||||
|
||||
public RectDesc Scissor
|
||||
public ScissorRectDesc Scissor
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
@@ -22,7 +22,7 @@ internal class SwapChainRenderOutput : IRenderOutput
|
||||
_swapChain = swapChain;
|
||||
|
||||
Viewport = new ViewportDesc { Width = swapChain.Width, Height = swapChain.Height, MinDepth = 0, MaxDepth = 1 };
|
||||
Scissor = new RectDesc { Right = swapChain.Width, Bottom = swapChain.Height };
|
||||
Scissor = new ScissorRectDesc { Right = swapChain.Width, Bottom = swapChain.Height };
|
||||
}
|
||||
|
||||
public Handle<Texture> GetRenderTarget()
|
||||
@@ -37,7 +37,7 @@ internal class SwapChainRenderOutput : IRenderOutput
|
||||
BarrierAccess.NoAccess, BarrierAccess.RenderTarget,
|
||||
BarrierLayout.Present, BarrierLayout.RenderTarget);
|
||||
|
||||
cmd.ResourceBarrier(barrierDesc);
|
||||
cmd.Barrier(barrierDesc);
|
||||
}
|
||||
|
||||
public void EndRender(ICommandBuffer cmd)
|
||||
@@ -47,7 +47,7 @@ internal class SwapChainRenderOutput : IRenderOutput
|
||||
BarrierAccess.RenderTarget, BarrierAccess.NoAccess,
|
||||
BarrierLayout.RenderTarget, BarrierLayout.Present);
|
||||
|
||||
cmd.ResourceBarrier(barrierDesc);
|
||||
cmd.Barrier(barrierDesc);
|
||||
}
|
||||
|
||||
public void Present()
|
||||
@@ -65,7 +65,7 @@ internal class TextureRenderOutput : IRenderOutput
|
||||
get; set;
|
||||
}
|
||||
|
||||
public RectDesc Scissor
|
||||
public ScissorRectDesc Scissor
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
@@ -157,11 +157,12 @@ public struct Frustum
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
public struct RenderView
|
||||
{
|
||||
public float4x4 viewMatrix;
|
||||
public float4x4 projectionMatrix;
|
||||
public float3 position;
|
||||
public float4x4 localToWorld;
|
||||
//public float4x4 viewMatrix;
|
||||
//public float4x4 projectionMatrix;
|
||||
//public float3 position;
|
||||
|
||||
public Frustum frustum; // 192 bytes
|
||||
//public Frustum frustum; // 192 bytes
|
||||
public float nearClipPlane;
|
||||
public float farClipPlane;
|
||||
|
||||
@@ -181,6 +182,7 @@ public unsafe struct RenderRequest: IDisposable
|
||||
{
|
||||
public RenderView view;
|
||||
|
||||
public int swapChainIndex;
|
||||
public Handle<Texture> colorTarget;
|
||||
public Handle<Texture> depthTarget;
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ public readonly unsafe ref struct RenderingContext
|
||||
data.access, newAccess);
|
||||
}
|
||||
|
||||
_directCmd.ResourceBarrier(new ReadOnlySpan<BarrierDesc>(in desc));
|
||||
_directCmd.Barrier(new ReadOnlySpan<BarrierDesc>(in desc));
|
||||
ResourceDatabase.SetResourceBarrierData(resource, new ResourceBarrierData(newLayout, newAccess, newSync));
|
||||
}
|
||||
|
||||
@@ -103,14 +103,14 @@ public readonly unsafe ref struct RenderingContext
|
||||
_directCmd.UploadBuffer(meshData.VertexBuffer, meshData.Vertices.AsSpan());
|
||||
_directCmd.UploadBuffer(meshData.IndexBuffer, meshData.Indices.AsSpan());
|
||||
|
||||
TransitionBarrier(vertexHandle, false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.VertexShading);
|
||||
TransitionBarrier(indexHandle, false, BarrierLayout.Undefined, BarrierAccess.IndexBuffer, BarrierSync.IndexInput);
|
||||
|
||||
if (staticMesh)
|
||||
{
|
||||
meshData.CookMeshlets();
|
||||
meshData.ReleaseCpuResources();
|
||||
TransitionBarrier(vertexHandle, false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.VertexShading);
|
||||
TransitionBarrier(indexHandle, false, BarrierLayout.Undefined, BarrierAccess.IndexBuffer, BarrierSync.IndexInput);
|
||||
|
||||
UploadMeshlets(mesh);
|
||||
meshData.ReleaseCpuResources();
|
||||
}
|
||||
|
||||
return mesh;
|
||||
@@ -142,7 +142,7 @@ public readonly unsafe ref struct RenderingContext
|
||||
return;
|
||||
}
|
||||
|
||||
ref readonly var meshRef = ref r.Value;
|
||||
ref var meshRef = ref r.Value;
|
||||
var vertexHandle = meshRef.VertexBuffer.AsResource();
|
||||
var indexHandle = meshRef.IndexBuffer.AsResource();
|
||||
|
||||
@@ -224,7 +224,7 @@ public readonly unsafe ref struct RenderingContext
|
||||
}
|
||||
|
||||
ref readonly var meshData = ref r.Value;
|
||||
var data = new PerObjectData
|
||||
var data = new MeshData
|
||||
{
|
||||
worldBoundsMin = meshData.BoundingBox.Min,
|
||||
worldBoundsMax = meshData.BoundingBox.Max,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
@@ -111,6 +112,7 @@ public sealed class RenderGraph : IDisposable
|
||||
var r = _resourceDatabase.GetResourceDescription(texture.AsResource());
|
||||
if (r.IsFailure)
|
||||
{
|
||||
Debug.Fail("Failed to get resource description for texture handle: " + texture);
|
||||
return Identifier<RGTexture>.Invalid;
|
||||
}
|
||||
|
||||
@@ -128,6 +130,7 @@ public sealed class RenderGraph : IDisposable
|
||||
var r = _resourceDatabase.GetResourceDescription(buffer.AsResource());
|
||||
if (r.IsFailure)
|
||||
{
|
||||
Debug.Fail("Failed to get resource description for buffer handle: " + buffer);
|
||||
return Identifier<RGBuffer>.Invalid;
|
||||
}
|
||||
|
||||
@@ -177,7 +180,7 @@ public sealed class RenderGraph : IDisposable
|
||||
/// <summary>
|
||||
/// Compiles the render graph by culling unused passes and determining resource lifetimes.
|
||||
/// </summary>
|
||||
public Error Compile(in ViewState viewState)
|
||||
public Error Compile(ViewState viewState)
|
||||
{
|
||||
if (_compiled)
|
||||
{
|
||||
|
||||
@@ -343,6 +343,11 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra
|
||||
throw new InvalidOperationException("RenderGraphBuilder must be disposed after setting up the render function.");
|
||||
}
|
||||
|
||||
if (_pass.type == RenderPassType.Raster && _pass.colorAccess[0].id.IsInvalid && _pass.depthAccess.id.IsInvalid)
|
||||
{
|
||||
throw new InvalidOperationException("Raster render pass must have at least one color or depth attachment.");
|
||||
}
|
||||
|
||||
_graph = null!;
|
||||
_pass = null!;
|
||||
_resources = null!;
|
||||
|
||||
@@ -16,13 +16,17 @@ public interface IRenderGraphContext
|
||||
|
||||
Handle<Texture> GetHistoryTexture(ReadOnlySpan<Identifier<RGTexture>> texture, int historyOffset);
|
||||
Handle<GraphicsBuffer> GetHistoryBuffer(ReadOnlySpan<Identifier<RGBuffer>> buffer, int historyOffset);
|
||||
|
||||
ICommandBuffer GetCommandBufferUnsafe();
|
||||
}
|
||||
|
||||
public interface IRasterRenderContext : IRenderGraphContext
|
||||
{
|
||||
int ActiveMeshIndexCount { get; }
|
||||
void SetViewport(ViewportDesc desc);
|
||||
void SetScissorRect(ScissorRectDesc desc);
|
||||
|
||||
void SetGlobalData(uint globalIndex, uint viewIndex);
|
||||
void SetInstanceData(uint instanceBuffer);
|
||||
void SetInstanceIndex(uint instanceIndex);
|
||||
|
||||
void SetActiveMaterial(Handle<Material> material);
|
||||
@@ -37,12 +41,11 @@ public interface IComputeRenderContext : IRenderGraphContext
|
||||
void DispatchCompute(uint3 threadGroupCount);
|
||||
}
|
||||
|
||||
public interface IUnsafeRenderContext : IRasterRenderContext, IRenderGraphContext
|
||||
public interface IUnsafeRenderContext : IRasterRenderContext, IComputeRenderContext
|
||||
{
|
||||
ICommandBuffer CommandBuffer { get; }
|
||||
}
|
||||
|
||||
internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderContext, IUnsafeRenderContext
|
||||
internal sealed class RenderGraphContext : IUnsafeRenderContext
|
||||
{
|
||||
private readonly ResourceManager _resourceManager;
|
||||
private readonly IResourceDatabase _resourceDatabase;
|
||||
@@ -61,8 +64,9 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
private Handle<GraphicsBuffer> _activePerMeshData;
|
||||
private int _activeMeshIndexCount;
|
||||
|
||||
private uint _activeGlobalIndex;
|
||||
private uint _activeViewIndex;
|
||||
private uint _activeFrameBuffer;
|
||||
private uint _activeViewBuffer;
|
||||
private uint _activeInstanceBuffer;
|
||||
private uint _activeInstanceIndex;
|
||||
|
||||
public ResourceManager ResourceManager => _resourceManager;
|
||||
@@ -70,8 +74,6 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
|
||||
public int ActiveMeshIndexCount => _activeMeshIndexCount;
|
||||
|
||||
public ICommandBuffer CommandBuffer => _commandBuffer;
|
||||
|
||||
internal RenderGraphContext(ResourceManager resourceManager, IResourceDatabase resourceDatabase, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler, RenderGraphResourceRegistry resources)
|
||||
{
|
||||
_resourceManager = resourceManager;
|
||||
@@ -105,6 +107,11 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
|
||||
public Handle<GPUResource> GetActualResource(Identifier<RGResource> resource)
|
||||
{
|
||||
if (resource.IsInvalid)
|
||||
{
|
||||
return Handle<GPUResource>.Invalid;
|
||||
}
|
||||
|
||||
return _resources.GetResource(resource).backingResource;
|
||||
}
|
||||
|
||||
@@ -150,6 +157,16 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
return GetActualBuffer(buffers[index]);
|
||||
}
|
||||
|
||||
public void SetViewport(ViewportDesc desc)
|
||||
{
|
||||
_commandBuffer.SetViewport(desc);
|
||||
}
|
||||
|
||||
public void SetScissorRect(ScissorRectDesc desc)
|
||||
{
|
||||
_commandBuffer.SetScissorRect(desc);
|
||||
}
|
||||
|
||||
public void SetActiveMaterial(Handle<Material> material)
|
||||
{
|
||||
var r = _resourceManager.GetMaterialReference(material);
|
||||
@@ -228,10 +245,15 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
_activeMeshIndexCount = mesh.IndexCount;
|
||||
}
|
||||
|
||||
public void SetGlobalData(uint globalIndex, uint viewIndex)
|
||||
public void SetGlobalData(uint frameBuffer, uint viewBuffer)
|
||||
{
|
||||
_activeGlobalIndex = globalIndex;
|
||||
_activeViewIndex = viewIndex;
|
||||
_activeFrameBuffer = frameBuffer;
|
||||
_activeViewBuffer = viewBuffer;
|
||||
}
|
||||
|
||||
public void SetInstanceData(uint instanceBuffer)
|
||||
{
|
||||
_activeInstanceBuffer = instanceBuffer;
|
||||
}
|
||||
|
||||
public void SetInstanceIndex(uint instanceIndex)
|
||||
@@ -241,14 +263,12 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
|
||||
public unsafe void DispatchMesh(uint3 threadGroupCount)
|
||||
{
|
||||
// TODO: Global, view, and instance constants
|
||||
var data = new PushConstantsData
|
||||
{
|
||||
globalIndex = _activeGlobalIndex,
|
||||
viewIndex = _activeViewIndex,
|
||||
objectIndex = _resourceDatabase.GetBindlessIndex(_activePerMeshData.AsResource()),
|
||||
frameBuffer = _activeFrameBuffer,
|
||||
viewBuffer = _activeViewBuffer,
|
||||
instanceBuffer = _activeInstanceBuffer,
|
||||
instanceIndex = _activeInstanceIndex,
|
||||
materialIndex = _resourceDatabase.GetBindlessIndex(_activePerMaterialData.AsResource()),
|
||||
};
|
||||
|
||||
var pushConstantSpan = new ReadOnlySpan<uint>(&data, sizeof(PushConstantsData) / sizeof(uint));
|
||||
@@ -260,4 +280,10 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public ICommandBuffer GetCommandBufferUnsafe()
|
||||
{
|
||||
return _commandBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Diagnostics;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
@@ -24,6 +26,59 @@ internal sealed class RenderGraphExecutor
|
||||
_context = context;
|
||||
}
|
||||
|
||||
private void SetViewport(ReadOnlySpan<RenderTargetInfo> color, DepthStencilInfo depthStencil)
|
||||
{
|
||||
// This should not happened since the compiler should have rejected any render pass with an invalid render target configuration, but just in case, we use Debug.Assert to validate our assumptions.
|
||||
Debug.Assert(color.Length > 0 || depthStencil.texture.IsValid);
|
||||
|
||||
ViewportDesc viewportDesc = default;
|
||||
ScissorRectDesc scissorDesc = default;
|
||||
|
||||
if (depthStencil.texture.IsValid)
|
||||
{
|
||||
viewportDesc = new ViewportDesc
|
||||
{
|
||||
X = 0,
|
||||
Y = 0,
|
||||
Width = _resources.GetResource(depthStencil.texture).resolvedWidth,
|
||||
Height = _resources.GetResource(depthStencil.texture).resolvedHeight,
|
||||
MinDepth = 0,
|
||||
MaxDepth = 1
|
||||
};
|
||||
|
||||
scissorDesc = new ScissorRectDesc
|
||||
{
|
||||
Left = 0,
|
||||
Top = 0,
|
||||
Right = (uint)viewportDesc.Width,
|
||||
Bottom = (uint)viewportDesc.Height
|
||||
};
|
||||
}
|
||||
else if (color[0].texture.IsValid)
|
||||
{
|
||||
viewportDesc = new ViewportDesc
|
||||
{
|
||||
X = 0,
|
||||
Y = 0,
|
||||
Width = _resources.GetResource(color[0].texture).resolvedWidth,
|
||||
Height = _resources.GetResource(color[0].texture).resolvedHeight,
|
||||
MinDepth = 0,
|
||||
MaxDepth = 1
|
||||
};
|
||||
|
||||
scissorDesc = new ScissorRectDesc
|
||||
{
|
||||
Left = 0,
|
||||
Top = 0,
|
||||
Right = (uint)viewportDesc.Width,
|
||||
Bottom = (uint)viewportDesc.Height
|
||||
};
|
||||
}
|
||||
|
||||
_context.SetViewport(viewportDesc);
|
||||
_context.SetScissorRect(scissorDesc);
|
||||
}
|
||||
|
||||
public unsafe Error Execute(
|
||||
ICommandBuffer commandBuffer,
|
||||
List<RenderGraphPassBase> compiledPasses,
|
||||
@@ -60,6 +115,9 @@ internal sealed class RenderGraphExecutor
|
||||
}
|
||||
|
||||
// Begin native render pass
|
||||
|
||||
SetViewport(nativePass.colorAttachments, nativePass.depthAttachment);
|
||||
|
||||
for (var i = 0; i < nativePass.colorAttachmentCount; i++)
|
||||
{
|
||||
var attachment = nativePass.colorAttachments[i];
|
||||
@@ -121,7 +179,10 @@ internal sealed class RenderGraphExecutor
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compute pass or standalone raster pass (not merged) or Unsafe pass
|
||||
// All the reaster pass should be merged into native render pass, so if we encounter a raster pass here, it means something went wrong during compilation.
|
||||
Debug.Assert(pass.type != RenderPassType.Raster);
|
||||
|
||||
// Compute pass or Unsafe pass
|
||||
var e = ExecuteBarriersForPass(commandBuffer, logicalPassIndex, ref barrierIndex, compiledBarriers);
|
||||
if (e != Error.None)
|
||||
{
|
||||
@@ -150,7 +211,7 @@ internal sealed class RenderGraphExecutor
|
||||
{
|
||||
if (barrierCount > 0)
|
||||
{
|
||||
cmd.ResourceBarrier(new ReadOnlySpan<BarrierDesc>(barriers, barrierCount));
|
||||
cmd.Barrier(new ReadOnlySpan<BarrierDesc>(barriers, barrierCount));
|
||||
barrierCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,21 @@ struct PixelInput
|
||||
float4 position : SV_POSITION;
|
||||
float4 color : COLOR;
|
||||
float4 uv : TEXCOORD0;
|
||||
nointerpolation uint meshletID : MESHLET_ID;
|
||||
};
|
||||
|
||||
[numthreads(128, 1, 1)] // 128 threads to cover max 64 vertices and 124 triangles
|
||||
[outputtopology("triangle")]
|
||||
void MSMain(
|
||||
uint3 groupThreadID : SV_GroupThreadID,
|
||||
uint groupID : SV_GroupID,
|
||||
uint3 groupID : SV_GroupID,
|
||||
out vertices PixelInput outVerts[64],
|
||||
out indices uint3 outTris[124])
|
||||
{
|
||||
PerObjectData perObjectData = LoadData<PerObjectData>(g_PushConstantData.perObjectBuffer, 0);
|
||||
InstanceData instanceData = LoadData<InstanceData>(g_PushConstantData.instanceBuffer, g_PushConstantData.instanceIndex);
|
||||
MeshData meshData = LoadData<MeshData>(instanceData.meshBuffer, 0);
|
||||
|
||||
ByteAddressBuffer meshletBuffer = GET_BUFFER(perObjectData.meshletBuffer);
|
||||
ByteAddressBuffer meshletBuffer = GET_BUFFER(meshData.meshletBuffer);
|
||||
Meshlet m = meshletBuffer.Load<Meshlet>(groupID.x * sizeof(Meshlet));
|
||||
|
||||
uint vertexCount = m.packedCounts & 0xFF;
|
||||
@@ -26,26 +28,27 @@ void MSMain(
|
||||
|
||||
SetMeshOutputCounts(vertexCount, triangleCount);
|
||||
|
||||
ByteAddressBuffer meshletVerticesBuffer = GET_BUFFER(perObjectData.meshletVerticesBuffer);
|
||||
ByteAddressBuffer meshletTrianglesBuffer = GET_BUFFER(perObjectData.meshletTrianglesBuffer);
|
||||
ByteAddressBuffer meshletVerticesBuffer = GET_BUFFER(meshData.meshletVerticesBuffer);
|
||||
ByteAddressBuffer meshletTrianglesBuffer = GET_BUFFER(meshData.meshletTrianglesBuffer);
|
||||
|
||||
// Write vertex output
|
||||
if (groupThreadID.x < vertexCount)
|
||||
{
|
||||
uint vertexIndex = meshletVerticesBuffer.Load((m.vertexOffset + groupThreadID.x) * 4);
|
||||
ByteAddressBuffer vertices = GET_BUFFER(perObjectData.vertexBuffer);
|
||||
ByteAddressBuffer vertices = GET_BUFFER(meshData.vertexBuffer);
|
||||
Vertex v = vertices.Load<Vertex>(vertexIndex * sizeof(Vertex));
|
||||
|
||||
// Basic MVP transform not needed if already in world space, but usually we need localToWorld and ViewProj
|
||||
PerViewData perViewData = LoadData<PerViewData>(g_PushConstantData.perViewBuffer, 0);
|
||||
PerInstanceData perInstanceData = LoadData<PerInstanceData>(g_PushConstantData.perInstanceBuffer, 0);
|
||||
FrameData globalFrameData = LoadData<FrameData>(g_PushConstantData.frameBuffer, 0);
|
||||
ViewData viewData = LoadData<ViewData>(g_PushConstantData.viewBuffer, 0);
|
||||
|
||||
float4 worldPos = mul(perInstanceData.localToWorld, float4(v.position.xyz, 1.0f));
|
||||
outVerts[groupThreadID.x].position = mul(perViewData.viewMatrix, worldPos);
|
||||
outVerts[groupThreadID.x].position = mul(perViewData.projectionMatrix, outVerts[groupThreadID.x].position);
|
||||
float4 worldPos = mul(instanceData.localToWorld, float4(v.position.xyz, 1.0f));
|
||||
float4 viewPos = mul(viewData.viewMatrix, worldPos);
|
||||
|
||||
outVerts[groupThreadID.x].position = mul(viewData.projectionMatrix, viewPos);
|
||||
|
||||
outVerts[groupThreadID.x].color = v.color;
|
||||
outVerts[groupThreadID.x].uv = v.uv;
|
||||
outVerts[groupThreadID.x].meshletID = groupID.x;
|
||||
}
|
||||
|
||||
// Write triangle output (1 thread processes 1 triangle)
|
||||
@@ -66,7 +69,7 @@ void MSMain(
|
||||
|
||||
float4 PSMain(PixelInput input) : SV_TARGET
|
||||
{
|
||||
// PerMaterialData perMaterialData = LoadData<PerMaterialData>(g_PushConstantData.perMaterialBuffer, 0);
|
||||
// PerMaterialData perMaterialData = LoadData<PerMaterialData>(g_PushConstantData.materialIndex, 0);
|
||||
//
|
||||
// float4 color1 = SAMPLE_TEXTURE2D(perMaterialData.texture1, perMaterialData.tex_sampler, input.uv.xy);
|
||||
// float4 color2 = SAMPLE_TEXTURE2D(perMaterialData.texture2, perMaterialData.tex_sampler, input.uv.xy);
|
||||
@@ -75,4 +78,14 @@ float4 PSMain(PixelInput input) : SV_TARGET
|
||||
//
|
||||
// float4 blendedColor = (color1 + color2 + color3 + color4) * 0.25f;
|
||||
// return perMaterialData.color * blendedColor + input.color;
|
||||
}
|
||||
|
||||
// TODO: Randome color on meshlet.
|
||||
// return float4(1, 0, 0, 1);
|
||||
uint hash = PCGHash(input.meshletID);
|
||||
|
||||
float r = float((hash & 0xFF0000u) >> 16) / 255.0;
|
||||
float g = float((hash & 0x00FF00u) >> 8) / 255.0;
|
||||
float b = float((hash & 0x0000FFu)) / 255.0;
|
||||
|
||||
return float4(r, g, b, 1.0);
|
||||
}
|
||||
@@ -68,7 +68,7 @@ public class RenderSystem : IDisposable
|
||||
[UnscopedRef]
|
||||
public ref UnsafeList<RenderRequest> RenderRequests => ref _renderRequests;
|
||||
|
||||
public void Dispose()
|
||||
public void Dispose()
|
||||
{
|
||||
CpuReadyEvent.Dispose();
|
||||
GpuReadyEvent.Dispose();
|
||||
@@ -87,6 +87,7 @@ public class RenderSystem : IDisposable
|
||||
|
||||
private readonly IGraphicsEngine _graphicsEngine;
|
||||
private readonly ResourceManager _resourceManager;
|
||||
private readonly SwapChainManager _swapChainManager;
|
||||
|
||||
private readonly FrameResource[] _frameResources;
|
||||
private readonly Thread _renderThread;
|
||||
@@ -104,6 +105,8 @@ public class RenderSystem : IDisposable
|
||||
private bool _isRunning;
|
||||
private bool _disposed;
|
||||
|
||||
internal SwapChainManager SwapChainManager => _swapChainManager;
|
||||
|
||||
public IGraphicsEngine GraphicsEngine => _graphicsEngine;
|
||||
public ResourceManager ResourceManager => _resourceManager;
|
||||
public bool IsRunning => _isRunning;
|
||||
@@ -161,6 +164,7 @@ public class RenderSystem : IDisposable
|
||||
}
|
||||
|
||||
_resourceManager = new ResourceManager(_graphicsEngine.ResourceAllocator, _graphicsEngine.ResourceDatabase);
|
||||
_swapChainManager = new SwapChainManager(_graphicsEngine);
|
||||
|
||||
// Create frame resources for synchronization
|
||||
_frameResources = new FrameResource[desc.FrameBufferCount];
|
||||
@@ -280,6 +284,7 @@ public class RenderSystem : IDisposable
|
||||
|
||||
// TODO: How can we support async compute and async copy?
|
||||
var cmd = _graphicsEngine.GetPooledCommandBuffer(CommandBufferType.Graphics);
|
||||
ref var renderRequests = ref frameResource.RenderRequests;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -290,8 +295,8 @@ public class RenderSystem : IDisposable
|
||||
CommandBuffer = cmd
|
||||
};
|
||||
|
||||
ref var renderRequests = ref frameResource.RenderRequests;
|
||||
_renderPipeline.Render(renderCtx, renderRequests.AsSpan());
|
||||
_swapChainManager.TransitionToPresent(cmd);
|
||||
|
||||
// End recording commands and submit
|
||||
r = cmd.End();
|
||||
@@ -302,6 +307,11 @@ public class RenderSystem : IDisposable
|
||||
}
|
||||
|
||||
_graphicsEngine.Device.GraphicsQueue.Submit(cmd);
|
||||
_swapChainManager.PresentAll(cmd);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_graphicsEngine.ReturnPooledCommandBuffer(cmd);
|
||||
|
||||
for (var i = 0; i < renderRequests.Count; i++)
|
||||
{
|
||||
@@ -310,10 +320,6 @@ public class RenderSystem : IDisposable
|
||||
|
||||
renderRequests.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_graphicsEngine.ReturnPooledCommandBuffer(cmd);
|
||||
}
|
||||
|
||||
// End the frame and present
|
||||
_resourceManager.EndFrame(_cpuFenceValue);
|
||||
@@ -325,9 +331,6 @@ public class RenderSystem : IDisposable
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Present here.
|
||||
|
||||
|
||||
// Prepare for the next frame.
|
||||
_gpuFenceValue++;
|
||||
|
||||
@@ -415,13 +418,15 @@ public class RenderSystem : IDisposable
|
||||
|
||||
Stop();
|
||||
|
||||
for (int i = 0; i < _frameResources.Length; i++)
|
||||
for (var i = 0; i < _frameResources.Length; i++)
|
||||
{
|
||||
ref var frameResource = ref _frameResources[i];
|
||||
frameResource.Dispose();
|
||||
}
|
||||
|
||||
_renderPipeline.Dispose();
|
||||
|
||||
_swapChainManager.Dispose();
|
||||
_resourceManager.Dispose();
|
||||
_graphicsEngine.Dispose();
|
||||
_shutdownEvent.Dispose();
|
||||
|
||||
@@ -112,8 +112,8 @@ public sealed class ResourceManager : IDisposable
|
||||
|
||||
var objectBufferDesc = new BufferDesc
|
||||
{
|
||||
Size = (uint)sizeof(PerObjectData),
|
||||
Stride = (uint)sizeof(PerObjectData),
|
||||
Size = (uint)sizeof(MeshData),
|
||||
Stride = (uint)sizeof(MeshData),
|
||||
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef BUILTIN_COMMON_HLSL
|
||||
#define BUILTIN_COMMON_HLSL
|
||||
#ifndef GHOST_COMMON_HLSL
|
||||
#define GHOST_COMMON_HLSL
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
@@ -104,4 +104,4 @@ static inline T LoadData(BYTE_ADDRESS_BUFFER buffer, uint index)
|
||||
return buf.Load<T>(index * sizeof(T));
|
||||
}
|
||||
|
||||
#endif // BUILTIN_COMMON_HLSL
|
||||
#endif // GHOST_COMMON_HLSL
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
#ifndef BUILTIN_PROPERTIES_HLSL
|
||||
#define BUILTIN_PROPERTIES_HLSL
|
||||
#ifndef GHOST_PROPERTIES_HLSL
|
||||
#define GHOST_PROPERTIES_HLSL
|
||||
|
||||
#include "F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/Shaders/Includes/Common.hlsl"
|
||||
|
||||
// TODO: This should be auto generated to match the c# side.
|
||||
|
||||
struct PushConstantData
|
||||
{
|
||||
uint globalIndex;
|
||||
uint viewIndex;
|
||||
uint objectIndex;
|
||||
BYTE_ADDRESS_BUFFER frameBuffer;
|
||||
BYTE_ADDRESS_BUFFER viewBuffer;
|
||||
BYTE_ADDRESS_BUFFER instanceBuffer;
|
||||
uint instanceIndex;
|
||||
uint materialIndex;
|
||||
};
|
||||
|
||||
struct GlobalFrameData
|
||||
struct FrameData
|
||||
{
|
||||
uint viewBufferIndex;
|
||||
uint instanceBufferIndex;
|
||||
uint viewBufferCount;
|
||||
uint instanceBufferCount;
|
||||
uint userBufferIndex;
|
||||
BYTE_ADDRESS_BUFFER viewBuffer;
|
||||
BYTE_ADDRESS_BUFFER instanceBuffer;
|
||||
BYTE_ADDRESS_BUFFER userBuffer;
|
||||
};
|
||||
|
||||
struct PerViewData
|
||||
struct ViewData
|
||||
{
|
||||
float4x4 viewMatrix;
|
||||
float4x4 projectionMatrix;
|
||||
@@ -32,17 +31,20 @@ struct PerViewData
|
||||
float4 screenSize; // xy: size, zw: 1/size
|
||||
};
|
||||
|
||||
struct PerInstanceData
|
||||
struct InstanceData
|
||||
{
|
||||
float4x4 localToWorld;
|
||||
BYTE_ADDRESS_BUFFER meshBuffer;
|
||||
BYTE_ADDRESS_BUFFER materialBuffer;
|
||||
};
|
||||
|
||||
struct PerObjectData
|
||||
struct MeshData
|
||||
{
|
||||
float3 worldBoundsMin;
|
||||
BYTE_ADDRESS_BUFFER vertexBuffer;
|
||||
float3 worldBoundsMax;
|
||||
BYTE_ADDRESS_BUFFER indexBuffer;
|
||||
|
||||
BYTE_ADDRESS_BUFFER meshletBuffer;
|
||||
BYTE_ADDRESS_BUFFER meshletVerticesBuffer;
|
||||
BYTE_ADDRESS_BUFFER meshletTrianglesBuffer;
|
||||
@@ -50,4 +52,4 @@ struct PerObjectData
|
||||
|
||||
PushConstantData g_PushConstantData : register(b0);
|
||||
|
||||
#endif // BUILTIN_PROPERTIES_HLSL
|
||||
#endif // GHOST_PROPERTIES_HLSL
|
||||
|
||||
123
src/Runtime/Ghost.Graphics/Shaders/Includes/Random.hlsl
Normal file
123
src/Runtime/Ghost.Graphics/Shaders/Includes/Random.hlsl
Normal file
@@ -0,0 +1,123 @@
|
||||
#ifndef GHOST_RANDOM_HLSL
|
||||
#define GHOST_RANDOM_HLSL
|
||||
|
||||
float RandomFloat(float2 uv)
|
||||
{
|
||||
return frac(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
float Hash(uint s)
|
||||
{
|
||||
s = s ^ 2747636419u;
|
||||
s = s * 2654435769u;
|
||||
s = s ^ (s >> 16);
|
||||
s = s * 2654435769u;
|
||||
s = s ^ (s >> 16);
|
||||
s = s * 2654435769u;
|
||||
return float(s) * rcp(4294967296.0); // 2^-32
|
||||
}
|
||||
|
||||
// A single iteration of Bob Jenkins' One-At-A-Time hashing algorithm.
|
||||
uint JenkinsHash(uint x)
|
||||
{
|
||||
x += (x << 10u);
|
||||
x ^= (x >> 6u);
|
||||
x += (x << 3u);
|
||||
x ^= (x >> 11u);
|
||||
x += (x << 15u);
|
||||
return x;
|
||||
}
|
||||
|
||||
// Compound versions of the hashing algorithm.
|
||||
uint JenkinsHash(uint2 v)
|
||||
{
|
||||
return JenkinsHash(v.x ^ JenkinsHash(v.y));
|
||||
}
|
||||
|
||||
uint JenkinsHash(uint3 v)
|
||||
{
|
||||
return JenkinsHash(v.x ^ JenkinsHash(v.yz));
|
||||
}
|
||||
|
||||
uint JenkinsHash(uint4 v)
|
||||
{
|
||||
return JenkinsHash(v.x ^ JenkinsHash(v.yzw));
|
||||
}
|
||||
|
||||
uint PCGHash(uint input)
|
||||
{
|
||||
uint state = input * 747796405u + 2891336453u;
|
||||
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
|
||||
return (word >> 22u) ^ word;
|
||||
}
|
||||
|
||||
// Construct a float with half-open range [0, 1) using low 23 bits.
|
||||
// All zeros yields 0, all ones yields the next smallest representable value below 1.
|
||||
float ConstructFloat(int m)
|
||||
{
|
||||
const int ieeeMantissa = 0x007FFFFF; // Binary FP32 mantissa bitmask
|
||||
const int ieeeOne = 0x3F800000; // 1.0 in FP32 IEEE
|
||||
|
||||
m &= ieeeMantissa; // Keep only mantissa bits (fractional part)
|
||||
m |= ieeeOne; // Add fractional part to 1.0
|
||||
|
||||
float f = asfloat(m); // Range [1, 2)
|
||||
return f - 1; // Range [0, 1)
|
||||
}
|
||||
|
||||
float ConstructFloat(uint m)
|
||||
{
|
||||
return ConstructFloat(asint(m));
|
||||
}
|
||||
|
||||
// Pseudo-random value in half-open range [0, 1). The distribution is reasonably uniform.
|
||||
// Ref: https://stackoverflow.com/a/17479300
|
||||
float GenerateHashedRandomFloat(uint x)
|
||||
{
|
||||
return ConstructFloat(JenkinsHash(x));
|
||||
}
|
||||
|
||||
float GenerateHashedRandomFloat(uint2 v)
|
||||
{
|
||||
return ConstructFloat(JenkinsHash(v));
|
||||
}
|
||||
|
||||
float GenerateHashedRandomFloat(uint3 v)
|
||||
{
|
||||
return ConstructFloat(JenkinsHash(v));
|
||||
}
|
||||
|
||||
float GenerateHashedRandomFloat(uint4 v)
|
||||
{
|
||||
return ConstructFloat(JenkinsHash(v));
|
||||
}
|
||||
|
||||
float2 InitRandom(float2 input)
|
||||
{
|
||||
float2 r;
|
||||
r.x = Hash(uint(input.x * 0xFFFFFFFFu));
|
||||
r.y = Hash(uint(input.y * 0xFFFFFFFFu));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
//From Next Generation Post Processing in Call of Duty: Advanced Warfare [Jimenez 2014]
|
||||
// http://advances.realtimerendering.com/s2014/index.html
|
||||
float InterleavedGradientNoise(float2 pixCoord, int frameCount)
|
||||
{
|
||||
const float3 magic = float3(0.06711056f, 0.00583715f, 52.9829189f);
|
||||
float2 frameMagicScale = float2(2.083f, 4.867f);
|
||||
pixCoord += frameCount * frameMagicScale;
|
||||
return frac(magic.z * frac(dot(pixCoord, magic.xy)));
|
||||
}
|
||||
|
||||
// 32-bit Xorshift random number generator
|
||||
uint XorShift(inout uint rngState)
|
||||
{
|
||||
rngState ^= rngState << 13;
|
||||
rngState ^= rngState >> 17;
|
||||
rngState ^= rngState << 5;
|
||||
return rngState;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -50,7 +50,7 @@ internal sealed class SwapChainRecord
|
||||
}
|
||||
}
|
||||
|
||||
internal class SwapChainManager
|
||||
internal class SwapChainManager : IDisposable
|
||||
{
|
||||
public const int MAX_SWAP_CHAINS = 8;
|
||||
private readonly IGraphicsEngine _graphicsEngine;
|
||||
@@ -115,4 +115,59 @@ internal class SwapChainManager
|
||||
Interlocked.CompareExchange(ref _swapChains[index], null, record);
|
||||
}
|
||||
}
|
||||
|
||||
public void TransitionToPresent(ICommandBuffer commandBuffer)
|
||||
{
|
||||
for (int i = 0; i < MAX_SWAP_CHAINS; i++)
|
||||
{
|
||||
var record = Volatile.Read(ref _swapChains[i]);
|
||||
if (record == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
commandBuffer.Barrier(BarrierDesc.Texture(record.SwapChain.GetCurrentBackBuffer().AsResource(),
|
||||
null, BarrierSync.None,
|
||||
null, BarrierAccess.NoAccess,
|
||||
null, BarrierLayout.Present));
|
||||
}
|
||||
}
|
||||
|
||||
public void Present(int index, ICommandBuffer commandBuffer)
|
||||
{
|
||||
var record = Volatile.Read(ref _swapChains[index]);
|
||||
if (record == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
record.SwapChain.Present();
|
||||
}
|
||||
|
||||
public void PresentAll(ICommandBuffer commandBuffer)
|
||||
{
|
||||
for (int i = 0; i < MAX_SWAP_CHAINS; i++)
|
||||
{
|
||||
var record = Volatile.Read(ref _swapChains[i]);
|
||||
if (record == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
record?.SwapChain.Present();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
for (int i = 0; i < MAX_SWAP_CHAINS; i++)
|
||||
{
|
||||
var record = Volatile.Read(ref _swapChains[i]);
|
||||
if (record != null)
|
||||
{
|
||||
record.SwapChain.Dispose();
|
||||
Interlocked.CompareExchange(ref _swapChains[i], null, record);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ shader "MyShader/Standard"
|
||||
includes
|
||||
{
|
||||
"F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/Shaders/Includes/Properties.hlsl";
|
||||
"F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/Shaders/Includes/Random.hlsl";
|
||||
}
|
||||
|
||||
hlsl
|
||||
@@ -33,6 +34,7 @@ shader "MyShader/Standard"
|
||||
float4 position : SV_POSITION;
|
||||
float4 color : COLOR;
|
||||
float4 uv : TEXCOORD0;
|
||||
nointerpolation uint meshletID : MESHLET_ID;
|
||||
};
|
||||
|
||||
[numthreads(128, 1, 1)] // 128 threads to cover max 64 vertices and 124 triangles
|
||||
@@ -43,9 +45,10 @@ shader "MyShader/Standard"
|
||||
out vertices PixelInput outVerts[64],
|
||||
out indices uint3 outTris[124])
|
||||
{
|
||||
PerObjectData perObjectData = LoadData<PerObjectData>(g_PushConstantData.objectIndex, 0);
|
||||
InstanceData instanceData = LoadData<InstanceData>(g_PushConstantData.instanceBuffer, g_PushConstantData.instanceIndex);
|
||||
MeshData meshData = LoadData<MeshData>(instanceData.meshBuffer, 0);
|
||||
|
||||
ByteAddressBuffer meshletBuffer = GET_BUFFER(perObjectData.meshletBuffer);
|
||||
ByteAddressBuffer meshletBuffer = GET_BUFFER(meshData.meshletBuffer);
|
||||
Meshlet m = meshletBuffer.Load<Meshlet>(groupID.x * sizeof(Meshlet));
|
||||
|
||||
uint vertexCount = m.packedCounts & 0xFF;
|
||||
@@ -53,27 +56,29 @@ shader "MyShader/Standard"
|
||||
|
||||
SetMeshOutputCounts(vertexCount, triangleCount);
|
||||
|
||||
ByteAddressBuffer meshletVerticesBuffer = GET_BUFFER(perObjectData.meshletVerticesBuffer);
|
||||
ByteAddressBuffer meshletTrianglesBuffer = GET_BUFFER(perObjectData.meshletTrianglesBuffer);
|
||||
ByteAddressBuffer meshletVerticesBuffer = GET_BUFFER(meshData.meshletVerticesBuffer);
|
||||
ByteAddressBuffer meshletTrianglesBuffer = GET_BUFFER(meshData.meshletTrianglesBuffer);
|
||||
|
||||
// Write vertex output
|
||||
if (groupThreadID.x < vertexCount)
|
||||
{
|
||||
uint vertexIndex = meshletVerticesBuffer.Load((m.vertexOffset + groupThreadID.x) * 4);
|
||||
ByteAddressBuffer vertices = GET_BUFFER(perObjectData.vertexBuffer);
|
||||
ByteAddressBuffer vertices = GET_BUFFER(meshData.vertexBuffer);
|
||||
Vertex v = vertices.Load<Vertex>(vertexIndex * sizeof(Vertex));
|
||||
|
||||
GlobalFrameData globalFrameData = LoadData<GlobalFrameData>(g_PushConstantData.globalIndex, 0);
|
||||
PerViewData perViewData = LoadData<PerViewData>(g_PushConstantData.viewIndex, 0);
|
||||
PerInstanceData perInstanceData = LoadData<PerInstanceData>(globalFrameData.instanceBufferIndex, g_PushConstantData.instanceIndex);
|
||||
FrameData globalFrameData = LoadData<FrameData>(g_PushConstantData.frameBuffer, 0);
|
||||
ViewData viewData = LoadData<ViewData>(g_PushConstantData.viewBuffer, 0);
|
||||
|
||||
float4 worldPos = mul(perInstanceData.localToWorld, float4(v.position.xyz, 1.0f));
|
||||
float4 viewPos = mul(perViewData.viewMatrix, worldPos);
|
||||
float4 worldPos = mul(instanceData.localToWorld, float4(v.position.xyz, 1.0f));
|
||||
float4 viewPos = mul(viewData.viewMatrix, worldPos);
|
||||
|
||||
outVerts[groupThreadID.x].position = mul(perViewData.projectionMatrix, viewPos);
|
||||
// outVerts[groupThreadID.x].position = mul(viewData.projectionMatrix, viewPos);
|
||||
// For testing.
|
||||
outVerts[groupThreadID.x].position = float4(v.position.xyz, 1.0f);
|
||||
|
||||
outVerts[groupThreadID.x].color = v.color;
|
||||
outVerts[groupThreadID.x].uv = v.uv;
|
||||
outVerts[groupThreadID.x].meshletID = groupID.x;
|
||||
}
|
||||
|
||||
// Write triangle output (1 thread processes 1 triangle)
|
||||
@@ -105,9 +110,15 @@ shader "MyShader/Standard"
|
||||
// return perMaterialData.color * blendedColor + input.color;
|
||||
|
||||
// TODO: Randome color on meshlet.
|
||||
return float4(1, 0, 0, 1);
|
||||
// return 1.0;
|
||||
uint hash = PCGHash(input.meshletID);
|
||||
|
||||
float r = float((hash & 0xFF0000u) >> 16) / 255.0;
|
||||
float g = float((hash & 0x00FF00u) >> 8) / 255.0;
|
||||
float b = float((hash & 0x0000FFu)) / 255.0;
|
||||
|
||||
return float4(r, g, b, 1.0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mesh "hlsl_block" : "MSMain";
|
||||
|
||||
Reference in New Issue
Block a user