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

Widget and viewer improvements #1674

Merged
merged 10 commits into from
May 5, 2014
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ Beta Releases

### b29 - 2014-06-02

* Breaking changes
* Removed `CesiumWidget.onRenderLoopError` and `Viewer.renderLoopError`. They have been replaced by `Scene.renderError`.
* Improved terrain and imagery rendering performance when very close to the surface.
* Added `preRender` and `postRender` events to `Scene`.

### b28 - 2014-05-01

Expand Down
143 changes: 104 additions & 39 deletions Source/Scene/Scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,21 @@ define([

this._transitioner = new SceneTransitioner(this);

this._renderError = new Event();
this._preRender = new Event();
this._postRender = new Event();

/**
* Exceptions occurring in <code>render</code> are always caught in order to raise the
* <code>renderError</code> event. If this property is true, the error is rethrown
* after the event is raised. If this property is false, the <code>render</code> function
* returns normally after raising the event.
*
* @type {Boolean}
* @default false
*/
this.rethrowRenderErrors = false;

/**
* Determines whether or not to instantly complete the
* scene transition animation on user input.
Expand Down Expand Up @@ -530,7 +545,7 @@ define([
* @memberof Scene.prototype
* @type {ImageryLayerCollection}
*/
imageryLayers: {
imageryLayers : {
get : function() {
return this.globe.imageryLayers;
}
Expand All @@ -541,13 +556,51 @@ define([
* @memberof Scene.prototype
* @type {TerrainProvider}
*/
terrainProvider: {
terrainProvider : {
get : function() {
return this.globe.terrainProvider;
},
set : function(terrainProvider) {
this.globe.terrainProvider = terrainProvider;
}
},

/**
* Gets the event that will be raised when an error is thrown inside the <code>render</code> function.
* The Scene instance and the thrown error are the only two parameters passed to the event handler.
* By default, errors are not rethrown after this event is raised, but that can be changed by setting
* the <code>rethrowRenderErrors</code> property.
* @memberof Scene.prototype
* @type {Event}
*/
renderError : {
get : function() {
return this._renderError;
}
},

/**
* Gets the event that will be raised at the start of each call to <code>render</code>. Subscribers to the event
* receive the Scene instance as the first parameter and the current time as the second parameter.
* @memberof Scene.prototype
* @type {Event}
*/
preRender : {
get : function() {
return this._preRender;
}
},

/**
* Gets the event that will be raised at the end of each call to <code>render</code>. Subscribers to the event
* receive the Scene instance as the first parameter and the current time as the second parameter.
* @memberof Scene.prototype
* @type {Event}
*/
postRender : {
get : function() {
return this._postRender;
}
}
});

Expand Down Expand Up @@ -1142,55 +1195,67 @@ define([
* @memberof Scene
*/
Scene.prototype.render = function(time) {
if (!defined(time)) {
time = new JulianDate();
}
try {
Copy link
Contributor

Choose a reason for hiding this comment

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

try/catch causes optimizer bailouts in v8 for the entire function. factor out all this logic into a helper method to keep the scope of the try-catch as small as possible.

if (!defined(time)) {
time = new JulianDate();
}

var us = this._context.uniformState;
var frameState = this._frameState;
this._preRender.raiseEvent(this, time);

var frameNumber = CesiumMath.incrementWrap(frameState.frameNumber, 15000000.0, 1.0);
updateFrameState(this, frameNumber, time);
frameState.passes.render = true;
frameState.creditDisplay.beginFrame();
var us = this._context.uniformState;
var frameState = this._frameState;

var context = this._context;
us.update(context, frameState);
var frameNumber = CesiumMath.incrementWrap(frameState.frameNumber, 15000000.0, 1.0);
updateFrameState(this, frameNumber, time);
frameState.passes.render = true;
frameState.creditDisplay.beginFrame();

this._commandList.length = 0;
this._overlayCommandList.length = 0;
var context = this._context;
us.update(context, frameState);

updatePrimitives(this);
createPotentiallyVisibleSet(this);
this._commandList.length = 0;
this._overlayCommandList.length = 0;

updatePrimitives(this);
createPotentiallyVisibleSet(this);

var passState = this._passState;

var passState = this._passState;
executeCommands(this, passState, defaultValue(this.backgroundColor, Color.BLACK));
executeOverlayCommands(this, passState);

executeCommands(this, passState, defaultValue(this.backgroundColor, Color.BLACK));
executeOverlayCommands(this, passState);
frameState.creditDisplay.endFrame();

frameState.creditDisplay.endFrame();
if (this.debugShowFramesPerSecond) {
if (!defined(this._performanceDisplay)) {
var performanceContainer = document.createElement('div');
performanceContainer.style.position = 'absolute';
performanceContainer.style.top = '10px';
performanceContainer.style.left = '10px';
var container = this._canvas.parentNode;
container.appendChild(performanceContainer);
var performanceDisplay = new PerformanceDisplay({container: performanceContainer});
this._performanceDisplay = performanceDisplay;
this._performanceContainer = performanceContainer;
}

if (this.debugShowFramesPerSecond) {
if (!defined(this._performanceDisplay)) {
var performanceContainer = document.createElement('div');
performanceContainer.style.position = 'absolute';
performanceContainer.style.top = '10px';
performanceContainer.style.left = '10px';
var container = this._canvas.parentNode;
container.appendChild(performanceContainer);
var performanceDisplay = new PerformanceDisplay({container: performanceContainer});
this._performanceDisplay = performanceDisplay;
this._performanceContainer = performanceContainer;
this._performanceDisplay.update();
} else if (defined(this._performanceDisplay)) {
this._performanceDisplay = this._performanceDisplay && this._performanceDisplay.destroy();
this._performanceContainer.parentNode.removeChild(this._performanceContainer);
}

this._performanceDisplay.update();
} else if (defined(this._performanceDisplay)) {
this._performanceDisplay = this._performanceDisplay && this._performanceDisplay.destroy();
this._performanceContainer.parentNode.removeChild(this._performanceContainer);
}
context.endFrame();
callAfterRenderFunctions(frameState);

context.endFrame();
callAfterRenderFunctions(frameState);
this._postRender.raiseEvent(this, time);
} catch (error) {
this._renderError.raiseEvent(this, error);

if (this.rethrowRenderErrors) {
throw error;
}
}
};

var orthoPickingFrustum = new OrthographicFrustum();
Expand Down
48 changes: 16 additions & 32 deletions Source/Widgets/CesiumWidget/CesiumWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,24 +61,12 @@ define([
return;
}

try {
if (widget._useDefaultRenderLoop) {
widget.resize();
widget.render();
requestAnimationFrame(render);
} else {
widget._renderLoopRunning = false;
}
} catch (error) {
widget._useDefaultRenderLoop = false;
if (widget._useDefaultRenderLoop) {
widget.resize();
widget.render();
requestAnimationFrame(render);
} else {
widget._renderLoopRunning = false;
widget._renderLoopError.raiseEvent(widget, error);
if (widget._showRenderLoopErrors) {
var title = 'An error occurred while rendering. Rendering has stopped.';
var message = formatError(error);
widget.showErrorPanel(title, message);
console.error(title + ' ' + message);
}
}
}

Expand Down Expand Up @@ -234,7 +222,6 @@ define([
this._creditContainer = creditContainer;
this._canRender = false;
this._showRenderLoopErrors = defaultValue(options.showRenderLoopErrors, true);
this._renderLoopError = new Event();

if (options.sceneMode) {
if (options.sceneMode === SceneMode.SCENE2D) {
Expand All @@ -247,6 +234,17 @@ define([

this.useDefaultRenderLoop = defaultValue(options.useDefaultRenderLoop, true);

var that = this;
scene.renderError.addEventListener(function(scene, error) {
that._useDefaultRenderLoop = false;
that._renderLoopRunning = false;
if (that._showRenderLoopErrors) {
var title = 'An error occurred while rendering. Rendering has stopped.';
var message = formatError(error);
that.showErrorPanel(title, message);
console.error(title + ' ' + message);
}
});
} catch (error) {
var title = 'Error constructing CesiumWidget. Check if WebGL is enabled.';
this.showErrorPanel(title, error);
Expand Down Expand Up @@ -327,20 +325,6 @@ define([
}
},

/**
* Gets the event that will be raised when an error is encountered during the default render loop.
* The widget instance and the generated exception are the only two parameters passed to the event handler.
* <code>useDefaultRenderLoop</code> will be set to false whenever an exception is generated and must
* be set back to true to continue rendering after an exception.
* @memberof Viewer.prototype
* @type {Event}
*/
onRenderLoopError : {
get : function() {
return this._renderLoopError;
}
},

/**
* Gets or sets whether or not this widget should control the render loop.
* If set to true the widget will use {@link requestAnimationFrame} to
Expand Down
Loading