Monday, November 15, 2010

pitfalls of early optimization

So I don't know that I've mentioned this on this blog, at least not recently.  Pict has had a serious issue drawing straight lines when run on the iPad.  As in it locks up the whole iPad for seconds at a time while drawing straight lines if they are longer than about an inch.

Drawing lines and curves over time isn't something that's normally done for computer graphics.  Most applications want to draw a line or a curve from start to finish, they aren't interested in the user watching this happen.  Incidentally the math is totally different.  Fortunately I'm building on the shoulders of giants.  Well, people better at math than I am.

I created my own actions in cocos2d by overriding the CCMoveTo and CCBezierTo classes.  In practice this means that I needed one additional piece of information (the last time we've updated) to be able to draw the points we may have missed along the way in addition to using a CCRenderTexture to draw the "pen" sprite into.

This has been a thorn in my side for months and a point of discouragement on this project.  What's the point of making a drawing app if the drawing takes way too long?  Without that the app is dead.  Very discouraging.

I've been hitting my drawing code off and on for the past four months and have made little headway.  This saturday past I sat down for almost 5 hours and gave it a serious rework.  I know WAY more than I want to about bezier paths and drawing a point on a line.  However I ended up giving up around 1 AM.  The one thing I knew at the end of the night was that most of my approaches had been flawed and I knew why.

However my focus was all for naught.  It seems that due to profiling where my time was being spent with instruments, I was misled. Yes, all of my time was spent drawing and that's where things pointed.  The solution was not how often I was calculating or where I was doing my drawing, but how often I drew to the CCRenderTexture.  Specifically how often I called CCRenderTexture's begin and end functions.  Rather than move my CCSprite and using the method [sprite visit], I saved all of my positions and then drew them in a for loop in my render texture.  In pseudo-code the original was doing something like this:

// figure out the new sprite position that's needed to draw
// draw the sprite into the render texture
  // CCRenderTexture begin drawing
  // set the sprite to the new position
  // [sprite visit]
  // CCRenderTexture end drawing
// repeat as necessary
// return out of our update function

After many many attempts at changing how many and often I drew, I changed the code to do this:


// figure out the new sprite position that's needed to draw
// save the new position
// repeat as necessary

// CCRenderTexture begin drawing
// iterate over my points, moving my sprite
  // [sprite visit]
// CCRenderTexture end drawing
// return out of our update function

This totally fixed the problem.  Ugh, what a pain!  In hindsight it seems somewhat obvious.  Maybe I'm clueless but I'd like to think it wasn't obvious from my previous perspective.

Lesson:  CCRenderTexture (and as an extension, some openGL calls) can be expensive if used too often!

Here is a reference to the (old) cocos2d threads related specifically to my drawing are here, here and here.

No comments:

Post a Comment