Search code examples
javascriptgoogle-chromecanvaswebgldevtools

Why I have "Update Layer Tree" and "Composite Layer" tasks after each requestAnimationFrame handler?


I'm trying to write high-performance 60 - 140fps rendering code, but I noticed that after each requestAnimationFrame Chrome calls "Update Layer Tree" and "Composite Layer" tasks that summary takes more scripting time than my rendering function. I don't understand why does this happen? I don't update any css or resize elements so I don't think that my code requires some additional recalculations.

dev-tools screenshot

You can find that the same thing happens in this example: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations


Solution

  • Why I have “Update Layer Tree” and “Composite Layer” tasks after each requestAnimationFrame handler?

    Beacuse the browser needs to do those tasks. "Update Layer Tree" may do nothing. If you've changed no HTML and no CSS and there are no CSS animations running the code might be a simple as

    function UpdateLayerTree() {
      performance.start("Update Layer Tree");
      if (layerTreeNeedsUpdating) {
         ... update tree ...
      }
      performance.end();
    }
    

    "Composite Layers" is how your WebGL gets on the screen. The browser, when GPU accelerated, stores various parts of the webpage elements as textures. A WebGL canvas has its own texture. It then draws all those textures on to the browser window. Unless nothing has changed, you haven't changed any HTML nor CSS nor drawn to your canvas no CSS animations no video no gifs) then there will always be a "Composite Layers" step. And like above, it might be implemented as

    function CompositeLayers() {
      performance.start("Composite Layers");
      if (needToRedrawBecauseContentChanged) {
         ... composite layers ...
      }
      performance.end();
    }
    

    Checking, If write code like this

    const gl = document.querySelector('canvas').getContext('webgl');
    
    function render(time) {
      requestAnimationFrame(render);
    }
    requestAnimationFrame(render);
    <canvas></canvas>

    I see those same two processes exist but I also see if I affect the canvas

    const gl = document.querySelector('canvas').getContext('webgl');
    
    function render(time) {
      gl.clearColor(time / 100 % 1, 0, 0, 1);
      gl.clear(gl.COLOR_BUFFER_BIT);
      requestAnimationFrame(render);
    }
    requestAnimationFrame(render);
    <canvas></canvas>

    They go up which suggests they are implemented as above, checking if they have any work to do. I tested on a fairly complicated page though which is not updating any html but has hundreds of elements and several iframes with live code editors in them (6319 elements) and I still saw those 2 tasks as not taking all that long, under 0.40ms each.