From 2faf138e8a4444c3e92f68e91c07cb28bef4b20a Mon Sep 17 00:00:00 2001 From: Matthew Amato Date: Mon, 29 Jun 2015 14:26:26 -0400 Subject: [PATCH] Fix material caching for canvases Cesium would incorrectly use an HTMLCanvasElement as the key to the Material texture cache dictionary. This means that if you had multiple canvas-based materials, we would only ever use the first one that was added (because the cache lookup would always succeed even if the canvases were difference). This change uses toDataURL to store canvas elements in the cache. This is sub-optimal since toDataURL is slow and also creates huge string, but the best we can do in the short term. We need a good overlal long term strategy for dealing with canvases, but that's outside the scope of this PR. --- Source/Scene/Material.js | 14 ++++++++++- Specs/Scene/MaterialSpec.js | 49 +++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/Source/Scene/Material.js b/Source/Scene/Material.js index 149a6eac9dba..5cbc5df51c35 100644 --- a/Source/Scene/Material.js +++ b/Source/Scene/Material.js @@ -949,6 +949,10 @@ define([ _textures : {}, addTexture : function(path, texture) { + if (path instanceof HTMLCanvasElement) { + path = path.toDataURL(); + } + this._textures[path] = { texture : texture, count : 1 @@ -956,6 +960,10 @@ define([ }, getTexture : function(path) { + if (path instanceof HTMLCanvasElement) { + path = path.toDataURL(); + } + var entry = this._textures[path]; if (defined(entry)) { @@ -967,10 +975,14 @@ define([ }, releaseTexture : function(path) { + if (path instanceof HTMLCanvasElement) { + path = path.toDataURL(); + } + var entry = this._textures[path]; if (defined(entry) && --entry.count === 0) { entry.texture = entry.texture && entry.texture.destroy(); - this._textures[path] = undefined; + delete this._textures[path]; } } }; diff --git a/Specs/Scene/MaterialSpec.js b/Specs/Scene/MaterialSpec.js index b3136e404e73..f135ef28c415 100644 --- a/Specs/Scene/MaterialSpec.js +++ b/Specs/Scene/MaterialSpec.js @@ -778,4 +778,53 @@ defineSuite([ expect(material.isDestroyed()).toEqual(true); expect(diffuseMap.isDestroyed()).toEqual(true); }); + + it('_textureCache works with canvas elements', function() { + var mockTexture = { + destroy : function() { + } + }; + + var canvas = document.createElement('canvas'); + canvas.height = 1; + canvas.width = 1; + var context = canvas.getContext('2d'); + context.rect(0,0,1,1); + context.fillStyle = 'red'; + context.fill(); + + var mockTexture2 = { + destroy : function() { + } + }; + + var canvas2 = document.createElement('canvas'); + canvas2.height = 1; + canvas2.width = 1; + context = canvas.getContext('2d'); + context.rect(0,0,1,1); + context.fillStyle = 'yellow'; + context.fill(); + + expect(Object.keys(Material._textureCache._textures).length).toEqual(0); + + Material._textureCache.addTexture(canvas, mockTexture); + expect(Object.keys(Material._textureCache._textures).length).toEqual(1); + + Material._textureCache.addTexture(canvas2, mockTexture2); + expect(Object.keys(Material._textureCache._textures).length).toEqual(2); + + expect(Material._textureCache.getTexture(canvas)).toBe(mockTexture); + expect(Material._textureCache.getTexture(canvas2)).toBe(mockTexture2); + + Material._textureCache.releaseTexture(canvas2); + Material._textureCache.releaseTexture(canvas2); + expect(Object.keys(Material._textureCache._textures).length).toEqual(1); + + Material._textureCache.releaseTexture(canvas); + Material._textureCache.releaseTexture(canvas); + expect(Object.keys(Material._textureCache._textures).length).toEqual(0); + + }); + }, 'WebGL');