Skip to content

Commit bc0468d

Browse files
committed
Merge branch 'fixed-pooling'
2 parents 6f3f388 + a3addb2 commit bc0468d

File tree

6 files changed

+146
-91
lines changed

6 files changed

+146
-91
lines changed

LibTessDotNet/Sources/Mesh.cs

Lines changed: 95 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
*/
3333

3434
using System;
35+
using System.Collections.Generic;
3536
using System.Diagnostics;
3637

3738
#if DOUBLE
@@ -46,12 +47,14 @@ internal class Mesh : MeshUtils.Pooled<Mesh>
4647
internal MeshUtils.Face _fHead;
4748
internal MeshUtils.Edge _eHead, _eHeadSym;
4849

49-
public Mesh()
50+
private readonly List<MeshUtils.EdgePair> _allEdgePairs = new List<MeshUtils.EdgePair>(20000);
51+
52+
public void Init()
5053
{
5154
var v = _vHead = MeshUtils.Vertex.Create();
5255
var f = _fHead = MeshUtils.Face.Create();
5356

54-
var pair = MeshUtils.EdgePair.Create();
57+
var pair = CreateEdgePair();
5558
var e = _eHead = pair._e;
5659
var eSym = _eHeadSym = pair._eSym;
5760

@@ -85,28 +88,58 @@ public Mesh()
8588

8689
public override void Reset()
8790
{
88-
_vHead = null;
89-
_fHead = null;
90-
_eHead = _eHeadSym = null;
91-
}
92-
93-
public override void OnFree()
94-
{
95-
for (MeshUtils.Face f = _fHead._next, fNext = _fHead; f != _fHead; f = fNext)
91+
MeshUtils.Face f = _fHead._next;
92+
while (true)
9693
{
97-
fNext = f._next;
94+
MeshUtils.Face fNext = f._next;
9895
f.Free();
96+
if (f == _fHead) break;
97+
f = fNext;
9998
}
100-
for (MeshUtils.Vertex v = _vHead._next, vNext = _vHead; v != _vHead; v = vNext)
99+
100+
MeshUtils.Vertex v = _vHead._next;
101+
while (true)
101102
{
102-
vNext = v._next;
103+
MeshUtils.Vertex vNext = v._next;
103104
v.Free();
105+
if (v == _vHead) break;
106+
v = vNext;
104107
}
105-
for (MeshUtils.Edge e = _eHead._next, eNext = _eHead; e != _eHead; e = eNext)
108+
109+
for (int i = 0; i < _allEdgePairs.Count; i++)
106110
{
107-
eNext = e._next;
108-
e.Free();
111+
MeshUtils.EdgePair pair = _allEdgePairs[i];
112+
113+
MeshUtils.Edge e = pair._e;
114+
if (!e.IsReturnedToPool()) // (can be Free in KillEdge)
115+
{
116+
e.Free();
117+
}
118+
119+
MeshUtils.Edge eSym = pair._eSym;
120+
if (!eSym.IsReturnedToPool()) // (can be Free in KillEdge)
121+
{
122+
eSym.Free();
123+
}
124+
125+
pair.Free();
109126
}
127+
_allEdgePairs.Clear();
128+
129+
_vHead = null;
130+
_fHead = null;
131+
_eHead = _eHeadSym = null;
132+
}
133+
134+
private MeshUtils.EdgePair CreateEdgePair()
135+
{
136+
var pair = MeshUtils.EdgePair.Create();
137+
pair._e = MeshUtils.Edge.Create();
138+
pair._e._pair = pair;
139+
pair._eSym = MeshUtils.Edge.Create();
140+
pair._eSym._pair = pair;
141+
_allEdgePairs.Add(pair);
142+
return pair;
110143
}
111144

112145
/// <summary>
@@ -115,7 +148,7 @@ public override void OnFree()
115148
/// </summary>
116149
public MeshUtils.Edge MakeEdge()
117150
{
118-
var e = MeshUtils.MakeEdge(_eHead);
151+
var e = MakeEdge(_eHead);
119152

120153
MeshUtils.MakeVertex(e, _vHead);
121154
MeshUtils.MakeVertex(e._Sym, _vHead);
@@ -124,6 +157,49 @@ public MeshUtils.Edge MakeEdge()
124157
return e;
125158
}
126159

160+
/// <summary>
161+
/// MakeEdge creates a new pair of half-edges which form their own loop.
162+
/// No vertex or face structures are allocated, but these must be assigned
163+
/// before the current edge operation is completed.
164+
/// </summary>
165+
private MeshUtils.Edge MakeEdge(MeshUtils.Edge eNext)
166+
{
167+
Debug.Assert(eNext != null);
168+
169+
var pair = CreateEdgePair();
170+
var e = pair._e;
171+
var eSym = pair._eSym;
172+
173+
// Make sure eNext points to the first edge of the edge pair
174+
MeshUtils.Edge.EnsureFirst(ref eNext);
175+
176+
// Insert in circular doubly-linked list before eNext.
177+
// Note that the prev pointer is stored in Sym->next.
178+
var ePrev = eNext._Sym._next;
179+
eSym._next = ePrev;
180+
ePrev._Sym._next = e;
181+
e._next = eNext;
182+
eNext._Sym._next = eSym;
183+
184+
e._Sym = eSym;
185+
e._Onext = e;
186+
e._Lnext = eSym;
187+
e._Org = null;
188+
e._Lface = null;
189+
e._winding = 0;
190+
e._activeRegion = null;
191+
192+
eSym._Sym = e;
193+
eSym._Onext = eSym;
194+
eSym._Lnext = e;
195+
eSym._Org = null;
196+
eSym._Lface = null;
197+
eSym._winding = 0;
198+
eSym._activeRegion = null;
199+
200+
return e;
201+
}
202+
127203
/// <summary>
128204
/// Splice is the basic operation for changing the
129205
/// mesh connectivity and topology. It changes the mesh so that
@@ -256,7 +332,7 @@ public void Delete(MeshUtils.Edge eDel)
256332
/// </summary>
257333
public MeshUtils.Edge AddEdgeVertex(MeshUtils.Edge eOrg)
258334
{
259-
var eNew = MeshUtils.MakeEdge(eOrg);
335+
var eNew = MakeEdge(eOrg);
260336
var eNewSym = eNew._Sym;
261337

262338
// Connect the new edge appropriately
@@ -306,7 +382,7 @@ public MeshUtils.Edge SplitEdge(MeshUtils.Edge eOrg)
306382
/// </summary>
307383
public MeshUtils.Edge Connect(MeshUtils.Edge eOrg, MeshUtils.Edge eDst)
308384
{
309-
var eNew = MeshUtils.MakeEdge(eOrg);
385+
var eNew = MakeEdge(eOrg);
310386
var eNewSym = eNew._Sym;
311387

312388
bool joiningLoops = false;

LibTessDotNet/Sources/MeshUtils.cs

Lines changed: 32 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
** LibTessDotNet: Remi Gillig, https://github.com/speps/LibTessDotNet
3232
*/
3333

34+
#if DEBUG
35+
#define DEBUG_CHECK_BALANCE
36+
#endif
37+
3438
using System;
3539
using System.Collections.Generic;
3640
using System.Diagnostics;
@@ -110,34 +114,41 @@ public override string ToString()
110114
}
111115
}
112116

117+
#if DEBUG
118+
public static class MeshUtils
119+
#else
113120
internal static class MeshUtils
121+
#endif
114122
{
115123
public const int Undef = ~0;
116124

117125
public abstract class Pooled<T> where T : Pooled<T>, new()
118126
{
119-
private static Stack<T> _stack;
127+
private static Stack<T> _stack = new Stack<T>();
128+
129+
#if DEBUG_CHECK_BALANCE
130+
public static int InvokedCreateCount = 0;
131+
public static int InvokedFreeCount = 0;
132+
#endif
120133

121134
public abstract void Reset();
122-
public virtual void OnFree() {}
123135

124136
public static T Create()
125137
{
126-
if (_stack != null && _stack.Count > 0)
127-
{
128-
return _stack.Pop();
129-
}
138+
#if DEBUG_CHECK_BALANCE
139+
InvokedCreateCount++;
140+
#endif
141+
if (_stack.Count > 0) return _stack.Pop();
130142
return new T();
131143
}
132144

133145
public void Free()
134146
{
135-
OnFree();
147+
#if DEBUG_CHECK_BALANCE
148+
InvokedFreeCount++;
149+
#endif
150+
136151
Reset();
137-
if (_stack == null)
138-
{
139-
_stack = new Stack<T>();
140-
}
141152
_stack.Push((T)this);
142153
}
143154
}
@@ -200,21 +211,11 @@ public override void Reset()
200211
}
201212
}
202213

203-
public struct EdgePair
214+
public class EdgePair : Pooled<EdgePair>
204215
{
205216
internal Edge _e, _eSym;
206217

207-
public static EdgePair Create()
208-
{
209-
var pair = new MeshUtils.EdgePair();
210-
pair._e = MeshUtils.Edge.Create();
211-
pair._e._pair = pair;
212-
pair._eSym = MeshUtils.Edge.Create();
213-
pair._eSym._pair = pair;
214-
return pair;
215-
}
216-
217-
public void Reset()
218+
public override void Reset()
218219
{
219220
_e = _eSym = null;
220221
}
@@ -249,56 +250,21 @@ internal static void EnsureFirst(ref Edge e)
249250

250251
public override void Reset()
251252
{
252-
_pair.Reset();
253+
_pair = null;
253254
_next = _Sym = _Onext = _Lnext = null;
254255
_Org = null;
255256
_Lface = null;
256257
_activeRegion = null;
257258
_winding = 0;
258259
}
259-
}
260260

261-
/// <summary>
262-
/// MakeEdge creates a new pair of half-edges which form their own loop.
263-
/// No vertex or face structures are allocated, but these must be assigned
264-
/// before the current edge operation is completed.
265-
/// </summary>
266-
public static Edge MakeEdge(Edge eNext)
267-
{
268-
Debug.Assert(eNext != null);
269-
270-
var pair = EdgePair.Create();
271-
var e = pair._e;
272-
var eSym = pair._eSym;
273-
274-
// Make sure eNext points to the first edge of the edge pair
275-
Edge.EnsureFirst(ref eNext);
276-
277-
// Insert in circular doubly-linked list before eNext.
278-
// Note that the prev pointer is stored in Sym->next.
279-
var ePrev = eNext._Sym._next;
280-
eSym._next = ePrev;
281-
ePrev._Sym._next = e;
282-
e._next = eNext;
283-
eNext._Sym._next = eSym;
284-
285-
e._Sym = eSym;
286-
e._Onext = e;
287-
e._Lnext = eSym;
288-
e._Org = null;
289-
e._Lface = null;
290-
e._winding = 0;
291-
e._activeRegion = null;
292-
293-
eSym._Sym = e;
294-
eSym._Onext = eSym;
295-
eSym._Lnext = e;
296-
eSym._Org = null;
297-
eSym._Lface = null;
298-
eSym._winding = 0;
299-
eSym._activeRegion = null;
300-
301-
return e;
261+
/// <summary>
262+
/// Is returned to pool, for avoid double free
263+
/// </summary>
264+
public bool IsReturnedToPool()
265+
{
266+
return (_pair == null);
267+
}
302268
}
303269

