From a9a20003c0a952bd259faa04d323665155f99d63 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 3 Mar 2017 23:06:51 +0100 Subject: [PATCH] Clipper memory optimization: Own memory manager for OutPt objects. Allocate OutPt by chunks of 32, reuse the released OutPt objects. --- xs/src/clipper.cpp | 112 +++++++++++++++++++++++---------------------- xs/src/clipper.hpp | 57 +++++++++++++++++------ 2 files changed, 100 insertions(+), 69 deletions(-) diff --git a/xs/src/clipper.cpp b/xs/src/clipper.cpp index 91d8442f4..7a7aef4e8 100644 --- a/xs/src/clipper.cpp +++ b/xs/src/clipper.cpp @@ -65,14 +65,6 @@ static int const Skip = -2; //edge that would otherwise close a path #define TOLERANCE (1.0e-20) #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. struct OutRec { 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) { std::memset(e, 0, sizeof(TEdge)); @@ -1219,11 +1198,14 @@ IntRect ClipperBase::GetBounds() // 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_StrictSimple = ((initOptions & ioStrictlySimple) != 0); m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); @@ -1351,12 +1333,32 @@ bool Clipper::ExecuteInternal() } //------------------------------------------------------------------------------ -void Clipper::DisposeAllOutRecs(){ - for (OutRec *outRec : m_PolyOuts) { - if (outRec->Pts) - DisposeOutPts(outRec->Pts); - delete outRec; +OutPt* Clipper::AllocateOutPt() +{ + OutPt *pt; + if (m_OutPtsFree) { + // 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(); } //------------------------------------------------------------------------------ @@ -2156,7 +2158,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) { OutRec *outRec = CreateOutRec(); outRec->IsOpen = (e->WindDelta == 0); - OutPt* newOp = new OutPt; + OutPt* newOp = this->AllocateOutPt(); outRec->Pts = newOp; newOp->Idx = outRec->Idx; newOp->Pt = pt; @@ -2176,7 +2178,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) if (ToFront && (pt == op->Pt)) return op; else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; - OutPt* newOp = new OutPt; + OutPt* newOp = this->AllocateOutPt(); newOp->Idx = outRec->Idx; newOp->Pt = pt; newOp->Next = op; @@ -2843,14 +2845,14 @@ void Clipper::FixupOutPolyline(OutRec &outrec) OutPt *tmpPP = pp->Prev; tmpPP->Next = pp->Next; pp->Next->Prev = tmpPP; - delete pp; + this->DisposeOutPt(pp); pp = tmpPP; } } if (pp == pp->Prev) { - DisposeOutPts(pp); + this->DisposeOutPts(pp); outrec.Pts = 0; return; } @@ -2871,7 +2873,7 @@ void Clipper::FixupOutPolygon(OutRec &outrec) if (pp->Prev == pp || pp->Prev == pp->Next) { // Empty loop or a stick. Release the polygon. - DisposeOutPts(pp); + this->DisposeOutPts(pp); outrec.Pts = nullptr; return; } @@ -2886,7 +2888,7 @@ void Clipper::FixupOutPolygon(OutRec &outrec) pp->Prev->Next = pp->Next; pp->Next->Prev = pp->Prev; pp = pp->Prev; - delete tmp; + this->DisposeOutPt(tmp); } else if (pp == lastOK) break; 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->Idx = outPt->Idx; 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) { 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 = 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) { op1 = op1b; op1->Pt = Pt; - op1b = DupOutPt(op1, !DiscardLeft); + op1b = this->DupOutPt(op1, !DiscardLeft); } } 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 = 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) { op1 = op1b; 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 = 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) { op2 = op2b; op2->Pt = Pt; - op2b = DupOutPt(op2, !DiscardLeft); + op2b = this->DupOutPt(op2, !DiscardLeft); }; } 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 = 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) { op2 = op2b; 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) { - op1b = DupOutPt(op1, false); - op2b = DupOutPt(op2, true); + op1b = this->DupOutPt(op1, false); + op2b = this->DupOutPt(op2, true); op1->Prev = op2; op2->Next = op1; op1b->Next = op2b; @@ -3214,8 +3216,8 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) return true; } else { - op1b = DupOutPt(op1, true); - op2b = DupOutPt(op2, false); + op1b = this->DupOutPt(op1, true); + op2b = this->DupOutPt(op2, false); op1->Next = op2; op2->Prev = op1; op1b->Prev = op2b; @@ -3307,8 +3309,8 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) if (Reverse1) { - op1b = DupOutPt(op1, false); - op2b = DupOutPt(op2, true); + op1b = this->DupOutPt(op1, false); + op2b = this->DupOutPt(op2, true); op1->Prev = op2; op2->Next = op1; op1b->Next = op2b; @@ -3318,8 +3320,8 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) return true; } else { - op1b = DupOutPt(op1, true); - op2b = DupOutPt(op2, false); + op1b = this->DupOutPt(op1, true); + op2b = this->DupOutPt(op2, false); op1->Next = op2; op2->Prev = op1; op1b->Prev = op2b; diff --git a/xs/src/clipper.hpp b/xs/src/clipper.hpp index 6969c861c..5e0d04edd 100644 --- a/xs/src/clipper.hpp +++ b/xs/src/clipper.hpp @@ -261,7 +261,20 @@ enum EdgeSide { esLeft = 1, esRight = 2}; 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 Join { Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) : @@ -318,6 +331,7 @@ class Clipper : public virtual ClipperBase public: Clipper(int initOptions = 0); ~Clipper() { Clear(); } + void Clear() { ClipperBase::Clear(); DisposeAllOutRecs(); } bool Execute(ClipType clipType, Paths &solution, PolyFillType fillType = pftEvenOdd) @@ -346,25 +360,34 @@ protected: void Reset(); virtual bool ExecuteInternal(); private: - std::vector m_PolyOuts; - std::vector m_Joins; - std::vector m_GhostJoins; + + // Output polygons. + std::vector m_PolyOuts; + // Output points, allocated by a continuous sets of m_OutPtsChunkSize. + std::vector 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 m_Joins; + std::vector m_GhostJoins; std::vector m_IntersectList; - ClipType m_ClipType; + ClipType m_ClipType; // A priority queue (a binary heap) of Y coordinates. std::priority_queue m_Scanbeam; // Maxima are collected by ProcessEdgesAtTopOfScanbeam(), consumed by ProcessHorizontal(). - std::vector m_Maxima; - TEdge *m_ActiveEdges; - TEdge *m_SortedEdges; - PolyFillType m_ClipFillType; - PolyFillType m_SubjFillType; - bool m_ReverseOutput; + std::vector m_Maxima; + TEdge *m_ActiveEdges; + TEdge *m_SortedEdges; + PolyFillType m_ClipFillType; + PolyFillType m_SubjFillType; + bool m_ReverseOutput; // Does the result go to a PolyTree or Paths? - bool m_UsingPolyTree; - bool m_StrictSimple; + bool m_UsingPolyTree; + bool m_StrictSimple; #ifdef use_xyz - ZFillCallback m_ZFill; //custom callback + ZFillCallback m_ZFill; //custom callback #endif void SetWindingCount(TEdge& edge) const; bool IsEvenOddFillType(const TEdge& edge) const @@ -393,6 +416,11 @@ private: OutRec* CreateOutRec(); OutPt* AddOutPt(TEdge *e, const IntPoint &pt); 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(); bool ProcessIntersections(const cInt topY); void BuildIntersectList(const cInt topY); @@ -406,6 +434,7 @@ private: bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); void FixHoleLinkage(OutRec &outrec); 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 DoSimplePolygons(); void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const;