text: function WebGLAttributes( gl, capabilities ) {
  
  	const isWebGL2 = capabilities.isWebGL2;
  
  	const buffers = new WeakMap();
  
  	function createBuffer( attribute, bufferType ) {
  
  		const array = attribute.array;
  		const usage = attribute.usage;
  
  		const buffer = gl.createBuffer();
  
  		gl.bindBuffer( bufferType, buffer );
  		gl.bufferData( bufferType, array, usage );
  
  		attribute.onUploadCallback();
  
  		let type = gl.FLOAT;
  
  		if ( array instanceof Float32Array ) {
  
  			type = gl.FLOAT;
  
  		} else if ( array instanceof Float64Array ) {
  
  			console.warn( 'THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.' );
  
  		} else if ( array instanceof Uint16Array ) {
  
  			if ( attribute.isFloat16BufferAttribute ) {
  
  				if ( isWebGL2 ) {
  
  					type = gl.HALF_FLOAT;
  
  				} else {
  
  					console.warn( 'THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.' );
  
  				}
  
  			} else {
  
  				type = gl.UNSIGNED_SHORT;
  
  			}
  
  		} else if ( array instanceof Int16Array ) {
  
  			type = gl.SHORT;
  
  		} else if ( array instanceof Uint32Array ) {
  
  			type = gl.UNSIGNED_INT;
  
  		} else if ( array instanceof Int32Array ) {
  
  			type = gl.INT;
  
  		} else if ( array instanceof Int8Array ) {
  
  			type = gl.BYTE;
  
  		} else if ( array instanceof Uint8Array ) {
  
  			type = gl.UNSIGNED_BYTE;
  
  		}
  
  		return {
  			buffer: buffer,
  			type: type,
  			bytesPerElement: array.BYTES_PER_ELEMENT,
  			version: attribute.version
  		};
  
  	}
  
  	function updateBuffer( buffer, attribute, bufferType ) {
  
  		const array = attribute.array;
  		const updateRange = attribute.updateRange;
  
  		gl.bindBuffer( bufferType, buffer );
  
  		if ( updateRange.count === - 1 ) {
  
  			// Not using update ranges
  
  			gl.bufferSubData( bufferType, 0, array );
  
  		} else {
  
  			if ( isWebGL2 ) {
  
  				gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,
  					array, updateRange.offset, updateRange.count );
  
  			} else {
  
  				gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,
  					array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) );
  
  			}
  
  			updateRange.count = - 1; // reset range
  
  		}
  
  	}
  
  	//
  
  	function get( attribute ) {
  
  		if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
  
  		return buffers.get( attribute );
  
  	}
  
  	function remove( attribute ) {
  
  		if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
  
  		const data = buffers.get( attribute );
  
  		if ( data ) {
  
  			gl.deleteBuffer( data.buffer );
  
  			buffers.delete( attribute );
  
  		}
  
  	}
  
  	function update( attribute, bufferType ) {
  
  		if ( attribute.isGLBufferAttribute ) {
  
  			const cached = buffers.get( attribute );
  
  			if ( ! cached || cached.version < attribute.version ) {
  
  				buffers.set( attribute, {
  					buffer: attribute.buffer,
  					type: attribute.type,
  					bytesPerElement: attribute.elementSize,
  					version: attribute.version
  				} );
  
  			}
  
  			return;
  
  		}
  
  		if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
  
  		const data = buffers.get( attribute );
  
  		if ( data === undefined ) {
  
  			buffers.set( attribute, createBuffer( attribute, bufferType ) );
  
  		} else if ( data.version < attribute.version ) {
  
  			updateBuffer( data.buffer, attribute, bufferType );
  
  			data.version = attribute.version;
  
  		}
  
  	}
  
  	return {
  
  		get: get,
  		remove: remove,
  		update: update
  
  	};
  
  }
  
  
  export { WebGLAttributes };
  