304270
/// <summary>

LibTessDotNet/Sources/Sweep.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ private bool CheckForIntersect(ActiveRegion regUp)
559559

560560
// At this point the edges intersect, at least marginally
561561

562-
var isect = MeshUtils.Vertex.Create();
562+
var isect = new MeshUtils.Vertex(); // (without pool because of stack scope)
563563
Geom.EdgeIntersect(dstUp, orgUp, dstLo, orgLo, isect);
564564
// The following properties are guaranteed:
565565
Debug.Assert(Math.Min(orgUp._t, dstUp._t) <= isect._t);

LibTessDotNet/Sources/Tess.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,10 @@ public partial class Tess
113113
/// <summary>
114114
/// If true, will remove empty (zero area) polygons.
115115
/// </summary>
116-
public bool NoEmptyPolygons = false;
117-
118-
/// <summary>
116+
public bool NoEmptyPolygons = false; /// <summary>
119117
/// If true, will use pooling to reduce GC (compare performance with/without, can vary wildly).
120118
/// </summary>
121-
public bool UsePooling = false;
119+
public bool UsePooling = true;
122120

123121
public ContourVertex[] Vertices { get { return _vertices; } }
124122
public int VertexCount { get { return _vertexCount; } }
@@ -647,7 +645,8 @@ public void AddContour(ContourVertex[] vertices, ContourOrientation forceOrienta
647645
{
648646
if (_mesh == null)
649647
{
650-
_mesh = new Mesh();
648+
_mesh = Mesh.Create();
649+
_mesh.Init();
651650
}
652651

653652
bool reverse = false;

TessBed/MainForm.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,9 @@ private void RefreshAsset(PolygonSet polygons)
266266

267267
statusMain.Text = string.Format("{0:F3} ms - {1} polygons (of {2} vertices) {3}", _sw.Elapsed.TotalMilliseconds, _tess.ElementCount, _polySize, _polySize == 3 ? "... triangles" : "");
268268

269+
/*string debugBalance = DebugPoolBalanceChecker.GetDebugAboutPoolBalanceAll();
270+
if (!debugBalance.Equals("")) MessageBox.Show("debugBalance: " + debugBalance);*/
271+
269272
_canvas.Input = input;
270273
_canvas.Output = output;
271274
_canvas.Invalidate();

TessBed/UnitTests.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,17 @@ public void Tessellate_WithAsset_ReturnsExpectedTriangulation(TestCaseData data)
200200
}
201201

202202
Assert.AreEqual(testData.Indices, indices.ToArray());
203+
AssertPooling();
204+
}
205+
206+
private void AssertPooling()
207+
{
208+
#if DEBUG
209+
Assert.AreEqual(MeshUtils.Vertex.InvokedCreateCount, MeshUtils.Vertex.InvokedFreeCount);
210+
Assert.AreEqual(MeshUtils.Face.InvokedCreateCount, MeshUtils.Face.InvokedFreeCount);
211+
Assert.AreEqual(MeshUtils.EdgePair.InvokedCreateCount, MeshUtils.EdgePair.InvokedFreeCount);
212+
Assert.AreEqual(MeshUtils.Edge.InvokedCreateCount, MeshUtils.Edge.InvokedFreeCount);
213+
#endif
203214
}
204215

205216
public TestData ParseTestData(WindingRule winding, int elementSize, Stream resourceStream)

0 commit comments

Comments
 (0)