Bug 90630 - Drawing to accelerated 2D canvas causes compositor to recompute layer tree
Summary: Drawing to accelerated 2D canvas causes compositor to recompute layer tree
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: New Bugs (show other bugs)
Version: 528+ (Nightly build)
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Justin Novosad
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-07-05 13:36 PDT by Justin Novosad
Modified: 2012-07-06 10:47 PDT (History)
7 users (show)

See Also:


Attachments
Patch (2.91 KB, patch)
2012-07-05 13:52 PDT, Justin Novosad
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Justin Novosad 2012-07-05 13:36:08 PDT
Drawing to accelerated 2D canvas causes compositor to recompute layer tree
Comment 1 Justin Novosad 2012-07-05 13:52:07 PDT
Created attachment 150979 [details]
Patch
Comment 2 Justin Novosad 2012-07-05 13:57:31 PDT
A key piece of code that is relevant to this patch is method RenderLayer::contentChanged (not modified by this patch). This is the method that was triggering the rebuild of the compositor tree when a 2D canvas is drawn to.
Comment 3 Justin Novosad 2012-07-05 14:06:57 PDT
kbr expressed concern that we must make sure that accelerated canvas layers will still trigger the compositor in all situations with this change.  I have verified that this is the case: RenderLayer::addChild will trigger the compositor (via RenderLayer::dirtyZOrderLists) when a new canvas is added.
Comment 4 Simon Fraser (smfr) 2012-07-05 14:09:02 PDT
What is the code path that causes the full layer rebuild? Is it via RenderLayer::contentChanged()?
Comment 5 Justin Novosad 2012-07-05 14:37:44 PDT
(In reply to comment #4)
> What is the code path that causes the full layer rebuild? Is it via RenderLayer::contentChanged()?

Yes.

All the methods in CanvasRenderingContext2D that draw something to the canvas end up calling CanvasRenderingContext2D::didDraw, which calls RenderBoxModelObject::contentChanged(), which calls RenderLAyer::contentChanged()
Comment 6 Simon Fraser (smfr) 2012-07-05 14:43:49 PDT
But that should only cause a layer rebuild if updateLayerCompositingState() returns true. Does it?
Comment 7 Justin Novosad 2012-07-06 08:28:22 PDT
(In reply to comment #6)
> But that should only cause a layer rebuild if updateLayerCompositingState() returns true. Does it?

updateLayerCompositingState is returning true because RenderLayerBacking::updateGraphicsLayerConfiguration() is returning true because of this bit of code:

else if (isAcceleratedCanvas(renderer)) {
    HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(renderer->node());
    if (CanvasRenderingContext* context = canvas->renderingContext())
        m_graphicsLayer->setContentsToCanvas(context->platformLayer());
    layerConfigChanged = true;
}

BTW, an important benefit of this patch is that will bypass the call to
updateLayerCompositingState which was a hot spot that I identified in a profiling experiment. When rendering a whole bunch of small primitives in a tight loop, all the calls to updateLayerCompositingState (can be thousands per frame) are often a much heavier burden than the compositor tree rebuild. In some experiments, I've seen up to 20% of CPU time being spent there.
Comment 8 Simon Fraser (smfr) 2012-07-06 08:52:49 PDT
(In reply to comment #7)
> (In reply to comment #6)
> > But that should only cause a layer rebuild if updateLayerCompositingState() returns true. Does it?
> 
> updateLayerCompositingState is returning true because RenderLayerBacking::updateGraphicsLayerConfiguration() is returning true because of this bit of code:
> 
> else if (isAcceleratedCanvas(renderer)) {
>     HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(renderer->node());
>     if (CanvasRenderingContext* context = canvas->renderingContext())
>         m_graphicsLayer->setContentsToCanvas(context->platformLayer());
>     layerConfigChanged = true;
> }

OK, for some reason Mac doesn't hit this code. I think your original patch is OK.

> BTW, an important benefit of this patch is that will bypass the call to
> updateLayerCompositingState which was a hot spot that I identified in a profiling experiment. When rendering a whole bunch of small primitives in a tight loop, all the calls to updateLayerCompositingState (can be thousands per frame) are often a much heavier burden than the compositor tree rebuild. In some experiments, I've seen up to 20% of CPU time being spent there.

Please file a new bug on that.
Comment 9 Justin Novosad 2012-07-06 10:16:50 PDT
> 
> Please file a new bug on that.

Done.
https://bugs.webkit.org/show_bug.cgi?id=90695
Comment 10 Simon Fraser (smfr) 2012-07-06 10:23:15 PDT
Comment on attachment 150979 [details]
Patch

What is the code path that causes the full layer rebuild? Is it via RenderLayer::contentChanged()?
Comment 11 Simon Fraser (smfr) 2012-07-06 10:23:34 PDT
> What is the code path that causes the full layer rebuild? Is it via RenderLayer::contentChanged()?
Ignore that comment.
Comment 12 WebKit Review Bot 2012-07-06 10:47:51 PDT
Comment on attachment 150979 [details]
Patch

Clearing flags on attachment: 150979

Committed r121987: <http://trac.webkit.org/changeset/121987>
Comment 13 WebKit Review Bot 2012-07-06 10:47:56 PDT
All reviewed patches have been landed.  Closing bug.