Rewrite box art loading to load directly from chrome.storage for a huge speed improvement

This commit is contained in:
Cameron Gutman 2018-03-28 00:18:09 -07:00
parent 4284394607
commit 6c457fe56d
2 changed files with 15 additions and 91 deletions

View File

@ -117,7 +117,6 @@ function restoreUiAfterNaClLoad() {
} }
function beginBackgroundPollingOfHost(host) { function beginBackgroundPollingOfHost(host) {
host.warmBoxArtCache();
if (host.online) { if (host.online) {
$("#hostgrid-" + host.serverUid).removeClass('host-cell-inactive'); $("#hostgrid-" + host.serverUid).removeClass('host-cell-inactive');
// The host was already online. Just start polling in the background now. // The host was already online. Just start polling in the background now.
@ -459,14 +458,11 @@ function showApps(host) {
// double clicking the button will cause multiple box arts to appear. // double clicking the button will cause multiple box arts to appear.
// to mitigate this we ensure we don't add a duplicate. // to mitigate this we ensure we don't add a duplicate.
// This isn't perfect: there's lots of RTTs before the logic prevents anything // This isn't perfect: there's lots of RTTs before the logic prevents anything
var imageBlob = new Blob([resolvedPromise], {type: "image/png"}); var outerDiv = $("<div>", {class: 'game-container mdl-card mdl-shadow--4dp', id: 'game-'+app.id, backgroundImage: resolvedPromise, role: 'link', tabindex: 0, title: app.title, 'aria-label': app.title });
var outerDiv = $("<div>", {class: 'game-container mdl-card mdl-shadow--4dp', id: 'game-'+app.id, backgroundImage: URL.createObjectURL(imageBlob), role: 'link', tabindex: 0, title: app.title, 'aria-label': app.title }); $(outerDiv).append($("<img \>", {src: resolvedPromise, id: 'game-'+app.id, name: app.title }));
$(outerDiv).append($("<img \>", {src: URL.createObjectURL(imageBlob), id: 'game-'+app.id, name: app.title }));
$(outerDiv).append($("<div>", {class: "game-title", html: $("<span>", {html: app.title} )})); $(outerDiv).append($("<div>", {class: "game-title", html: $("<span>", {html: app.title} )}));
$("#game-grid").append(outerDiv); $("#game-grid").append(outerDiv);
// $("#gameList").append($("<div>", {html:$("<img \>", {src: URL.createObjectURL(imageBlob), id: 'game-'+app.id, name: app.title }), class: 'box-art mdl-cell mdl-cell--3-col'}).append($("<span>", {html: app.title, class:"game-title"})));
$('#game-'+app.id).on('click', function () { $('#game-'+app.id).on('click', function () {
startGame(host, app.id); startGame(host, app.id);
}); });

View File

@ -71,7 +71,6 @@ function NvHTTP(address, clientUid, userEnteredAddress = '') {
this.serverMajorVersion = 0; this.serverMajorVersion = 0;
this.appVersion = ''; this.appVersion = '';
this.clientUid = clientUid; this.clientUid = clientUid;
this._memCachedBoxArtArray = {};
this._pollCount = 0; this._pollCount = 0;
this._consecutivePollFailures = 0; this._consecutivePollFailures = 0;
this.online = false; this.online = false;
@ -223,10 +222,6 @@ NvHTTP.prototype = {
return string; return string;
}, },
_prepareForStorage: function() {
this._memCachedBoxArtArray = {};
},
_parseServerInfo: function(xmlStr) { _parseServerInfo: function(xmlStr) {
$xml = this._parseXML(xmlStr); $xml = this._parseXML(xmlStr);
$root = $xml.find('root'); $root = $xml.find('root');
@ -354,77 +349,17 @@ NvHTTP.prototype = {
return this.getAppListWithCacheFlush(); return this.getAppListWithCacheFlush();
}, },
// warms `this` _memCachedBoxArtArray with ALL box art from ALL servers
// this is inefficient, but works well.
warmBoxArtCache: function () {
if (!this.paired) {
console.log('%c[utils.js, warmBoxArtCache]', 'color: grey;', 'Not warming box art cache for unpaired host');
return;
}
if (Object.keys(this._memCachedBoxArtArray).length != 0) {
console.log('%c[utils.js, warmBoxArtCache]', 'color: grey;', 'Box art cache already warmed');
return;
}
if (chrome.storage) {
chrome.storage.local.get('boxArtCache', function(JSONCachedBoxArtArray) {
var storedBoxArtArray; // load cached data if it exists
if (JSONCachedBoxArtArray.boxArtCache != undefined) {
storedBoxArtArray = JSONCachedBoxArtArray.boxArtCache;
for (var key in storedBoxArtArray) {
this._memCachedBoxArtArray[key] = _base64ToArrayBuffer(storedBoxArtArray[key]);
}
console.log('%c[utils.js, warmBoxArtCache]', 'color: grey;', 'Box art cache warmed');
} else {
console.warn('%c[utils.js, warmBoxArtCache]', 'color: grey;', 'No box art found in storage. Cannot warm cache!');
return;
}
}.bind(this));
}
},
// returns the box art of the given appID. // returns the box art of the given appID.
// three layers of response time are possible: memory cached (in javascript), storage cached (in chrome.storage.local), and streamed (host sends binary over the network) // three layers of response time are possible: memory cached (in javascript), storage cached (in chrome.storage.local), and streamed (host sends binary over the network)
getBoxArt: function (appId) { getBoxArt: function (appId) {
// TODO: unfortunately we do N lookups from storage cache, each of them filling up the memory cache.
// once the first round of calls are all made, each subsequent request hits this and returns from memory cache
if (this._memCachedBoxArtArray[appId] === null) {
// This means a previous box art request failed, don't try again
return new Promise(function (resolve, reject) {
console.error('%c[utils.js, utils.js, getBoxArt]', 'color: gray;', 'Returning cached box-art failure result')
reject(null);
return;
}.bind(this));
} else if (this._memCachedBoxArtArray[appId] !== undefined) {
return new Promise(function (resolve, reject) {
console.log('%c[utils.js, utils.js, getBoxArt]', 'color: gray;', 'Returning memory-cached box-art');
resolve(this._memCachedBoxArtArray[appId]);
return;
}.bind(this));
}
if (chrome.storage) { if (chrome.storage) {
// This may be bad practice to push/pull this much data through local storage? // This may be bad practice to push/pull this much data through local storage?
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
chrome.storage.local.get('boxArtCache', function(JSONCachedBoxArtArray) { chrome.storage.local.get('boxart-'+appId, function(storageData) {
var storedBoxArtArray; // load cached data if it exists
if (JSONCachedBoxArtArray.boxArtCache != undefined && JSONCachedBoxArtArray.boxArtCache[appId] != undefined) {
storedBoxArtArray = JSONCachedBoxArtArray.boxArtCache;
storedBoxArtArray[appId] = _base64ToArrayBuffer(storedBoxArtArray[appId]);
this._memCachedBoxArtArray[appId] = storedBoxArtArray[appId];
} else {
storedBoxArtArray = {};
}
// if we already have it, load it. // if we already have it, load it.
if (storedBoxArtArray[appId] !== undefined && Object.keys(storedBoxArtArray).length !== 0 && storedBoxArtArray[appId].constructor !== Object) { if (storageData !== undefined && Object.keys(storageData).length !== 0 && storageData['boxart-'+appId].constructor !== Object) {
console.log('%c[utils.js, getBoxArt]', 'color: gray;', 'Returning strage-cached box art for app: ', appId); console.log('%c[utils.js, getBoxArt]', 'color: gray;', 'Returning storage-cached box art for app: ', appId);
resolve(storedBoxArtArray[appId]); resolve(storageData['boxart-'+appId]);
return; return;
} }
@ -435,24 +370,17 @@ NvHTTP.prototype = {
'&appid=' + appId + '&appid=' + appId +
'&AssetType=2&AssetIdx=0', '&AssetType=2&AssetIdx=0',
true true
]).then(function(streamedBoxArt) { ]).then(function(boxArtBuffer) {
// the memcached data is global to all the async calls we're doing. This way there's only one array that holds everything properly. var reader = new FileReader();
this._memCachedBoxArtArray[appId] = streamedBoxArt; reader.onloadend = function() {
var obj = {}; var obj = {};
var arrayToStore = {} obj['boxart-'+appId] = this.result;
chrome.storage.local.set(obj, function(onSuccess) {});
for (key in this._memCachedBoxArtArray) { // convert the arraybuffer into a string console.log('%c[utils.js, utils.js, getBoxArt]', 'color: gray;', 'Returning network-fetched box art');
arrayToStore[key] = _arrayBufferToBase64(this._memCachedBoxArtArray[key]); resolve(this.result);
} }
reader.readAsDataURL(new Blob([boxArtBuffer], {type: "image/png"}));
obj['boxArtCache'] = arrayToStore; // storage is in JSON format. JSON does not support binary data.
chrome.storage.local.set(obj, function(onSuccess) {});
console.log('%c[utils.js, utils.js, getBoxArt]', 'color: gray;', 'Returning streamed box art');
resolve(streamedBoxArt);
return;
}.bind(this), function(error) { }.bind(this), function(error) {
// Cache the failure but not persistently
this._memCachedBoxArtArray[appId] = null;
console.error('%c[utils.js, utils.js, getBoxArt]', 'color: gray;', 'Box-art request failed!', error); console.error('%c[utils.js, utils.js, getBoxArt]', 'color: gray;', 'Box-art request failed!', error);
reject(error); reject(error);
return; return;