Skip to content

Commit

Permalink
fix(PanoViewer): fix delayed frame is not render
Browse files Browse the repository at this point in the history
  • Loading branch information
Jongmoon Yoon committed Apr 4, 2019
1 parent 0b92ac3 commit df8007c
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 3 deletions.
50 changes: 48 additions & 2 deletions src/PanoViewer/PanoViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import WebGLUtils from "../PanoImageRenderer/WebGLUtils";
import {ERROR_TYPE, EVENTS, GYRO_MODE, PROJECTION_TYPE} from "./consts";
import {glMatrix} from "../utils/math-util.js";
import {VERSION} from "../version";
import {IS_SAFARI_ON_DESKTOP} from "../utils/browser";

export default class PanoViewer extends Component {
/**
Expand Down Expand Up @@ -801,26 +802,71 @@ export default class PanoViewer extends Component {
* Register the callback on the raf to call _renderLoop every frame.
*/
_startRender() {
this._renderLoop = this._renderLoop.bind(this);
if (IS_SAFARI_ON_DESKTOP) {
this._renderLoop = this._renderLoopForNextTick.bind(this);
} else {
this._renderLoop = this._renderLoop.bind(this);
}

this._rafId = window.requestAnimationFrame(this._renderLoop);
}

_renderLoop() {
_render() {
if (this._photoSphereRenderer) {
if (this._quaternion) {
this._photoSphereRenderer.renderWithQuaternion(this._quaternion, this._fov);
} else {
this._photoSphereRenderer.render(this._yaw, this._pitch, this._fov);
}
}
}

_renderLoop() {
this._render();

this._rafId = window.requestAnimationFrame(this._renderLoop);
}

/**
* MacOS X Safari Bug Fix
* This code guarantees that rendering should be occurred.
*
* In MacOS X(10.14.2), Safari (12.0.2)
* The requestAnimationFrame(RAF) callback is called just after previous RAF callback without term
* only if requestAnimationFrame is called for next frame while updating frame is delayed (~over 2ms)
* So browser cannot render the frame and may be freezing.
*/
_renderLoopForNextTick() {
const before = performance.now();

this._render();

const diff = performance.now() - before;

if (this._rafTimer) {
clearTimeout(this._rafTimer);
this._rafTimer = null;
}

/** Use requestAnimationFrame only if current rendering could be possible over 60fps (1000/60) */
if (diff < 16) {
this._rafId = window.requestAnimationFrame(this._renderLoop);
} else {
/** Otherwise, Call setTimeout instead of requestAnimationFrame to gaurantee renering should be occurred*/
this._rafTimer = setTimeout(this._renderLoop, 0);
}
}

_stopRender() {
if (this._rafId) {
window.cancelAnimationFrame(this._rafId);
delete this._rafId;
}

if (this._rafTimer) {
clearTimeout(this._rafTimer);
delete this._rafTimer;
}
}

/**
Expand Down
7 changes: 6 additions & 1 deletion src/utils/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ const win = typeof window !== "undefined" && window.Math === Math ? window : typ
/* eslint-enable no-new-func, no-nested-ternary */

const doc = win.document;
const userAgent = win.navigator.userAgent;
const IS_SAFARI_ON_DESKTOP = userAgent.indexOf("Safari") !== -1 &&
userAgent.indexOf("Chrome") === -1 &&
userAgent.indexOf("Mac OS X") !== 1;

export {
win as window,
doc as document
doc as document,
IS_SAFARI_ON_DESKTOP
};
50 changes: 50 additions & 0 deletions test/unit/PanoViewer/PanoViewer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -781,4 +781,54 @@ describe("PanoViewer", () => {
cleanup();
});
});

/**
* Exceptional Case
*/
describe("Exception Case", () => {
let target;

beforeEach(() => {
target = sandbox();
target.innerHTML = `<div"></div>`;
});

afterEach(() => {
cleanup();
});

IT("should invoke renderLoopTimer if rendering frame is too delayed on PC Safari Browser", async () => {
// Given, When
const PanoViewerOnSafari = PanoViewerInjector({
"../utils/browser": {
IS_SAFARI_ON_DESKTOP: true
}
}).default;

const panoViewer = new PanoViewerOnSafari(target, {
image: "./images/test_equi.png"
});

/**
* Delaying a frame with '20 ms' by force.
*/
panoViewer._render = function() {
const start = Date.now();
let now = start;

/** */
while (now - start < 20) {
now = Date.now();
}
};

// wait for renering to be occurred.
await new Promise(res => setTimeout(res, 100));

// This cannot be tested by BLACKBOX-TEST
expect(panoViewer._rafTimer).to.not.undefined;

panoViewer.destroy();
});
});
});

0 comments on commit df8007c

Please sign in to comment.