UnityGame/Library/PackageCache/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/PersistentGPUArray.cs
2024-10-27 10:53:47 +03:00

164 lines
5.4 KiB
C#

using System;
using System.Collections;
using System.Runtime.InteropServices;
using Unity.Collections;
using UnityEngine.Assertions;
namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal sealed class PersistentGpuArray<Tstruct> : IDisposable
where Tstruct : struct
{
BlockAllocator m_SlotAllocator;
ComputeBuffer m_GpuBuffer;
NativeArray<Tstruct> m_CpuList;
BitArray m_Updates;
bool m_gpuBufferDirty = true;
int m_ElementCount = 0;
public int elementCount { get { return m_ElementCount; } }
public PersistentGpuArray(int initialSize)
{
m_SlotAllocator.Initialize(initialSize);
m_GpuBuffer = new ComputeBuffer(initialSize, Marshal.SizeOf<Tstruct>());
m_CpuList = new NativeArray<Tstruct>(initialSize, Allocator.Persistent);
m_Updates = new BitArray(initialSize);
m_ElementCount = 0;
}
public void Dispose()
{
m_ElementCount = 0;
m_SlotAllocator.Dispose();
m_GpuBuffer.Dispose();
m_CpuList.Dispose();
}
public BlockAllocator.Allocation Add(Tstruct element)
{
m_ElementCount++;
var slotAllocation = m_SlotAllocator.Allocate(1);
if (!slotAllocation.valid)
{
Grow();
slotAllocation = m_SlotAllocator.Allocate(1);
Assert.IsTrue(slotAllocation.valid);
}
m_CpuList[slotAllocation.block.offset] = element;
m_Updates[slotAllocation.block.offset] = true;
m_gpuBufferDirty = true;
return slotAllocation;
}
public BlockAllocator.Allocation[] Add(int elementCount)
{
m_ElementCount+= elementCount;
var slotAllocation = m_SlotAllocator.Allocate(elementCount);
if (!slotAllocation.valid)
{
Grow();
slotAllocation = m_SlotAllocator.Allocate(elementCount);
Assert.IsTrue(slotAllocation.valid);
}
return m_SlotAllocator.SplitAllocation(slotAllocation, elementCount);
}
public void Remove(BlockAllocator.Allocation allocation)
{
m_ElementCount--;
m_SlotAllocator.FreeAllocation(allocation);
}
public void Clear()
{
m_ElementCount = 0;
var currentCapacity = m_SlotAllocator.capacity;
m_SlotAllocator.Dispose();
m_SlotAllocator = new BlockAllocator();
m_SlotAllocator.Initialize(currentCapacity);
m_Updates = new BitArray(currentCapacity);
m_gpuBufferDirty = false;
}
public void Set(BlockAllocator.Allocation allocation, Tstruct element)
{
m_CpuList[allocation.block.offset] = element;
m_Updates[allocation.block.offset] = true;
m_gpuBufferDirty = true;
}
public Tstruct Get(BlockAllocator.Allocation allocation)
{
return m_CpuList[allocation.block.offset];
}
public void ModifyForEach(Func<Tstruct, Tstruct> lambda)
{
for (int i = 0; i < m_CpuList.Length; ++i)
{
m_CpuList[i] = lambda(m_CpuList[i]);
m_Updates[i] = true;
}
m_gpuBufferDirty = true;
}
// Note: this should ideally be used with only one command buffer. If used with more than one cmd buffers, the order of their execution is important.
public ComputeBuffer GetGpuBuffer(CommandBuffer cmd)
{
if (m_gpuBufferDirty)
{
int copyStartIndex = -1;
for (int i = 0; i < m_Updates.Length; ++i)
{
if (m_Updates[i])
{
if (copyStartIndex == -1)
copyStartIndex = i;
m_Updates[i] = false;
}
else if (copyStartIndex != -1)
{
int copyEndIndex = i;
cmd.SetBufferData(m_GpuBuffer, m_CpuList, copyStartIndex, copyStartIndex, copyEndIndex - copyStartIndex);
copyStartIndex = -1;
}
}
if (copyStartIndex != -1)
{
int copyEndIndex = m_Updates.Length;
cmd.SetBufferData(m_GpuBuffer, m_CpuList, copyStartIndex, copyStartIndex, copyEndIndex - copyStartIndex);
}
m_gpuBufferDirty = false;
}
return m_GpuBuffer;
}
private void Grow()
{
var oldCapacity = m_SlotAllocator.capacity;
m_SlotAllocator.Grow(m_SlotAllocator.capacity + 1);
m_GpuBuffer.Dispose();
m_GpuBuffer = new ComputeBuffer(m_SlotAllocator.capacity, Marshal.SizeOf<Tstruct>());
var oldList = m_CpuList;
m_CpuList = new NativeArray<Tstruct>(m_SlotAllocator.capacity, Allocator.Persistent);
NativeArray<Tstruct>.Copy(oldList, m_CpuList, oldCapacity);
oldList.Dispose();
var oldUpdates = m_Updates;
m_Updates = new BitArray(m_SlotAllocator.capacity);
for (int i = 0; i < oldCapacity; ++i)
m_Updates[i] = oldUpdates[i];
}
}
}