Re: NSScrollView bug or misunderstood behavior?
Re: NSScrollView bug or misunderstood behavior?
- Subject: Re: NSScrollView bug or misunderstood behavior?
- From: Erik Buck <email@hidden>
- Date: Sat, 11 Nov 2006 15:01:49 -0500
Short answer: misunderstood behavior.
You are touching on several fundamental aspect of the Quartz Window
server architecture and AppKit. There are lots of documents on this
subject and several third party books including "Cocoa Programming"
which I happen to know describes these things. You aren't reading the
available documentation, and I like to describe things without re-
reading to refresh my memory, so here is an explanation based on my
understanding that may or may not be completely correct. If my
answer doesn't appeal to you, I recommend that you consult the
aforementioned documentation that I am not consulting :)
The Quartz Window Server manages the graphics card's frame buffer as
well as the "backing store" for all windows whether they are visible
on screen or not. The backing store for a window is the memory used
to store the bitmap representation of the entire window including its
frame.
All drawing within a graphics context (with the possible exception of
OpenGL contexts) actually modifies the backing store for a window.
The Quartz Window Server then "flushes" the visible portions of the
backing stores for all on-screen window into the graphics card's
frame buffer. Quartz Extreme is a technique that uses hardware
accelerated OpenGL texture mapping to perform the copying of backing
stores into the frame buffer as opposed to using a CPU for the memory
copy. Thus, the Window server manages all window layering. Special
effects such as shadows and genie effects are just modifications to
the way backing stores are copied into the frame buffer. The
rotating cube effect when users are changed is produced by using the
frame buffer's of two different screens as the sources for OpenGL
textures used by Quartz Extreme.
See the "Flushing graphics" section of the NSWindow documentation for
some interesting tips.
There are several advantages to this Quartz Window Server architecture:
Full window drag, window re-layering, window hiding and restoration,
window transparency, and neat window miniaturization effects are
implemented entirely within the Window Server and individual
applications are not bothered with routine drawing needs. For
example, the Window Server manages redrawing of window backing stores
into the frame buffer when a window is uncovered: the application
that owns the window does not need any processor time to handle
this. Contrast this to MS Windows XP where when a window is
uncovered, the owning application has to redraw the revealed window
contents. If the owning application is busy, the screen is not
updated correctly and the user sees graphical "turds" e.g. incomplete
inconsistent drawing is visible on screen. Heaven forbid the Windows
Explorer locks up and practically nothing draws correctly on
Windows. On the Mac, even a locked up application's windows can be
hidden, revealed, etc.
The primary disadvantage to the Quartz Window Server architecture is
that is uses memory for the backing store of every window whether the
window is visible or not.
So, Quartz should never leave "turds" on the screen, but it uses more
memory that other window servers.
Note: Nextstep used to support unbuffered and "retained" window
backing store schemes as well as "buffered". Interface Builder (last
time I looked) still preserves these options, but I don't think they
do anything on OS X. I think Cocoa buffers all windows regardless of
the setting.
Now, given that window backing stores exist, there are several
possible optimizations within applications. One is image caching
from the backing store. See the "Bracketing temporary drawing"
section of the NSWindow documentation. Another is the similar
"copies on scroll" optimization.
NSClipView's - (void)setCopiesOnScroll:(BOOL)flag method is used to
tell the clip view whether it should implement scrolling by copying a
portion of the window's backing store to another location and then
asking the document view to redraw only newly revealed areas. The
alternative requires the document view to redraw the entire visible
area including the areas that were already visible before. Copy on
scroll is normally disabled when the clipview does not draw a
background and the document view is not opaque. Otherwise, stray
bits of the window's backing store might be drawn incorrectly.
So, to answer your question, copy on scroll is implemented using the
the window's backing store. The effect you are seeing is presumably
caused by trying to use copy on scroll with a document view that is
not opaque and no background drawing.
There are several ways you can improve behavior:
1) Triple buffer: Have your document view draw into a bitmap image.
When the document view is asked to draw, just composite from the
bitmap image rather than redrawing from scratch. Disable copy on
scroll. This approach requires more memory because there will be
three buffers total: Your custom image, the window's backing store,
and the frame buffer. However, it should be as fast as copy on
scroll while enabling you to do "strange" things with
expensive_to_draw views.
2) Use an overlay window (child window): Let the Window Server do the
work for you by using an additional window and therefore an
additional backing store. Put whatever transparent drawing you want
in a child window that is layered correctly. The Window Server will
composite from both layered windows to produce the final result in
the frame buffer. You can have a transparent document view layered
above the child window or visa versa. It's all good to the Window
Server. I use this technique to layer Cocoa controls over pure
OpenGL drawing all the time.
3) Take over the tiling of the enclosing scroll view. This is how
rulers and scroll bars are implemented already. If you search the
net, you will find examples of how to do this that date back to the
Nextstep days. Just search for NSScrollView and tile.
4) Add a non-opaque subview to the scroll view and tell the subview -
setNeedsDisplay:YES every time the scroll view's -tile method is
called. I suspect having a non-opaque subview will defeat the copy
on scroll behavior anyway, but it should produce the correct results
on screen.
5) Tell us what you are really trying to achieve, and we may be able
to suggest other techniques. If this is just about water marking,
you are making it all way too hard.
_______________________________________________
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