Update render graph
This commit is contained in:
@@ -20,7 +20,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Misaki.HighPerformance" Version="1.0.3" />
|
<PackageReference Include="Misaki.HighPerformance" Version="1.0.4" />
|
||||||
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.2.2" />
|
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.2.2" />
|
||||||
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.3.3" />
|
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.3.3" />
|
||||||
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.3.1" />
|
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.3.1" />
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Ghost.Core.Utilities;
|
|||||||
public class CollectionPool<TCollection, TItem>
|
public class CollectionPool<TCollection, TItem>
|
||||||
where TCollection : class, ICollection<TItem>, new()
|
where TCollection : class, ICollection<TItem>, new()
|
||||||
{
|
{
|
||||||
internal static readonly ObjectPool<TCollection> s_pool = new ObjectPool<TCollection>(() => new TCollection(), 1);
|
internal static readonly ObjectPool<TCollection> s_pool = new ObjectPool<TCollection>(() => new TCollection(), null, 1);
|
||||||
|
|
||||||
public static TCollection Rent()
|
public static TCollection Rent()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Misaki.HighPerformance.LowLevel.Collections;
|
|||||||
using Misaki.HighPerformance.Mathematics;
|
using Misaki.HighPerformance.Mathematics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
using Ghost.Graphics.Core;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.Core;
|
namespace Ghost.Graphics.Core;
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public class RenderGraphBenchmark
|
|||||||
// Create output texture
|
// Create output texture
|
||||||
lightingOutput = builder.CreateTexture(
|
lightingOutput = builder.CreateTexture(
|
||||||
new TextureDescriptor(1920, 1080, TextureFormat.RGBA16F, "LightingResult"));
|
new TextureDescriptor(1920, 1080, TextureFormat.RGBA16F, "LightingResult"));
|
||||||
builder.SetColorAttachment(lightingOutput, 1);
|
builder.SetColorAttachment(lightingOutput, 0);
|
||||||
|
|
||||||
lightingData.OutputLighting = lightingOutput;
|
lightingData.OutputLighting = lightingOutput;
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
using Ghost.Core;
|
|
||||||
using Ghost.RenderGraph.Concept;
|
using Ghost.RenderGraph.Concept;
|
||||||
using Ghost.RenderGraph.Concept.Benchmark;
|
using Ghost.RenderGraph.Concept.Benchmark;
|
||||||
|
|
||||||
var renderGraph = new RenderGraph();
|
|
||||||
|
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
|
|
||||||
BenchmarkDotNet.Running.BenchmarkRunner.Run<RenderGraphBenchmark>();
|
BenchmarkDotNet.Running.BenchmarkRunner.Run<RenderGraphBenchmark>();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//const int _ITERATION = 500000;
|
var renderGraph = new RenderGraph();
|
||||||
//for (var i = 0; i < _ITERATION; i++)
|
const int _ITERATION = 500000;
|
||||||
//{
|
for (var i = 0; i < _ITERATION; i++)
|
||||||
// RenderGraphBenchmark.ExecuteGraph(renderGraph);
|
{
|
||||||
//}
|
RenderGraphBenchmark.ExecuteGraph(renderGraph);
|
||||||
|
}
|
||||||
|
|
||||||
//GC.Collect();
|
GC.Collect();
|
||||||
//GC.WaitForPendingFinalizers();
|
GC.WaitForPendingFinalizers();
|
||||||
////Thread.Sleep(1000); // Leave a gap in visual studio allocations timeline
|
//Thread.Sleep(1000); // Leave a gap in visual studio allocations timeline
|
||||||
//var sw = new System.Diagnostics.Stopwatch();
|
var sw = new System.Diagnostics.Stopwatch();
|
||||||
//var gcBefore = GC.GetAllocatedBytesForCurrentThread();
|
var gcBefore = GC.GetAllocatedBytesForCurrentThread();
|
||||||
//sw.Start();
|
sw.Start();
|
||||||
|
|
||||||
//for (var i = 0; i < _ITERATION; i++)
|
for (var i = 0; i < _ITERATION; i++)
|
||||||
//{
|
{
|
||||||
// RenderGraphBenchmark.ExecuteGraph(renderGraph);
|
RenderGraphBenchmark.ExecuteGraph(renderGraph);
|
||||||
//}
|
}
|
||||||
|
|
||||||
//sw.Stop();
|
sw.Stop();
|
||||||
//var gcAfter = GC.GetAllocatedBytesForCurrentThread();
|
var gcAfter = GC.GetAllocatedBytesForCurrentThread();
|
||||||
|
|
||||||
//Console.WriteLine($"{sw.Elapsed.TotalNanoseconds / _ITERATION} ns (per iteration)");
|
Console.WriteLine($"{sw.Elapsed.TotalNanoseconds / _ITERATION} ns (per iteration)");
|
||||||
//Console.WriteLine($"GC Allocated Bytes: {(gcAfter - gcBefore) / _ITERATION} bytes (per iteration)");
|
Console.WriteLine($"GC Allocated Bytes: {(gcAfter - gcBefore) / _ITERATION} bytes (per iteration)");
|
||||||
#else
|
#else
|
||||||
|
var renderGraph = new RenderGraph();
|
||||||
|
|
||||||
// Run twice to demonstrate cache hit
|
// Run twice to demonstrate cache hit
|
||||||
Console.WriteLine("=== FRAME 1 (Cache Miss Expected) ===");
|
Console.WriteLine("=== FRAME 1 (Cache Miss Expected) ===");
|
||||||
RenderGraphBenchmark.ExecuteGraph(renderGraph);
|
RenderGraphBenchmark.ExecuteGraph(renderGraph);
|
||||||
|
|||||||
@@ -105,11 +105,32 @@ public sealed class RenderGraph
|
|||||||
return _builder;
|
return _builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private unsafe int ComputeTextureHash(byte* pData, int offset, Identifier<RGTexture> texture)
|
||||||
/// Computes a _hasher of the render graph structure for caching.
|
{
|
||||||
/// Does NOT include pass names (they don't affect compilation).
|
if (texture.IsInvalid)
|
||||||
/// Uses XxHash3 with SIMD optimizations for fast hashing.
|
{
|
||||||
/// </summary>
|
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()
|
private unsafe ulong ComputeGraphHash()
|
||||||
{
|
{
|
||||||
using var scope = AllocationManager.CreateStackScope();
|
using var scope = AllocationManager.CreateStackScope();
|
||||||
@@ -135,15 +156,14 @@ public sealed class RenderGraph
|
|||||||
*(bool*)(pData + offset) = pass.asyncCompute;
|
*(bool*)(pData + offset) = pass.asyncCompute;
|
||||||
offset += sizeof(bool);
|
offset += sizeof(bool);
|
||||||
|
|
||||||
*(TextureAccess*)(pData + offset) = pass.depthAccess;
|
// Hash depth attachment
|
||||||
offset += sizeof(TextureAccess);
|
offset = ComputeTextureHash(pData, offset, pass.depthAccess.id);
|
||||||
|
|
||||||
*(int*)(pData + offset) = pass.maxColorIndex;
|
*(int*)(pData + offset) = pass.maxColorIndex;
|
||||||
offset += sizeof(int);
|
offset += sizeof(int);
|
||||||
for (var j = 0; j <= pass.maxColorIndex; j++)
|
for (var j = 0; j <= pass.maxColorIndex; j++)
|
||||||
{
|
{
|
||||||
*(TextureAccess*)(pData + offset) = pass.colorAccess[j];
|
offset = ComputeTextureHash(pData, offset, pass.colorAccess[j].id);
|
||||||
offset += sizeof(TextureAccess);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*(int*)(pData + offset) = pass.resourceReads.Count;
|
*(int*)(pData + offset) = pass.resourceReads.Count;
|
||||||
@@ -171,23 +191,23 @@ public sealed class RenderGraph
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash resource descriptors
|
//// Hash resource descriptors
|
||||||
for (var i = 0; i < _resources.TextureResourceCount; i++)
|
//for (var i = 0; i < _resources.TextureResourceCount; i++)
|
||||||
{
|
//{
|
||||||
var resource = _resources.GetTextureResourceByIndex(i);
|
// var resource = _resources.GetTextureResourceByIndex(i);
|
||||||
|
|
||||||
*(int*)(pData + offset) = resource.Descriptor.Width;
|
// *(int*)(pData + offset) = resource.descriptor.width;
|
||||||
offset += sizeof(int);
|
// offset += sizeof(int);
|
||||||
|
|
||||||
*(int*)(pData + offset) = resource.Descriptor.Height;
|
// *(int*)(pData + offset) = resource.descriptor.height;
|
||||||
offset += sizeof(int);
|
// offset += sizeof(int);
|
||||||
|
|
||||||
*(TextureFormat*)(pData + offset) = resource.Descriptor.Format;
|
// *(TextureFormat*)(pData + offset) = resource.descriptor.format;
|
||||||
offset += sizeof(TextureFormat);
|
// offset += sizeof(TextureFormat);
|
||||||
|
|
||||||
*(bool*)(pData + offset) = resource.IsImported;
|
// *(bool*)(pData + offset) = resource.isImported;
|
||||||
offset += sizeof(bool);
|
// offset += sizeof(bool);
|
||||||
}
|
//}
|
||||||
|
|
||||||
var span = new Span<byte>(pData, offset);
|
var span = new Span<byte>(pData, offset);
|
||||||
return XxHash64.HashToUInt64(span);
|
return XxHash64.HashToUInt64(span);
|
||||||
@@ -208,7 +228,7 @@ public sealed class RenderGraph
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Step 0: Check cache
|
// Step 0: Check cache
|
||||||
var graphHash = ComputeGraphHash(); // 1321433047288519964
|
var graphHash = ComputeGraphHash(); // 17020363347016000737
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
var hashTime = sw.Elapsed.TotalMicroseconds;
|
var hashTime = sw.Elapsed.TotalMicroseconds;
|
||||||
@@ -245,7 +265,7 @@ public sealed class RenderGraph
|
|||||||
{
|
{
|
||||||
var writeHandle = pass.resourceWrites[j];
|
var writeHandle = pass.resourceWrites[j];
|
||||||
var resource = _resources.GetResource(writeHandle);
|
var resource = _resources.GetResource(writeHandle);
|
||||||
if (resource.IsImported)
|
if (resource.isImported)
|
||||||
{
|
{
|
||||||
pass.hasSideEffects = true;
|
pass.hasSideEffects = true;
|
||||||
break;
|
break;
|
||||||
@@ -299,31 +319,31 @@ public sealed class RenderGraph
|
|||||||
{
|
{
|
||||||
// Restore compiled pass list
|
// Restore compiled pass list
|
||||||
_compiledPasses.Clear();
|
_compiledPasses.Clear();
|
||||||
for (var i = 0; i < cached.CompiledPassIndices.Count; i++)
|
for (var i = 0; i < cached.compiledPassIndices.Count; i++)
|
||||||
{
|
{
|
||||||
var passIndex = cached.CompiledPassIndices[i];
|
var passIndex = cached.compiledPassIndices[i];
|
||||||
_compiledPasses.Add(_passes[passIndex]);
|
_compiledPasses.Add(_passes[passIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore culling flags
|
// Restore culling flags
|
||||||
for (var i = 0; i < _passes.Count && i < cached.PassCulledFlags.Count; i++)
|
for (var i = 0; i < _passes.Count && i < cached.passCulledFlags.Count; i++)
|
||||||
{
|
{
|
||||||
_passes[i].culled = cached.PassCulledFlags[i];
|
_passes[i].culled = cached.passCulledFlags[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore aliasing mappings (need to update ResourceAliasingManager)
|
// Restore aliasing mappings (need to update ResourceAliasingManager)
|
||||||
_aliasingManager.RestoreFromCache(cached.LogicalToPhysical, cached.PhysicalResources);
|
_aliasingManager.RestoreFromCache(cached.logicalToPhysical, cached.physicalResources);
|
||||||
|
|
||||||
// Restore barriers (deep copy to avoid shared references)
|
// Restore barriers (deep copy to avoid shared references)
|
||||||
_barriers.Clear();
|
_barriers.Clear();
|
||||||
for (var i = 0; i < cached.Barriers.Count; i++)
|
for (var i = 0; i < cached.barriers.Count; i++)
|
||||||
{
|
{
|
||||||
_barriers.Add(cached.Barriers[i]);
|
_barriers.Add(cached.barriers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore resource states
|
// Restore resource states
|
||||||
_resourceStates.Clear();
|
_resourceStates.Clear();
|
||||||
foreach (var kvp in cached.ResourceStates)
|
foreach (var kvp in cached.resourceStates)
|
||||||
{
|
{
|
||||||
_resourceStates[kvp.Key] = kvp.Value;
|
_resourceStates[kvp.Key] = kvp.Value;
|
||||||
}
|
}
|
||||||
@@ -339,54 +359,75 @@ public sealed class RenderGraph
|
|||||||
// Store compiled pass indices
|
// Store compiled pass indices
|
||||||
for (var i = 0; i < _compiledPasses.Count; i++)
|
for (var i = 0; i < _compiledPasses.Count; i++)
|
||||||
{
|
{
|
||||||
cacheData.CompiledPassIndices.Add(_compiledPasses[i].index);
|
cacheData.compiledPassIndices.Add(_compiledPasses[i].index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store culling flags for all passes
|
// Store culling flags for all passes
|
||||||
for (var i = 0; i < _passes.Count; i++)
|
for (var i = 0; i < _passes.Count; i++)
|
||||||
{
|
{
|
||||||
cacheData.PassCulledFlags.Add(_passes[i].culled);
|
cacheData.passCulledFlags.Add(_passes[i].culled);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store aliasing mappings
|
// Store aliasing mappings
|
||||||
_aliasingManager.StoreToCache(cacheData.LogicalToPhysical, cacheData.PhysicalResources);
|
_aliasingManager.StoreToCache(cacheData.logicalToPhysical, cacheData.physicalResources);
|
||||||
|
|
||||||
// Store barriers
|
// Store barriers
|
||||||
for (var i = 0; i < _barriers.Count; i++)
|
for (var i = 0; i < _barriers.Count; i++)
|
||||||
{
|
{
|
||||||
cacheData.Barriers.Add(_barriers[i]);
|
cacheData.barriers.Add(_barriers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store resource states
|
// Store resource states
|
||||||
foreach (var kvp in _resourceStates)
|
foreach (var kvp in _resourceStates)
|
||||||
{
|
{
|
||||||
cacheData.ResourceStates[kvp.Key] = kvp.Value;
|
cacheData.resourceStates[kvp.Key] = kvp.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
_compilationCache.Store(graphHash, cacheData);
|
_compilationCache.Store(graphHash, cacheData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private void UnculProducer(Identifier<RGResource> resource)
|
||||||
/// Recursively un-cull passes that a given pass depends on.
|
{
|
||||||
/// </summary>
|
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)
|
private void UnculDependencies(RenderGraphPassBase pass)
|
||||||
{
|
{
|
||||||
// Un-cull all producers of textures we read
|
// Un-cull producers of read resources
|
||||||
for (var i = 0; i < pass.resourceReads.Count; i++)
|
for (var i = 0; i < pass.resourceReads.Count; i++)
|
||||||
{
|
{
|
||||||
var readHandle = pass.resourceReads[i];
|
UnculProducer(pass.resourceReads[i]);
|
||||||
var resource = _resources.GetResource(readHandle);
|
}
|
||||||
|
|
||||||
if (resource.ProducerPass >= 0)
|
// Un-cull producers of color attachments
|
||||||
|
for (var i = 0; i <= pass.maxColorIndex; i++)
|
||||||
|
{
|
||||||
|
if (pass.colorAccess[i].id.IsValid)
|
||||||
{
|
{
|
||||||
var producer = _passes[resource.ProducerPass];
|
UnculProducer(pass.colorAccess[i].id.AsResource());
|
||||||
if (producer.culled)
|
|
||||||
{
|
|
||||||
producer.culled = false;
|
|
||||||
UnculDependencies(producer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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>
|
/// <summary>
|
||||||
@@ -431,11 +472,11 @@ public sealed class RenderGraph
|
|||||||
var resource = _resources.GetResource(id);
|
var resource = _resources.GetResource(id);
|
||||||
|
|
||||||
// Skip imported resources
|
// Skip imported resources
|
||||||
if (resource.IsImported)
|
if (resource.isImported)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check if this is the first use of this logical resource
|
// Check if this is the first use of this logical resource
|
||||||
if (resource.FirstUsePass == pass.index)
|
if (resource.firstUsePass == pass.index)
|
||||||
{
|
{
|
||||||
// Rent the physical resource
|
// Rent the physical resource
|
||||||
var physicalIndex = _aliasingManager.GetPhysicalResourceIndex(id.Value);
|
var physicalIndex = _aliasingManager.GetPhysicalResourceIndex(id.Value);
|
||||||
@@ -445,22 +486,22 @@ public sealed class RenderGraph
|
|||||||
|
|
||||||
// If this physical resource has multiple aliased resources,
|
// If this physical resource has multiple aliased resources,
|
||||||
// we need an aliasing barrier when switching between them
|
// we need an aliasing barrier when switching between them
|
||||||
if (physical != null && physical.AliasedLogicalResources.Count > 1)
|
if (physical != null && physical.aliasedLogicalResources.Count > 1)
|
||||||
{
|
{
|
||||||
// Find the resource that used this physical memory most recently before this pass
|
// Find the resource that used this physical memory most recently before this pass
|
||||||
Identifier<RGResource> resourceBefore = default;
|
Identifier<RGResource> resourceBefore = default;
|
||||||
var mostRecentLastUse = -1;
|
var mostRecentLastUse = -1;
|
||||||
|
|
||||||
foreach (var otherLogicalIndex in physical.AliasedLogicalResources)
|
foreach (var otherLogicalIndex in physical.aliasedLogicalResources)
|
||||||
{
|
{
|
||||||
if (otherLogicalIndex != id.Value)
|
if (otherLogicalIndex != id.Value)
|
||||||
{
|
{
|
||||||
var otherResource = _resources.GetTextureResourceByIndex(otherLogicalIndex);
|
var otherResource = _resources.GetTextureResourceByIndex(otherLogicalIndex);
|
||||||
// Check if this resource finished before our resource starts
|
// Check if this resource finished before our resource starts
|
||||||
if (otherResource.LastUsePass < pass.index &&
|
if (otherResource.lastUsePass < pass.index &&
|
||||||
otherResource.LastUsePass > mostRecentLastUse)
|
otherResource.lastUsePass > mostRecentLastUse)
|
||||||
{
|
{
|
||||||
mostRecentLastUse = otherResource.LastUsePass;
|
mostRecentLastUse = otherResource.lastUsePass;
|
||||||
resourceBefore = otherLogicalIndex;
|
resourceBefore = otherLogicalIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -596,6 +637,13 @@ public sealed class RenderGraph
|
|||||||
barrier.StateAfter
|
barrier.StateAfter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
else if (barrier.Type == BarrierType.Aliasing)
|
||||||
|
{
|
||||||
|
_commandBuffer.AliasBarrier(
|
||||||
|
barrier.ResourceBefore,
|
||||||
|
barrier.ResourceAfter
|
||||||
|
);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
// In a real implementation, you would execute the barrier here:
|
// In a real implementation, you would execute the barrier here:
|
||||||
// ExecuteBarrier(_barriers[barrierIndex]);
|
// ExecuteBarrier(_barriers[barrierIndex]);
|
||||||
|
|||||||
@@ -7,54 +7,54 @@ namespace Ghost.RenderGraph.Concept;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class PhysicalResource
|
internal sealed class PhysicalResource
|
||||||
{
|
{
|
||||||
public int Index;
|
public int index;
|
||||||
public int Width;
|
public int width;
|
||||||
public int Height;
|
public int height;
|
||||||
public TextureFormat Format;
|
public TextureFormat format;
|
||||||
public int SizeInBytes;
|
public int sizeInBytes;
|
||||||
|
|
||||||
// Lifetime tracking
|
// Lifetime tracking
|
||||||
public int FirstUsePass = int.MaxValue;
|
public int firstUsePass = int.MaxValue;
|
||||||
public int LastUsePass = -1;
|
public int lastUsePass = -1;
|
||||||
|
|
||||||
// Aliasing tracking
|
// Aliasing tracking
|
||||||
public readonly List<int> AliasedLogicalResources = new(4);
|
public readonly List<int> aliasedLogicalResources = new(4);
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
Index = -1;
|
index = -1;
|
||||||
Width = 0;
|
width = 0;
|
||||||
Height = 0;
|
height = 0;
|
||||||
Format = TextureFormat.RGBA8;
|
format = TextureFormat.RGBA8;
|
||||||
SizeInBytes = 0;
|
sizeInBytes = 0;
|
||||||
FirstUsePass = int.MaxValue;
|
firstUsePass = int.MaxValue;
|
||||||
LastUsePass = -1;
|
lastUsePass = -1;
|
||||||
AliasedLogicalResources.Clear();
|
aliasedLogicalResources.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanAlias(TextureDescriptor descriptor)
|
public bool CanAlias(TextureDescriptor descriptor)
|
||||||
{
|
{
|
||||||
// For aliasing, resources must be identical in size and format
|
// 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)
|
// In a real implementation, you could be more flexible (e.g., same size but different format)
|
||||||
return Width == descriptor.Width &&
|
return width == descriptor.width &&
|
||||||
Height == descriptor.Height &&
|
height == descriptor.height &&
|
||||||
Format == descriptor.Format;
|
format == descriptor.format;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateLifetime(int passIndex)
|
public void UpdateLifetime(int passIndex)
|
||||||
{
|
{
|
||||||
FirstUsePass = Math.Min(FirstUsePass, passIndex);
|
firstUsePass = Math.Min(firstUsePass, passIndex);
|
||||||
LastUsePass = Math.Max(LastUsePass, passIndex);
|
lastUsePass = Math.Max(lastUsePass, passIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsAliveAt(int passIndex)
|
public bool IsAliveAt(int passIndex)
|
||||||
{
|
{
|
||||||
return passIndex >= FirstUsePass && passIndex <= LastUsePass;
|
return passIndex >= firstUsePass && passIndex <= lastUsePass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int CalculateSize()
|
public int CalculateSize()
|
||||||
{
|
{
|
||||||
int bytesPerPixel = Format switch
|
int bytesPerPixel = format switch
|
||||||
{
|
{
|
||||||
TextureFormat.RGBA8 => 4,
|
TextureFormat.RGBA8 => 4,
|
||||||
TextureFormat.RGBA16F => 8,
|
TextureFormat.RGBA16F => 8,
|
||||||
@@ -63,7 +63,7 @@ internal sealed class PhysicalResource
|
|||||||
TextureFormat.Depth24Stencil8 => 4,
|
TextureFormat.Depth24Stencil8 => 4,
|
||||||
_ => 4
|
_ => 4
|
||||||
};
|
};
|
||||||
return Width * Height * bytesPerPixel;
|
return width * height * bytesPerPixel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,21 +109,21 @@ internal sealed class ResourceAliasingManager
|
|||||||
for (int i = 0; i < registry.TextureResourceCount; i++)
|
for (int i = 0; i < registry.TextureResourceCount; i++)
|
||||||
{
|
{
|
||||||
var resource = registry.GetTextureResourceByIndex(i);
|
var resource = registry.GetTextureResourceByIndex(i);
|
||||||
if (!resource.IsImported) // Don't alias imported resources
|
if (!resource.isImported) // Don't alias imported resources
|
||||||
{
|
{
|
||||||
logicalResources.Add((i, resource));
|
logicalResources.Add((i, resource));
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
int size = CalculateSize(resource.Descriptor);
|
int size = CalculateSize(resource.descriptor);
|
||||||
totalLogicalSize += size;
|
totalLogicalSize += size;
|
||||||
Console.WriteLine($"Logical Resource {i}: {resource.Descriptor.Name}");
|
Console.WriteLine($"Logical Resource {i}: {resource.descriptor.name}");
|
||||||
Console.WriteLine($" Lifetime: Pass {resource.FirstUsePass} -> {resource.LastUsePass}");
|
Console.WriteLine($" Lifetime: Pass {resource.firstUsePass} -> {resource.lastUsePass}");
|
||||||
Console.WriteLine($" Size: {size / 1024.0:F2} KB");
|
Console.WriteLine($" Size: {size / 1024.0:F2} KB");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by first use pass (earlier resources first)
|
// Sort by first use pass (earlier resources first)
|
||||||
logicalResources.Sort((a, b) => a.resource.FirstUsePass.CompareTo(b.resource.FirstUsePass));
|
logicalResources.Sort((a, b) => a.resource.firstUsePass.CompareTo(b.resource.firstUsePass));
|
||||||
|
|
||||||
// Greedy interval scheduling: assign each logical resource to a physical resource
|
// Greedy interval scheduling: assign each logical resource to a physical resource
|
||||||
foreach (var (logicalIndex, logicalResource) in logicalResources)
|
foreach (var (logicalIndex, logicalResource) in logicalResources)
|
||||||
@@ -137,7 +137,7 @@ internal sealed class ResourceAliasingManager
|
|||||||
{
|
{
|
||||||
var physical = _physicalResources[i];
|
var physical = _physicalResources[i];
|
||||||
|
|
||||||
if (physical.CanAlias(logicalResource.Descriptor) &&
|
if (physical.CanAlias(logicalResource.descriptor) &&
|
||||||
!HasLifetimeOverlap(physical, logicalResource))
|
!HasLifetimeOverlap(physical, logicalResource))
|
||||||
{
|
{
|
||||||
assignedPhysical = physical;
|
assignedPhysical = physical;
|
||||||
@@ -149,40 +149,40 @@ internal sealed class ResourceAliasingManager
|
|||||||
if (assignedPhysical == null)
|
if (assignedPhysical == null)
|
||||||
{
|
{
|
||||||
assignedPhysical = GetOrCreatePhysicalResource();
|
assignedPhysical = GetOrCreatePhysicalResource();
|
||||||
assignedPhysical.Index = _physicalResourceCount - 1;
|
assignedPhysical.index = _physicalResourceCount - 1;
|
||||||
assignedPhysical.Width = logicalResource.Descriptor.Width;
|
assignedPhysical.width = logicalResource.descriptor.width;
|
||||||
assignedPhysical.Height = logicalResource.Descriptor.Height;
|
assignedPhysical.height = logicalResource.descriptor.height;
|
||||||
assignedPhysical.Format = logicalResource.Descriptor.Format;
|
assignedPhysical.format = logicalResource.descriptor.format;
|
||||||
assignedPhysical.SizeInBytes = assignedPhysical.CalculateSize();
|
assignedPhysical.sizeInBytes = assignedPhysical.CalculateSize();
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Console.WriteLine($"\nAllocated NEW Physical Resource {assignedPhysical.Index}:");
|
Console.WriteLine($"\nAllocated NEW Physical Resource {assignedPhysical.index}:");
|
||||||
Console.WriteLine($" Size: {assignedPhysical.Width}x{assignedPhysical.Height}");
|
Console.WriteLine($" Size: {assignedPhysical.width}x{assignedPhysical.height}");
|
||||||
Console.WriteLine($" Format: {assignedPhysical.Format}");
|
Console.WriteLine($" Format: {assignedPhysical.format}");
|
||||||
Console.WriteLine($" Memory: {assignedPhysical.SizeInBytes / 1024.0:F2} KB");
|
Console.WriteLine($" Memory: {assignedPhysical.sizeInBytes / 1024.0:F2} KB");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine($"\nALIASING: {logicalResource.Descriptor.Name} -> Physical Resource {assignedPhysical.Index}");
|
Console.WriteLine($"\nALIASING: {logicalResource.descriptor.name} -> Physical Resource {assignedPhysical.index}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Update physical resource lifetime
|
// Update physical resource lifetime
|
||||||
assignedPhysical.UpdateLifetime(logicalResource.FirstUsePass);
|
assignedPhysical.UpdateLifetime(logicalResource.firstUsePass);
|
||||||
assignedPhysical.UpdateLifetime(logicalResource.LastUsePass);
|
assignedPhysical.UpdateLifetime(logicalResource.lastUsePass);
|
||||||
assignedPhysical.AliasedLogicalResources.Add(logicalIndex);
|
assignedPhysical.aliasedLogicalResources.Add(logicalIndex);
|
||||||
|
|
||||||
// Record the mapping
|
// Record the mapping
|
||||||
_logicalToPhysical[logicalIndex] = assignedPhysical.Index;
|
_logicalToPhysical[logicalIndex] = assignedPhysical.index;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
int totalPhysicalSize = 0;
|
int totalPhysicalSize = 0;
|
||||||
for (int i = 0; i < _physicalResourceCount; i++)
|
for (int i = 0; i < _physicalResourceCount; i++)
|
||||||
{
|
{
|
||||||
totalPhysicalSize += _physicalResources[i].SizeInBytes;
|
totalPhysicalSize += _physicalResources[i].sizeInBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"\n=== Aliasing Summary ===");
|
Console.WriteLine($"\n=== Aliasing Summary ===");
|
||||||
@@ -213,8 +213,8 @@ internal sealed class ResourceAliasingManager
|
|||||||
{
|
{
|
||||||
// Check if the lifetimes overlap
|
// Check if the lifetimes overlap
|
||||||
// No overlap if: logical.First > physical.Last OR logical.Last < physical.First
|
// No overlap if: logical.First > physical.Last OR logical.Last < physical.First
|
||||||
return !(logical.FirstUsePass > physical.LastUsePass ||
|
return !(logical.firstUsePass > physical.lastUsePass ||
|
||||||
logical.LastUsePass < physical.FirstUsePass);
|
logical.lastUsePass < physical.firstUsePass);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PhysicalResource GetOrCreatePhysicalResource()
|
private PhysicalResource GetOrCreatePhysicalResource()
|
||||||
@@ -238,7 +238,7 @@ internal sealed class ResourceAliasingManager
|
|||||||
|
|
||||||
private static int CalculateSize(TextureDescriptor descriptor)
|
private static int CalculateSize(TextureDescriptor descriptor)
|
||||||
{
|
{
|
||||||
int bytesPerPixel = descriptor.Format switch
|
int bytesPerPixel = descriptor.format switch
|
||||||
{
|
{
|
||||||
TextureFormat.RGBA8 => 4,
|
TextureFormat.RGBA8 => 4,
|
||||||
TextureFormat.RGBA16F => 8,
|
TextureFormat.RGBA16F => 8,
|
||||||
@@ -247,7 +247,7 @@ internal sealed class ResourceAliasingManager
|
|||||||
TextureFormat.Depth24Stencil8 => 4,
|
TextureFormat.Depth24Stencil8 => 4,
|
||||||
_ => 4
|
_ => 4
|
||||||
};
|
};
|
||||||
return descriptor.Width * descriptor.Height * bytesPerPixel;
|
return descriptor.width * descriptor.height * bytesPerPixel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
@@ -290,13 +290,13 @@ internal sealed class ResourceAliasingManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
var data = physicalData[i];
|
var data = physicalData[i];
|
||||||
physical.Index = data.Index;
|
physical.index = data.index;
|
||||||
physical.Width = data.Width;
|
physical.width = data.width;
|
||||||
physical.Height = data.Height;
|
physical.height = data.height;
|
||||||
physical.Format = data.Format;
|
physical.format = data.format;
|
||||||
physical.FirstUsePass = data.FirstUsePass;
|
physical.firstUsePass = data.firstUsePass;
|
||||||
physical.LastUsePass = data.LastUsePass;
|
physical.lastUsePass = data.lastUsePass;
|
||||||
physical.SizeInBytes = physical.CalculateSize();
|
physical.sizeInBytes = physical.CalculateSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,12 +317,12 @@ internal sealed class ResourceAliasingManager
|
|||||||
var physical = _physicalResources[i];
|
var physical = _physicalResources[i];
|
||||||
outPhysicalData.Add(new PhysicalResourceData
|
outPhysicalData.Add(new PhysicalResourceData
|
||||||
{
|
{
|
||||||
Index = physical.Index,
|
index = physical.index,
|
||||||
Width = physical.Width,
|
width = physical.width,
|
||||||
Height = physical.Height,
|
height = physical.height,
|
||||||
Format = physical.Format,
|
format = physical.format,
|
||||||
FirstUsePass = physical.FirstUsePass,
|
firstUsePass = physical.firstUsePass,
|
||||||
LastUsePass = physical.LastUsePass
|
lastUsePass = physical.lastUsePass
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,6 +123,16 @@ internal struct ResourceBarrier
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override readonly string ToString()
|
||||||
|
{
|
||||||
|
return Type switch
|
||||||
|
{
|
||||||
|
BarrierType.Transition => $"[Pass {PassIndex}] Transition Barrier: Resource {Resource.Value} from {StateBefore} to {StateAfter}",
|
||||||
|
BarrierType.Aliasing => $"[Pass {PassIndex}] Aliasing Barrier: ResourceBefore {ResourceBefore.Value} to ResourceAfter {ResourceAfter.Value}",
|
||||||
|
_ => "Unknown Barrier Type"
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -9,31 +9,31 @@ namespace Ghost.RenderGraph.Concept;
|
|||||||
internal sealed class CachedCompilation
|
internal sealed class CachedCompilation
|
||||||
{
|
{
|
||||||
// Compiled pass indices (indices into the _passes list)
|
// Compiled pass indices (indices into the _passes list)
|
||||||
public readonly List<int> CompiledPassIndices = new(64);
|
public readonly List<int> compiledPassIndices = new(64);
|
||||||
|
|
||||||
// Culling decisions for each pass
|
// Culling decisions for each pass
|
||||||
public readonly List<bool> PassCulledFlags = new(64);
|
public readonly List<bool> passCulledFlags = new(64);
|
||||||
|
|
||||||
// Physical resource aliasing mappings (logical index -> physical index)
|
// Physical resource aliasing mappings (logical index -> physical index)
|
||||||
public readonly Dictionary<int, int> LogicalToPhysical = new(128);
|
public readonly Dictionary<int, int> logicalToPhysical = new(128);
|
||||||
|
|
||||||
// Physical resource metadata
|
// Physical resource metadata
|
||||||
public readonly List<PhysicalResourceData> PhysicalResources = new(32);
|
public readonly List<PhysicalResourceData> physicalResources = new(32);
|
||||||
|
|
||||||
// Resource barriers
|
// Resource barriers
|
||||||
public readonly List<ResourceBarrier> Barriers = new(128);
|
public readonly List<ResourceBarrier> barriers = new(128);
|
||||||
|
|
||||||
// Resource state mappings (for barrier generation)
|
// Resource state mappings (for barrier generation)
|
||||||
public readonly Dictionary<int, ResourceState> ResourceStates = new(128);
|
public readonly Dictionary<int, ResourceState> resourceStates = new(128);
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
CompiledPassIndices.Clear();
|
compiledPassIndices.Clear();
|
||||||
PassCulledFlags.Clear();
|
passCulledFlags.Clear();
|
||||||
LogicalToPhysical.Clear();
|
logicalToPhysical.Clear();
|
||||||
PhysicalResources.Clear();
|
physicalResources.Clear();
|
||||||
Barriers.Clear();
|
barriers.Clear();
|
||||||
ResourceStates.Clear();
|
resourceStates.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,12 +42,12 @@ internal sealed class CachedCompilation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal struct PhysicalResourceData
|
internal struct PhysicalResourceData
|
||||||
{
|
{
|
||||||
public int Index;
|
public int index;
|
||||||
public int Width;
|
public int width;
|
||||||
public int Height;
|
public int height;
|
||||||
public TextureFormat Format;
|
public TextureFormat format;
|
||||||
public int FirstUsePass;
|
public int firstUsePass;
|
||||||
public int LastUsePass;
|
public int lastUsePass;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -92,20 +92,20 @@ internal sealed class RenderGraphCompilationCache
|
|||||||
// Deep copy the data
|
// Deep copy the data
|
||||||
_cached.Clear();
|
_cached.Clear();
|
||||||
|
|
||||||
_cached.CompiledPassIndices.AddRange(data.CompiledPassIndices);
|
_cached.compiledPassIndices.AddRange(data.compiledPassIndices);
|
||||||
_cached.PassCulledFlags.AddRange(data.PassCulledFlags);
|
_cached.passCulledFlags.AddRange(data.passCulledFlags);
|
||||||
|
|
||||||
foreach (var kvp in data.LogicalToPhysical)
|
foreach (var kvp in data.logicalToPhysical)
|
||||||
{
|
{
|
||||||
_cached.LogicalToPhysical[kvp.Key] = kvp.Value;
|
_cached.logicalToPhysical[kvp.Key] = kvp.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
_cached.PhysicalResources.AddRange(data.PhysicalResources);
|
_cached.physicalResources.AddRange(data.physicalResources);
|
||||||
_cached.Barriers.AddRange(data.Barriers);
|
_cached.barriers.AddRange(data.barriers);
|
||||||
|
|
||||||
foreach (var kvp in data.ResourceStates)
|
foreach (var kvp in data.resourceStates)
|
||||||
{
|
{
|
||||||
_cached.ResourceStates[kvp.Key] = kvp.Value;
|
_cached.resourceStates[kvp.Key] = kvp.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ internal sealed class RenderGraphObjectPool
|
|||||||
|
|
||||||
private static ObjectPool<T> AllocatePool()
|
private static ObjectPool<T> AllocatePool()
|
||||||
{
|
{
|
||||||
var newPool = new ObjectPool<T>(() => new T());
|
var newPool = new ObjectPool<T>(() => new T(), null);
|
||||||
// Storing instance to clear the static pool of the same type if needed
|
// Storing instance to clear the static pool of the same type if needed
|
||||||
s_allocatedPools.Add(new SharedObjectPool<T>());
|
s_allocatedPools.Add(new SharedObjectPool<T>());
|
||||||
return newPool;
|
return newPool;
|
||||||
@@ -42,6 +42,8 @@ internal sealed class RenderGraphObjectPool
|
|||||||
/// Rent a new instance from the pool.
|
/// Rent a new instance from the pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
// FIX: ObjectPool<T>.Rent() has a critical bug that it will put the newly created object into the pool directly and give out the same instance again.
|
||||||
|
// This will cause multiple renters to get the same instance.
|
||||||
public static T Rent() => s_pool.Rent();
|
public static T Rent() => s_pool.Rent();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -78,25 +80,25 @@ internal sealed class RenderGraphObjectPool
|
|||||||
internal sealed class RenderGraphResource
|
internal sealed class RenderGraphResource
|
||||||
{
|
{
|
||||||
public RenderGraphResourceType type;
|
public RenderGraphResourceType type;
|
||||||
public int Index;
|
public int index;
|
||||||
public TextureDescriptor Descriptor;
|
public TextureDescriptor descriptor;
|
||||||
public bool IsImported;
|
public bool isImported;
|
||||||
public int FirstUsePass = -1;
|
public int firstUsePass = -1;
|
||||||
public int LastUsePass = -1;
|
public int lastUsePass = -1;
|
||||||
public int ProducerPass = -1;
|
public int producerPass = -1;
|
||||||
public List<int> ConsumerPasses = new(4);
|
public List<int> consumerPasses = new(4);
|
||||||
public int RefCount;
|
public int refCount;
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
Index = -1;
|
index = -1;
|
||||||
Descriptor = default;
|
descriptor = default;
|
||||||
IsImported = false;
|
isImported = false;
|
||||||
FirstUsePass = -1;
|
firstUsePass = -1;
|
||||||
LastUsePass = -1;
|
lastUsePass = -1;
|
||||||
ProducerPass = -1;
|
producerPass = -1;
|
||||||
ConsumerPasses.Clear();
|
consumerPasses.Clear();
|
||||||
RefCount = 0;
|
refCount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,102 +110,73 @@ internal sealed class RenderGraphResourceRegistry
|
|||||||
{
|
{
|
||||||
private readonly List<RenderGraphResource> _resources = new(64);
|
private readonly List<RenderGraphResource> _resources = new(64);
|
||||||
private readonly RenderGraphObjectPool _pool = new();
|
private readonly RenderGraphObjectPool _pool = new();
|
||||||
private int _textureResourceCount;
|
|
||||||
|
|
||||||
public int TextureResourceCount => _textureResourceCount;
|
public int TextureResourceCount => _resources.Count;
|
||||||
|
|
||||||
public void BeginFrame()
|
public void BeginFrame()
|
||||||
{
|
{
|
||||||
// Don't clear the lists, just reset the count
|
for (var i = 0; i < _resources.Count; i++)
|
||||||
// This avoids reallocating the backing arrays
|
{
|
||||||
_textureResourceCount = 0;
|
_pool.Return(_resources[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_resources.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Identifier<RGTexture> ImportTexture(TextureDescriptor descriptor)
|
public Identifier<RGTexture> ImportTexture(TextureDescriptor descriptor)
|
||||||
{
|
{
|
||||||
var resource = GetOrCreateTextureResource();
|
var resource = _pool.Rent<RenderGraphResource>();
|
||||||
resource.Index = _textureResourceCount - 1;
|
resource.type = RenderGraphResourceType.Texture;
|
||||||
resource.Descriptor = descriptor;
|
resource.index = _resources.Count;
|
||||||
resource.IsImported = true;
|
resource.descriptor = descriptor;
|
||||||
|
resource.isImported = true;
|
||||||
|
|
||||||
return new Identifier<RGTexture>(resource.Index);
|
_resources.Add(resource);
|
||||||
|
|
||||||
|
return new Identifier<RGTexture>(resource.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Identifier<RGTexture> CreateTexture(TextureDescriptor descriptor)
|
public Identifier<RGTexture> CreateTexture(TextureDescriptor descriptor)
|
||||||
{
|
{
|
||||||
var resource = GetOrCreateTextureResource();
|
var resource = _pool.Rent<RenderGraphResource>();
|
||||||
resource.Index = _textureResourceCount - 1;
|
resource.type = RenderGraphResourceType.Texture;
|
||||||
resource.Descriptor = descriptor;
|
resource.index = _resources.Count;
|
||||||
resource.IsImported = false;
|
resource.descriptor = descriptor;
|
||||||
|
resource.isImported = false;
|
||||||
|
|
||||||
return new Identifier<RGTexture>(resource.Index);
|
_resources.Add(resource);
|
||||||
|
|
||||||
|
return new Identifier<RGTexture>(resource.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RenderGraphResource GetResource(Identifier<RGResource> resource)
|
public RenderGraphResource GetResource(Identifier<RGResource> resource)
|
||||||
{
|
{
|
||||||
if (resource.Value < 0 || resource.Value >= _textureResourceCount)
|
|
||||||
throw new ArgumentException($"Invalid texture handle: {resource}");
|
|
||||||
|
|
||||||
return _resources[resource.Value];
|
return _resources[resource.Value];
|
||||||
}
|
}
|
||||||
|
|
||||||
public RenderGraphResource GetTextureResourceByIndex(int index)
|
public RenderGraphResource GetTextureResourceByIndex(int index)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= _textureResourceCount)
|
|
||||||
throw new ArgumentException($"Invalid texture index: {index}");
|
|
||||||
|
|
||||||
return _resources[index];
|
return _resources[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetProducer(Identifier<RGResource> resourceID, int passIndex)
|
public void SetProducer(Identifier<RGResource> resourceID, int passIndex)
|
||||||
{
|
{
|
||||||
var resource = GetResource(resourceID);
|
var resource = GetResource(resourceID);
|
||||||
resource.ProducerPass = passIndex;
|
resource.producerPass = passIndex;
|
||||||
if (resource.FirstUsePass < 0)
|
if (resource.firstUsePass < 0)
|
||||||
{
|
{
|
||||||
resource.FirstUsePass = passIndex;
|
resource.firstUsePass = passIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddConsumer(Identifier<RGResource> resourceID, int passIndex)
|
public void AddConsumer(Identifier<RGResource> resourceID, int passIndex)
|
||||||
{
|
{
|
||||||
var resource = GetResource(resourceID);
|
var resource = GetResource(resourceID);
|
||||||
resource.ConsumerPasses.Add(passIndex);
|
resource.consumerPasses.Add(passIndex);
|
||||||
resource.LastUsePass = passIndex;
|
resource.lastUsePass = passIndex;
|
||||||
if (resource.FirstUsePass < 0)
|
if (resource.firstUsePass < 0)
|
||||||
{
|
{
|
||||||
resource.FirstUsePass = passIndex;
|
resource.firstUsePass = passIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RenderGraphResource GetOrCreateTextureResource()
|
|
||||||
{
|
|
||||||
RenderGraphResource resource;
|
|
||||||
if (_textureResourceCount < _resources.Count)
|
|
||||||
{
|
|
||||||
// Reuse existing slot
|
|
||||||
resource = _resources[_textureResourceCount];
|
|
||||||
resource.Reset();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Need to grow the list
|
|
||||||
resource = _pool.Rent<RenderGraphResource>();
|
|
||||||
resource.Reset();
|
|
||||||
_resources.Add(resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
_textureResourceCount++;
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
for (var i = 0; i < _resources.Count; i++)
|
|
||||||
{
|
|
||||||
_pool.Return(_resources[i]);
|
|
||||||
}
|
|
||||||
_resources.Clear();
|
|
||||||
_textureResourceCount = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,27 +71,27 @@ public enum TextureFormat : int
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly struct TextureDescriptor : IEquatable<TextureDescriptor>
|
public readonly struct TextureDescriptor : IEquatable<TextureDescriptor>
|
||||||
{
|
{
|
||||||
public readonly int Width;
|
public readonly int width;
|
||||||
public readonly int Height;
|
public readonly int height;
|
||||||
public readonly TextureFormat Format;
|
public readonly TextureFormat format;
|
||||||
public readonly string Name;
|
public readonly string name;
|
||||||
|
|
||||||
public TextureDescriptor(int width, int height, TextureFormat format, string name)
|
public TextureDescriptor(int width, int height, TextureFormat format, string name)
|
||||||
{
|
{
|
||||||
Width = width;
|
this.width = width;
|
||||||
Height = height;
|
this.height = height;
|
||||||
Format = format;
|
this.format = format;
|
||||||
Name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly bool Equals(TextureDescriptor other) =>
|
public readonly bool Equals(TextureDescriptor other) =>
|
||||||
Width == other.Width &&
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user