Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core(full-page-screenshot): get the MAX_TEXTURE_SIZE from the browser #11847

Merged
merged 8 commits into from
Jan 13, 2021

Conversation

paulirish
Copy link
Member

Connor called this out before and I confirmed and documented it: https://bugs.chromium.org/p/chromium/issues/detail?id=770769#c13

(the 16384 number is the max_texture_size for swiftshader, used by headless (most of the time), but other devices have other ones)

This changes our impl to pull it from the browser rather than use a hardcoded value that might cause problems.

@paulirish paulirish requested a review from a team as a code owner December 17, 2020 08:10
@paulirish paulirish requested review from Beytoven and removed request for a team December 17, 2020 08:10
@google-cla google-cla bot added the cla: yes label Dec 17, 2020
@connorjclark
Copy link
Collaborator

connorjclark commented Dec 17, 2020

lgtm but my concerns: what's the overhead for making a webgl context? Could this be more noticeable in LR given we are software rendering based there?

/* istanbul ignore next */ // todo, c8 ignore?
function getMaxTextureSize() {
const canvas = document.createElement('canvas');
const webGL = canvas.getContext('webgl');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is destroying necessary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, but i added it anyway.

@patrickhulce
Copy link
Collaborator

what's the overhead for making a webgl context? Could this be more noticeable in LR given we are software rendering based there?

Related: are there any environments where webGL might be disabled entirely? How does this fail when it is and should we have a fallback?

AFAIK you'd have to go manually disable WebGL in Chrome flags, so probably not a big deal, but pretty costless to have a fallback too.

@brendankenny
Copy link
Member

Related: are there any environments where webGL might be disabled entirely? How does this fail when it is and should we have a fallback?

it used to be disabled quite a bit due to driver/OS bugs possibly being security issues (e.g. being able to read rasterized pixels from visible windows other than the browser). Now that swiftshader is cross platform...does Chrome always fall back to it?

Since webgl isn't actually used for taking the screenshot, this is just a query about machine capabilities, maybe it doesn't matter and we should just have a reasonable fallback number no matter what.

lighthouse-core/gather/gatherers/full-page-screenshot.js Outdated Show resolved Hide resolved
/* istanbul ignore next */ // todo, c8 ignore?
function getMaxTextureSize() {
const canvas = document.createElement('canvas');
const webGL = canvas.getContext('webgl');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tsc would correctly call out webGL as WebGLRenderingContext | null here if the file wasn't // @ts-nocheck, so need to handle that case (e.g. webgl is disabled on that machine, as discussed in the other comments :)

Co-authored-by: Brendan Kenny <bckenny@gmail.com>
@paulirish
Copy link
Member Author

what's the overhead for making a webgl context?

~10ms on LR. ~20ms on my oldass MBP.

Could this be more noticeable in LR given we are software rendering based there?

overhead nah, but the M_T_S there is 8192, so our 16k number will give us a problem in LR. either crashes or image repeating. (not sure which)

should we have a fallback?

yes good call. added one.


all comments addressed afaik.

}
if (code.includes('document.documentElement.clientWidth')) {
evaluate: async function(fn) {
if (fn.name === 'resolveNodes') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this so much nicer

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fn.name is pretty tight. :) good call.

* @see https://bugs.chromium.org/p/chromium/issues/detail?id=770769
*/
async getMaxScreenshotHeight(driver) {
return await driver.evaluate(pageFunctions.getMaxTextureSize, {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious why a page function and not local to this module?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly because theres a lot of ways in which PFs are very diff than node code and so i'd like to ideally have all PFs live in separate files.

  • code coverage exemption (istanbul ignore/c8 ignore) is easier since they're colocated
  • can apply a eslint-env browser instead of our current handcrafted /* global window document Node ShadowRoot */ list.
  • maybe use some browserish config for typescript on these files

--

but until all are migrated there i'll just place new ones in pf.js. thats my thinking.

i don't care a lot about this particular case tho.

Copy link
Collaborator

@connorjclark connorjclark Jan 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but until all are migrated there i'll just place new ones in pf.js. thats my thinking.

Funny, I have the exact opposite thinking. Not speaking to the idea at all, but if we are currently doing "all page functions local if possible", we should keep doing that until we change how we structure things, instead of starting a transitionary thing w/o any discussion.

fwiw i like your bullet points

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

word.

well... looking at the fn's in pf.js, there already are plenty that are one-offs used by gatherrunner & driver. oh and a singleuse for iframe-elements! :)

let canvas = document.createElement('canvas');
let gl = canvas.getContext('webgl');
const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
canvas = gl = undefined; // Cleanup for GC
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought there might have been a more explicit "destroy" method for a context, but there isn't. As long as the element is not on the DOM, we're good. Can remove the "cleanup" line.

sorry for confusion

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe i'm overly cautious but memory mgmt with webgl feels like it has some gotchas.. as such, i'm prone to keep the explicit cleanup and not just wait for auto-GC

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mathiasbynens we're just two developers who don't know how GC works. wdyt? :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neither canvas nor the gl context escapes from this block, so it should in theory be fine even without the explicit cleanup. (In case anyone‘s interested, I wrote a high-level explanation of escape analysis when I learned about it.)

That said, I’m not familiar with Chromium’s WebGL context lifecycle, and if perhaps there are weird side effects to creating such contexts that I don’t know about.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants