using System; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Rendering; using Unity.Assertions; using UnityEngine; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// namespace Rukhanka.Toolbox { // This class purpose to wrap compute buffer which updated every frame public class FrameFencedGPUBufferPool: IDisposable where T: unmanaged { BufferPool bufferPool; NativeQueue busyBuffers; int currentFrameBufferIndex = -1; int elementCount; readonly GraphicsBuffer.Target bufferTargetFlags; readonly GraphicsBuffer.UsageFlags bufferUsageFlags; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public FrameFencedGPUBufferPool(int count, GraphicsBuffer.Target target, GraphicsBuffer.UsageFlags usageFlags) { elementCount = count; bufferTargetFlags = target; bufferUsageFlags = usageFlags; bufferPool = new BufferPool(count, UnsafeUtility.SizeOf(), target, usageFlags); busyBuffers = new (Allocator.Persistent); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Dispose() { busyBuffers.Dispose(); bufferPool?.Dispose(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void BeginFrame() { Assert.IsTrue(currentFrameBufferIndex == -1); RecoverBuffers(); currentFrameBufferIndex = bufferPool.GetBufferId(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void EndFrame() { Assert.IsFalse(currentFrameBufferIndex == -1); busyBuffers.Enqueue(currentFrameBufferIndex); currentFrameBufferIndex = -1; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void RecoverBuffers() { var totalLiveFrames = SparseUploader.NumFramesInFlight + 1; for (var i = totalLiveFrames; i < busyBuffers.Count; ++i) { var bufID = busyBuffers.Dequeue(); bufferPool.PutBufferId(bufID); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public bool Grow(int requiredElementsCount) { var chunkSizeInBytes = 0x1000 * 16; var elementSizeInBytes = UnsafeUtility.SizeOf(); var chunkSizeInElements = chunkSizeInBytes / elementSizeInBytes; var newSizeInElements = requiredElementsCount + chunkSizeInElements; if (elementCount < newSizeInElements) { var newSizeInBytes = newSizeInElements * elementSizeInBytes; if (newSizeInBytes > SystemInfo.maxGraphicsBufferSize) { var formattedRequiredBytes = CommonTools.FormatMemory(newSizeInBytes); var formattedMaxComputeBufferBytes = CommonTools.FormatMemory(SystemInfo.maxGraphicsBufferSize); throw new InvalidOperationException($"Requested buffer size ({requiredElementsCount} elements, {formattedRequiredBytes}) exceeded maximum compute buffer capacity of '{formattedMaxComputeBufferBytes}'"); } bufferPool?.Dispose(); busyBuffers.Clear(); bufferPool = new BufferPool(newSizeInElements, UnsafeUtility.SizeOf(), bufferTargetFlags, bufferUsageFlags); elementCount = newSizeInElements; currentFrameBufferIndex = -1; return true; } return false; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public NativeArray LockBufferForWrite(int startIndex, int count) { var b = bufferPool.GetBufferFromId(currentFrameBufferIndex); var rv = b.LockBufferForWrite(startIndex, count); return rv; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void UnlockBufferAfterWrite(int count) { var b = bufferPool.GetBufferFromId(currentFrameBufferIndex); b.UnlockBufferAfterWrite(count); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public GraphicsBuffer GetBuffer() { if (currentFrameBufferIndex < 0) return null; var b = bufferPool.GetBufferFromId(currentFrameBufferIndex); return b; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static implicit operator GraphicsBuffer(FrameFencedGPUBufferPool b) => b.GetBuffer(); } }