// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Buffers; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using EpicGames.Core; namespace EpicGames.Horde.Compute.Buffers { /// /// In-process buffer used to store compute messages /// public sealed class PooledBuffer : ComputeBuffer { /// /// Constructor /// /// Total capacity of the buffer public PooledBuffer(int capacity) : this(2, capacity / 2) { } /// /// Constructor /// /// Number of chunks in the buffer /// Length of each chunk /// Number of readers for this buffer public PooledBuffer(int numChunks, int chunkLength, int numReaders = 1) : base(PooledBufferDetail.Create(numChunks, chunkLength, numReaders)) { } private PooledBuffer(ComputeBufferDetail resources) : base(resources) { } /// public override PooledBuffer AddRef() { _detail.AddRef(); return new PooledBuffer(_detail); } } /// /// Core implementation of /// class PooledBufferDetail : ComputeBufferDetail { GCHandle _headerHandle; IMemoryOwner[] _chunkOwners; readonly AsyncEvent _writerEvent = new AsyncEvent(); readonly AsyncEvent _readerEvent = new AsyncEvent(); internal PooledBufferDetail(HeaderPtr headerPtr, GCHandle headerHandle, Memory[] chunks, IMemoryOwner[] chunkOwners) : base(headerPtr, chunks) { _headerHandle = headerHandle; _chunkOwners = chunkOwners; } public static unsafe PooledBufferDetail Create(int numChunks, int chunkLength, int numReaders) { byte[] header = new byte[HeaderSize]; GCHandle headerHandle = GCHandle.Alloc(header, GCHandleType.Pinned); HeaderPtr headerPtr = new HeaderPtr((ulong*)headerHandle.AddrOfPinnedObject().ToPointer(), numReaders, numChunks, chunkLength); Memory[] chunks = new Memory[numChunks]; IMemoryOwner[] chunkOwners = new IMemoryOwner[numChunks]; for (int idx = 0; idx < numChunks; idx++) { chunkOwners[idx] = MemoryPool.Shared.Rent(chunkLength); chunks[idx] = chunkOwners[idx].Memory.Slice(0, chunkLength); } return new PooledBufferDetail(headerPtr, headerHandle, chunks, chunkOwners); } /// protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { for (int idx = 0; idx < _chunkOwners.Length; idx++) { _chunkOwners[idx].Dispose(); } _chunkOwners = Array.Empty>(); _headerHandle.Free(); _headerHandle = default; } } /// public override void SetReadEvent(int readerIdx) => _readerEvent.Set(); /// public override Task WaitToReadAsync(int readerIdx, CancellationToken cancellationToken) => _readerEvent.Task.WaitAsync(cancellationToken); /// public override void SetWriteEvent() => _writerEvent.Set(); /// public override Task WaitToWriteAsync(CancellationToken cancellationToken) => _writerEvent.Task.WaitAsync(cancellationToken); } }