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:
2026-04-06 22:05:48 +09:00
parent c6bdbe0710
commit 6c96d4cf50
20 changed files with 399 additions and 200 deletions

View File

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

View File

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

View 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)
{
}
}

View File

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