Files

153 lines
4.6 KiB
C#

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);
}
/// <summary>
/// Release the given number of particles or the most available.
/// Returns a started iterator to iterate over the new particles.
/// </summary>
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;
}
}
}
}