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

HTML5 Exports cause high CPU usage in Chrome #37473

Closed
MarshallJDem opened this issue Mar 31, 2020 · 10 comments
Closed

HTML5 Exports cause high CPU usage in Chrome #37473

MarshallJDem opened this issue Mar 31, 2020 · 10 comments

Comments

@MarshallJDem
Copy link

Godot version:
3.2-Stable

OS/device including version:
Windows 10, Mac OSX Catalina 10.15.4, Google Chrome V 80.0.3987.149

Issue description:
When exporting a game for HTML5, if you run it in Google Chrome, it takes a very large chunk of the CPU. This happens indefinitely - most browsers experience a spike when loading up the game, but Chrome maintains this high CPU. I ran it for 5 minutes and it did not drop, whereas Firefox for example drops in CPU usage a few seconds after startup.

The CPU usage for chrome is coming from something called "Software Reporter Tool". This may mean Godot is raising a red flag and chrome is using high CPU to monitor the program, but that's just a guess of mine after some brief research.

This has been tested on two different computers, running the game directly out of Godot as well as off an Apache server.

This is a photo of running the game directly in the editor. There is no abnormal CPU usage.
2020-03-31 (9)

This is a photo of running the game using the HTML5 export on chrome. This has high CPU usage.
2020-03-31 (10)

Steps to reproduce:
Run any Godot project on HTML5 export on Chrome.
Minimal reproduction project:
This can be done with a new project that has nothing other than an empty scene file.

@Calinou
Copy link
Member

Calinou commented Mar 31, 2020

If this is a non-game application (i.e. smooth animations aren't important), try enabling Application > Run > Low Processor Mode in the Project Settings then export the project to HTML5 again. Other than that, I don't think we can do much about the issue at hand.

@MarshallJDem
Copy link
Author

This is a game, however luckily in my case it is 2D and the CPU usage is low enough anyway that performance hasn't been an issue. I just have noticed that my fan has been turning into a tornado when I'm running the game.

But just curious - is this unsolvable because you think that chrome is to blame for this?

@Calinou
Copy link
Member

Calinou commented Apr 1, 2020

But just curious - is this unsolvable because you think that chrome is to blame for this?

Unfortunately, that might be the case. On Windows, Chrome uses ANGLE to render WebGL, which means WebGL is always translated to Direct3D before being rendered. This incurs an additional cost. I presume Google did it this way because they didn't want to rely on low-quality OpenGL drivers, but it's a shame even NVIDIA users have to pay for this cost (even though their OpenGL drivers are good).

I think Firefox on Windows does this as well, but I'm not sure. In this case, the high CPU usage must be due to something else. I've always found WebGL performance difficult to troubleshoot 🙁

@MarshallJDem
Copy link
Author

I'll drink to that... WebGL does not have the best profiling :P.

A bit of a good news on this is that having additional tabs of the game open doesn't scale CPU usage linearly, so it seems like Chrome is at least adjusting to higher loads which would imply that bigger projects won't necessarily suffer more than mine.

If this becomes more of an issue I'll dive into this and post any solutions I find in this thread. Thanks for the reply!

@Giwayume
Copy link
Contributor

Chrome & other browsers have throttled requestAnimationFrame on inactive tabs for a long time now. In some cases, you can get less than 1fps which obviously saves a lot of CPU.

@davehayes
Copy link

davehayes commented Jun 22, 2020

I have another observation that may relate to this issue. Consider this code, which is found in your html5 export:

var animationCallbacks = [];
function animate(time) {
	animationCallbacks.forEach(callback => callback(time));
	requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

function adjustCanvasDimensions() {
	var scale = window.devicePixelRatio || 1;
	var width = window.innerWidth;
	var height = window.innerHeight;
	canvas.width = width * scale;
	canvas.height = height * scale;
	canvas.style.width = width + "px";
	canvas.style.height = height + "px";
}
animationCallbacks.push(adjustCanvasDimensions);

Is there really a need to alter the canvas width and height every animation tick?

I see a lot of cpu with Chromium (not chrome) at the moment with this code in place.

@Giwayume
Copy link
Contributor

Giwayume commented Jun 23, 2020

Seems like an easy way to guarantee it's always sized correctly, any reason not to do this, though?

window.addEventListener('resize', adjustCanvasDimensions);

Of course, you'd need to draw everything to the canvas immediately afterward.

Or simpler,

var animationCallbacks = [];
var isWindowResized = true;
function animate(time) {
	animationCallbacks.forEach(callback => callback(time));
	requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

window.addEventListener('resize', () => {
    isWindowResized = true;
});

function adjustCanvasDimensions() {
    if (isWindowResized) {
        isWindowResized = false;
	var scale = window.devicePixelRatio || 1;
	var width = window.innerWidth;
	var height = window.innerHeight;
	canvas.width = width * scale;
	canvas.height = height * scale;
	canvas.style.width = width + "px";
	canvas.style.height = height + "px";
    }
}
animationCallbacks.push(adjustCanvasDimensions);

Setting the size unnecessarily to the same value every loop is definitely wasting CPU time.

https://jsperf.com/setting-canvas-size-in-loop/1

@Calinou
Copy link
Member

Calinou commented Jun 23, 2020

For reference, here are the benchmark results I get using Firefox 77:

image

@davehayes
Copy link

Seems like an easy way to guarantee it's always sized correctly, any reason not to do this, though?

That was my implied question.

Your solution is almost exactly what I did to fix this issue and a related one where I could not get a canvas resize to stick. Finding this illustrated why. Here's what's currently on my page:

  function adjustCanvasDimensions() {
    if (godotResizeTrigger) {
      godotResizeTrigger = false;
      var scale = window.devicePixelRatio || 1;
      var width = window.innerWidth - godotWidthAdjust;
      var height = window.innerHeight - godotHeightAdjust;
      canvas.width = width * scale;
      canvas.height = height * scale;
      canvas.style.width = width + "px";
      canvas.style.height = height + "px";
      console.log("GODOT code adjusted canvas dimensions to " + canvas.width + " and " + canvas.height);
    }
  }

If someone decides to fix this, I would recommend those *Adjust variables above. My use case specifically needs the Godot canvas in the bottom half of the page, and without those variables you cannot achieve this.

@Faless
Copy link
Collaborator

Faless commented Oct 6, 2020

The adjustCanvasDimesions function has been improved in #42178 (and backported via #42266).
The changes will be available in 3.2.4 . Additionally, the low_processor_usage_mode has been also fixed via #40018 .
Closing this a fixed (browser version will most likely still be less performant than desktop version, but according to my profiling this specific issue is fixed and I'm registering lower CPU usage).

@Faless Faless closed this as completed Oct 6, 2020
@akien-mga akien-mga added the bug label Oct 6, 2020
@akien-mga akien-mga added this to the 4.0 milestone Oct 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants