From f34d46a12fe3b5c0108cf65d500fa4f1ed7b9d5b Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Tue, 23 Dec 2014 14:39:57 -0500 Subject: [PATCH 1/5] Start of GlobeSurfaceShaderSet.getShaderProgram optimization --- Source/Scene/Globe.js | 4 - Source/Scene/GlobeSurfaceShaderSet.js | 150 ++++++++++++----------- Source/Scene/GlobeSurfaceTile.js | 15 +++ Source/Scene/GlobeSurfaceTileProvider.js | 2 +- 4 files changed, 92 insertions(+), 79 deletions(-) diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index 5a84444fed7a..4dd3509c65cb 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -937,8 +937,6 @@ define([ sources : [GlobeFS] }); - surfaceShaderSet.invalidateShaders(); - var poleShaderProgram = context.replaceShaderProgram(northPoleCommand.shaderProgram, GlobeVSPole, GlobeFSPole, terrainAttributeLocations); northPoleCommand.shaderProgram = poleShaderProgram; @@ -1037,8 +1035,6 @@ define([ this._northPoleCommand.vertexArray = this._northPoleCommand.vertexArray && this._northPoleCommand.vertexArray.destroy(); this._southPoleCommand.vertexArray = this._southPoleCommand.vertexArray && this._southPoleCommand.vertexArray.destroy(); - this._surfaceShaderSet = this._surfaceShaderSet && this._surfaceShaderSet.destroy(); - this._northPoleCommand.shaderProgram = this._northPoleCommand.shaderProgram && this._northPoleCommand.shaderProgram.destroy(); this._southPoleCommand.shaderProgram = this._northPoleCommand.shaderProgram; diff --git a/Source/Scene/GlobeSurfaceShaderSet.js b/Source/Scene/GlobeSurfaceShaderSet.js index 58cb3df3424c..6965236ed555 100644 --- a/Source/Scene/GlobeSurfaceShaderSet.js +++ b/Source/Scene/GlobeSurfaceShaderSet.js @@ -1,11 +1,9 @@ /*global define*/ define([ '../Core/defined', - '../Core/destroyObject', '../Scene/terrainAttributeLocations' ], function( defined, - destroyObject, terrainAttributeLocations) { "use strict"; @@ -19,97 +17,101 @@ define([ this.baseVertexShaderSource = undefined; this.baseFragmentShaderSource = undefined; this._attributeLocations = terrainAttributeLocations; - this._shaders = {}; } - 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) { + if (defined(surfaceTile.shaderProgram) && + ((surfaceTile.numberOfDayTextures === numberOfDayTextures) && + (surfaceTile.sceneMode === sceneMode) && + (surfaceTile.applyBrightness === applyBrightness) && + (surfaceTile.applyContrast === applyContrast) && + (surfaceTile.applyHue === applyHue) && + (surfaceTile.applySaturation === applySaturation) && + (surfaceTile.applyGamma === applyGamma) && + (surfaceTile.applyAlpha === applyAlpha) && + (surfaceTile.showReflectiveOcean === showReflectiveOcean) && + (surfaceTile.showOceanWaves === showOceanWaves))) { + + return surfaceTile.shaderProgram; } - this._shaders = {}; - }; + // Cache miss. + surfaceTile.shaderProgram = surfaceTile.shaderProgram && surfaceTile.shaderProgram.destroy(); + surfaceTile.numberOfDayTextures = numberOfDayTextures; + surfaceTile.sceneMode = sceneMode; + surfaceTile.applyBrightness = applyBrightness; + surfaceTile.applyContrast = applyContrast; + surfaceTile.applyHue = applyHue; + surfaceTile.applySaturation = applySaturation; + surfaceTile.applyGamma = applyGamma; + surfaceTile.applyAlpha = applyAlpha; + surfaceTile.showReflectiveOcean = showReflectiveOcean; + surfaceTile.showOceanWaves = showOceanWaves; - function getShaderKey(textureCount, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves) { - return '' + textureCount + (+applyBrightness) + (+applyContrast) + (+applyHue) + (+applySaturation) + (+applyGamma) + (+applyAlpha) + (+showReflectiveOcean) + (+showOceanWaves); - } + var vs = this.baseVertexShaderSource.clone(); + var fs = this.baseFragmentShaderSource.clone(); - 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)) { - var vs = this.baseVertexShaderSource.clone(); - var fs = this.baseFragmentShaderSource.clone(); + fs.defines.push('TEXTURE_UNITS ' + numberOfDayTextures); - fs.defines.push('TEXTURE_UNITS ' + textureCount); - - if (applyBrightness) { - fs.defines.push('APPLY_BRIGHTNESS'); - } - if (applyContrast) { - fs.defines.push('APPLY_CONTRAST'); - } - if (applyHue) { - fs.defines.push('APPLY_HUE'); - } - if (applySaturation) { - fs.defines.push('APPLY_SATURATION'); - } - if (applyGamma) { - fs.defines.push('APPLY_GAMMA'); - } - if (applyAlpha) { - fs.defines.push('APPLY_ALPHA'); - } - if (showReflectiveOcean) { - fs.defines.push('SHOW_REFLECTIVE_OCEAN'); - vs.defines.push('SHOW_REFLECTIVE_OCEAN'); - } - if (showOceanWaves) { - fs.defines.push('SHOW_OCEAN_WAVES'); - } + if (applyBrightness) { + fs.defines.push('APPLY_BRIGHTNESS'); + } + if (applyContrast) { + fs.defines.push('APPLY_CONTRAST'); + } + if (applyHue) { + fs.defines.push('APPLY_HUE'); + } + if (applySaturation) { + fs.defines.push('APPLY_SATURATION'); + } + if (applyGamma) { + fs.defines.push('APPLY_GAMMA'); + } + if (applyAlpha) { + fs.defines.push('APPLY_ALPHA'); + } + if (showReflectiveOcean) { + fs.defines.push('SHOW_REFLECTIVE_OCEAN'); + vs.defines.push('SHOW_REFLECTIVE_OCEAN'); + } + if (showOceanWaves) { + fs.defines.push('SHOW_OCEAN_WAVES'); + } - var computeDayColor = '\ + var computeDayColor = '\ vec4 computeDayColor(vec4 initialColor, vec2 textureCoordinates)\n\ {\n\ vec4 color = initialColor;\n'; - for (var i = 0; i < textureCount; ++i) { - computeDayColor += '\ - color = sampleAndBlend(\n\ - color,\n\ - u_dayTextures[' + i + '],\n\ - textureCoordinates,\n\ - u_dayTextureTexCoordsRectangle[' + i + '],\n\ - u_dayTextureTranslationAndScale[' + i + '],\n\ - ' + (applyAlpha ? 'u_dayTextureAlpha[' + i + ']' : '1.0') + ',\n\ - ' + (applyBrightness ? 'u_dayTextureBrightness[' + i + ']' : '0.0') + ',\n\ - ' + (applyContrast ? 'u_dayTextureContrast[' + i + ']' : '0.0') + ',\n\ - ' + (applyHue ? 'u_dayTextureHue[' + i + ']' : '0.0') + ',\n\ - ' + (applySaturation ? 'u_dayTextureSaturation[' + i + ']' : '0.0') + ',\n\ - ' + (applyGamma ? 'u_dayTextureOneOverGamma[' + i + ']' : '0.0') + '\n\ - );\n'; - } - + for (var i = 0; i < numberOfDayTextures; ++i) { computeDayColor += '\ +color = sampleAndBlend(\n\ + color,\n\ + u_dayTextures[' + i + '],\n\ + textureCoordinates,\n\ + u_dayTextureTexCoordsRectangle[' + i + '],\n\ + u_dayTextureTranslationAndScale[' + i + '],\n\ + ' + (applyAlpha ? 'u_dayTextureAlpha[' + i + ']' : '1.0') + ',\n\ + ' + (applyBrightness ? 'u_dayTextureBrightness[' + i + ']' : '0.0') + ',\n\ + ' + (applyContrast ? 'u_dayTextureContrast[' + i + ']' : '0.0') + ',\n\ + ' + (applyHue ? 'u_dayTextureHue[' + i + ']' : '0.0') + ',\n\ + ' + (applySaturation ? 'u_dayTextureSaturation[' + i + ']' : '0.0') + ',\n\ + ' + (applyGamma ? 'u_dayTextureOneOverGamma[' + i + ']' : '0.0') + '\n\ +);\n'; + } + + computeDayColor += '\ return color;\n\ }'; - fs.sources.push(computeDayColor); + fs.sources.push(computeDayColor); - shader = context.createShaderProgram(vs, fs, this._attributeLocations); - this._shaders[key] = shader; - } +// TODO: Surface tile should destroy this. + var shader = context.createShaderProgram(vs, fs, this._attributeLocations); + surfaceTile.shaderProgram = shader; return shader; }; - GlobeSurfaceShaderSet.prototype.destroy = function() { - this.invalidateShaders(); - return destroyObject(this); - }; - return GlobeSurfaceShaderSet; }); \ No newline at end of file diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index 8c423ee9ad2f..5af2e4ec3f45 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -131,6 +131,19 @@ define([ this.pickBoundingSphere = new BoundingSphere(); this.pickTerrain = undefined; + + // Cached shader program and cache key(s) + this.shaderProgram = undefined; + this.sceneMode = undefined; + this.numberOfDayTextures = 0; + this.applyBrightness = false; + this.applyContrast = false; + this.applyHue = false; + this.applySaturation = false; + this.applyGamma = false; + this.applyAlpha = false; + this.showReflectiveOcean = false; + this.showOceanWaves = false; }; defineProperties(GlobeSurfaceTile.prototype, { @@ -257,6 +270,8 @@ define([ this.imagery.length = 0; this.freeVertexArray(); +// TODO: +// this.shaderProgram = this.shaderProgram && this.shaderProgram.destroy(); }; GlobeSurfaceTile.prototype.freeVertexArray = function() { diff --git a/Source/Scene/GlobeSurfaceTileProvider.js b/Source/Scene/GlobeSurfaceTileProvider.js index 085ee9d0f4a1..289c427f3b63 100644 --- a/Source/Scene/GlobeSurfaceTileProvider.js +++ b/Source/Scene/GlobeSurfaceTileProvider.js @@ -986,7 +986,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); command.renderState = renderState; command.primitiveType = PrimitiveType.TRIANGLES; command.vertexArray = surfaceTile.vertexArray; From bf0b4088576209f4224749ab734532f529aedd0d Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Tue, 23 Dec 2014 15:50:43 -0500 Subject: [PATCH 2/5] Free shader program --- Source/Scene/GlobeSurfaceShaderSet.js | 2 +- Source/Scene/GlobeSurfaceTile.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Scene/GlobeSurfaceShaderSet.js b/Source/Scene/GlobeSurfaceShaderSet.js index 6965236ed555..5b29191a7a06 100644 --- a/Source/Scene/GlobeSurfaceShaderSet.js +++ b/Source/Scene/GlobeSurfaceShaderSet.js @@ -22,7 +22,7 @@ define([ GlobeSurfaceShaderSet.prototype.getShaderProgram = function(context, sceneMode, surfaceTile, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves) { if (defined(surfaceTile.shaderProgram) && ((surfaceTile.numberOfDayTextures === numberOfDayTextures) && - (surfaceTile.sceneMode === sceneMode) && + (surfaceTile.sceneMode === sceneMode) && // Position vertex attributes change (surfaceTile.applyBrightness === applyBrightness) && (surfaceTile.applyContrast === applyContrast) && (surfaceTile.applyHue === applyHue) && diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index 5af2e4ec3f45..deae141c5d05 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -270,8 +270,10 @@ define([ this.imagery.length = 0; this.freeVertexArray(); -// TODO: -// this.shaderProgram = this.shaderProgram && this.shaderProgram.destroy(); + + if (defined(this.shaderProgram)) { + this.shaderProgram = this.shaderProgram.destroy(); + } }; GlobeSurfaceTile.prototype.freeVertexArray = function() { @@ -280,8 +282,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; @@ -294,8 +295,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; From 5dc905514ad52045413565c36b75ec19afe845ca Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Tue, 23 Dec 2014 16:03:28 -0500 Subject: [PATCH 3/5] Remove TODO --- Source/Scene/GlobeSurfaceShaderSet.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Scene/GlobeSurfaceShaderSet.js b/Source/Scene/GlobeSurfaceShaderSet.js index 5b29191a7a06..3acf7fb12e4b 100644 --- a/Source/Scene/GlobeSurfaceShaderSet.js +++ b/Source/Scene/GlobeSurfaceShaderSet.js @@ -107,7 +107,6 @@ color = sampleAndBlend(\n\ fs.sources.push(computeDayColor); -// TODO: Surface tile should destroy this. var shader = context.createShaderProgram(vs, fs, this._attributeLocations); surfaceTile.shaderProgram = shader; return shader; From e5dcd610d5ff123ca24f3ef9d4940dfe1cfd40f7 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 24 Dec 2014 14:31:48 +1100 Subject: [PATCH 4/5] Further optimization of globe shader generation. --- Source/Scene/Globe.js | 80 ++------ Source/Scene/GlobeSurfaceShaderSet.js | 251 +++++++++++++++-------- Source/Scene/GlobeSurfaceTile.js | 17 +- Source/Scene/GlobeSurfaceTileProvider.js | 8 +- 4 files changed, 190 insertions(+), 166 deletions(-) diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index 4dd3509c65cb..265847de3837 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -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, @@ -240,7 +248,6 @@ define([ * @default false */ this.enableLighting = false; - this._enableLighting = false; /** * The distance where everything becomes lit. This only takes effect @@ -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; @@ -872,78 +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] - }); + !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; @@ -979,6 +920,7 @@ define([ tileProvider.zoomedOutOceanSpecularIntensity = this._zoomedOutOceanSpecularIntensity; tileProvider.hasWaterMask = hasWaterMask; tileProvider.oceanNormalMap = this._oceanNormalMap; + tileProvider.enableLighting = this.enableLighting; surface.update(context, frameState, commandList); @@ -1035,6 +977,8 @@ define([ this._northPoleCommand.vertexArray = this._northPoleCommand.vertexArray && this._northPoleCommand.vertexArray.destroy(); this._southPoleCommand.vertexArray = this._southPoleCommand.vertexArray && this._southPoleCommand.vertexArray.destroy(); + this._surfaceShaderSet = this._surfaceShaderSet && this._surfaceShaderSet.destroy(); + this._northPoleCommand.shaderProgram = this._northPoleCommand.shaderProgram && this._northPoleCommand.shaderProgram.destroy(); this._southPoleCommand.shaderProgram = this._northPoleCommand.shaderProgram; diff --git a/Source/Scene/GlobeSurfaceShaderSet.js b/Source/Scene/GlobeSurfaceShaderSet.js index 3acf7fb12e4b..0cee7bb1d890 100644 --- a/Source/Scene/GlobeSurfaceShaderSet.js +++ b/Source/Scene/GlobeSurfaceShaderSet.js @@ -1,12 +1,22 @@ /*global define*/ 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}. * @@ -17,99 +27,178 @@ define([ this.baseVertexShaderSource = undefined; this.baseFragmentShaderSource = undefined; this._attributeLocations = terrainAttributeLocations; + + this._shadersByTexturesFlags = []; } - GlobeSurfaceShaderSet.prototype.getShaderProgram = function(context, sceneMode, surfaceTile, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves) { - if (defined(surfaceTile.shaderProgram) && - ((surfaceTile.numberOfDayTextures === numberOfDayTextures) && - (surfaceTile.sceneMode === sceneMode) && // Position vertex attributes change - (surfaceTile.applyBrightness === applyBrightness) && - (surfaceTile.applyContrast === applyContrast) && - (surfaceTile.applyHue === applyHue) && - (surfaceTile.applySaturation === applySaturation) && - (surfaceTile.applyGamma === applyGamma) && - (surfaceTile.applyAlpha === applyAlpha) && - (surfaceTile.showReflectiveOcean === showReflectiveOcean) && - (surfaceTile.showOceanWaves === showOceanWaves))) { - - return surfaceTile.shaderProgram; - } + 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); - // Cache miss. - surfaceTile.shaderProgram = surfaceTile.shaderProgram && surfaceTile.shaderProgram.destroy(); - surfaceTile.numberOfDayTextures = numberOfDayTextures; - surfaceTile.sceneMode = sceneMode; - surfaceTile.applyBrightness = applyBrightness; - surfaceTile.applyContrast = applyContrast; - surfaceTile.applyHue = applyHue; - surfaceTile.applySaturation = applySaturation; - surfaceTile.applyGamma = applyGamma; - surfaceTile.applyAlpha = applyAlpha; - surfaceTile.showReflectiveOcean = showReflectiveOcean; - surfaceTile.showOceanWaves = showOceanWaves; - - var vs = this.baseVertexShaderSource.clone(); - var fs = this.baseFragmentShaderSource.clone(); - - fs.defines.push('TEXTURE_UNITS ' + numberOfDayTextures); - - if (applyBrightness) { - fs.defines.push('APPLY_BRIGHTNESS'); - } - if (applyContrast) { - fs.defines.push('APPLY_CONTRAST'); - } - if (applyHue) { - fs.defines.push('APPLY_HUE'); - } - if (applySaturation) { - fs.defines.push('APPLY_SATURATION'); - } - if (applyGamma) { - fs.defines.push('APPLY_GAMMA'); - } - if (applyAlpha) { - fs.defines.push('APPLY_ALPHA'); - } - if (showReflectiveOcean) { - fs.defines.push('SHOW_REFLECTIVE_OCEAN'); - vs.defines.push('SHOW_REFLECTIVE_OCEAN'); + var surfaceShader = surfaceTile.surfaceShader; + if (defined(surfaceShader) && + surfaceShader.numberOfDayTextures === numberOfDayTextures && + surfaceShader.flags === flags) { + + return surfaceShader.shaderProgram; } - if (showOceanWaves) { - fs.defines.push('SHOW_OCEAN_WAVES'); + + // New tile, or tile changed number of textures or flags. + var shadersByFlags = this._shadersByTexturesFlags[numberOfDayTextures]; + if (!defined(shadersByFlags)) { + shadersByFlags = this._shadersByTexturesFlags[numberOfDayTextures] = []; } - var computeDayColor = '\ -vec4 computeDayColor(vec4 initialColor, vec2 textureCoordinates)\n\ -{\n\ - vec4 color = initialColor;\n'; + var 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 ' + numberOfDayTextures); + + if (applyBrightness) { + fs.defines.push('APPLY_BRIGHTNESS'); + } + if (applyContrast) { + fs.defines.push('APPLY_CONTRAST'); + } + if (applyHue) { + fs.defines.push('APPLY_HUE'); + } + if (applySaturation) { + fs.defines.push('APPLY_SATURATION'); + } + if (applyGamma) { + fs.defines.push('APPLY_GAMMA'); + } + if (applyAlpha) { + fs.defines.push('APPLY_ALPHA'); + } + if (showReflectiveOcean) { + fs.defines.push('SHOW_REFLECTIVE_OCEAN'); + vs.defines.push('SHOW_REFLECTIVE_OCEAN'); + } + if (showOceanWaves) { + 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'; + + for (var i = 0; i < numberOfDayTextures; ++i) { + computeDayColor += '\ + color = sampleAndBlend(\n\ + color,\n\ + u_dayTextures[' + i + '],\n\ + textureCoordinates,\n\ + u_dayTextureTexCoordsRectangle[' + i + '],\n\ + u_dayTextureTranslationAndScale[' + i + '],\n\ + ' + (applyAlpha ? 'u_dayTextureAlpha[' + i + ']' : '1.0') + ',\n\ + ' + (applyBrightness ? 'u_dayTextureBrightness[' + i + ']' : '0.0') + ',\n\ + ' + (applyContrast ? 'u_dayTextureContrast[' + i + ']' : '0.0') + ',\n\ + ' + (applyHue ? 'u_dayTextureHue[' + i + ']' : '0.0') + ',\n\ + ' + (applySaturation ? 'u_dayTextureSaturation[' + i + ']' : '0.0') + ',\n\ + ' + (applyGamma ? 'u_dayTextureOneOverGamma[' + i + ']' : '0.0') + '\n\ + );\n'; + } - for (var i = 0; i < numberOfDayTextures; ++i) { computeDayColor += '\ -color = sampleAndBlend(\n\ - color,\n\ - u_dayTextures[' + i + '],\n\ - textureCoordinates,\n\ - u_dayTextureTexCoordsRectangle[' + i + '],\n\ - u_dayTextureTranslationAndScale[' + i + '],\n\ - ' + (applyAlpha ? 'u_dayTextureAlpha[' + i + ']' : '1.0') + ',\n\ - ' + (applyBrightness ? 'u_dayTextureBrightness[' + i + ']' : '0.0') + ',\n\ - ' + (applyContrast ? 'u_dayTextureContrast[' + i + ']' : '0.0') + ',\n\ - ' + (applyHue ? 'u_dayTextureHue[' + i + ']' : '0.0') + ',\n\ - ' + (applySaturation ? 'u_dayTextureSaturation[' + i + ']' : '0.0') + ',\n\ - ' + (applyGamma ? 'u_dayTextureOneOverGamma[' + i + ']' : '0.0') + '\n\ -);\n'; + return color;\n\ + }'; + + fs.sources.push(computeDayColor); + + 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); } - computeDayColor += '\ - return color;\n\ -}'; + surfaceTile.surfaceShader = surfaceShader; + return surfaceShader.shaderProgram; + }; + + GlobeSurfaceShaderSet.prototype.destroy = function() { + var shadersByTexturesFlags = this._shadersByTexturesFlags; + for (var textureCount in shadersByTexturesFlags) { + if (shadersByTexturesFlags.hasOwnProperty(textureCount)) { + var shadersByFlags = shadersByTexturesFlags[textureCount]; + if (!defined(shadersByFlags)) { + continue; + } - fs.sources.push(computeDayColor); + for (var flags in shadersByFlags) { + if (shadersByFlags.hasOwnProperty(flags)) { + var shader = shadersByFlags[flags]; + if (defined(shader)) { + shader.shaderProgram.destroy(); + } + } + } + } + } - var shader = context.createShaderProgram(vs, fs, this._attributeLocations); - surfaceTile.shaderProgram = shader; - return shader; + return destroyObject(this); }; return GlobeSurfaceShaderSet; diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index deae141c5d05..ba6039493603 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -132,18 +132,7 @@ define([ this.pickBoundingSphere = new BoundingSphere(); this.pickTerrain = undefined; - // Cached shader program and cache key(s) - this.shaderProgram = undefined; - this.sceneMode = undefined; - this.numberOfDayTextures = 0; - this.applyBrightness = false; - this.applyContrast = false; - this.applyHue = false; - this.applySaturation = false; - this.applyGamma = false; - this.applyAlpha = false; - this.showReflectiveOcean = false; - this.showOceanWaves = false; + this.surfaceShader = undefined; }; defineProperties(GlobeSurfaceTile.prototype, { @@ -270,10 +259,6 @@ define([ this.imagery.length = 0; this.freeVertexArray(); - - if (defined(this.shaderProgram)) { - this.shaderProgram = this.shaderProgram.destroy(); - } }; GlobeSurfaceTile.prototype.freeVertexArray = function() { diff --git a/Source/Scene/GlobeSurfaceTileProvider.js b/Source/Scene/GlobeSurfaceTileProvider.js index 289c427f3b63..e48671865475 100644 --- a/Source/Scene/GlobeSurfaceTileProvider.js +++ b/Source/Scene/GlobeSurfaceTileProvider.js @@ -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; @@ -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; @@ -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); @@ -860,6 +864,8 @@ define([ southMercatorYLow = southMercatorY - float32ArrayScratch[0]; oneOverMercatorHeight = 1.0 / (northMercatorY - southMercatorY); + + useWebMercatorProjection = true; } } @@ -986,7 +992,7 @@ define([ uniformMap.waterMask = waterMaskTexture; Cartesian4.clone(surfaceTile.waterMaskTranslationAndScale, uniformMap.waterMaskTranslationAndScale); - command.shaderProgram = tileProvider._surfaceShaderSet.getShaderProgram(context, frameState.mode, surfaceTile, 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; From 9e8093ce3d94fc3f9de331fb93e9824a61d9b7b5 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 24 Dec 2014 14:41:12 +1100 Subject: [PATCH 5/5] Fix jshint warning. --- Source/Scene/GlobeSurfaceShaderSet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Scene/GlobeSurfaceShaderSet.js b/Source/Scene/GlobeSurfaceShaderSet.js index 0cee7bb1d890..f99eb8a090ff 100644 --- a/Source/Scene/GlobeSurfaceShaderSet.js +++ b/Source/Scene/GlobeSurfaceShaderSet.js @@ -59,7 +59,7 @@ define([ shadersByFlags = this._shadersByTexturesFlags[numberOfDayTextures] = []; } - var surfaceShader = shadersByFlags[flags]; + surfaceShader = shadersByFlags[flags]; if (!defined(surfaceShader)) { // Cache miss - we've never seen this combination of numberOfDayTextures and flags before. var vs = this.baseVertexShaderSource.clone();