Clipper memory optimization: Own memory manager for OutPt objects.

Allocate OutPt by chunks of 32, reuse the released OutPt objects.
This commit is contained in:
bubnikv 2017-03-03 23:06:51 +01:00
parent f24427cd76
commit a9a20003c0
2 changed files with 100 additions and 69 deletions

View File

@ -65,14 +65,6 @@ static int const Skip = -2; //edge that would otherwise close a path
#define TOLERANCE (1.0e-20) #define TOLERANCE (1.0e-20)
#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) #define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE))
// Point of an output polygon.
struct OutPt {
int Idx;
IntPoint Pt;
OutPt *Next;
OutPt *Prev;
};
// Output polygon. // Output polygon.
struct OutRec { struct OutRec {
int Idx; int Idx;
@ -571,19 +563,6 @@ void ReversePolyPtLinks(OutPt *pp)
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void DisposeOutPts(OutPt*& pp)
{
if (pp == 0) return;
pp->Prev->Next = 0;
while( pp )
{
OutPt *tmpPp = pp;
pp = pp->Next;
delete tmpPp;
}
}
//------------------------------------------------------------------------------
inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt) inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt)
{ {
std::memset(e, 0, sizeof(TEdge)); std::memset(e, 0, sizeof(TEdge));
@ -1219,11 +1198,14 @@ IntRect ClipperBase::GetBounds()
// TClipper methods ... // TClipper methods ...
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
Clipper::Clipper(int initOptions) : ClipperBase() //constructor Clipper::Clipper(int initOptions) :
ClipperBase(),
m_OutPtsFree(nullptr),
m_OutPtsChunkSize(32),
m_OutPtsChunkLast(32),
m_ActiveEdges(nullptr),
m_SortedEdges(nullptr)
{ {
m_ActiveEdges = 0;
m_SortedEdges = 0;
m_UseFullRange = false;
m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); m_ReverseOutput = ((initOptions & ioReverseSolution) != 0);
m_StrictSimple = ((initOptions & ioStrictlySimple) != 0); m_StrictSimple = ((initOptions & ioStrictlySimple) != 0);
m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0);
@ -1351,12 +1333,32 @@ bool Clipper::ExecuteInternal()
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void Clipper::DisposeAllOutRecs(){ OutPt* Clipper::AllocateOutPt()
for (OutRec *outRec : m_PolyOuts) { {
if (outRec->Pts) OutPt *pt;
DisposeOutPts(outRec->Pts); if (m_OutPtsFree) {
delete outRec; // Recycle some of the already released points.
pt = m_OutPtsFree;
m_OutPtsFree = pt->Next;
} else if (m_OutPtsChunkLast < m_OutPtsChunkSize) {
// Get a point from the last chunk.
pt = m_OutPts.back() + (m_OutPtsChunkLast ++);
} else {
// The last chunk is full. Allocate a new one.
m_OutPts.push_back(new OutPt[m_OutPtsChunkSize]);
m_OutPtsChunkLast = 1;
pt = m_OutPts.back();
} }
return pt;
}
void Clipper::DisposeAllOutRecs()
{
for (OutPt *pts : m_OutPts)
delete[] pts;
m_OutPts.clear();
m_OutPtsFree = nullptr;
m_OutPtsChunkLast = m_OutPtsChunkSize;
m_PolyOuts.clear(); m_PolyOuts.clear();
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -2156,7 +2158,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
{ {
OutRec *outRec = CreateOutRec(); OutRec *outRec = CreateOutRec();
outRec->IsOpen = (e->WindDelta == 0); outRec->IsOpen = (e->WindDelta == 0);
OutPt* newOp = new OutPt; OutPt* newOp = this->AllocateOutPt();
outRec->Pts = newOp; outRec->Pts = newOp;
newOp->Idx = outRec->Idx; newOp->Idx = outRec->Idx;
newOp->Pt = pt; newOp->Pt = pt;
@ -2176,7 +2178,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
if (ToFront && (pt == op->Pt)) return op; if (ToFront && (pt == op->Pt)) return op;
else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev;
OutPt* newOp = new OutPt; OutPt* newOp = this->AllocateOutPt();
newOp->Idx = outRec->Idx; newOp->Idx = outRec->Idx;
newOp->Pt = pt; newOp->Pt = pt;
newOp->Next = op; newOp->Next = op;
@ -2843,14 +2845,14 @@ void Clipper::FixupOutPolyline(OutRec &outrec)
OutPt *tmpPP = pp->Prev; OutPt *tmpPP = pp->Prev;
tmpPP->Next = pp->Next; tmpPP->Next = pp->Next;
pp->Next->Prev = tmpPP; pp->Next->Prev = tmpPP;
delete pp; this->DisposeOutPt(pp);
pp = tmpPP; pp = tmpPP;
} }
} }
if (pp == pp->Prev) if (pp == pp->Prev)
{ {
DisposeOutPts(pp); this->DisposeOutPts(pp);
outrec.Pts = 0; outrec.Pts = 0;
return; return;
} }
@ -2871,7 +2873,7 @@ void Clipper::FixupOutPolygon(OutRec &outrec)
if (pp->Prev == pp || pp->Prev == pp->Next) if (pp->Prev == pp || pp->Prev == pp->Next)
{ {
// Empty loop or a stick. Release the polygon. // Empty loop or a stick. Release the polygon.
DisposeOutPts(pp); this->DisposeOutPts(pp);
outrec.Pts = nullptr; outrec.Pts = nullptr;
return; return;
} }
@ -2886,7 +2888,7 @@ void Clipper::FixupOutPolygon(OutRec &outrec)
pp->Prev->Next = pp->Next; pp->Prev->Next = pp->Next;
pp->Next->Prev = pp->Prev; pp->Next->Prev = pp->Prev;
pp = pp->Prev; pp = pp->Prev;
delete tmp; this->DisposeOutPt(tmp);
} }
else if (pp == lastOK) break; else if (pp == lastOK) break;
else else
@ -3063,9 +3065,9 @@ void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge)
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
OutPt* DupOutPt(OutPt* outPt, bool InsertAfter) OutPt* Clipper::DupOutPt(OutPt* outPt, bool InsertAfter)
{ {
OutPt* result = new OutPt; OutPt* result = this->AllocateOutPt();
result->Pt = outPt->Pt; result->Pt = outPt->Pt;
result->Idx = outPt->Idx; result->Idx = outPt->Idx;
if (InsertAfter) if (InsertAfter)
@ -3086,7 +3088,7 @@ OutPt* DupOutPt(OutPt* outPt, bool InsertAfter)
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, bool Clipper::JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b,
const IntPoint &Pt, bool DiscardLeft) const IntPoint &Pt, bool DiscardLeft)
{ {
Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight);
@ -3104,12 +3106,12 @@ bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b,
op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y)
op1 = op1->Next; op1 = op1->Next;
if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next;
op1b = DupOutPt(op1, !DiscardLeft); op1b = this->DupOutPt(op1, !DiscardLeft);
if (op1b->Pt != Pt) if (op1b->Pt != Pt)
{ {
op1 = op1b; op1 = op1b;
op1->Pt = Pt; op1->Pt = Pt;
op1b = DupOutPt(op1, !DiscardLeft); op1b = this->DupOutPt(op1, !DiscardLeft);
} }
} }
else else
@ -3118,12 +3120,12 @@ bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b,
op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y)
op1 = op1->Next; op1 = op1->Next;
if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next;
op1b = DupOutPt(op1, DiscardLeft); op1b = this->DupOutPt(op1, DiscardLeft);
if (op1b->Pt != Pt) if (op1b->Pt != Pt)
{ {
op1 = op1b; op1 = op1b;
op1->Pt = Pt; op1->Pt = Pt;
op1b = DupOutPt(op1, DiscardLeft); op1b = this->DupOutPt(op1, DiscardLeft);
} }
} }
@ -3133,12 +3135,12 @@ bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b,
op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y)
op2 = op2->Next; op2 = op2->Next;
if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next;
op2b = DupOutPt(op2, !DiscardLeft); op2b = this->DupOutPt(op2, !DiscardLeft);
if (op2b->Pt != Pt) if (op2b->Pt != Pt)
{ {
op2 = op2b; op2 = op2b;
op2->Pt = Pt; op2->Pt = Pt;
op2b = DupOutPt(op2, !DiscardLeft); op2b = this->DupOutPt(op2, !DiscardLeft);
}; };
} else } else
{ {
@ -3146,12 +3148,12 @@ bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b,
op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y)
op2 = op2->Next; op2 = op2->Next;
if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next;
op2b = DupOutPt(op2, DiscardLeft); op2b = this->DupOutPt(op2, DiscardLeft);
if (op2b->Pt != Pt) if (op2b->Pt != Pt)
{ {
op2 = op2b; op2 = op2b;
op2->Pt = Pt; op2->Pt = Pt;
op2b = DupOutPt(op2, DiscardLeft); op2b = this->DupOutPt(op2, DiscardLeft);
}; };
}; };
@ -3203,8 +3205,8 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
if (reverse1 == reverse2) return false; if (reverse1 == reverse2) return false;
if (reverse1) if (reverse1)
{ {
op1b = DupOutPt(op1, false); op1b = this->DupOutPt(op1, false);
op2b = DupOutPt(op2, true); op2b = this->DupOutPt(op2, true);
op1->Prev = op2; op1->Prev = op2;
op2->Next = op1; op2->Next = op1;
op1b->Next = op2b; op1b->Next = op2b;
@ -3214,8 +3216,8 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
return true; return true;
} else } else
{ {
op1b = DupOutPt(op1, true); op1b = this->DupOutPt(op1, true);
op2b = DupOutPt(op2, false); op2b = this->DupOutPt(op2, false);
op1->Next = op2; op1->Next = op2;
op2->Prev = op1; op2->Prev = op1;
op1b->Prev = op2b; op1b->Prev = op2b;
@ -3307,8 +3309,8 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
if (Reverse1) if (Reverse1)
{ {
op1b = DupOutPt(op1, false); op1b = this->DupOutPt(op1, false);
op2b = DupOutPt(op2, true); op2b = this->DupOutPt(op2, true);
op1->Prev = op2; op1->Prev = op2;
op2->Next = op1; op2->Next = op1;
op1b->Next = op2b; op1b->Next = op2b;
@ -3318,8 +3320,8 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
return true; return true;
} else } else
{ {
op1b = DupOutPt(op1, true); op1b = this->DupOutPt(op1, true);
op2b = DupOutPt(op2, false); op2b = this->DupOutPt(op2, false);
op1->Next = op2; op1->Next = op2;
op2->Prev = op1; op2->Prev = op1;
op1b->Prev = op2b; op1b->Prev = op2b;

View File

@ -261,7 +261,20 @@ enum EdgeSide { esLeft = 1, esRight = 2};
TEdge *RightBound; TEdge *RightBound;
}; };
struct OutPt; // Point of an output polygon.
// 36B on 64bit system with not use_int32 and not use_xyz.
struct OutPt {
// 4B
int Idx;
// 8B (if use_int32 and not use_xyz) or 16B (if not use_int32 and not use_xyz)
// or 12B (if use_int32 and use_xyz) or 24B (if not use_int32 and use_xyz)
IntPoint Pt;
// 4B on 32bit system, 8B on 64bit system
OutPt *Next;
// 4B on 32bit system, 8B on 64bit system
OutPt *Prev;
};
struct OutRec; struct OutRec;
struct Join { struct Join {
Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) : Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) :
@ -318,6 +331,7 @@ class Clipper : public virtual ClipperBase
public: public:
Clipper(int initOptions = 0); Clipper(int initOptions = 0);
~Clipper() { Clear(); } ~Clipper() { Clear(); }
void Clear() { ClipperBase::Clear(); DisposeAllOutRecs(); }
bool Execute(ClipType clipType, bool Execute(ClipType clipType,
Paths &solution, Paths &solution,
PolyFillType fillType = pftEvenOdd) PolyFillType fillType = pftEvenOdd)
@ -346,25 +360,34 @@ protected:
void Reset(); void Reset();
virtual bool ExecuteInternal(); virtual bool ExecuteInternal();
private: private:
std::vector<OutRec*> m_PolyOuts;
std::vector<Join> m_Joins; // Output polygons.
std::vector<Join> m_GhostJoins; std::vector<OutRec*> m_PolyOuts;
// Output points, allocated by a continuous sets of m_OutPtsChunkSize.
std::vector<OutPt*> m_OutPts;
// List of free output points, to be used before taking a point from m_OutPts or allocating a new chunk.
OutPt *m_OutPtsFree;
size_t m_OutPtsChunkSize;
size_t m_OutPtsChunkLast;
std::vector<Join> m_Joins;
std::vector<Join> m_GhostJoins;
std::vector<IntersectNode> m_IntersectList; std::vector<IntersectNode> m_IntersectList;
ClipType m_ClipType; ClipType m_ClipType;
// A priority queue (a binary heap) of Y coordinates. // A priority queue (a binary heap) of Y coordinates.
std::priority_queue<cInt> m_Scanbeam; std::priority_queue<cInt> m_Scanbeam;
// Maxima are collected by ProcessEdgesAtTopOfScanbeam(), consumed by ProcessHorizontal(). // Maxima are collected by ProcessEdgesAtTopOfScanbeam(), consumed by ProcessHorizontal().
std::vector<cInt> m_Maxima; std::vector<cInt> m_Maxima;
TEdge *m_ActiveEdges; TEdge *m_ActiveEdges;
TEdge *m_SortedEdges; TEdge *m_SortedEdges;
PolyFillType m_ClipFillType; PolyFillType m_ClipFillType;
PolyFillType m_SubjFillType; PolyFillType m_SubjFillType;
bool m_ReverseOutput; bool m_ReverseOutput;
// Does the result go to a PolyTree or Paths? // Does the result go to a PolyTree or Paths?
bool m_UsingPolyTree; bool m_UsingPolyTree;
bool m_StrictSimple; bool m_StrictSimple;
#ifdef use_xyz #ifdef use_xyz
ZFillCallback m_ZFill; //custom callback ZFillCallback m_ZFill; //custom callback
#endif #endif
void SetWindingCount(TEdge& edge) const; void SetWindingCount(TEdge& edge) const;
bool IsEvenOddFillType(const TEdge& edge) const bool IsEvenOddFillType(const TEdge& edge) const
@ -393,6 +416,11 @@ private:
OutRec* CreateOutRec(); OutRec* CreateOutRec();
OutPt* AddOutPt(TEdge *e, const IntPoint &pt); OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
OutPt* GetLastOutPt(TEdge *e); OutPt* GetLastOutPt(TEdge *e);
OutPt* AllocateOutPt();
OutPt* DupOutPt(OutPt* outPt, bool InsertAfter);
// Add the point to a list of free points.
void DisposeOutPt(OutPt *pt) { pt->Next = m_OutPtsFree; m_OutPtsFree = pt; }
void DisposeOutPts(OutPt*& pp) { if (pp != nullptr) { pp->Prev->Next = m_OutPtsFree; m_OutPtsFree = pp; } }
void DisposeAllOutRecs(); void DisposeAllOutRecs();
bool ProcessIntersections(const cInt topY); bool ProcessIntersections(const cInt topY);
void BuildIntersectList(const cInt topY); void BuildIntersectList(const cInt topY);
@ -406,6 +434,7 @@ private:
bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
void FixHoleLinkage(OutRec &outrec); void FixHoleLinkage(OutRec &outrec);
bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2);
bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, const IntPoint &Pt, bool DiscardLeft);
void JoinCommonEdges(); void JoinCommonEdges();
void DoSimplePolygons(); void DoSimplePolygons();
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const; void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const;