From 629cf655de67ff6aba32b36bef68031c113070e0 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 9 Sep 2015 15:22:35 -0400 Subject: [PATCH 1/8] Render to texture instead of copy from framebuffer on texture atlas resize. --- Source/Renderer/RenderState.js | 17 ++++++++++++++++ Source/Scene/TextureAtlas.js | 37 +++++++++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/Source/Renderer/RenderState.js b/Source/Renderer/RenderState.js index 3d7ab0356571..91b9bcf530b2 100644 --- a/Source/Renderer/RenderState.js +++ b/Source/Renderer/RenderState.js @@ -422,6 +422,23 @@ define([ return cachedState; }; + /** + * Use sparingly until we have a real solution for removing render states from the cache, e.g. TextureAtlas + * polutting the cache on resize. + * + * @private + */ + RenderState.removeFromCache = function(renderState) { + // remove full key + var states = new RenderState(renderState); + var fullKey = JSON.stringify(states); + delete renderStateCache[fullKey]; + + // remove partial key + var partialKey = JSON.stringify(renderState); + delete renderStateCache[partialKey]; + }; + function enableOrDisable(gl, glEnum, enable) { if (enable) { gl.enable(glEnum); diff --git a/Source/Scene/TextureAtlas.js b/Source/Scene/TextureAtlas.js index de0e81bf9a35..865891b4fac6 100644 --- a/Source/Scene/TextureAtlas.js +++ b/Source/Scene/TextureAtlas.js @@ -12,6 +12,7 @@ define([ '../Core/PixelFormat', '../Core/RuntimeError', '../Renderer/Framebuffer', + '../Renderer/RenderState', '../Renderer/Texture', '../ThirdParty/when' ], function( @@ -27,6 +28,7 @@ define([ PixelFormat, RuntimeError, Framebuffer, + RenderState, Texture, when) { "use strict"; @@ -95,6 +97,26 @@ define([ pixelFormat : this._pixelFormat }); this._root = new TextureAtlasNode(new Cartesian2(), new Cartesian2(initialSize.x, initialSize.y)); + + var that = this; + var uniformMap = { + u_texture : function() { + return that._texture; + } + }; + + var fs = + 'uniform sampler2D u_texture;\n' + + 'varying vec2 v_textureCoordinates;\n' + + 'void main()\n' + + '{\n' + + ' gl_FragColor = texture2D(u_texture, v_textureCoordinates);\n' + + '}\n'; + + + this._copyCommand = this._context.createViewportQuadCommand(fs, { + uniformMap : uniformMap + }); }; defineProperties(TextureAtlas.prototype, { @@ -201,16 +223,25 @@ define([ pixelFormat : textureAtlas._pixelFormat }); - // Copy old texture into new using an fbo. var framebuffer = new Framebuffer({ context : context, - colorTextures : [textureAtlas._texture] + colorTextures : [newTexture], + destroyAttachments : false + }); + + var command = textureAtlas._copyCommand; + command.renderState = RenderState.fromCache({ + viewport : new BoundingRectangle(0, 0, oldAtlasWidth, oldAtlasHeight) }); + framebuffer._bind(); - newTexture.copyFromFramebuffer(0, 0, 0, 0, oldAtlasWidth, oldAtlasHeight); + command.execute(textureAtlas._context); framebuffer._unBind(); framebuffer.destroy(); textureAtlas._texture = newTexture; + + RenderState.removeFromCache(command.renderState); + command.renderState = undefined; } else { // First image exceeds initialSize var initialWidth = scalingFactor * (image.width + textureAtlas._borderWidthInPixels); From 2a2d8848e45d157ac39b95a0048848aa4a0f46ce Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Wed, 9 Sep 2015 17:11:13 -0400 Subject: [PATCH 2/8] Tweak comment --- Source/Renderer/RenderState.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Renderer/RenderState.js b/Source/Renderer/RenderState.js index 5c4e734d3816..8261d9334325 100644 --- a/Source/Renderer/RenderState.js +++ b/Source/Renderer/RenderState.js @@ -429,12 +429,12 @@ define([ * @private */ RenderState.removeFromCache = function(renderState) { - // remove full key + // remove full key if it exists var states = new RenderState(renderState); var fullKey = JSON.stringify(states); delete renderStateCache[fullKey]; - // remove partial key + // remove partial key if it exists var partialKey = JSON.stringify(renderState); delete renderStateCache[partialKey]; }; From b91476f7240089919367ad2404aa5ce1cb2e448a Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 15 Sep 2015 10:46:44 -0400 Subject: [PATCH 3/8] Add reference counting to render states for adding/removing from the cache. --- Source/Renderer/RenderState.js | 30 +++++++++++++++++++++--------- Source/Scene/TextureAtlas.js | 7 ++++--- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/Source/Renderer/RenderState.js b/Source/Renderer/RenderState.js index 66ad6a5fe641..a9f0cad75486 100644 --- a/Source/Renderer/RenderState.js +++ b/Source/Renderer/RenderState.js @@ -272,6 +272,7 @@ define([ //>>includeEnd('debug'); this.id = 0; + this._referenceCount = 0; this._applyFunctions = []; }; @@ -401,6 +402,7 @@ define([ var partialKey = JSON.stringify(renderState); var cachedState = renderStateCache[partialKey]; if (defined(cachedState)) { + ++cachedState._referenceCount; return cachedState; } @@ -417,6 +419,8 @@ define([ renderStateCache[fullKey] = cachedState; } + ++cachedState._referenceCount; + // Cache partial render state so we can skip validation on a cache hit for a partially defined render state renderStateCache[partialKey] = cachedState; @@ -424,20 +428,28 @@ define([ }; /** - * Use sparingly until we have a real solution for removing render states from the cache, e.g. TextureAtlas - * polutting the cache on resize. - * * @private */ RenderState.removeFromCache = function(renderState) { - // remove full key if it exists + // decrement partial key reference count + var partialKey = JSON.stringify(renderState); + var cachedState = renderStateCache[partialKey]; + if (defined(cachedState)) { + --cachedState._referenceCount; + + if (cachedState._referenceCount === 0) { + // remove partial key + delete renderStateCache[partialKey]; + } + } + + // remove full key if reference count is zero var states = new RenderState(renderState); var fullKey = JSON.stringify(states); - delete renderStateCache[fullKey]; - - // remove partial key if it exists - var partialKey = JSON.stringify(renderState); - delete renderStateCache[partialKey]; + cachedState = renderStateCache[fullKey]; + if (defined(cachedState) && cachedState._referenceCount === 0) { + delete renderStateCache[fullKey]; + } }; function enableOrDisable(gl, glEnum, enable) { diff --git a/Source/Scene/TextureAtlas.js b/Source/Scene/TextureAtlas.js index 865891b4fac6..8ed97b0107a9 100644 --- a/Source/Scene/TextureAtlas.js +++ b/Source/Scene/TextureAtlas.js @@ -230,9 +230,10 @@ define([ }); var command = textureAtlas._copyCommand; - command.renderState = RenderState.fromCache({ + var renderState = { viewport : new BoundingRectangle(0, 0, oldAtlasWidth, oldAtlasHeight) - }); + }; + command.renderState = RenderState.fromCache(renderState); framebuffer._bind(); command.execute(textureAtlas._context); @@ -240,7 +241,7 @@ define([ framebuffer.destroy(); textureAtlas._texture = newTexture; - RenderState.removeFromCache(command.renderState); + RenderState.removeFromCache(renderState); command.renderState = undefined; } else { // First image exceeds initialSize From 089758a47f6a1d1b84f35b8cef3b6437d6e03c34 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 15 Sep 2015 10:49:27 -0400 Subject: [PATCH 4/8] Update CHANGES.md. --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 03f94d6d0734..8f219a35c4fc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ Change Log * Fixed an issue with drill picking at low frame rates that would cause a crash. [#3010](https://github.com/AnalyticalGraphicsInc/cesium/pull/3010) * Fixed a bug that prevented `setView` from working across all scene modes. * Fixed a bug that caused `camera.positionWC` to occasionally return the incorrect value. +* Fixed a bug where glyphs of labels with small font sizes would not render. [#3011](https://github.com/AnalyticalGraphicsInc/cesium/pull/3011) ### 1.13 - 2015-09-01 From d996ba310c9d0d4ecc13edc5c9d9048cfb077c42 Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Tue, 15 Sep 2015 11:08:50 -0400 Subject: [PATCH 5/8] Tweak CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 8f219a35c4fc..3265e3010b81 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,7 +15,7 @@ Change Log * Fixed an issue with drill picking at low frame rates that would cause a crash. [#3010](https://github.com/AnalyticalGraphicsInc/cesium/pull/3010) * Fixed a bug that prevented `setView` from working across all scene modes. * Fixed a bug that caused `camera.positionWC` to occasionally return the incorrect value. -* Fixed a bug where glyphs of labels with small font sizes would not render. [#3011](https://github.com/AnalyticalGraphicsInc/cesium/pull/3011) +* Added a workaround for Chrome 45, where the first character in a label with a small font size would not appear. [#3011](https://github.com/AnalyticalGraphicsInc/cesium/pull/3011) ### 1.13 - 2015-09-01 From 5347b0a33e6ec9619ad25df4d5cd050992872f13 Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Tue, 15 Sep 2015 11:11:41 -0400 Subject: [PATCH 6/8] Tweak --- Source/Renderer/RenderState.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Renderer/RenderState.js b/Source/Renderer/RenderState.js index a9f0cad75486..237ab3f0bf58 100644 --- a/Source/Renderer/RenderState.js +++ b/Source/Renderer/RenderState.js @@ -447,7 +447,7 @@ define([ var states = new RenderState(renderState); var fullKey = JSON.stringify(states); cachedState = renderStateCache[fullKey]; - if (defined(cachedState) && cachedState._referenceCount === 0) { + if (defined(cachedState) && (cachedState._referenceCount === 0)) { delete renderStateCache[fullKey]; } }; From 3da95af44902d8d630f5c816bfd43debdc304b15 Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Tue, 15 Sep 2015 11:13:53 -0400 Subject: [PATCH 7/8] Add comment --- Source/Scene/TextureAtlas.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Scene/TextureAtlas.js b/Source/Scene/TextureAtlas.js index 8ed97b0107a9..d8a17038c345 100644 --- a/Source/Scene/TextureAtlas.js +++ b/Source/Scene/TextureAtlas.js @@ -235,6 +235,8 @@ define([ }; command.renderState = RenderState.fromCache(renderState); + // Copy by rendering a viewport quad, instead of using Texture.copyFromFramebuffer, + // to workaround a Chrome 45 issue, https://github.com/AnalyticalGraphicsInc/cesium/issues/2997 framebuffer._bind(); command.execute(textureAtlas._context); framebuffer._unBind(); From 000291f13475e01dd117f19101e44bfc66f024aa Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 16 Sep 2015 10:31:38 -0400 Subject: [PATCH 8/8] Change how render states are reference counted. Add unit test. --- Source/Renderer/RenderState.js | 53 +++++++++++++++++++++++-------- Specs/Renderer/RenderStateSpec.js | 46 +++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 13 deletions(-) diff --git a/Source/Renderer/RenderState.js b/Source/Renderer/RenderState.js index 237ab3f0bf58..121263df4ebf 100644 --- a/Source/Renderer/RenderState.js +++ b/Source/Renderer/RenderState.js @@ -272,7 +272,6 @@ define([ //>>includeEnd('debug'); this.id = 0; - this._referenceCount = 0; this._applyFunctions = []; }; @@ -402,8 +401,8 @@ define([ var partialKey = JSON.stringify(renderState); var cachedState = renderStateCache[partialKey]; if (defined(cachedState)) { - ++cachedState._referenceCount; - return cachedState; + ++cachedState.referenceCount; + return cachedState.state; } // Cache miss. Fully define render state and try again. @@ -413,45 +412,73 @@ define([ if (!defined(cachedState)) { states.id = nextRenderStateId++; - cachedState = states; + cachedState = { + referenceCount : 0, + state : states + }; // Cache full render state. Multiple partially defined render states may map to this. renderStateCache[fullKey] = cachedState; } - ++cachedState._referenceCount; + ++cachedState.referenceCount; // Cache partial render state so we can skip validation on a cache hit for a partially defined render state - renderStateCache[partialKey] = cachedState; + renderStateCache[partialKey] = { + referenceCount : 1, + state : cachedState.state + }; - return cachedState; + return cachedState.state; }; /** * @private */ RenderState.removeFromCache = function(renderState) { + var states = new RenderState(renderState); + var fullKey = JSON.stringify(states); + var fullCachedState = renderStateCache[fullKey]; + // decrement partial key reference count var partialKey = JSON.stringify(renderState); var cachedState = renderStateCache[partialKey]; if (defined(cachedState)) { - --cachedState._referenceCount; + --cachedState.referenceCount; - if (cachedState._referenceCount === 0) { + if (cachedState.referenceCount === 0) { // remove partial key delete renderStateCache[partialKey]; + + // decrement full key reference count + if (defined(fullCachedState)) { + --fullCachedState.referenceCount; + } } } // remove full key if reference count is zero - var states = new RenderState(renderState); - var fullKey = JSON.stringify(states); - cachedState = renderStateCache[fullKey]; - if (defined(cachedState) && (cachedState._referenceCount === 0)) { + if (defined(fullCachedState) && (fullCachedState.referenceCount === 0)) { delete renderStateCache[fullKey]; } }; + /** + * This function is for testing purposes only. + * @private + */ + RenderState.getCache = function() { + return renderStateCache; + }; + + /** + * This function is for testing purposes only. + * @private + */ + RenderState.clearCache = function() { + renderStateCache = {}; + }; + function enableOrDisable(gl, glEnum, enable) { if (enable) { gl.enable(glEnum); diff --git a/Specs/Renderer/RenderStateSpec.js b/Specs/Renderer/RenderStateSpec.js index 1b559ec45063..58172641ae0b 100644 --- a/Specs/Renderer/RenderStateSpec.js +++ b/Specs/Renderer/RenderStateSpec.js @@ -363,6 +363,52 @@ defineSuite([ expect(rs4).not.toBe(rs); }); + it('removes from render cache', function() { + RenderState.clearCache(); + var cache = RenderState.getCache(); + + var rs = RenderState.fromCache(); + var undefinedKey = JSON.stringify(undefined); + var fullKey = JSON.stringify(new RenderState()); + + expect(cache[fullKey].referenceCount).toEqual(1); + expect(cache[undefinedKey].referenceCount).toEqual(1); + + var rs2 = RenderState.fromCache(); + + expect(cache[fullKey].referenceCount).toEqual(1); + expect(cache[undefinedKey].referenceCount).toEqual(2); + + // rs3 is still the same state as rs and rs2, but with a partial definition + var param = { + depthTest : { + enabled : false, + func : WebGLConstants.LESS + } + }; + var rs3 = RenderState.fromCache(param); + var paramKey = JSON.stringify(param); + + expect(rs2).toBe(rs); + expect(rs3).toBe(rs); + + expect(cache[fullKey].referenceCount).toEqual(2); + expect(cache[undefinedKey].referenceCount).toEqual(2); + expect(cache[paramKey].referenceCount).toEqual(1); + + RenderState.removeFromCache(param); + + expect(cache[fullKey].referenceCount).toEqual(1); + expect(cache[undefinedKey].referenceCount).toEqual(2); + expect(cache[paramKey]).not.toBeDefined(); + + RenderState.removeFromCache(); + RenderState.removeFromCache(); + + expect(cache[undefinedKey]).not.toBeDefined(); + expect(cache[fullKey]).not.toBeDefined(); + }); + it('fails to create (frontFace)', function() { expect(function() { RenderState.fromCache({