1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
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;
}
}
}
}
|