+2014-07-14 Tim Horton <timothy_horton@apple.com>
+
+ [iOS] Throttle painting using a UI-process-side CADisplayLink
+ https://bugs.webkit.org/show_bug.cgi?id=134879
+ <rdar://problem/17641699>
+
+ Reviewed by Simon Fraser.
+
+ Just waiting for CA to commit is insufficient to actually throttle to 60fps,
+ because nothing will block the main runloop from spinning.
+
+ Instead, listen to a CADisplayLink, and send didUpdate to the WebProcess
+ the first time it fires after we commit. This is not a guarantee that
+ our content is on the screen, but we don't have any way to make that guarantee yet.
+
+ This will throttle painting, rAF, etc. to the display refresh rate.
+
+ * UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.h:
+ * UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm:
+ (-[OneShotDisplayLinkHandler initWithDrawingAreaProxy:]):
+ (-[OneShotDisplayLinkHandler dealloc]):
+ (-[OneShotDisplayLinkHandler displayLinkFired:]):
+ (-[OneShotDisplayLinkHandler invalidate]):
+ (-[OneShotDisplayLinkHandler schedule]):
+ (WebKit::RemoteLayerTreeDrawingAreaProxy::RemoteLayerTreeDrawingAreaProxy):
+ (WebKit::RemoteLayerTreeDrawingAreaProxy::~RemoteLayerTreeDrawingAreaProxy):
+ (WebKit::RemoteLayerTreeDrawingAreaProxy::commitLayerTree):
+ (WebKit::RemoteLayerTreeDrawingAreaProxy::didRefreshDisplay):
+ (WebKit::RemoteLayerTreeDrawingAreaProxy::coreAnimationDidCommitLayers): Deleted.
+
2014-07-14 Dan Bernstein <mitz@apple.com>
<rdar://problem/17657391> [iOS] Networking process writes persistent credentials to the keychain
#include <WebCore/FloatPoint.h>
#include <WebCore/IntPoint.h>
#include <WebCore/IntSize.h>
-#include <WebCore/RunLoopObserver.h>
+
+OBJC_CLASS OneShotDisplayLinkHandler;
namespace WebKit {
void acceleratedAnimationDidStart(uint64_t layerID, const String& key, double startTime);
- void coreAnimationDidCommitLayers();
-
uint64_t nextLayerTreeTransactionID() const { return m_pendingLayerTreeTransactionID + 1; }
uint64_t lastCommittedLayerTreeTransactionID() const { return m_transactionIDForPendingCACommit; }
+ void didRefreshDisplay(double timestamp);
+
private:
virtual void sizeDidChange() override;
virtual void deviceScaleFactorDidChange() override;
RetainPtr<CALayer> m_tileMapHostLayer;
RetainPtr<CALayer> m_exposedRectIndicatorLayer;
- std::unique_ptr<WebCore::RunLoopObserver> m_layerCommitObserver;
-
uint64_t m_pendingLayerTreeTransactionID;
uint64_t m_lastVisibleTransactionID;
uint64_t m_transactionIDForPendingCACommit;
CallbackMap m_callbacks;
+
+ RetainPtr<OneShotDisplayLinkHandler> m_displayLinkHandler;
};
DRAWING_AREA_PROXY_TYPE_CASTS(RemoteLayerTreeDrawingAreaProxy, type() == DrawingAreaTypeRemoteLayerTree);
using namespace IPC;
using namespace WebCore;
-static const CFIndex didCommitLayersRunLoopOrder = (CFIndex)RunLoopObserver::WellKnownRunLoopOrders::CoreAnimationCommit + 1;
+// FIXME: Mac will need something similar; we should figure out how to share this with DisplayRefreshMonitor without
+// breaking WebKit1 behavior or WebKit2-WebKit1 coexistence.
+#if PLATFORM(IOS)
+@interface OneShotDisplayLinkHandler : NSObject {
+ WebKit::RemoteLayerTreeDrawingAreaProxy* _drawingAreaProxy;
+ CADisplayLink *_displayLink;
+}
+
+- (id)initWithDrawingAreaProxy:(WebKit::RemoteLayerTreeDrawingAreaProxy*)drawingAreaProxy;
+- (void)displayLinkFired:(CADisplayLink *)sender;
+- (void)invalidate;
+- (void)schedule;
+
+@end
+
+@implementation OneShotDisplayLinkHandler
+
+- (id)initWithDrawingAreaProxy:(WebKit::RemoteLayerTreeDrawingAreaProxy*)drawingAreaProxy
+{
+ if (self = [super init]) {
+ _drawingAreaProxy = drawingAreaProxy;
+ // Note that CADisplayLink retains its target (self), so a call to -invalidate is needed on teardown.
+ _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkFired:)];
+ [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
+ _displayLink.paused = YES;
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ ASSERT(!_displayLink);
+ [super dealloc];
+}
+
+- (void)displayLinkFired:(CADisplayLink *)sender
+{
+ ASSERT(isMainThread());
+ _drawingAreaProxy->didRefreshDisplay(sender.timestamp);
+ _displayLink.paused = YES;
+}
+
+- (void)invalidate
+{
+ [_displayLink invalidate];
+ _displayLink = nullptr;
+}
+
+- (void)schedule
+{
+ _displayLink.paused = NO;
+}
+
+@end
+#endif
namespace WebKit {
, m_pendingLayerTreeTransactionID(0)
, m_lastVisibleTransactionID(0)
, m_transactionIDForPendingCACommit(0)
+#if PLATFORM(IOS)
+ , m_displayLinkHandler(adoptNS([[OneShotDisplayLinkHandler alloc] initWithDrawingAreaProxy:this]))
+#endif
{
#if USE(IOSURFACE)
// We don't want to pool surfaces in the UI process.
if (m_webPageProxy->preferences().tiledScrollingIndicatorVisible())
initializeDebugIndicator();
-
- m_layerCommitObserver = RunLoopObserver::create(didCommitLayersRunLoopOrder, [this]() {
- this->coreAnimationDidCommitLayers();
- });
}
RemoteLayerTreeDrawingAreaProxy::~RemoteLayerTreeDrawingAreaProxy()
{
m_callbacks.invalidate(CallbackBase::Error::OwnerWasInvalidated);
m_webPageProxy->process().removeMessageReceiver(Messages::RemoteLayerTreeDrawingAreaProxy::messageReceiverName(), m_webPageProxy->pageID());
+
+#if PLATFORM(IOS)
+ [m_displayLinkHandler invalidate];
+#endif
}
void RemoteLayerTreeDrawingAreaProxy::sizeDidChange()
asLayer(m_debugIndicatorLayerTreeHost->rootLayer()).name = @"Indicator host root";
}
- m_layerCommitObserver->schedule();
+#if PLATFORM(IOS)
+ [m_displayLinkHandler schedule];
+#else
+ didRefreshDisplay(monotonicallyIncreasingTime());
+#endif
}
void RemoteLayerTreeDrawingAreaProxy::acceleratedAnimationDidStart(uint64_t layerID, const String& key, double startTime)
}
}
-void RemoteLayerTreeDrawingAreaProxy::coreAnimationDidCommitLayers()
+void RemoteLayerTreeDrawingAreaProxy::didRefreshDisplay(double)
{
- m_layerCommitObserver->invalidate();
-
if (!m_webPageProxy->isValid())
return;