1
0
Fork 0
mirror of https://github.com/sbrl/Nibriboard.git synced 2018-01-10 21:33:49 +00:00

[client] Utilise symbosl to clean up chunk cache & reduce memory usage. Also add primitive chunk unloading system - we'll need to keep an eye on how effective this is.

This commit is contained in:
Starbeamrainbowlabs 2017-12-26 14:10:02 +00:00
parent 552fd4c0e7
commit 395e92dc05
Signed by: sbrl
GPG key ID: 1BE5172E637709C2
2 changed files with 70 additions and 8 deletions

View file

@ -22,6 +22,8 @@ class Chunk
this.size = inSize; this.size = inSize;
/** @type {[ { Points: [Vector], Width: number, Color: string }] */ /** @type {[ { Points: [Vector], Width: number, Color: string }] */
this.lines = []; this.lines = [];
/** The last time this chunk was seen on (or near) the user's screen. @type {Date} */
this.lastSeen = new Date();
} }
/** /**
@ -111,7 +113,7 @@ class Chunk
update(dt) update(dt)
{ {
this.lastSeen = new Date();
} }
/** /**

View file

@ -16,6 +16,14 @@ class ChunkCache
this.showRenderedChunks = false; this.showRenderedChunks = false;
this.boardWindow.rippleLink.on("ChunkUpdate", this.handleChunkUpdate.bind(this)); this.boardWindow.rippleLink.on("ChunkUpdate", this.handleChunkUpdate.bind(this));
/** A few presetsymbols for various non-chunk entries in the cache. */
this.cacheTypes = {
/** An empty chunk @type {Symbol} */
empty: Symbol("empty-chunk"),
/** A chunk that's been requested from the server, but hasn't arrived yet. @type {Symbol} */
requested: Symbol("requested-chunk")
}
} }
/** /**
@ -31,7 +39,7 @@ class ChunkCache
return null; return null;
let requestedChunk = this.cache.get(chunkRef.toString()); let requestedChunk = this.cache.get(chunkRef.toString());
if(!(requestedChunk instanceof Chunk)) { if(!this.isChunk(requestedChunk)) {
if(!quiet) if(!quiet)
console.warn(`Attempt to access a chunk at ${chunkRef} that's not loaded yet.`); console.warn(`Attempt to access a chunk at ${chunkRef} that's not loaded yet.`);
return null; return null;
@ -52,7 +60,7 @@ class ChunkCache
let currentChunk = this.fetchChunk(startingChunkRef); let currentChunk = this.fetchChunk(startingChunkRef);
let nextUniqueId = lineUniqueId; let nextUniqueId = lineUniqueId;
while(currentChunk instanceof Chunk) while(currentChunk instanceof Chunk) // No need to search empty chunks
{ {
let nextLineFragment = currentChunk.getLineByUniqueId(nextUniqueId); let nextLineFragment = currentChunk.getLineByUniqueId(nextUniqueId);
if(nextLineFragment == null) if(nextLineFragment == null)
@ -70,10 +78,14 @@ class ChunkCache
return lineFragments; return lineFragments;
} }
/**
* Deletes all the stray chunk requests we've filed in the chunk cache.
* @return {[type]} [description]
*/
clearRequestedChunks() clearRequestedChunks()
{ {
for(let [chunkRefStr, chunk] of this.cache.entries()) { for(let [chunkRefStr, chunk] of this.cache.entries()) {
if(!(chunk instanceof Chunk)) if(chunk == this.cacheTypes.requested)
this.cache.delete(chunkRefStr); this.cache.delete(chunkRefStr);
} }
} }
@ -97,6 +109,44 @@ class ChunkCache
return !hasChunk; return !hasChunk;
} }
/**
* Prunes the cache of all the chunks that haven't been seen on (or near)
* the screen for at least the specified number of milliseconds.
* @param {number} msSinceSeen The minimum number of milliseconds since a chunk as been seen for it to be unloaded.
* @return {number} The number of chunks unloaded.
*/
prune(msSinceSeen)
{
let now = +new Date(), chunksPruned = 0;
for(let [cRefStr, chunk] of this.cache) {
// Skip over symbols and other such oddities
// future: handle these
if(!(chunk instanceof Chunk))
continue;
if(typeof chunk.lastSeen == "undefined") debugger;
if(now - chunk.lastSeen.getTime() >= msSinceSeen) {
this.cache.delete(cRefStr);
chunksPruned++;
}
}
return chunksPruned;
}
/**
* Works out whether _thing_ is a chunk or not.
* @param {object} thing The thing to analyze.
* @return {bool} Whether _thing_ is a chunk or not.
*/
isChunk(thing) {
if(thing instanceof Chunk)
return true;
if(thing === this.cacheTypes.empty)
return true;
return false;
}
/** /**
* Updates the chunk cache ready for the next frame. * Updates the chunk cache ready for the next frame.
* @param {number} dt The amount of time, in milliseconds, since that last frame was rendered. * @param {number} dt The amount of time, in milliseconds, since that last frame was rendered.
@ -117,10 +167,15 @@ class ChunkCache
this.boardWindow.currentPlaneName, this.boardWindow.currentPlaneName,
cx / chunkSize, cy / chunkSize cx / chunkSize, cy / chunkSize
); );
if(!this.cache.has(cChunk.toString())) { let chunk = this.cache.get(cChunk.toString());
if(!this.isChunk(chunk) && chunk != this.cacheTypes.requested) {
console.info(`Requesting ${cChunk}`); console.info(`Requesting ${cChunk}`);
missingChunks.push(cChunk); missingChunks.push(cChunk);
this.cache.set(cChunk.toString(), { requestedFromServer: true }); this.cache.set(cChunk.toString(), this.cacheTypes.requested);
}
else if(chunk instanceof Chunk){
// It's a real (non-empty), so update it
chunk.update(dt);
} }
} }
} }
@ -134,6 +189,12 @@ class ChunkCache
"ForgottenChunks": missingChunks "ForgottenChunks": missingChunks
}); });
} }
// Prune chunks from the cache that haven't been accessed in 60
// seconds or more
let prunedChunkCount = this.prune(60 * 1000);
if(prunedChunkCount > 0)
console.debug(`Pruned ${prunedChunkCount} from the local chunk cache, leaving ${this.cache.size} entries total remaining.`);
} }
/** /**
@ -185,7 +246,7 @@ class ChunkCache
chunkData.Location.Y chunkData.Location.Y
); );
console.info(`Chunk Update @ ${newChunkRef}`) console.info(`Chunk Update @ ${newChunkRef}`);
let newChunk = new Chunk(newChunkRef, chunkData.Size); let newChunk = new Chunk(newChunkRef, chunkData.Size);
let newLines = chunkData.lines.map((line) => { let newLines = chunkData.lines.map((line) => {
@ -229,7 +290,6 @@ ChunkCache.CalculateChunkArea = function(visibleArea, chunkSize) {
(Math.ceil((Math.abs(visibleArea.x) + (visibleArea.width)) / chunkSize) * chunkSize), (Math.ceil((Math.abs(visibleArea.x) + (visibleArea.width)) / chunkSize) * chunkSize),
(Math.ceil((Math.abs(visibleArea.y) + (visibleArea.height)) / chunkSize) * chunkSize) (Math.ceil((Math.abs(visibleArea.y) + (visibleArea.height)) / chunkSize) * chunkSize)
); );
} }
export default ChunkCache; export default ChunkCache;