Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Cocoa: Flush backing store on the attached QWindow only

Up until now, we would flush the backing store on the passed
QWindow. This had the effect of marking dirty the specified
region on the QWindow's NSView. In turn, Cocoa would call
-[QNSView drawRect:] and cover the dirty region while possibly
optimizing overlapping areas.

However, this approach doesn't work as well for a hierarchy of
QWindow since it doesn't take advantage of the fact that the
backingstore is shared among all the descendants of the toplevel
QWindow. Therefore, it results on the same pixels being copied
over several times for the same backing store. It is important
to notice that Cocoa doesn't have any way of optimizing this
process since it doesn't know that we share the backing store
contents among several QNSViews on the same toplevel window.

Our approach takes advantage of the shared backing store contents
by stating that, in the end, we can display everything on the
toplevel QNSView. This requires the child QNSViews to never be
marked dirty in Cocoa's eyes (but that's no longer necessary since
they don't need to be rendered anyway). By doing so, Cocoa can
minimize the number of calls to -[QNSView drawRect:]. Notice that
this approach also guarantees that late updates to child QWindows
will get drawn properly because that QWindow flush will always
result in a call to -[NSView setNeedsDisplayInRect:].

Note about the 'opaque' property value: In order to avoid child
QNSViews in front of the backing store QNSView to obfuscate the
latter, we need to ensure the child views are not opaque. This
doesn't introduce any pessimization since those non-opaque views
are never marked as needing to be displayed.
  • Loading branch information
wayne-arnold-adsk committed Jun 28, 2016
1 parent 216facc commit 72e3fbb
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 16 deletions.
24 changes: 21 additions & 3 deletions src/plugins/platforms/cocoa/qcocoabackingstore.mm
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,17 @@
QCocoaBackingStore::QCocoaBackingStore(QWindow *window)
: QPlatformBackingStore(window)
{
if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle()))
cocoaWindow->setHasBackingStore(true);
}

QCocoaBackingStore::~QCocoaBackingStore()
{
if (QWindow *w = window())
if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(w->handle()))
if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(w->handle())) {
cocoaWindow->setHasBackingStore(false);
[cocoaWindow->m_qtView clearBackingStore:this];
}
}

QPaintDevice *QCocoaBackingStore::paintDevice()
Expand All @@ -71,8 +75,22 @@
void QCocoaBackingStore::flush(QWindow *win, const QRegion &region, const QPoint &offset)
{
if (!m_qImage.isNull()) {
if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(win->handle()))
[cocoaWindow->m_qtView flushBackingStore:this region:region offset:offset];
QCocoaWindow *bsCocoaWindow = static_cast<QCocoaWindow *>(window()->handle());
if (bsCocoaWindow) {
// In case the window was not created when the constructor got called.
bsCocoaWindow->setHasBackingStore(true);
}
QNSView *bsQtView = bsCocoaWindow ? bsCocoaWindow->qtView() : Q_NULLPTR;
QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(win->handle());
QNSView *qtView = cocoaWindow ? cocoaWindow->qtView() : Q_NULLPTR;
if (bsQtView && qtView) {
QNSView *daQtView = bsQtView.window == qtView.window ? bsQtView : qtView;
if (daQtView != qtView) {
// Clear any disabled backing store related view.
cocoaWindow->setHasBackingStore(false);
}
[daQtView flushBackingStore:this region:region offset:offset];
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/plugins/platforms/cocoa/qcocoawindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@ class QCocoaWindow : public QPlatformWindow

static QPoint bottomLeftClippedByNSWindowOffsetStatic(QWindow *window);
QPoint bottomLeftClippedByNSWindowOffset() const;

void setHasBackingStore(bool hasBS);
bool hasBackingStore() const;

protected:
void recreateWindow(const QPlatformWindow *parentWindow);
QCocoaNSWindow *createNSWindow();
Expand Down Expand Up @@ -346,6 +350,8 @@ class QCocoaWindow : public QPlatformWindow
// This object is tracked by QCocoaWindowPointer,
// preventing the use of dangling pointers.
QObject sentinel;

bool m_hasBackingStore;
};

QT_END_NAMESPACE
Expand Down
10 changes: 10 additions & 0 deletions src/plugins/platforms/cocoa/qcocoawindow.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1902,6 +1902,16 @@ - (void)dealloc
return QPoint(visibleRect.origin.x, -visibleRect.origin.y + (origin.y - visibleRect.size.height));
}

void QCocoaWindow::setHasBackingStore(bool hasBS)
{
m_hasBackingStore = hasBS;
}

bool QCocoaWindow::hasBackingStore() const
{
return m_hasBackingStore;
}

QMargins QCocoaWindow::frameMargins() const
{
NSRect frameW = [m_nsWindow frame];
Expand Down
1 change: 0 additions & 1 deletion src/plugins/platforms/cocoa/qnsview.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper));

@interface QT_MANGLE_NAMESPACE(QNSView) : NSView <NSTextInputClient> {
QCocoaBackingStore* m_backingStore;
QPoint m_backingStoreOffset;
CGImageRef m_maskImage;
uchar *m_maskData;
bool m_shouldInvalidateWindowShadow;
Expand Down
23 changes: 11 additions & 12 deletions src/plugins/platforms/cocoa/qnsview.mm
Original file line number Diff line number Diff line change
Expand Up @@ -526,9 +526,8 @@ - (void)removeFromSuperview
- (void) flushBackingStore:(QCocoaBackingStore *)backingStore region:(const QRegion &)region offset:(QPoint)offset
{
m_backingStore = backingStore;
m_backingStoreOffset = offset * m_backingStore->getBackingStoreDevicePixelRatio();
foreach (QRect rect, region.rects())
[self setNeedsDisplayInRect:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height())];
foreach (const QRect &rect, region.rects())
[self setNeedsDisplayInRect:NSMakeRect(rect.x() + offset.x(), rect.y() + offset.y(), rect.width(), rect.height())];
}

- (void)clearBackingStore:(QCocoaBackingStore *)backingStore
Expand All @@ -545,7 +544,14 @@ - (BOOL) hasMask
- (BOOL) isOpaque
{
if (!m_platformWindow)
return true;
return YES;
if (!m_platformWindow->hasBackingStore()) {
// Non backing store related QNSViews are never marked dirty
// since we only draw the ancestor related to that BS.
// Therefore, they should not hide their BS related ancestor
// or any other view behind it.
return NO;
}
return m_platformWindow->isOpaque();
}

Expand Down Expand Up @@ -628,15 +634,8 @@ - (void) drawRect:(NSRect)dirtyRect
CGContextClipToMask(cgContext, dirtyWindowRect, subMask);
}

// Clip out and draw the correct sub image from the (shared) backingstore:
CGRect backingStoreRect = CGRectMake(
dirtyBackingRect.origin.x + m_backingStoreOffset.x(),
dirtyBackingRect.origin.y + m_backingStoreOffset.y(),
dirtyBackingRect.size.width,
dirtyBackingRect.size.height
);
CGImageRef bsCGImage = qt_mac_toCGImage(m_backingStore->toImage());
CGImageRef cleanImg = CGImageCreateWithImageInRect(bsCGImage, backingStoreRect);
CGImageRef cleanImg = CGImageCreateWithImageInRect(bsCGImage, dirtyBackingRect);

// Optimization: Copy frame buffer content instead of blending for
// top-level windows where Qt fills the entire window content area.
Expand Down

0 comments on commit 72e3fbb

Please sign in to comment.