[wxPython-users] number of objects drawn to dc

Chris Mellon arkanes at gmail.com
Mon Oct 1 08:57:37 PDT 2007


On 10/1/07, Martin Landa <landa.martin at gmail.com> wrote:
> Hi all,
>
> trying to simplify my question..., 'stupid' example below
>
>         import time
>         start = time.clock()
>         #nlines = self.__display.DrawMap(True) # force
>         nlines = 1e4
>         for line in range(nlines):
>             self.mapWindow.pdcVector.SetId(line) # <--- HERE
>             self.mapWindow.pdcVector.DrawLine(0,0,100,100)
>         stop = time.clock()
>         Debug.msg(3, "CDisplayDriver.DrawMap(): nlines=%d, sec=%f" % \
>                       (nlines, stop-start))
>
> On my machine it takes 3s (stop-start), if I comment out SetId() fn,
> time is 0.04s. I am wondering why is pseudoDC.SetId() soooo
> expensive...
>
> Or I am just doing something wrong?
>
> Thanks! Martin
>
> 2007/9/29, Martin Landa <landa.martin at gmail.com>:
> > Hi Chris,
> >
> > thanks a lot for pointing me out to floatcanvas. I am quite newbie in
> > wxPython as you can see;-)
> >
> > > Martin Landa wrote:
> > > > I need to draw to DC huge number (> 1e6) of objects
> > >
> > > That's a LOT of objects -- can you really see them all at the same time?
> > > You might want to do a bounding box check or something first. You might
> > > also want to see if you can change what you draw depending on scale.
> > >
> > > Also, is all this stuff changing? you could perhaps buffer some of it.
> > >
> > > >(GIS application,
> > > > tool for editing vector maps).
> > >
> > > Cool -- I"d love to see what you come up with. Also, be sure to check
> > > out FloatCanvas (wx.lib.floatcanvas, or newer version at:
> > >
> > > http://morticia.cs.dal.ca/FloatCanvas/
> > >
> > > It takes care of a lot of this for you. I'm also hoping to add more
> > > GISey stuff with it -- I'd love help!
> > >
> > > There is simplified OnPaint fn.
> > > >         dc = wx.BufferedPaintDC(self, self._Buffer)
> > > >         import time
> > > >         start = time.clock()
> > > >         nlines = 1000000
> > > >         for i in range(nlines):
> > > >             dc.DrawLine(0, 0, 100, 100)
> > > >         stop = time.clock()
> > > >         print >> sys.stderr, "time=%f" % (stop-start)
> > >
> > > Maybe this is just a test, but the point of a bufferedDC is that you can
> > > just blit the buffer in the OnPaint -- you only want to draw when
> > > something changes (again, see FloatCanvas)
> >
> > Well, this was not simplified OnPaint fn, anyway not good example...
> >
> > Currently I am using one pseudoDC for drawing background map,
> > decorations, etc. The second pseudoDC for drawing vector map which is
> > edited. This pseudoDC object is input for C++ driver (using Swig
> > interface for Python). The C++ driver (library) is needed because
> > vector objects of vector map need to be read using C API of the given
> > GIS application.
> >
> > So OnPaint():
> >
> >        dc = wx.BufferedPaintDC(self, self._Buffer)
> >
> >         # we need to clear the dc BEFORE calling PrepareDC
> >         bg = wx.Brush(self.GetBackgroundColour())
> >         dc.SetBackground(bg)
> >         dc.Clear()
> >
> >         # use PrepareDC to set position correctly
> >         self.PrepareDC(dc)
> >
> >         # create a clipping rect from our position and size
> >         # and update region
> >         rgn = self.GetUpdateRegion().GetBox()
> >         dc.SetClippingRect(rgn)
> >         # draw to the dc using the calculated clipping rect
> >         self.pdc.DrawToDCClipped(dc, rgn)
> >
> >         # draw vector map layer
> >         if self.pdcVector:
> >             self.pdcVector.DrawToDCClipped(dc, rgn)
> >
> > Vector objects are drawn to PseudoDC using C++ driver
> > ...
> > self.driver.DrawMap(self.pdcVector)
> > ...
> >
> > There is DrawLine method, each rendered line need to have unique id.
> >
> > void DrawLine(line) {
> > ...
> >             dc->SetId(id++);
> >             dc->DrawLines(pointsScreen->GetCount(), points);
> > ...
> > }
> >
> > There something very strange at least for me;-) If I comment out
> > 'dc->SetId(startId);', speed of rendering is quite OK.
> >
> > E.g. for vector map with 1e4 lines/points (each line has two nodes
> > which are also drawn), it takes 1.2s for drawing and 0.6s for each
> > redrawing.
> >
> > If set up id by 'dc->SetId(startId++);' drawing takes 6s and more!!!
> > The response is significantly worse. I am just wondering why is
> > SetId() so expensive.
> >
> > Thanks again for any hits!
> >
> > Martin
> >

I haven't profiled this, so I'm committing a cardinal sin by
speculating on performance-related results by just looking at the
source, but here's my guess.

wxPsuedoDC (wxPDC hereafter) has a list of PDC operations. It's in Z
order, and a PDC operation is a list of actual drawing operations +
some metadata.

When you call SetID(), that just sets the ID that the next drawing
operation will use.

When you do a drawing operation, it calls AddToList, which scans the
current list of operations for the current id, fails to find it, and
then creates a new one. There's an optimization in the search for
using the last ID, so repeated draws with the same ID are fast, but a
draw with a new id is slow - there's a linear search for each id, so
the time complexity for drawing n individual IDs is over O(n**2).

Adding an index for id->node lookups is probably the best solution.




More information about the wxpython-users mailing list