The Canvas Caveat

This isn't going to be a real post, but in case anyone else is having issues with performance on canvas-backed views, I learned something fairly important this past week.  Canvas, while it does perform fairly well when drawing lots of objects, is absolutely horrendous at drawing on a large space.  In Mockingbird, this meant that people with pages that were bigger than about 2000x2000 were suffering from choppiness and slowdowns, and as soon as I just removed drawing on a canvas element this big, things sped up tremendously.  This is a blog post that has some numbers and graphs.

For those that drink the Cappuccino, Cappuccino will call lockFocus on a view when the view needs display, even if the view has nothing in drawRect.  This means that if you have a giant CPView, even if you don't implement drawRect on it, a giant canvas element gets created in lockFocus.  It's not a huge performance hit, but getting rid of the lockFocus call did speed things up an a lower powered Macbook running Safari.  I made the following view subclass to do this:

Really simple - by default, a BigView will not draw anything.  But you can still set the isDrawable flag to make it call your drawRect method.  

EDIT: Just talked with the guys at 280 North and they explained to me that Cappuccino is, in fact, smart about not calling lockFocus unnecessarily.  The reason for the performance differences on the Macbook Pro vs the Macbook was that the code on the Macbook Pro did not have a drawRect method at all, whereas the code on the Macbook had an empty drawRect method on the view.  Cappuccino, apparently, figures out if your CPView subclass has an overridden drawRect method before calling lockFocus and all the other drawing code.  So, the default CPView already optimizes for this (just don't have an empty drawRect method in your CPView subclass).

Hope this helps anyone else running into performance problems with the Canvas tag.

Loading mentions Retweet
Filed under  //  Cappuccino  
Comments (0)
Posted 12 days ago

Three things I did to tackle Mockingbird performance issues

I posted an update to Mockingbird earlier today that should make loading large projects much faster.  Most of what I did is probably fairly obvious, but I thought I should share what I did anyway for any Cappuccino newbies looking to help the performance of their app.

1. Use the Safari profiler

The first step to optimization is to always, ALWAYS profile your app.  The things slowing you down will often surprise you, and the guys at 280North have done an amazing job to make Safari's profile very usable with Cappuccino.  You can read more about what they've done here.  After using the profiler, I found a few easy ways to speed things up, but no matter what I did, some of the larger projects were still giving me slow script errors on Firefox.  It was at this point that I realized that if I could somehow break up the work that's being done up front when a new project is loaded, things would work out much better.  And then I remembered that I could...

2. Use Javascript's setTimeout method to break up a large chunk of work

The main issue with loading large projects up front is that each page was being recreated serially in one go.  Instead of doing this, I used setTimeout to load each page, and then give the browser a break between each page load.  My function was basically something like:

- (void)loadPageFunction { window.setTimeout(function() { [self loadPageFunction]; }, 0); }

This way, the browser doesn't think that loading all the pages is one giant chunk of work and, so, for large projects, unless the project has a really crazed page, the browser shouldn't complain.

But then, I noticed one more odd thing - whenever I loaded a project after already having a large project, things ran much more slowly.  After using the profiler to see what was going on, I noticed that my app was spending a lot of time cleaning up notifications from the old project.  After some investigation, I found out that the implementation for - [CPNotificationCenter removeObserver:] is way slower than explicitly removing your observer for each notification using - [CPNotificationtCenter removeObserver:name:object:], and I was using the former everywhere.  So, I decided to change these to...

3. Use - [CPNotificationtCenter removeObserver:name:object:] instead of - [CPNotificationCenter removeObserver:]

One of the problems with Javascript is its lack of weak references, which becomes an issue with singletons like CPNotificationCenter's defaultCenter.  I've tried fairly hard to make sure I always remove my observers wherever I set them up, but I'm pretty sure there are still leaks in there somewhere.  These leaks don't really affect performance until you use a - [CPNotificationCenter removeObserver:] call since this will end up iterating through all your notifications (so if you have many, that becomes a problem).  This is, however, a hack around a more fundamental problem - either we need to figure out a way to implement some kind of weak referencing system in Objective-J or, (the easier route), make it easier to not use the default notification center to handle notifications.  A lot of classes internal to the framework use the default notification center for messaging, but it would definitely be a lot easier for me to just trash the notification center whenever I open a new project (still not perfect, I know, but I think better than what currently happens).  

Loading mentions Retweet
Filed under  //  Cappuccino  
Comments (4)
Posted 1 month ago

A small step towards better text support in Cappuccino

Though Cappuccino has treated me very well so far, text support in Cappuccino is still an area that needs much improvement.  Many newcomers still ask about multiline text support, and so far, the solution has been the minimal implementation of CPTextView located here.  I've been using this for a while, but recently ran into a few problems, and decided to make it just a little less hacked together =).

First, the code:  http://gist.github.com/239696

This code is a bit of an amalgam of code from CPTextField and the old CPTextView.  I've tried to make it a bit more cross-browser compatible and have added support for tabbing.  I've also changed the behavior to be based roughly on the behavior Ross Boucher implemented for CPTextField as described here.  I implement the same delegate methods, except that for CPTextView, the action fires on escape instead of return.  Note that this is not actually the behavior of CPTextView in Cocoa, so if you want to override this behavior, I've made sure to let CPTextView be easily overridable.  There are no file-scoped functions - you can just override the private methods _keyUpFunction, _keyDownFunction, _keyPressFunction and _blurFunction. 

There's still obviously a lot lacking in the code.  For one, I didn't really adhere strictly to the Cocoa API for CPTextView when writing this.  Additionally, autoscrolling doesn't work, entering text is fairly slow after scrollbars appear, and I would like to implement mouse tracking so that clicking on the CPTextView's scroll bar doesn't fire the blur method for the CPTextView.  But this is doing the job for now on Mockingbird.

Loading mentions Retweet
Filed under  //  Cappuccino   Objective-J  
Comments (0)
Posted 4 months ago