Reputation: 1070
I launched this site yesterday (a site for live editing three.js examples) and found that when making updates to the code or navigating to multiple example files, the frame rate skyrockets to around 1000 f/s.
The first mention of this is here. I'm not sure why the frame rate would increase after updating. The WebGL canvas is inside an iframe, and I'm updating the iframe content with this code (iframe has an id of 'preview):
var previewFrame = document.getElementById('preview');
var preview = previewFrame.contentDocument || previewFrame.contentWindow.document;
preview.open();
preview.write(this.props.code);
preview.close();
Does anyone have an idea how to resolve this? The editing is done with CodeMirror and the site is built with React. All src code is in the repo here.
Upvotes: 0
Views: 355
Reputation:
My guess is you're starting multiple requestAnimationFrame loops.
For example
let numLoops = 0;
const countElem = document.querySelector("#count");
const stats = new Stats();
document.body.appendChild(stats.domElement);
function loop() {
stats.update();
requestAnimationFrame(loop);
}
function startLoop() {
++numLoops;
countElem.textContent = numLoops;
requestAnimationFrame(loop);
}
startLoop();
document.querySelector("button").addEventListener('click', startLoop);
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
<button>Click to add another requestAnimationFrame loop</button>
<div>Num Loops Running: <span id="count"></span></div>
The way I made my examples editable and then runable on http://webglfundamentals.org is to run the examples in an iframe using a blob. Anytime the user picks "update" I generate a new blob with their edited source and then set the iframe to that new blob's URL. This means the example gets completely reloaded so any old code/loops/events/webgl contexts, etc are discarded by the browser.
You can see the code here which is effectively
function runLastVersionOfUsersCode() {
var url = getSourceBlob(usersEditedHtml);
someIFrameElement.src = url;
}
var blobUrl;
function getSourceBlob(options, htmlForIFrame) {
// if we already did this discard the old one otherwise
// it will stick around wasting memory
if (blobUrl) {
URL.revokeObjectURL(blobUrl);
}
var blob = new Blob([htmlForIFrame], {type: 'text/html'});
blobUrl = URL.createObjectURL(blob);
return blobUrl;
}
If you look at the actual code for getSourceBlob
you'll see it does a little more work but that's basically it above.
Upvotes: 1
Reputation: 1070
To build on gman's helpful answer, I used cancelAnimationFrame in the React render loop (not the threejs render loop). The commit is here: https://github.com/ekatzenstein/three.js-live/commit/2cad65afa5fe066618a7aac179e096ee9e29ed76
//in the iframe
window.parent.three_live = requestAnimationFrame(animate)
//in the parent, on render loop
_resetAnimationFrame(){
//disables abnormally high frame rates
if(window.three_live){
var previewWindow = document.getElementById('preview').contentWindow;
previewWindow.cancelAnimationFrame(window.three_live);
}
}
Doing window.parent wasn't necessary but wanted to reference in the global project.
Upvotes: 0