Browse Source

VAO Support

Takahiro 6 years ago
parent
commit
fe2533d700

+ 62 - 6
src/core/BufferAttribute.js

@@ -17,23 +17,79 @@ function BufferAttribute( array, itemSize, normalized ) {
 
 
 	this.name = '';
 	this.name = '';
 
 
-	this.array = array;
-	this.itemSize = itemSize;
+	this._array = array;
+	this._itemSize = itemSize;
 	this.count = array !== undefined ? array.length / itemSize : 0;
 	this.count = array !== undefined ? array.length / itemSize : 0;
-	this.normalized = normalized === true;
+	this._normalized = normalized === true;
 
 
 	this.dynamic = false;
 	this.dynamic = false;
 	this.updateRange = { offset: 0, count: - 1 };
 	this.updateRange = { offset: 0, count: - 1 };
 
 
 	this.version = 0;
 	this.version = 0;
+	this.version2 = 0;
 
 
 }
 }
 
 
-Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', {
+Object.defineProperties( BufferAttribute.prototype, {
 
 
-	set: function ( value ) {
+	needsUpdate: {
 
 
-		if ( value === true ) this.version ++;
+		set: function ( value ) {
+
+			if ( value === true ) this.version ++;
+
+		}
+
+	},
+
+	array: {
+
+		get: function () {
+
+			return this._array;
+
+		},
+
+		set: function ( value ) {
+
+			this._array = value;
+			this.version2 ++;
+
+		}
+
+	},
+
+	itemSize: {
+
+		get: function () {
+
+			return this._itemSize;
+
+		},
+
+		set: function ( value ) {
+
+			this._itemSize = value;
+			this.version2 ++;
+
+		}
+
+	},
+
+	normalized: {
+
+		get: function () {
+
+			return this._normalized;
+
+		},
+
+		set: function ( value ) {
+
+			this._normalized = value;
+			this.version2 ++;
+
+		}
 
 
 	}
 	}
 
 

+ 22 - 1
src/core/InstancedBufferAttribute.js

@@ -18,7 +18,9 @@ function InstancedBufferAttribute( array, itemSize, normalized, meshPerAttribute
 
 
 	BufferAttribute.call( this, array, itemSize, normalized );
 	BufferAttribute.call( this, array, itemSize, normalized );
 
 
-	this.meshPerAttribute = meshPerAttribute || 1;
+	this._meshPerAttribute = meshPerAttribute || 1;
+
+	this.version2 = 0;
 
 
 }
 }
 
 
@@ -40,6 +42,25 @@ InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribu
 
 
 } );
 } );
 
 
+Object.defineProperties( InstancedBufferAttribute.prototype, {
+
+	meshPerAttribute: {
+
+		get: function () {
+
+			return this._meshPerAttribute;
+
+		},
+
+		set: function ( value ) {
 
 
+			this._meshPerAttribute = value;
+			this.version2 ++;
+
+		}
+
+	}
+
+} );
 
 
 export { InstancedBufferAttribute };
 export { InstancedBufferAttribute };

+ 24 - 1
src/core/InstancedInterleavedBuffer.js

@@ -8,7 +8,9 @@ function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) {
 
 
 	InterleavedBuffer.call( this, array, stride );
 	InterleavedBuffer.call( this, array, stride );
 
 
-	this.meshPerAttribute = meshPerAttribute || 1;
+	this._meshPerAttribute = meshPerAttribute || 1;
+
+	this.version2 = 0;
 
 
 }
 }
 
 
@@ -30,4 +32,25 @@ InstancedInterleavedBuffer.prototype = Object.assign( Object.create( Interleaved
 
 
 } );
 } );
 
 
+Object.defineProperties( InstancedInterleavedBuffer.prototype, {
+
+	meshPerAttribute: {
+
+		get: function () {
+
+			return this._meshPerAttribute;
+
+		},
+
+		set: function ( value ) {
+
+			this._meshPerAttribute = value;
+			this.version2 ++;
+
+		}
+
+	}
+
+} );
+
 export { InstancedInterleavedBuffer };
 export { InstancedInterleavedBuffer };

+ 44 - 5
src/core/InterleavedBuffer.js

@@ -5,22 +5,61 @@
 
 
 function InterleavedBuffer( array, stride ) {
 function InterleavedBuffer( array, stride ) {
 
 
-	this.array = array;
-	this.stride = stride;
+	this._array = array;
+	this._stride = stride;
 	this.count = array !== undefined ? array.length / stride : 0;
 	this.count = array !== undefined ? array.length / stride : 0;
 
 
 	this.dynamic = false;
 	this.dynamic = false;
 	this.updateRange = { offset: 0, count: - 1 };
 	this.updateRange = { offset: 0, count: - 1 };
 
 
 	this.version = 0;
 	this.version = 0;
+	this.version2 = 0;
 
 
 }
 }
 
 
-Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', {
+Object.defineProperties( InterleavedBuffer.prototype, {
 
 
-	set: function ( value ) {
+	needsUpdate: {
 
 
-		if ( value === true ) this.version ++;
+		set: function ( value ) {
+
+			if ( value === true ) this.version ++;
+
+		}
+
+	},
+
+	array: {
+
+		get: function () {
+
+			return this._array;
+
+		},
+
+		set: function ( value ) {
+
+			this._array = value;
+			this.version2 ++;
+
+		}
+
+	},
+
+	stride: {
+
+		get: function () {
+
+			return this._stride;
+
+		},
+
+		set: function ( value ) {
+
+			this._stride = value;
+			this.version2 ++;
+
+		}
 
 
 	}
 	}
 
 

+ 74 - 4
src/core/InterleavedBufferAttribute.js

@@ -5,11 +5,13 @@
 
 
 function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) {
 function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) {
 
 
-	this.data = interleavedBuffer;
-	this.itemSize = itemSize;
-	this.offset = offset;
+	this._data = interleavedBuffer;
+	this._itemSize = itemSize;
+	this._offset = offset;
 
 
-	this.normalized = normalized === true;
+	this._normalized = normalized === true;
+
+	this.version2 = 0;
 
 
 }
 }
 
 
