feat(render): add ECS-based test render pipeline
Introduce TestRenderPipeline and settings, replacing MeshRenderPass. The new pipeline manages per-frame instance, view, and global data buffers, and uploads them for each render request. Refactor GraphicsTestWindow to use ECS World, setting up camera and mesh entities. Remove MeshRenderPass and related demo code. Add TotalRecordCount to RenderList, new data structs for buffer uploads, and static masks to RenderingLayerMask. Update project references and InternalsVisibleTo for Ghost.Graphics.Test access.
This commit is contained in:
@@ -1,8 +1,15 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Engine.Components;
|
||||
using Ghost.Engine.Systems;
|
||||
using Ghost.Engine.Utilities;
|
||||
using Ghost.Entities;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Utilities;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
|
||||
namespace Ghost.Graphics.Test.Windows;
|
||||
@@ -10,8 +17,8 @@ namespace Ghost.Graphics.Test.Windows;
|
||||
public sealed partial class GraphicsTestWindow : Window
|
||||
{
|
||||
private RenderSystem? _renderSystem;
|
||||
private IRenderer? _renderer;
|
||||
private ISwapChain? _swapChain;
|
||||
private World? _world;
|
||||
|
||||
private bool _isFirstActivationHandled;
|
||||
|
||||
@@ -33,16 +40,12 @@ public sealed partial class GraphicsTestWindow : Window
|
||||
return;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
Misaki.HighPerformance.LowLevel.Buffer.AllocationManager.EnableDebugLayer();
|
||||
#endif
|
||||
|
||||
_renderSystem = new RenderSystem(new RenderSystemDesc()
|
||||
{
|
||||
FrameBufferCount = 2,
|
||||
GraphicsAPI = GraphicsAPI.Direct3D12
|
||||
});
|
||||
_renderer = _renderSystem.GraphicsEngine.CreateRenderer();
|
||||
|
||||
_swapChain = _renderSystem.GraphicsEngine.CreateSwapChain(new SwapChainDesc
|
||||
{
|
||||
Width = (uint)AppWindow.Size.Width,
|
||||
@@ -53,9 +56,96 @@ public sealed partial class GraphicsTestWindow : Window
|
||||
Target = SwapChainTarget.FromCompositionSurface(Panel)
|
||||
});
|
||||
|
||||
_renderer.RenderOutput = new SwapChainRenderOutput(_swapChain);
|
||||
|
||||
_renderSystem.RenderPipelineSettings = new RenderPasses.TestRenderPipelineSettings();
|
||||
_renderSystem.Start();
|
||||
|
||||
// ECS Setup
|
||||
_world = World.Create();
|
||||
_world.AddService(_renderSystem);
|
||||
|
||||
// Add Systems
|
||||
_world.SystemManager.GetSystem<DefaultSystemGroup>().AddSystem<RenderExtractionSystem>();
|
||||
|
||||
_world.SystemManager.InitializeAll(default);
|
||||
|
||||
// Create Camera Entity
|
||||
|
||||
using var scope = AllocationManager.CreateStackScope();
|
||||
var camSet = new ComponentSet(scope.AllocationHandle, ComponentTypeID<Camera>.Value, ComponentTypeID<LocalToWorld>.Value);
|
||||
var cameraEntity = _world.EntityManager.CreateEntity(camSet);
|
||||
|
||||
_world.EntityManager.SetComponent(cameraEntity, new Camera
|
||||
{
|
||||
colorTarget = _swapChain.GetCurrentBackBuffer(), // TODO: This should be updated every frame to the current back buffer.
|
||||
depthTarget = Handle<Texture>.Invalid,
|
||||
nearClipPlane = 0.1f,
|
||||
farClipPlane = 1000.0f,
|
||||
focalLength = 50.0f,
|
||||
sensorSize = new float2(36.0f, 24.0f),
|
||||
gateFit = GateFit.Vertical,
|
||||
renderingLayerMask = RenderingLayerMask.All,
|
||||
});
|
||||
|
||||
_world.EntityManager.SetComponent(cameraEntity, new LocalToWorld
|
||||
{
|
||||
matrix = float4x4.TRS(new float3(0.0f, 0.0f, -5.0f), quaternion.identity, new float3(1.0f, 1.0f, 1.0f))
|
||||
});
|
||||
|
||||
// var cameraEntity = _world.EntityManager.CreateEntity();
|
||||
// _world.EntityManager.AddComponent(cameraEntity, new Camera
|
||||
// {
|
||||
// colorTarget = _swapChain.GetCurrentBackBuffer(),
|
||||
// depthTarget = Handle<Texture>.Invalid,
|
||||
// nearClipPlane = 0.1f,
|
||||
// farClipPlane = 1000.0f,
|
||||
// focalLength = 50.0f,
|
||||
// sensorSize = new float2(36.0f, 24.0f),
|
||||
// gateFit = GateFit.Fill,
|
||||
// renderingLayerMask = new RenderingLayerMask(uint.MaxValue),
|
||||
// });
|
||||
//
|
||||
// _world.EntityManager.AddComponent(cameraEntity, new LocalToWorld
|
||||
// {
|
||||
// matrix = float4x4.TRS(new float3(0.0f, 0.0f, -5.0f), quaternion.identity, new float3(1.0f, 1.0f, 1.0f))
|
||||
// });
|
||||
|
||||
// Create Mesh Entity
|
||||
var meshEntity = _world.EntityManager.CreateEntity();
|
||||
|
||||
MeshBuilder.CreateCube(0.75f, default, Allocator.Persistent, out var vertices, out var indices);
|
||||
|
||||
var directCmd = _renderSystem.GraphicsEngine.CreateCommandBuffer(CommandBufferType.Graphics);
|
||||
var ctx = new RenderingContext(_renderSystem.GraphicsEngine, _renderSystem.ResourceManager, directCmd);
|
||||
|
||||
directCmd.Begin(_renderSystem.GraphicsEngine.CreateCommandAllocator(CommandBufferType.Graphics));
|
||||
|
||||
var meshHandle = ctx.CreateMesh(vertices, indices, true);
|
||||
|
||||
var meshRefResult = _renderSystem.ResourceManager.GetMeshReference(meshHandle);
|
||||
if (meshRefResult.IsSuccess)
|
||||
{
|
||||
meshRefResult.Value.CookMeshlets();
|
||||
}
|
||||
|
||||
ctx.UploadMeshlets(meshHandle);
|
||||
ctx.UpdateObjectData(meshHandle, float4x4.identity);
|
||||
|
||||
directCmd.End().ThrowIfFailed();
|
||||
_renderSystem.GraphicsEngine.Device.GraphicsQueue.Submit(directCmd);
|
||||
_renderSystem.GraphicsEngine.Device.GraphicsQueue.WaitIdle();
|
||||
|
||||
_world.EntityManager.AddComponent(meshEntity, new MeshInstance
|
||||
{
|
||||
mesh = meshHandle,
|
||||
renderingLayerMask = new RenderingLayerMask(uint.MaxValue),
|
||||
shadowCastingMode = Engine.ShadowCastingMode.On
|
||||
});
|
||||
|
||||
_world.EntityManager.AddComponent(meshEntity, new LocalToWorld
|
||||
{
|
||||
matrix = float4x4.identity
|
||||
});
|
||||
|
||||
CompositionTarget.Rendering += OnRendering;
|
||||
|
||||
e.Handled = true;
|
||||
@@ -67,16 +157,20 @@ public sealed partial class GraphicsTestWindow : Window
|
||||
CompositionTarget.Rendering -= OnRendering;
|
||||
_renderSystem?.Stop();
|
||||
|
||||
_renderer?.Dispose();
|
||||
if (_world != null)
|
||||
{
|
||||
World.Destroy(_world.ID);
|
||||
}
|
||||
|
||||
_swapChain?.Dispose();
|
||||
_renderSystem?.Dispose();
|
||||
|
||||
Misaki.HighPerformance.LowLevel.Buffer.AllocationManager.Dispose();
|
||||
AllocationManager.Dispose();
|
||||
}
|
||||
|
||||
private void SwapChainPanel_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (_renderSystem == null || _swapChain == null || _renderer == null)
|
||||
if (_renderSystem == null || _swapChain == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -90,8 +184,6 @@ public sealed partial class GraphicsTestWindow : Window
|
||||
}
|
||||
|
||||
_renderSystem.RequestSwapChainResize(_swapChain, new uint2(newWidth, newHeight));
|
||||
_renderer.RenderOutput!.Viewport = new ViewportDesc { Width = newWidth, Height = newHeight, MinDepth = 0.0f, MaxDepth = 1.0f };
|
||||
_renderer.RenderOutput!.Scissor = new RectDesc { Right = newWidth, Bottom = newHeight };
|
||||
}
|
||||
|
||||
private void SwapChainPanel_CompositionScaleChanged(SwapChainPanel sender, object args)
|
||||
@@ -101,13 +193,17 @@ public sealed partial class GraphicsTestWindow : Window
|
||||
|
||||
private void OnRendering(object? sender, object e)
|
||||
{
|
||||
if (_renderSystem == null)
|
||||
if (_renderSystem == null || _world == null || _swapChain == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_renderSystem.CPUFenceValue < _renderSystem.GPUFenceValue + _renderSystem.MaxFrameLatency)
|
||||
{
|
||||
// TODO: In a real system, the camera target would be updated correctly.
|
||||
// For now, let's just make sure it renders to the correct back buffer.
|
||||
|
||||
_world.SystemManager.UpdateAll(default); // This runs RenderExtractionSystem, extracting data and queueing RenderRequests
|
||||
_renderSystem.SignalCPUReady();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user