Bug 208876 - [GPU Process] RemoteImageBufferProxy should ensure the state stack of the context is cleared before destruction
Summary: [GPU Process] RemoteImageBufferProxy should ensure the state stack of the con...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Canvas (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Said Abou-Hallawa
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2020-03-10 12:40 PDT by Said Abou-Hallawa
Modified: 2020-09-29 16:39 PDT (History)
6 users (show)

See Also:


Attachments
Patch (2.40 KB, patch)
2020-03-10 12:47 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (2.46 KB, patch)
2020-03-12 10:54 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Reupload Said’s patch (2.74 KB, patch)
2020-09-29 14:46 PDT, Wenson Hsieh
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Said Abou-Hallawa 2020-03-10 12:40:12 PDT
For any reason, if the GraphicsContext::save() did not get a matching GraphicsContext::restore(), the destructor of GraphicsContext will assert: the state stack is not empty. In some cases, the GPU Process may terminate before receiving the matching restore() for the RemoteImageBufferProxy. This can depend on when CanvasRenderingContext2D.save() and its matching restore() are called.

This causes many failures in the canvas layout tests. Currently running 'run-webkit-tests --debug --no-retry LayoutTests/canvas/ LayoutTests/fast/canvas  --internal-feature RenderCanvasInGPUProcessEnabled' results 255 failures.
Comment 1 Said Abou-Hallawa 2020-03-10 12:47:10 PDT
Created attachment 393173 [details]
Patch
Comment 2 Said Abou-Hallawa 2020-03-10 12:48:27 PDT
This patch lowers the number of failures in the above command from 255 to 100 failures.
Comment 3 Myles C. Maxfield 2020-03-11 15:54:48 PDT
Comment on attachment 393173 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=393173&action=review

> Source/WebKit/GPUProcess/graphics/RemoteImageBufferProxy.h:58
> +        // destruction, to avoid assertions in the GraphicsContext dtor.

Does it make sense to delete the assertion instead? The purpose of assertions is to find bugs, not to force us to do busywork.
Comment 4 Said Abou-Hallawa 2020-03-11 20:32:58 PDT
Comment on attachment 393173 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=393173&action=review

>> Source/WebKit/GPUProcess/graphics/RemoteImageBufferProxy.h:58
>> +        // destruction, to avoid assertions in the GraphicsContext dtor.
> 
> Does it make sense to delete the assertion instead? The purpose of assertions is to find bugs, not to force us to do busywork.

I think the assertion is important to have. I hit this assertion only when running layout tests. When reloading the same page or load different pages, I see we are doing the right thing: the restore commands are pushed from the WebProcess to the GPUProcess and the loop below, does not execute even once. This is the same we do in CanvasRenderingContext2DBase::unwindStateStack().

I can do the same thing that other function is doing:

#if ASSERT_ENABLED
    while (context().stackSize())
        context().restore();
#endif
Comment 5 Simon Fraser (smfr) 2020-03-11 20:42:04 PDT
Comment on attachment 393173 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=393173&action=review

>>> Source/WebKit/GPUProcess/graphics/RemoteImageBufferProxy.h:58
>>> +        // destruction, to avoid assertions in the GraphicsContext dtor.
>> 
>> Does it make sense to delete the assertion instead? The purpose of assertions is to find bugs, not to force us to do busywork.
> 
> I think the assertion is important to have. I hit this assertion only when running layout tests. When reloading the same page or load different pages, I see we are doing the right thing: the restore commands are pushed from the WebProcess to the GPUProcess and the loop below, does not execute even once. This is the same we do in CanvasRenderingContext2DBase::unwindStateStack().
> 
> I can do the same thing that other function is doing:
> 
> #if ASSERT_ENABLED
>     while (context().stackSize())
>         context().restore();
> #endif

I don't think we should have different behavior in debug builds vs. release. Either always clear the stack, or remove the assertion.
Comment 6 Said Abou-Hallawa 2020-03-12 10:54:21 PDT
Created attachment 393389 [details]
Patch
Comment 7 Simon Fraser (smfr) 2020-03-12 10:57:38 PDT
Comment on attachment 393389 [details]
Patch

We should not have different behavior in debug.
Comment 8 Said Abou-Hallawa 2020-03-12 11:09:57 PDT
Comment on attachment 393173 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=393173&action=review

>>>> Source/WebKit/GPUProcess/graphics/RemoteImageBufferProxy.h:58
>>>> +        // destruction, to avoid assertions in the GraphicsContext dtor.
>>> 
>>> Does it make sense to delete the assertion instead? The purpose of assertions is to find bugs, not to force us to do busywork.
>> 
>> I think the assertion is important to have. I hit this assertion only when running layout tests. When reloading the same page or load different pages, I see we are doing the right thing: the restore commands are pushed from the WebProcess to the GPUProcess and the loop below, does not execute even once. This is the same we do in CanvasRenderingContext2DBase::unwindStateStack().
>> 
>> I can do the same thing that other function is doing:
>> 
>> #if ASSERT_ENABLED
>>     while (context().stackSize())
>>         context().restore();
>> #endif
> 
> I don't think we should have different behavior in debug builds vs. release. Either always clear the stack, or remove the assertion.

-- I think the assertion is very useful for the render tree rendering where we control the save/restore. This assertion will detect if we miss a restore.
-- In CanvasRenderingContext2DBase, the save and restore are controlled by the javascript. If the javascript missed a restore, the assertion should not fire.
-- In RemoteImageBufferProxy, the restore might be lost because the connection between the GPUProcess and the WebProcess is terminated. In this case the assertion should not fire.

In the three cases the code to unwind the state stack and check the state stack is empty is only under #if ASSERT_ENABLED.

If this is not the right approach, I think we can do one of the following:

1. Remove the assertion and CanvasRenderingContext2DBase::unwindStateStack() also.
2. Add a flag to GraphicsContext like m_externalSaveRestoreControl. It will be true for the ImageBuffer context of CanvasRenderingContext2DBase and for the graphics context of RemoteImageBufferProxy. In the destructor of GraphicsContext. We can assert

ASSERT_IMPLIES(!m_externalSaveRestoreControl, m_stack.isEmpty());
Comment 9 Wenson Hsieh 2020-09-29 14:46:26 PDT
Created attachment 410054 [details]
Reupload Said’s patch
Comment 10 Tim Horton 2020-09-29 16:31:50 PDT
Comment on attachment 410054 [details]
Reupload Said’s patch

As I said on Slack... "seems a little funky, but OK".
Comment 11 EWS 2020-09-29 16:38:23 PDT
Committed r267772: <https://trac.webkit.org/changeset/267772>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 410054 [details].
Comment 12 Radar WebKit Bug Importer 2020-09-29 16:39:16 PDT
<rdar://problem/69769208>