UNPKG

phaser

Version:

A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.

1,396 lines (1,223 loc) 99.1 kB
/** * @author Richard Davey <rich@phaser.io> * @author Felipe Alfonso <@bitnenfer> * @copyright 2013-2026 Phaser Studio Inc. * @license {@link https://opensource.org/licenses/MIT|MIT License} */ var ArrayEach = require('../../utils/array/Each'); var ArrayRemove = require('../../utils/array/Remove'); var Class = require('../../utils/Class'); var CONST = require('../../const'); var EventEmitter = require('eventemitter3'); var Events = require('../events'); var IsSizePowerOfTwo = require('../../math/pow2/IsSizePowerOfTwo'); var Matrix4 = require('../../math/Matrix4'); var NOOP = require('../../utils/NOOP'); var DrawingContext = require('./DrawingContext'); var ScaleEvents = require('../../scale/events'); var TextureEvents = require('../../textures/events'); var Utils = require('./Utils'); var WebGLSnapshot = require('../snapshot/WebGLSnapshot'); var WebGLBufferWrapper = require('./wrappers/WebGLBufferWrapper'); var WebGLGlobalWrapper = require('./wrappers/WebGLGlobalWrapper'); var WebGLProgramWrapper = require('./wrappers/WebGLProgramWrapper'); var WebGLShaderSetterWrapper = require('./wrappers/WebGLShaderSetterWrapper'); var WebGLTextureWrapper = require('./wrappers/WebGLTextureWrapper'); var WebGLTextureUnitsWrapper = require('./wrappers/WebGLTextureUnitsWrapper'); var WebGLFramebufferWrapper = require('./wrappers/WebGLFramebufferWrapper'); var WebGLVAOWrapper = require('./wrappers/WebGLVAOWrapper'); var WebGLBlendParametersFactory = require('./parameters/WebGLBlendParametersFactory'); var WebGLGlobalParametersFactory = require('./parameters/WebGLGlobalParametersFactory'); var RenderNodeManager = require('./renderNodes/RenderNodeManager'); var DrawingContextPool = require('./DrawingContextPool'); var ShaderProgramFactory = require('./ShaderProgramFactory'); var DEBUG = false; if (typeof WEBGL_DEBUG) { var SPECTOR = require('phaser3spectorjs'); DEBUG = true; } /** * @classdesc * WebGLRenderer is a class that contains the needed functionality to keep the * WebGLRenderingContext state clean. The main idea of the WebGLRenderer is to keep track of * any context change that happens for WebGL rendering inside of Phaser. This means * if raw webgl functions are called outside the WebGLRenderer of the Phaser WebGL * rendering ecosystem they might pollute the current WebGLRenderingContext state producing * unexpected behavior. It's recommended that WebGL interaction is done through * WebGLRenderer and/or built-in RenderNodes. * * Persistent WebGL objects are stored in "wrappers" which are created by the WebGLRenderer. * Wrappers contain WebGL objects and metadata about those objects. * This can be used to recreate the WebGL state after a context loss. * Prefer to pass references to the wrappers, rather than the raw WebGL objects, * as the raw objects may be destroyed or replaced at any time. * Extract them only when needed. * * WebGL state, such as blend mode or texture units, is managed by the WebGLRenderer. * Use `WebGLRenderer.glWrapper` to manage the current state * rather than setting it directly on the WebGLRenderingContext. * The state wrapper will ensure that the state is only set if it has changed, * and can restore the state after a context loss or external render call. * * @class WebGLRenderer * @extends Phaser.Events.EventEmitter * @memberof Phaser.Renderer.WebGL * @constructor * @since 3.0.0 * * @param {Phaser.Game} game - The Game instance which owns this WebGL Renderer. */ var WebGLRenderer = new Class({ Extends: EventEmitter, initialize: function WebGLRenderer (game) { EventEmitter.call(this); var gameConfig = game.config; var contextCreationConfig = { alpha: gameConfig.transparent, desynchronized: gameConfig.desynchronized, depth: true, antialias: gameConfig.antialiasGL, premultipliedAlpha: gameConfig.premultipliedAlpha, stencil: true, failIfMajorPerformanceCaveat: gameConfig.failIfMajorPerformanceCaveat, powerPreference: gameConfig.powerPreference, preserveDrawingBuffer: gameConfig.preserveDrawingBuffer, willReadFrequently: false }; /** * The local configuration settings of this WebGL Renderer. * * @name Phaser.Renderer.WebGL.WebGLRenderer#config * @type {object} * @since 3.0.0 */ this.config = { clearBeforeRender: gameConfig.clearBeforeRender, antialias: gameConfig.antialias, backgroundColor: gameConfig.backgroundColor, contextCreation: contextCreationConfig, roundPixels: gameConfig.roundPixels, pathDetailThreshold: gameConfig.pathDetailThreshold, maxTextures: gameConfig.maxTextures, maxTextureSize: gameConfig.maxTextureSize, batchSize: gameConfig.batchSize, maxLights: gameConfig.maxLights, mipmapFilter: gameConfig.mipmapFilter, mipmapRegeneration: gameConfig.mipmapRegeneration }; /** * The Game instance which owns this WebGL Renderer. * * @name Phaser.Renderer.WebGL.WebGLRenderer#game * @type {Phaser.Game} * @since 3.0.0 */ this.game = game; /** * A constant which allows the renderer to be easily identified as a WebGL Renderer. * * @name Phaser.Renderer.WebGL.WebGLRenderer#type * @type {number} * @since 3.0.0 */ this.type = CONST.WEBGL; /** * An instance of the RenderNodeManager class which handles all * RenderNodes used by the WebGLRenderer. * * @name Phaser.Renderer.WebGL.WebGLRenderer#renderNodes * @type {Phaser.Renderer.WebGL.RenderNodes.RenderNodeManager} * @since 4.0.0 */ this.renderNodes = null; /** * The RenderNode to use for rendering individual cameras. * * This will be populated during the `boot` method. * * @name Phaser.Renderer.WebGL.WebGLRenderer#cameraRenderNode * @type {Phaser.Renderer.WebGL.RenderNodes.RenderNode} * @since 4.0.0 */ this.cameraRenderNode = null; /** * The shader program factory for managing variant shaders. * * @name Phaser.Renderer.WebGL.WebGLRenderer#shaderProgramFactory * @type {Phaser.Renderer.WebGL.ShaderProgramFactory} * @since 4.0.0 */ this.shaderProgramFactory = new ShaderProgramFactory(this); /** * The width of the canvas being rendered to. * This is populated in the onResize event handler. * * @name Phaser.Renderer.WebGL.WebGLRenderer#width * @type {number} * @since 3.0.0 */ this.width = 0; /** * The height of the canvas being rendered to. * This is populated in the onResize event handler. * * @name Phaser.Renderer.WebGL.WebGLRenderer#height * @type {number} * @since 3.0.0 */ this.height = 0; /** * The canvas which this WebGL Renderer draws to. * * @name Phaser.Renderer.WebGL.WebGLRenderer#canvas * @type {HTMLCanvasElement} * @since 3.0.0 */ this.canvas = game.canvas; /** * An array of blend modes supported by the WebGL Renderer. * * This array includes the default blend modes as well as any custom blend modes added through {@link #addBlendMode}. * * @name Phaser.Renderer.WebGL.WebGLRenderer#blendModes * @type {Phaser.Types.Renderer.WebGL.WebGLBlendParameters[]} * @default [] * @since 3.0.0 */ this.blendModes = []; /** * This property is set to `true` if the WebGL context of the renderer is lost. * * @name Phaser.Renderer.WebGL.WebGLRenderer#contextLost * @type {boolean} * @default false * @since 3.0.0 */ this.contextLost = false; /** * Details about the currently scheduled snapshot. * * If a non-null `callback` is set in this object, a snapshot of the canvas will be taken after the current frame is fully rendered. * * @name Phaser.Renderer.WebGL.WebGLRenderer#snapshotState * @type {Phaser.Types.Renderer.Snapshot.SnapshotState} * @since 3.0.0 */ this.snapshotState = { x: 0, y: 0, width: 1, height: 1, getPixel: false, callback: null, type: 'image/png', encoder: 0.92, isFramebuffer: false, bufferWidth: 0, bufferHeight: 0, unpremultiplyAlpha: true }; /** * The maximum number of textures the GPU can handle. The minimum under the WebGL1 spec is 8. * This is set via the Game Config `maxTextures` property and should never be changed after boot. * * @name Phaser.Renderer.WebGL.WebGLRenderer#maxTextures * @type {number} * @since 3.50.0 */ this.maxTextures = 0; /** * A list containing the indices of all available texture units. * This is populated during the `init` method. * It is used internally to connect texture units to shaders. * * @name Phaser.Renderer.WebGL.WebGLRenderer#textureUnitIndices * @type {number[]} * @since 4.0.0 */ this.textureUnitIndices = []; /** * A list of all WebGLBufferWrappers that have been created by this renderer. * * @name Phaser.Renderer.WebGL.WebGLRenderer#glBufferWrappers * @type {Phaser.Renderer.WebGL.Wrappers.WebGLBufferWrapper[]} * @since 3.80.0 */ this.glBufferWrappers = []; /** * A list of all WebGLProgramWrappers that have been created by this renderer. * * @name Phaser.Renderer.WebGL.WebGLRenderer#glProgramWrappers * @type {Phaser.Renderer.WebGL.Wrappers.WebGLProgramWrapper[]} * @since 3.80.0 */ this.glProgramWrappers = []; /** * A list of all WebGLTextureWrappers that have been created by this renderer. * * @name Phaser.Renderer.WebGL.WebGLRenderer#glTextureWrappers * @type {Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper[]} * @since 3.80.0 */ this.glTextureWrappers = []; /** * A list of all WebGLFramebufferWrappers that have been created by this renderer. * * @name Phaser.Renderer.WebGL.WebGLRenderer#glFramebufferWrappers * @type {Phaser.Renderer.WebGL.Wrappers.WebGLFramebufferWrapper[]} * @since 3.80.0 */ this.glFramebufferWrappers = []; /** * A list of all WebGLVAOWrappers that have been created by this renderer. * * @name Phaser.Renderer.WebGL.WebGLRenderer#glVAOWrappers * @type {Phaser.Renderer.WebGL.Wrappers.WebGLVAOWrapper[]} * @since 4.0.0 */ this.glVAOWrappers = []; /** * A generic quad index buffer. This is a READ-ONLY buffer. * It describes the four corners of a quad, * a structure which is used in several places in the renderer. * * @name Phaser.Renderer.WebGL.WebGLRenderer#genericQuadIndexBuffer * @type {Phaser.Renderer.WebGL.Wrappers.WebGLBufferWrapper} * @since 4.0.0 * @readonly */ this.genericQuadIndexBuffer = null; /** * The DrawingContext used for the base canvas. * * @name Phaser.Renderer.WebGL.WebGLRenderer#baseDrawingContext * @type {Phaser.Renderer.WebGL.DrawingContext} * @since 4.0.0 */ this.baseDrawingContext = null; /** * The camera currently being rendered by `render`. * * @name Phaser.Renderer.WebGL.WebGLRenderer#currentViewCamera * @type {Phaser.Cameras.Scene2D.Camera} * @since 4.0.0 * @default null */ this.currentViewCamera = null; /** * A pool of DrawingContexts which can be reused. * * @name Phaser.Renderer.WebGL.WebGLRenderer#drawingContextPool * @type {Phaser.Renderer.WebGL.DrawingContextPool} * @since 4.0.0 */ this.drawingContextPool = new DrawingContextPool(this, 1000, 1024); /** * The handler to invoke when the context is lost. * This should not be changed and is set in the boot method. * * @name Phaser.Renderer.WebGL.WebGLRenderer#contextLostHandler * @type {function} * @since 3.19.0 */ this.contextLostHandler = NOOP; /** * The handler to invoke when the context is restored. * This should not be changed and is set in the boot method. * * @name Phaser.Renderer.WebGL.WebGLRenderer#contextRestoredHandler * @type {function} * @since 3.19.0 */ this.contextRestoredHandler = NOOP; /** * The previous contextLostHandler that was in use. * This is set when `setContextHandlers` is called. * * @name Phaser.Renderer.WebGL.WebGLRenderer#previousContextLostHandler * @type {function} * @since 3.85.0 */ this.previousContextLostHandler = NOOP; /** * The previous contextRestoredHandler that was in use. * This is set when `setContextHandlers` is called. * * @name Phaser.Renderer.WebGL.WebGLRenderer#previousContextRestoredHandler * @type {function} * @since 3.85.0 */ this.previousContextRestoredHandler = NOOP; /** * The underlying WebGL context of the renderer. * * @name Phaser.Renderer.WebGL.WebGLRenderer#gl * @type {WebGLRenderingContext} * @default null * @since 3.0.0 */ this.gl = null; /** * The current WebGLRenderingContext state. * * @name Phaser.Renderer.WebGL.WebGLRenderer#glWrapper * @type {Phaser.Renderer.WebGL.Wrappers.WebGLGlobalWrapper} * @default null * @since 4.0.0 */ this.glWrapper = null; /** * The current WebGL texture units in use. * * @name Phaser.Renderer.WebGL.WebGLRenderer#glTextureUnits * @type {Phaser.Renderer.WebGL.Wrappers.WebGLTextureUnitsWrapper} * @default null * @since 4.0.0 */ this.glTextureUnits = null; /** * Array of strings that indicate which WebGL extensions are supported by the browser. * This is populated in the `setExtensions` method. * * @name Phaser.Renderer.WebGL.WebGLRenderer#supportedExtensions * @type {string[]} * @default null * @since 3.0.0 */ this.supportedExtensions = null; /** * If the browser supports the `ANGLE_instanced_arrays` extension, this property will hold * a reference to the glExtension for it. * * This is populated in the `setExtensions` method. * * @name Phaser.Renderer.WebGL.WebGLRenderer#instancedArraysExtension * @type {ANGLE_instanced_arrays} * @default null * @since 3.50.0 */ this.instancedArraysExtension = null; /** * If the browser supports the `KHR_parallel_shader_compile` extension, * this property will hold a reference to the glExtension for it. * * This is populated in the `setExtensions` method. * * @name Phaser.Renderer.WebGL.WebGLRenderer#parallelShaderCompileExtension * @type {KHR_parallel_shader_compile} * @default null * @since 4.0.0 */ this.parallelShaderCompileExtension = null; /** * If the browser supports the `OES_standard_derivatives` extension, * and the `smoothPixelArt` config option is true, * this property will hold a reference to the glExtension for it. * * This is populated in the `setExtensions` method. * * @name Phaser.Renderer.WebGL.WebGLRenderer#standardDerivativesExtension * @type {OES_standard_derivatives} * @default null * @since 4.0.0 */ this.standardDerivativesExtension = null; /** * If the browser supports the `OES_vertex_array_object` extension, this property will hold * a reference to the glExtension for it. * * This is populated in the `setExtensions` method. * * @name Phaser.Renderer.WebGL.WebGLRenderer#vaoExtension * @type {OES_vertex_array_object} * @default null * @since 3.50.0 */ this.vaoExtension = null; /** * The WebGL Extensions loaded into the current context. * * @name Phaser.Renderer.WebGL.WebGLRenderer#extensions * @type {object} * @default {} * @since 3.0.0 */ this.extensions = {}; /** * Stores the current WebGL component formats for further use. * * This array is populated in the `init` method. * * @name Phaser.Renderer.WebGL.WebGLRenderer#glFormats * @type {array} * @since 3.2.0 */ this.glFormats; /** * Stores the WebGL texture compression formats that this device and browser supports. * * Support for using compressed texture formats was added in Phaser version 3.60. * * @name Phaser.Renderer.WebGL.WebGLRenderer#compression * @type {Phaser.Types.Renderer.WebGL.WebGLTextureCompression} * @since 3.8.0 */ this.compression; /** * Cached drawing buffer height to reduce gl calls. * * @name Phaser.Renderer.WebGL.WebGLRenderer#drawingBufferHeight * @type {number} * @readonly * @since 3.11.0 */ this.drawingBufferHeight = 0; /** * A blank 32x32 transparent texture, as used by the Graphics system where needed. * This is set in the `boot` method. * * @name Phaser.Renderer.WebGL.WebGLRenderer#blankTexture * @type {Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper} * @readonly * @since 3.12.0 */ this.blankTexture = null; /** * A blank 1x1 #7f7fff texture, a flat normal map, * as used by the Graphics system where needed. * This is set in the `boot` method. * * @name Phaser.Renderer.WebGL.WebGLRenderer#normalTexture * @type {Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper} * @readonly * @since 3.80.0 */ this.normalTexture = null; /** * A pure white 4x4 texture, as used by the Graphics system where needed. * This is set in the `boot` method. * * @name Phaser.Renderer.WebGL.WebGLRenderer#whiteTexture * @type {Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper} * @readonly * @since 3.50.0 */ this.whiteTexture = null; /** * Internal gl function mapping for uniform and attribute look-up. * * https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/uniform * * https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/vertexAttribPointer * * @name Phaser.Renderer.WebGL.WebGLRenderer#shaderSetters * @type {Phaser.Renderer.WebGL.Wrappers.WebGLShaderSetterWrapper} * @since 4.0.0 */ this.shaderSetters = null; /** * The mipmap magFilter to be used when creating textures. * * You can specify this as a string in the game config, i.e.: * * `render: { mipmapFilter: 'NEAREST_MIPMAP_LINEAR' }` * * The 6 options for WebGL1 are, in order from least to most computationally expensive: * * NEAREST (for pixel art) * LINEAR (the default) * NEAREST_MIPMAP_NEAREST * LINEAR_MIPMAP_NEAREST * NEAREST_MIPMAP_LINEAR * LINEAR_MIPMAP_LINEAR * * Mipmaps only work with textures that are fully power-of-two in size. * * For more details see https://webglfundamentals.org/webgl/lessons/webgl-3d-textures.html * * As of v3.60 no mipmaps will be generated unless a string is given in * the game config. This saves on VRAM use when it may not be required. * To obtain the previous result set the property to `LINEAR` in the config. * * @name Phaser.Renderer.WebGL.WebGLRenderer#mipmapFilter * @type {GLenum} * @since 3.21.0 */ this.mipmapFilter = null; /** * Has this renderer fully booted yet? * * @name Phaser.Renderer.WebGL.WebGLRenderer#isBooted * @type {boolean} * @since 3.50.0 */ this.isBooted = false; /** * The global game Projection matrix, used by shaders as 'uProjectionMatrix' uniform. * * @name Phaser.Renderer.WebGL.WebGLRenderer#projectionMatrix * @type {Phaser.Math.Matrix4} * @since 3.50.0 */ this.projectionMatrix; /** * The cached width of the Projection matrix. * * @name Phaser.Renderer.WebGL.WebGLRenderer#projectionWidth * @type {number} * @since 3.50.0 */ this.projectionWidth = 0; /** * The cached height of the Projection matrix. * * @name Phaser.Renderer.WebGL.WebGLRenderer#projectionHeight * @type {number} * @since 3.50.0 */ this.projectionHeight = 0; /** * The cached flipY state of the Projection matrix. * * This is usually `false`, preserving WebGL coordinate space. * * @name Phaser.Renderer.WebGL.WebGLRenderer#projectionFlipY * @type {boolean} * @since 4.0.0 */ this.projectionFlipY = false; /** * An instance of SpectorJS used for WebGL Debugging. * * Only available in the Phaser Debug build. * * @name Phaser.Renderer.WebGL.WebGLRenderer#spector * @type {function} * @since 3.60.0 */ this.spector = null; /** * Is Spector currently capturing a WebGL frame? * * @name Phaser.Renderer.WebGL.WebGLRenderer#_debugCapture * @type {boolean} * @private * @since 3.60.0 */ this._debugCapture = false; this.init(this.config); }, /** * Creates a new WebGLRenderingContext and initializes all internal state. * * @method Phaser.Renderer.WebGL.WebGLRenderer#init * @since 3.0.0 * * @param {object} config - The configuration object for the renderer. * * @return {this} This WebGLRenderer instance. */ init: function (config) { var gl; var game = this.game; var canvas = this.canvas; var clearColor = config.backgroundColor; if (DEBUG) { this.spector = new SPECTOR.Spector(); this.spector.onCapture.add(this.onCapture.bind(this)); } // Did they provide their own context? if (game.config.context) { gl = game.config.context; } else { gl = canvas.getContext('webgl', config.contextCreation) || canvas.getContext('experimental-webgl', config.contextCreation); } if (!gl || gl.isContextLost()) { this.contextLost = true; throw new Error('WebGL unsupported'); } this.gl = gl; this.setExtensions(); this.setContextHandlers(); // Set it back into the Game, so developers can access it from there too game.context = gl; for (var i = 0; i <= 27; i++) { this.blendModes.push(WebGLBlendParametersFactory.createCombined(this)); } // ADD this.blendModes[1].func = [ gl.ONE, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA ]; // MULTIPLY this.blendModes[2].func = [ gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA ]; // SCREEN this.blendModes[3].func = [ gl.ONE, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR ]; // ERASE this.blendModes[17].equation = [ gl.FUNC_REVERSE_SUBTRACT, gl.FUNC_REVERSE_SUBTRACT ]; this.blendModes[17].func = [ gl.ZERO, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE_MINUS_SRC_ALPHA ]; this.glFormats = [ gl.BYTE, gl.SHORT, gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT, gl.FLOAT ]; this.shaderSetters = new WebGLShaderSetterWrapper(this); if (!config.maxTextures || config.maxTextures === -1) { config.maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); } if (!config.maxTextureSize) { config.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); } this.compression = this.getCompressedTextures(); // Setup initial WebGL state this.glWrapper = new WebGLGlobalWrapper(this); this.glWrapper.update(undefined, true); gl.clearColor(clearColor.redGL, clearColor.greenGL, clearColor.blueGL, clearColor.alphaGL); gl.clear(gl.COLOR_BUFFER_BIT); // Mipmaps var validMipMaps = [ 'NEAREST', 'LINEAR', 'NEAREST_MIPMAP_NEAREST', 'LINEAR_MIPMAP_NEAREST', 'NEAREST_MIPMAP_LINEAR', 'LINEAR_MIPMAP_LINEAR' ]; if (validMipMaps.indexOf(config.mipmapFilter) !== -1) { this.mipmapFilter = gl[config.mipmapFilter]; } // Check maximum supported textures this.maxTextures = Utils.checkShaderMax(gl, config.maxTextures); for (i = 0; i < this.maxTextures; i++) { this.textureUnitIndices.push(i); } this.glTextureUnits = new WebGLTextureUnitsWrapper(this); this.projectionMatrix = new Matrix4().identity(); game.textures.once(TextureEvents.READY, this.boot, this); return this; }, /** * Internal boot handler. * * @method Phaser.Renderer.WebGL.WebGLRenderer#boot * @private * @since 3.11.0 */ boot: function () { var game = this.game; var gl = this.gl; var baseSize = game.scale.baseSize; var width = baseSize.width; var height = baseSize.height; this.width = width; this.height = height; this.isBooted = true; // Provision the generic quad index buffer. // Ensure that there is no VAO bound, because the following index buffer // will modify any currently bound VAO. this.glWrapper.updateVAO({ vao: null }); this.genericQuadIndexBuffer = this.createIndexBuffer(new Uint16Array([ 0, 1, 2, 3 ]), gl.STATIC_DRAW); // Set up render nodes. this.renderNodes = new RenderNodeManager(this); this.cameraRenderNode = this.renderNodes.getNode('Camera'); // Set-up default textures, fbo and scissor this.blankTexture = game.textures.getFrame('__DEFAULT').glTexture; this.normalTexture = game.textures.getFrame('__NORMAL').glTexture; this.whiteTexture = game.textures.getFrame('__WHITE').glTexture; // Set up drawing contexts. this.baseDrawingContext = new DrawingContext(this, { autoClear: true, useCanvas: true, clearColor: [ this.config.backgroundColor.redGL, this.config.backgroundColor.greenGL, this.config.backgroundColor.blueGL, this.config.backgroundColor.alphaGL ] } ); this.on(Events.RESIZE, this.baseDrawingContext.resize, this.baseDrawingContext); game.scale.on(ScaleEvents.RESIZE, this.onResize, this); this.resize(width, height); }, /** * Queries the GL context to get the supported extensions. * * Then sets them into the `supportedExtensions`, `instancedArraysExtension` and `vaoExtension` properties. * * Called automatically during the `init` method. * * @method Phaser.Renderer.WebGL.WebGLRenderer#setExtensions * @since 3.85.2 */ setExtensions: function () { var gl = this.gl; var game = this.game; var exts = gl.getSupportedExtensions(); this.supportedExtensions = exts; var angleString = 'ANGLE_instanced_arrays'; this.instancedArraysExtension = (exts.indexOf(angleString) > -1) ? gl.getExtension(angleString) : null; if (game.config.skipUnreadyShaders) { var parallelShaderCompileString = 'KHR_parallel_shader_compile'; this.parallelShaderCompileExtension = (exts.indexOf(parallelShaderCompileString) > -1) ? gl.getExtension(parallelShaderCompileString) : null; if (!this.parallelShaderCompileExtension) { // Disable the option if the extension is not supported. game.config.skipUnreadyShaders = false; } } var vaoString = 'OES_vertex_array_object'; this.vaoExtension = (exts.indexOf(vaoString) > -1) ? gl.getExtension(vaoString) : null; var stdDerivativesString = 'OES_standard_derivatives'; this.standardDerivativesExtension = (exts.indexOf(stdDerivativesString) > -1) ? gl.getExtension(stdDerivativesString) : null; // Make WebGL2 core features which were extensions available on the WebGL1 context. // This allows us to use a WebGL2 context. if (gl instanceof WebGLRenderingContext) { // Incorporate instanced arrays. if (this.instancedArraysExtension) { gl.vertexAttribDivisor = this.instancedArraysExtension.vertexAttribDivisorANGLE.bind(this.instancedArraysExtension); gl.drawArraysInstanced = this.instancedArraysExtension.drawArraysInstancedANGLE.bind(this.instancedArraysExtension); gl.drawElementsInstanced = this.instancedArraysExtension.drawElementsInstancedANGLE.bind(this.instancedArraysExtension); } else { throw new Error('ANGLE_instanced_arrays extension not supported. Required for rendering.'); } // Incorporate vertex array objects. if (this.vaoExtension) { gl.createVertexArray = this.vaoExtension.createVertexArrayOES.bind(this.vaoExtension); gl.bindVertexArray = this.vaoExtension.bindVertexArrayOES.bind(this.vaoExtension); gl.deleteVertexArray = this.vaoExtension.deleteVertexArrayOES.bind(this.vaoExtension); gl.isVertexArray = this.vaoExtension.isVertexArrayOES.bind(this.vaoExtension); } else { throw new Error('OES_vertex_array_object extension not supported. Required for rendering.'); } // Incorporate standard derivatives. if (this.standardDerivativesExtension) { gl.FRAGMENT_SHADER_DERIVATIVE_HINT = this.standardDerivativesExtension.FRAGMENT_SHADER_DERIVATIVE_HINT_OES; } else if (game.config.smoothPixelArt) { throw new Error('OES_standard_derivatives extension not supported. Cannot use smoothPixelArt.'); } } }, /** * Sets the handlers that are called when WebGL context is lost or restored by the browser. * * The default handlers are referenced via the properties `WebGLRenderer.contextLostHandler` and `WebGLRenderer.contextRestoredHandler`. * By default, these map to the methods `WebGLRenderer.dispatchContextLost` and `WebGLRenderer.dispatchContextRestored`. * * You can override these handlers with your own via this method. * * If you do override them, make sure that your handlers invoke the methods `WebGLRenderer.dispatchContextLost` and `WebGLRenderer.dispatchContextRestored` in due course, otherwise the renderer will not be able to restore itself fully. * * @method Phaser.Renderer.WebGL.WebGLRenderer#setContextHandlers * @since 3.85.0 * * @param {function} [contextLost] - Custom handler for responding to the WebGL context lost event. Set as `undefined` to use the default handler. * @param {function} [contextRestored] - Custom handler for responding to the WebGL context restored event. Set as `undefined` to use the default handler. */ setContextHandlers: function (contextLost, contextRestored) { if (this.previousContextLostHandler) { this.canvas.removeEventListener('webglcontextlost', this.previousContextLostHandler, false); } if (this.previousContextRestoredHandler) { this.canvas.removeEventListener('webglcontextrestored', this.previousContextRestoredHandler, false); } if (typeof contextLost === 'function') { this.contextLostHandler = contextLost.bind(this); } else { this.contextLostHandler = this.dispatchContextLost.bind(this); } if (typeof contextRestored === 'function') { this.contextRestoredHandler = contextRestored.bind(this); } else { this.contextRestoredHandler = this.dispatchContextRestored.bind(this); } this.canvas.addEventListener('webglcontextlost', this.contextLostHandler, false); this.canvas.addEventListener('webglcontextrestored', this.contextRestoredHandler, false); this.previousContextLostHandler = this.contextLostHandler; this.previousContextRestoredHandler = this.contextRestoredHandler; }, /** * This method is called when the WebGL context is lost. By default this is bound to the property `WebGLRenderer.contextLostHandler`. * If you override the context loss handler via the `setContextHandlers` method then be sure to invoke this method in due course. * * @method Phaser.Renderer.WebGL.WebGLRenderer#dispatchContextLost * @since 3.85.0 * * @param {WebGLContextEvent } event - The WebGL context lost Event. */ dispatchContextLost: function (event) { this.contextLost = true; if (console) { console.warn('WebGL Context lost. Renderer disabled'); } this.emit(Events.LOSE_WEBGL, this); event.preventDefault(); }, /** * This method is called when the WebGL context is restored. By default this is bound to the property `WebGLRenderer.contextRestoredHandler`. * If you override the context restored handler via the `setContextHandlers` method then be sure to invoke this method in due course. * * @method Phaser.Renderer.WebGL.WebGLRenderer#dispatchContextRestored * @since 3.85.0 * * @param {WebGLContextEvent } event - The WebGL context restored Event. */ dispatchContextRestored: function (event) { var gl = this.gl; if (gl.isContextLost()) { if (console) { console.log('WebGL Context restored, but context is still lost'); } return; } // Restore GL extensions. this.setExtensions(); // Force update the GL state. this.glWrapper.update(WebGLGlobalParametersFactory.getDefault(this), true); this.glTextureUnits.init(); // Re-enable compressed texture formats. this.compression = this.getCompressedTextures(); // Restore wrapped GL objects. // Order matters, as some wrappers depend on others. var wrapperCreateResource = function (wrapper) { wrapper.createResource(); }; ArrayEach(this.glTextureWrappers, wrapperCreateResource); ArrayEach(this.glBufferWrappers, wrapperCreateResource); ArrayEach(this.glFramebufferWrappers, wrapperCreateResource); ArrayEach(this.glProgramWrappers, wrapperCreateResource); ArrayEach(this.glVAOWrappers, wrapperCreateResource); // Restore texture unit assignment. this.glTextureUnits.bindUnits(this.glTextureUnits.units, true); // Apply resize. this.resize(this.game.scale.baseSize.width, this.game.scale.baseSize.height); // Context has been restored. this.contextLost = false; if (console) { console.warn('WebGL Context restored. Renderer running again.'); } this.emit(Events.RESTORE_WEBGL, this); event.preventDefault(); }, /** * This method is only available in the Debug Build of Phaser, or a build with the * `WEBGL_DEBUG` flag set in the Webpack Config. * * Phaser v3.60 Debug has a build of Spector.js embedded in it, which is a WebGL inspector * that allows for live inspection of your WebGL calls. Although it's easy to add the Spector * extension to a desktop browser, by embedding it in Phaser we can make it available in mobile * browsers too, making it a powerful tool for debugging WebGL games on mobile devices where * extensions are not permitted. * * See https://github.com/BabylonJS/Spector.js for more details. * * This method will capture the current WebGL frame and send it to the Spector.js tool for inspection. * * @method Phaser.Renderer.WebGL.WebGLRenderer#captureFrame * @since 3.60.0 * * @param {boolean} [quickCapture=false] - If `true` thumbnails are not captured in order to speed up the capture. * @param {boolean} [fullCapture=false] - If `true` all details are captured. */ captureFrame: function (quickCapture, fullCapture) { if (quickCapture === undefined) { quickCapture = false; } if (fullCapture === undefined) { fullCapture = false; } if (DEBUG && this.spector && !this._debugCapture) { this.spector.captureCanvas(this.canvas, 0, quickCapture, fullCapture); this._debugCapture = true; } }, /** * This method is only available in the Debug Build of Phaser, or a build with the * `WEBGL_DEBUG` flag set in the Webpack Config. * * Phaser v3.60 Debug has a build of Spector.js embedded in it, which is a WebGL inspector * that allows for live inspection of your WebGL calls. Although it's easy to add the Spector * extension to a desktop browser, by embedding it in Phaser we can make it available in mobile * browsers too, making it a powerful tool for debugging WebGL games on mobile devices where * extensions are not permitted. * * See https://github.com/BabylonJS/Spector.js for more details. * * This method will capture the next WebGL frame and send it to the Spector.js tool for inspection. * * @method Phaser.Renderer.WebGL.WebGLRenderer#captureNextFrame * @since 3.60.0 */ captureNextFrame: function () { if (DEBUG && this.spector && !this._debugCapture) { this._debugCapture = true; this.spector.captureNextFrame(this.canvas); } }, /** * This method is only available in the Debug Build of Phaser, or a build with the * `WEBGL_DEBUG` flag set in the Webpack Config. * * Phaser v3.60 Debug has a build of Spector.js embedded in it, which is a WebGL inspector * that allows for live inspection of your WebGL calls. Although it's easy to add the Spector * extension to a desktop browser, by embedding it in Phaser we can make it available in mobile * browsers too, making it a powerful tool for debugging WebGL games on mobile devices where * extensions are not permitted. * * See https://github.com/BabylonJS/Spector.js for more details. * * This method will return the current FPS of the WebGL canvas. * * @method Phaser.Renderer.WebGL.WebGLRenderer#getFps * @since 3.60.0 * * @return {number} The current FPS of the WebGL canvas. */ getFps: function () { if (DEBUG && this.spector) { return this.spector.getFps(); } }, /** * This method is only available in the Debug Build of Phaser, or a build with the * `WEBGL_DEBUG` flag set in the Webpack Config. * * Phaser v3.60 Debug has a build of Spector.js embedded in it, which is a WebGL inspector * that allows for live inspection of your WebGL calls. Although it's easy to add the Spector * extension to a desktop browser, by embedding it in Phaser we can make it available in mobile * browsers too, making it a powerful tool for debugging WebGL games on mobile devices where * extensions are not permitted. * * See https://github.com/BabylonJS/Spector.js for more details. * * This method adds a command with the name value in the list. This can be filtered in the search. * All logs can be filtered searching for "LOG". * * @method Phaser.Renderer.WebGL.WebGLRenderer#log * @since 3.60.0 * * @param {...*} arguments - The arguments to log to Spector. * * @return {string} The current log. */ log: function () { if (DEBUG && this.spector) { var t = Array.prototype.slice.call(arguments).join(' '); return this.spector.log(t); } }, /** * This method is only available in the Debug Build of Phaser, or a build with the * `WEBGL_DEBUG` flag set in the Webpack Config. * * Phaser v3.60 Debug has a build of Spector.js embedded in it, which is a WebGL inspector * that allows for live inspection of your WebGL calls. Although it's easy to add the Spector * extension to a desktop browser, by embedding it in Phaser we can make it available in mobile * browsers too, making it a powerful tool for debugging WebGL games on mobile devices where * extensions are not permitted. * * See https://github.com/BabylonJS/Spector.js for more details. * * This method will start a capture on the Phaser canvas. The capture will stop once it reaches * the number of commands specified as a parameter, or after 10 seconds. If quick capture is true, * the thumbnails are not captured in order to speed up the capture. * * @method Phaser.Renderer.WebGL.WebGLRenderer#startCapture * @since 3.60.0 * * @param {number} [commandCount=0] - The number of commands to capture. If zero it will capture for 10 seconds. * @param {boolean} [quickCapture=false] - If `true` thumbnails are not captured in order to speed up the capture. * @param {boolean} [fullCapture=false] - If `true` all details are captured. */ startCapture: function (commandCount, quickCapture, fullCapture) { if (commandCount === undefined) { commandCount = 0; } if (quickCapture === undefined) { quickCapture = false; } if (fullCapture === undefined) { fullCapture = false; } if (DEBUG && this.spector && !this._debugCapture) { this.spector.startCapture(this.canvas, commandCount, quickCapture, fullCapture); this._debugCapture = true; } }, /** * This method is only available in the Debug Build of Phaser, or a build with the * `WEBGL_DEBUG` flag set in the Webpack Config. * * Phaser v3.60 Debug has a build of Spector.js embedded in it, which is a WebGL inspector * that allows for live inspection of your WebGL calls. Although it's easy to add the Spector * extension to a desktop browser, by embedding it in Phaser we can make it available in mobile * browsers too, making it a powerful tool for debugging WebGL games on mobile devices where * extensions are not permitted. * * See https://github.com/BabylonJS/Spector.js for more details. * * This method will stop the current capture and returns the result in JSON. It displays the * result if the UI has been displayed. This returns undefined if the capture has not been completed * or did not find any commands. * * @method Phaser.Renderer.WebGL.WebGLRenderer#stopCapture * @since 3.60.0 * * @return {object} The current capture. */ stopCapture: function () { if (DEBUG && this.spector && this._debugCapture) { return this.spector.stopCapture(); } }, /** * This method is only available in the Debug Build of Phaser, or a build with the * `WEBGL_DEBUG` flag set in the Webpack Config. * * Internal onCapture handler. * * @method Phaser.Renderer.WebGL.WebGLRenderer#onCapture * @private * @since 3.60.0 * * @param {object} capture - The capture data. */ onCapture: function (capture) { if (DEBUG) { var view = this.spector.getResultUI(); view.display(capture); this._debugCapture = false; } }, /** * The event handler that manages the `resize` event dispatched by the Scale Manager. * * @method Phaser.Renderer.WebGL.WebGLRenderer#onResize * @since 3.16.0 * * @param {Phaser.Structs.Size} gameSize - The default Game Size object. This is the un-modified game dimensions. * @param {Phaser.Structs.Size} baseSize - The base Size object. The game dimensions. The canvas width / height values match this. */ onResize: function (gameSize, baseSize) { // Has the underlying canvas size changed? if (baseSize.width !== this.width || baseSize.height !== this.height) { this.resize(baseSize.width, baseSize.height); } }, /** * Resizes the drawing buffer to match that required by the Scale Manager. * * @method Phaser.Renderer.WebGL.WebGLRenderer#resize * @fires Phaser.Renderer.Events#RESIZE * @since 3.0.0 * * @param {number} [width] - The new width of the renderer. * @param {number} [height] - The new height of the renderer. * * @return {this} This WebGLRenderer instance. */ resize: function (width, height) { var gl = this.gl; this.width = width; this.height = height; this.setProjectionMatrix(width, height); this.drawingBufferHeight = gl.drawingBufferHeight; this.glWrapper.update({ scissor: { box: [ 0, (gl.drawingBufferHeight - height), width, height ] }, viewport: [ 0, 0, width, height ] }); this.emit(Events.RESIZE, width, height); return this; }, /** * Determines which compressed texture formats this browser and device supports. * * Called automatically as part of the WebGL Renderer init process. If you need to investigate * which formats it supports, see the `Phaser.Renderer.WebGL.WebGLRenderer#compression` property instead. * * @method Phaser.Renderer.WebGL.WebGLRenderer#getCompressedTextures * @since 3.60.0 * * @return {Phaser.Types.Renderer.WebGL.WebGLTextureCompression} The compression object. */ getCompressedTextures: function () { var extString = 'WEBGL_compressed_texture_'; var wkExtString = 'WEBKIT_' + extString; var extEXTString = 'EXT_texture_compression_'; var hasExt = function (gl, format) { var results = gl.getExtension(extString + format) || gl.getExtension(wkExtString + format) || gl.getExtension(extEXTString + format); if (results) { var glEnums = {}; for (var key in results) { glEnums[results[key]] = key; } return glEnums; } }; var gl = this.gl; return { ETC: hasExt(gl, 'etc'), ETC1: hasExt(gl, 'etc1'), ATC: hasExt(gl, 'atc'), ASTC: hasExt(gl, 'astc'), BPTC: hasExt(gl, 'bptc'), RGTC: hasExt(gl, 'rgtc'), PVRTC: hasExt(gl, 'pvrtc'), S3TC: hasExt(gl, 's3tc'), S3TCSRGB: hasExt(gl, 's3tc_srgb'), IMG: true }; }, /** * Returns a compressed texture format GLenum name based on the given format. * * @method Phaser.Renderer.WebGL.WebGLRenderer#getCompressedTextureName * @since 3.60.0 * * @param {string} baseFormat - The Base Format to check. * @param {GLenum} [format] - An optional GLenum format to check within the base format. * * @return {