Re: No regions, reason for Cocoa's inefficient window resizing?
Re: No regions, reason for Cocoa's inefficient window resizing?
- Subject: Re: No regions, reason for Cocoa's inefficient window resizing?
- From: Henk Kampman <email@hidden>
- Date: Sat, 14 Sep 2002 11:21:09 +0200
It seems that Apple doesn't care:
Quote: <
http://cocoa.mamasam.com/COCOADEV/2001/12/2/20390.php>
--------------
Deja vu
In 1997 I had a conversation with a well known Apple engineer on this
subject, who passed my comments on to the then "view guy" who sent me
this.
...
> ...
> My idea right now is to internally keep a
> list of rectangles instead of merging them immediately. I have this
> piece of code that allows me to very effectively manipulate a list of
> rectangles and do all kind of operations on them (xor, offset,
> difference, union etc.). One possibility for the API in NSView would
> be:
>
> - (void)drawRects:(NSXXXX *)rects { // Don't know if we are going
> to expose a new class or simply use a NSArray
> [self drawRect:[rects bounds]];
> }
>
> - (void)drawRect:(NSRect)rect {
> ....
>
> }
>
> -drawRects will be called instead of drawRect. Up to you to override
> it instead of -drawRect: to iterate through all the rectangles.
Maybe the time has come to finally do something about it?
Regards
--------------
Almost 5 years have passed since that post, and so far nothing
substantial has been done to improve the Cocoa drawing architecture.
Something needs to be done, just look at the crappy drawing performance
of the latest Cocoa iApps (iPhoto,iChat,iCal).
Sure resizing a window maybe not the best benchmark but it does show
that something is wrong.
Back to the original subject
Why Apple didn't use regions to pass the invalidated area is beyond me.
Apple already has one of the best region implementations.
Porting can't be a problem because its already running on Windows as
part of Quicktime.
Apple could easily fix it by introducing a new class (NSRegion?) and
add the following methods to NSView
- (void) setNeedsDisplayInRegion:(NSRegion* )region
- (void) displayInRegion:(NSRegion* )region
- (void)drawRegion:(NSRegion* )region
The default implementation of drawRegion should call drawRect with the
bounds of the region to avoid breaking existing applications.
However I suspect that the REAL problem is the windowmanager in that it
only supports a single dirty rect per window.
Henk
On zaterdag, sep 14, 2002, at 00:02 Europe/Amsterdam, Erik M. Buck
wrote:
Prior to the Opestep Specification, the -drawRect: method was
actually -drawRects:count: and accepted an array of rectangles to
update.
The first rectangle was the union of all the others. This allowed
very fine
grained redraw if desired or very course redraw if lazy ;). With the
introduction of Openstep, the method was changed to -drawRect:, but it
would
be called multiple times with small rectangles as needed. The intended
purpose was to implement -drawRect: to clip to the rectangle being
redrawn
and be careful not to waste cycles trying to draw things outside the
clipping rectangle. This had the effect of preventing class
implementers
from being lazy as they sometimes were with the old way :(
In the transition from Rhapsody to max OS X, Apple seriously broke the
semantics of -drawRect:. It is now called at most once per cycle of the
application event loop* and it is always called with the union of all
rectangles that need redisplay. This means that in a single pixel
needs to
be updated in one corner of a window and another pixel needs to be
updated
in the opposite corner, the entire visible portion of every view in the
window has to be redraw. Yes, I know it is bonehead and many bugs
have been
filed about this!
*(unless multiple direct calls to -display or -displayRect: calls are
made)
Apple recommends that developers use -setNeedsDisplay:
and -setNeedsDisplayInRect: instead of -display and -displayInRect:.
The
former methods register rectangles that need to be updated before the
next
pass through the application's run loop. All such rectangles are
coalesced
into one giant rectangle that is used to ask every view in the relevant
window to redraw whatever portion of the views intersect the giant
rectangle. Apple follows their own advise and uses -setNeedsDisplay:
and -setNeedsDisplayInRect: extensively including during window resize.
In contrast, -display and -displayInRect: each cause an immediate
messages
to -drawRect: with an appropriate rectangle. Multiple calls
to -displayInRect: with small rectangles is likely to be much faster
than
multiple calls to -displayInRect: in spite of Apple's recommendation!
Search for setNeedsDisplay at http://cocoa.mamasam.com/ for many past
discussions of this topic including my adamant assertion
that -setNeedsDisplayInRect: should be used because it permits frame
buffer
flush optimizations followed by my complete reversal on the issue when
the
absolute stupidity of Apple's current implementation was amply
demonstrated.
Here is an exceperpt from a relevant quote:
Use -display INSTEAD of -setNeedsDisplay:
I need to humbly apologize to Norbert and the list as a whole. I was
completely wrong and Norbert was completely correct.
I hereby reverse my previous very insistent assertion that
-setNeedsDisplay:
should be used in preference to -display whenever possible. The
opposite is
actually true.
Use -display rather than -setNeedsDisplay: whenever possible.
Apple has completely broken all display optimization that used to
exist in
Openstep variants. Norbert's excellent example proves without shadow of
doubt that using -display and doing your own context dependent display
area
optimization is necessary due to Apple's evident failure to perform
even the
tiniest optimization that is possible when -setNeedsDisplay: is used.
In
fact, as Norbert demonstrated, Apple's implementation of window
display is
always the WORST case when -setNeedsDisplay: is used and only
sometimes the
worst case when -display is used.
I suppose in retrospect this is one reason why Cocoa app display
performance
is so unimpressive in OS X.
Apple must fix several items of documentation ASAP:
http://developer.apple.com/techpubs/macosx/Cocoa/JavaTutorial/
customview/Cre
ating_a__s_of_NSView.html
<Quote>
This indication is called invalidation. Invalidation marks an entire
view or
a portion of a view as "invalid," thus requiring a redisplay. NSView
defines
two methods for marking a view's image as invalid: setNeedsDisplay,
which
invalidates the view's entire frame rectangle, and
setNeedsDisplayInRect,
which invalidates a portion of the view.
You can also force an immediate redisplay of a view with the display
and
displayRect methods, which are the counterparts to the methods
mentioned
above. However, you should use these and related display... methods
only
when necessary. Frequent forced displays can markedly degrade
application
performance.
<End Quote>
The use of -setNeedsDisplay is incorrectly advised several times in the
following
http://developer.apple.com/techpubs/macosx/Cocoa/Reference/
ApplicationKit/Ob
jC_classic/Classes/NSMatrix.html
I suspect that Apple's implementation of many classes including NSBox,
NSClipView, NSSplitView, and NSMatrix must be re-optimized to use
-display
rather than -setNeedsDisplay: given the terrible performance induced
by -setNeedsDisplay:
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.