RESOLVED FIXED 87873
[chromium] Single thread proxy should not tick animations unless the layer renderer has been initialized
https://bugs.webkit.org/show_bug.cgi?id=87873
Summary [chromium] Single thread proxy should not tick animations unless the layer re...
vollick
Reported 2012-05-30 10:28:59 PDT
Currently, in CCSingleThreadProxy::initializeLayerRenderer(), the reference to m_contextBeforeInitialization is released and passed to m_layerTreeHostImpl->initializeLayerRenderer(...), but if that call fails, then m_contextBeforeInitialization is null, so subsequent calls to CCSingleThreadProxy::initializeLayerRenderer() will cause a crash. It is reasonable to retry initialization, because it can fail due to memory issues that may eventually be resolved.
Attachments
Patch (1.77 KB, patch)
2012-05-30 10:35 PDT, vollick
no flags
Patch (1.75 KB, patch)
2012-05-30 11:15 PDT, vollick
no flags
Patch (2.18 KB, patch)
2012-05-30 11:53 PDT, vollick
no flags
Patch (12.02 KB, patch)
2012-05-31 08:12 PDT, vollick
no flags
Patch (11.08 KB, patch)
2012-05-31 09:26 PDT, vollick
no flags
Patch (16.69 KB, patch)
2012-05-31 11:47 PDT, vollick
no flags
Patch (17.05 KB, patch)
2012-05-31 13:17 PDT, vollick
no flags
vollick
Comment 1 2012-05-30 10:35:21 PDT
James Robinson
Comment 2 2012-05-30 11:05:18 PDT
Comment on attachment 144862 [details] Patch If initializeLayerRenderer() fails then we should stop using the compositor completely and never hit the function again - initialization errors are fatal and permanent until the embedder attempts to instantiate compositing again.
James Robinson
Comment 3 2012-05-30 11:12:43 PDT
(In reply to comment #0) > Currently, in CCSingleThreadProxy::initializeLayerRenderer(), the reference to m_contextBeforeInitialization is released and passed to m_layerTreeHostImpl->initializeLayerRenderer(...), but if that call fails, then m_contextBeforeInitialization is null, so subsequent calls to CCSingleThreadProxy::initializeLayerRenderer() will cause a crash. It is reasonable to retry initialization, because it can fail due to memory issues that may eventually be resolved. The embedder should decide to retry by passing in a new context, the compositor shouldn't attempt to retry with the existing context.
vollick
Comment 4 2012-05-30 11:15:55 PDT
Created attachment 144875 [details] Patch (In reply to comment #3) > (In reply to comment #0) > > Currently, in CCSingleThreadProxy::initializeLayerRenderer(), the reference to m_contextBeforeInitialization is released and passed to m_layerTreeHostImpl->initializeLayerRenderer(...), but if that call fails, then m_contextBeforeInitialization is null, so subsequent calls to CCSingleThreadProxy::initializeLayerRenderer() will cause a crash. It is reasonable to retry initialization, because it can fail due to memory issues that may eventually be resolved. > > The embedder should decide to retry by passing in a new context, the compositor shouldn't attempt to retry with the existing context. Sg. If we fail with the first context. We won't try with it again.
James Robinson
Comment 5 2012-05-30 11:21:12 PDT
Comment on attachment 144875 [details] Patch This still feels fishy - nobody should be calling initializeLayerRenderer() if we returned false. Where's this call coming from?
James Robinson
Comment 6 2012-05-30 11:21:46 PDT
Comment on attachment 144875 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=144875&action=review > Source/WebCore/platform/graphics/chromium/cc/CCSingleThreadProxy.cpp:164 > ASSERT(m_contextBeforeInitialization); > { > DebugScopedSetImplThread impl; > - bool ok = m_layerTreeHostImpl->initializeLayerRenderer(m_contextBeforeInitialization.release(), UnthrottledUploader); > + bool ok = m_contextBeforeInitialization && m_layerTreeHostImpl->initializeLayerRenderer(m_contextBeforeInitialization.release(), UnthrottledUploader); Clearly somebody is confused about the expectations here - either the ASSERT() is wrong or the code is wrong
vollick
Comment 7 2012-05-30 11:53:18 PDT
Created attachment 144886 [details] Patch (In reply to comment #5) > (From update of attachment 144875 [details]) > This still feels fishy - nobody should be calling initializeLayerRenderer() if we returned false. Where's this call coming from? Someone started the animation timer before the layer renderer was initialized, and when it fires we land in this code. If the CCSingleThreadProxy is destroyed, then this timer is stopped, but it doesn't seems like that's happening, so I've put guard code in the timer callback to ensure that we only tick if we've got an initialized layer renderer.
Dana Jansens
Comment 8 2012-05-30 12:06:20 PDT
To bikeshed, this does feel like the wrong solution to me because ticking calls updateLayers which does in fact do the initialization. We could ensure that initialization can't be attempted twice, if that's the desired behaviour, in CCLTH::updateLayers, by having it early-out instead.
James Robinson
Comment 9 2012-05-30 12:51:35 PDT
(In reply to comment #7) > Created an attachment (id=144886) [details] > Patch > > (In reply to comment #5) > > (From update of attachment 144875 [details] [details]) > > This still feels fishy - nobody should be calling initializeLayerRenderer() if we returned false. Where's this call coming from? > Someone started the animation timer before the layer renderer was initialized, and when it fires we land in this code. > If the CCSingleThreadProxy is destroyed, then this timer is stopped, but it doesn't seems like that's happening I'd like to understand why this isn't happening. Can we repro?
James Robinson
Comment 10 2012-05-30 18:48:36 PDT
Comment on attachment 144886 [details] Patch R- until we have a better understanding of why the timer is still alive in this case
vollick
Comment 11 2012-05-31 06:22:20 PDT
Here's the chain of events: 1. An animation is added. This can happen before we attempt to initialize the layer renderer, and causes a timer to be set to schedule a composite. 2. We get around to initializing the layer renderer. More specifically: CCLayerTreeHost::initializeLayerRenderer() - calls CCSingleThreadProxy::initializeLayerRenderer() - when it fails, calls -> WebLayerTreeViewClientAdapter::didRecreateContext(false) -> WebViewImpl::didRebindGraphicsContext(false) -> WebViewImpl::setIsAcceleratedCompositingActive(false) *** NOTE: this does not cause WebViewImpl::m_layerTreeView to be deleted, nor, I think, should it. This means that the proxy is not destroyed nor is its timer stopped. -> RenderWidget::didDeactivateCompositor() -> (via IPC)RenderWidgetHostImpl::OnMsgDidActivateAcceleratedCompositing(false) -> RenderWidgetHostViewAura::OnAcceleratedCompositingStateChange() -> ui::Layer::set_scale_content(false) 3. The timer fires and indirectly tries to reinitialize the renderer (by trying to composite). It should not do this, a composite should only be attempted if we have a layer renderer.
vollick
Comment 12 2012-05-31 08:12:17 PDT
Created attachment 145096 [details] Patch CCLayerTreeHost::finishAllRendering is called iff we are shutting down accelerated compositing. If we are shutting down accelerated compositing, it makes sense to stop CCSingleThreadProxy's timer here so it never fires after we've switched to software mode. This required a minor change to finishAllRendering. Previously, CCLTH::finishAllRendering bailed early if we hadn't initialized the layer renderer (which foils the plan to stop the timer). Now, the proxy's know that this function may be called without an initialized layer renderer and have the appropriate checks. I've added a unit test to ensure that we do indeed stop the timer in this case.
vollick
Comment 13 2012-05-31 09:26:21 PDT
Created attachment 145103 [details] Patch Sorry for the spam. Removed some code that would have surely been contentious.
James Robinson
Comment 14 2012-05-31 10:29:52 PDT
Comment on attachment 145103 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=145103&action=review > Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.h:97 > + virtual void didStopAnimationTimer() { } This is an odd part of the API, especially since the notion of animation timer is CCSingleThreadProxy specific. Is this just a test hook? Why not simply have a test that calls finishAllRendering and then ASSERT()s that the timer doesn't fire? > Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.h:126 > + virtual bool initializeLayerRenderer(PassRefPtr<GraphicsContext3D>, TextureUploaderOption); Also a weird test hook - we can make initialization fail by passing a GraphicsContext3D in that's not useful (say, make the first makeContextCurrent() call return false) - that's a much more realistic test and we do it in other places > Source/WebCore/platform/graphics/chromium/cc/CCSingleThreadProxy.cpp:133 > + // We're done rendering and should not process any future animation events. I'd much prefer that we cancel the timer when initialization fails. The fact that WebViewImpl calls finishAllRendering() is a strange historical artifact of how shutdown used to work and isn't really a concern of the compositor - in fact I've been planning to delete that call.
vollick
Comment 15 2012-05-31 11:47:47 PDT
Created attachment 145124 [details] Patch (In reply to comment #14) > (From update of attachment 145103 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=145103&action=review > > > Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.h:97 > > + virtual void didStopAnimationTimer() { } > > This is an odd part of the API, especially since the notion of animation timer is CCSingleThreadProxy specific. Is this just a test hook? Why not simply have a test that calls finishAllRendering and then ASSERT()s that the timer doesn't fire? Done. > > > Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.h:126 > > + virtual bool initializeLayerRenderer(PassRefPtr<GraphicsContext3D>, TextureUploaderOption); > > Also a weird test hook - we can make initialization fail by passing a GraphicsContext3D in that's not useful (say, make the first makeContextCurrent() call return false) - that's a much more realistic test and we do it in other places Done. > > > Source/WebCore/platform/graphics/chromium/cc/CCSingleThreadProxy.cpp:133 > > + // We're done rendering and should not process any future animation events. > > I'd much prefer that we cancel the timer when initialization fails. The fact that WebViewImpl calls finishAllRendering() is a strange historical artifact of how shutdown used to work and isn't really a concern of the compositor - in fact I've been planning to delete that call. Done.
James Robinson
Comment 16 2012-05-31 12:34:48 PDT
Comment on attachment 145124 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=145124&action=review Looks good > Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp:1474 > + m_layerTreeHost->finishAllRendering(); We don't need this call any more, do we? > Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp:1475 > + endTestAfterDelay(CCSingleThreadProxy::animationTimerDelay() * 1000); I think it's worth having a comment here explaining why this value is chosen (since we're setting the timer against the animation timer). This will round down since it's truncating from a double to millis - might be safer to explicitly ceil() here so if the timer implementation does something else we can be sure we don't run before it.
vollick
Comment 17 2012-05-31 13:17:15 PDT
Created attachment 145143 [details] Patch (In reply to comment #16) > (From update of attachment 145124 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=145124&action=review > > Looks good > > > Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp:1474 > > + m_layerTreeHost->finishAllRendering(); > > We don't need this call any more, do we? Good point. Removed. > > > Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp:1475 > > + endTestAfterDelay(CCSingleThreadProxy::animationTimerDelay() * 1000); > > I think it's worth having a comment here explaining why this value is chosen (since we're setting the timer against the animation timer). This will round down since it's truncating from a double to millis - might be safer to explicitly ceil() here so if the timer implementation does something else we can be sure we don't run before it. Done.
WebKit Review Bot
Comment 18 2012-05-31 16:48:47 PDT
Comment on attachment 145143 [details] Patch Clearing flags on attachment: 145143 Committed r119173: <http://trac.webkit.org/changeset/119173>
WebKit Review Bot
Comment 19 2012-05-31 16:48:57 PDT
All reviewed patches have been landed. Closing bug.
Note You need to log in before you can comment on or make changes to this bug.