finishRender( renderContext ) {

		const { gl, state } = this;
		const renderContextData = this.get( renderContext );
		const previousContext = renderContextData.previousContext;

		state.resetVertexState();

		const occlusionQueryCount = renderContext.occlusionQueryCount;

		if ( occlusionQueryCount > 0 ) {

			if ( occlusionQueryCount > renderContextData.occlusionQueryIndex ) {

				gl.endQuery( gl.ANY_SAMPLES_PASSED );

			}

			this.resolveOccludedAsync( renderContext );

		}

		const textures = renderContext.textures;

		if ( textures !== null ) {

			for ( let i = 0; i < textures.length; i ++ ) {

				const texture = textures[ i ];

				if ( texture.generateMipmaps ) {

					this.generateMipmaps( texture );

				}

			}

		}

		this._currentContext = previousContext;
		const renderTarget = renderContext.renderTarget;

		if ( renderContext.textures !== null && renderTarget ) {

			const renderTargetContextData = this.get( renderTarget );

			if ( renderTarget.samples > 0 && this._useMultisampledExtension( renderTarget ) === false ) {

				const fb = renderTargetContextData.framebuffers[ renderContext.getCacheKey() ];

				let mask = gl.COLOR_BUFFER_BIT;

				if ( renderTarget.resolveDepthBuffer ) {

					if ( renderTarget.depthBuffer ) mask |= gl.DEPTH_BUFFER_BIT;
					if ( renderTarget.stencilBuffer && renderTarget.resolveStencilBuffer ) mask |= gl.STENCIL_BUFFER_BIT;

				}

				const msaaFrameBuffer = renderTargetContextData.msaaFrameBuffer;
				const msaaRenderbuffers = renderTargetContextData.msaaRenderbuffers;

				const textures = renderContext.textures;
				const isMRT = textures.length > 1;

				state.bindFramebuffer( gl.READ_FRAMEBUFFER, msaaFrameBuffer );
				state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb );

				if ( isMRT ) {

					// blitFramebuffer() can only copy/resolve the first color attachment of a framebuffer. When using MRT,
					// the engine temporarily removes all attachments and then configures each attachment for the resolve.

					for ( let i = 0; i < textures.length; i ++ ) {

						gl.framebufferRenderbuffer( gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.RENDERBUFFER, null );
						gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, null, 0 );

					}

				}

				for ( let i = 0; i < textures.length; i ++ ) {

					if ( isMRT ) {

						// configure attachment for resolve

						const { textureGPU } = this.get( textures[ i ] );

						gl.framebufferRenderbuffer( gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, msaaRenderbuffers[ i ] );
						gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textureGPU, 0 );

					}

					if ( renderContext.scissor ) {

						const { x, y, width, height } = renderContext.scissorValue;

						const viewY = renderContext.height - height - y;

						gl.blitFramebuffer( x, viewY, x + width, viewY + height, x, viewY, x + width, viewY + height, mask, gl.NEAREST );

						if ( this._supportsInvalidateFramebuffer === true ) {

							gl.invalidateSubFramebuffer( gl.READ_FRAMEBUFFER, renderTargetContextData.invalidationArray, x, viewY, width, height );

						}

					} else {

						gl.blitFramebuffer( 0, 0, renderContext.width, renderContext.height, 0, 0, renderContext.width, renderContext.height, mask, gl.NEAREST );

						if ( this._supportsInvalidateFramebuffer === true ) {

							gl.invalidateFramebuffer( gl.READ_FRAMEBUFFER, renderTargetContextData.invalidationArray );

						}

					}

				}

				if ( isMRT ) {

					// restore attachments

					for ( let i = 0; i < textures.length; i ++ ) {

						const { textureGPU } = this.get( textures[ i ] );

						gl.framebufferRenderbuffer( gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.RENDERBUFFER, msaaRenderbuffers[ i ] );
						gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, textureGPU, 0 );

					}

				}

			} else if ( renderTarget.resolveDepthBuffer === false && renderTargetContextData.framebuffers ) {

				const fb = renderTargetContextData.framebuffers[ renderContext.getCacheKey() ];
				state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb );
				gl.invalidateFramebuffer( gl.DRAW_FRAMEBUFFER, renderTargetContextData.depthInvalidationArray );

			}

		}

		if ( previousContext !== null ) {

			this._setFramebuffer( previousContext );

			if ( previousContext.viewport ) {

				this.updateViewport( previousContext );

			} else {

				const { width, height } = this.getDrawingBufferSize( _drawingBufferSize );
				state.viewport( 0, 0, width, height );

			}

		}

		this.prepareTimestampBuffer( renderContext );

	}
This method is executed at the end of a render call and finalizes work
after draw calls.

@param {RenderContext} renderContext - The render context.