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:
2026-03-27 22:23:44 +09:00
parent 0a2eb619eb
commit d8a7b07624
495 changed files with 51961 additions and 892 deletions

View File

@@ -50,12 +50,11 @@ public readonly struct Result
public void Deconstruct(out bool success, out string? message)
{
success = IsSuccess;
message = Message;
success = _isSuccess;
message = _message;
}
public override string ToString() => IsSuccess ? "OK" : $"Error: {Message}";
public override string ToString() => _isSuccess ? "OK" : $"Error: {_message}";
public static implicit operator bool(Result result) => result.IsSuccess;
}
@@ -105,12 +104,12 @@ public readonly struct Result<T>
public void Deconstruct(out bool success, out T value, out string? message)
{
success = IsSuccess;
value = Value;
message = Message;
success = _isSuccess;
value = _value;
message = _message;
}
public override string ToString() => IsSuccess ? $"OK: {Value}" : $"Error: {Message}";
public override string ToString() => _isSuccess ? $"OK: {_value}" : $"Error: {_message}";
public static implicit operator Result<T>(T? data) => data is not null ? Success(data) : Failure(null);
public static implicit operator Result<T>(Result result) => result.IsSuccess ? Success(default!) : Failure(result.Message);
@@ -179,8 +178,8 @@ public readonly struct Result<T, E>
public void Deconstruct(out T value, out E status)
{
value = Value;
status = Error;
value = _value;
status = _error;
}
public override string ToString() => $"Value: {_value}, Status: {_error}";
@@ -233,11 +232,10 @@ public readonly ref struct RefResult<T, E>
return new RefResult<T, E>(ref Unsafe.NullRef<T>(), error);
}
public void Deconstruct(out bool success, out Ref<T> value, out E status)
public void Deconstruct(out Ref<T> value, out E status)
{
success = IsSuccess;
value = new Ref<T>(ref Value);
status = Error;
value = new Ref<T>(ref _value);
status = _error;
}
public override string ToString() => $"Value: {_value}, Status: {_error}";

View File

