Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3D Tiles cache management #3808

Merged
merged 24 commits into from
Apr 12, 2016
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions Apps/Sandcastle/gallery/3D Tiles.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@
reset();

tileset = scene.primitives.add(new Cesium.Cesium3DTileset({
url : url
url : url,
debugShowStatistics : true,
maximumNumberOfLoadedTiles : 3
}));

return Cesium.when(tileset.readyPromise).then(function(tileset) {
Expand Down Expand Up @@ -103,7 +105,11 @@
return;
}

console.log('Loading: requests: ' + numberOfPendingRequests + ', processing: ' + numberProcessing);
//console.log('Loading: requests: ' + numberOfPendingRequests + ', processing: ' + numberProcessing);
});

tileset.tileUnload.addEventListener(function(tile) {
//console.log('Tile unloaded.')
});

addExpressionUI();
Expand Down Expand Up @@ -374,6 +380,10 @@
console.log('New max SSE: ' + tileset.maximumScreenSpaceError);
});

Sandcastle.addToolbarButton('Trim tiles (cache)', function() {
tileset.trimLoadedTiles();
});

// Styling ////////////////////////////////////////////////////////////////////

var numberofColors = 6;
Expand Down
111 changes: 111 additions & 0 deletions Source/Core/DoublyLinkedList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*global define*/
define([
'../Core/defined',
'../Core/defineProperties',
'../Core/DeveloperError'
], function(
defined,
defineProperties,
DeveloperError) {
'use strict';

/**
* @private
*/
function DoublyLinkedList() {
this.head = undefined;
this.tail = undefined;
this._length = 0;
}

defineProperties(DoublyLinkedList.prototype, {
length : {
get : function() {
return this._length;
}
}
});

function DoublyLinkedListNode(item, previous, next) {
this.item = item;
this.previous = previous;
this.next = next;
}

DoublyLinkedList.prototype.add = function(item) {
var node = new DoublyLinkedListNode(item, this.tail, undefined);

if (defined(this.tail)) {
this.tail.next = node;
this.tail = node;
} else {
// Insert into empty linked list
this.head = node;
this.tail = node;
}

++this._length;

return node;
};

function remove(list, node) {
if (defined(node.previous) && defined(node.next)) {
node.previous.next = node.next;
node.next.previous = node.previous;
} else if (defined(node.previous)) {
// Remove last node
node.previous.next = undefined;
list.tail = node.previous;
} else if (defined(node.next)) {
// Remove first node
node.next.previous = undefined;
list.head = node.next;
} else {
// Remove last node in the linked list
list.head = undefined;
list.tail = undefined;
}

node.next = undefined;
node.previous = undefined;
}

DoublyLinkedList.prototype.remove = function(node) {
if (!defined(node)) {
return;
}

remove(this, node);

--this._length;
};

DoublyLinkedList.prototype.splice = function(node, nextNode) {
if (!defined(node) || !defined(nextNode)) {
throw new DeveloperError('node and nextNode are required.');
}

if (node === nextNode) {
return;
}

// Remove nextNode, then insert after node
remove(this, nextNode);

var oldNodeNext = node.next;
node.next = nextNode;

// nextNode is the new tail
if (this.tail === node) {
this.tail = nextNode;
} else {
oldNodeNext.previous = nextNode;
}

nextNode.next = oldNodeNext;
nextNode.previous = node;
};

return DoublyLinkedList;
});
114 changes: 65 additions & 49 deletions Source/Scene/Cesium3DTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,20 +166,10 @@ define([
*/
this.numberOfChildrenWithoutContent = defined(header.children) ? header.children.length : 0;

/**
* Gets the promise that will be resolved when the tile's content is ready to render.
*
* @type {Promise.<Cesium3DTile>}
* @readonly
*
* @private
*/
this.contentReadyPromise = when.defer();

var content;
var hasContent;
var hasTilesetContent;
var requestServer;
var createContent;

if (defined(contentHeader)) {
var contentUrl = contentHeader.url;
Expand All @@ -202,14 +192,23 @@ define([
}
//>>includeEnd('debug');

content = contentFactory(tileset, this, url);
var that = this;
createContent = function() {
return contentFactory(tileset, that, url);
};
} else {
content = new Empty3DTileContent();
hasContent = false;
hasTilesetContent = false;

createContent = function() {
return new Empty3DTileContent();
};
}

this._content = content;
this._createContent = createContent;
this._content = createContent();
addContentReadyPromise(this);

this._requestServer = requestServer;

/**
Expand All @@ -235,21 +234,15 @@ define([
*/
this.hasTilesetContent = hasTilesetContent;

var that = this;

// Content enters the READY state
when(content.readyPromise).then(function(content) {
if (defined(that.parent)) {
--that.parent.numberOfChildrenWithoutContent;
}

that.contentReadyPromise.resolve(that);
}).otherwise(function(error) {
// In this case, that.parent.numberOfChildrenWithoutContent will never reach zero
// and therefore that.parent will never refine. If this becomes an issue, failed
// requests can be reissued.
that.contentReadyPromise.reject(error);
});
/**
* The corresponding node in the cache replacement list.
*
* @type {DoublyLinkedList}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DoublyLinkedListNode

* @readonly
*
* @private
*/
this.replacementNode = undefined;

// Members that are updated every frame for tree traversal and rendering optimizations:

Expand Down Expand Up @@ -281,13 +274,13 @@ define([
this.selected = false;

/**
* The last frame number the tile was visible in.
* The last frame number the tile was selected in.
*
* @type {Number}
*
* @private
*/
this.lastFrameNumber = 0;
this.lastSelectedFrameNumber = 0;

/**
* The time when a style was last applied to this tile.
Expand Down Expand Up @@ -336,24 +329,6 @@ define([
}
},

/**
* Gets the promise that will be resolved when the tile's content is ready to process.
* This happens after the content is downloaded but before the content is ready
* to render.
*
* @memberof Cesium3DTile.prototype
*
* @type {Promise.<Cesium3DTileContent>}
* @readonly
*
* @private
*/
contentReadyToProcessPromise : {
get : function() {
return this._content.contentReadyToProcessPromise;
}
},

/**
* @readonly
* @private
Expand Down Expand Up @@ -395,6 +370,19 @@ define([
}
});

function addContentReadyPromise(tile) {
// Content enters the READY state
when(tile._content.readyPromise).then(function(content) {
if (defined(tile.parent)) {
--tile.parent.numberOfChildrenWithoutContent;
}
}).otherwise(function(error) {
// In this case, that.parent.numberOfChildrenWithoutContent will never reach zero
// and therefore that.parent will never refine. If this becomes an issue, failed
// requests can be reissued.
});
}

/**
* Requests the tile's content.
* <p>
Expand Down Expand Up @@ -423,6 +411,34 @@ define([
return this._requestServer.hasAvailableRequests();
};

/**
* Unloads the tile's content and returns the tile's state to the state of when
* it was first created, before its content were loaded.
*
* @private
*/
Cesium3DTile.prototype.unloadContent = function() {
if (defined(this.parent)) {
++this.parent.numberOfChildrenWithoutContent;
}

this._content = this._content && this._content.destroy();
this._content = this._createContent();
addContentReadyPromise(this);

this.replacementNode = undefined;

// Restore properties set per frame to their defaults
this.distanceToCamera = 0;
this.parentPlaneMask = 0;
this.selected = false;
this.lastSelectedFrameNumber = 0;
this.lastStyleTime = 0;

this._debugBoundingVolume = this._debugBoundingVolume && this._debugBoundingVolume.destroy();
this._debugContentBoundingVolume = this._debugContentBoundingVolume && this._debugContentBoundingVolume.destroy();
};

/**
* Determines whether the tile's bounding volume intersects the culling volume.
*
Expand Down
4 changes: 2 additions & 2 deletions Source/Scene/Cesium3DTileBatchTableResources.js
Original file line number Diff line number Diff line change
Expand Up @@ -506,14 +506,14 @@ define([
' { \n' +
' if (!isStyleTranslucent && !tile_translucentCommand) \n' + // Do not render opaque features in the translucent pass
' { \n' +
' gl_Position *= 0.0; \n' +
' discard; \n' +
' } \n' +
' } \n' +
' else \n' +
' { \n' +
' if (isStyleTranslucent) \n' + // Do not render translucent features in the opaque pass
' { \n' +
' gl_Position *= 0.0; \n' +
' discard; \n' +
' } \n' +
' } \n' +
' tile_main(); \n' +
Expand Down