<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[166540] trunk/Source/WebKit2</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/166540">166540</a></dd>
<dt>Author</dt> <dd>timothy_horton@apple.com</dd>
<dt>Date</dt> <dd>2014-03-31 15:52:31 -0700 (Mon, 31 Mar 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Synchronize Web process remote layer tree commits with CoreAnimation commits in the UI process
https://bugs.webkit.org/show_bug.cgi?id=130984

Reviewed by Simon Fraser.

There's no reason for the Web process to paint faster than the UI process
can apply to and commit the CoreAnimation layer tree.

Also, once we get proper double-buffering and purgeable back buffers, we
will need the ability to make some assumptions about in-use surfaces.

Unfortunately, we can't actually tell when the render server commits, 
as this is an asynchronous operation in a process we don't control, so
for now, this is only an approximation.

This also means that the assumptions we would like to make won't be
strong guarantees yet.

* UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.h:
* UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm:
(WebKit::RemoteLayerTreeDrawingAreaProxy::commitLayerTree):
When we commit, install a run loop observer to fire immediately after CoreAnimation commits.

(WebKit::RemoteLayerTreeDrawingAreaProxy::showDebugIndicator):
Fix a typo ('cmponents').

(WebKit::coreAnimationDidCommitLayersCallback):
(WebKit::RemoteLayerTreeDrawingAreaProxy::scheduleCoreAnimationLayerCommitObserver):
(WebKit::RemoteLayerTreeDrawingAreaProxy::coreAnimationDidCommitLayers):
When CA commits, send a message to the Web process letting it know that we
swapped to the new backing store.

* WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.h:
* WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.mm:
(WebKit::RemoteLayerTreeDrawingArea::RemoteLayerTreeDrawingArea):
(WebKit::RemoteLayerTreeDrawingArea::flushLayers):
(WebKit::RemoteLayerTreeDrawingArea::didUpdate):
Defer remote layer tree commits until we get didUpdate() from the UI process.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2UIProcessmacRemoteLayerTreeDrawingAreaProxyh">trunk/Source/WebKit2/UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessmacRemoteLayerTreeDrawingAreaProxymm">trunk/Source/WebKit2/UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPagemacRemoteLayerTreeDrawingAreah">trunk/Source/WebKit2/WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.h</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPagemacRemoteLayerTreeDrawingAreamm">trunk/Source/WebKit2/WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (166539 => 166540)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2014-03-31 22:29:36 UTC (rev 166539)
+++ trunk/Source/WebKit2/ChangeLog        2014-03-31 22:52:31 UTC (rev 166540)
</span><span class="lines">@@ -1,3 +1,44 @@
</span><ins>+2014-03-31  Tim Horton  &lt;timothy_horton@apple.com&gt;
+
+        Synchronize Web process remote layer tree commits with CoreAnimation commits in the UI process
+        https://bugs.webkit.org/show_bug.cgi?id=130984
+
+        Reviewed by Simon Fraser.
+
+        There's no reason for the Web process to paint faster than the UI process
+        can apply to and commit the CoreAnimation layer tree.
+
+        Also, once we get proper double-buffering and purgeable back buffers, we
+        will need the ability to make some assumptions about in-use surfaces.
+
+        Unfortunately, we can't actually tell when the render server commits, 
+        as this is an asynchronous operation in a process we don't control, so
+        for now, this is only an approximation.
+
+        This also means that the assumptions we would like to make won't be
+        strong guarantees yet.
+
+        * UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.h:
+        * UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm:
+        (WebKit::RemoteLayerTreeDrawingAreaProxy::commitLayerTree):
+        When we commit, install a run loop observer to fire immediately after CoreAnimation commits.
+
+        (WebKit::RemoteLayerTreeDrawingAreaProxy::showDebugIndicator):
+        Fix a typo ('cmponents').
+
+        (WebKit::coreAnimationDidCommitLayersCallback):
+        (WebKit::RemoteLayerTreeDrawingAreaProxy::scheduleCoreAnimationLayerCommitObserver):
+        (WebKit::RemoteLayerTreeDrawingAreaProxy::coreAnimationDidCommitLayers):
+        When CA commits, send a message to the Web process letting it know that we
+        swapped to the new backing store.
+
+        * WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.h:
+        * WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.mm:
+        (WebKit::RemoteLayerTreeDrawingArea::RemoteLayerTreeDrawingArea):
+        (WebKit::RemoteLayerTreeDrawingArea::flushLayers):
+        (WebKit::RemoteLayerTreeDrawingArea::didUpdate):
+        Defer remote layer tree commits until we get didUpdate() from the UI process.
+
</ins><span class="cx"> 2014-03-31  Dean Jackson  &lt;dino@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Remove WEB_ANIMATIONS
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessmacRemoteLayerTreeDrawingAreaProxyh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.h (166539 => 166540)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.h        2014-03-31 22:29:36 UTC (rev 166539)
+++ trunk/Source/WebKit2/UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.h        2014-03-31 22:52:31 UTC (rev 166540)
</span><span class="lines">@@ -44,6 +44,8 @@
</span><span class="cx"> 
</span><span class="cx">     const RemoteLayerTreeHost&amp; remoteLayerTreeHost() const { return m_remoteLayerTreeHost; }
</span><span class="cx"> 
</span><ins>+    void coreAnimationDidCommitLayers();
+
</ins><span class="cx"> private:
</span><span class="cx">     virtual void sizeDidChange() override;
</span><span class="cx">     virtual void deviceScaleFactorDidChange() override;
</span><span class="lines">@@ -71,6 +73,8 @@
</span><span class="cx">     
</span><span class="cx">     void sendUpdateGeometry();
</span><span class="cx"> 
</span><ins>+    void scheduleCoreAnimationLayerCommitObserver();
+
</ins><span class="cx">     RemoteLayerTreeHost m_remoteLayerTreeHost;
</span><span class="cx">     bool m_isWaitingForDidUpdateGeometry;
</span><span class="cx"> 
</span><span class="lines">@@ -80,6 +84,8 @@
</span><span class="cx">     std::unique_ptr&lt;RemoteLayerTreeHost&gt; m_debugIndicatorLayerTreeHost;
</span><span class="cx">     RetainPtr&lt;CALayer&gt; m_tileMapHostLayer;
</span><span class="cx">     RetainPtr&lt;CALayer&gt; m_exposedRectIndicatorLayer;
</span><ins>+
+    RetainPtr&lt;CFRunLoopObserverRef&gt; m_layerCommitObserver;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> DRAWING_AREA_PROXY_TYPE_CASTS(RemoteLayerTreeDrawingAreaProxy, type() == DrawingAreaTypeRemoteLayerTree);
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessmacRemoteLayerTreeDrawingAreaProxymm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm (166539 => 166540)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm        2014-03-31 22:29:36 UTC (rev 166539)
+++ trunk/Source/WebKit2/UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm        2014-03-31 22:52:31 UTC (rev 166540)
</span><span class="lines">@@ -34,6 +34,9 @@
</span><span class="cx"> #import &quot;WebProcessProxy.h&quot;
</span><span class="cx"> #import &lt;WebCore/WebCoreCALayerExtras.h&gt;
</span><span class="cx"> 
</span><ins>+static const CFIndex CoreAnimationCommitRunLoopOrder = 2000000;
+static const CFIndex DidCommitLayersRunLoopOrder = CoreAnimationCommitRunLoopOrder + 1;
+
</ins><span class="cx"> using namespace WebCore;
</span><span class="cx"> 
</span><span class="cx"> namespace WebKit {
</span><span class="lines">@@ -49,6 +52,11 @@
</span><span class="cx"> RemoteLayerTreeDrawingAreaProxy::~RemoteLayerTreeDrawingAreaProxy()
</span><span class="cx"> {
</span><span class="cx">     m_webPageProxy-&gt;process().removeMessageReceiver(Messages::RemoteLayerTreeDrawingAreaProxy::messageReceiverName(), m_webPageProxy-&gt;pageID());
</span><ins>+
+    if (m_layerCommitObserver) {
+        CFRunLoopObserverInvalidate(m_layerCommitObserver.get());
+        m_layerCommitObserver = nullptr;
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void RemoteLayerTreeDrawingAreaProxy::sizeDidChange()
</span><span class="lines">@@ -129,6 +137,8 @@
</span><span class="cx">         updateDebugIndicator(layerTreeTransaction.contentsSize(), rootLayerChanged, scale);
</span><span class="cx">         asLayer(m_debugIndicatorLayerTreeHost-&gt;rootLayer()).name = @&quot;Indicator host root&quot;;
</span><span class="cx">     }
</span><ins>+
+    scheduleCoreAnimationLayerCommitObserver();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static const float indicatorInset = 10;
</span><span class="lines">@@ -253,8 +263,8 @@
</span><span class="cx">         RetainPtr&lt;CGColorRef&gt; color = adoptCF(CGColorCreate(colorSpace.get(), components));
</span><span class="cx">         [m_tileMapHostLayer setBackgroundColor:color.get()];
</span><span class="cx"> 
</span><del>-        const CGFloat borderCmponents[] = { 0, 0, 0, 1 };
-        RetainPtr&lt;CGColorRef&gt; borderColor = adoptCF(CGColorCreate(colorSpace.get(), borderCmponents));
</del><ins>+        const CGFloat borderComponents[] = { 0, 0, 0, 1 };
+        RetainPtr&lt;CGColorRef&gt; borderColor = adoptCF(CGColorCreate(colorSpace.get(), borderComponents));
</ins><span class="cx">         [m_tileMapHostLayer setBorderColor:borderColor.get()];
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="lines">@@ -269,4 +279,41 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static void coreAnimationDidCommitLayersCallback(CFRunLoopObserverRef, CFRunLoopActivity, void* context)
+{
+    static_cast&lt;RemoteLayerTreeDrawingAreaProxy*&gt;(context)-&gt;coreAnimationDidCommitLayers();
+}
+
+void RemoteLayerTreeDrawingAreaProxy::scheduleCoreAnimationLayerCommitObserver()
+{
+    if (m_layerCommitObserver)
+        return;
+
+    CFRunLoopRef runLoop = CFRunLoopGetCurrent();
+
+    // Make sure we wake up the loop or the observer could be delayed until some other source fires.
+    CFRunLoopWakeUp(runLoop);
+
+    CFRunLoopObserverContext context = { 0, this, 0, 0, 0 };
+    m_layerCommitObserver = adoptCF(CFRunLoopObserverCreate(0, kCFRunLoopBeforeWaiting | kCFRunLoopExit, true, DidCommitLayersRunLoopOrder, coreAnimationDidCommitLayersCallback, &amp;context));
+
+    CFRunLoopAddObserver(runLoop, m_layerCommitObserver.get(), kCFRunLoopCommonModes);
+}
+
+void RemoteLayerTreeDrawingAreaProxy::coreAnimationDidCommitLayers()
+{
+    if (m_layerCommitObserver) {
+        CFRunLoopObserverInvalidate(m_layerCommitObserver.get());
+        m_layerCommitObserver = nullptr;
+    }
+
+    if (!m_webPageProxy-&gt;isValid())
+        return;
+
+    // Waiting for CA to commit is insufficient, because the render server can still be
+    // using our backing store. We can improve this by waiting for the render server to commit
+    // if we find API to do so, but for now we will make extra buffers if need be.
+    m_webPageProxy-&gt;process().send(Messages::DrawingArea::DidUpdate(), m_webPageProxy-&gt;pageID());
+}
+
</ins><span class="cx"> } // namespace WebKit
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPagemacRemoteLayerTreeDrawingAreah"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.h (166539 => 166540)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.h        2014-03-31 22:29:36 UTC (rev 166539)
+++ trunk/Source/WebKit2/WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.h        2014-03-31 22:52:31 UTC (rev 166540)
</span><span class="lines">@@ -79,6 +79,8 @@
</span><span class="cx">     virtual void setExposedContentRect(const WebCore::FloatRect&amp;) override;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+    virtual void didUpdate() override;
+
</ins><span class="cx">     // WebCore::GraphicsLayerClient
</span><span class="cx">     virtual void notifyAnimationStarted(const WebCore::GraphicsLayer*, double time) override { }
</span><span class="cx">     virtual void notifyFlushRequired(const WebCore::GraphicsLayer*) override { }
</span><span class="lines">@@ -109,6 +111,9 @@
</span><span class="cx">     WebCore::Timer&lt;RemoteLayerTreeDrawingArea&gt; m_layerFlushTimer;
</span><span class="cx">     bool m_isFlushingSuspended;
</span><span class="cx">     bool m_hasDeferredFlush;
</span><ins>+
+    bool m_waitingForBackingStoreSwap;
+    bool m_hadFlushDeferredWhileWaitingForBackingStoreSwap;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> DRAWING_AREA_TYPE_CASTS(RemoteLayerTreeDrawingArea, type() == DrawingAreaTypeRemoteLayerTree);
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPagemacRemoteLayerTreeDrawingAreamm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.mm (166539 => 166540)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.mm        2014-03-31 22:29:36 UTC (rev 166539)
+++ trunk/Source/WebKit2/WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.mm        2014-03-31 22:52:31 UTC (rev 166540)
</span><span class="lines">@@ -51,6 +51,8 @@
</span><span class="cx">     , m_layerFlushTimer(this, &amp;RemoteLayerTreeDrawingArea::layerFlushTimerFired)
</span><span class="cx">     , m_isFlushingSuspended(false)
</span><span class="cx">     , m_hasDeferredFlush(false)
</span><ins>+    , m_waitingForBackingStoreSwap(false)
+    , m_hadFlushDeferredWhileWaitingForBackingStoreSwap(false)
</ins><span class="cx"> {
</span><span class="cx">     webPage-&gt;corePage()-&gt;settings().setForceCompositingMode(true);
</span><span class="cx"> #if PLATFORM(IOS)
</span><span class="lines">@@ -312,13 +314,16 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    if (m_waitingForBackingStoreSwap) {
+        m_hadFlushDeferredWhileWaitingForBackingStoreSwap = true;
+        return;
+    }
+
</ins><span class="cx">     m_webPage-&gt;layoutIfNeeded();
</span><span class="cx">     m_webPage-&gt;corePage()-&gt;mainFrame().view()-&gt;flushCompositingStateIncludingSubframes();
</span><span class="cx"> 
</span><span class="cx">     m_remoteLayerTreeContext-&gt;flushOutOfTreeLayers();
</span><span class="cx"> 
</span><del>-    ASSERT(m_rootLayer);
-
</del><span class="cx">     // FIXME: minize these transactions if nothing changed.
</span><span class="cx">     RemoteLayerTreeTransaction layerTransaction;
</span><span class="cx">     m_remoteLayerTreeContext-&gt;buildTransaction(layerTransaction, *m_rootLayer);
</span><span class="lines">@@ -330,7 +335,21 @@
</span><span class="cx">         toRemoteScrollingCoordinator(m_webPage-&gt;scrollingCoordinator())-&gt;buildTransaction(scrollingTransaction);
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+    m_waitingForBackingStoreSwap = true;
</ins><span class="cx">     m_webPage-&gt;send(Messages::RemoteLayerTreeDrawingAreaProxy::CommitLayerTree(layerTransaction, scrollingTransaction));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void RemoteLayerTreeDrawingArea::didUpdate()
+{
+    // FIXME: This should use a counted replacement for setLayerTreeStateIsFrozen, but
+    // the callers of that function are not strictly paired.
+
+    m_waitingForBackingStoreSwap = false;
+
+    if (m_hadFlushDeferredWhileWaitingForBackingStoreSwap) {
+        scheduleCompositingLayerFlush();
+        m_hadFlushDeferredWhileWaitingForBackingStoreSwap = false;
+    }
+}
+
</ins><span class="cx"> } // namespace WebKit
</span></span></pre>
</div>
</div>

</body>
</html>