feat(engine)!: refactor graphics, ECS, and logging APIs
Major refactor of graphics and ECS infrastructure: - Removed IResourceManager, IRenderSystem, IFenceSynchronizer interfaces; ResourceManager and RenderSystem are now concrete classes. - Updated all render graph, pipeline, and context code to use concrete ResourceManager. - Refactored camera/frustum math and render extraction for clarity and correctness; frustum now uses inline arrays. - RenderingLayerMask is now an immutable struct with bitwise operators. - Meshlet and meshlet group data structures improved; meshlet build callback signature updated. - Logging system overhauled: LogMessage is now a class, LogCollection supports change events, and Logger is used directly in the debug console. - ECS query API: ChunkView.Count renamed to EntityCount; query builder/iterators use VirtualStack.Scope. - Updated render pipeline and passes for new resource manager and render list APIs. - Cleaned up obsolete files, improved code style, and updated documentation. - HLSL meshlet shader updated for new struct layout. - Debug console now uses new logger and log collection. BREAKING CHANGE: Public APIs for resource management, rendering, ECS queries, and logging have changed. Interfaces removed; use new concrete types and updated method signatures.
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RenderGraphModule;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics.RenderPipeline;
|
||||
|
||||
public partial class GhostRenderPipeline
|
||||
{
|
||||
private class MeshRenderPassData
|
||||
{
|
||||
public RenderList renderList;
|
||||
public Identifier<RGTexture> renderTarget;
|
||||
}
|
||||
|
||||
private class BlitPassData
|
||||
{
|
||||
public Identifier<RGTexture> source;
|
||||
public Identifier<RGTexture> destination;
|
||||
|
||||
public Handle<Material> blitMaterial;
|
||||
public Identifier<Sampler> sampler;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct ShaderProperties_MyShader_Standard
|
||||
{
|
||||
public float4 color;
|
||||
public uint texture1;
|
||||
public uint texture2;
|
||||
public uint texture3;
|
||||
public uint texture4;
|
||||
public uint tex_sampler;
|
||||
|
||||
private readonly uint _padding1;
|
||||
private readonly uint _padding2;
|
||||
private readonly uint _padding3;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct ShaderProperties_Hidden_Blit
|
||||
{
|
||||
public uint mainTex;
|
||||
public uint sampler_mainTex;
|
||||
private readonly uint _padding1;
|
||||
private readonly uint _padding2;
|
||||
}
|
||||
|
||||
private void RenderTest(RenderGraph graph, Identifier<RGTexture> backbuffer)
|
||||
{
|
||||
Identifier<RGTexture> renderTarget;
|
||||
using (var builder = graph.AddRasterRenderPass<MeshRenderPassData>("Mesh Render Pass", out var passData))
|
||||
{
|
||||
passData.mesh = _mesh;
|
||||
passData.material = _material;
|
||||
|
||||
passData.renderTarget = builder.CreateTexture(RGTextureDesc.Relative(1.0f, TextureFormat.R8G8B8A8_UNorm), "Render Target");
|
||||
builder.SetColorAttachment(passData.renderTarget, 0);
|
||||
|
||||
renderTarget = passData.renderTarget;
|
||||
|
||||
builder.SetRenderFunc<MeshRenderPassData>(static (data, ctx) =>
|
||||
{
|
||||
ctx.SetActiveMaterial(data.material);
|
||||
ctx.SetActiveMesh(data.mesh);
|
||||
|
||||
var threadGroupCountX = ((uint)ctx.ActiveMeshIndexCount + 2u) / 3u;
|
||||
ctx.DispatchMesh(new uint3(threadGroupCountX, 1u, 1u));
|
||||
});
|
||||
}
|
||||
|
||||
using (var builder = graph.AddUnsafeRenderPass<BlitPassData>("Blit Pass", out var passData))
|
||||
{
|
||||
passData.source = renderTarget;
|
||||
passData.destination = backbuffer;
|
||||
passData.blitMaterial = _blitMaterial;
|
||||
passData.sampler = _sampler;
|
||||
|
||||
builder.UseTexture(passData.source, AccessFlags.Read);
|
||||
builder.UseTexture(passData.destination, AccessFlags.WriteAll);
|
||||
|
||||
builder.SetRenderFunc<BlitPassData>(static (data, ctx) =>
|
||||
{
|
||||
var r = ctx.ResourceManager.GetMaterialReference(data.blitMaterial);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var matRef = ref r.Value;
|
||||
var blitProps = new ShaderProperties_Hidden_Blit
|
||||
{
|
||||
mainTex = ctx.ResourceDatabase.GetBindlessIndex(ctx.GetActualResource(data.source.AsResource())),
|
||||
sampler_mainTex = (uint)data.sampler.Value,
|
||||
};
|
||||
|
||||
matRef.SetPropertyCache(in blitProps).ThrowIfFailed();
|
||||
matRef.UploadData(ctx.CommandBuffer, ctx.ResourceDatabase);
|
||||
|
||||
ctx.CommandBuffer.SetRenderTargets([ctx.GetActualTexture(data.destination)], Handle<Texture>.Invalid);
|
||||
|
||||
ctx.SetActiveMaterial(data.blitMaterial);
|
||||
ctx.SetActiveMesh(Handle<Mesh>.Invalid); // Generate a full-screen triangle dynamically in mesh shader.
|
||||
ctx.DispatchMesh(new uint3(1, 1, 1));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace Ghost.Graphics.RenderPipeline;
|
||||
|
||||
public sealed class GhostRenderPipelineSettings : IRenderPipelineSettings
|
||||
{
|
||||
public static IRenderPipeline CreatePipeline(IRenderSystem renderSystem)
|
||||
public static IRenderPipeline CreatePipeline(RenderSystem renderSystem)
|
||||
{
|
||||
return new GhostRenderPipeline(renderSystem);
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public unsafe partial class GhostRenderPipeline : IRenderPipeline
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
}
|
||||
|
||||
internal GhostRenderPipeline(IRenderSystem renderSystem)
|
||||
internal GhostRenderPipeline(RenderSystem renderSystem)
|
||||
{
|
||||
_renderGraph = new RenderGraph(renderSystem.ResourceManager,
|
||||
renderSystem.GraphicsEngine.ResourceAllocator,
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Ghost.Graphics.RenderPipeline;
|
||||
|
||||
public interface IRenderPipelineSettings
|
||||
{
|
||||
static abstract IRenderPipeline CreatePipeline(IRenderSystem renderSystem);
|
||||
static abstract IRenderPipeline CreatePipeline(RenderSystem renderSystem);
|
||||
}
|
||||
|
||||
public interface IRenderPipeline : IDisposable
|
||||
|
||||
99
src/Runtime/Ghost.Graphics/RenderPipeline/ShaderCode.hlsl
Normal file
99
src/Runtime/Ghost.Graphics/RenderPipeline/ShaderCode.hlsl
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "F:/csharp/GhostEngine/src/Runtime//Ghost.Graphics/Shaders/Includes/Properties.hlsl"
|
||||
#include "F:/csharp/GhostEngine/src/Runtime//Ghost.Graphics/Shaders/Includes/Common.hlsl"
|
||||
|
||||
struct PixelInput
|
||||
{
|
||||
float4 position : SV_POSITION;
|
||||
float4 color : COLOR;
|
||||
float4 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct Meshlet
|
||||
{
|
||||
float4 boundingSphere;
|
||||
float3 boundingBoxMin;
|
||||
float3 boundingBoxMax;
|
||||
uint vertexOffset;
|
||||
uint triangleOffset;
|
||||
uint groupIndex;
|
||||
float parentError;
|
||||
uint packedCounts; // byte vertexCount, byte triangleCount, byte localMaterialIndex, byte lodLevel
|
||||
};
|
||||
|
||||
[numthreads(64, 1, 1)] // 64 threads for max 64 vertices and up to 124 triangles
|
||||
[outputtopology("triangle")]
|
||||
void MSMain(
|
||||
uint3 groupThreadID : SV_GroupThreadID,
|
||||
uint groupID : SV_GroupID,
|
||||
out vertices PixelInput outVerts[64],
|
||||
out indices uint3 outTris[124])
|
||||
{
|
||||
PerObjectData perObjectData = LoadData<PerObjectData>(g_PushConstantData.perObjectBuffer, 0);
|
||||
|
||||
ByteAddressBuffer meshletBuffer = GET_BUFFER(perObjectData.meshletBuffer);
|
||||
Meshlet m = meshletBuffer.Load<Meshlet>(groupID.x * sizeof(Meshlet));
|
||||
|
||||
uint vertexCount = m.packedCounts & 0xFF;
|
||||
uint triangleCount = (m.packedCounts >> 8) & 0xFF;
|
||||
|
||||
SetMeshOutputCounts(vertexCount, triangleCount);
|
||||
|
||||
ByteAddressBuffer meshletVerticesBuffer = GET_BUFFER(perObjectData.meshletVerticesBuffer);
|
||||
ByteAddressBuffer meshletTrianglesBuffer = GET_BUFFER(perObjectData.meshletTrianglesBuffer);
|
||||
|
||||
// Write vertex output
|
||||
if (groupThreadID.x < vertexCount)
|
||||
{
|
||||
uint vertexIndex = meshletVerticesBuffer.Load((m.vertexOffset + groupThreadID.x) * 4);
|
||||
ByteAddressBuffer vertices = GET_BUFFER(perObjectData.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);
|
||||
float4 worldPos = mul(perObjectData.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);
|
||||
|
||||
outVerts[groupThreadID.x].color = v.color;
|
||||
outVerts[groupThreadID.x].uv = v.uv;
|
||||
}
|
||||
|
||||
// Write triangle output (1 thread processes 1 triangle)
|
||||
// We could pack 3 indices in a uint or just use byte offset
|
||||
// In our CPU code, we packed it as individual bytes, so 3 bytes per triangle.
|
||||
// For 124 triangles, we have 372 bytes.
|
||||
if (groupThreadID.x < triangleCount)
|
||||
{
|
||||
uint triangleIndex = groupThreadID.x;
|
||||
uint baseOffset = m.triangleOffset + triangleIndex * 3;
|
||||
|
||||
// Load 4 bytes to get the 3 index bytes
|
||||
// Needs byte-aligned loading
|
||||
uint wordOffset = baseOffset & ~3;
|
||||
uint shift = (baseOffset & 3) * 8;
|
||||
uint packedIndices1 = meshletTrianglesBuffer.Load(wordOffset);
|
||||
uint packedIndices2 = meshletTrianglesBuffer.Load(wordOffset + 4);
|
||||
|
||||
uint64_t combined = ((uint64_t)packedIndices2 << 32) | packedIndices1;
|
||||
uint packedIndices = (uint)(combined >> shift);
|
||||
|
||||
uint i0 = packedIndices & 0xFF;
|
||||
uint i1 = (packedIndices >> 8) & 0xFF;
|
||||
uint i2 = (packedIndices >> 16) & 0xFF;
|
||||
|
||||
outTris[triangleIndex] = uint3(i0, i1, i2);
|
||||
}
|
||||
}
|
||||
|
||||
float4 PSMain(PixelInput input) : SV_TARGET
|
||||
{
|
||||
PerMaterialData perMaterialData = LoadData<PerMaterialData>(g_PushConstantData.perMaterialBuffer, 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);
|
||||
float4 color3 = SAMPLE_TEXTURE2D(perMaterialData.texture3, perMaterialData.tex_sampler, input.uv.xy);
|
||||
float4 color4 = SAMPLE_TEXTURE2D(perMaterialData.texture4, perMaterialData.tex_sampler, input.uv.xy);
|
||||
|
||||
float4 blendedColor = (color1 + color2 + color3 + color4) * 0.25f;
|
||||
return perMaterialData.color * blendedColor + input.color;
|
||||
}
|
||||
Reference in New Issue
Block a user