forked from Misaki/GhostEngine
small backup
This commit is contained in:
@@ -383,7 +383,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
for (var i = 0; i < rtDescs.Length; i++)
|
for (var i = 0; i < rtDescs.Length; i++)
|
||||||
{
|
{
|
||||||
var rtDesc = rtDescs[i];
|
var rtDesc = rtDescs[i];
|
||||||
if (!rtDesc.Texture.IsValid)
|
if (rtDesc.Texture.IsInvalid)
|
||||||
{
|
{
|
||||||
RecordError(nameof(BeginRenderPass), ErrorStatus.InvalidArgument);
|
RecordError(nameof(BeginRenderPass), ErrorStatus.InvalidArgument);
|
||||||
continue;
|
continue;
|
||||||
@@ -396,7 +396,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var record = recordResult.Value;
|
ref var record = ref recordResult.Value;
|
||||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.rtv);
|
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.rtv);
|
||||||
var format = record.desc.TextureDescription.Format.ToDXGIFormat();
|
var format = record.desc.TextureDescription.Format.ToDXGIFormat();
|
||||||
var clearColor = rtDesc.ClearColor;
|
var clearColor = rtDesc.ClearColor;
|
||||||
@@ -431,7 +431,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var record = recordResult.Value;
|
ref var record = ref recordResult.Value;
|
||||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.dsv);
|
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.dsv);
|
||||||
var format = record.desc.TextureDescription.Format.ToDXGIFormat();
|
var format = record.desc.TextureDescription.Format.ToDXGIFormat();
|
||||||
|
|
||||||
@@ -544,7 +544,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var record = recordResult.Value;
|
ref var record = ref recordResult.Value;
|
||||||
var vbView = new D3D12_VERTEX_BUFFER_VIEW
|
var vbView = new D3D12_VERTEX_BUFFER_VIEW
|
||||||
{
|
{
|
||||||
BufferLocation = record.ResourcePtr.Get()->GetGPUVirtualAddress() + offset,
|
BufferLocation = record.ResourcePtr.Get()->GetGPUVirtualAddress() + offset,
|
||||||
|
|||||||
@@ -166,35 +166,42 @@ public sealed class RenderGraph
|
|||||||
offset = ComputeTextureHash(pData, offset, pass.colorAccess[j].id);
|
offset = ComputeTextureHash(pData, offset, pass.colorAccess[j].id);
|
||||||
}
|
}
|
||||||
|
|
||||||
*(int*)(pData + offset) = pass.resourceReads.Count;
|
for (var j = 0; j < (int)RenderGraphResourceType.Count; j++)
|
||||||
offset += sizeof(int);
|
|
||||||
for (var j = 0; j < pass.resourceReads.Count; j++)
|
|
||||||
{
|
{
|
||||||
*(int*)(pData + offset) = pass.resourceReads[j].Value;
|
var readList = pass.resourceReads[j];
|
||||||
offset += sizeof(int);
|
var writeList = pass.resourceWrites[j];
|
||||||
}
|
var createList = pass.resourceCreates[j];
|
||||||
|
|
||||||
*(int*)(pData + offset) = pass.resourceWrites.Count;
|
*(int*)(pData + offset) = readList.Count;
|
||||||
offset += sizeof(int);
|
|
||||||
for (var j = 0; j < pass.resourceWrites.Count; j++)
|
|
||||||
{
|
|
||||||
*(int*)(pData + offset) = pass.resourceWrites[j].Value;
|
|
||||||
offset += sizeof(int);
|
offset += sizeof(int);
|
||||||
}
|
for (var k = 0; k < readList.Count; k++)
|
||||||
|
{
|
||||||
|
*(int*)(pData + offset) = readList[k].Value;
|
||||||
|
offset += sizeof(int);
|
||||||
|
}
|
||||||
|
|
||||||
*(int*)(pData + offset) = pass.resourceCreates.Count;
|
*(int*)(pData + offset) = writeList.Count;
|
||||||
offset += sizeof(int);
|
|
||||||
for (var j = 0; j < pass.resourceCreates.Count; j++)
|
|
||||||
{
|
|
||||||
*(int*)(pData + offset) = pass.resourceCreates[j].Value;
|
|
||||||
offset += sizeof(int);
|
offset += sizeof(int);
|
||||||
|
for (var k = 0; k < writeList.Count; k++)
|
||||||
|
{
|
||||||
|
*(int*)(pData + offset) = writeList[k].Value;
|
||||||
|
offset += sizeof(int);
|
||||||
|
}
|
||||||
|
|
||||||
|
*(int*)(pData + offset) = createList.Count;
|
||||||
|
offset += sizeof(int);
|
||||||
|
for (var k = 0; k < createList.Count; k++)
|
||||||
|
{
|
||||||
|
*(int*)(pData + offset) = createList[k].Value;
|
||||||
|
offset += sizeof(int);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//// Hash resource descriptors
|
//// Hash resource descriptors
|
||||||
//for (var i = 0; i < _resources.TextureResourceCount; i++)
|
//for (var j = 0; j < _resources.TextureResourceCount; j++)
|
||||||
//{
|
//{
|
||||||
// var resource = _resources.GetTextureResourceByIndex(i);
|
// var resource = _resources.GetTextureResourceByIndex(j);
|
||||||
|
|
||||||
// *(int*)(pData + offset) = resource.descriptor.width;
|
// *(int*)(pData + offset) = resource.descriptor.width;
|
||||||
// offset += sizeof(int);
|
// offset += sizeof(int);
|
||||||
@@ -260,15 +267,19 @@ public sealed class RenderGraph
|
|||||||
{
|
{
|
||||||
var pass = _passes[i];
|
var pass = _passes[i];
|
||||||
|
|
||||||
// Check if this pass writes to any imported textures
|
// Check if this pass writes to any imported resources
|
||||||
for (var j = 0; j < pass.resourceWrites.Count; j++)
|
for (var j = 0; j < (int)RenderGraphResourceType.Count; j++)
|
||||||
{
|
{
|
||||||
var writeHandle = pass.resourceWrites[j];
|
var writeList = pass.resourceWrites[j];
|
||||||
var resource = _resources.GetResource(writeHandle);
|
for (var k = 0; k < writeList.Count; k++)
|
||||||
if (resource.isImported)
|
|
||||||
{
|
{
|
||||||
pass.hasSideEffects = true;
|
var writeHandle = writeList[k];
|
||||||
break;
|
var resource = _resources.GetResource(writeHandle);
|
||||||
|
if (resource.isImported)
|
||||||
|
{
|
||||||
|
pass.hasSideEffects = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -403,13 +414,17 @@ public sealed class RenderGraph
|
|||||||
private void UnculDependencies(RenderGraphPassBase pass)
|
private void UnculDependencies(RenderGraphPassBase pass)
|
||||||
{
|
{
|
||||||
// Un-cull producers of read resources
|
// Un-cull producers of read resources
|
||||||
for (var i = 0; i < pass.resourceReads.Count; i++)
|
for (var i = 0; i < (int)RenderGraphResourceType.Count; i++)
|
||||||
{
|
{
|
||||||
UnculProducer(pass.resourceReads[i]);
|
var readList = pass.resourceReads[i];
|
||||||
|
for (var j = 0; j < readList.Count; j++)
|
||||||
|
{
|
||||||
|
UnculProducer(readList[j]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Un-cull producers of color attachments
|
// Un-cull producers of color attachments
|
||||||
for (var i = 0; i <= pass.maxColorIndex; i++)
|
for (var i = 0; i < pass.maxColorIndex; i++)
|
||||||
{
|
{
|
||||||
if (pass.colorAccess[i].id.IsValid)
|
if (pass.colorAccess[i].id.IsValid)
|
||||||
{
|
{
|
||||||
@@ -533,16 +548,20 @@ public sealed class RenderGraph
|
|||||||
private void InsertTransitionBarriers(RenderGraphPassBase pass, int passIdx)
|
private void InsertTransitionBarriers(RenderGraphPassBase pass, int passIdx)
|
||||||
{
|
{
|
||||||
// Process reads (transition to shader resource)
|
// Process reads (transition to shader resource)
|
||||||
for (var i = 0; i < pass.resourceReads.Count; i++)
|
for (var i = 0; i < (int)RenderGraphResourceType.Count; i++)
|
||||||
{
|
{
|
||||||
var handle = pass.resourceReads[i];
|
var readList = pass.resourceReads[i];
|
||||||
InsertTransitionIfNeeded(handle, ResourceState.ShaderResource, passIdx);
|
for (var j = 0; j < readList.Count; j++)
|
||||||
|
{
|
||||||
|
var handle = readList[j];
|
||||||
|
InsertTransitionIfNeeded(handle, ResourceState.ShaderResource, passIdx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (pass.type)
|
switch (pass.type)
|
||||||
{
|
{
|
||||||
case RenderPassType.Raster:
|
case RenderPassType.Raster:
|
||||||
for (var i = 0; i <= pass.maxColorIndex; i++)
|
for (var i = 0; i < pass.maxColorIndex; i++)
|
||||||
{
|
{
|
||||||
var access = pass.colorAccess[i];
|
var access = pass.colorAccess[i];
|
||||||
InsertTransitionIfNeeded(access.id.AsResource(), ResourceState.RenderTarget, passIdx);
|
InsertTransitionIfNeeded(access.id.AsResource(), ResourceState.RenderTarget, passIdx);
|
||||||
@@ -561,10 +580,14 @@ public sealed class RenderGraph
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
case RenderPassType.Compute:
|
case RenderPassType.Compute:
|
||||||
for (var i = 0; i < pass.resourceWrites.Count; i++)
|
for (var i = 0; i < (int)RenderGraphResourceType.Count; i++)
|
||||||
{
|
{
|
||||||
var id = pass.resourceWrites[i];
|
var writeList = pass.resourceWrites[i];
|
||||||
InsertTransitionIfNeeded(id, ResourceState.UnorderedAccess, passIdx);
|
for (var j = 0; j < writeList.Count; j++)
|
||||||
|
{
|
||||||
|
var id = writeList[j];
|
||||||
|
InsertTransitionIfNeeded(id, ResourceState.UnorderedAccess, passIdx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
663
Ghost.RenderGraph.Concept/RenderGraph.cs.bak
Normal file
663
Ghost.RenderGraph.Concept/RenderGraph.cs.bak
Normal file
@@ -0,0 +1,663 @@
|
|||||||
|
using Ghost.Core;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
using System.IO.Hashing;
|
||||||
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
|
namespace Ghost.RenderGraph.Concept;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Main render graph class that manages resource allocation and pass execution.
|
||||||
|
///
|
||||||
|
/// Design principles for minimal GC:
|
||||||
|
/// - Object pooling for all passes and resources
|
||||||
|
/// - Reuse collections across frames (Clear() instead of new)
|
||||||
|
/// - Avoid LINQ and foreach over interfaces
|
||||||
|
/// - Pre-allocate capacity based on expected usage
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RenderGraph
|
||||||
|
{
|
||||||
|
private readonly RenderGraphResourceRegistry _resources = new();
|
||||||
|
private readonly RenderGraphObjectPool _objectPool = new();
|
||||||
|
private readonly List<RenderGraphPassBase> _passes = new(64);
|
||||||
|
private readonly List<RenderGraphPassBase> _compiledPasses = new(64);
|
||||||
|
private readonly RenderGraphBuilder _builder = new();
|
||||||
|
private readonly MockCommandBuffer _commandBuffer = new();
|
||||||
|
private readonly RenderContext _renderContext;
|
||||||
|
private readonly ResourceAliasingManager _aliasingManager = new();
|
||||||
|
private readonly Dictionary<int, ResourceState> _resourceStates = new(128);
|
||||||
|
private readonly List<ResourceBarrier> _barriers = new(128);
|
||||||
|
private readonly RenderGraphCompilationCache _compilationCache = new();
|
||||||
|
|
||||||
|
private bool _compiled;
|
||||||
|
|
||||||
|
public RenderGraphBlackboard Blackboard { get; } = new();
|
||||||
|
|
||||||
|
public RenderGraph()
|
||||||
|
{
|
||||||
|
_renderContext = new RenderContext(_commandBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the render graph for a new frame.
|
||||||
|
/// Reuses existing allocations to minimize GC.
|
||||||
|
/// </summary>
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
// Clear blackboard data
|
||||||
|
Blackboard.Clear();
|
||||||
|
|
||||||
|
// Reset resources but keep allocations
|
||||||
|
_resources.BeginFrame();
|
||||||
|
|
||||||
|
// Reset aliasing manager
|
||||||
|
_aliasingManager.BeginFrame();
|
||||||
|
|
||||||
|
// Clear resource states and barriers
|
||||||
|
_resourceStates.Clear();
|
||||||
|
_barriers.Clear();
|
||||||
|
|
||||||
|
// Return passes to the pool and reset count
|
||||||
|
for (var i = 0; i < _passes.Count; i++)
|
||||||
|
{
|
||||||
|
var pass = _passes[i];
|
||||||
|
pass.Reset(_objectPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
_passes.Clear();
|
||||||
|
|
||||||
|
// Clear compiled passes list
|
||||||
|
_compiledPasses.Clear();
|
||||||
|
_compiled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Imports an external texture into the render graph.
|
||||||
|
/// </summary>
|
||||||
|
public Identifier<RGTexture> ImportTexture(TextureDescriptor descriptor)
|
||||||
|
{
|
||||||
|
return _resources.ImportTexture(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IRasterRenderGraphBuilder AddRasterRenderPass<TPassData>(string name, out TPassData passData)
|
||||||
|
where TPassData : class, new()
|
||||||
|
{
|
||||||
|
var renderPass = _objectPool.Rent<RasterRenderGraphPass<TPassData>>();
|
||||||
|
renderPass.Init(_passes.Count, _objectPool.Rent<TPassData>(), name, RenderPassType.Raster);
|
||||||
|
passData = renderPass.passData;
|
||||||
|
|
||||||
|
_passes.Add(renderPass);
|
||||||
|
|
||||||
|
_builder.Init(this, renderPass, _resources);
|
||||||
|
return _builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IComputeRenderGraphBuilder AddComputeRenderPass<TPassData>(string name, out TPassData passData)
|
||||||
|
where TPassData : class, new()
|
||||||
|
{
|
||||||
|
var renderPass = _objectPool.Rent<ComputeRenderGraphPass<TPassData>>();
|
||||||
|
renderPass.Init(_passes.Count, _objectPool.Rent<TPassData>(), name, RenderPassType.Compute);
|
||||||
|
passData = renderPass.passData;
|
||||||
|
|
||||||
|
_passes.Add(renderPass);
|
||||||
|
|
||||||
|
_builder.Init(this, renderPass, _resources);
|
||||||
|
return _builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe int ComputeTextureHash(byte* pData, int offset, Identifier<RGTexture> texture)
|
||||||
|
{
|
||||||
|
if (texture.IsInvalid)
|
||||||
|
{
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
var resource = _resources.GetResource(texture.AsResource());
|
||||||
|
|
||||||
|
// In real implementation, we typically need to handle imported resources differently.
|
||||||
|
|
||||||
|
*(pData + offset) = resource.isImported ? (byte)1 : (byte)0;
|
||||||
|
offset += sizeof(byte);
|
||||||
|
|
||||||
|
*(TextureFormat*)(pData + offset) = resource.descriptor.format;
|
||||||
|
offset += sizeof(TextureFormat);
|
||||||
|
|
||||||
|
*(int*)(pData + offset) = resource.descriptor.width;
|
||||||
|
offset += sizeof(int);
|
||||||
|
|
||||||
|
*(int*)(pData + offset) = resource.descriptor.height;
|
||||||
|
offset += sizeof(int);
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe ulong ComputeGraphHash()
|
||||||
|
{
|
||||||
|
using var scope = AllocationManager.CreateStackScope();
|
||||||
|
var bufferPool = new UnsafeList<byte>(2048, scope.AllocationHandle);
|
||||||
|
var pData = (byte*)bufferPool.GetUnsafePtr();
|
||||||
|
var offset = 0;
|
||||||
|
|
||||||
|
// Hash pass count
|
||||||
|
*(int*)(pData + offset) = _passes.Count;
|
||||||
|
offset += sizeof(int);
|
||||||
|
|
||||||
|
// Hash each pass structure (excluding names)
|
||||||
|
for (var i = 0; i < _passes.Count; i++)
|
||||||
|
{
|
||||||
|
var pass = _passes[i];
|
||||||
|
|
||||||
|
*(RenderPassType*)(pData + offset) = pass.type;
|
||||||
|
offset += sizeof(RenderPassType);
|
||||||
|
|
||||||
|
*(bool*)(pData + offset) = pass.allowCulling;
|
||||||
|
offset += sizeof(bool);
|
||||||
|
|
||||||
|
*(bool*)(pData + offset) = pass.asyncCompute;
|
||||||
|
offset += sizeof(bool);
|
||||||
|
|
||||||
|
// Hash depth attachment
|
||||||
|
offset = ComputeTextureHash(pData, offset, pass.depthAccess.id);
|
||||||
|
|
||||||
|
*(int*)(pData + offset) = pass.maxColorIndex;
|
||||||
|
offset += sizeof(int);
|
||||||
|
for (var j = 0; j <= pass.maxColorIndex; j++)
|
||||||
|
{
|
||||||
|
offset = ComputeTextureHash(pData, offset, pass.colorAccess[j].id);
|
||||||
|
}
|
||||||
|
|
||||||
|
*(int*)(pData + offset) = pass.resourceReads.Count;
|
||||||
|
offset += sizeof(int);
|
||||||
|
for (var j = 0; j < pass.resourceReads.Count; j++)
|
||||||
|
{
|
||||||
|
*(int*)(pData + offset) = pass.resourceReads[j].Value;
|
||||||
|
offset += sizeof(int);
|
||||||
|
}
|
||||||
|
|
||||||
|
*(int*)(pData + offset) = pass.resourceWrites.Count;
|
||||||
|
offset += sizeof(int);
|
||||||
|
for (var j = 0; j < pass.resourceWrites.Count; j++)
|
||||||
|
{
|
||||||
|
*(int*)(pData + offset) = pass.resourceWrites[j].Value;
|
||||||
|
offset += sizeof(int);
|
||||||
|
}
|
||||||
|
|
||||||
|
*(int*)(pData + offset) = pass.resourceCreates.Count;
|
||||||
|
offset += sizeof(int);
|
||||||
|
for (var j = 0; j < pass.resourceCreates.Count; j++)
|
||||||
|
{
|
||||||
|
*(int*)(pData + offset) = pass.resourceCreates[j].Value;
|
||||||
|
offset += sizeof(int);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//// Hash resource descriptors
|
||||||
|
//for (var i = 0; i < _resources.TextureResourceCount; i++)
|
||||||
|
//{
|
||||||
|
// var resource = _resources.GetTextureResourceByIndex(i);
|
||||||
|
|
||||||
|
// *(int*)(pData + offset) = resource.descriptor.width;
|
||||||
|
// offset += sizeof(int);
|
||||||
|
|
||||||
|
// *(int*)(pData + offset) = resource.descriptor.height;
|
||||||
|
// offset += sizeof(int);
|
||||||
|
|
||||||
|
// *(TextureFormat*)(pData + offset) = resource.descriptor.format;
|
||||||
|
// offset += sizeof(TextureFormat);
|
||||||
|
|
||||||
|
// *(bool*)(pData + offset) = resource.isImported;
|
||||||
|
// offset += sizeof(bool);
|
||||||
|
//}
|
||||||
|
|
||||||
|
var span = new Span<byte>(pData, offset);
|
||||||
|
return XxHash64.HashToUInt64(span);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compiles the render graph by culling unused passes and determining resource lifetimes.
|
||||||
|
/// </summary>
|
||||||
|
public void Compile()
|
||||||
|
{
|
||||||
|
if (_compiled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Step 0: Check cache
|
||||||
|
var graphHash = ComputeGraphHash(); // 17020363347016000737
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
var hashTime = sw.Elapsed.TotalMicroseconds;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (_compilationCache.TryGetCached(graphHash, out var cached))
|
||||||
|
{
|
||||||
|
// CACHE HIT - restore from cache
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine($"\n[CACHE HIT] Hash: {graphHash:X16} (computed in {hashTime:F2}μs)");
|
||||||
|
#endif
|
||||||
|
RestoreFromCache(cached);
|
||||||
|
#if DEBUG
|
||||||
|
sw.Stop();
|
||||||
|
Console.WriteLine($"[CACHE HIT] Total restore time: {sw.Elapsed.TotalMicroseconds:F2}μs");
|
||||||
|
#endif
|
||||||
|
_compiled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine($"\n[CACHE MISS] Hash: {graphHash:X16} (computed in {hashTime:F2}μs)");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_compiledPasses.Clear();
|
||||||
|
|
||||||
|
// Step 1: Mark passes with side effects (writes to imported resources)
|
||||||
|
for (var i = 0; i < _passes.Count; i++)
|
||||||
|
{
|
||||||
|
var pass = _passes[i];
|
||||||
|
|
||||||
|
// Check if this pass writes to any imported textures
|
||||||
|
for (var j = 0; j < pass.resourceWrites.Count; j++)
|
||||||
|
{
|
||||||
|
var writeHandle = pass.resourceWrites[j];
|
||||||
|
var resource = _resources.GetResource(writeHandle);
|
||||||
|
if (resource.isImported)
|
||||||
|
{
|
||||||
|
pass.hasSideEffects = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Cull passes based on dependency analysis
|
||||||
|
// Mark all passes as culled initially
|
||||||
|
for (var i = 0; i < _passes.Count; i++)
|
||||||
|
{
|
||||||
|
_passes[i].culled = _passes[i].allowCulling && !_passes[i].hasSideEffects;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Traverse backwards from passes with side effects
|
||||||
|
for (var i = _passes.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var pass = _passes[i];
|
||||||
|
if (!pass.culled)
|
||||||
|
{
|
||||||
|
UnculDependencies(pass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Build final pass list (only non-culled passes)
|
||||||
|
for (var i = 0; i < _passes.Count; i++)
|
||||||
|
{
|
||||||
|
var pass = _passes[i];
|
||||||
|
if (!pass.culled)
|
||||||
|
{
|
||||||
|
_compiledPasses.Add(pass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5: Perform resource aliasing to minimize memory usage
|
||||||
|
_aliasingManager.AssignPhysicalResources(_resources, _passes.Count);
|
||||||
|
|
||||||
|
// Step 6: Generate barriers for state transitions and aliasing
|
||||||
|
GenerateBarriers();
|
||||||
|
|
||||||
|
// Step 7: Store in cache for future frames
|
||||||
|
StoreInCache(graphHash);
|
||||||
|
|
||||||
|
_compiled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restores the render graph state from cached compilation results.
|
||||||
|
/// </summary>
|
||||||
|
private void RestoreFromCache(CachedCompilation cached)
|
||||||
|
{
|
||||||
|
// Restore compiled pass list
|
||||||
|
_compiledPasses.Clear();
|
||||||
|
for (var i = 0; i < cached.compiledPassIndices.Count; i++)
|
||||||
|
{
|
||||||
|
var passIndex = cached.compiledPassIndices[i];
|
||||||
|
_compiledPasses.Add(_passes[passIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore culling flags
|
||||||
|
for (var i = 0; i < _passes.Count && i < cached.passCulledFlags.Count; i++)
|
||||||
|
{
|
||||||
|
_passes[i].culled = cached.passCulledFlags[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore aliasing mappings (need to update ResourceAliasingManager)
|
||||||
|
_aliasingManager.RestoreFromCache(cached.logicalToPhysical, cached.physicalResources);
|
||||||
|
|
||||||
|
// Restore barriers (deep copy to avoid shared references)
|
||||||
|
_barriers.Clear();
|
||||||
|
for (var i = 0; i < cached.barriers.Count; i++)
|
||||||
|
{
|
||||||
|
_barriers.Add(cached.barriers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore resource states
|
||||||
|
_resourceStates.Clear();
|
||||||
|
foreach (var kvp in cached.resourceStates)
|
||||||
|
{
|
||||||
|
_resourceStates[kvp.Key] = kvp.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores current compilation results in the cache.
|
||||||
|
/// </summary>
|
||||||
|
private void StoreInCache(ulong graphHash)
|
||||||
|
{
|
||||||
|
var cacheData = new CachedCompilation();
|
||||||
|
|
||||||
|
// Store compiled pass indices
|
||||||
|
for (var i = 0; i < _compiledPasses.Count; i++)
|
||||||
|
{
|
||||||
|
cacheData.compiledPassIndices.Add(_compiledPasses[i].index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store culling flags for all passes
|
||||||
|
for (var i = 0; i < _passes.Count; i++)
|
||||||
|
{
|
||||||
|
cacheData.passCulledFlags.Add(_passes[i].culled);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store aliasing mappings
|
||||||
|
_aliasingManager.StoreToCache(cacheData.logicalToPhysical, cacheData.physicalResources);
|
||||||
|
|
||||||
|
// Store barriers
|
||||||
|
for (var i = 0; i < _barriers.Count; i++)
|
||||||
|
{
|
||||||
|
cacheData.barriers.Add(_barriers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store resource states
|
||||||
|
foreach (var kvp in _resourceStates)
|
||||||
|
{
|
||||||
|
cacheData.resourceStates[kvp.Key] = kvp.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_compilationCache.Store(graphHash, cacheData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnculProducer(Identifier<RGResource> resource)
|
||||||
|
{
|
||||||
|
var res = _resources.GetResource(resource);
|
||||||
|
if (res.producerPass >= 0)
|
||||||
|
{
|
||||||
|
var producer = _passes[res.producerPass];
|
||||||
|
if (producer.culled)
|
||||||
|
{
|
||||||
|
producer.culled = false;
|
||||||
|
UnculDependencies(producer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnculDependencies(RenderGraphPassBase pass)
|
||||||
|
{
|
||||||
|
// Un-cull producers of read resources
|
||||||
|
for (var i = 0; i < pass.resourceReads.Count; i++)
|
||||||
|
{
|
||||||
|
UnculProducer(pass.resourceReads[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Un-cull producers of color attachments
|
||||||
|
for (var i = 0; i <= pass.maxColorIndex; i++)
|
||||||
|
{
|
||||||
|
if (pass.colorAccess[i].id.IsValid)
|
||||||
|
{
|
||||||
|
UnculProducer(pass.colorAccess[i].id.AsResource());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Un-cull producer of depth attachment
|
||||||
|
if (pass.depthAccess.id.IsValid)
|
||||||
|
{
|
||||||
|
UnculProducer(pass.depthAccess.id.AsResource());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Un-cull producers of UAV resources (if not already in reads/writes)
|
||||||
|
for (var i = 0; i < pass.randomAccess.Count; i++)
|
||||||
|
{
|
||||||
|
UnculProducer(pass.randomAccess[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates resource barriers for state transitions and aliasing.
|
||||||
|
/// </summary>
|
||||||
|
private void GenerateBarriers()
|
||||||
|
{
|
||||||
|
_barriers.Clear();
|
||||||
|
_resourceStates.Clear();
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine("\n=== Barrier Generation ===");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Process each compiled pass in order
|
||||||
|
for (var passIdx = 0; passIdx < _compiledPasses.Count; passIdx++)
|
||||||
|
{
|
||||||
|
var pass = _compiledPasses[passIdx];
|
||||||
|
|
||||||
|
// Insert aliasing barriers for resources that reuse physical memory
|
||||||
|
InsertAliasingBarriers(pass, passIdx);
|
||||||
|
|
||||||
|
// Insert transition barriers for state changes
|
||||||
|
InsertTransitionBarriers(pass, passIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine($"Total Barriers: {_barriers.Count}");
|
||||||
|
Console.WriteLine("==========================\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inserts aliasing barriers when a physical resource is reused.
|
||||||
|
/// </summary>
|
||||||
|
private void InsertAliasingBarriers(RenderGraphPassBase pass, int passIdx)
|
||||||
|
{
|
||||||
|
// Check all resources written by this pass
|
||||||
|
for (var i = 0; i < pass.resourceWrites.Count; i++)
|
||||||
|
{
|
||||||
|
var id = pass.resourceWrites[i];
|
||||||
|
var resource = _resources.GetResource(id);
|
||||||
|
|
||||||
|
// Skip imported resources
|
||||||
|
if (resource.isImported)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if this is the first use of this logical resource
|
||||||
|
if (resource.firstUsePass == pass.index)
|
||||||
|
{
|
||||||
|
// Rent the physical resource
|
||||||
|
var physicalIndex = _aliasingManager.GetPhysicalResourceIndex(id.Value);
|
||||||
|
if (physicalIndex >= 0)
|
||||||
|
{
|
||||||
|
var physical = _aliasingManager.GetPhysicalResource(physicalIndex);
|
||||||
|
|
||||||
|
// If this physical resource has multiple aliased resources,
|
||||||
|
// we need an aliasing barrier when switching between them
|
||||||
|
if (physical != null && physical.aliasedLogicalResources.Count > 1)
|
||||||
|
{
|
||||||
|
// Find the resource that used this physical memory most recently before this pass
|
||||||
|
Identifier<RGResource> resourceBefore = default;
|
||||||
|
var mostRecentLastUse = -1;
|
||||||
|
|
||||||
|
foreach (var otherLogicalIndex in physical.aliasedLogicalResources)
|
||||||
|
{
|
||||||
|
if (otherLogicalIndex != id.Value)
|
||||||
|
{
|
||||||
|
var otherResource = _resources.GetTextureResourceByIndex(otherLogicalIndex);
|
||||||
|
// Check if this resource finished before our resource starts
|
||||||
|
if (otherResource.lastUsePass < pass.index &&
|
||||||
|
otherResource.lastUsePass > mostRecentLastUse)
|
||||||
|
{
|
||||||
|
mostRecentLastUse = otherResource.lastUsePass;
|
||||||
|
resourceBefore = otherLogicalIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we found a previous resource, insert aliasing barrier
|
||||||
|
if (mostRecentLastUse >= 0)
|
||||||
|
{
|
||||||
|
var barrier = ResourceBarrier.CreateAliasingBarrier(
|
||||||
|
resourceBefore,
|
||||||
|
id,
|
||||||
|
passIdx
|
||||||
|
);
|
||||||
|
_barriers.Add(barrier);
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine($" {barrier}");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inserts transition barriers when a resource changes state.
|
||||||
|
/// </summary>
|
||||||
|
private void InsertTransitionBarriers(RenderGraphPassBase pass, int passIdx)
|
||||||
|
{
|
||||||
|
// Process reads (transition to shader resource)
|
||||||
|
for (var i = 0; i < pass.resourceReads.Count; i++)
|
||||||
|
{
|
||||||
|
var handle = pass.resourceReads[i];
|
||||||
|
InsertTransitionIfNeeded(handle, ResourceState.ShaderResource, passIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pass.type)
|
||||||
|
{
|
||||||
|
case RenderPassType.Raster:
|
||||||
|
for (var i = 0; i <= pass.maxColorIndex; i++)
|
||||||
|
{
|
||||||
|
var access = pass.colorAccess[i];
|
||||||
|
InsertTransitionIfNeeded(access.id.AsResource(), ResourceState.RenderTarget, passIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pass.depthAccess.id.IsValid)
|
||||||
|
{
|
||||||
|
var depthAccess = pass.depthAccess;
|
||||||
|
InsertTransitionIfNeeded(depthAccess.id.AsResource(), ResourceState.DepthWrite, passIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < pass.randomAccess.Count; i++)
|
||||||
|
{
|
||||||
|
InsertTransitionIfNeeded(pass.randomAccess[i], ResourceState.UnorderedAccess, passIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case RenderPassType.Compute:
|
||||||
|
for (var i = 0; i < pass.resourceWrites.Count; i++)
|
||||||
|
{
|
||||||
|
var id = pass.resourceWrites[i];
|
||||||
|
InsertTransitionIfNeeded(id, ResourceState.UnorderedAccess, passIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inserts a transition barrier if the resource state changes.
|
||||||
|
/// </summary>
|
||||||
|
private void InsertTransitionIfNeeded(Identifier<RGResource> resource, ResourceState newState, int passIdx)
|
||||||
|
{
|
||||||
|
if (!_resourceStates.TryGetValue(resource.Value, out var currentState))
|
||||||
|
{
|
||||||
|
// First time seeing this resource, assume undefined
|
||||||
|
currentState = ResourceState.Common;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentState != newState)
|
||||||
|
{
|
||||||
|
var barrier = ResourceBarrier.CreateTransitionBarrier(
|
||||||
|
resource,
|
||||||
|
currentState,
|
||||||
|
newState,
|
||||||
|
passIdx
|
||||||
|
);
|
||||||
|
_barriers.Add(barrier);
|
||||||
|
_resourceStates[resource.Value] = newState;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine($" {barrier}");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes all compiled passes.
|
||||||
|
/// </summary>
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
if (!_compiled)
|
||||||
|
{
|
||||||
|
Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute each non-culled pass
|
||||||
|
var barrierIndex = 0;
|
||||||
|
for (var i = 0; i < _compiledPasses.Count; i++)
|
||||||
|
{
|
||||||
|
var pass = _compiledPasses[i];
|
||||||
|
|
||||||
|
// Execute all barriers for this pass
|
||||||
|
#if DEBUG
|
||||||
|
bool hasBarriers = false;
|
||||||
|
#endif
|
||||||
|
while (barrierIndex < _barriers.Count && _barriers[barrierIndex].PassIndex == i)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if (!hasBarriers)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\n=== Barriers before Pass {i}: {pass.name} ===");
|
||||||
|
hasBarriers = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var barrier = _barriers[barrierIndex];
|
||||||
|
if (barrier.Type == BarrierType.Transition)
|
||||||
|
{
|
||||||
|
_commandBuffer.ResourceBarrier(
|
||||||
|
barrier.Resource,
|
||||||
|
barrier.StateBefore,
|
||||||
|
barrier.StateAfter
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (barrier.Type == BarrierType.Aliasing)
|
||||||
|
{
|
||||||
|
_commandBuffer.AliasBarrier(
|
||||||
|
barrier.ResourceBefore,
|
||||||
|
barrier.ResourceAfter
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// In a real implementation, you would execute the barrier here:
|
||||||
|
// ExecuteBarrier(_barriers[barrierIndex]);
|
||||||
|
|
||||||
|
barrierIndex++;
|
||||||
|
}
|
||||||
|
#if DEBUG
|
||||||
|
if (hasBarriers)
|
||||||
|
{
|
||||||
|
Console.WriteLine("=====================================\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pass.Execute(_renderContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
329
Ghost.RenderGraph.Concept/RenderGraphAliasing.cs.bak
Normal file
329
Ghost.RenderGraph.Concept/RenderGraphAliasing.cs.bak
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
using Ghost.Core.Utilities;
|
||||||
|
|
||||||
|
namespace Ghost.RenderGraph.Concept;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a physical GPU resource that can be aliased by multiple logical resources.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class PhysicalResource
|
||||||
|
{
|
||||||
|
public int index;
|
||||||
|
public int width;
|
||||||
|
public int height;
|
||||||
|
public TextureFormat format;
|
||||||
|
public int sizeInBytes;
|
||||||
|
|
||||||
|
// Lifetime tracking
|
||||||
|
public int firstUsePass = int.MaxValue;
|
||||||
|
public int lastUsePass = -1;
|
||||||
|
|
||||||
|
// Aliasing tracking
|
||||||
|
public readonly List<int> aliasedLogicalResources = new(4);
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
index = -1;
|
||||||
|
width = 0;
|
||||||
|
height = 0;
|
||||||
|
format = TextureFormat.RGBA8;
|
||||||
|
sizeInBytes = 0;
|
||||||
|
firstUsePass = int.MaxValue;
|
||||||
|
lastUsePass = -1;
|
||||||
|
aliasedLogicalResources.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanAlias(TextureDescriptor descriptor)
|
||||||
|
{
|
||||||
|
// For aliasing, resources must be identical in size and format
|
||||||
|
// In a real implementation, you could be more flexible (e.g., same size but different format)
|
||||||
|
return width == descriptor.width &&
|
||||||
|
height == descriptor.height &&
|
||||||
|
format == descriptor.format;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateLifetime(int passIndex)
|
||||||
|
{
|
||||||
|
firstUsePass = Math.Min(firstUsePass, passIndex);
|
||||||
|
lastUsePass = Math.Max(lastUsePass, passIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsAliveAt(int passIndex)
|
||||||
|
{
|
||||||
|
return passIndex >= firstUsePass && passIndex <= lastUsePass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CalculateSize()
|
||||||
|
{
|
||||||
|
int bytesPerPixel = format switch
|
||||||
|
{
|
||||||
|
TextureFormat.RGBA8 => 4,
|
||||||
|
TextureFormat.RGBA16F => 8,
|
||||||
|
TextureFormat.RGBA32F => 16,
|
||||||
|
TextureFormat.Depth32F => 4,
|
||||||
|
TextureFormat.Depth24Stencil8 => 4,
|
||||||
|
_ => 4
|
||||||
|
};
|
||||||
|
return width * height * bytesPerPixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages physical resource allocation and aliasing.
|
||||||
|
/// Uses interval scheduling algorithm to minimize memory usage.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class ResourceAliasingManager
|
||||||
|
{
|
||||||
|
private readonly List<PhysicalResource> _physicalResources = new(32);
|
||||||
|
private readonly RenderGraphObjectPool _pool = new();
|
||||||
|
private int _physicalResourceCount;
|
||||||
|
|
||||||
|
// Mapping from logical resource index to physical resource index
|
||||||
|
private readonly Dictionary<int, int> _logicalToPhysical = new(64);
|
||||||
|
|
||||||
|
public void BeginFrame()
|
||||||
|
{
|
||||||
|
_physicalResourceCount = 0;
|
||||||
|
_logicalToPhysical.Clear();
|
||||||
|
|
||||||
|
// Reset physical resources but keep them in the pool
|
||||||
|
for (int i = 0; i < _physicalResources.Count; i++)
|
||||||
|
{
|
||||||
|
_physicalResources[i].Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assigns physical resources to logical resources using greedy interval scheduling.
|
||||||
|
/// This minimizes total GPU memory usage.
|
||||||
|
/// </summary>
|
||||||
|
public void AssignPhysicalResources(RenderGraphResourceRegistry registry, int passCount)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine("\n=== Resource Aliasing Analysis ===");
|
||||||
|
int totalLogicalSize = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Build list of all logical resources with their lifetimes
|
||||||
|
var logicalResources = ListPool<(int index, RenderGraphResource resource)>.Rent();
|
||||||
|
|
||||||
|
for (int i = 0; i < registry.TextureResourceCount; i++)
|
||||||
|
{
|
||||||
|
var resource = registry.GetTextureResourceByIndex(i);
|
||||||
|
if (!resource.isImported) // Don't alias imported resources
|
||||||
|
{
|
||||||
|
logicalResources.Add((i, resource));
|
||||||
|
#if DEBUG
|
||||||
|
int size = CalculateSize(resource.descriptor);
|
||||||
|
totalLogicalSize += size;
|
||||||
|
Console.WriteLine($"Logical Resource {i}: {resource.descriptor.name}");
|
||||||
|
Console.WriteLine($" Lifetime: Pass {resource.firstUsePass} -> {resource.lastUsePass}");
|
||||||
|
Console.WriteLine($" Size: {size / 1024.0:F2} KB");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by first use pass (earlier resources first)
|
||||||
|
logicalResources.Sort((a, b) => a.resource.firstUsePass.CompareTo(b.resource.firstUsePass));
|
||||||
|
|
||||||
|
// Greedy interval scheduling: assign each logical resource to a physical resource
|
||||||
|
foreach (var (logicalIndex, logicalResource) in logicalResources)
|
||||||
|
{
|
||||||
|
PhysicalResource? assignedPhysical = null;
|
||||||
|
|
||||||
|
// Try to find an existing physical resource that:
|
||||||
|
// 1. Has compatible format/size
|
||||||
|
// 2. Is not alive during this logical resource's lifetime
|
||||||
|
for (int i = 0; i < _physicalResourceCount; i++)
|
||||||
|
{
|
||||||
|
var physical = _physicalResources[i];
|
||||||
|
|
||||||
|
if (physical.CanAlias(logicalResource.descriptor) &&
|
||||||
|
!HasLifetimeOverlap(physical, logicalResource))
|
||||||
|
{
|
||||||
|
assignedPhysical = physical;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No compatible physical resource found, allocate a new one
|
||||||
|
if (assignedPhysical == null)
|
||||||
|
{
|
||||||
|
assignedPhysical = GetOrCreatePhysicalResource();
|
||||||
|
assignedPhysical.index = _physicalResourceCount - 1;
|
||||||
|
assignedPhysical.width = logicalResource.descriptor.width;
|
||||||
|
assignedPhysical.height = logicalResource.descriptor.height;
|
||||||
|
assignedPhysical.format = logicalResource.descriptor.format;
|
||||||
|
assignedPhysical.sizeInBytes = assignedPhysical.CalculateSize();
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine($"\nAllocated NEW Physical Resource {assignedPhysical.index}:");
|
||||||
|
Console.WriteLine($" Size: {assignedPhysical.width}x{assignedPhysical.height}");
|
||||||
|
Console.WriteLine($" Format: {assignedPhysical.format}");
|
||||||
|
Console.WriteLine($" Memory: {assignedPhysical.sizeInBytes / 1024.0:F2} KB");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if DEBUG
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\nALIASING: {logicalResource.descriptor.name} -> Physical Resource {assignedPhysical.index}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Update physical resource lifetime
|
||||||
|
assignedPhysical.UpdateLifetime(logicalResource.firstUsePass);
|
||||||
|
assignedPhysical.UpdateLifetime(logicalResource.lastUsePass);
|
||||||
|
assignedPhysical.aliasedLogicalResources.Add(logicalIndex);
|
||||||
|
|
||||||
|
// Record the mapping
|
||||||
|
_logicalToPhysical[logicalIndex] = assignedPhysical.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
int totalPhysicalSize = 0;
|
||||||
|
for (int i = 0; i < _physicalResourceCount; i++)
|
||||||
|
{
|
||||||
|
totalPhysicalSize += _physicalResources[i].sizeInBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"\n=== Aliasing Summary ===");
|
||||||
|
Console.WriteLine($"Logical Resources: {logicalResources.Count}");
|
||||||
|
Console.WriteLine($"Physical Resources: {_physicalResourceCount}");
|
||||||
|
Console.WriteLine($"Total Logical Memory: {totalLogicalSize / 1024.0:F2} KB");
|
||||||
|
Console.WriteLine($"Total Physical Memory: {totalPhysicalSize / 1024.0:F2} KB");
|
||||||
|
Console.WriteLine($"Memory Saved: {(totalLogicalSize - totalPhysicalSize) / 1024.0:F2} KB ({(1.0 - (double)totalPhysicalSize / totalLogicalSize) * 100.0:F1}%)");
|
||||||
|
Console.WriteLine("================================\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ListPool<(int index, RenderGraphResource resource)>.Return(logicalResources);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetPhysicalResourceIndex(int logicalIndex)
|
||||||
|
{
|
||||||
|
return _logicalToPhysical.TryGetValue(logicalIndex, out var physicalIndex) ? physicalIndex : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PhysicalResource? GetPhysicalResource(int physicalIndex)
|
||||||
|
{
|
||||||
|
return physicalIndex >= 0 && physicalIndex < _physicalResourceCount
|
||||||
|
? _physicalResources[physicalIndex]
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasLifetimeOverlap(PhysicalResource physical, RenderGraphResource logical)
|
||||||
|
{
|
||||||
|
// Check if the lifetimes overlap
|
||||||
|
// No overlap if: logical.First > physical.Last OR logical.Last < physical.First
|
||||||
|
return !(logical.firstUsePass > physical.lastUsePass ||
|
||||||
|
logical.lastUsePass < physical.firstUsePass);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PhysicalResource GetOrCreatePhysicalResource()
|
||||||
|
{
|
||||||
|
PhysicalResource resource;
|
||||||
|
if (_physicalResourceCount < _physicalResources.Count)
|
||||||
|
{
|
||||||
|
resource = _physicalResources[_physicalResourceCount];
|
||||||
|
resource.Reset();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resource = _pool.Rent<PhysicalResource>();
|
||||||
|
resource.Reset();
|
||||||
|
_physicalResources.Add(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
_physicalResourceCount++;
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CalculateSize(TextureDescriptor descriptor)
|
||||||
|
{
|
||||||
|
int bytesPerPixel = descriptor.format switch
|
||||||
|
{
|
||||||
|
TextureFormat.RGBA8 => 4,
|
||||||
|
TextureFormat.RGBA16F => 8,
|
||||||
|
TextureFormat.RGBA32F => 16,
|
||||||
|
TextureFormat.Depth32F => 4,
|
||||||
|
TextureFormat.Depth24Stencil8 => 4,
|
||||||
|
_ => 4
|
||||||
|
};
|
||||||
|
return descriptor.width * descriptor.height * bytesPerPixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _physicalResources.Count; i++)
|
||||||
|
{
|
||||||
|
_pool.Return(_physicalResources[i]);
|
||||||
|
}
|
||||||
|
_physicalResources.Clear();
|
||||||
|
_physicalResourceCount = 0;
|
||||||
|
_logicalToPhysical.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restores aliasing state from cache.
|
||||||
|
/// </summary>
|
||||||
|
public void RestoreFromCache(Dictionary<int, int> logicalToPhysical, List<PhysicalResourceData> physicalData)
|
||||||
|
{
|
||||||
|
_logicalToPhysical.Clear();
|
||||||
|
foreach (var kvp in logicalToPhysical)
|
||||||
|
{
|
||||||
|
_logicalToPhysical[kvp.Key] = kvp.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore physical resources
|
||||||
|
_physicalResourceCount = physicalData.Count;
|
||||||
|
for (int i = 0; i < physicalData.Count; i++)
|
||||||
|
{
|
||||||
|
PhysicalResource physical;
|
||||||
|
if (i < _physicalResources.Count)
|
||||||
|
{
|
||||||
|
physical = _physicalResources[i];
|
||||||
|
physical.Reset();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
physical = _pool.Rent<PhysicalResource>();
|
||||||
|
physical.Reset();
|
||||||
|
_physicalResources.Add(physical);
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = physicalData[i];
|
||||||
|
physical.index = data.index;
|
||||||
|
physical.width = data.width;
|
||||||
|
physical.height = data.height;
|
||||||
|
physical.format = data.format;
|
||||||
|
physical.firstUsePass = data.firstUsePass;
|
||||||
|
physical.lastUsePass = data.lastUsePass;
|
||||||
|
physical.sizeInBytes = physical.CalculateSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores current aliasing state to cache.
|
||||||
|
/// </summary>
|
||||||
|
public void StoreToCache(Dictionary<int, int> outLogicalToPhysical, List<PhysicalResourceData> outPhysicalData)
|
||||||
|
{
|
||||||
|
outLogicalToPhysical.Clear();
|
||||||
|
foreach (var kvp in _logicalToPhysical)
|
||||||
|
{
|
||||||
|
outLogicalToPhysical[kvp.Key] = kvp.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
outPhysicalData.Clear();
|
||||||
|
for (int i = 0; i < _physicalResourceCount; i++)
|
||||||
|
{
|
||||||
|
var physical = _physicalResources[i];
|
||||||
|
outPhysicalData.Add(new PhysicalResourceData
|
||||||
|
{
|
||||||
|
index = physical.index,
|
||||||
|
width = physical.width,
|
||||||
|
height = physical.height,
|
||||||
|
format = physical.format,
|
||||||
|
firstUsePass = physical.firstUsePass,
|
||||||
|
lastUsePass = physical.lastUsePass
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -110,17 +110,17 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra
|
|||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Identifier<RGResource> UseResource(Identifier<RGResource> resource, AccessFlags accessFlags)
|
private Identifier<RGResource> UseResource(Identifier<RGResource> resource, AccessFlags accessFlags, RenderGraphResourceType type)
|
||||||
{
|
{
|
||||||
if (accessFlags.HasFlag(AccessFlags.Read))
|
if (accessFlags.HasFlag(AccessFlags.Read))
|
||||||
{
|
{
|
||||||
_pass.resourceReads.Add(resource);
|
_pass.resourceReads[(int)type].Add(resource);
|
||||||
_resources.AddConsumer(resource, _pass.index);
|
_resources.AddConsumer(resource, _pass.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accessFlags.HasFlag(AccessFlags.Write))
|
if (accessFlags.HasFlag(AccessFlags.Write))
|
||||||
{
|
{
|
||||||
_pass.resourceWrites.Add(resource);
|
_pass.resourceWrites[(int)type].Add(resource);
|
||||||
_resources.SetProducer(resource, _pass.index);
|
_resources.SetProducer(resource, _pass.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra
|
|||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
var handle = _resources.CreateTexture(descriptor);
|
var handle = _resources.CreateTexture(descriptor);
|
||||||
_pass.resourceCreates.Add(handle.AsResource());
|
_pass.resourceCreates[(int)RenderGraphResourceType.Texture].Add(handle.AsResource());
|
||||||
_resources.SetProducer(handle.AsResource(), _pass.index);
|
_resources.SetProducer(handle.AsResource(), _pass.index);
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
@@ -150,8 +150,7 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra
|
|||||||
public Identifier<RGTexture> UseTexture(Identifier<RGTexture> texture, AccessFlags flags)
|
public Identifier<RGTexture> UseTexture(Identifier<RGTexture> texture, AccessFlags flags)
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
return UseResource(texture.AsResource(), flags, RenderGraphResourceType.Texture).AsTexture();
|
||||||
return UseResource(texture.AsResource(), flags).AsTexture();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Identifier<RGTexture> UseRandomAccessTexture(Identifier<RGTexture> texture)
|
public Identifier<RGTexture> UseRandomAccessTexture(Identifier<RGTexture> texture)
|
||||||
@@ -159,7 +158,7 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra
|
|||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
var resource = texture.AsResource();
|
var resource = texture.AsResource();
|
||||||
UseResource(resource, AccessFlags.ReadWrite);
|
UseResource(resource, AccessFlags.ReadWrite, RenderGraphResourceType.Texture);
|
||||||
_pass.randomAccess.Add(resource);
|
_pass.randomAccess.Add(resource);
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
@@ -169,7 +168,7 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra
|
|||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
var resource = buffer.AsResource();
|
var resource = buffer.AsResource();
|
||||||
UseResource(resource, AccessFlags.ReadWrite);
|
UseResource(resource, AccessFlags.ReadWrite, RenderGraphResourceType.Buffer);
|
||||||
_pass.randomAccess.Add(resource);
|
_pass.randomAccess.Add(resource);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|||||||
240
Ghost.RenderGraph.Concept/RenderGraphBuilder.cs.bak
Normal file
240
Ghost.RenderGraph.Concept/RenderGraphBuilder.cs.bak
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
using Ghost.Core;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Ghost.RenderGraph.Concept;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum AccessFlags
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Read = 1 << 0,
|
||||||
|
Write = 1 << 1,
|
||||||
|
ReadWrite = Read | Write,
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IRenderGraphBuilder : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Enables or disables pass culling for the current context.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A value indicating whether pass culling is allowed.</param>
|
||||||
|
void AllowPassCulling(bool value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new texture resource based on the specified descriptor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="descriptor">A structure that defines the properties and configuration of the texture to create.</param>
|
||||||
|
/// <returns>An identifier for the newly created texture resource.</returns>
|
||||||
|
Identifier<RGTexture> CreateTexture(in TextureDescriptor descriptor);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers the specified texture for use in the current render graph pass with the given access mode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">The identifier of the texture to be used in the render graph pass.</param>
|
||||||
|
/// <param name="accessMode">The access mode specifying how the texture will be read or written during the pass.</param>
|
||||||
|
/// <returns>An identifier for the texture.</returns>
|
||||||
|
Identifier<RGTexture> UseTexture(Identifier<RGTexture> texture, AccessFlags accessMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IRasterRenderGraphBuilder : IRenderGraphBuilder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Binds a texture for random access operations within the current rendering pass.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">The identifier of the texture to be used for random access.</param>
|
||||||
|
/// <returns>An identifier for the texture.</returns>
|
||||||
|
Identifier<RGTexture> UseRandomAccessTexture(Identifier<RGTexture> texture);
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies that the given buffer will be used for random access operations with the specified access mode within the current context.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">An identifier for the buffer to be used for random access. Must reference a valid buffer resource.</param>
|
||||||
|
/// <returns>An identifier for the buffer.</returns>
|
||||||
|
Identifier<RGBuffer> UseRandomAccessBuffer(Identifier<RGBuffer> buffer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the color attachment at the specified index to the given texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">The identifier of the texture to use as the color attachment.</param>
|
||||||
|
/// <param name="index">The zero-based index of the color attachment to set.</param>
|
||||||
|
void SetColorAttachment(Identifier<RGTexture> texture, int index);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the depth attachment for the current render pass using the specified texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">The identifier of the texture to use as the depth attachment. Cannot be null.</param>
|
||||||
|
void SetDepthAttachment(Identifier<RGTexture> texture);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the function used to render a pass with the specified pass data and render context.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TPassData">The type of data associated with the render pass.</typeparam>
|
||||||
|
/// <param name="renderFunc">The delegate that defines the rendering logic for the pass.</param>
|
||||||
|
void SetRenderFunc<TPassData>(Action<TPassData, RasterRenderContext> renderFunc)
|
||||||
|
where TPassData : class, new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IComputeRenderGraphBuilder : IRenderGraphBuilder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Enables or disables asynchronous compute operations.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">true to enable asynchronous compute; otherwise, false.</param>
|
||||||
|
void EnableAsyncCompute(bool value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the render function to be invoked during the compute rendering process.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TPassData">The type of the data object passed to the render function.</typeparam>
|
||||||
|
/// <param name="renderFunc">The delegate that defines the rendering logic to execute.</param>
|
||||||
|
void SetRenderFunc<TPassData>(Action<TPassData, ComputeRenderContext> renderFunc)
|
||||||
|
where TPassData : class, new();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGraphBuilder
|
||||||
|
{
|
||||||
|
private RenderGraph _graph = null!;
|
||||||
|
private RenderGraphPassBase _pass = null!;
|
||||||
|
private RenderGraphResourceRegistry _resources = null!;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
internal void Init(RenderGraph graph, RenderGraphPassBase pass, RenderGraphResourceRegistry resources)
|
||||||
|
{
|
||||||
|
_graph = graph;
|
||||||
|
_pass = pass;
|
||||||
|
_resources = resources;
|
||||||
|
_disposed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThrowIfDisposed()
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Identifier<RGResource> UseResource(Identifier<RGResource> resource, AccessFlags accessFlags)
|
||||||
|
{
|
||||||
|
if (accessFlags.HasFlag(AccessFlags.Read))
|
||||||
|
{
|
||||||
|
_pass.resourceReads.Add(resource);
|
||||||
|
_resources.AddConsumer(resource, _pass.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accessFlags.HasFlag(AccessFlags.Write))
|
||||||
|
{
|
||||||
|
_pass.resourceWrites.Add(resource);
|
||||||
|
_resources.SetProducer(resource, _pass.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AllowPassCulling(bool value)
|
||||||
|
{
|
||||||
|
_pass.allowCulling = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnableAsyncCompute(bool value)
|
||||||
|
{
|
||||||
|
_pass.asyncCompute = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Identifier<RGTexture> CreateTexture(in TextureDescriptor descriptor)
|
||||||
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
var handle = _resources.CreateTexture(descriptor);
|
||||||
|
_pass.resourceCreates.Add(handle.AsResource());
|
||||||
|
_resources.SetProducer(handle.AsResource(), _pass.index);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Identifier<RGTexture> UseTexture(Identifier<RGTexture> texture, AccessFlags flags)
|
||||||
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
return UseResource(texture.AsResource(), flags).AsTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Identifier<RGTexture> UseRandomAccessTexture(Identifier<RGTexture> texture)
|
||||||
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
var resource = texture.AsResource();
|
||||||
|
UseResource(resource, AccessFlags.ReadWrite);
|
||||||
|
_pass.randomAccess.Add(resource);
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Identifier<RGBuffer> UseRandomAccessBuffer(Identifier<RGBuffer> buffer)
|
||||||
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
var resource = buffer.AsResource();
|
||||||
|
UseResource(resource, AccessFlags.ReadWrite);
|
||||||
|
_pass.randomAccess.Add(resource);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetColorAttachment(Identifier<RGTexture> texture, int index)
|
||||||
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
Debug.Assert(index >= 0 && index < _pass.colorAccess.Length, "Color attachment index out of range.");
|
||||||
|
|
||||||
|
var id = UseTexture(texture, AccessFlags.Write);
|
||||||
|
if (_pass.colorAccess[index].id == id || _pass.colorAccess[index].id.IsInvalid)
|
||||||
|
{
|
||||||
|
_pass.maxColorIndex = Math.Max(_pass.maxColorIndex, index);
|
||||||
|
_pass.colorAccess[index] = new TextureAccess(id, AccessFlags.Write);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Color attachment at index {index} is already set to a different texture.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDepthAttachment(Identifier<RGTexture> texture)
|
||||||
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
var id = UseTexture(texture, AccessFlags.Write);
|
||||||
|
if (_pass.depthAccess.id == id || _pass.depthAccess.id.IsInvalid)
|
||||||
|
{
|
||||||
|
_pass.depthAccess = new TextureAccess(id, AccessFlags.Write);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Depth attachment is already set to a different texture.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRenderFunc<TPassData>(Action<TPassData, RasterRenderContext> renderFunc)
|
||||||
|
where TPassData : class, new()
|
||||||
|
{
|
||||||
|
((RasterRenderGraphPass<TPassData>)_pass).renderFunc = renderFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRenderFunc<TPassData>(Action<TPassData, ComputeRenderContext> renderFunc)
|
||||||
|
where TPassData : class, new()
|
||||||
|
{
|
||||||
|
((ComputeRenderGraphPass<TPassData>)_pass).renderFunc = renderFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_pass.HasRenderFunc())
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("RenderGraphBuilder must be disposed after setting up the render function.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_graph = null!;
|
||||||
|
_pass = null!;
|
||||||
|
_resources = null!;
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ghost.RenderGraph.Concept;
|
namespace Ghost.RenderGraph.Concept;
|
||||||
|
|
||||||
@@ -31,14 +30,24 @@ internal abstract class RenderGraphPassBase
|
|||||||
public List<Identifier<RGResource>> randomAccess = new(8);
|
public List<Identifier<RGResource>> randomAccess = new(8);
|
||||||
|
|
||||||
// Resource dependencies
|
// Resource dependencies
|
||||||
public readonly List<Identifier<RGResource>> resourceReads = new(8);
|
public readonly List<Identifier<RGResource>>[] resourceReads = new List<Identifier<RGResource>>[(int)RenderGraphResourceType.Count];
|
||||||
public readonly List<Identifier<RGResource>> resourceWrites = new(4);
|
public readonly List<Identifier<RGResource>>[] resourceWrites = new List<Identifier<RGResource>>[(int)RenderGraphResourceType.Count];
|
||||||
public readonly List<Identifier<RGResource>> resourceCreates = new(4);
|
public readonly List<Identifier<RGResource>>[] resourceCreates = new List<Identifier<RGResource>>[(int)RenderGraphResourceType.Count];
|
||||||
|
|
||||||
// Execution state
|
// Execution state
|
||||||
public bool culled;
|
public bool culled;
|
||||||
public bool hasSideEffects;
|
public bool hasSideEffects;
|
||||||
|
|
||||||
|
public RenderGraphPassBase()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < (int)RenderGraphResourceType.Count; i++)
|
||||||
|
{
|
||||||
|
resourceReads[i] = new List<Identifier<RGResource>>(8);
|
||||||
|
resourceWrites[i] = new List<Identifier<RGResource>>(4);
|
||||||
|
resourceCreates[i] = new List<Identifier<RGResource>>(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void Execute(RenderContext context);
|
public abstract void Execute(RenderContext context);
|
||||||
public abstract void Clear();
|
public abstract void Clear();
|
||||||
public abstract bool HasRenderFunc();
|
public abstract bool HasRenderFunc();
|
||||||
@@ -57,9 +66,13 @@ internal abstract class RenderGraphPassBase
|
|||||||
|
|
||||||
randomAccess.Clear();
|
randomAccess.Clear();
|
||||||
|
|
||||||
resourceReads.Clear();
|
for (var i = 0; i < (int)RenderGraphResourceType.Count; i++)
|
||||||
resourceWrites.Clear();
|
{
|
||||||
resourceCreates.Clear();
|
resourceReads[i].Clear();
|
||||||
|
resourceWrites[i].Clear();
|
||||||
|
resourceCreates[i].Clear();
|
||||||
|
}
|
||||||
|
|
||||||
culled = false;
|
culled = false;
|
||||||
hasSideEffects = false;
|
hasSideEffects = false;
|
||||||
}
|
}
|
||||||
|
|||||||
129
Ghost.RenderGraph.Concept/RenderGraphPass.cs.bak
Normal file
129
Ghost.RenderGraph.Concept/RenderGraphPass.cs.bak
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
using Ghost.Core;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Ghost.RenderGraph.Concept;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents different types of render passes.
|
||||||
|
/// </summary>
|
||||||
|
public enum RenderPassType : byte
|
||||||
|
{
|
||||||
|
Raster,
|
||||||
|
Compute
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for render passes.
|
||||||
|
/// Uses pooling to avoid allocations after the first frame.
|
||||||
|
/// </summary>
|
||||||
|
internal abstract class RenderGraphPassBase
|
||||||
|
{
|
||||||
|
public string name = string.Empty;
|
||||||
|
public int index;
|
||||||
|
public RenderPassType type;
|
||||||
|
public bool allowCulling = true;
|
||||||
|
public bool asyncCompute;
|
||||||
|
|
||||||
|
public TextureAccess depthAccess;
|
||||||
|
public TextureAccess[] colorAccess = new TextureAccess[8];
|
||||||
|
public int maxColorIndex = -1;
|
||||||
|
|
||||||
|
public List<Identifier<RGResource>> randomAccess = new(8);
|
||||||
|
|
||||||
|
// Resource dependencies
|
||||||
|
public readonly List<Identifier<RGResource>> resourceReads = new(8);
|
||||||
|
public readonly List<Identifier<RGResource>> resourceWrites = new(4);
|
||||||
|
public readonly List<Identifier<RGResource>> resourceCreates = new(4);
|
||||||
|
|
||||||
|
// Execution state
|
||||||
|
public bool culled;
|
||||||
|
public bool hasSideEffects;
|
||||||
|
|
||||||
|
public abstract void Execute(RenderContext context);
|
||||||
|
public abstract void Clear();
|
||||||
|
public abstract bool HasRenderFunc();
|
||||||
|
|
||||||
|
public virtual void Reset(RenderGraphObjectPool pool)
|
||||||
|
{
|
||||||
|
name = string.Empty;
|
||||||
|
index = -1;
|
||||||
|
type = RenderPassType.Raster;
|
||||||
|
allowCulling = true;
|
||||||
|
asyncCompute = false;
|
||||||
|
|
||||||
|
depthAccess = default;
|
||||||
|
colorAccess.AsSpan().Clear();
|
||||||
|
maxColorIndex = -1;
|
||||||
|
|
||||||
|
randomAccess.Clear();
|
||||||
|
|
||||||
|
resourceReads.Clear();
|
||||||
|
resourceWrites.Clear();
|
||||||
|
resourceCreates.Clear();
|
||||||
|
culled = false;
|
||||||
|
hasSideEffects = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal abstract class RenderGraphPassT<TPassData, TRenderContext> : RenderGraphPassBase
|
||||||
|
where TPassData : class, new()
|
||||||
|
{
|
||||||
|
public TPassData passData = null!;
|
||||||
|
public Action<TPassData, TRenderContext>? renderFunc;
|
||||||
|
|
||||||
|
public void Init(int index, TPassData passData, string name, RenderPassType type)
|
||||||
|
{
|
||||||
|
this.index = index;
|
||||||
|
this.passData = passData;
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed override bool HasRenderFunc()
|
||||||
|
{
|
||||||
|
return renderFunc != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Clear()
|
||||||
|
{
|
||||||
|
passData = null!;
|
||||||
|
renderFunc = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Reset(RenderGraphObjectPool pool)
|
||||||
|
{
|
||||||
|
base.Reset(pool);
|
||||||
|
pool.Return(passData);
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class RasterRenderGraphPass<TPassData> : RenderGraphPassT<TPassData, RasterRenderContext>
|
||||||
|
where TPassData : class, new()
|
||||||
|
{
|
||||||
|
public override void Execute(RenderContext context)
|
||||||
|
{
|
||||||
|
renderFunc!(passData, context.RasterContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Reset(RenderGraphObjectPool pool)
|
||||||
|
{
|
||||||
|
base.Reset(pool);
|
||||||
|
pool.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class ComputeRenderGraphPass<TPassData> : RenderGraphPassT<TPassData, ComputeRenderContext>
|
||||||
|
where TPassData : class, new()
|
||||||
|
{
|
||||||
|
public override void Execute(RenderContext context)
|
||||||
|
{
|
||||||
|
renderFunc!(passData, context.ComputeContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Reset(RenderGraphObjectPool pool)
|
||||||
|
{
|
||||||
|
base.Reset(pool);
|
||||||
|
pool.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,11 +3,11 @@ using System.Runtime.CompilerServices;
|
|||||||
|
|
||||||
namespace Ghost.RenderGraph.Concept;
|
namespace Ghost.RenderGraph.Concept;
|
||||||
|
|
||||||
internal enum RenderGraphResourceType
|
internal enum RenderGraphResourceType : int
|
||||||
{
|
{
|
||||||
Texture,
|
Texture,
|
||||||
Buffer,
|
Buffer,
|
||||||
AccelerationStructure,
|
// AccelerationStructure,
|
||||||
Count
|
Count
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,14 +84,79 @@ public readonly struct TextureDescriptor : IEquatable<TextureDescriptor>
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly bool Equals(TextureDescriptor other) =>
|
public readonly bool Equals(TextureDescriptor other)
|
||||||
width == other.width &&
|
{
|
||||||
|
return width == other.width &&
|
||||||
height == other.height &&
|
height == other.height &&
|
||||||
format == other.format &&
|
format == other.format &&
|
||||||
name == other.name;
|
name == other.name;
|
||||||
|
}
|
||||||
|
|
||||||
public override readonly bool Equals(object? obj) => obj is TextureDescriptor other && Equals(other);
|
public override readonly bool Equals(object? obj) => obj is TextureDescriptor other && Equals(other);
|
||||||
public override readonly int GetHashCode() => HashCode.Combine(width, height, format, name);
|
public override readonly int GetHashCode() => HashCode.Combine(width, height, format, name);
|
||||||
|
|
||||||
|
public static bool operator ==(TextureDescriptor left, TextureDescriptor right)
|
||||||
|
{
|
||||||
|
return left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(TextureDescriptor left, TextureDescriptor right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum BufferUsage
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Vertex = 1 << 0,
|
||||||
|
Index = 1 << 1,
|
||||||
|
IndirectArgument = 1 << 7,
|
||||||
|
Constant = 1 << 2,
|
||||||
|
ShaderResource = 1 << 3,
|
||||||
|
UnorderedAccess = 1 << 4,
|
||||||
|
Structured = 1 << 5,
|
||||||
|
Raw = 1 << 6,
|
||||||
|
Upload = 1 << 8,
|
||||||
|
Readback = 1 << 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct BufferDescriptor : IEquatable<BufferDescriptor>
|
||||||
|
{
|
||||||
|
public readonly uint sizeInBytes;
|
||||||
|
public readonly uint stride;
|
||||||
|
public readonly BufferUsage usage;
|
||||||
|
public readonly string name;
|
||||||
|
|
||||||
|
public BufferDescriptor(uint sizeInBytes, uint stride, BufferUsage usage, string name)
|
||||||
|
{
|
||||||
|
this.sizeInBytes = sizeInBytes;
|
||||||
|
this.stride = stride;
|
||||||
|
this.usage = usage;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly bool Equals(BufferDescriptor other)
|
||||||
|
{
|
||||||
|
return sizeInBytes == other.sizeInBytes &&
|
||||||
|
stride == other.stride &&
|
||||||
|
usage == other.usage &&
|
||||||
|
name == other.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly bool Equals(object? obj) => obj is BufferDescriptor other && Equals(other);
|
||||||
|
public override readonly int GetHashCode() => HashCode.Combine(sizeInBytes, name);
|
||||||
|
|
||||||
|
public static bool operator ==(BufferDescriptor left, BufferDescriptor right)
|
||||||
|
{
|
||||||
|
return left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(BufferDescriptor left, BufferDescriptor right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user