@@ -33,6 +35,74 @@ Object.defineProperties( InterleavedBufferAttribute.prototype, {
 
 
 		}
 		}
 
 
+	},
+
+	data: {
+
+		get: function () {
+
+			return this._data;
+
+		},
+
+		set: function ( value ) {
+
+			this._data = value;
+			this.version2 ++;
+
+		}
+
+	},
+
+	itemSize: {
+
+		get: function () {
+
+			return this._itemSize;
+
+		},
+
+		set: function ( value ) {
+
+			this._itemSize = value;
+			this.version2 ++;
+
+		}
+
+	},
+
+	offset: {
+
+		get: function () {
+
+			return this._offset;
+
+		},
+
+		set: function ( value ) {
+
+			this._offset = value;
+			this.version2 ++;
+
+		}
+
+	},
+
+	normalized: {
+
+		get: function () {
+
+			return this._normalized;
+
+		},
+
+		set: function ( value ) {
+
+			this._normalized = value;
+			this.version2 ++;
+
+		}
+
 	}
 	}
 
 
 } );
 } );

+ 24 - 186
src/renderers/WebGLRenderer.js

@@ -23,6 +23,7 @@ import { Vector4 } from '../math/Vector4.js';
 import { WebGLAnimation } from './webgl/WebGLAnimation.js';
 import { WebGLAnimation } from './webgl/WebGLAnimation.js';
 import { WebGLAttributes } from './webgl/WebGLAttributes.js';
 import { WebGLAttributes } from './webgl/WebGLAttributes.js';
 import { WebGLBackground } from './webgl/WebGLBackground.js';
 import { WebGLBackground } from './webgl/WebGLBackground.js';
+import { WebGLBindingStates } from './webgl/WebGLBindingStates.js';
 import { WebGLBufferRenderer } from './webgl/WebGLBufferRenderer.js';
 import { WebGLBufferRenderer } from './webgl/WebGLBufferRenderer.js';
 import { WebGLCapabilities } from './webgl/WebGLCapabilities.js';
 import { WebGLCapabilities } from './webgl/WebGLCapabilities.js';
 import { WebGLClipping } from './webgl/WebGLClipping.js';
 import { WebGLClipping } from './webgl/WebGLClipping.js';
@@ -138,14 +139,6 @@ function WebGLRenderer( parameters ) {
 		_currentFramebuffer = null,
 		_currentFramebuffer = null,
 		_currentMaterialId = - 1,
 		_currentMaterialId = - 1,
 
 
-		// geometry and program caching
-
-		_currentGeometryProgram = {
-			geometry: null,
-			program: null,
-			wireframe: false
-		},
-
 		_currentCamera = null,
 		_currentCamera = null,
 		_currentArrayCamera = null,
 		_currentArrayCamera = null,
 
 
@@ -248,7 +241,7 @@ function WebGLRenderer( parameters ) {
 
 
 	var background, morphtargets, bufferRenderer, indexedBufferRenderer;
 	var background, morphtargets, bufferRenderer, indexedBufferRenderer;
 
 
-	var utils;
+	var utils, bindingStates;
 
 
 	function initGLContext() {
 	function initGLContext() {
 
 
@@ -264,6 +257,7 @@ function WebGLRenderer( parameters ) {
 			extensions.get( 'OES_texture_half_float_linear' );
 			extensions.get( 'OES_texture_half_float_linear' );
 			extensions.get( 'OES_standard_derivatives' );
 			extensions.get( 'OES_standard_derivatives' );
 			extensions.get( 'OES_element_index_uint' );
 			extensions.get( 'OES_element_index_uint' );
+			extensions.get( 'OES_vertex_array_object' );
 			extensions.get( 'ANGLE_instanced_arrays' );
 			extensions.get( 'ANGLE_instanced_arrays' );
 
 
 		}
 		}
@@ -272,7 +266,7 @@ function WebGLRenderer( parameters ) {
 
 
 		utils = new WebGLUtils( _gl, extensions, capabilities );
 		utils = new WebGLUtils( _gl, extensions, capabilities );
 
 
-		state = new WebGLState( _gl, extensions, utils, capabilities );
+		state = new WebGLState( _gl, extensions, utils );
 		state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) );
 		state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) );
 		state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) );
 		state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) );
 
 
@@ -280,10 +274,11 @@ function WebGLRenderer( parameters ) {
 		properties = new WebGLProperties();
 		properties = new WebGLProperties();
 		textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );
 		textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );
 		attributes = new WebGLAttributes( _gl );
 		attributes = new WebGLAttributes( _gl );
-		geometries = new WebGLGeometries( _gl, attributes, info );
+		bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities );
+		geometries = new WebGLGeometries( _gl, attributes, info, bindingStates );
 		objects = new WebGLObjects( geometries, info );
 		objects = new WebGLObjects( geometries, info );
 		morphtargets = new WebGLMorphtargets( _gl );
 		morphtargets = new WebGLMorphtargets( _gl );
-		programCache = new WebGLPrograms( _this, extensions, capabilities, textures );
+		programCache = new WebGLPrograms( _this, extensions, capabilities, textures, bindingStates );
 		renderLists = new WebGLRenderLists();
 		renderLists = new WebGLRenderLists();
 		renderStates = new WebGLRenderStates();
 		renderStates = new WebGLRenderStates();
 
 
@@ -567,6 +562,7 @@ function WebGLRenderer( parameters ) {
 		renderStates.dispose();
 		renderStates.dispose();
 		properties.dispose();
 		properties.dispose();
 		objects.dispose();
 		objects.dispose();
+		bindingStates.dispose();
 
 
 		vr.dispose();
 		vr.dispose();
 
 
@@ -645,7 +641,7 @@ function WebGLRenderer( parameters ) {
 
 
 	this.renderBufferImmediate = function ( object, program ) {
 	this.renderBufferImmediate = function ( object, program ) {
 
 
-		state.initAttributes();
+		bindingStates.initAttributes();
 
 
 		var buffers = properties.get( object );
 		var buffers = properties.get( object );
 
 
@@ -661,7 +657,7 @@ function WebGLRenderer( parameters ) {
 			_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position );
 			_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position );
 			_gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW );
 			_gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW );
 
 
-			state.enableAttribute( programAttributes.position );
+			bindingStates.enableAttribute( programAttributes.position );
 			_gl.vertexAttribPointer( programAttributes.position, 3, _gl.FLOAT, false, 0, 0 );
 			_gl.vertexAttribPointer( programAttributes.position, 3, _gl.FLOAT, false, 0, 0 );
 
 
 		}
 		}
@@ -671,7 +667,7 @@ function WebGLRenderer( parameters ) {
 			_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal );
 			_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal );
 			_gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW );
 			_gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW );
 
 
-			state.enableAttribute( programAttributes.normal );
+			bindingStates.enableAttribute( programAttributes.normal );
 			_gl.vertexAttribPointer( programAttributes.normal, 3, _gl.FLOAT, false, 0, 0 );
 			_gl.vertexAttribPointer( programAttributes.normal, 3, _gl.FLOAT, false, 0, 0 );
 
 
 		}
 		}
@@ -681,7 +677,7 @@ function WebGLRenderer( parameters ) {
 			_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv );
 			_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv );
 			_gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW );
 			_gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW );
 
 
-			state.enableAttribute( programAttributes.uv );
+			bindingStates.enableAttribute( programAttributes.uv );
 			_gl.vertexAttribPointer( programAttributes.uv, 2, _gl.FLOAT, false, 0, 0 );
 			_gl.vertexAttribPointer( programAttributes.uv, 2, _gl.FLOAT, false, 0, 0 );
 
 
 		}
 		}
@@ -691,12 +687,12 @@ function WebGLRenderer( parameters ) {
 			_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color );
 			_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color );
 			_gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW );
 			_gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW );
 
 
-			state.enableAttribute( programAttributes.color );
+			bindingStates.enableAttribute( programAttributes.color );
 			_gl.vertexAttribPointer( programAttributes.color, 3, _gl.FLOAT, false, 0, 0 );
 			_gl.vertexAttribPointer( programAttributes.color, 3, _gl.FLOAT, false, 0, 0 );
 
 
 		}
 		}
 
 
-		state.disableUnusedAttributes();
+		bindingStates.disableUnusedAttributes();
 
 
 		_gl.drawArrays( _gl.TRIANGLES, 0, object.count );
 		_gl.drawArrays( _gl.TRIANGLES, 0, object.count );
 
 
@@ -712,27 +708,6 @@ function WebGLRenderer( parameters ) {
 
 
 		var program = setProgram( camera, fog, material, object );
 		var program = setProgram( camera, fog, material, object );
 
 
-		var updateBuffers = false;
-
-		if ( _currentGeometryProgram.geometry !== geometry.id ||
-			_currentGeometryProgram.program !== program.id ||
-			_currentGeometryProgram.wireframe !== ( material.wireframe === true ) ) {
-
-			_currentGeometryProgram.geometry = geometry.id;
-			_currentGeometryProgram.program = program.id;
-			_currentGeometryProgram.wireframe = material.wireframe === true;
-			updateBuffers = true;
-
-		}
-
-		if ( object.morphTargetInfluences ) {
-
-			morphtargets.update( object, geometry, material, program );
-
-			updateBuffers = true;
-
-		}
-
 		//
 		//
 
 
 		var index = geometry.index;
 		var index = geometry.index;
@@ -746,6 +721,14 @@ function WebGLRenderer( parameters ) {
 
 
 		}
 		}
 
 
+		if ( object.morphTargetInfluences ) {
+
+			morphtargets.update( object, geometry, material, program );
+
+		}
+
+		bindingStates.setup( material, program, geometry, index );
+
 		var attribute;
 		var attribute;
 		var renderer = bufferRenderer;
 		var renderer = bufferRenderer;
 
 
@@ -758,18 +741,6 @@ function WebGLRenderer( parameters ) {
 
 
 		}
 		}
 
 
-		if ( updateBuffers ) {
-
-			setupVertexAttributes( material, program, geometry );
-
-			if ( index !== null ) {
-
-				_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attribute.buffer );
-
-			}
-
-		}
-
 		//
 		//
 
 
 		var dataCount = Infinity;
 		var dataCount = Infinity;
@@ -875,135 +846,6 @@ function WebGLRenderer( parameters ) {
 
 
 	};
 	};
 
 
-	function setupVertexAttributes( material, program, geometry ) {
-
-		if ( geometry && geometry.isInstancedBufferGeometry && ! capabilities.isWebGL2 ) {
-
-			if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) {
-
-				console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
-				return;
-
-			}
-
-		}
-
-		state.initAttributes();
-
-		var geometryAttributes = geometry.attributes;
-
-		var programAttributes = program.getAttributes();
-
-		var materialDefaultAttributeValues = material.defaultAttributeValues;
-
-		for ( var name in programAttributes ) {
-
-			var programAttribute = programAttributes[ name ];
-
-			if ( programAttribute >= 0 ) {
-
-				var geometryAttribute = geometryAttributes[ name ];
-
-				if ( geometryAttribute !== undefined ) {
-
-					var normalized = geometryAttribute.normalized;
-					var size = geometryAttribute.itemSize;
-
-					var attribute = attributes.get( geometryAttribute );
-
-					// TODO Attribute may not be available on context restore
-
-					if ( attribute === undefined ) continue;
-
-					var buffer = attribute.buffer;
-					var type = attribute.type;
-					var bytesPerElement = attribute.bytesPerElement;
-
-					if ( geometryAttribute.isInterleavedBufferAttribute ) {
-
-						var data = geometryAttribute.data;
-						var stride = data.stride;
-						var offset = geometryAttribute.offset;
-
-						if ( data && data.isInstancedInterleavedBuffer ) {
-
-							state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute );
-
-							if ( geometry.maxInstancedCount === undefined ) {
-
-								geometry.maxInstancedCount = data.meshPerAttribute * data.count;
-
-							}
-
-						} else {
-
-							state.enableAttribute( programAttribute );
-
-						}
-
-						_gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );
-						_gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement );
-
-					} else {
-
-						if ( geometryAttribute.isInstancedBufferAttribute ) {
-
-							state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute );
-
-							if ( geometry.maxInstancedCount === undefined ) {
-
-								geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;
-
-							}
-
-						} else {
-
-							state.enableAttribute( programAttribute );
-
-						}
-
-						_gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );
-						_gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 );
-
-					}
-
-				} else if ( materialDefaultAttributeValues !== undefined ) {
-
-					var value = materialDefaultAttributeValues[ name ];
-
-					if ( value !== undefined ) {
-
-						switch ( value.length ) {
-
-							case 2:
-								_gl.vertexAttrib2fv( programAttribute, value );
-								break;
-
-							case 3:
-								_gl.vertexAttrib3fv( programAttribute, value );
-								break;
-
-							case 4:
-								_gl.vertexAttrib4fv( programAttribute, value );
-								break;
-
-							default:
-								_gl.vertexAttrib1fv( programAttribute, value );
-
-						}
-
-					}
-
-				}
-
-			}
-
-		}
-
-		state.disableUnusedAttributes();
-
-	}
-
 	// Compile
 	// Compile
 
 
 	this.compile = function ( scene, camera ) {
 	this.compile = function ( scene, camera ) {
@@ -1109,9 +951,7 @@ function WebGLRenderer( parameters ) {
 
 
 		// reset caching for this frame
 		// reset caching for this frame
 
 
-		_currentGeometryProgram.geometry = null;
-		_currentGeometryProgram.program = null;
-		_currentGeometryProgram.wireframe = false;
+		bindingStates.resetDefaultState();
 		_currentMaterialId = - 1;
 		_currentMaterialId = - 1;
 		_currentCamera = null;
 		_currentCamera = null;
 
 
@@ -1430,9 +1270,7 @@ function WebGLRenderer( parameters ) {
 
 
 			var program = setProgram( camera, scene.fog, material, object );
 			var program = setProgram( camera, scene.fog, material, object );
 
 
-			_currentGeometryProgram.geometry = null;
-			_currentGeometryProgram.program = null;
-			_currentGeometryProgram.wireframe = false;
+			bindingStates.reset();
 
 
 			renderObjectImmediate( object, program );
 			renderObjectImmediate( object, program );
 
 

+ 532 - 0
src/renderers/webgl/WebGLBindingStates.js

@@ -0,0 +1,532 @@
+/**
+ * @author Mugen87 / https://github.com/Mugen87
+ * @author Takahiro / https://github.com/takahirox
+ */
+
+function WebGLBindingStates( gl, extensions, attributes, capabilities ) {
+
+	var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );
+
+	var extension = capabilities.isWebGL2 ? null : extensions.get( 'OES_vertex_array_object' );
+	var vaoAvailable = capabilities.isWebGL2 || extension !== null;
+
+	var bindingStates = {};
+
+	var defaultState = createBindingState( null );
+	var currentState = defaultState;
+
+	function setup( material, program, geometry, index ) {
+
+		var updateBuffers = false;
+
+		if ( vaoAvailable ) {
+
+			var state = getBindingState( geometry, program, material );
+
+			if ( currentState !== state ) {
+
+				currentState = state;
+				bindVertexArrayObject( currentState.object );
+
+			}
+
+			updateBuffers = needsUpdate( geometry );
+
+			if ( updateBuffers ) saveCache( geometry );
+
+		} else {
+
+			var wireframe = ( material.wireframe === true );
+
+			if ( currentState.geometry !== geometry.id ||
+				currentState.program !== program.id ||
+				currentState.wireframe !== wireframe ) {
+
+				currentState.geometry = geometry.id;
+				currentState.program = program.id;
+				currentState.wireframe = wireframe;
+
+				updateBuffers = true;
+
+			}
+
+		}
+
+		if ( index !== null ) {
+
+			attributes.update( index, gl.ELEMENT_ARRAY_BUFFER );
+
+		}
+
+		if ( updateBuffers ) {
+
+			setupVertexAttributes( material, program, geometry );
+
+			if ( index !== null ) {
+
+				gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, attributes.get( index ).buffer );
+
+			}
+
+		}
+
+	}
+
+	function createVertexArrayObject() {
+
+		if ( capabilities.isWebGL2 ) return gl.createVertexArray();
+
+		return extension.createVertexArrayOES();
+
+	}
+
+	function bindVertexArrayObject( vao ) {
+
+		if ( capabilities.isWebGL2 ) return gl.bindVertexArray( vao );
+
+		return extension.bindVertexArrayOES( vao );
+
+	}
+
+	function deleteVertexArrayObject( vao ) {
+
+		if ( capabilities.isWebGL2 ) return gl.deleteVertexArray( vao );
+
+		return extension.deleteVertexArrayOES( vao );
+
+	}
+
+	function getBindingState( geometry, program, material ) {
+
+		var wireframe = ( material.wireframe === true );
+
+		var programMap = bindingStates[ geometry.id ];
+
+		if ( programMap === undefined ) {
+
+			programMap = {};
+			bindingStates[ geometry.id ] = programMap;
+
+		}
+
+		var stateMap = programMap[ program.id ];
+
+		if ( stateMap === undefined ) {
+
+			stateMap = {};
+			programMap[ program.id ] = stateMap;
+
+		}
+
+		var state = stateMap[ wireframe ];
+
+		if ( state === undefined ) {
+
+			state = createBindingState( createVertexArrayObject() );
+			stateMap[ wireframe ] = state;
+
+		}
+
+		return state;
+
+	}
+
+	function createBindingState( vao ) {
+
+		var newAttributes = [];
+		var enabledAttributes = [];
+		var attributeDivisors = [];
+
+		for ( var i = 0; i < maxVertexAttributes; i ++ ) {
+
+			newAttributes[ i ] = 0;
+			enabledAttributes[ i ] = 0;
+			attributeDivisors[ i ] = 0;
+
+		}
+
+		return {
+
+			// for backward compatibility on non-VAO support browser
+			geometry: null,
+			program: null,
+			wireframe: false,
+
+			newAttributes: newAttributes,
+			enabledAttributes: enabledAttributes,
+			attributeDivisors: attributeDivisors,
+			object: vao,
+			attributes: {}
+
+		};
+
+	}
+
+	function needsUpdate( geometry ) {
+
+		var cachedAttributes = currentState.attributes;
+		var geometryAttributes = geometry.attributes;
+
+		if ( Object.keys( cachedAttributes ).length !== Object.keys( geometryAttributes ).length ) return true;
+
+		for ( var key in geometryAttributes ) {
+
+			var cachedAttribute = cachedAttributes[ key ];
+			var geometryAttribute = geometryAttributes[ key ];
+
+			if ( cachedAttribute.attribute !== geometryAttribute ) return true;
+
+			if ( cachedAttribute.version !== geometryAttribute.version2 ) return true;
+
+			if ( cachedAttribute.data.buffer !== geometryAttribute.data ) return true;
+
+			if ( geometryAttribute.data &&
+				cachedAttribute.data.version !== geometryAttribute.data.version2 ) return true;
+
+		}
+
+		return false;
+
+	}
+
+	function saveCache( geometry ) {
+
+		var cache = {};
+		var attributes = geometry.attributes;
+
+		for ( var key in attributes ) {
+
+			var attribute = attributes[ key ];
+
+			var data = {};
+			data.attribute = attribute;
+			data.version = attribute.version2;
+
+			data.data = {};
+
+			if ( attribute.data ) {
+
+				data.data.buffer = attribute.data;
+				data.data.version = attribute.data.version2;
+
+			}
+
+			cache[ key ] = data;
+
+		}
+
+		currentState.attributes = cache;
+
+	}
+
+	function initAttributes() {
+
+		var newAttributes = currentState.newAttributes;
+
+		for ( var i = 0, il = newAttributes.length; i < il; i ++ ) {
+
+			newAttributes[ i ] = 0;
+
+		}
+
+	}
+
+	function enableAttribute( attribute ) {
+
+		enableAttributeAndDivisor( attribute, 0 );
+
+	}
+
+	function enableAttributeAndDivisor( attribute, meshPerAttribute ) {
+
+		var newAttributes = currentState.newAttributes;
+		var enabledAttributes = currentState.enabledAttributes;
+		var attributeDivisors = currentState.attributeDivisors;
+
+		newAttributes[ attribute ] = 1;
+
+		if ( enabledAttributes[ attribute ] === 0 ) {
+
+			gl.enableVertexAttribArray( attribute );
+			enabledAttributes[ attribute ] = 1;
+
+		}
+
+		if ( attributeDivisors[ attribute ] !== meshPerAttribute ) {
+
+			var extension = capabilities.isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' );
+
+			extension[ capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute );
+			attributeDivisors[ attribute ] = meshPerAttribute;
+
+		}
+
+	}
+
+	function disableUnusedAttributes() {
+
+		var newAttributes = currentState.newAttributes;
+		var enabledAttributes = currentState.enabledAttributes;
+
+		for ( var i = 0, il = enabledAttributes.length; i < il; i ++ ) {
+
+			if ( enabledAttributes[ i ] !== newAttributes[ i ] ) {
+
+				gl.disableVertexAttribArray( i );
+				enabledAttributes[ i ] = 0;
+
+			}
+
+		}
+
+	}
+
+	function setupVertexAttributes( material, program, geometry ) {
+
+		if ( geometry && geometry.isInstancedBufferGeometry & ! capabilities.isWebGL2 ) {
+
+			if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) {
+
+				console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
+				return;
+
+			}
+
+		}
+
+		initAttributes();
+
+		var geometryAttributes = geometry.attributes;
+
+		var programAttributes = program.getAttributes();
+
+		var materialDefaultAttributeValues = material.defaultAttributeValues;
+
+		for ( var name in programAttributes ) {
+
+			var programAttribute = programAttributes[ name ];
+
+			if ( programAttribute >= 0 ) {
+
+				var geometryAttribute = geometryAttributes[ name ];
+
+				if ( geometryAttribute !== undefined ) {
+
+					var normalized = geometryAttribute.normalized;
+					var size = geometryAttribute.itemSize;
+
+					var attribute = attributes.get( geometryAttribute );
+
+					// TODO Attribute may not be available on context restore
+
+					if ( attribute === undefined ) continue;
+
+					var buffer = attribute.buffer;
+					var type = attribute.type;
+					var bytesPerElement = attribute.bytesPerElement;
+
+					if ( geometryAttribute.isInterleavedBufferAttribute ) {
+
+						var data = geometryAttribute.data;
+						var stride = data.stride;
+						var offset = geometryAttribute.offset;
+
+						if ( data && data.isInstancedInterleavedBuffer ) {
+
+							enableAttributeAndDivisor( programAttribute, data.meshPerAttribute );
+
+							if ( geometry.maxInstancedCount === undefined ) {
+
+								geometry.maxInstancedCount = data.meshPerAttribute * data.count;
+
+							}
+
+						} else {
+
+							enableAttribute( programAttribute );
+
+						}
+
+						gl.bindBuffer( gl.ARRAY_BUFFER, buffer );
+						gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement );
+
+					} else {
+
+						if ( geometryAttribute.isInstancedBufferAttribute ) {
+
+							enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute );
+
+							if ( geometry.maxInstancedCount === undefined ) {
+
+								geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;
+
+							}
+
+						} else {
+
+							enableAttribute( programAttribute );
+
+						}
+
+						gl.bindBuffer( gl.ARRAY_BUFFER, buffer );
+						gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 );
+
+					}
+
+				} else if ( materialDefaultAttributeValues !== undefined ) {
+
+					var value = materialDefaultAttributeValues[ name ];
+
+					if ( value !== undefined ) {
+
+						switch ( value.length ) {
+
+							case 2:
+								gl.vertexAttrib2fv( programAttribute, value );
+								break;
+
+							case 3:
+								gl.vertexAttrib3fv( programAttribute, value );
+								break;
+
+							case 4:
+								gl.vertexAttrib4fv( programAttribute, value );
+								break;
+
+							default:
+								gl.vertexAttrib1fv( programAttribute, value );
+
+						}
+
+					}
+
+				}
+
+			}
+
+		}
+
+		disableUnusedAttributes();
+
+	}
+
+	function dispose() {
+
+		reset();
+
+		for ( var geometryId in bindingStates ) {
+
+			var programMap = bindingStates[ geometryId ];
+
+			for ( var programId in programMap ) {
+
+				var stateMap = programMap[ programId ];
+
+				for ( var wireframe in stateMap ) {
+
+					deleteVertexArrayObject( stateMap[ wireframe ].object );
+
+					delete stateMap[ wireframe ];
+
+				}
+
+				delete programMap[ programId ];
+
+			}
+
+			delete bindingStates[ geometryId ];
+
+		}
+
+	}
+
+	function releaseStatesOfGeometry( geometry ) {
+
+		if ( bindingStates[ geometry.id ] === undefined ) return;
+
+		var programMap = bindingStates[ geometry.id ];
+
+		for ( var programId in programMap ) {
+
+			var stateMap = programMap[ programId ];
+
+			for ( var wireframe in stateMap ) {
+
+				deleteVertexArrayObject( stateMap[ wireframe ].object );
+
+				delete stateMap[ wireframe ];
+
+			}
+
+			delete programMap[ programId ];
+
+		}
+
+		delete bindingStates[ geometry.id ];
+
+	}
+
+	function releaseStatesOfProgram( program ) {
+
+		for ( var geometryId in bindingStates ) {
+
+			var programMap = bindingStates[ geometryId ];
+
+			if ( programMap[ program.id ] === undefined ) continue;
+
+			var stateMap = programMap[ program.id ];
+
+			for ( var wireframe in stateMap ) {
+
+				deleteVertexArrayObject( stateMap[ wireframe ].object );
+
+				delete stateMap[ wireframe ];
+
+			}
+
+			delete programMap[ program.id ];
+
+		}
+
+	}
+
+	function reset() {
+
+		resetDefaultState();
+
+		if ( currentState === defaultState ) return;
+
+		currentState = defaultState;
+		bindVertexArrayObject( currentState.object );
+
+	}
+
+	// for backward-compatilibity
+
+	function resetDefaultState() {
+
+		defaultState.geometry = null;
+		defaultState.program = null;
+		defaultState.wireframe = false;
+
+	}
+
+	return {
+
+		setup: setup,
+		reset: reset,
+		resetDefaultState: resetDefaultState,
+		dispose: dispose,
+		releaseStatesOfGeometry: releaseStatesOfGeometry,
+		releaseStatesOfProgram: releaseStatesOfProgram,
+
+		initAttributes: initAttributes,
+		enableAttribute: enableAttribute,
+		disableUnusedAttributes: disableUnusedAttributes
+
+	};
+
+}
+
+
+export { WebGLBindingStates };

+ 5 - 8
src/renderers/webgl/WebGLGeometries.js

@@ -6,7 +6,7 @@ import { Uint16BufferAttribute, Uint32BufferAttribute } from '../../core/BufferA
 import { BufferGeometry } from '../../core/BufferGeometry.js';
 import { BufferGeometry } from '../../core/BufferGeometry.js';
 import { arrayMax } from '../../utils.js';
 import { arrayMax } from '../../utils.js';
 
 
-function WebGLGeometries( gl, attributes, info ) {
+function WebGLGeometries( gl, attributes, info, bindingStates ) {
 
 
 	var geometries = {};
 	var geometries = {};
 	var wireframeAttributes = {};
 	var wireframeAttributes = {};
@@ -41,6 +41,8 @@ function WebGLGeometries( gl, attributes, info ) {
 
 
 		}
 		}
 
 
+		bindingStates.releaseStatesOfGeometry( geometry );
+
 		//
 		//
 
 
 		info.memory.geometries --;
 		info.memory.geometries --;
@@ -81,14 +83,9 @@ function WebGLGeometries( gl, attributes, info ) {
 
 
 	function update( geometry ) {
 	function update( geometry ) {
 
 
-		var index = geometry.index;
 		var geometryAttributes = geometry.attributes;
 		var geometryAttributes = geometry.attributes;
 
 
-		if ( index !== null ) {
-
-			attributes.update( index, gl.ELEMENT_ARRAY_BUFFER );
-
-		}
+		// Updating index buffer in VAO now. See WebGLBindingStates.
 
 
 		for ( var name in geometryAttributes ) {
 		for ( var name in geometryAttributes ) {
 
 
@@ -161,7 +158,7 @@ function WebGLGeometries( gl, attributes, info ) {
 
 
 		attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 );
 		attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 );
 
 
-		attributes.update( attribute, gl.ELEMENT_ARRAY_BUFFER );
+		// Updating index buffer in VAO now. See WebGLBindingStates
 
 
 		wireframeAttributes[ geometry.id ] = attribute;
 		wireframeAttributes[ geometry.id ] = attribute;
 
 

+ 59 - 27
src/renderers/webgl/WebGLMorphtargets.js

@@ -2,6 +2,12 @@
  * @author mrdoob / http://mrdoob.com/
  * @author mrdoob / http://mrdoob.com/
  */
  */
 
 
+function numericalSort( a, b ) {
+
+	return a[ 0 ] - b[ 0 ];
+
+}
+
 function absNumericalSort( a, b ) {
 function absNumericalSort( a, b ) {
 
 
 	return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] );
 	return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] );
@@ -13,6 +19,14 @@ function WebGLMorphtargets( gl ) {
 	var influencesList = {};
 	var influencesList = {};
 	var morphInfluences = new Float32Array( 8 );
 	var morphInfluences = new Float32Array( 8 );
 
 
+	var workInfluences = [];
+
+	for ( var i = 0; i < 8; i ++ ) {
+
+		workInfluences[ i ] = [ i, 0 ];
+
+	}
+
 	function update( object, geometry, material, program ) {
 	function update( object, geometry, material, program ) {
 
 
 		var objectInfluences = object.morphTargetInfluences;
 		var objectInfluences = object.morphTargetInfluences;
@@ -37,61 +51,79 @@ function WebGLMorphtargets( gl ) {
 
 
 		}
 		}
 
 
-		var morphTargets = material.morphTargets && geometry.morphAttributes.position;
-		var morphNormals = material.morphNormals && geometry.morphAttributes.normal;
-
-		// Remove current morphAttributes
+		// Collect influences
 
 
 		for ( var i = 0; i < length; i ++ ) {
 		for ( var i = 0; i < length; i ++ ) {
 
 
 			var influence = influences[ i ];
 			var influence = influences[ i ];
 
 
-			if ( influence[ 1 ] !== 0 ) {
+			influence[ 0 ] = i;
+			influence[ 1 ] = objectInfluences[ i ];
 
 
-				if ( morphTargets ) geometry.removeAttribute( 'morphTarget' + i );
-				if ( morphNormals ) geometry.removeAttribute( 'morphNormal' + i );
+		}
 
 
-			}
+		influences.sort( absNumericalSort );
 
 
-		}
+		for ( var i = 0; i < 8; i ++ ) {
 
 
-		// Collect influences
+			if ( i < length && influences[ i ][ 1 ] ) {
 
 
-		for ( var i = 0; i < length; i ++ ) {
+				workInfluences[ i ][ 0 ] = influences[ i ][ 0 ];
+				workInfluences[ i ][ 1 ] = influences[ i ][ 1 ];
 
 
-			var influence = influences[ i ];
+			} else {
 
 
-			influence[ 0 ] = i;
-			influence[ 1 ] = objectInfluences[ i ];
+				workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER;
+				workInfluences[ i ][ 1 ] = 0;
+
+			}
 
 
 		}
 		}
 
 
-		influences.sort( absNumericalSort );
+		workInfluences.sort( numericalSort );
 
 
-		// Add morphAttributes
+		var morphTargets = material.morphTargets && geometry.morphAttributes.position;
+		var morphNormals = material.morphNormals && geometry.morphAttributes.normal;
 
 
 		for ( var i = 0; i < 8; i ++ ) {
 		for ( var i = 0; i < 8; i ++ ) {
 
 
-			var influence = influences[ i ];
+			var influence = workInfluences[ i ];
+			var index = influence[ 0 ];
+			var value = influence[ 1 ];
 
 
-			if ( influence ) {
+			if ( index !== Number.MAX_SAFE_INTEGER && value ) {
 
 
-				var index = influence[ 0 ];
-				var value = influence[ 1 ];
+				if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) {
 
 
-				if ( value ) {
+					geometry.addAttribute( 'morphTarget' + i, morphTargets[ index ] );
 
 
-					if ( morphTargets ) geometry.addAttribute( 'morphTarget' + i, morphTargets[ index ] );
-					if ( morphNormals ) geometry.addAttribute( 'morphNormal' + i, morphNormals[ index ] );
+				}
 
 
-					morphInfluences[ i ] = value;
-					continue;
+				if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) {
+
+					geometry.addAttribute( 'morphNormal' + i, morphNormals[ index ] );
 
 
 				}
 				}
 
 
-			}
+				morphInfluences[ i ] = value;
 
 
-			morphInfluences[ i ] = 0;
+			} else {
+
+				if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== undefined ) {
+
+					geometry.removeAttribute( 'morphTarget' + i );
+
+				}
+
+				if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== undefined ) {
+
+					geometry.removeAttribute( 'morphNormal' + i );
+
+				}
+
+				morphInfluences[ i ] = 0;
+
+			}
 
 
 		}
 		}
 
 

+ 3 - 1
src/renderers/webgl/WebGLProgram.js

@@ -206,7 +206,7 @@ function unrollLoops( string ) {
 
 
 }
 }
 
 
-function WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities, textures ) {
+function WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities, textures, bindingStates ) {
 
 
 	var gl = renderer.context;
 	var gl = renderer.context;
 
 
@@ -703,6 +703,8 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters,
 
 
 	this.destroy = function () {
 	this.destroy = function () {
 
 
+		bindingStates.releaseStatesOfProgram( this );
+
 		gl.deleteProgram( program );
 		gl.deleteProgram( program );
 		this.program = undefined;
 		this.program = undefined;
 
 

+ 2 - 2
src/renderers/webgl/WebGLPrograms.js

@@ -5,7 +5,7 @@
 import { BackSide, DoubleSide, CubeUVRefractionMapping, CubeUVReflectionMapping, GammaEncoding, LinearEncoding, ObjectSpaceNormalMap } from '../../constants.js';
 import { BackSide, DoubleSide, CubeUVRefractionMapping, CubeUVReflectionMapping, GammaEncoding, LinearEncoding, ObjectSpaceNormalMap } from '../../constants.js';
 import { WebGLProgram } from './WebGLProgram.js';
 import { WebGLProgram } from './WebGLProgram.js';
 
 
-function WebGLPrograms( renderer, extensions, capabilities, textures ) {
+function WebGLPrograms( renderer, extensions, capabilities, textures, bindingStates ) {
 
 
 	var programs = [];
 	var programs = [];
 
 
@@ -279,7 +279,7 @@ function WebGLPrograms( renderer, extensions, capabilities, textures ) {
 
 
 		if ( program === undefined ) {
 		if ( program === undefined ) {
 
 
-			program = new WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities, textures );
+			program = new WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities, textures, bindingStates );
 			programs.push( program );
 			programs.push( program );
 
 
 		}
 		}

+ 1 - 74
src/renderers/webgl/WebGLState.js

@@ -5,7 +5,7 @@
 import { NotEqualDepth, GreaterDepth, GreaterEqualDepth, EqualDepth, LessEqualDepth, LessDepth, AlwaysDepth, NeverDepth, CullFaceFront, CullFaceBack, CullFaceNone, CustomBlending, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NoBlending, NormalBlending, AddEquation, DoubleSide, BackSide } from '../../constants.js';
 import { NotEqualDepth, GreaterDepth, GreaterEqualDepth, EqualDepth, LessEqualDepth, LessDepth, AlwaysDepth, NeverDepth, CullFaceFront, CullFaceBack, CullFaceNone, CustomBlending, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NoBlending, NormalBlending, AddEquation, DoubleSide, BackSide } from '../../constants.js';
 import { Vector4 } from '../../math/Vector4.js';
 import { Vector4 } from '../../math/Vector4.js';
 
 
-function WebGLState( gl, extensions, utils, capabilities ) {
+function WebGLState( gl, extensions, utils ) {
 
 
 	function ColorBuffer() {
 	function ColorBuffer() {
 
 
@@ -312,11 +312,6 @@ function WebGLState( gl, extensions, utils, capabilities ) {
 	var depthBuffer = new DepthBuffer();
 	var depthBuffer = new DepthBuffer();
 	var stencilBuffer = new StencilBuffer();
 	var stencilBuffer = new StencilBuffer();
 
 
-	var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );
-	var newAttributes = new Uint8Array( maxVertexAttributes );
-	var enabledAttributes = new Uint8Array( maxVertexAttributes );
-	var attributeDivisors = new Uint8Array( maxVertexAttributes );
-
 	var enabledCapabilities = {};
 	var enabledCapabilities = {};
 
 
 	var compressedTextureFormats = null;
 	var compressedTextureFormats = null;
@@ -405,59 +400,6 @@ function WebGLState( gl, extensions, utils, capabilities ) {
 
 
 	//
 	//
 
 
-	function initAttributes() {
-
-		for ( var i = 0, l = newAttributes.length; i < l; i ++ ) {
-
-			newAttributes[ i ] = 0;
-
-		}
-
-	}
-
-	function enableAttribute( attribute ) {
-
-		enableAttributeAndDivisor( attribute, 0 );
-
-	}
-
-	function enableAttributeAndDivisor( attribute, meshPerAttribute ) {
-
-		newAttributes[ attribute ] = 1;
-
-		if ( enabledAttributes[ attribute ] === 0 ) {
-
-			gl.enableVertexAttribArray( attribute );
-			enabledAttributes[ attribute ] = 1;
-
-		}
-
-		if ( attributeDivisors[ attribute ] !== meshPerAttribute ) {
-
-			var extension = capabilities.isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' );
-
-			extension[ capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute );
-			attributeDivisors[ attribute ] = meshPerAttribute;
-
-		}
-
-	}
-
-	function disableUnusedAttributes() {
-
-		for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) {
-
-			if ( enabledAttributes[ i ] !== newAttributes[ i ] ) {
-
-				gl.disableVertexAttribArray( i );
-				enabledAttributes[ i ] = 0;
-
-			}
-
-		}
-
-	}
-
 	function enable( id ) {
 	function enable( id ) {
 
 
 		if ( enabledCapabilities[ id ] !== true ) {
 		if ( enabledCapabilities[ id ] !== true ) {
@@ -899,17 +841,6 @@ function WebGLState( gl, extensions, utils, capabilities ) {
 
 
 	function reset() {
 	function reset() {
 
 
-		for ( var i = 0; i < enabledAttributes.length; i ++ ) {
-
-			if ( enabledAttributes[ i ] === 1 ) {
-
-				gl.disableVertexAttribArray( i );
-				enabledAttributes[ i ] = 0;
-
-			}
-
-		}
-
 		enabledCapabilities = {};
 		enabledCapabilities = {};
 
 
 		compressedTextureFormats = null;
 		compressedTextureFormats = null;
@@ -938,10 +869,6 @@ function WebGLState( gl, extensions, utils, capabilities ) {
 			stencil: stencilBuffer
 			stencil: stencilBuffer
 		},
 		},
 
 
-		initAttributes: initAttributes,
-		enableAttribute: enableAttribute,
-		enableAttributeAndDivisor: enableAttributeAndDivisor,
-		disableUnusedAttributes: disableUnusedAttributes,
 		enable: enable,
 		enable: enable,
 		disable: disable,
 		disable: disable,
 		getCompressedTextureFormats: getCompressedTextureFormats,
 		getCompressedTextureFormats: getCompressedTextureFormats,