copyTextureToTexture( srcTexture, dstTexture, srcRegion = null, dstPosition = null, srcLevel = 0, dstLevel = 0 ) {

		const { gl, backend } = this;
		const { state } = this.backend;

		const { textureGPU: dstTextureGPU, glTextureType, glType, glFormat } = backend.get( dstTexture );

		state.bindTexture( glTextureType, dstTextureGPU );

		// gather the necessary dimensions to copy
		let width, height, depth, minX, minY, minZ;
		let dstX, dstY, dstZ;
		const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ dstLevel ] : srcTexture.image;

		if ( srcRegion !== null ) {

			width = srcRegion.max.x - srcRegion.min.x;
			height = srcRegion.max.y - srcRegion.min.y;
			depth = srcRegion.isBox3 ? srcRegion.max.z - srcRegion.min.z : 1;
			minX = srcRegion.min.x;
			minY = srcRegion.min.y;
			minZ = srcRegion.isBox3 ? srcRegion.min.z : 0;

		} else {

			const levelScale = Math.pow( 2, - srcLevel );
			width = Math.floor( image.width * levelScale );
			height = Math.floor( image.height * levelScale );

			if ( srcTexture.isDataArrayTexture || srcTexture.isArrayTexture ) {

				depth = image.depth;

			} else if ( srcTexture.isData3DTexture ) {

				depth = Math.floor( image.depth * levelScale );

			} else {

				depth = 1;

			}

			minX = 0;
			minY = 0;
			minZ = 0;

		}

		if ( dstPosition !== null ) {

			dstX = dstPosition.x;
			dstY = dstPosition.y;
			dstZ = dstPosition.z;

		} else {

			dstX = 0;
			dstY = 0;
			dstZ = 0;

		}


		gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY );
		gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha );
		gl.pixelStorei( gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );

		// used for copying data from cpu
		const currentUnpackRowLen = gl.getParameter( gl.UNPACK_ROW_LENGTH );
		const currentUnpackImageHeight = gl.getParameter( gl.UNPACK_IMAGE_HEIGHT );
		const currentUnpackSkipPixels = gl.getParameter( gl.UNPACK_SKIP_PIXELS );
		const currentUnpackSkipRows = gl.getParameter( gl.UNPACK_SKIP_ROWS );
		const currentUnpackSkipImages = gl.getParameter( gl.UNPACK_SKIP_IMAGES );

		gl.pixelStorei( gl.UNPACK_ROW_LENGTH, image.width );
		gl.pixelStorei( gl.UNPACK_IMAGE_HEIGHT, image.height );
		gl.pixelStorei( gl.UNPACK_SKIP_PIXELS, minX );
		gl.pixelStorei( gl.UNPACK_SKIP_ROWS, minY );
		gl.pixelStorei( gl.UNPACK_SKIP_IMAGES, minZ );

		// set up the src texture
		const isDst3D = dstTexture.isDataArrayTexture || dstTexture.isData3DTexture || dstTexture.isArrayTexture;
		if ( srcTexture.isRenderTargetTexture || srcTexture.isDepthTexture ) {

			const srcTextureData = backend.get( srcTexture );
			const dstTextureData = backend.get( dstTexture );

			const srcRenderContextData = backend.get( srcTextureData.renderTarget );
			const dstRenderContextData = backend.get( dstTextureData.renderTarget );

			const srcFramebuffer = srcRenderContextData.framebuffers[ srcTextureData.cacheKey ];
			const dstFramebuffer = dstRenderContextData.framebuffers[ dstTextureData.cacheKey ];

			state.bindFramebuffer( gl.READ_FRAMEBUFFER, srcFramebuffer );
			state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, dstFramebuffer );

			let mask = gl.COLOR_BUFFER_BIT;

			if ( srcTexture.isDepthTexture ) mask = gl.DEPTH_BUFFER_BIT;

			gl.blitFramebuffer( minX, minY, width, height, dstX, dstY, width, height, mask, gl.NEAREST );

			state.bindFramebuffer( gl.READ_FRAMEBUFFER, null );
			state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null );

		} else {

			if ( isDst3D ) {

				// copy data into the 3d texture
				if ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) {

					gl.texSubImage3D( glTextureType, dstLevel, dstX, dstY, dstZ, width, height, depth, glFormat, glType, image.data );

				} else if ( dstTexture.isCompressedArrayTexture ) {

					gl.compressedTexSubImage3D( glTextureType, dstLevel, dstX, dstY, dstZ, width, height, depth, glFormat, image.data );

				} else {

					gl.texSubImage3D( glTextureType, dstLevel, dstX, dstY, dstZ, width, height, depth, glFormat, glType, image );

				}

			} else {

				// copy data into the 2d texture
				if ( srcTexture.isDataTexture ) {

					gl.texSubImage2D( glTextureType, dstLevel, dstX, dstY, width, height, glFormat, glType, image.data );

				} else if ( srcTexture.isCompressedTexture ) {

					gl.compressedTexSubImage2D( glTextureType, dstLevel, dstX, dstY, image.width, image.height, glFormat, image.data );

				} else {

					gl.texSubImage2D( glTextureType, dstLevel, dstX, dstY, width, height, glFormat, glType, image );

				}

			}

		}

		// reset values
		gl.pixelStorei( gl.UNPACK_ROW_LENGTH, currentUnpackRowLen );
		gl.pixelStorei( gl.UNPACK_IMAGE_HEIGHT, currentUnpackImageHeight );
		gl.pixelStorei( gl.UNPACK_SKIP_PIXELS, currentUnpackSkipPixels );
		gl.pixelStorei( gl.UNPACK_SKIP_ROWS, currentUnpackSkipRows );
		gl.pixelStorei( gl.UNPACK_SKIP_IMAGES, currentUnpackSkipImages );

		// Generate mipmaps only when copying level 0
		if ( dstLevel === 0 && dstTexture.generateMipmaps ) {

			gl.generateMipmap( glTextureType );

		}

		state.unbindTexture();

	}
Copies data of the given source texture to the given destination texture.

@param {Texture} srcTexture - The source texture.
@param {Texture} dstTexture - The destination texture.
@param {?(Box3|Box2)} [srcRegion=null] - The region of the source texture to copy.
@param {?(Vector2|Vector3)} [dstPosition=null] - The destination position of the copy.
@param {number} [srcLevel=0] - The source mip level to copy from.
@param {number} [dstLevel=0] - The destination mip level to copy to.