using System; using System.Runtime.InteropServices; namespace MonoGame.Extended.Particles { public class ParticleBuffer : IDisposable { private readonly ParticleIterator _iterator; private readonly IntPtr _nativePointer; // points to the first memory pos after the buffer protected readonly unsafe Particle* BufferEnd; private bool _disposed; // points to the particle after the last active particle. protected unsafe Particle* Tail; public unsafe ParticleBuffer(int size) { Size = size; _nativePointer = Marshal.AllocHGlobal(SizeInBytes); BufferEnd = (Particle*) (_nativePointer + SizeInBytes); Head = (Particle*) _nativePointer; Tail = (Particle*) _nativePointer; _iterator = new ParticleIterator(this); GC.AddMemoryPressure(SizeInBytes); } public int Size { get; } public ParticleIterator Iterator => _iterator.Reset(); // pointer to the first particle public unsafe Particle* Head { get; private set; } // Number of available particle spots in the buffer public int Available => Size - Count; // current number of particles public int Count { get; private set; } // total size of the buffer (add one extra spot in memory for margin between head and tail so the iterator can see that it's at the end) public int SizeInBytes => Particle.SizeInBytes*(Size + 1); // total size of active particles public int ActiveSizeInBytes => Particle.SizeInBytes*Count; public void Dispose() { if (!_disposed) { Marshal.FreeHGlobal(_nativePointer); _disposed = true; GC.RemoveMemoryPressure(SizeInBytes); } GC.SuppressFinalize(this); } /// /// Release the given number of particles or the most available. /// Returns a started iterator to iterate over the new particles. /// public unsafe ParticleIterator Release(int releaseQuantity) { var numToRelease = Math.Min(releaseQuantity, Available); var prevCount = Count; Count += numToRelease; Tail += numToRelease; if (Tail >= BufferEnd) Tail -= Size + 1; return Iterator.Reset(prevCount); } public unsafe void Reclaim(int number) { Count -= number; Head += number; if (Head >= BufferEnd) Head -= Size + 1; } //public void CopyTo(IntPtr destination) //{ // memcpy(destination, _nativePointer, ActiveSizeInBytes); //} //public void CopyToReverse(IntPtr destination) //{ // var offset = 0; // for (var i = ActiveSizeInBytes - Particle.SizeInBytes; i >= 0; i -= Particle.SizeInBytes) // { // memcpy(IntPtr.Add(destination, offset), IntPtr.Add(_nativePointer, i), Particle.SizeInBytes); // offset += Particle.SizeInBytes; // } //} //[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] //public static extern void memcpy(IntPtr dest, IntPtr src, int count); ~ParticleBuffer() { Dispose(); } public class ParticleIterator { private readonly ParticleBuffer _buffer; private unsafe Particle* _current; public int Total; public ParticleIterator(ParticleBuffer buffer) { _buffer = buffer; } public unsafe bool HasNext => _current != _buffer.Tail; public unsafe ParticleIterator Reset() { _current = _buffer.Head; Total = _buffer.Count; return this; } internal unsafe ParticleIterator Reset(int offset) { Total = _buffer.Count; _current = _buffer.Head + offset; if (_current >= _buffer.BufferEnd) _current -= _buffer.Size + 1; return this; } public unsafe Particle* Next() { var p = _current; _current++; if (_current == _buffer.BufferEnd) _current = (Particle*) _buffer._nativePointer; return p; } } } }