Skip to content

Commit

Permalink
Merge pull request #248 from jsantell/unresolved-displays
Browse files Browse the repository at this point in the history
Set a timeout on getVRDisplays() to handle scenario where the promise never resolves. Fixes #199
  • Loading branch information
toji authored Jun 1, 2017
2 parents f43a209 + da9e3c2 commit 52827e9
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 17 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,19 @@ WebVRConfig = {
// gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING,
// and gl.TEXTURE_BINDING_2D for texture unit 0.
DIRTY_SUBMIT_FRAME_BINDINGS: true // Default: false.

// When set to true, this will cause a polyfilled VRDisplay to always be
// appended to the list returned by navigator.getVRDisplays(), even if that
// list includes a native VRDisplay.
ALWAYS_APPEND_POLYFILL_DISPLAY: false,

// There are scenarios where the native WebVR API exists, and instead of returning
// 0 VR displays when none are detected, `navigator.getVRDisplays()`'s promise never
// resolves. This results in the polyfill hanging and not being able to provide fallback
// displays, so set a timeout in milliseconds to stop waiting for a response
// and just use polyfilled displays.
// https://bugs.chromium.org/p/chromium/issues/detail?id=727969
GET_VR_DISPLAYS_TIMEOUT: 1000,
}
```

Expand Down
10 changes: 9 additions & 1 deletion src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,15 @@ window.WebVRConfig = Util.extend({
// When set to true, this will cause a polyfilled VRDisplay to always be
// appended to the list returned by navigator.getVRDisplays(), even if that
// list includes a native VRDisplay.
ALWAYS_APPEND_POLYFILL_DISPLAY: false
ALWAYS_APPEND_POLYFILL_DISPLAY: false,

// There are scenarios where the native WebVR API exists, and instead of returning
// 0 VR displays when none are detected, `navigator.getVRDisplays()`'s promise never
// resolves. This results in the polyfill hanging and not being able to provide fallback
// displays, so set a timeout in milliseconds to stop waiting for a response
// and just use polyfilled displays.
// https://bugs.chromium.org/p/chromium/issues/detail?id=727969
GET_VR_DISPLAYS_TIMEOUT: 1000,
}, window.WebVRConfig);

if (!window.WebVRConfig.DEFER_INITIALIZATION) {
Expand Down
19 changes: 19 additions & 0 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@ Util.lerp = function(a, b, t) {
return a + ((b - a) * t);
};

/**
* Light polyfill for `Promise.race`. Returns
* a promise that resolves when the first promise
* provided resolves.
*
* @param {Array<Promise>} promises
*/
Util.race = function(promises) {
if (Promise.race) {
return Promise.race(promises);
}

return new Promise(function (resolve, reject) {
for (var i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject);
}
});
};

Util.isIOS = (function() {
var isIOS = /iPad|iPhone|iPod/.test(navigator.platform);
return function() {
Expand Down
38 changes: 22 additions & 16 deletions src/webvr-polyfill.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,23 +159,29 @@ WebVRPolyfill.prototype.getVRDisplays = function() {
this.populateDevices();
var polyfillDisplays = this.displays;

if (this.nativeWebVRAvailable) {
return this.nativeGetVRDisplaysFunc.call(navigator).then(function(nativeDisplays) {
if (window.WebVRConfig.ALWAYS_APPEND_POLYFILL_DISPLAY) {
return nativeDisplays.concat(polyfillDisplays);
} else {
return nativeDisplays.length > 0 ? nativeDisplays : polyfillDisplays;
}
});
} else {
return new Promise(function(resolve, reject) {
try {
resolve(polyfillDisplays);
} catch (e) {
reject(e);
}
});
if (!this.nativeWebVRAvailable) {
return Promise.resolve(polyfillDisplays);
}

// Set up a race condition if this browser has a bug where
// `navigator.getVRDisplays()` never resolves.
var vrDisplaysNative = this.nativeGetVRDisplaysFunc.call(navigator);
var timeout = new Promise(function(resolve) {
setTimeout(function() {
resolve([])
}, window.WebVRConfig.GET_VR_DISPLAYS_TIMEOUT);
}).then(function (displays) {
console.warn('Native WebVR implementation detected, but `getVRDisplays()` failed to resolve. Falling back to polyfill.');
return displays;
});

return Util.race([vrDisplaysNative, timeout]).then(function(nativeDisplays) {
if (window.WebVRConfig.ALWAYS_APPEND_POLYFILL_DISPLAY) {
return nativeDisplays.concat(polyfillDisplays);
} else {
return nativeDisplays.length > 0 ? nativeDisplays : polyfillDisplays;
}
});
};

WebVRPolyfill.prototype.getVRDevices = function() {
Expand Down

0 comments on commit 52827e9

Please sign in to comment.