title: Three.js: Setting `texture.needsUpdate = true` is very slow
body: <p>I'm working on a Three.js scene in which I'd like to update some textures after some time. I'm finding that updating the textures is very slow, however, and drags FPS to only 1-2 FPS for several seconds (when updating just a single texture).</p>
  
  <p>Is there anything one can do to expedite texture updates? Any insights others can offer on this question would be very appreciated.</p>
  
  <p>To see this behavior, click the window of the example below. This will load the first texture update (another click will trigger the second texture update). If you try to zoom after one of these clicks, you'll find the screen freezes and the FPS will drop terribly. Does anyone know how to fix this problem?</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;html&gt;
  &lt;head&gt;
    &lt;style&gt;
    html, body { width: 100%; height: 100%; background: #000; }
    body { margin: 0; overflow: hidden; }
    canvas { width: 100%; height: 100%; }
    &lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js'&gt;&lt;/script&gt;
    &lt;script src='https://rawgit.com/YaleDHLab/pix-plot/master/assets/js/trackball-controls.js'&gt;&lt;/script&gt;
    &lt;script src='https://rawgit.com/mrdoob/stats.js/master/build/stats.min.js'&gt;&lt;/script&gt;
  
      &lt;script type='x-shader/x-vertex' id='vertex-shader'&gt;
      precision highp float;
  
      uniform mat4 modelViewMatrix;
      uniform mat4 projectionMatrix;
  
      uniform vec3 cameraPosition;
  
      attribute vec3 position; // sets the blueprint's vertex positions
      attribute vec3 translation; // x y translation offsets for an instance
      attribute float texIdx; // the texture index to access
  
      varying float vTexIdx;
  
      void main() {
        // set point position
        vec3 pos = position + translation;
        vec4 projected = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
        gl_Position = projected;
  
        // assign the varyings
        vTexIdx = texIdx;
  
        // use the delta between the point position and camera position to size point
        float xDelta = pow(projected[0] - cameraPosition[0], 2.0);
        float yDelta = pow(projected[1] - cameraPosition[1], 2.0);
        float zDelta = pow(projected[2] - cameraPosition[2], 2.0);
        float delta  = pow(xDelta + yDelta + zDelta, 0.5);
        gl_PointSize = 40000.0 / delta;
      }
      &lt;/script&gt;
  
      &lt;script type='x-shader/x-fragment' id='fragment-shader'&gt;
      precision highp float;
  
      uniform sampler2D a;
      uniform sampler2D b;
  
      varying float vTexIdx;
  
      void main() {
        int textureIndex = int(vTexIdx);
        vec2 uv = vec2(gl_PointCoord.x, gl_PointCoord.y);
        if (textureIndex == 0) {
          gl_FragColor = texture2D(a, uv);
        } else if (textureIndex == 1) {
          gl_FragColor = texture2D(b, uv);
        }
      }
      &lt;/script&gt;
  
    &lt;script&gt;
  
    /**
    * Generate a scene object with a background color
    **/
  
    function getScene() {
      var scene = new THREE.Scene();
      scene.background = new THREE.Color(0xaaaaaa);
      return scene;
    }
  
    /**
    * Generate the camera to be used in the scene
    **/
  
    function getCamera() {
      var aspectRatio = window.innerWidth / window.innerHeight;
      var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 100000);
      camera.position.set(0, 1, -6000);
      return camera;
    }
  
    /**
    * Generate the renderer to be used in the scene
    **/
  
    function getRenderer() {
      // Create the canvas with a renderer
      var renderer = new THREE.WebGLRenderer({antialias: true});
      // Add support for retina displays
      renderer.setPixelRatio(window.devicePixelRatio);
      // Specify the size of the canvas
      renderer.setSize(window.innerWidth, window.innerHeight);
      // Add the canvas to the DOM
      document.body.appendChild(renderer.domElement);
      return renderer;
    }
  
    /**
    * Generate the controls to be used in the scene
    **/
  
    function getControls(camera, renderer) {
      var controls = new THREE.TrackballControls(camera, renderer.domElement);
      controls.zoomSpeed = 0.4;
      controls.panSpeed = 0.4;
      return controls;
    }
  
    /**
    * Generate the points for the scene
    **/
  
    function addPoints(scene) {
      var BA = THREE.BufferAttribute;
      var IBA = THREE.InstancedBufferAttribute;
      var geometry  = new THREE.InstancedBufferGeometry();
  
      // add data for each observation
      var n = 10000; // number of observations
      var rootN = n**(1/2);
      var cellSize = 20;
      var translation = new Float32Array( n * 3 );
      var texIdx = new Float32Array( n );
      var translationIterator = 0;
      var texIterator = 0;
      for (var i=0; i&lt;n*3; i++) {
        var x = Math.random() * n - (n/2);
        var y = Math.random() * n - (n/2);
        translation[translationIterator++] = x;
        translation[translationIterator++] = y;
        translation[translationIterator++] = Math.random() * n - (n/2);
        texIdx[texIterator++] = (x + y) &gt; (n/8) ? 1 : 0;
      }
      var positionAttr = new BA(new Float32Array( [0, 0, 0] ), 3);
      var translationAttr = new IBA(translation, 3, 1);
      var texIdxAttr = new IBA(texIdx, 1, 1);
      positionAttr.dynamic = true;
      translationAttr.dynamic = true;
      texIdxAttr.dynamic = true;
      geometry.addAttribute('position', positionAttr);
      geometry.addAttribute('translation', translationAttr);
      geometry.addAttribute('texIdx', texIdxAttr);
  
      var canvases = [
        getElem('canvas', { width: 16384, height: 16384, }),
        getElem('canvas', { width: 16384, height: 16384, }),
      ]
  
      var textures = [
        getTexture( canvases[0] ),
        getTexture( canvases[1] ),
      ];
  
      var material = new THREE.RawShaderMaterial({
        uniforms: {
          a: {
            type: 't',
            value: textures[0],
          },
          b: {
            type: 't',
            value: textures[1],
          }
        },
        vertexShader: document.getElementById('vertex-shader').textContent,
        fragmentShader: document.getElementById('fragment-shader').textContent,
      });
      var mesh = new THREE.Points(geometry, material);
      mesh.frustumCulled = false; // prevent the mesh from being clipped on drag
      scene.add(mesh);
  
      // on the first window click, paint red points
      // on the second window click, paint blue points
      var clicks = 0;
      window.addEventListener('click', function() {
        if (clicks == 0 || clicks == 1) {
          var canvas = canvases[clicks];
          var ctx = canvas.getContext('2d');
          ctx.fillStyle = clicks == 0 ? 'red' : 'blue';
          ctx.rect(0, 0, 16384, 16384);
          ctx.fill();
          textures[clicks].needsUpdate = true;
          clicks++;
        }
      })
    }
  
    function getTexture(canvas) {
      var tex = new THREE.Texture(canvas);
      tex.needsUpdate = true;
      tex.flipY = false;
      return tex;
    }
  
    /**
    * Create an element
    **/
  
    function getElem(tag, obj) {
      var obj = obj || {};
      var elem = document.createElement(tag);
      Object.keys(obj).forEach(function(attr) {
        elem[attr] = obj[attr];
      })
      return elem;
    }
  
    /**
    * Add stats
    **/
  
    function getStats() {
      var stats = new Stats();
      stats.domElement.style.position = 'absolute';
      stats.domElement.style.top = '65px';
      stats.domElement.style.right = '5px';
      stats.domElement.style.left = 'initial';
      document.body.appendChild(stats.domElement);
      return stats;
    }
  
  
    /**
    * Render!
    **/
  
    function render() {
      requestAnimationFrame(render);
      renderer.render(scene, camera);
      controls.update();
      stats.update();
    };
  
    /**
    * Main
    **/
  
    var stats = getStats();
    var scene = getScene();
    var camera = getCamera();
    var renderer = getRenderer();
    var controls = getControls(camera, renderer);
    addPoints(scene);
    render();
  
    &lt;/script&gt;
  &lt;/body&gt;
  &lt;/html&gt;</code></pre>
  </div>
  </div>
  </p>