text: define([
          '../Core/Cartesian2',
          '../Core/Cartesian3',
          '../Core/Cartesian4',
          '../Core/combine',
          '../Core/ComponentDatatype',
          '../Core/defined',
          '../Core/defineProperties',
          '../Core/destroyObject',
          '../Core/DeveloperError',
          '../Core/FeatureDetection',
          '../Core/Math',
          '../Core/PixelFormat',
          '../Renderer/ContextLimits',
          '../Renderer/PixelDatatype',
          '../Renderer/Sampler',
          '../Renderer/Texture',
          '../Renderer/TextureMagnificationFilter',
          '../Renderer/TextureMinificationFilter'
      ], function(
          Cartesian2,
          Cartesian3,
          Cartesian4,
          combine,
          ComponentDatatype,
          defined,
          defineProperties,
          destroyObject,
          DeveloperError,
          FeatureDetection,
          CesiumMath,
          PixelFormat,
          ContextLimits,
          PixelDatatype,
          Sampler,
          Texture,
          TextureMagnificationFilter,
          TextureMinificationFilter) {
      'use strict';
  
      /**
       * Creates a texture to look up per instance attributes for batched primitives. For example, store each primitive's pick color in the texture.
       *
       * @alias BatchTable
       * @constructor
       * @private
       *
       * @param {Context} context The context in which the batch table is created.
       * @param {Object[]} attributes An array of objects describing a per instance attribute. Each object contains a datatype, components per attributes, whether it is normalized and a function name
       *     to retrieve the value in the vertex shader.
       * @param {Number} numberOfInstances The number of instances in a batch table.
       *
       * @example
       * // create the batch table
       * var attributes = [{
       *     functionName : 'getShow',
       *     componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
       *     componentsPerAttribute : 1
       * }, {
       *     functionName : 'getPickColor',
       *     componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
       *     componentsPerAttribute : 4,
       *     normalize : true
       * }];
       * var batchTable = new BatchTable(context, attributes, 5);
       *
       * // when creating the draw commands, update the uniform map and the vertex shader
       * vertexShaderSource = batchTable.getVertexShaderCallback()(vertexShaderSource);
       * var shaderProgram = ShaderProgram.fromCache({
       *    // ...
       *    vertexShaderSource : vertexShaderSource,
       * });
       *
       * drawCommand.shaderProgram = shaderProgram;
       * drawCommand.uniformMap = batchTable.getUniformMapCallback()(uniformMap);
       *
       * // use the attribute function names in the shader to retrieve the instance values
       * // ...
       * attribute float batchId;
       *
       * void main() {
       *     // ...
       *     float show = getShow(batchId);
       *     vec3 pickColor = getPickColor(batchId);
       *     // ...
       * }
       */
      function BatchTable(context, attributes, numberOfInstances) {
          //>>includeStart('debug', pragmas.debug);
          if (!defined(context)) {
              throw new DeveloperError('context is required');
          }
          if (!defined(attributes)) {
              throw new DeveloperError('attributes is required');
          }
          if (!defined(numberOfInstances)) {
              throw new DeveloperError('numberOfInstances is required');
          }
          //>>includeEnd('debug');
  
          this._attributes = attributes;
          this._numberOfInstances = numberOfInstances;
  
          if (attributes.length === 0) {
              return;
          }
  
          // PERFORMANCE_IDEA: We may be able to arrange the attributes so they can be packing into fewer texels.
          // Right now, an attribute with one component uses an entire texel when 4 single component attributes can
          // be packed into a texel.
          //
          // Packing floats into unsigned byte textures makes the problem worse. A single component float attribute
          // will be packed into a single texel leaving 3 texels unused. 4 texels are reserved for each float attribute
          // regardless of how many components it has.
          var pixelDatatype = getDatatype(attributes);
          var textureFloatSupported = context.floatingPointTexture;
          var packFloats = pixelDatatype === PixelDatatype.FLOAT && !textureFloatSupported;
          var offsets = createOffsets(attributes, packFloats);
  
          var stride = getStride(offsets, attributes, packFloats);
          var maxNumberOfInstancesPerRow = Math.floor(ContextLimits.maximumTextureSize / stride);
  
          var instancesPerWidth = Math.min(numberOfInstances, maxNumberOfInstancesPerRow);
          var width = stride * instancesPerWidth;
          var height = Math.ceil(numberOfInstances / instancesPerWidth);
  
          var stepX = 1.0 / width;
          var centerX = stepX * 0.5;
          var stepY = 1.0 / height;
          var centerY = stepY * 0.5;
  
          this._textureDimensions = new Cartesian2(width, height);
          this._textureStep = new Cartesian4(stepX, centerX, stepY, centerY);
          this._pixelDatatype = !packFloats ? pixelDatatype : PixelDatatype.UNSIGNED_BYTE;
          this._packFloats = packFloats;
          this._offsets = offsets;
          this._stride = stride;
          this._texture = undefined;
  
          var batchLength = 4 * width * height;
          this._batchValues = pixelDatatype === PixelDatatype.FLOAT && !packFloats ? new Float32Array(batchLength) : new Uint8Array(batchLength);
          this._batchValuesDirty = false;
      }
  
      defineProperties(BatchTable.prototype, {
          /**
           * The attribute descriptions.
           * @memberOf BatchTable.prototype
           * @type {Object[]}
           * @readonly
           */
          attributes : {
              get : function() {
                  return this._attributes;
              }
          },
          /**
           * The number of instances.
           * @memberOf BatchTable.prototype
           * @type {Number}
           * @readonly
           */
          numberOfInstances : {
              get : function () {
                  return this._numberOfInstances;
              }
          }
      });
  
      function getDatatype(attributes) {
          var foundFloatDatatype = false;
          var length = attributes.length;
          for (var i = 0; i < length; ++i) {
              if (attributes[i].componentDatatype !== ComponentDatatype.UNSIGNED_BYTE) {
                  foundFloatDatatype = true;
                  break;
              }
          }
          return foundFloatDatatype ? PixelDatatype.FLOAT : PixelDatatype.UNSIGNED_BYTE;
      }
  
      function getAttributeType(attributes, attributeIndex) {
          var componentsPerAttribute = attributes[attributeIndex].componentsPerAttribute;
          if (componentsPerAttribute === 2) {
              return Cartesian2;
          } else if (componentsPerAttribute === 3) {
              return Cartesian3;
          } else if (componentsPerAttribute === 4) {
              return Cartesian4;
          }
          return Number;
      }
  
      function createOffsets(attributes, packFloats) {
          var offsets = new Array(attributes.length);
  
          var currentOffset = 0;
          var attributesLength = attributes.length;
          for (var i = 0; i < attributesLength; ++i) {
              var attribute = attributes[i];
              var componentDatatype = attribute.componentDatatype;
  
              offsets[i] = currentOffset;
  
              if (componentDatatype !== ComponentDatatype.UNSIGNED_BYTE && packFloats) {
                  currentOffset += 4;
              } else {
                  ++currentOffset;
              }
          }
  
          return offsets;
      }
  
      function getStride(offsets, attributes, packFloats) {
          var length = offsets.length;
          var lastOffset = offsets[length - 1];
          var lastAttribute = attributes[length - 1];
          var componentDatatype = lastAttribute.componentDatatype;
  
          if (componentDatatype !== ComponentDatatype.UNSIGNED_BYTE && packFloats) {
              return lastOffset + 4;
          }
          return lastOffset + 1;
      }
  
      var scratchPackedFloatCartesian4 = new Cartesian4();
  
      var SHIFT_LEFT_8 = 256.0;
      var SHIFT_LEFT_16 = 65536.0;
      var SHIFT_LEFT_24 = 16777216.0;
  
      var SHIFT_RIGHT_8 = 1.0 / SHIFT_LEFT_8;
      var SHIFT_RIGHT_16 = 1.0 / SHIFT_LEFT_16;
      var SHIFT_RIGHT_24 = 1.0 / SHIFT_LEFT_24;
  
      var BIAS = 38.0;
  
      function unpackFloat(value) {
          var temp = value.w / 2.0;
          var exponent = Math.floor(temp);
          var sign = (temp - exponent) * 2.0;
          exponent = exponent - BIAS;
  
          sign = sign * 2.0 - 1.0;
          sign = -sign;
  
          if (exponent >= BIAS) {
              return sign < 0.0 ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY;
          }
  
          var unpacked = sign * value.x * SHIFT_RIGHT_8;
          unpacked += sign * value.y * SHIFT_RIGHT_16;
          unpacked += sign * value.z * SHIFT_RIGHT_24;
  
          return unpacked * Math.pow(10.0, exponent);
      }
  
      function getPackedFloat(array, index, result) {
          var packed = Cartesian4.unpack(array, index, scratchPackedFloatCartesian4);
          var x = unpackFloat(packed);
  
          packed = Cartesian4.unpack(array, index + 4, scratchPackedFloatCartesian4);
          var y = unpackFloat(packed);
  
          packed = Cartesian4.unpack(array, index + 8, scratchPackedFloatCartesian4);
          var z = unpackFloat(packed);
  
          packed = Cartesian4.unpack(array, index + 12, scratchPackedFloatCartesian4);
          var w = unpackFloat(packed);
  
          return Cartesian4.fromElements(x, y, z, w, result);
      }
  
      if (!FeatureDetection.supportsTypedArrays()) {
          return;
      }
      var scratchFloatArray = new Float32Array(1);
  
      function packFloat(value, result) {
          scratchFloatArray[0] = value;
          value = scratchFloatArray[0];
  
          if (value === 0.0) {
              return Cartesian4.clone(Cartesian4.ZERO, result);
          }
  
          var sign = value < 0.0 ? 1.0 : 0.0;
          var exponent;
  
          if (!isFinite(value)) {
              value = 0.1;
              exponent = BIAS;
          } else {
              value = Math.abs(value);
              exponent = Math.floor(CesiumMath.logBase(value, 10)) + 1.0;
              value = value / Math.pow(10.0, exponent);
          }
  
          var temp = value * SHIFT_LEFT_8;
          result.x = Math.floor(temp);
          temp = (temp - result.x) * SHIFT_LEFT_8;
          result.y = Math.floor(temp);
          temp = (temp - result.y) * SHIFT_LEFT_8;
          result.z = Math.floor(temp);
          result.w = (exponent + BIAS) * 2.0 + sign;
  
          return result;
      }
  
      function setPackedAttribute(value, array, index) {
          var packed = packFloat(value.x, scratchPackedFloatCartesian4);
          Cartesian4.pack(packed, array, index);
  
          packed = packFloat(value.y, packed);
          Cartesian4.pack(packed, array, index + 4);
  
          packed = packFloat(value.z, packed);
          Cartesian4.pack(packed, array, index + 8);
  
          packed = packFloat(value.w, packed);
          Cartesian4.pack(packed, array, index + 12);
      }
  
      var scratchGetAttributeCartesian4 = new Cartesian4();
  
      /**
       * Gets the value of an attribute in the table.
       *
       * @param {Number} instanceIndex The index of the instance.
       * @param {Number} attributeIndex The index of the attribute.
       * @param {undefined|Cartesian2|Cartesian3|Cartesian4} [result] The object onto which to store the result. The type is dependent on the attribute's number of components.
       * @returns {Number|Cartesian2|Cartesian3|Cartesian4} The attribute value stored for the instance.
       *
       * @exception {DeveloperError} instanceIndex is out of range.
       * @exception {DeveloperError} attributeIndex is out of range.
       */
      BatchTable.prototype.getBatchedAttribute = function(instanceIndex, attributeIndex, result) {
          //>>includeStart('debug', pragmas.debug);
          if (instanceIndex < 0 || instanceIndex >= this._numberOfInstances) {
              throw new DeveloperError('instanceIndex is out of range.');
          }
          if (attributeIndex < 0 || attributeIndex >= this._attributes.length) {
              throw new DeveloperError('attributeIndex is out of range');
          }
          //>>includeEnd('debug');
  
          var attributes = this._attributes;
          var offset = this._offsets[attributeIndex];
          var stride = this._stride;
  
          var index = 4 * stride * instanceIndex + 4 * offset;
          var value;
  
          if (this._packFloats && attributes[attributeIndex].componentDatatype !== PixelDatatype.UNSIGNED_BYTE) {
              value = getPackedFloat(this._batchValues, index, scratchGetAttributeCartesian4);
          } else {
              value = Cartesian4.unpack(this._batchValues, index, scratchGetAttributeCartesian4);
          }
  
          var attributeType = getAttributeType(attributes, attributeIndex);
          if (defined(attributeType.fromCartesian4)) {
              return attributeType.fromCartesian4(value, result);
          } else if (defined(attributeType.clone)) {
              return attributeType.clone(value, result);
          }
  
          return value.x;
      };
  
      var setAttributeScratchValues = [undefined, undefined, new Cartesian2(), new Cartesian3(), new Cartesian4()];
      var setAttributeScratchCartesian4 = new Cartesian4();
  
      /**
       * Sets the value of an attribute in the table.
       *
       * @param {Number} instanceIndex The index of the instance.
       * @param {Number} attributeIndex The index of the attribute.
       * @param {Number|Cartesian2|Cartesian3|Cartesian4} value The value to be stored in the table. The type of value will depend on the number of components of the attribute.
       *
       * @exception {DeveloperError} instanceIndex is out of range.
       * @exception {DeveloperError} attributeIndex is out of range.
       */
      BatchTable.prototype.setBatchedAttribute = function(instanceIndex, attributeIndex, value) {
          //>>includeStart('debug', pragmas.debug);
          if (instanceIndex < 0 || instanceIndex >= this._numberOfInstances) {
              throw new DeveloperError('instanceIndex is out of range.');
          }
          if (attributeIndex < 0 || attributeIndex >= this._attributes.length) {
              throw new DeveloperError('attributeIndex is out of range');
          }
          if (!defined(value)) {
              throw new DeveloperError('value is required.');
          }
          //>>includeEnd('debug');
  
          var attributes = this._attributes;
          var result = setAttributeScratchValues[attributes[attributeIndex].componentsPerAttribute];
          var currentAttribute = this.getBatchedAttribute(instanceIndex, attributeIndex, result);
          var attributeType = getAttributeType(this._attributes, attributeIndex);
          var entriesEqual = defined(attributeType.equals) ? attributeType.equals(currentAttribute, value) : currentAttribute === value;
          if (entriesEqual) {
              return;
          }
  
          var attributeValue = setAttributeScratchCartesian4;
          attributeValue.x = defined(value.x) ? value.x : value;
          attributeValue.y = defined(value.y) ? value.y : 0.0;
          attributeValue.z = defined(value.z) ? value.z : 0.0;
          attributeValue.w = defined(value.w) ? value.w : 0.0;
  
          var offset = this._offsets[attributeIndex];
          var stride = this._stride;
          var index = 4 * stride * instanceIndex + 4 * offset;
  
          if (this._packFloats && attributes[attributeIndex].componentDatatype !== PixelDatatype.UNSIGNED_BYTE) {
              setPackedAttribute(attributeValue, this._batchValues, index);
          } else {
              Cartesian4.pack(attributeValue, this._batchValues, index);
          }
  
          this._batchValuesDirty = true;
      };
  
      function createTexture(batchTable, context) {
          var dimensions = batchTable._textureDimensions;
          batchTable._texture = new Texture({
              context : context,
              pixelFormat : PixelFormat.RGBA,
              pixelDatatype : batchTable._pixelDatatype,
              width : dimensions.x,
              height : dimensions.y,
              sampler : new Sampler({
                  minificationFilter : TextureMinificationFilter.NEAREST,
                  magnificationFilter : TextureMagnificationFilter.NEAREST
              })
          });
      }
  
      function updateTexture(batchTable) {
          var dimensions = batchTable._textureDimensions;
          batchTable._texture.copyFrom({
              width : dimensions.x,
              height : dimensions.y,
              arrayBufferView : batchTable._batchValues
          });
      }
  
      /**
       * Creates/updates the batch table texture.
       * @param {FrameState} frameState The frame state.
       *
       * @exception {RuntimeError} The floating point texture extension is required but not supported.
       */
      BatchTable.prototype.update = function(frameState) {
          if ((defined(this._texture) && !this._batchValuesDirty) || this._attributes.length === 0) {
              return;
          }
  
          this._batchValuesDirty = false;
  
          if (!defined(this._texture)) {
              createTexture(this, frameState.context);
          }
          updateTexture(this);
      };
  
      /**
       * Gets a function that will update a uniform map to contain values for looking up values in the batch table.
       *
       * @returns {BatchTable~updateUniformMapCallback} A callback for updating uniform maps.
       */
      BatchTable.prototype.getUniformMapCallback = function() {
          var that = this;
          return function(uniformMap) {
              if (that._attributes.length === 0) {
                  return uniformMap;
              }
  
              var batchUniformMap = {
                  batchTexture : function() {
                      return that._texture;
                  },
                  batchTextureDimensions : function() {
                      return that._textureDimensions;
                  },
                  batchTextureStep : function() {
                      return that._textureStep;
                  }
              };
              return combine(uniformMap, batchUniformMap);
          };
      };
  
      function getGlslComputeSt(batchTable) {
          var stride = batchTable._stride;
  
          // GLSL batchId is zero-based: [0, numberOfInstances - 1]
          if (batchTable._textureDimensions.y === 1) {
              return 'uniform vec4 batchTextureStep; \n' +
                     'vec2 computeSt(float batchId) \n' +
                     '{ \n' +
                     '    float stepX = batchTextureStep.x; \n' +
                     '    float centerX = batchTextureStep.y; \n' +
                     '    float numberOfAttributes = float('+ stride + '); \n' +
                     '    return vec2(centerX + (batchId * numberOfAttributes * stepX), 0.5); \n' +
                     '} \n';
          }
  
          return 'uniform vec4 batchTextureStep; \n' +
                 'uniform vec2 batchTextureDimensions; \n' +
                 'vec2 computeSt(float batchId) \n' +
                 '{ \n' +
                 '    float stepX = batchTextureStep.x; \n' +
                 '    float centerX = batchTextureStep.y; \n' +
                 '    float stepY = batchTextureStep.z; \n' +
                 '    float centerY = batchTextureStep.w; \n' +
                 '    float numberOfAttributes = float('+ stride + '); \n' +
                 '    float xId = mod(batchId * numberOfAttributes, batchTextureDimensions.x); \n' +
                 '    float yId = floor(batchId * numberOfAttributes / batchTextureDimensions.x); \n' +
                 '    return vec2(centerX + (xId * stepX), 1.0 - (centerY + (yId * stepY))); \n' +
                 '} \n';
      }
  
      function getGlslUnpackFloat(batchTable) {
          if (!batchTable._packFloats) {
              return '';
          }
  
          return 'float unpackFloat(vec4 value) \n' +
                 '{ \n' +
                 '    value *= 255.0; \n' +
                 '    float temp = value.w / 2.0; \n' +
                 '    float exponent = floor(temp); \n' +
                 '    float sign = (temp - exponent) * 2.0; \n' +
                 '    exponent = exponent - float(' + BIAS + '); \n' +
                 '    sign = sign * 2.0 - 1.0; \n' +
                 '    sign = -sign; \n' +
                 '    float unpacked = sign * value.x * float(' + SHIFT_RIGHT_8 + '); \n' +
                 '    unpacked += sign * value.y * float(' + SHIFT_RIGHT_16 + '); \n' +
                 '    unpacked += sign * value.z * float(' + SHIFT_RIGHT_24 + '); \n' +
                 '    return unpacked * pow(10.0, exponent); \n' +
                 '} \n';
      }
  
      function getComponentType(componentsPerAttribute) {
          if (componentsPerAttribute === 1) {
              return 'float';
          }
          return 'vec' + componentsPerAttribute;
      }
  
      function getComponentSwizzle(componentsPerAttribute) {
          if (componentsPerAttribute === 1) {
              return '.x';
          } else if (componentsPerAttribute === 2) {
              return '.xy';
          } else if (componentsPerAttribute === 3) {
              return '.xyz';
          }
          return '';
      }
  
      function getGlslAttributeFunction(batchTable, attributeIndex) {
          var attributes = batchTable._attributes;
          var attribute = attributes[attributeIndex];
          var componentsPerAttribute = attribute.componentsPerAttribute;
          var functionName = attribute.functionName;
          var functionReturnType = getComponentType(componentsPerAttribute);
          var functionReturnValue = getComponentSwizzle(componentsPerAttribute);
  
          var offset = batchTable._offsets[attributeIndex];
  
          var glslFunction =
              functionReturnType + ' ' + functionName + '(float batchId) \n' +
              '{ \n' +
              '    vec2 st = computeSt(batchId); \n' +
              '    st.x += batchTextureStep.x * float(' + offset + '); \n';
  
          if (batchTable._packFloats && attribute.componentDatatype !== PixelDatatype.UNSIGNED_BYTE) {
              glslFunction += 'vec4 textureValue; \n' +
                              'textureValue.x = unpackFloat(texture2D(batchTexture, st)); \n' +
                              'textureValue.y = unpackFloat(texture2D(batchTexture, st + vec2(batchTextureStep.x, 0.0))); \n' +
                              'textureValue.z = unpackFloat(texture2D(batchTexture, st + vec2(batchTextureStep.x * 2.0, 0.0))); \n' +
                              'textureValue.w = unpackFloat(texture2D(batchTexture, st + vec2(batchTextureStep.x * 3.0, 0.0))); \n';
  
          } else {
              glslFunction += '    vec4 textureValue = texture2D(batchTexture, st); \n';
          }
  
          glslFunction += '    ' + functionReturnType + ' value = textureValue' + functionReturnValue + '; \n';
  
          if (batchTable._pixelDatatype === PixelDatatype.UNSIGNED_BYTE && attribute.componentDatatype === ComponentDatatype.UNSIGNED_BYTE && !attribute.normalize) {
              glslFunction += 'value *= 255.0; \n';
          } else if (batchTable._pixelDatatype === PixelDatatype.FLOAT && attribute.componentDatatype === ComponentDatatype.UNSIGNED_BYTE && attribute.normalize) {
              glslFunction += 'value /= 255.0; \n';
          }
  
          glslFunction +=
              '    return value; \n' +
              '} \n';
          return glslFunction;
      }
  
      /**
       * Gets a function that will update a vertex shader to contain functions for looking up values in the batch table.
       *
       * @returns {BatchTable~updateVertexShaderSourceCallback} A callback for updating a vertex shader source.
       */
      BatchTable.prototype.getVertexShaderCallback = function() {
          var attributes = this._attributes;
          if (attributes.length === 0) {
              return function(source) {
                  return source;
              };
          }
  
          var batchTableShader = 'uniform sampler2D batchTexture; \n';
          batchTableShader += getGlslComputeSt(this) + '\n';
          batchTableShader += getGlslUnpackFloat(this) + '\n';
  
          var length = attributes.length;
          for (var i = 0; i < length; ++i) {
              batchTableShader += getGlslAttributeFunction(this, i);
          }
  
          return function(source) {
              var mainIndex = source.indexOf('void main');
              var beforeMain = source.substring(0, mainIndex);
              var afterMain = source.substring(mainIndex);
              return beforeMain + '\n' + batchTableShader + '\n' + afterMain;
          };
      };
  
      /**
       * Returns true if this object was destroyed; otherwise, false.
       * <br /><br />
       * If this object was destroyed, it should not be used; calling any function other than
       * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
       *
       * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
       *
       * @see BatchTable#destroy
       */
      BatchTable.prototype.isDestroyed = function() {
          return false;
      };
  
      /**
       * Destroys the WebGL resources held by this object.  Destroying an object allows for deterministic
       * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
       * <br /><br />
       * Once an object is destroyed, it should not be used; calling any function other than
       * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.  Therefore,
       * assign the return value (<code>undefined</code>) to the object as done in the example.
       *
       * @returns {undefined}
       *
       * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
       *
       * @see BatchTable#isDestroyed
       */
      BatchTable.prototype.destroy = function() {
          this._texture = this._texture && this._texture.destroy();
          return destroyObject(this);
      };
  
      /**
       * A callback for updating uniform maps.
       * @callback BatchTable~updateUniformMapCallback
       *
       * @param {Object} uniformMap The uniform map.
       * @returns {Object} The new uniform map with properties for retrieving values from the batch table.
       */
  
      /**
       * A callback for updating a vertex shader source.
       * @callback BatchTable~updateVertexShaderSourceCallback
       *
       * @param {String} vertexShaderSource The vertex shader source.
       * @returns {String} The new vertex shader source with the functions for retrieving batch table values injected.
       */
  
      return BatchTable;
  });
  