Skip to content

Commit

Permalink
Merge pull request #2346 from AnalyticalGraphicsInc/the-need-for-speed
Browse files Browse the repository at this point in the history
Optimize GlobeSurfaceShaderSet.getShaderProgram
  • Loading branch information
pjcozzi committed Dec 24, 2014
2 parents e8976ef + 9e8093c commit fcc95cf
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 103 deletions.
80 changes: 10 additions & 70 deletions Source/Scene/Globe.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ define([

this._surfaceShaderSet = new GlobeSurfaceShaderSet();

this._surfaceShaderSet.baseVertexShaderSource = new ShaderSource({
sources : [GlobeVS]
});

this._surfaceShaderSet.baseFragmentShaderSource = new ShaderSource({
sources : [GlobeFS]
});

this._surface = new QuadtreePrimitive({
tileProvider : new GlobeSurfaceTileProvider({
terrainProvider : terrainProvider,
Expand Down Expand Up @@ -240,7 +248,6 @@ define([
* @default false
*/
this.enableLighting = false;
this._enableLighting = false;

/**
* The distance where everything becomes lit. This only takes effect
Expand Down Expand Up @@ -272,7 +279,6 @@ define([

this._oceanNormalMap = undefined;
this._zoomedOutOceanSpecularIntensity = 0.5;
this._hasVertexNormals = false;
this._lightingFadeDistance = new Cartesian2(this.lightingFadeOutDistance, this.lightingFadeInDistance);

var that = this;
Expand Down Expand Up @@ -872,80 +878,13 @@ define([
}
}

// Initial compile or re-compile if uber-shader parameters changed
var hasVertexNormals = terrainProvider.ready && terrainProvider.hasVertexNormals;
var enableLighting = this.enableLighting;

if (!defined(northPoleCommand.shaderProgram) ||
!defined(southPoleCommand.shaderProgram) ||
modeChanged ||
this._hasVertexNormals !== hasVertexNormals ||
this._enableLighting !== enableLighting) {

var getPosition3DMode = 'vec4 getPosition(vec3 position3DWC) { return getPosition3DMode(position3DWC); }';
var getPosition2DMode = 'vec4 getPosition(vec3 position3DWC) { return getPosition2DMode(position3DWC); }';
var getPositionColumbusViewMode = 'vec4 getPosition(vec3 position3DWC) { return getPositionColumbusViewMode(position3DWC); }';
var getPositionMorphingMode = 'vec4 getPosition(vec3 position3DWC) { return getPositionMorphingMode(position3DWC); }';

var getPositionMode;

switch (mode) {
case SceneMode.SCENE3D:
getPositionMode = getPosition3DMode;
break;
case SceneMode.SCENE2D:
getPositionMode = getPosition2DMode;
break;
case SceneMode.COLUMBUS_VIEW:
getPositionMode = getPositionColumbusViewMode;
break;
case SceneMode.MORPHING:
getPositionMode = getPositionMorphingMode;
break;
}

var get2DYPositionFractionGeographicProjection = 'float get2DYPositionFraction() { return get2DGeographicYPositionFraction(); }';
var get2DYPositionFractionMercatorProjection = 'float get2DYPositionFraction() { return get2DMercatorYPositionFraction(); }';

var get2DYPositionFraction;

if (projection instanceof GeographicProjection) {
get2DYPositionFraction = get2DYPositionFractionGeographicProjection;
} else {
get2DYPositionFraction = get2DYPositionFractionMercatorProjection;
}

var surfaceShaderSet = this._surfaceShaderSet;

var shaderDefines = [];

if (enableLighting) {
if (hasVertexNormals) {
shaderDefines.push('ENABLE_VERTEX_LIGHTING');
} else {
shaderDefines.push('ENABLE_DAYNIGHT_SHADING');
}
}

surfaceShaderSet.baseVertexShaderSource = new ShaderSource({
defines : shaderDefines,
sources : [GlobeVS, getPositionMode, get2DYPositionFraction]
});

surfaceShaderSet.baseFragmentShaderSource = new ShaderSource({
defines : shaderDefines,
sources : [GlobeFS]
});

surfaceShaderSet.invalidateShaders();
!defined(southPoleCommand.shaderProgram)) {

var poleShaderProgram = context.replaceShaderProgram(northPoleCommand.shaderProgram, GlobeVSPole, GlobeFSPole, terrainAttributeLocations);

northPoleCommand.shaderProgram = poleShaderProgram;
southPoleCommand.shaderProgram = poleShaderProgram;

this._hasVertexNormals = hasVertexNormals;
this._enableLighting = enableLighting;
}

this._occluder.cameraPosition = frameState.camera.positionWC;
Expand Down Expand Up @@ -981,6 +920,7 @@ define([
tileProvider.zoomedOutOceanSpecularIntensity = this._zoomedOutOceanSpecularIntensity;
tileProvider.hasWaterMask = hasWaterMask;
tileProvider.oceanNormalMap = this._oceanNormalMap;
tileProvider.enableLighting = this.enableLighting;

surface.update(context, frameState, commandList);

Expand Down
146 changes: 118 additions & 28 deletions Source/Scene/GlobeSurfaceShaderSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@
define([
'../Core/defined',
'../Core/destroyObject',
'../Scene/SceneMode',
'../Scene/terrainAttributeLocations'
], function(
defined,
destroyObject,
SceneMode,
terrainAttributeLocations) {
"use strict";

function GlobeSurfaceShader(numberOfDayTextures, flags, shaderProgram) {
this.numberOfDayTextures = numberOfDayTextures;
this.flags = flags;
this.shaderProgram = shaderProgram;
}

/**
* Manages the shaders used to shade the surface of a {@link Globe}.
*
Expand All @@ -19,32 +27,45 @@ define([
this.baseVertexShaderSource = undefined;
this.baseFragmentShaderSource = undefined;
this._attributeLocations = terrainAttributeLocations;
this._shaders = {};

this._shadersByTexturesFlags = [];
}

GlobeSurfaceShaderSet.prototype.invalidateShaders = function() {
var shaders = this._shaders;
for ( var keyword in shaders) {
if (shaders.hasOwnProperty(keyword)) {
shaders[keyword].destroy();
}
}
GlobeSurfaceShaderSet.prototype.getShaderProgram = function(context, sceneMode, surfaceTile, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves, enableLighting, hasVertexNormals, useWebMercatorProjection) {
var flags = sceneMode |
(applyBrightness << 2) |
(applyContrast << 3) |
(applyHue << 4) |
(applySaturation << 5) |
(applyGamma << 6) |
(applyAlpha << 7) |
(showReflectiveOcean << 8) |
(showOceanWaves << 9) |
(enableLighting << 10) |
(hasVertexNormals << 11) |
(useWebMercatorProjection << 12);

this._shaders = {};
};
var surfaceShader = surfaceTile.surfaceShader;
if (defined(surfaceShader) &&
surfaceShader.numberOfDayTextures === numberOfDayTextures &&
surfaceShader.flags === flags) {

function getShaderKey(textureCount, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves) {
return '' + textureCount + (+applyBrightness) + (+applyContrast) + (+applyHue) + (+applySaturation) + (+applyGamma) + (+applyAlpha) + (+showReflectiveOcean) + (+showOceanWaves);
}
return surfaceShader.shaderProgram;
}

// New tile, or tile changed number of textures or flags.
var shadersByFlags = this._shadersByTexturesFlags[numberOfDayTextures];
if (!defined(shadersByFlags)) {
shadersByFlags = this._shadersByTexturesFlags[numberOfDayTextures] = [];
}

GlobeSurfaceShaderSet.prototype.getShaderProgram = function(context, textureCount, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves) {
var key = getShaderKey(textureCount, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves);
var shader = this._shaders[key];
if (!defined(shader)) {
surfaceShader = shadersByFlags[flags];
if (!defined(surfaceShader)) {
// Cache miss - we've never seen this combination of numberOfDayTextures and flags before.
var vs = this.baseVertexShaderSource.clone();
var fs = this.baseFragmentShaderSource.clone();

fs.defines.push('TEXTURE_UNITS ' + textureCount);
fs.defines.push('TEXTURE_UNITS ' + numberOfDayTextures);

if (applyBrightness) {
fs.defines.push('APPLY_BRIGHTNESS');
Expand Down Expand Up @@ -72,12 +93,22 @@ define([
fs.defines.push('SHOW_OCEAN_WAVES');
}

if (enableLighting) {
if (hasVertexNormals) {
vs.defines.push('ENABLE_VERTEX_LIGHTING');
fs.defines.push('ENABLE_VERTEX_LIGHTING');
} else {
vs.defines.push('ENABLE_DAYNIGHT_SHADING');
fs.defines.push('ENABLE_DAYNIGHT_SHADING');
}
}

var computeDayColor = '\
vec4 computeDayColor(vec4 initialColor, vec2 textureCoordinates)\n\
{\n\
vec4 color = initialColor;\n';
vec4 computeDayColor(vec4 initialColor, vec2 textureCoordinates)\n\
{\n\
vec4 color = initialColor;\n';

for (var i = 0; i < textureCount; ++i) {
for (var i = 0; i < numberOfDayTextures; ++i) {
computeDayColor += '\
color = sampleAndBlend(\n\
color,\n\
Expand All @@ -95,19 +126,78 @@ vec4 computeDayColor(vec4 initialColor, vec2 textureCoordinates)\n\
}

computeDayColor += '\
return color;\n\
}';
return color;\n\
}';

fs.sources.push(computeDayColor);

shader = context.createShaderProgram(vs, fs, this._attributeLocations);
this._shaders[key] = shader;
var getPosition3DMode = 'vec4 getPosition(vec3 position3DWC) { return getPosition3DMode(position3DWC); }';
var getPosition2DMode = 'vec4 getPosition(vec3 position3DWC) { return getPosition2DMode(position3DWC); }';
var getPositionColumbusViewMode = 'vec4 getPosition(vec3 position3DWC) { return getPositionColumbusViewMode(position3DWC); }';
var getPositionMorphingMode = 'vec4 getPosition(vec3 position3DWC) { return getPositionMorphingMode(position3DWC); }';

var getPositionMode;

switch (sceneMode) {
case SceneMode.SCENE3D:
getPositionMode = getPosition3DMode;
break;
case SceneMode.SCENE2D:
getPositionMode = getPosition2DMode;
break;
case SceneMode.COLUMBUS_VIEW:
getPositionMode = getPositionColumbusViewMode;
break;
case SceneMode.MORPHING:
getPositionMode = getPositionMorphingMode;
break;
}

vs.sources.push(getPositionMode);

if (sceneMode !== SceneMode.SCENE3D) {
var get2DYPositionFractionGeographicProjection = 'float get2DYPositionFraction() { return get2DGeographicYPositionFraction(); }';
var get2DYPositionFractionMercatorProjection = 'float get2DYPositionFraction() { return get2DMercatorYPositionFraction(); }';

var get2DYPositionFraction;

if (useWebMercatorProjection) {
get2DYPositionFraction = get2DYPositionFractionMercatorProjection;
} else {
get2DYPositionFraction = get2DYPositionFractionGeographicProjection;
}

vs.sources.push(get2DYPositionFraction);
}

var shader = context.createShaderProgram(vs, fs, this._attributeLocations);
surfaceShader = shadersByFlags[flags] = new GlobeSurfaceShader(numberOfDayTextures, flags, shader);
}
return shader;

surfaceTile.surfaceShader = surfaceShader;
return surfaceShader.shaderProgram;
};

GlobeSurfaceShaderSet.prototype.destroy = function() {
this.invalidateShaders();
var shadersByTexturesFlags = this._shadersByTexturesFlags;
for (var textureCount in shadersByTexturesFlags) {
if (shadersByTexturesFlags.hasOwnProperty(textureCount)) {
var shadersByFlags = shadersByTexturesFlags[textureCount];
if (!defined(shadersByFlags)) {
continue;
}

for (var flags in shadersByFlags) {
if (shadersByFlags.hasOwnProperty(flags)) {
var shader = shadersByFlags[flags];
if (defined(shader)) {
shader.shaderProgram.destroy();
}
}
}
}
}

return destroyObject(this);
};

Expand Down
8 changes: 4 additions & 4 deletions Source/Scene/GlobeSurfaceTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ define([

this.pickBoundingSphere = new BoundingSphere();
this.pickTerrain = undefined;

this.surfaceShader = undefined;
};

defineProperties(GlobeSurfaceTile.prototype, {
Expand Down Expand Up @@ -265,8 +267,7 @@ define([
if (defined(this.vertexArray)) {
indexBuffer = this.vertexArray.indexBuffer;

this.vertexArray.destroy();
this.vertexArray = undefined;
this.vertexArray = this.vertexArray.destroy();

if (!indexBuffer.isDestroyed() && defined(indexBuffer.referenceCount)) {
--indexBuffer.referenceCount;
Expand All @@ -279,8 +280,7 @@ define([
if (defined(this.wireframeVertexArray)) {
indexBuffer = this.wireframeVertexArray.indexBuffer;

this.wireframeVertexArray.destroy();
this.wireframeVertexArray = undefined;
this.wireframeVertexArray = this.wireframeVertexArray.destroy();

if (!indexBuffer.isDestroyed() && defined(indexBuffer.referenceCount)) {
--indexBuffer.referenceCount;
Expand Down
8 changes: 7 additions & 1 deletion Source/Scene/GlobeSurfaceTileProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ define([
this.hasWaterMask = false;
this.oceanNormalMap = undefined;
this.zoomedOutOceanSpecularIntensity = 0.5;
this.enableLighting = false;

this._quadtree = undefined;
this._terrainProvider = options.terrainProvider;
Expand Down Expand Up @@ -806,6 +807,7 @@ define([
var showReflectiveOcean = tileProvider.hasWaterMask && defined(waterMaskTexture);
var oceanNormalMap = tileProvider.oceanNormalMap;
var showOceanWaves = showReflectiveOcean && defined(oceanNormalMap);
var hasVertexNormals = tileProvider.terrainProvider.ready && tileProvider.terrainProvider.hasVertexNormals;

if (showReflectiveOcean) {
--maxTextures;
Expand All @@ -826,6 +828,8 @@ define([
var southMercatorYLow = 0.0;
var oneOverMercatorHeight = 0.0;

var useWebMercatorProjection = false;

if (frameState.mode !== SceneMode.SCENE3D) {
var projection = frameState.mapProjection;
var southwest = projection.project(Rectangle.southwest(tile.rectangle), southwestScratch);
Expand Down Expand Up @@ -860,6 +864,8 @@ define([
southMercatorYLow = southMercatorY - float32ArrayScratch[0];

oneOverMercatorHeight = 1.0 / (northMercatorY - southMercatorY);

useWebMercatorProjection = true;
}
}

Expand Down Expand Up @@ -986,7 +992,7 @@ define([
uniformMap.waterMask = waterMaskTexture;
Cartesian4.clone(surfaceTile.waterMaskTranslationAndScale, uniformMap.waterMaskTranslationAndScale);

command.shaderProgram = tileProvider._surfaceShaderSet.getShaderProgram(context, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves);
command.shaderProgram = tileProvider._surfaceShaderSet.getShaderProgram(context, frameState.mode, surfaceTile, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves, tileProvider.enableLighting, hasVertexNormals, useWebMercatorProjection);
command.renderState = renderState;
command.primitiveType = PrimitiveType.TRIANGLES;
command.vertexArray = surfaceTile.vertexArray;
Expand Down

0 comments on commit fcc95cf

Please sign in to comment.