Bug 154819 - WebGL canvas resumes at incorrect size/scale
Summary: WebGL canvas resumes at incorrect size/scale
Status: NEW
Alias: None
Product: WebKit
Classification: Unclassified
Component: Canvas (show other bugs)
Version: Safari 9
Hardware: iPhone / iPad iOS 9.2
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2016-02-29 09:15 PST by Ashley Gullen
Modified: 2016-04-26 10:38 PDT (History)
5 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Ashley Gullen 2016-02-29 09:15:01 PST
URL: http://www.scirra.com/labs/bugs/iosc2bug/

Steps to reproduce:

1. Visit the URL and observe the content running in Safari
2. Press the device power button to put it to sleep
3. Wake it up again and unlock it
4. Observe the content running in Safari again

Observed result:

The canvas (WebGL rendered) restores at a strange size. It appears the canvas has become much larger, and inside the Safari viewport only the top-left area of the canvas is visible. The content is however still running.

Expected result:

Canvas to resume at same size it was before.


We are developers of the widely-used HTML5 game engine Construct 2. This bug affects all Construct 2 content. We have found a workaround: we have a setSize() method which is called in the resize event. If we ignore resize events and simply poll window.innerWidth and window.innerHeight every requestAnimationFrame, and call the same setSize() method when this polling notices them changing, it appears to work. You can see this approach used here: http://www.scirra.com/labs/bugs/iosc2bugworkaround/
We will likely ship this workaround shortly, but this does not fix all existing Construct 2 content on the web.

I've tried to produce a more minimal test case. I'm afraid I haven't been able to do this, but along the way filed bug 154815 and bug 154816 which may be related; in particular bug 154816 involves firing incorrect resize events when resuming. I speculate that trying to resize the WebGL canvas in this incorrectly-fired resize event causes Safari to get confused about how to display it.

Not all devices are affected - of the devices I have access to:

iPad Pro: affected
iPad Air 2: affected
iPad 3: not affected
iPad 2: not affected
iPhone 4S: not affected
(Chrome for Android does not appear to be affected at all)

This is the same list of affected devices as in bug 154816. However I feel I should point out I believe this is a different problem to that bug. Firing incorrect resize events is one problem, but even in this case the last event fired had the right size, and this means means our engine should only temporarily set the wrong size, then set the right size before a frame is displayed. This should work fine, but the issue with *this* bug is it appears to corrupt the canvas size even though it should work in the face of a spurious resize event.
Comment 1 Ashley Gullen 2016-02-29 09:20:46 PST
I should add I believe this is a regression; we never had any reports of this prior to iOS 9, but I'm not sure exactly when this started happening.
Comment 2 Radar WebKit Bug Importer 2016-03-01 20:31:12 PST
<rdar://problem/24924463>
Comment 3 David 2016-04-26 08:37:02 PDT
Here's a much more minimal test case that reproduces this issue:

https://dl.dropboxusercontent.com/u/364079/2016-04-26-webkit/broken.html

Repro steps:

- Visit the URL on an affected device (I tested on an iPad Pro). If all is well, you should see an animated border on all 4 sides.
- Press the HOME button to close Safari.
- Press the Safari button again to re-open.
- Note the canvas resizes so that the top and left borders are larger, and the right and bottom borders are not visible.

This issue only occurs when trying to set the WebGL canvas to higher resolution for retina displays, e.g., setting the back buffer resolution to be higher than the CSS pixel resolution by modifying the canvas 'width' and 'height' attributes separately from the CSS style.width and style.height attributes.

My theory is that some code somewhere is assuming that setting the width and height attributes should affect the CSS layout, and my assumptions/understanding is that the CSS size and WebGL resolution sizes of a WebGL canvas should be independent and unaffected by each other.
Comment 4 David 2016-04-26 10:38:57 PDT
For what it's worth - I'm not sure if extra detail is helpful, but here are a few things I've tried & seen.

- If the canvas size is specified in px in the html document, then this problem doesn't occur. When the canvas size is specified in percent units, then the problem does happen, even if the canvas.style.{width,height} are later set to px units in javascript.

- If the canvas CSS width/height is set to '100%', and it resizes incorrectly, then changing the size to 99% and back to 100% after a small delay causes a reflow that restores the correct canvas layout size. Toggling visibility also works. But this isn't generally a practical workaround.

- I don't get a resize event when the app is resumed from sleep. I do see 2 resize events when the app is suspended. Perhaps this is a separate bug?

- The two resize events I get have strange sizes, for example in landscape orientation with Safari on an iPad Pro (iOS 9.3.1), my initial window size is 980x665. When I hit the home button and log the window size from inside onWindowResize(), I see two resize events back to back, and the first one has an incorrect size & aspect.

[Log] onWindowResize – Event {clipboardData: undefined, type: "resize", target: Window, …} (fix.js, line 106)
[Log] window.innerWidth=980 window.innerHeight=1215 (fix.js, line 109)

[Log] onWindowResize – Event {clipboardData: undefined, type: "resize", target: Window, …} (fix.js, line 106)
[Log] window.innerWidth=980 window.innerHeight=665 (fix.js, line 109)

- When the canvas resizes to be incorrectly large, the canvas style.width and style.height attributes reported are the original, correct values, so I can't detect when things have gone bad. I don't see changes to window.innerWidth during suspend/resume either, so the workaround Ashley mentioned doesn't seem to work for me.