Canvas sometimes freezes with requestAnimationFrame and setTimeout |
||||||||||
Issue description
Chrome Version: Version 69.0.3497.100 (Official Build) (64-bit)
OS: macOS 10.14 Mojave
What steps will reproduce the problem?
(1) Open the below HTML
<!DOCTYPE html>
<canvas id="canvas" width="512" height="512"></canvas>
<script>
window.addEventListener('load', () => {
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl');
gl.viewport(0, 0, 512, 512);
var i = 0;
let f = () => {
setTimeout(() => {
i++
const c = (i % 64) / 64
gl.clearColor(c, c, c, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
requestAnimationFrame(f);
}, 0);
};
f();
});
</script>
(2) Switch the desktops several times (this might require 10 or more switching to reproduce)
What is the expected result?
The canvas never stops updating.
What happens instead?
The canvas freezes. The JavaScript doesn't stop but only the rendering stops.
Without setTimeout, it looks like the canvas never freezes.
Please use labels and text to provide additional information.
On 10.13 High Sierra, probably I could not reproduce this.
If this is a regression (i.e., worked before), please consider using the
bisect tool (https://www.chromium.org/developers/bisect-builds-py) to help
us identify the root cause and more rapidly triage the issue.
For graphics-related bugs, please copy/paste the contents of the about:gpu
page at the end of this report.
,
Oct 15
Attached the profile when the canvas freezes. JavaScript continues to run but frames are not updated.
,
Oct 15
Can reproduce on a dual-GPU MacBook Pro with AMD and Intel GPUs running 10.14 by putting one Chrome window with this test on one Space (via Mission Control), another Chrome window on another Space, and switching rapidly back and forth between the Spaces. I was able to reproduce this (once, with more difficulty) on 10.13, so this is not an issue completely new to 10.14 and doesn't appear to be a new regression in Chrome - although I haven't tried searching back to see if this was working reliably at some point. Submitter: how did you find this? Is real-world content affected by this? I wasn't able to reproduce this with either a sample using 2D canvas (freeze-canvas.html), or a WebGL sample that didn't include both setTimeout and requestAnimationFrame (freeze-webgl-no-settimeout.html). (All three variants, and about:gpu from the machine, are attached.) Most applications should be using solely requestAnimationFrame so it's not clear to me how widespread an issue this is and whether it warrants P1.
,
Oct 15
It's not DescheduleUntilFinishedCHROMIUM - that mechanism appears to be working correctly, and the command buffer seems to be getting rescheduled properly. Setting a breakpoint in DevTools, and resuming, causes the canvas to start updating again correctly.
,
Oct 16
When this happens, DrawingBuffer::PrepareTransferableResource stops getting called, so the cc::TextureLayer containing the WebGL back buffer's texture stops asking for updates. https://chromium-review.googlesource.com/1282225 adds some logging in this area. When the problem happens, this is the sequence of calls: [1532:775:1015/165147.560038:ERROR:webgl_rendering_context_base.cc(1352)] WebGLRenderingContextBase::MarkContextChanged already-dirty early-out [1532:775:1015/165147.560077:ERROR:webgl_rendering_context_base.cc(1375)] WebGLRenderingContextBase::MarkContextChanged full invalidation [1532:775:1015/165147.560149:ERROR:webgl_rendering_context_base.cc(1404)] WebGLRenderingContextBase::FinalizeFrame [1532:775:1015/165147.575550:ERROR:drawing_buffer.cc(309)] DrawingBuffer::PrepareTransferableResource [1532:775:1015/165147.576259:ERROR:webgl_rendering_context_base.cc(1352)] WebGLRenderingContextBase::MarkContextChanged already-dirty early-out [1532:775:1015/165147.576295:ERROR:webgl_rendering_context_base.cc(1375)] WebGLRenderingContextBase::MarkContextChanged full invalidation [1532:775:1015/165147.576357:ERROR:webgl_rendering_context_base.cc(1404)] WebGLRenderingContextBase::FinalizeFrame [1532:775:1015/165147.595715:ERROR:drawing_buffer.cc(309)] DrawingBuffer::PrepareTransferableResource [1532:775:1015/165147.596544:ERROR:webgl_rendering_context_base.cc(1352)] WebGLRenderingContextBase::MarkContextChanged already-dirty early-out [1532:775:1015/165147.596580:ERROR:webgl_rendering_context_base.cc(1375)] WebGLRenderingContextBase::MarkContextChanged full invalidation [1532:775:1015/165147.596643:ERROR:webgl_rendering_context_base.cc(1404)] WebGLRenderingContextBase::FinalizeFrame [1532:775:1015/165147.879562:ERROR:webgl_rendering_context_base.cc(1352)] WebGLRenderingContextBase::MarkContextChanged already-dirty early-out [1532:775:1015/165147.893315:ERROR:webgl_rendering_context_base.cc(1352)] WebGLRenderingContextBase::MarkContextChanged already-dirty early-out [1532:775:1015/165147.915791:ERROR:webgl_rendering_context_base.cc(1352)] WebGLRenderingContextBase::MarkContextChanged already-dirty early-out [1532:775:1015/165147.932169:ERROR:webgl_rendering_context_base.cc(1352)] WebGLRenderingContextBase::MarkContextChanged already-dirty early-out [1532:775:1015/165147.949040:ERROR:webgl_rendering_context_base.cc(1352)] WebGLRenderingContextBase::MarkContextChanged already-dirty early-out [1532:775:1015/165147.966331:ERROR:webgl_rendering_context_base.cc(1352)] WebGLRenderingContextBase::MarkContextChanged already-dirty early-out [1532:775:1015/165147.983216:ERROR:webgl_rendering_context_base.cc(1352)] WebGLRenderingContextBase::MarkContextChanged already-dirty early-out ... Basically, one call to DrawingBuffer::PrepareTransferableResource is skipped, and this makes the WebGL context think that it never needs to notify the outside world again that it's produced a frame. The WebGL context attempts to optimize things so that back-to-back draw calls don't send dirty notifications all the way up the stack. It looks like perhaps the contract is that if FinalizeFrame is called, the rendering context must assume that it must notify the outside world about future draw calls which have caused it to become dirty.
,
Oct 16
https://chromium-review.googlesource.com/1281963 up for review simplifying these dirty notifications and fixing the bug.
,
Oct 16
> Submitter: how did you find this? Is real-world content affected by this? I found this in my game with Go/Wasm + WebGL. Go/Wasm runtime uses setTimeout to emulate concurrency even if Go users don't want to do. A callback set at rAF is called via setTimeout since all callbacks are treated in a different special goroutine: https://golang.org/pkg/syscall/js/?GOOS=js&GOARCH=wasm#NewCallback Thus I don't think it is a major problem so far in the real world, but this will be a serious problem when Go/Wasm game development gets more popular.
,
Oct 16
Thank you for taking a very quick look!
,
Oct 17
The following revision refers to this bug: https://chromium.googlesource.com/chromium/src.git/+/a614021bc653b35bef12c80407b2c3b92dad3839 commit a614021bc653b35bef12c80407b2c3b92dad3839 Author: Kenneth Russell <kbr@chromium.org> Date: Wed Oct 17 21:12:51 2018 Simplify WebGL dirty notifications. The WebGL context attempts to optimize dirty notifications to the canvas and compositor, but the logic was fragile if the compositor skips a frame due to policy decisions. There were two almost-redundant state variables governing this. Remove animation_frame_in_progress_, and reuse marked_canvas_dirty_ for its purpose, clearing marked_canvas_dirty_ in FinalizeFrame. Verified with the test case from the bug and switching spaces 30 times on macOS Mojave. Bug: 895423 Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel Change-Id: I456194746a1d20f4f2874b13c6cf6ba54dcf4ea1 Reviewed-on: https://chromium-review.googlesource.com/c/1281963 Reviewed-by: Kai Ninomiya <kainino@chromium.org> Commit-Queue: Kenneth Russell <kbr@chromium.org> Cr-Commit-Position: refs/heads/master@{#600553} [modify] https://crrev.com/a614021bc653b35bef12c80407b2c3b92dad3839/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc [modify] https://crrev.com/a614021bc653b35bef12c80407b2c3b92dad3839/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
,
Oct 18
This should be fixed by the CL above. I hope it doesn't result in performance regressions for OffscreenCanvas.
,
Oct 18
Tried testing the issue on the build without fix #69.0.3497.100 using Mac OS 10.13.3 as the primary monitor and Ubuntu 17.10 as secondary monitor by following the below steps. Steps: ===== 1.Launched chrome. 2.Copied the whole code snippet from comment#0(Step 1) and saved with ".html" extension. 3.Opened the HTML file and switched between the two desktops several times. 4.Unable to see the canvas getting frozen. Attached screencast and file for reference. @Kenneth Russell: Could you please review attached screencast and let us know if anything is being missed from our end and help us in verifying the fix on the latest M-72 build. Thanks.!
,
Oct 19
swarnasree.mukkala@: to reproduce the original bug you need to be running macOS 10.14, and create two Spaces (three-finger swipe up on trackpad). Put one browser window containing the test case on one screen and another browser window on the other. Then use two-finger swipe back and forth to switch between the two Spaces.
,
Oct 22
,
Oct 30
,
Oct 30
The following revision refers to this bug: https://chromium.googlesource.com/chromium/src.git/+/b0d076ed460fc404ffbec486a01c0d5e535aea85 commit b0d076ed460fc404ffbec486a01c0d5e535aea85 Author: Kenneth Russell <kbr@chromium.org> Date: Tue Oct 30 19:52:13 2018 Simplify WebGL dirty notifications. The WebGL context attempts to optimize dirty notifications to the canvas and compositor, but the logic was fragile if the compositor skips a frame due to policy decisions. There were two almost-redundant state variables governing this. Remove animation_frame_in_progress_, and reuse marked_canvas_dirty_ for its purpose, clearing marked_canvas_dirty_ in FinalizeFrame. Verified with the test case from the bug and switching spaces 30 times on macOS Mojave. TBR=kbr@chromium.org (cherry picked from commit a614021bc653b35bef12c80407b2c3b92dad3839) Bug: 895423 , 895528 Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel Change-Id: I456194746a1d20f4f2874b13c6cf6ba54dcf4ea1 Reviewed-on: https://chromium-review.googlesource.com/c/1281963 Reviewed-by: Kai Ninomiya <kainino@chromium.org> Commit-Queue: Kenneth Russell <kbr@chromium.org> Cr-Original-Commit-Position: refs/heads/master@{#600553} Reviewed-on: https://chromium-review.googlesource.com/c/1308666 Reviewed-by: Kenneth Russell <kbr@chromium.org> Cr-Commit-Position: refs/branch-heads/3578@{#414} Cr-Branched-From: 4226ddf99103e493d7afb23a4c7902ee496108b6-refs/heads/master@{#599034} [modify] https://crrev.com/b0d076ed460fc404ffbec486a01c0d5e535aea85/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc [modify] https://crrev.com/b0d076ed460fc404ffbec486a01c0d5e535aea85/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
,
Oct 30
The following revision refers to this bug: https://chromium.googlesource.com/chromium/src.git/+/b0d076ed460fc404ffbec486a01c0d5e535aea85 Commit: b0d076ed460fc404ffbec486a01c0d5e535aea85 Author: kbr@chromium.org Commiter: kbr@chromium.org Date: 2018-10-30 19:52:13 +0000 UTC Simplify WebGL dirty notifications. The WebGL context attempts to optimize dirty notifications to the canvas and compositor, but the logic was fragile if the compositor skips a frame due to policy decisions. There were two almost-redundant state variables governing this. Remove animation_frame_in_progress_, and reuse marked_canvas_dirty_ for its purpose, clearing marked_canvas_dirty_ in FinalizeFrame. Verified with the test case from the bug and switching spaces 30 times on macOS Mojave. TBR=kbr@chromium.org (cherry picked from commit a614021bc653b35bef12c80407b2c3b92dad3839) Bug: 895423 , 895528 Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel Change-Id: I456194746a1d20f4f2874b13c6cf6ba54dcf4ea1 Reviewed-on: https://chromium-review.googlesource.com/c/1281963 Reviewed-by: Kai Ninomiya <kainino@chromium.org> Commit-Queue: Kenneth Russell <kbr@chromium.org> Cr-Original-Commit-Position: refs/heads/master@{#600553} Reviewed-on: https://chromium-review.googlesource.com/c/1308666 Reviewed-by: Kenneth Russell <kbr@chromium.org> Cr-Commit-Position: refs/branch-heads/3578@{#414} Cr-Branched-From: 4226ddf99103e493d7afb23a4c7902ee496108b6-refs/heads/master@{#599034}
,
Oct 31
Tried testing the issue on reported chrome version #69.0.3497.100 using Mac 10.14 by following below steps, but could not reproduce the issue. Note: Tried with freeze-canvas2d.html and 895423.html files. Steps: ===== 1.Launched chrome. 2.Opened freeze-canvas2d.html on one Space, another Chrome window on another Space. 3.By using the two finger back and forth switched between the spaces rapidly, observed that the canvas did not freeze (tried more than 30 times by switching both desktops). Attached screencast for reference. @kenneth: As the issue is not getting reproduced from TE end, hence request you to help us in verifying the fix from your end. Thanks.!
,
Oct 31
Verified fixed on 10.14 with 71.0.3578.30 (Official Build) beta (64-bit). Test case from c#0 above survives 30 desktop switches with this version, and froze within 5 iterations on 70.0.3538.77. |
||||||||||
►
Sign in to add a comment |
||||||||||
Comment 1 by hajimehoshi@chromium.org
, Oct 15