Mister Epic
Mister Epic

Reputation: 16733

Are GPU-bound animations still affected by CPU load?

I have created a compositing layer for a div with the following styles:

div {
  position: absolute;
  height: 50px;
  width: 50px;
  background: #900;
  top: 100px;
  left: 200px;
  will-change: transform;
  transform: translateZ(0);
}

I then apply an animation to it using the Web Animation API:

document.getElementById("box").animate(
  [
    { transform: 'rotate(0) translate3D(-50%, -50%, 0)' },
    { transform: 'rotate(360deg) translate3D(-50%, -50%, 0)' }
  ], {
    duration: 500,
    iterations: Infinity
  }
);

From my understanding, this animation is now being handled by the GPU and this layer is independent from other layers so the GPU can act upon on it in isolation without having to worry about calculating the geometry of other parts of the page.

What I don't understand is if I invoke a CPU intensive function, that animation will halt entirely while the function is running, and will pick up again when the function exits:

function mySlowFunction(baseNumber) {
    console.time('mySlowFunction');
    var result = 0; 
    for (var i = Math.pow(baseNumber, 10); i >= 0; i--) {       
        result += Math.atan(i) * Math.tan(i);
    };
    console.timeEnd('mySlowFunction');
    return result;
}

setTimeout(() => mySlowFunction(5), 3000);

Is there any way to prevent this?

https://jsfiddle.net/Lsmw85rv/4/

Upvotes: 4

Views: 304

Answers (1)

Kaiido
Kaiido

Reputation: 136717

Yes they may still get affected by CPU load.
The update the rendering algorithm is part of the Event Loop, so if somehow you do block the event loop, you also do block the rendering.

Now, implementors are encouraged to "spin the event loop" when they meet a long running code, so that UI can still be reactive (and non-js powered animations can keep running), but this is just an encouragement and all implementations don't do it the same way.

For instance, on my Firefox, I don't see any slowdown from your script, nor even from the more aggressive one below, while on my Chrome I can clearly see the rendering being blocked.

Now to avoid this, as has been said in comment, the real bullet proof solution would be to run your blocking script in a second thread using a Web Worker.

document.getElementById("box").animate(
  [{
      transform: 'rotate(0) translate3D(-50%, -50%, 0)'
    },
    {
      transform: 'rotate(360deg) translate3D(-50%, -50%, 0)'
    }
  ], {
    duration: 500,
    iterations: Infinity
  }
);

function mySlowFunction(baseNumber) {
  console.time('mySlowFunction');
  const now = performance.now();
  while (performance.now() - now < baseNumber * 1000);
  console.timeEnd('mySlowFunction');
}

setTimeout(() => mySlowFunction(3), 3000);
#box {
  position: absolute;
  height: 50px;
  width: 50px;
  background: #900;
  top: 100px;
  left: 200px;
  will-change: transform;
  transform: translateZ(0);
}
<div id="box"></div>

Upvotes: 2

Related Questions