forked from Misaki/GhostEngine
Updated alias algorithm
This commit is contained in:
@@ -40,10 +40,13 @@ internal class ResourceAllocator
|
||||
Console.WriteLine("\n[RG] ===== RESOURCE ALIASING ANALYSIS =====");
|
||||
|
||||
// Separate imported and transient resources
|
||||
// Sort by SIZE FIRST (descending), then by FIRST USE (ascending)
|
||||
// This allows smaller resources (A, B) to alias into larger resources' (C) space
|
||||
// Example: C=10MB[1..2], A=4MB[0..1], B=6MB[0..1] → Allocate C first, then A and B alias into C's space
|
||||
var transientResources = resourceLifetimes
|
||||
.Where(lt => !lt.Handle.IsImported && lt.FirstUse != int.MaxValue)
|
||||
.OrderBy(lt => lt.FirstUse)
|
||||
.ThenByDescending(lt => GetResourceSize(lt.Handle))
|
||||
.OrderByDescending(lt => GetResourceSize(lt.Handle))
|
||||
.ThenBy(lt => lt.FirstUse)
|
||||
.ToList();
|
||||
|
||||
if (!transientResources.Any())
|
||||
@@ -76,27 +79,34 @@ internal class ResourceAllocator
|
||||
|
||||
if (reuseSlot != null)
|
||||
{
|
||||
// Reuse existing allocation
|
||||
reuseSlot.AddResource(resource);
|
||||
// Reuse existing allocation - find offset within the allocation
|
||||
ulong offsetInAllocation = reuseSlot.FindFreeOffset(size, alignment, resource);
|
||||
reuseSlot.AddResource(resource, offsetInAllocation, size);
|
||||
|
||||
Console.WriteLine($"[ALIAS] '{resource.Handle.Name}' aliases with '{reuseSlot.Allocation.DebugName}' " +
|
||||
$"(offset: {reuseSlot.Allocation.OffsetInBytes}, size: {size} bytes, " +
|
||||
$"(heap offset: {reuseSlot.Allocation.OffsetInBytes}, resource offset: {offsetInAllocation}, size: {size} bytes, " +
|
||||
$"lifetime: [{resource.FirstUse}..{resource.LastUse}])");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create new allocation
|
||||
// Calculate heap offset (simulated - in real D3D12MA this would be the actual heap offset)
|
||||
ulong heapOffset = allocationSlots.Count > 0
|
||||
? allocationSlots.Max(s => s.Allocation.OffsetInBytes + s.Allocation.SizeInBytes)
|
||||
: 0;
|
||||
|
||||
var allocation = new PhysicalResourceAllocation(
|
||||
_allocationIdCounter++,
|
||||
size,
|
||||
offsetInBytes: 0, // In a real implementation, this would be a heap offset
|
||||
offsetInBytes: heapOffset,
|
||||
$"Physical_{resource.Handle.Type}_{_allocationIdCounter}");
|
||||
|
||||
var newSlot = new AllocationSlot(allocation, resource.Handle.Type);
|
||||
newSlot.AddResource(resource);
|
||||
newSlot.AddResource(resource, 0, size); // Offset 0 within this new allocation
|
||||
allocationSlots.Add(newSlot);
|
||||
|
||||
Console.WriteLine($"[ALLOC] '{resource.Handle.Name}' gets new allocation '{allocation.DebugName}' " +
|
||||
$"(size: {size} bytes, lifetime: [{resource.FirstUse}..{resource.LastUse}])");
|
||||
$"(heap offset: {heapOffset}, size: {size} bytes, lifetime: [{resource.FirstUse}..{resource.LastUse}])");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,6 +207,9 @@ internal class ResourceAllocator
|
||||
public PhysicalResourceAllocation Allocation { get; }
|
||||
public ResourceType ResourceType { get; }
|
||||
public List<ResourceLifetime> Resources { get; } = new();
|
||||
|
||||
// Track occupied regions within this allocation: (offset, size, lifetime)
|
||||
private readonly List<(ulong Offset, ulong Size, ResourceLifetime Resource)> _occupiedRegions = new();
|
||||
|
||||
public AllocationSlot(PhysicalResourceAllocation allocation, ResourceType resourceType)
|
||||
{
|
||||
@@ -204,9 +217,119 @@ internal class ResourceAllocator
|
||||
ResourceType = resourceType;
|
||||
}
|
||||
|
||||
public void AddResource(ResourceLifetime resource)
|
||||
/// <summary>
|
||||
/// Find a free offset within this allocation that can fit the required size
|
||||
/// and doesn't conflict with active resources
|
||||
/// </summary>
|
||||
public ulong FindFreeOffset(ulong requiredSize, ulong alignment, ResourceLifetime newResource)
|
||||
{
|
||||
// If no resources yet, return 0
|
||||
if (_occupiedRegions.Count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Sort regions by offset
|
||||
var sortedRegions = _occupiedRegions.OrderBy(r => r.Offset).ToList();
|
||||
|
||||
// Try to fit at the beginning (offset 0)
|
||||
ulong candidateOffset = 0;
|
||||
bool fitsAtStart = true;
|
||||
|
||||
foreach (var region in sortedRegions)
|
||||
{
|
||||
// Check if this region overlaps with our candidate position
|
||||
if (region.Offset < requiredSize)
|
||||
{
|
||||
// Check lifetime - if no overlap, we can still use this space
|
||||
if (LifetimesOverlap(region.Resource, newResource))
|
||||
{
|
||||
fitsAtStart = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fitsAtStart)
|
||||
{
|
||||
return AlignUp(0, alignment);
|
||||
}
|
||||
|
||||
// Try gaps between regions
|
||||
for (int i = 0; i < sortedRegions.Count; i++)
|
||||
{
|
||||
var current = sortedRegions[i];
|
||||
|
||||
// Skip if current region's lifetime overlaps with new resource
|
||||
if (LifetimesOverlap(current.Resource, newResource))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try placing after this region
|
||||
candidateOffset = AlignUp(current.Offset + current.Size, alignment);
|
||||
|
||||
// Check if it fits before the next region (or end of allocation)
|
||||
ulong nextRegionStart = (i + 1 < sortedRegions.Count)
|
||||
? sortedRegions[i + 1].Offset
|
||||
: Allocation.SizeInBytes;
|
||||
|
||||
if (candidateOffset + requiredSize <= nextRegionStart)
|
||||
{
|
||||
// Check no lifetime conflicts with any regions in this range
|
||||
bool hasConflict = false;
|
||||
for (int j = i + 1; j < sortedRegions.Count; j++)
|
||||
{
|
||||
var other = sortedRegions[j];
|
||||
if (other.Offset < candidateOffset + requiredSize)
|
||||
{
|
||||
if (LifetimesOverlap(other.Resource, newResource))
|
||||
{
|
||||
hasConflict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasConflict)
|
||||
{
|
||||
return candidateOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try placing at the end
|
||||
if (sortedRegions.Count > 0)
|
||||
{
|
||||
var last = sortedRegions[^1];
|
||||
if (!LifetimesOverlap(last.Resource, newResource))
|
||||
{
|
||||
candidateOffset = AlignUp(last.Offset + last.Size, alignment);
|
||||
if (candidateOffset + requiredSize <= Allocation.SizeInBytes)
|
||||
{
|
||||
return candidateOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No space found - caller should create new allocation
|
||||
return 0;
|
||||
}
|
||||
|
||||
private bool LifetimesOverlap(ResourceLifetime a, ResourceLifetime b)
|
||||
{
|
||||
return !(a.LastUse < b.FirstUse || b.LastUse < a.FirstUse);
|
||||
}
|
||||
|
||||
private ulong AlignUp(ulong value, ulong alignment)
|
||||
{
|
||||
return (value + alignment - 1) / alignment * alignment;
|
||||
}
|
||||
|
||||
public void AddResource(ResourceLifetime resource, ulong offsetInAllocation, ulong size)
|
||||
{
|
||||
Resources.Add(resource);
|
||||
_occupiedRegions.Add((offsetInAllocation, size, resource));
|
||||
Allocation.AliasedResources.Add(resource.Handle);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user