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:
parent
552fd4c0e7
commit
395e92dc05
2 changed files with 70 additions and 8 deletions
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue