feat(core,rendering)!: add cleanup component support, refactor render pipeline
Introduce ICleanupComponent and cleanup archetype logic in ECS. Refactor component versioning to uint. Update IResourceDatabase to use map/unmap pattern. Decouple per-frame render requests from RenderSystem via IRenderPayload. Update render pipeline and extraction system to new API. BREAKING CHANGE: Entity destruction and render pipeline APIs have changed. IResourceDatabase.MapResource signature is updated; all callers must use map/memcpy/unmap. RenderSystem no longer manages per-frame render requests directly.
This commit is contained in:
@@ -8,15 +8,7 @@ using Misaki.HighPerformance.Mathematics;
|
||||
using Misaki.HighPerformance.Mathematics.Geometry;
|
||||
using Misaki.HighPerformance.Utilities;
|
||||
|
||||
namespace Ghost.Graphics.Test.RenderPasses;
|
||||
|
||||
public sealed class TestRenderPipelineSettings : IRenderPipelineSettings
|
||||
{
|
||||
public IRenderPipeline CreatePipeline(RenderSystem renderSystem)
|
||||
{
|
||||
return new TestRenderPipeline(renderSystem);
|
||||
}
|
||||
}
|
||||
namespace Ghost.Graphics.Test.RenderPipeline;
|
||||
|
||||
public unsafe partial class TestRenderPipeline : IRenderPipeline
|
||||
{
|
||||
@@ -29,8 +21,9 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
|
||||
public uint instanceIndex;
|
||||
}
|
||||
|
||||
private readonly RenderGraph _renderGraph;
|
||||
private readonly RenderSystem _renderSystem;
|
||||
|
||||
private readonly RenderGraph _renderGraph;
|
||||
private Identifier<Shader> _meshletShader;
|
||||
private Handle<Material> _meshletMaterial;
|
||||
|
||||
@@ -44,6 +37,7 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
|
||||
internal TestRenderPipeline(RenderSystem renderSystem)
|
||||
{
|
||||
_renderSystem = renderSystem;
|
||||
|
||||
_renderGraph = new RenderGraph(renderSystem.ResourceManager,
|
||||
renderSystem.GraphicsEngine.ResourceAllocator,
|
||||
renderSystem.GraphicsEngine.ResourceDatabase,
|
||||
@@ -106,12 +100,17 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
|
||||
return frustum;
|
||||
}
|
||||
|
||||
public void Render(RenderContext ctx, ReadOnlySpan<RenderRequest> requests)
|
||||
public void Render(RenderContext ctx, int frameIndex, IRenderPayload payload)
|
||||
{
|
||||
var resourceManager = _renderSystem.ResourceManager;
|
||||
var resourceDatabase = _renderSystem.GraphicsEngine.ResourceDatabase;
|
||||
var testPayload = (TestRenderPayload)payload;
|
||||
|
||||
for (var i = 0; i < requests.Length; i++)
|
||||
var renderSystem = testPayload.RenderSystem;
|
||||
var resourceManager = renderSystem.ResourceManager;
|
||||
var resourceDatabase = renderSystem.GraphicsEngine.ResourceDatabase;
|
||||
|
||||
var requests = testPayload.FrameRequestData[frameIndex].renderRequests;
|
||||
|
||||
for (var i = 0; i < requests.Count; i++)
|
||||
{
|
||||
ref readonly var request = ref requests[i];
|
||||
|
||||
@@ -127,7 +126,7 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
|
||||
{
|
||||
rt = request.colorTarget;
|
||||
}
|
||||
else if (_renderSystem.SwapChainManager.TryGetSwapChain(request.swapChainIndex, out var swapChain))
|
||||
else if (renderSystem.SwapChainManager.TryGetSwapChain(request.swapChainIndex, out var swapChain))
|
||||
{
|
||||
rt = swapChain.GetCurrentBackBuffer();
|
||||
}
|
||||
@@ -138,7 +137,7 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
|
||||
|
||||
try
|
||||
{
|
||||
var rtResult = _renderSystem.GraphicsEngine.ResourceDatabase.GetResourceDescription(rt.AsResource());
|
||||
var rtResult = renderSystem.GraphicsEngine.ResourceDatabase.GetResourceDescription(rt.AsResource());
|
||||
if (rtResult.IsFailure)
|
||||
{
|
||||
continue;
|
||||
@@ -296,31 +295,24 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
|
||||
//ctx.UploadBuffer(frameBufferHandle, new ReadOnlySpan<FrameData>(in frameData));
|
||||
//ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, BarrierSync.AllShading, BarrierAccess.ShaderResource));
|
||||
|
||||
if (request.renderFunc != null)
|
||||
{
|
||||
request.renderFunc(in ctx, in request);
|
||||
}
|
||||
else
|
||||
{
|
||||
_renderGraph.Reset();
|
||||
_renderGraph.Reset();
|
||||
|
||||
var backBuffer = _renderGraph.ImportTexture(rt, "BackBuffer");
|
||||
var backBuffer = _renderGraph.ImportTexture(rt, "BackBuffer");
|
||||
|
||||
MeshletDebugPass(backBuffer, request.opaqueRenderList,
|
||||
uint.MaxValue,
|
||||
resourceDatabase.GetBindlessIndex(viewBufferResource),
|
||||
resourceDatabase.GetBindlessIndex(instanceBufferResource));
|
||||
MeshletDebugPass(backBuffer, request.opaqueRenderList,
|
||||
uint.MaxValue,
|
||||
resourceDatabase.GetBindlessIndex(viewBufferResource),
|
||||
resourceDatabase.GetBindlessIndex(instanceBufferResource));
|
||||
|
||||
var viewState = new ViewState(rtSize.x, rtSize.y, rtSize.x, rtSize.y);
|
||||
_renderGraph.Compile(viewState);
|
||||
_renderGraph.Execute(ctx.CommandBuffer);
|
||||
}
|
||||
var viewState = new ViewState(rtSize.x, rtSize.y, rtSize.x, rtSize.y);
|
||||
_renderGraph.Compile(viewState);
|
||||
_renderGraph.Execute(ctx.CommandBuffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (request.swapChainIndex >= 0)
|
||||
{
|
||||
_renderSystem.SwapChainManager.ReleaseSwapChain(request.swapChainIndex);
|
||||
renderSystem.SwapChainManager.ReleaseSwapChain(request.swapChainIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RenderPipeline;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Ghost.Graphics.Test.RenderPipeline;
|
||||
|
||||
internal sealed class TestRenderPayload : IRenderPayload
|
||||
{
|
||||
public class FrameData
|
||||
{
|
||||
public UnsafeList<RenderRequest> renderRequests;
|
||||
}
|
||||
|
||||
private readonly RenderSystem _renderSystem;
|
||||
private readonly FrameData[] _frameData;
|
||||
|
||||
public RenderSystem RenderSystem => _renderSystem;
|
||||
public ReadOnlySpan<FrameData> FrameRequestData => _frameData;
|
||||
|
||||
public TestRenderPayload(RenderSystem renderSystem)
|
||||
{
|
||||
_renderSystem = renderSystem;
|
||||
_frameData = new FrameData[renderSystem.MaxFrameLatency];
|
||||
|
||||
for (int i = 0; i < _frameData.Length; i++)
|
||||
{
|
||||
_frameData[i].renderRequests = new UnsafeList<RenderRequest>(2, Allocator.Persistent);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRenderRequest(RenderRequest request)
|
||||
{
|
||||
var index = (int)(_renderSystem.CPUFenceValue % (uint)_frameData.Length);
|
||||
_frameData[index].renderRequests.Add(request);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
for (int i = 0; i < _frameData.Length; i++)
|
||||
{
|
||||
_frameData[i].renderRequests.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TestRenderPipelineSettings : IRenderPipelineSettings
|
||||
{
|
||||
public void CreatePipeline(RenderSystem renderSystem, out IRenderPipeline renderPipeline, out IRenderPayload renderPayload)
|
||||
{
|
||||
renderPipeline = new TestRenderPipeline(renderSystem);
|
||||
renderPayload = new TestRenderPayload(renderSystem);
|
||||
}
|
||||
}
|
||||
145
src/Test/Ghost.Graphics.Test/Systems/RenderExtractionSystem.cs
Normal file
145
src/Test/Ghost.Graphics.Test/Systems/RenderExtractionSystem.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Engine;
|
||||
using Ghost.Engine.Components;
|
||||
using Ghost.Entities;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.Test.RenderPipeline;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
|
||||
namespace Ghost.Graphics.Test.Systems;
|
||||
|
||||
public class RenderExtractionSystem : ISystem
|
||||
{
|
||||
private RenderSystem _renderSystem = null!;
|
||||
|
||||
private Identifier<EntityQuery> _cameraQueryID;
|
||||
private Identifier<EntityQuery> _meshQueryID;
|
||||
|
||||
public void Initialize(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
_renderSystem = systemAPI.World.GetService<RenderSystem>();
|
||||
|
||||
var builder = new QueryBuilder();
|
||||
|
||||
_cameraQueryID = builder
|
||||
.WithAll<Camera, LocalToWorld>()
|
||||
.Build(systemAPI.World, false);
|
||||
|
||||
builder.Clear();
|
||||
|
||||
_meshQueryID = builder
|
||||
.WithAll<MeshInstance, LocalToWorld>()
|
||||
.Build(systemAPI.World, true);
|
||||
}
|
||||
|
||||
public void Update(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
if (_meshQueryID.IsInvalid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var cameraQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_cameraQueryID);
|
||||
ref var meshQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_meshQueryID);
|
||||
|
||||
foreach (var (cam, camLtw) in cameraQuery.GetComponentIterator<Camera, LocalToWorld>())
|
||||
{
|
||||
ref readonly var camRef = ref cam.Get();
|
||||
ref readonly var camLtwRef = ref camLtw.Get();
|
||||
|
||||
// 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);
|
||||
var shadowCasterRenderList = new RenderList(1, 64, Allocator.FreeList);
|
||||
|
||||
// TODO: This chould be done in earallel jobs.
|
||||
foreach (var chunk in meshQuery.GetChunkIterator())
|
||||
{
|
||||
var meshInstances = chunk.GetComponentData<MeshInstance>();
|
||||
var localToWorlds = chunk.GetComponentData<LocalToWorld>();
|
||||
|
||||
for (var i = 0; i < chunk.EntityCount; i++)
|
||||
{
|
||||
ref readonly var meshInstance = ref meshInstances[i];
|
||||
if ((meshInstance.renderingLayerMask & camRef.renderingLayerMask) == 0u)
|
||||
{
|
||||
// Not in the same rendering layer, skip.
|
||||
continue;
|
||||
}
|
||||
|
||||
ref readonly var meshLtw = ref localToWorlds[i];
|
||||
|
||||
var meshPosition = meshLtw.matrix.c3.xyz;
|
||||
var camPosition = camLtwRef.matrix.c3.xyz;
|
||||
var distance = math.distance(meshPosition, camPosition);
|
||||
|
||||
// TODO: Use bounding sphere or AABB for better culling. Currently it just uses the pivot point which can cause popping when the pivot is far from the actual geometry.
|
||||
if (distance < camRef.nearClipPlane || distance > camRef.farClipPlane)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (meshInstance.shadowCastingMode != ShadowCastingMode.ShadowsOnly)
|
||||
{
|
||||
renderList.Add(new RenderRecord
|
||||
{
|
||||
localToWorld = meshLtw.matrix,
|
||||
mesh = meshInstance.mesh,
|
||||
materialPalette = meshInstance.materialPalette,
|
||||
renderingLayerMask = meshInstance.renderingLayerMask,
|
||||
}, 0);
|
||||
}
|
||||
|
||||
if (meshInstance.shadowCastingMode != ShadowCastingMode.Off)
|
||||
{
|
||||
shadowCasterRenderList.Add(new RenderRecord
|
||||
{
|
||||
localToWorld = meshLtw.matrix,
|
||||
mesh = meshInstance.mesh,
|
||||
materialPalette = meshInstance.materialPalette,
|
||||
renderingLayerMask = meshInstance.renderingLayerMask,
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var request = new RenderRequest
|
||||
{
|
||||
swapChainIndex = camRef.swapChainIndex,
|
||||
colorTarget = camRef.colorTarget,
|
||||
depthTarget = camRef.depthTarget,
|
||||
opaqueRenderList = renderList,
|
||||
shadowCasterRenderList = shadowCasterRenderList,
|
||||
transparentRenderList = transparentRenderList,
|
||||
view = new RenderView
|
||||
{
|
||||
localToWorld = camLtwRef.matrix,
|
||||
//viewMatrix = viewMatrix,
|
||||
//projectionMatrix = projectionMatrix,
|
||||
//position = camLtwRef.matrix.c3.xyz,
|
||||
|
||||
//frustum = frustum,
|
||||
nearClipPlane = camRef.nearClipPlane,
|
||||
farClipPlane = camRef.farClipPlane,
|
||||
|
||||
sensorSize = camRef.sensorSize,
|
||||
gateFit = camRef.gateFit,
|
||||
iso = camRef.iso,
|
||||
shutterSpeed = camRef.shutterSpeed,
|
||||
aperture = camRef.aperture,
|
||||
focalLength = camRef.focalLength,
|
||||
focusDistance = camRef.focusDistance,
|
||||
|
||||
renderingLayerMask = camRef.renderingLayerMask,
|
||||
},
|
||||
};
|
||||
|
||||
((TestRenderPayload)_renderSystem.RenderPayload).AddRenderRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
public void Cleanup(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ghost.Graphics.Test.Windows;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user