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
JavaScript
/**
* @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 {