Re: Fastest way to push strings to the screen in an NSView?
Re: Fastest way to push strings to the screen in an NSView?
- Subject: Re: Fastest way to push strings to the screen in an NSView?
- From: Douglas Davidson <email@hidden>
- Date: Thu, 18 Nov 2004 10:46:27 -0800
On Nov 17, 2004, at 6:45 PM, Robbie Haertel wrote:
I got that quote straight from Apple's documentation. I agree that
NSLayoutManager should only be used if it is really worth it, but he
asked why he might be having problems so I'm assuming there's a
possibility that he thinks it is worth it or he wouldn't of asked. I
also agree that things aren't always optimized for all cases, but hey,
he wanted to know why he was having a bottleneck and I offered an
answer :)!
I quite agree, and I think your recommendation was the right one for
this case. I just want to make sure that more detailed information is
out there for others who might be in slightly different situations.
I'm the engineer responsible for large portions of the string drawing
code, and its performance characteristics are something I spend quite a
bit of time on, so please forgive me if I go on at some length about
it. :)
I do believe that NSLayoutManager also caches glyphs. So, even if the
same letter and attribute (I believe he said his attributes are always
the same) are reused more than once (versus say entire strings that
are the same), there should still be an increase in performance. The
amount of the increase will depend on the amount of redudancy, in this
case I imagine certain letters will be used with very high frequency
(the vowels, t, n, etc.)
The situation is this: text drawing requires a fair bit of work to get
from characters and their attributes to pixels on the screen. In most
cases performance depends heavily on how much of that work can be
avoided by caching. There are several steps in the process and several
ways in which that can be accomplished.
The string drawing methods--drawAtPoint:, drawInRect:, etc.--are the
simplest APIs for drawing arbitrary text in a view. They also offer
the least opportunity for caching, because they don't provide an object
to retain any of the information used when drawing. Under the hood, we
do make attempts to cache as much information as we reasonably can, but
our ability to do so is inherently limited. There is definitely a
large class of texts for which we have made these methods quite
fast--principally the sort of strings commonly seen in drawing user
interfaces. I won't attempt to characterize them, because the class
expands from release to release; what I can recommend is that you
sample to see whether text drawing is a significant portion of your
drawing time, and if so whether methods and functions with "layout" in
the name take up most of it. If so, then you might consider moving to
the use of NSLayoutManager methods for your drawing.
(One performance hint with string drawing is that the string drawing
methods are designed to be used in a flipped view; they can be used in
a non-flipped view, but there often is a performance penalty for this.
NSLayoutManager drawing, on the other hand, assumes a flipped view and
is not intended to be used in a non-flipped view.)
When you create an NSTextStorage/NSTextContainer/NSLayoutManager
combination, as in the CircleView and Worm examples, you have control
over the caching of layout information. NSLayoutManager stores the
identities of the glyphs, and their positions in the layout. All of
this information is calculated the first time it is needed, and then
reused as long as the text remains the same. Even if the text changes,
NSLayoutManager recalculates only the portion actually affected by the
change. Drawing then is just a matter of sending the glyphs and their
locations to Quartz to be rendered into bits on the screen. In most
cases this would be the combination I would recommend for
performance-sensitive drawing.
It is also possible to use Quartz glyph drawing APIs directly.
However, in most cases the performance gain over the use of
NSLayoutManager will be slight. The primary performance difference is
that with the Quartz APIs you have control over the setting of the
font, color, and other portions of the graphics state. NSLayoutManager
must do this for each drawing call, but you could potentially avoid
some repetition if you were to do it yourself. One case that might
benefit from this would be EvenBetterWormView, in which glyphs are
drawn one at a time; this is a bit exceptional, though, as in most
cases glyphs will be drawn in larger chunks, and graphics state
manipulation becomes less significant.
The primary caution about using Quartz directly is that Quartz does not
do any of the work of properly converting characters to glyphs and
laying these glyphs out relative to each other, which is necessary for
correct text display. The right thing to do would be to use
NSLayoutManager for this, and to obtain from it the glyphs and their
relative positions. Alternatively, one could use ATSUI, which provides
similar functionality in a lower-level API, giving you somewhat more
control but requiring more work. An additional caution about using
Quartz directly is that NSLayoutManager provides certain additional
drawing such as underlines and shadows; if you needed these, with
Quartz you would have to do it yourself.
Some developers go beyond this to avoid even the work of having Quartz
render the glyphs, by caching the actual pixels in an NSImage. If you
have only a few strings to draw and your performance needs are extreme,
this can be a useful technique. The problem is that it requires
significant amounts of memory. I should warn you, also, that Quartz
glyph rendering has been the subject of a great deal of performance
work, and is something that has improved and will improve significantly
from release to release. It is likely that in some cases use of cached
images will actually be detrimental to performance, because of the VRAM
impact of the images. The performance characteristics of Quartz glyph
rendering vary based on the range of glyphs that are used--not really
at the level of character frequency in English, but more at the level
of the dozens of glyphs used in English vs. the thousands used in
Japanese or Chinese. I would not use image caching without careful
performance measurement.
For certain special needs it is possible to go beyond image caching,
and actually draw the text once in a separate window, then move or
order that window as needed. That would avoid the drawing stage
altogether, and put the text on screen directly through the window
compositor. If you needed to have a piece of text move rapidly across
the screen, especially if it was to go beyond the bounds of ordinary
windows, that could be something to consider. It's not really suitable
for ordinary string drawing, however, and the overhead is quite large;
I would use this only with a very small number of windows, and only in
very special cases.
Douglas Davidson
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden