File: //var/www/aspa/three/renderers/webgl/WebGLState.js
import { NotEqualDepth, GreaterDepth, GreaterEqualDepth, EqualDepth, LessEqualDepth, LessDepth, AlwaysDepth, NeverDepth, CullFaceFront, CullFaceBack, CullFaceNone, DoubleSide, BackSide, CustomBlending, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NoBlending, NormalBlending, AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation, ZeroFactor, OneFactor, SrcColorFactor, SrcAlphaFactor, SrcAlphaSaturateFactor, DstColorFactor, DstAlphaFactor, OneMinusSrcColorFactor, OneMinusSrcAlphaFactor, OneMinusDstColorFactor, OneMinusDstAlphaFactor, ConstantColorFactor, OneMinusConstantColorFactor, ConstantAlphaFactor, OneMinusConstantAlphaFactor } from '../../constants.js';
import { Color } from '../../math/Color.js';
import { Vector4 } from '../../math/Vector4.js';
function WebGLState( gl, extensions, capabilities ) {
	const isWebGL2 = capabilities.isWebGL2;
	function ColorBuffer() {
		let locked = false;
		const color = new Vector4();
		let currentColorMask = null;
		const currentColorClear = new Vector4( 0, 0, 0, 0 );
		return {
			setMask: function ( colorMask ) {
				if ( currentColorMask !== colorMask && ! locked ) {
					gl.colorMask( colorMask, colorMask, colorMask, colorMask );
					currentColorMask = colorMask;
				}
			},
			setLocked: function ( lock ) {
				locked = lock;
			},
			setClear: function ( r, g, b, a, premultipliedAlpha ) {
				if ( premultipliedAlpha === true ) {
					r *= a; g *= a; b *= a;
				}
				color.set( r, g, b, a );
				if ( currentColorClear.equals( color ) === false ) {
					gl.clearColor( r, g, b, a );
					currentColorClear.copy( color );
				}
			},
			reset: function () {
				locked = false;
				currentColorMask = null;
				currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state
			}
		};
	}
	function DepthBuffer() {
		let locked = false;
		let currentDepthMask = null;
		let currentDepthFunc = null;
		let currentDepthClear = null;
		return {
			setTest: function ( depthTest ) {
				if ( depthTest ) {
					enable( gl.DEPTH_TEST );
				} else {
					disable( gl.DEPTH_TEST );
				}
			},
			setMask: function ( depthMask ) {
				if ( currentDepthMask !== depthMask && ! locked ) {
					gl.depthMask( depthMask );
					currentDepthMask = depthMask;
				}
			},
			setFunc: function ( depthFunc ) {
				if ( currentDepthFunc !== depthFunc ) {
					switch ( depthFunc ) {
						case NeverDepth:
							gl.depthFunc( gl.NEVER );
							break;
						case AlwaysDepth:
							gl.depthFunc( gl.ALWAYS );
							break;
						case LessDepth:
							gl.depthFunc( gl.LESS );
							break;
						case LessEqualDepth:
							gl.depthFunc( gl.LEQUAL );
							break;
						case EqualDepth:
							gl.depthFunc( gl.EQUAL );
							break;
						case GreaterEqualDepth:
							gl.depthFunc( gl.GEQUAL );
							break;
						case GreaterDepth:
							gl.depthFunc( gl.GREATER );
							break;
						case NotEqualDepth:
							gl.depthFunc( gl.NOTEQUAL );
							break;
						default:
							gl.depthFunc( gl.LEQUAL );
					}
					currentDepthFunc = depthFunc;
				}
			},
			setLocked: function ( lock ) {
				locked = lock;
			},
			setClear: function ( depth ) {
				if ( currentDepthClear !== depth ) {
					gl.clearDepth( depth );
					currentDepthClear = depth;
				}
			},
			reset: function () {
				locked = false;
				currentDepthMask = null;
				currentDepthFunc = null;
				currentDepthClear = null;
			}
		};
	}
	function StencilBuffer() {
		let locked = false;
		let currentStencilMask = null;
		let currentStencilFunc = null;
		let currentStencilRef = null;
		let currentStencilFuncMask = null;
		let currentStencilFail = null;
		let currentStencilZFail = null;
		let currentStencilZPass = null;
		let currentStencilClear = null;
		return {
			setTest: function ( stencilTest ) {
				if ( ! locked ) {
					if ( stencilTest ) {
						enable( gl.STENCIL_TEST );
					} else {
						disable( gl.STENCIL_TEST );
					}
				}
			},
			setMask: function ( stencilMask ) {
				if ( currentStencilMask !== stencilMask && ! locked ) {
					gl.stencilMask( stencilMask );
					currentStencilMask = stencilMask;
				}
			},
			setFunc: function ( stencilFunc, stencilRef, stencilMask ) {
				if ( currentStencilFunc !== stencilFunc ||
				     currentStencilRef !== stencilRef ||
				     currentStencilFuncMask !== stencilMask ) {
					gl.stencilFunc( stencilFunc, stencilRef, stencilMask );
					currentStencilFunc = stencilFunc;
					currentStencilRef = stencilRef;
					currentStencilFuncMask = stencilMask;
				}
			},
			setOp: function ( stencilFail, stencilZFail, stencilZPass ) {
				if ( currentStencilFail !== stencilFail ||
				     currentStencilZFail !== stencilZFail ||
				     currentStencilZPass !== stencilZPass ) {
					gl.stencilOp( stencilFail, stencilZFail, stencilZPass );
					currentStencilFail = stencilFail;
					currentStencilZFail = stencilZFail;
					currentStencilZPass = stencilZPass;
				}
			},
			setLocked: function ( lock ) {
				locked = lock;
			},
			setClear: function ( stencil ) {
				if ( currentStencilClear !== stencil ) {
					gl.clearStencil( stencil );
					currentStencilClear = stencil;
				}
			},
			reset: function () {
				locked = false;
				currentStencilMask = null;
				currentStencilFunc = null;
				currentStencilRef = null;
				currentStencilFuncMask = null;
				currentStencilFail = null;
				currentStencilZFail = null;
				currentStencilZPass = null;
				currentStencilClear = null;
			}
		};
	}
	//
	const colorBuffer = new ColorBuffer();
	const depthBuffer = new DepthBuffer();
	const stencilBuffer = new StencilBuffer();
	const uboBindings = new WeakMap();
	const uboProgramMap = new WeakMap();
	let enabledCapabilities = {};
	let currentBoundFramebuffers = {};
	let currentDrawbuffers = new WeakMap();
	let defaultDrawbuffers = [];
	let currentProgram = null;
	let currentBlendingEnabled = false;
	let currentBlending = null;
	let currentBlendEquation = null;
	let currentBlendSrc = null;
	let currentBlendDst = null;
	let currentBlendEquationAlpha = null;
	let currentBlendSrcAlpha = null;
	let currentBlendDstAlpha = null;
	let currentBlendColor = new Color( 0, 0, 0 );
	let currentBlendAlpha = 0;
	let currentPremultipledAlpha = false;
	let currentFlipSided = null;
	let currentCullFace = null;
	let currentLineWidth = null;
	let currentPolygonOffsetFactor = null;
	let currentPolygonOffsetUnits = null;
	const maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS );
	let lineWidthAvailable = false;
	let version = 0;
	const glVersion = gl.getParameter( gl.VERSION );
	if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) {
		version = parseFloat( /^WebGL (\d)/.exec( glVersion )[ 1 ] );
		lineWidthAvailable = ( version >= 1.0 );
	} else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) {
		version = parseFloat( /^OpenGL ES (\d)/.exec( glVersion )[ 1 ] );
		lineWidthAvailable = ( version >= 2.0 );
	}
	let currentTextureSlot = null;
	let currentBoundTextures = {};
	const scissorParam = gl.getParameter( gl.SCISSOR_BOX );
	const viewportParam = gl.getParameter( gl.VIEWPORT );
	const currentScissor = new Vector4().fromArray( scissorParam );
	const currentViewport = new Vector4().fromArray( viewportParam );
	function createTexture( type, target, count, dimensions ) {
		const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4.
		const texture = gl.createTexture();
		gl.bindTexture( type, texture );
		gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
		gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
		for ( let i = 0; i < count; i ++ ) {
			if ( isWebGL2 && ( type === gl.TEXTURE_3D || type === gl.TEXTURE_2D_ARRAY ) ) {
				gl.texImage3D( target, 0, gl.RGBA, 1, 1, dimensions, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );
			} else {
				gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );
			}
		}
		return texture;
	}
	const emptyTextures = {};
	emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 );
	emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 );
	if ( isWebGL2 ) {
		emptyTextures[ gl.TEXTURE_2D_ARRAY ] = createTexture( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_2D_ARRAY, 1, 1 );
		emptyTextures[ gl.TEXTURE_3D ] = createTexture( gl.TEXTURE_3D, gl.TEXTURE_3D, 1, 1 );
	}
	// init
	colorBuffer.setClear( 0, 0, 0, 1 );
	depthBuffer.setClear( 1 );
	stencilBuffer.setClear( 0 );
	enable( gl.DEPTH_TEST );
	depthBuffer.setFunc( LessEqualDepth );
	setFlipSided( false );
	setCullFace( CullFaceBack );
	enable( gl.CULL_FACE );
	setBlending( NoBlending );
	//
	function enable( id ) {
		if ( enabledCapabilities[ id ] !== true ) {
			gl.enable( id );
			enabledCapabilities[ id ] = true;
		}
	}
	function disable( id ) {
		if ( enabledCapabilities[ id ] !== false ) {
			gl.disable( id );
			enabledCapabilities[ id ] = false;
		}
	}
	function bindFramebuffer( target, framebuffer ) {
		if ( currentBoundFramebuffers[ target ] !== framebuffer ) {
			gl.bindFramebuffer( target, framebuffer );
			currentBoundFramebuffers[ target ] = framebuffer;
			if ( isWebGL2 ) {
				// gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER
				if ( target === gl.DRAW_FRAMEBUFFER ) {
					currentBoundFramebuffers[ gl.FRAMEBUFFER ] = framebuffer;
				}
				if ( target === gl.FRAMEBUFFER ) {
					currentBoundFramebuffers[ gl.DRAW_FRAMEBUFFER ] = framebuffer;
				}
			}
			return true;
		}
		return false;
	}
	function drawBuffers( renderTarget, framebuffer ) {
		let drawBuffers = defaultDrawbuffers;
		let needsUpdate = false;
		if ( renderTarget ) {
			drawBuffers = currentDrawbuffers.get( framebuffer );
			if ( drawBuffers === undefined ) {
				drawBuffers = [];
				currentDrawbuffers.set( framebuffer, drawBuffers );
			}
			const textures = renderTarget.textures;
			if ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) {
				for ( let i = 0, il = textures.length; i < il; i ++ ) {
					drawBuffers[ i ] = gl.COLOR_ATTACHMENT0 + i;
				}
				drawBuffers.length = textures.length;
				needsUpdate = true;
			}
		} else {
			if ( drawBuffers[ 0 ] !== gl.BACK ) {
				drawBuffers[ 0 ] = gl.BACK;
				needsUpdate = true;
			}
		}
		if ( needsUpdate ) {
			if ( capabilities.isWebGL2 ) {
				gl.drawBuffers( drawBuffers );
			} else if ( extensions.has( 'WEBGL_draw_buffers' ) === true ) {
				extensions.get( 'WEBGL_draw_buffers' ).drawBuffersWEBGL( drawBuffers );
			} else {
				throw new Error( 'THREE.WebGLState: Usage of gl.drawBuffers() require WebGL2 or WEBGL_draw_buffers extension' );
			}
		}
	}
	function useProgram( program ) {
		if ( currentProgram !== program ) {
			gl.useProgram( program );
			currentProgram = program;
			return true;
		}
		return false;
	}
	const equationToGL = {
		[ AddEquation ]: gl.FUNC_ADD,
		[ SubtractEquation ]: gl.FUNC_SUBTRACT,
		[ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT
	};
	if ( isWebGL2 ) {
		equationToGL[ MinEquation ] = gl.MIN;
		equationToGL[ MaxEquation ] = gl.MAX;
	} else {
		const extension = extensions.get( 'EXT_blend_minmax' );
		if ( extension !== null ) {
			equationToGL[ MinEquation ] = extension.MIN_EXT;
			equationToGL[ MaxEquation ] = extension.MAX_EXT;
		}
	}
	const factorToGL = {
		[ ZeroFactor ]: gl.ZERO,
		[ OneFactor ]: gl.ONE,
		[ SrcColorFactor ]: gl.SRC_COLOR,
		[ SrcAlphaFactor ]: gl.SRC_ALPHA,
		[ SrcAlphaSaturateFactor ]: gl.SRC_ALPHA_SATURATE,
		[ DstColorFactor ]: gl.DST_COLOR,
		[ DstAlphaFactor ]: gl.DST_ALPHA,
		[ OneMinusSrcColorFactor ]: gl.ONE_MINUS_SRC_COLOR,
		[ OneMinusSrcAlphaFactor ]: gl.ONE_MINUS_SRC_ALPHA,
		[ OneMinusDstColorFactor ]: gl.ONE_MINUS_DST_COLOR,
		[ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA,
		[ ConstantColorFactor ]: gl.CONSTANT_COLOR,
		[ OneMinusConstantColorFactor ]: gl.ONE_MINUS_CONSTANT_COLOR,
		[ ConstantAlphaFactor ]: gl.CONSTANT_ALPHA,
		[ OneMinusConstantAlphaFactor ]: gl.ONE_MINUS_CONSTANT_ALPHA
	};
	function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, blendColor, blendAlpha, premultipliedAlpha ) {
		if ( blending === NoBlending ) {
			if ( currentBlendingEnabled === true ) {
				disable( gl.BLEND );
				currentBlendingEnabled = false;
			}
			return;
		}
		if ( currentBlendingEnabled === false ) {
			enable( gl.BLEND );
			currentBlendingEnabled = true;
		}
		if ( blending !== CustomBlending ) {
			if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) {
				if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) {
					gl.blendEquation( gl.FUNC_ADD );
					currentBlendEquation = AddEquation;
					currentBlendEquationAlpha = AddEquation;
				}
				if ( premultipliedAlpha ) {
					switch ( blending ) {
						case NormalBlending:
							gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
							break;
						case AdditiveBlending:
							gl.blendFunc( gl.ONE, gl.ONE );
							break;
						case SubtractiveBlending:
							gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE );
							break;
						case MultiplyBlending:
							gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA );
							break;
						default:
							console.error( 'THREE.WebGLState: Invalid blending: ', blending );
							break;
					}
				} else {
					switch ( blending ) {
						case NormalBlending:
							gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
							break;
						case AdditiveBlending:
							gl.blendFunc( gl.SRC_ALPHA, gl.ONE );
							break;
						case SubtractiveBlending:
							gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE );
							break;
						case MultiplyBlending:
							gl.blendFunc( gl.ZERO, gl.SRC_COLOR );
							break;
						default:
							console.error( 'THREE.WebGLState: Invalid blending: ', blending );
							break;
					}
				}
				currentBlendSrc = null;
				currentBlendDst = null;
				currentBlendSrcAlpha = null;
				currentBlendDstAlpha = null;
				currentBlendColor.set( 0, 0, 0 );
				currentBlendAlpha = 0;
				currentBlending = blending;
				currentPremultipledAlpha = premultipliedAlpha;
			}
			return;
		}
		// custom blending
		blendEquationAlpha = blendEquationAlpha || blendEquation;
		blendSrcAlpha = blendSrcAlpha || blendSrc;
		blendDstAlpha = blendDstAlpha || blendDst;
		if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {
			gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] );
			currentBlendEquation = blendEquation;
			currentBlendEquationAlpha = blendEquationAlpha;
		}
		if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {
			gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] );
			currentBlendSrc = blendSrc;
			currentBlendDst = blendDst;
			currentBlendSrcAlpha = blendSrcAlpha;
			currentBlendDstAlpha = blendDstAlpha;
		}
		if ( blendColor.equals( currentBlendColor ) === false || blendAlpha !== currentBlendAlpha ) {
			gl.blendColor( blendColor.r, blendColor.g, blendColor.b, blendAlpha );
			currentBlendColor.copy( blendColor );
			currentBlendAlpha = blendAlpha;
		}
		currentBlending = blending;
		currentPremultipledAlpha = false;
	}
	function setMaterial( material, frontFaceCW ) {
		material.side === DoubleSide
			? disable( gl.CULL_FACE )
			: enable( gl.CULL_FACE );
		let flipSided = ( material.side === BackSide );
		if ( frontFaceCW ) flipSided = ! flipSided;
		setFlipSided( flipSided );
		( material.blending === NormalBlending && material.transparent === false )
			? setBlending( NoBlending )
			: setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha );
		depthBuffer.setFunc( material.depthFunc );
		depthBuffer.setTest( material.depthTest );
		depthBuffer.setMask( material.depthWrite );
		colorBuffer.setMask( material.colorWrite );
		const stencilWrite = material.stencilWrite;
		stencilBuffer.setTest( stencilWrite );
		if ( stencilWrite ) {
			stencilBuffer.setMask( material.stencilWriteMask );
			stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask );
			stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass );
		}
		setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
		material.alphaToCoverage === true
			? enable( gl.SAMPLE_ALPHA_TO_COVERAGE )
			: disable( gl.SAMPLE_ALPHA_TO_COVERAGE );
	}
	//
	function setFlipSided( flipSided ) {
		if ( currentFlipSided !== flipSided ) {
			if ( flipSided ) {
				gl.frontFace( gl.CW );
			} else {
				gl.frontFace( gl.CCW );
			}
			currentFlipSided = flipSided;
		}
	}
	function setCullFace( cullFace ) {
		if ( cullFace !== CullFaceNone ) {
			enable( gl.CULL_FACE );
			if ( cullFace !== currentCullFace ) {
				if ( cullFace === CullFaceBack ) {
					gl.cullFace( gl.BACK );
				} else if ( cullFace === CullFaceFront ) {
					gl.cullFace( gl.FRONT );
				} else {
					gl.cullFace( gl.FRONT_AND_BACK );
				}
			}
		} else {
			disable( gl.CULL_FACE );
		}
		currentCullFace = cullFace;
	}
	function setLineWidth( width ) {
		if ( width !== currentLineWidth ) {
			if ( lineWidthAvailable ) gl.lineWidth( width );
			currentLineWidth = width;
		}
	}
	function setPolygonOffset( polygonOffset, factor, units ) {
		if ( polygonOffset ) {
			enable( gl.POLYGON_OFFSET_FILL );
			if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) {
				gl.polygonOffset( factor, units );
				currentPolygonOffsetFactor = factor;
				currentPolygonOffsetUnits = units;
			}
		} else {
			disable( gl.POLYGON_OFFSET_FILL );
		}
	}
	function setScissorTest( scissorTest ) {
		if ( scissorTest ) {
			enable( gl.SCISSOR_TEST );
		} else {
			disable( gl.SCISSOR_TEST );
		}
	}
	// texture
	function activeTexture( webglSlot ) {
		if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;
		if ( currentTextureSlot !== webglSlot ) {
			gl.activeTexture( webglSlot );
			currentTextureSlot = webglSlot;
		}
	}
	function bindTexture( webglType, webglTexture, webglSlot ) {
		if ( webglSlot === undefined ) {
			if ( currentTextureSlot === null ) {
				webglSlot = gl.TEXTURE0 + maxTextures - 1;
			} else {
				webglSlot = currentTextureSlot;
			}
		}
		let boundTexture = currentBoundTextures[ webglSlot ];
		if ( boundTexture === undefined ) {
			boundTexture = { type: undefined, texture: undefined };
			currentBoundTextures[ webglSlot ] = boundTexture;
		}
		if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {
			if ( currentTextureSlot !== webglSlot ) {
				gl.activeTexture( webglSlot );
				currentTextureSlot = webglSlot;
			}
			gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] );
			boundTexture.type = webglType;
			boundTexture.texture = webglTexture;
		}
	}
	function unbindTexture() {
		const boundTexture = currentBoundTextures[ currentTextureSlot ];
		if ( boundTexture !== undefined && boundTexture.type !== undefined ) {
			gl.bindTexture( boundTexture.type, null );
			boundTexture.type = undefined;
			boundTexture.texture = undefined;
		}
	}
	function compressedTexImage2D() {
		try {
			gl.compressedTexImage2D.apply( gl, arguments );
		} catch ( error ) {
			console.error( 'THREE.WebGLState:', error );
		}
	}
	function compressedTexImage3D() {
		try {
			gl.compressedTexImage3D.apply( gl, arguments );
		} catch ( error ) {
			console.error( 'THREE.WebGLState:', error );
		}
	}
	function texSubImage2D() {
		try {
			gl.texSubImage2D.apply( gl, arguments );
		} catch ( error ) {
			console.error( 'THREE.WebGLState:', error );
		}
	}
	function texSubImage3D() {
		try {
			gl.texSubImage3D.apply( gl, arguments );
		} catch ( error ) {
			console.error( 'THREE.WebGLState:', error );
		}
	}
	function compressedTexSubImage2D() {
		try {
			gl.compressedTexSubImage2D.apply( gl, arguments );
		} catch ( error ) {
			console.error( 'THREE.WebGLState:', error );
		}
	}
	function compressedTexSubImage3D() {
		try {
			gl.compressedTexSubImage3D.apply( gl, arguments );
		} catch ( error ) {
			console.error( 'THREE.WebGLState:', error );
		}
	}
	function texStorage2D() {
		try {
			gl.texStorage2D.apply( gl, arguments );
		} catch ( error ) {
			console.error( 'THREE.WebGLState:', error );
		}
	}
	function texStorage3D() {
		try {
			gl.texStorage3D.apply( gl, arguments );
		} catch ( error ) {
			console.error( 'THREE.WebGLState:', error );
		}
	}
	function texImage2D() {
		try {
			gl.texImage2D.apply( gl, arguments );
		} catch ( error ) {
			console.error( 'THREE.WebGLState:', error );
		}
	}
	function texImage3D() {
		try {
			gl.texImage3D.apply( gl, arguments );
		} catch ( error ) {
			console.error( 'THREE.WebGLState:', error );
		}
	}
	//
	function scissor( scissor ) {
		if ( currentScissor.equals( scissor ) === false ) {
			gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w );
			currentScissor.copy( scissor );
		}
	}
	function viewport( viewport ) {
		if ( currentViewport.equals( viewport ) === false ) {
			gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w );
			currentViewport.copy( viewport );
		}
	}
	function updateUBOMapping( uniformsGroup, program ) {
		let mapping = uboProgramMap.get( program );
		if ( mapping === undefined ) {
			mapping = new WeakMap();
			uboProgramMap.set( program, mapping );
		}
		let blockIndex = mapping.get( uniformsGroup );
		if ( blockIndex === undefined ) {
			blockIndex = gl.getUniformBlockIndex( program, uniformsGroup.name );
			mapping.set( uniformsGroup, blockIndex );
		}
	}
	function uniformBlockBinding( uniformsGroup, program ) {
		const mapping = uboProgramMap.get( program );
		const blockIndex = mapping.get( uniformsGroup );
		if ( uboBindings.get( program ) !== blockIndex ) {
			// bind shader specific block index to global block point
			gl.uniformBlockBinding( program, blockIndex, uniformsGroup.__bindingPointIndex );
			uboBindings.set( program, blockIndex );
		}
	}
	//
	function reset() {
		// reset state
		gl.disable( gl.BLEND );
		gl.disable( gl.CULL_FACE );
		gl.disable( gl.DEPTH_TEST );
		gl.disable( gl.POLYGON_OFFSET_FILL );
		gl.disable( gl.SCISSOR_TEST );
		gl.disable( gl.STENCIL_TEST );
		gl.disable( gl.SAMPLE_ALPHA_TO_COVERAGE );
		gl.blendEquation( gl.FUNC_ADD );
		gl.blendFunc( gl.ONE, gl.ZERO );
		gl.blendFuncSeparate( gl.ONE, gl.ZERO, gl.ONE, gl.ZERO );
		gl.blendColor( 0, 0, 0, 0 );
		gl.colorMask( true, true, true, true );
		gl.clearColor( 0, 0, 0, 0 );
		gl.depthMask( true );
		gl.depthFunc( gl.LESS );
		gl.clearDepth( 1 );
		gl.stencilMask( 0xffffffff );
		gl.stencilFunc( gl.ALWAYS, 0, 0xffffffff );
		gl.stencilOp( gl.KEEP, gl.KEEP, gl.KEEP );
		gl.clearStencil( 0 );
		gl.cullFace( gl.BACK );
		gl.frontFace( gl.CCW );
		gl.polygonOffset( 0, 0 );
		gl.activeTexture( gl.TEXTURE0 );
		gl.bindFramebuffer( gl.FRAMEBUFFER, null );
		if ( isWebGL2 === true ) {
			gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null );
			gl.bindFramebuffer( gl.READ_FRAMEBUFFER, null );
		}
		gl.useProgram( null );
		gl.lineWidth( 1 );
		gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height );
		gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height );
		// reset internals
		enabledCapabilities = {};
		currentTextureSlot = null;
		currentBoundTextures = {};
		currentBoundFramebuffers = {};
		currentDrawbuffers = new WeakMap();
		defaultDrawbuffers = [];
		currentProgram = null;
		currentBlendingEnabled = false;
		currentBlending = null;
		currentBlendEquation = null;
		currentBlendSrc = null;
		currentBlendDst = null;
		currentBlendEquationAlpha = null;
		currentBlendSrcAlpha = null;
		currentBlendDstAlpha = null;
		currentBlendColor = new Color( 0, 0, 0 );
		currentBlendAlpha = 0;
		currentPremultipledAlpha = false;
		currentFlipSided = null;
		currentCullFace = null;
		currentLineWidth = null;
		currentPolygonOffsetFactor = null;
		currentPolygonOffsetUnits = null;
		currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height );
		currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height );
		colorBuffer.reset();
		depthBuffer.reset();
		stencilBuffer.reset();
	}
	return {
		buffers: {
			color: colorBuffer,
			depth: depthBuffer,
			stencil: stencilBuffer
		},
		enable: enable,
		disable: disable,
		bindFramebuffer: bindFramebuffer,
		drawBuffers: drawBuffers,
		useProgram: useProgram,
		setBlending: setBlending,
		setMaterial: setMaterial,
		setFlipSided: setFlipSided,
		setCullFace: setCullFace,
		setLineWidth: setLineWidth,
		setPolygonOffset: setPolygonOffset,
		setScissorTest: setScissorTest,
		activeTexture: activeTexture,
		bindTexture: bindTexture,
		unbindTexture: unbindTexture,
		compressedTexImage2D: compressedTexImage2D,
		compressedTexImage3D: compressedTexImage3D,
		texImage2D: texImage2D,
		texImage3D: texImage3D,
		updateUBOMapping: updateUBOMapping,
		uniformBlockBinding: uniformBlockBinding,
		texStorage2D: texStorage2D,
		texStorage3D: texStorage3D,
		texSubImage2D: texSubImage2D,
		texSubImage3D: texSubImage3D,
		compressedTexSubImage2D: compressedTexSubImage2D,
		compressedTexSubImage3D: compressedTexSubImage3D,
		scissor: scissor,
		viewport: viewport,
		reset: reset
	};
}
export { WebGLState };