diff --git a/CHANGES.md b/CHANGES.md index 03f94d6d0734..3265e3010b81 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. +* 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 diff --git a/Source/Renderer/RenderState.js b/Source/Renderer/RenderState.js index 8be8fb966647..121263df4ebf 100644 --- a/Source/Renderer/RenderState.js +++ b/Source/Renderer/RenderState.js @@ -401,7 +401,8 @@ define([ var partialKey = JSON.stringify(renderState); var cachedState = renderStateCache[partialKey]; if (defined(cachedState)) { - return cachedState; + ++cachedState.referenceCount; + return cachedState.state; } // Cache miss. Fully define render state and try again. @@ -411,16 +412,71 @@ 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; + // 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.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; + + if (cachedState.referenceCount === 0) { + // remove partial key + delete renderStateCache[partialKey]; - return cachedState; + // decrement full key reference count + if (defined(fullCachedState)) { + --fullCachedState.referenceCount; + } + } + } + + // remove full key if reference count is zero + 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) { diff --git a/Source/Scene/TextureAtlas.js b/Source/Scene/TextureAtlas.js index de0e81bf9a35..d8a17038c345 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,28 @@ 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; + var renderState = { + viewport : new BoundingRectangle(0, 0, oldAtlasWidth, oldAtlasHeight) + }; + 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(); - newTexture.copyFromFramebuffer(0, 0, 0, 0, oldAtlasWidth, oldAtlasHeight); + command.execute(textureAtlas._context); framebuffer._unBind(); framebuffer.destroy(); textureAtlas._texture = newTexture; + + RenderState.removeFromCache(renderState); + command.renderState = undefined; } else { // First image exceeds initialSize var initialWidth = scalingFactor * (image.width + textureAtlas._borderWidthInPixels); 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({