@@ -32,42 +32,6 @@ public class RenderExtractionSystem : ISystem
.Build(systemAPI.World, true);
}
private static float3 IntersectFrustumPlanes(float4 p0, float4 p1, float4 p2)
{
float3 n0 = p0.xyz;
float3 n1 = p1.xyz;
float3 n2 = p2.xyz;
float det = math.dot(math.cross(n0, n1), n2);
return (math.cross(n2, n1) * p0.w + math.cross(n0, n2) * p1.w - math.cross(n0, n1) * p2.w) * (1.0f / det);
}
private static Frustum CreateFrustum(Camera camRef, float4x4 vp, float3 viewDir, float3 viewPos)
{
var frustum = new Frustum();
Frustum.CalculateFrustumPlanes(vp, ref frustum.planes);
// We need to recalculate the near and far planes otherwise it does not work for oblique projection matrices used for reflection.
var nearPlane = Plane.CreateFromUnitNormalAndPointInPlane(viewDir, viewPos);
nearPlane.Distance -= camRef.nearClipPlane;
var farPlane = Plane.CreateFromUnitNormalAndPointInPlane(-viewDir, viewPos);
farPlane.Distance += camRef.farClipPlane;
frustum.planes[4] = nearPlane;
frustum.planes[5] = farPlane;
frustum.corners[0] = IntersectFrustumPlanes(frustum.planes[0], frustum.planes[3], frustum.planes[4]);
frustum.corners[1] = IntersectFrustumPlanes(frustum.planes[1], frustum.planes[3], frustum.planes[4]);
frustum.corners[2] = IntersectFrustumPlanes(frustum.planes[0], frustum.planes[2], frustum.planes[4]);
frustum.corners[3] = IntersectFrustumPlanes(frustum.planes[1], frustum.planes[2], frustum.planes[4]);
frustum.corners[4] = IntersectFrustumPlanes(frustum.planes[0], frustum.planes[3], frustum.planes[5]);
frustum.corners[5] = IntersectFrustumPlanes(frustum.planes[1], frustum.planes[3], frustum.planes[5]);
frustum.corners[6] = IntersectFrustumPlanes(frustum.planes[0], frustum.planes[2], frustum.planes[5]);
frustum.corners[7] = IntersectFrustumPlanes(frustum.planes[1], frustum.planes[2], frustum.planes[5]);
return frustum;
}
public unsafe void Update(ref readonly SystemAPI systemAPI)
{
if (_meshQueryID.IsInvalid)
@@ -83,15 +47,6 @@ public class RenderExtractionSystem : ISystem
ref readonly var camRef = ref cam.Get();
ref readonly var camLtwRef = ref camLtw.Get();
var rtResult = _renderSystem.GraphicsEngine.ResourceDatabase.GetResourceDescription(camRef.colorTarget.AsResource());
if (rtResult.IsFailure)
{
continue;
}
var rtSize = new uint2(rtResult.Value.TextureDescription.Width, rtResult.Value.TextureDescription.Height);
var aspectScreen = (float)rtSize.x / rtSize.y;
// TODO: Classify transparent objects into a separate render list and render via oit.
var renderList = new RenderList(1, 64, Allocator.FreeList);
var transparentRenderList = new RenderList(1, 64, Allocator.FreeList);
@@ -148,70 +103,9 @@ public class RenderExtractionSystem : ISystem
}
}
// NOTE: We assume camera's scale is always (1, 1, 1). Otherwise fastinverse will fail and we need to use regular inverse which is more expensive.
var viewMatrix = math.fastinverse(camLtwRef.matrix);
var vfov = 2.0f * math.atan(camRef.sensorSize.y / 2.0f * camRef.focalLength);
var hfov = 2.0f * math.atan(camRef.sensorSize.x / 2.0f * camRef.focalLength);
var aspectSensor = camRef.sensorSize.x / camRef.sensorSize.y;
float vfovF;
switch (camRef.gateFit)
{
case GateFit.Vertical:
vfovF = vfov;
break;
case GateFit.Horizontal:
// Adjust VFOV so that the sensor width fits the screen width
var horizontalAspectBuffer = math.tan(hfov * 0.5f);
vfovF = 2.0f * math.atan(horizontalAspectBuffer / aspectScreen);
break;
case GateFit.Fill:
if (aspectSensor > aspectScreen)
{
goto case GateFit.Vertical;
}
else
{
goto case GateFit.Horizontal;
}
case GateFit.Overscan:
if (aspectSensor > aspectScreen)
{
goto case GateFit.Horizontal;
}
else
{
goto case GateFit.Vertical;
}
default:
vfovF = vfov;
break;
}
var m_00 = 1.0f / aspectScreen * math.tan(vfovF * 0.5f);
var m_11 = 1.0f / math.tan(vfovF * 0.5f);
var m_22 = -(camRef.farClipPlane + camRef.nearClipPlane) / (camRef.farClipPlane - camRef.nearClipPlane);
var m_23 = -(2.0f * camRef.farClipPlane * camRef.nearClipPlane) / (camRef.farClipPlane - camRef.nearClipPlane);
var projectionMatrix = new float4x4
(
m_00, 0, 0, 0,
0, m_11, 0, 0,
0, 0, m_22, m_23,
0, 0, -1, 0
);
var vp = math.mul(projectionMatrix, viewMatrix);
var viewDir = math.normalize(camLtwRef.matrix.c2.xyz);
var viewPos = camLtwRef.matrix.c3.xyz;
var frustum = CreateFrustum(camRef, vp, viewDir, viewPos);
var request = new RenderRequest
{
swapChainIndex = camRef.swapChainIndex,
colorTarget = camRef.colorTarget,
depthTarget = camRef.depthTarget,
opaqueRenderList = renderList,
@@ -220,11 +114,12 @@ public class RenderExtractionSystem : ISystem
renderFunc = camRef.renderFunc,
view = new RenderView
{
viewMatrix = viewMatrix,
projectionMatrix = projectionMatrix,
position = camLtwRef.matrix.c3.xyz,
localToWorld = camLtwRef.matrix,
//viewMatrix = viewMatrix,
//projectionMatrix = projectionMatrix,
//position = camLtwRef.matrix.c3.xyz,
frustum = frustum,
//frustum = frustum,
nearClipPlane = camRef.nearClipPlane,
farClipPlane = camRef.farClipPlane,

View File

@@ -138,7 +138,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
return Result.Success();
}
public void SetScissorRect(RectDesc rect)
public void SetScissorRect(ScissorRectDesc rect)
{
AssertNotDisposed();
ThrowIfNotRecording();
@@ -154,7 +154,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
pNativeObject->RSSetScissorRects(1, &d3d12Rect);
}
public void ResourceBarrier(params ReadOnlySpan<BarrierDesc> barrierDescs)
public void Barrier(params ReadOnlySpan<BarrierDesc> barrierDescs)
{
AssertNotDisposed();
ThrowIfNotRecording();
@@ -201,9 +201,9 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
case BarrierType.Global:
pGlobalBarriers[globalIndex++] = new D3D12_GLOBAL_BARRIER
{
SyncBefore = (D3D12_BARRIER_SYNC)desc.SyncBefore,
SyncBefore = (D3D12_BARRIER_SYNC)(desc.SyncBefore ?? 0),
SyncAfter = (D3D12_BARRIER_SYNC)desc.SyncAfter,
AccessBefore = (D3D12_BARRIER_ACCESS)desc.AccessBefore,
AccessBefore = (D3D12_BARRIER_ACCESS)(desc.AccessBefore ?? 0),
AccessAfter = (D3D12_BARRIER_ACCESS)desc.AccessAfter
};
break;
@@ -212,7 +212,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
var r = _resourceDatabase.GetResourceRecord(desc.Resource);
if (r.IsFailure)
{
RecordError(nameof(ResourceBarrier), r.Error);
RecordError(nameof(Barrier), r.Error);
continue;
}
@@ -220,9 +220,9 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
var resource = record.ResourcePtr;
pBufferBarriers[bufferIndex++] = new D3D12_BUFFER_BARRIER
{
SyncBefore = (D3D12_BARRIER_SYNC)desc.SyncBefore,
SyncBefore = (D3D12_BARRIER_SYNC)(desc.SyncBefore ?? record.barrierData.sync),
SyncAfter = (D3D12_BARRIER_SYNC)desc.SyncAfter,
AccessBefore = (D3D12_BARRIER_ACCESS)desc.AccessBefore,
AccessBefore = (D3D12_BARRIER_ACCESS)(desc.AccessBefore ?? record.barrierData.access),
AccessAfter = (D3D12_BARRIER_ACCESS)desc.AccessAfter,
pResource = resource,
Offset = 0,
@@ -237,7 +237,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
var r = _resourceDatabase.GetResourceRecord(desc.Resource);
if (r.IsFailure)
{
RecordError(nameof(ResourceBarrier), r.Error);
RecordError(nameof(Barrier), r.Error);
continue;
}
@@ -245,11 +245,11 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
var resource = record.ResourcePtr;
pTextureBarriers[textureIndex++] = new D3D12_TEXTURE_BARRIER
{
SyncBefore = (D3D12_BARRIER_SYNC)desc.SyncBefore,
SyncBefore = (D3D12_BARRIER_SYNC)(desc.SyncBefore ?? record.barrierData.sync),
SyncAfter = (D3D12_BARRIER_SYNC)desc.SyncAfter,
AccessBefore = (D3D12_BARRIER_ACCESS)desc.AccessBefore,
AccessBefore = (D3D12_BARRIER_ACCESS)(desc.AccessBefore ?? record.barrierData.access),
AccessAfter = (D3D12_BARRIER_ACCESS)desc.AccessAfter,
LayoutBefore = (D3D12_BARRIER_LAYOUT)desc.LayoutBefore,
LayoutBefore = (D3D12_BARRIER_LAYOUT)(desc.LayoutBefore ?? record.barrierData.layout),
LayoutAfter = (D3D12_BARRIER_LAYOUT)desc.LayoutAfter,
pResource = resource,
Subresources = new D3D12_BARRIER_SUBRESOURCE_RANGE
@@ -844,6 +844,14 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
public void UploadBuffer<T>(Handle<GraphicsBuffer> buffer, params ReadOnlySpan<T> data)
where T : unmanaged
{
static void Map(T* pData, nuint size, ulong offset, SharedPtr<ID3D12Resource> resource)
{
void* pMappedData;
resource.Get()->Map(0, null, &pMappedData);
MemoryUtility.MemCpy((byte*)pMappedData + offset, pData, size);
resource.Get()->Unmap(0, null);
}
AssertNotDisposed();
ThrowIfNotRecording();
#if !DEBUG
@@ -854,21 +862,33 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
#endif
IncrementCommandCount();
var sizeInBytes = (uint)(data.Length * sizeof(T));
var uploadHandle = _resourceAllocator.CreateTempUploadBuffer(sizeInBytes, out var offset);
var uploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
void* pMappedData;
uploadResource.Get()->Map(0, null, &pMappedData);
fixed (T* pData = data)
{
MemoryUtility.MemCpy((byte*)pMappedData + offset, pData, sizeInBytes);
}
uploadResource.Get()->Unmap(0, null);
var pResource = _resourceDatabase.GetResource(buffer.AsResource());
pNativeObject->CopyBufferRegion(pResource, 0, uploadResource, offset, sizeInBytes);
D3D12_HEAP_PROPERTIES properties;
D3D12_HEAP_FLAGS flags;
ThrowIfFailed(pResource.Get()->GetHeapProperties(&properties, &flags));
if (properties.Type == D3D12_HEAP_TYPE_UPLOAD)
{
fixed (T* pData = data)
{
Map(pData, (nuint)(data.Length * sizeof(T)), 0, pResource);
}
}
else
{
var sizeInBytes = (uint)(data.Length * sizeof(T));
var uploadHandle = _resourceAllocator.CreateTempUploadBuffer(sizeInBytes, out var offset);
var uploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
fixed (T* pData = data)
{
Map(pData, sizeInBytes, offset, uploadResource);
}
pNativeObject->CopyBufferRegion(pResource, 0, uploadResource, offset, sizeInBytes);
}
}
public void UploadTexture(Handle<Texture> texture, params ReadOnlySpan<SubResourceData> subresources)

View File

@@ -83,7 +83,7 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
{
ShaderRegister = 0, // b0
RegisterSpace = 0, // space0
Num32BitValues = 4 // Global, View, Object, Material indices
Num32BitValues = PushConstantsData.NUM_32BITS_VALUE
}
};

View File

@@ -77,7 +77,7 @@ internal class D3D12Renderer : IRenderer
}
//// TODO: A proper render graph integration.
//private Error RenderScene(Handle<Texture> target, ViewportDesc viewport, RectDesc rect)
//private Error RenderScene(Handle<Texture> target, ViewportDesc viewport, ScissorRectDesc rect)
//{
// // NOTE: Testing only.
// var ctx = new RenderingContext(_graphicsEngine, _commandBuffer);

View File

@@ -186,6 +186,7 @@ internal unsafe class DXGISwapChain : ISwapChain
var presentFlags = 0u;
var syncInterval = vsync ? 1u : 0u;
var i =_swapChain.Get()->GetCurrentBackBufferIndex();
ThrowIfFailed(_swapChain.Get()->Present(syncInterval, presentFlags));
}

View File

@@ -302,7 +302,7 @@ public struct RenderDesc
get; set;
}
public RectDesc Viewport
public ScissorRectDesc Viewport
{
get; set;
}
@@ -345,7 +345,7 @@ public struct ViewportDesc
}
public struct RectDesc
public struct ScissorRectDesc
{
public uint Left
{
@@ -539,7 +539,7 @@ public struct BarrierDesc
get; set;
}
public BarrierSync SyncBefore
public BarrierSync? SyncBefore
{
get; set;
}
@@ -549,7 +549,7 @@ public struct BarrierDesc
get; set;
}
public BarrierAccess AccessBefore
public BarrierAccess? AccessBefore
{
get; set;
}
@@ -559,7 +559,7 @@ public struct BarrierDesc
get; set;
}
public BarrierLayout LayoutBefore
public BarrierLayout? LayoutBefore
{
get; set;
}
@@ -596,7 +596,7 @@ public struct BarrierDesc
};
}
public static BarrierDesc Buffer(Handle<GPUResource> resource, BarrierSync syncBefore, BarrierSync syncAfter, BarrierAccess accessBefore, BarrierAccess accessAfter)
public static BarrierDesc Buffer(Handle<GPUResource> resource, BarrierSync? syncBefore, BarrierSync syncAfter, BarrierAccess? accessBefore, BarrierAccess accessAfter)
{
return new BarrierDesc
{
@@ -609,7 +609,7 @@ public struct BarrierDesc
};
}
public static BarrierDesc Texture(Handle<GPUResource> resource, BarrierSync syncBefore, BarrierSync syncAfter, BarrierAccess accessBefore, BarrierAccess accessAfter, BarrierLayout layoutBefore, BarrierLayout layoutAfter, BarrierSubresourceRange subresources = default, bool discard = false)
public static BarrierDesc Texture(Handle<GPUResource> resource, BarrierSync? syncBefore, BarrierSync syncAfter, BarrierAccess? accessBefore, BarrierAccess accessAfter, BarrierLayout? layoutBefore, BarrierLayout layoutAfter, BarrierSubresourceRange subresources = default, bool discard = false)
{
return new BarrierDesc
{

View File

@@ -51,7 +51,7 @@ public interface ICommandBuffer : IDisposable
/// Sets the scissor rectangle
/// </summary>
/// <param name="rect">Scissor rectangle to set</param>
void SetScissorRect(RectDesc rect);
void SetScissorRect(ScissorRectDesc rect);
/// <summary>
/// Sets the optional render targets and optional depth Target for subsequent rendering operations.
@@ -99,7 +99,7 @@ public interface ICommandBuffer : IDisposable
/// Inserts multiple resource barriers.
/// </summary>
/// <param name="barrierDescs">Resource barrier descriptions</param>
void ResourceBarrier(params ReadOnlySpan<BarrierDesc> barrierDescs);
void Barrier(params ReadOnlySpan<BarrierDesc> barrierDescs);
/// <summary>
/// Sets the pipeline state object

View File

@@ -9,7 +9,7 @@ public interface IRenderOutput
get; set;
}
RectDesc Scissor
ScissorRectDesc Scissor
{
get; set;
}

View File

@@ -10,23 +10,22 @@ public static class RootSignatureLayout
public const int ROOT_PARAMETER_COUNT = 1;
}
[StructLayout(LayoutKind.Sequential, Size = 20)]
[StructLayout(LayoutKind.Sequential, Size = 16)]
public struct PushConstantsData
{
public uint globalIndex;
public uint viewIndex;
public uint objectIndex;
public const uint NUM_32BITS_VALUE = 16u / sizeof(uint);
public uint frameBuffer;
public uint viewBuffer;
public uint instanceBuffer;
public uint instanceIndex;
public uint materialIndex;
}
[StructLayout(LayoutKind.Sequential, Size = 20)]
public struct GlobalFrameData
[StructLayout(LayoutKind.Sequential)]
public struct FrameData
{
public uint viewBufferIndex;
public uint instanceBufferIndex;
public uint viewBufferCount;
public uint instanceBufferCount;
public uint userBufferIndex;
}
@@ -34,10 +33,12 @@ public struct GlobalFrameData
public struct InstanceData
{
public float4x4 localToWorld;
public uint meshBufferIndex;
public uint materialBufferIndex;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct PerViewData
public struct ViewData
{
public float4x4 viewMatrix;
public float4x4 projectionMatrix;
@@ -49,7 +50,7 @@ public struct PerViewData
};
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct PerObjectData
public struct MeshData
{
public float3 worldBoundsMin;
public uint vertexBuffer;

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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,

View File

@@ -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)
{

View File

@@ -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!;

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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,
};

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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);
}
}
}
}

View File

@@ -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";