RESOLVED FIXED 80629
[chromium] Canvas acceleration is not restored after context loss
https://bugs.webkit.org/show_bug.cgi?id=80629
Summary [chromium] Canvas acceleration is not restored after context loss
Stephen White
Reported 2012-03-08 12:59:37 PST
1) Load a page containing <canvas> in Chrome. 2) Check that acceleration is active (e.g., FPS meter). 3) Kill the GPU process. 4) Reload the page. 5) GPU acceleration is not restored. See also http://crbug.com/117394.
Attachments
Patch (2.06 KB, patch)
2012-03-08 13:35 PST, Stephen White
no flags
Patch (3.08 KB, patch)
2012-03-08 14:59 PST, Stephen White
no flags
Patch (3.67 KB, patch)
2012-03-08 15:44 PST, Stephen White
no flags
Patch (3.67 KB, patch)
2012-03-08 15:51 PST, Stephen White
no flags
Patch (3.67 KB, patch)
2012-03-08 15:56 PST, Stephen White
no flags
Patch (3.82 KB, patch)
2012-03-08 16:29 PST, Stephen White
no flags
Patch (14.37 KB, patch)
2012-03-12 10:56 PDT, Stephen White
jamesr: review+
Stephen White
Comment 1 2012-03-08 13:35:19 PST
Stephen White
Comment 2 2012-03-08 14:59:30 PST
James Robinson
Comment 3 2012-03-08 15:20:30 PST
Comment on attachment 130910 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=130910&action=review Would you mind (briefly) describing the semantics in SharedGraphicsContext3D.h? I believe the rules are something like: every caller can hold a ref for as long as they like to the returned context. they should periodically check to see if it's been lost. if the context has been lost, they should drop their ref and go ask SharedGraphicsContext3D for a new context. they may or may not get one. does that sound about right? > Source/WebCore/ChangeLog:9 > + Tested manually by killing the GPU process (no testing infrastructure > + to simulate lost context yet). have you seen LayoutTests/platform/chromium/compositing/lost-context-* ? > Source/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.cpp:49 > + if (m_context && !m_context->makeContextCurrent()) { are you sure that if we lose a context we can't make it current? i think this may need to be separate conditions if we do have a non-null context and we can't make it current, i don't think that means implies that getGraphicsResetStatusARB() is going to return something other than NO_ERROR - what if we can't allocate a command buffer at all? > Source/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.cpp:53 > + // If we still can't make it current, just give up. i think this function might be a little clearer if structured as: if (m_context && it's lost or we can't make it current) m_context.clear(); if (!m_context) m_context = new thingy return m_context.get()
Stephen White
Comment 4 2012-03-08 15:33:56 PST
(In reply to comment #3) > (From update of attachment 130910 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=130910&action=review > > Would you mind (briefly) describing the semantics in SharedGraphicsContext3D.h? I believe the rules are something like: every caller can hold a ref for as long as they like to the returned context. they should periodically check to see if it's been lost. if the context has been lost, they should drop their ref and go ask SharedGraphicsContext3D for a new context. they may or may not get one. does that sound about right? That sounds right. I'll put that in a comment. > > Source/WebCore/ChangeLog:9 > > + Tested manually by killing the GPU process (no testing infrastructure > > + to simulate lost context yet). > > have you seen LayoutTests/platform/chromium/compositing/lost-context-* ? Unfortunately, it's compositor specific. I tried plumbing through a similar loseCanvasContext() call in LayoutTestController, and it turned into kudzu that quickly spread to a ridiculous number of files. I think the right thing to do something at a lower level, perhaps something like this: https://chromiumcodereview.appspot.com/9609008/patch/19001/20007. > > > Source/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.cpp:49 > > + if (m_context && !m_context->makeContextCurrent()) { > > are you sure that if we lose a context we can't make it current? i think this may need to be separate conditions When I did that, things went all crashy-crashy. Not sure why... I'll investigate further. > if we do have a non-null context and we can't make it current, i don't think that means implies that getGraphicsResetStatusARB() is going to return something other than NO_ERROR - what if we can't allocate a command buffer at all? I thought GraphicsContext3D::create() would return NULL in that case? > > Source/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.cpp:53 > > + // If we still can't make it current, just give up. > > i think this function might be a little clearer if structured as: > > if (m_context && it's lost or we can't make it current) > m_context.clear(); > > if (!m_context) > m_context = new thingy > > return m_context.get() Better, but not quite right, since we still need to check if we can make it current the second time. Let me riff on it..
Stephen White
Comment 5 2012-03-08 15:44:38 PST
James Robinson
Comment 6 2012-03-08 15:46:54 PST
(In reply to comment #4) > > if we do have a non-null context and we can't make it current, i don't think that means implies that getGraphicsResetStatusARB() is going to return something other than NO_ERROR - what if we can't allocate a command buffer at all? > > I thought GraphicsContext3D::create() would return NULL in that case? The initialization is in two phases. The first phase tries to establish a renderer<->GPU IPC channel if it doesn't exist and returns NULL if this fails or if it doesn't like something about the attributes. The second phase of initialization takes place on the first makeContextCurrent() call and it's what actually creates the GLContext, command buffer, etc. The second phase also binds you to a particular thread. GraphicsContext3D::create() -> WebGraphicsContext3DCommandBufferImpl::Initialize() first makeContextCurrent() Call -> WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL() If we can establish a GPU channel, but we can't actually spin up a command buffer (maybe we're out of virtual address space to map the command buffer in), then GraphicsContext3D::create will return something non-NULL and the first makeContextCurrent() will return false.
Stephen White
Comment 7 2012-03-08 15:49:46 PST
Comment on attachment 130925 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=130925&action=review > Source/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.h:38 > + // The caller may ref on this pointer as long as they like. Whoops, that grammar is weird on. Fixing on...
James Robinson
Comment 8 2012-03-08 15:50:03 PST
Comment on attachment 130925 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=130925&action=review R=me but looking at the code I'm pretty sure you gotta flip the makeCurrent and extension query around or it'll crash. Try inducing a failure in MaybeInitializeGL() to test that theory out > Source/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.cpp:40 > + if (m_context && ((m_context->getExtensions()->getGraphicsResetStatusARB() != GraphicsContext3D::NO_ERROR) || !m_context->makeContextCurrent())) nit: don't we have to make the context current before querying the extensions? i think these have to be flipped around. obviously if we can't make the context current we shouldn't try to look at its extensions bits either.
Stephen White
Comment 9 2012-03-08 15:51:23 PST
Stephen White
Comment 10 2012-03-08 15:56:36 PST
James Robinson
Comment 11 2012-03-08 16:03:07 PST
Comment on attachment 130928 [details] Patch R=me. Definitely want to kick the tires a bit manually to flush stuff out - maybe make every Nth context initialization fail in different places and kill the GPU process a whole bunch.
Stephen White
Comment 12 2012-03-08 16:29:38 PST
Stephen White
Comment 13 2012-03-12 10:47:23 PDT
Further manual testing (by forcing the return of NULL on the 2nd call to SharedGraphicsContext3D::get()) revealed that other parts of the code were not yet ready to handle this case. In particular, checking for context loss in Canvas2DLayerChromium::drawsContext() was crashing (but sadly, not in Debug, so I'm not sure what's going on there -- the pointer should still be valid). I've put in the same change as in WebGLLayerChromium: check for context loss in Canvas2DLayerChromium::paintContentsIfDirty(), and check that flag in Canvas2DLayerChromium::setNeedsDisplayRect(). In order to make things clearer, I'm also going to return an actual PassRefPtr from SharedGraphicsContext3D::get(), to force the caller to evaluate ownership. This required the patch to get a little bigger, as I audited the bare ptr usage throughout the code: Canvas2DLayerChromium will own the ref used for canvas 2D. PlatformContextSkia: removed the bare context ptr and replaced it with a bool, since that's how it was being used anyway. AcceleratedDeviceContext will still own a bare ptr, since its lifetime is as long as the Canvas2DLayerChromium (and in fact, I think we can will rework this callback to hold only a C2DLC pointer, instead of GC3D, and perhaps be owned by C2DLC instead, to ensure lifetime -- Justin to follow up). RateLimiter also holds a bare pointer, so we must ensure we call stopRateLimiter() it when the CLC2D goes away (the same is done in WebGL).
Stephen White
Comment 14 2012-03-12 10:53:19 PDT
(In reply to comment #13) > This required the patch to get a little bigger, as I audited the bare ptr usage throughout the code: Note: one place I wasn't sure about is the new call WebViewImpl::sharedGraphicsContext3D(). As I understand it, this will only be used to set up a texture for Pepper, so as long as the pointer isn't stored, a bare ptr is fine. If it is, we should probably change this to be ref'ed too.
Stephen White
Comment 15 2012-03-12 10:56:48 PDT
James Robinson
Comment 16 2012-03-12 11:23:38 PDT
(In reply to comment #14) > (In reply to comment #13) > > This required the patch to get a little bigger, as I audited the bare ptr usage throughout the code: > > Note: one place I wasn't sure about is the new call WebViewImpl::sharedGraphicsContext3D(). As I understand it, this will only be used to set up a texture for Pepper, so as long as the pointer isn't stored, a bare ptr is fine. Correct - callers don't have ownership of this pointer and shouldn't be storing it. It only needs to be valid for the callstack.
James Robinson
Comment 17 2012-03-12 14:21:30 PDT
Comment on attachment 131350 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=131350&action=review Looks good! > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurface.cpp:228 > + return CCRenderSurfaceFilters::apply(m_filters, m_contentsTexture->textureId(), m_contentRect.size(), SharedGraphicsContext3D::get().get()); get().get() - nice. need some more data().data().data() up in here > Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp:113 > data->m_platformLayer = Canvas2DLayerChromium::create(context3D, size); i think i'd find the ownership a bit easier to follow if this was a .release() call, just to make it clear that ownership of the local is being transferred to the Canvas2DLayerChromium and the layer was responsible for holding the context alive. i couldn't tell when initially looking at this why this function was taking a reference in a local and who was holding the ref after this function returned.
Stephen White
Comment 18 2012-03-12 14:46:31 PDT
Comment on attachment 131350 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=131350&action=review > Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp:113 > data->m_platformLayer = Canvas2DLayerChromium::create(context3D, size); Cool, I didn't know about RefPtr::release()! Will fix on landing.
Stephen White
Comment 19 2012-03-12 14:52:32 PDT
Note You need to log in before you can comment on or make changes to this bug.