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,541 lines (1,329 loc) 93 kB
/** * @author Richard Davey <rich@phaser.io> * @copyright 2013-2025 Phaser Studio Inc. * @license {@link https://opensource.org/licenses/MIT|MIT License} */ var Class = require('../../utils/Class'); var DeepCopy = require('../../utils/object/DeepCopy'); var EventEmitter = require('eventemitter3'); var Events = require('./pipelines/events'); var GetFastValue = require('../../utils/object/GetFastValue'); var Matrix4 = require('../../math/Matrix4'); var RendererEvents = require('../events'); var RenderTarget = require('./RenderTarget'); var Utils = require('./Utils'); var WebGLShader = require('./WebGLShader'); /** * @classdesc * The `WebGLPipeline` is a base class used by all of the core Phaser pipelines. * * It describes the way elements will be rendered in WebGL. Internally, it handles * compiling the shaders, creating vertex buffers, assigning primitive topology and * binding vertex attributes, all based on the given configuration data. * * The pipeline is configured by passing in a `WebGLPipelineConfig` object. Please * see the documentation for this type to fully understand the configuration options * available to you. * * Usually, you would not extend from this class directly, but would instead extend * from one of the core pipelines, such as the Multi Pipeline. * * The pipeline flow per render-step is as follows: * * 1) onPreRender - called once at the start of the render step * 2) onRender - call for each Scene Camera that needs to render (so can be multiple times per render step) * 3) Internal flow: * 3a) bind (only called if a Game Object is using this pipeline and it's not currently active) * 3b) onBind (called for every Game Object that uses this pipeline) * 3c) flush (can be called by a Game Object, internal method or from outside by changing pipeline) * 4) onPostRender - called once at the end of the render step * * @class WebGLPipeline * @extends Phaser.Events.EventEmitter * @memberof Phaser.Renderer.WebGL * @constructor * @since 3.0.0 * * @param {Phaser.Types.Renderer.WebGL.WebGLPipelineConfig} config - The configuration object for this WebGL Pipeline. */ var WebGLPipeline = new Class({ Extends: EventEmitter, initialize: function WebGLPipeline (config) { EventEmitter.call(this); var game = config.game; var renderer = game.renderer; var gl = renderer.gl; /** * Name of the pipeline. Used for identification and setting from Game Objects. * * @name Phaser.Renderer.WebGL.WebGLPipeline#name * @type {string} * @since 3.0.0 */ this.name = GetFastValue(config, 'name', 'WebGLPipeline'); /** * The Phaser Game instance to which this pipeline is bound. * * @name Phaser.Renderer.WebGL.WebGLPipeline#game * @type {Phaser.Game} * @since 3.0.0 */ this.game = game; /** * The WebGL Renderer instance to which this pipeline is bound. * * @name Phaser.Renderer.WebGL.WebGLPipeline#renderer * @type {Phaser.Renderer.WebGL.WebGLRenderer} * @since 3.0.0 */ this.renderer = renderer; /** * A reference to the WebGL Pipeline Manager. * * This is initially undefined and only set when this pipeline is added * to the manager. * * @name Phaser.Renderer.WebGL.WebGLPipeline#manager * @type {?Phaser.Renderer.WebGL.PipelineManager} * @since 3.50.0 */ this.manager; /** * The WebGL context this WebGL Pipeline uses. * * @name Phaser.Renderer.WebGL.WebGLPipeline#gl * @type {WebGLRenderingContext} * @since 3.0.0 */ this.gl = gl; /** * The canvas which this WebGL Pipeline renders to. * * @name Phaser.Renderer.WebGL.WebGLPipeline#view * @type {HTMLCanvasElement} * @since 3.0.0 */ this.view = game.canvas; /** * Width of the current viewport. * * @name Phaser.Renderer.WebGL.WebGLPipeline#width * @type {number} * @since 3.0.0 */ this.width = 0; /** * Height of the current viewport. * * @name Phaser.Renderer.WebGL.WebGLPipeline#height * @type {number} * @since 3.0.0 */ this.height = 0; /** * The current number of vertices that have been added to the pipeline batch. * * @name Phaser.Renderer.WebGL.WebGLPipeline#vertexCount * @type {number} * @default 0 * @since 3.0.0 */ this.vertexCount = 0; /** * The total number of vertices that this pipeline batch can hold before it will flush. * * This defaults to `renderer batchSize * 6`, where `batchSize` is defined in the Renderer Game Config. * * @name Phaser.Renderer.WebGL.WebGLPipeline#vertexCapacity * @type {number} * @since 3.0.0 */ this.vertexCapacity = 0; /** * Raw byte buffer of vertices. * * Either set via the config object `vertices` property, or generates a new Array Buffer of * size `vertexCapacity * vertexSize`. * * @name Phaser.Renderer.WebGL.WebGLPipeline#vertexData * @type {ArrayBuffer} * @readonly * @since 3.0.0 */ this.vertexData; /** * The WebGLBuffer that holds the vertex data. * * Created from the `vertexData` ArrayBuffer. If `vertices` are set in the config, a `STATIC_DRAW` buffer * is created. If not, a `DYNAMIC_DRAW` buffer is created. * * @name Phaser.Renderer.WebGL.WebGLPipeline#vertexBuffer * @type {Phaser.Renderer.WebGL.Wrappers.WebGLBufferWrapper} * @readonly * @since 3.0.0 */ this.vertexBuffer; /** * The currently active WebGLBuffer. * * @name Phaser.Renderer.WebGL.WebGLPipeline#activeBuffer * @type {Phaser.Renderer.WebGL.Wrappers.WebGLBufferWrapper} * @since 3.60.0 */ this.activeBuffer; /** * The primitive topology which the pipeline will use to submit draw calls. * * Defaults to GL_TRIANGLES if not otherwise set in the config. * * @name Phaser.Renderer.WebGL.WebGLPipeline#topology * @type {GLenum} * @since 3.0.0 */ this.topology = GetFastValue(config, 'topology', gl.TRIANGLES); /** * Uint8 view to the `vertexData` ArrayBuffer. Used for uploading vertex buffer resources to the GPU. * * @name Phaser.Renderer.WebGL.WebGLPipeline#bytes * @type {Uint8Array} * @since 3.0.0 */ this.bytes; /** * Float32 view of the array buffer containing the pipeline's vertices. * * @name Phaser.Renderer.WebGL.WebGLPipeline#vertexViewF32 * @type {Float32Array} * @since 3.0.0 */ this.vertexViewF32; /** * Uint32 view of the array buffer containing the pipeline's vertices. * * @name Phaser.Renderer.WebGL.WebGLPipeline#vertexViewU32 * @type {Uint32Array} * @since 3.0.0 */ this.vertexViewU32; /** * Indicates if the current pipeline is active, or not. * * Toggle this property to enable or disable a pipeline from rendering anything. * * @name Phaser.Renderer.WebGL.WebGLPipeline#active * @type {boolean} * @since 3.10.0 */ this.active = true; /** * Some pipelines require the forced use of texture zero (like the light pipeline). * * This property should be set when that is the case. * * @name Phaser.Renderer.WebGL.WebGLPipeline#forceZero * @type {boolean} * @since 3.50.0 */ this.forceZero = GetFastValue(config, 'forceZero', false); /** * Indicates if this pipeline has booted or not. * * A pipeline boots only when the Game instance itself, and all associated systems, is * fully ready. * * @name Phaser.Renderer.WebGL.WebGLPipeline#hasBooted * @type {boolean} * @readonly * @since 3.50.0 */ this.hasBooted = false; /** * Indicates if this is a Post FX Pipeline, or not. * * @name Phaser.Renderer.WebGL.WebGLPipeline#isPostFX * @type {boolean} * @readonly * @since 3.50.0 */ this.isPostFX = false; /** * Indicates if this is a Pre FX Pipeline, or not. * * @name Phaser.Renderer.WebGL.WebGLPipeline#isPreFX * @type {boolean} * @readonly * @since 3.60.0 */ this.isPreFX = false; /** * An array of RenderTarget instances that belong to this pipeline. * * @name Phaser.Renderer.WebGL.WebGLPipeline#renderTargets * @type {Phaser.Renderer.WebGL.RenderTarget[]} * @since 3.50.0 */ this.renderTargets = []; /** * A reference to the currently bound Render Target instance from the `WebGLPipeline.renderTargets` array. * * @name Phaser.Renderer.WebGL.WebGLPipeline#currentRenderTarget * @type {Phaser.Renderer.WebGL.RenderTarget} * @since 3.50.0 */ this.currentRenderTarget; /** * An array of all the WebGLShader instances that belong to this pipeline. * * Shaders manage their own attributes and uniforms, but share the same vertex data buffer, * which belongs to this pipeline. * * Shaders are set in a call to the `setShadersFromConfig` method, which happens automatically, * but can also be called at any point in your game. See the method documentation for details. * * @name Phaser.Renderer.WebGL.WebGLPipeline#shaders * @type {Phaser.Renderer.WebGL.WebGLShader[]} * @since 3.50.0 */ this.shaders = []; /** * A reference to the currently bound WebGLShader instance from the `WebGLPipeline.shaders` array. * * For lots of pipelines, this is the only shader, so it is a quick way to reference it without * an array look-up. * * @name Phaser.Renderer.WebGL.WebGLPipeline#currentShader * @type {Phaser.Renderer.WebGL.WebGLShader} * @since 3.50.0 */ this.currentShader; /** * The Projection matrix, used by shaders as 'uProjectionMatrix' uniform. * * @name Phaser.Renderer.WebGL.WebGLPipeline#projectionMatrix * @type {Phaser.Math.Matrix4} * @since 3.50.0 */ this.projectionMatrix; /** * The cached width of the Projection matrix. * * @name Phaser.Renderer.WebGL.WebGLPipeline#projectionWidth * @type {number} * @since 3.50.0 */ this.projectionWidth = 0; /** * The cached height of the Projection matrix. * * @name Phaser.Renderer.WebGL.WebGLPipeline#projectionHeight * @type {number} * @since 3.50.0 */ this.projectionHeight = 0; /** * The configuration object that was used to create this pipeline. * * Treat this object as 'read only', because changing it post-creation will not * impact this pipeline in any way. However, it is used internally for cloning * and post-boot set-up. * * @name Phaser.Renderer.WebGL.WebGLPipeline#config * @type {Phaser.Types.Renderer.WebGL.WebGLPipelineConfig} * @since 3.50.0 */ this.config = config; /** * Has the GL Context been reset to the Phaser defaults since the last time * this pipeline was bound? This is set automatically when the Pipeline Manager * resets itself, usually after handing off to a 3rd party renderer like Spine. * * You should treat this property as read-only. * * @name Phaser.Renderer.WebGL.WebGLPipeline#glReset * @type {boolean} * @since 3.53.0 */ this.glReset = false; /** * The temporary Pipeline batch. This array contains the batch entries for * the current frame, which is a package of textures and vertex offsets used * for drawing. This package is built dynamically as the frame is built * and cleared during the flush method. * * Treat this array and all of its contents as read-only. * * @name Phaser.Renderer.WebGL.WebGLPipeline#batch * @type {Phaser.Types.Renderer.WebGL.WebGLPipelineBatchEntry[]} * @since 3.60.0 */ this.batch = []; /** * The most recently created Pipeline batch entry. * * Reset to null as part of the flush method. * * Treat this value as read-only. * * @name Phaser.Renderer.WebGL.WebGLPipeline#currentBatch * @type {?Phaser.Types.Renderer.WebGL.WebGLPipelineBatchEntry} * @since 3.60.0 */ this.currentBatch = null; /** * The most recently bound texture, used as part of the batch process. * * Reset to null as part of the flush method. * * Treat this value as read-only. * * @name Phaser.Renderer.WebGL.WebGLPipeline#currentTexture * @type {?Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper} * @since 3.60.0 */ this.currentTexture = null; /** * Holds the most recently assigned texture unit. * * Treat this value as read-only. * * @name Phaser.Renderer.WebGL.WebGLPipeline#currentUnit * @type {number} * @since 3.50.0 */ this.currentUnit = 0; /** * The currently active WebGLTextures, used as part of the batch process. * * Reset to empty as part of the bind method. * * Treat this array as read-only. * * @name Phaser.Renderer.WebGL.WebGLPipeline#activeTextures * @type {Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper[]} * @since 3.60.0 */ this.activeTextures = []; /** * If the WebGL Renderer changes size, this uniform will be set with the new width and height values * as part of the pipeline resize method. Various built-in pipelines, such as the MultiPipeline, set * this property automatically to `uResolution`. * * @name Phaser.Renderer.WebGL.WebGLPipeline#resizeUniform * @type {string} * @since 3.80.0 */ this.resizeUniform = GetFastValue(config, 'resizeUniform', ''); }, /** * Called when the Game has fully booted and the Renderer has finished setting up. * * By this stage all Game level systems are now in place. You can perform any final tasks that the * pipeline may need, that relies on game systems such as the Texture Manager being ready. * * @method Phaser.Renderer.WebGL.WebGLPipeline#boot * @fires Phaser.Renderer.WebGL.Pipelines.Events#BOOT * @since 3.11.0 */ boot: function () { var i; var gl = this.gl; var config = this.config; var renderer = this.renderer; if (!this.isPostFX) { this.projectionMatrix = new Matrix4().identity(); } // Create the Render Targets var renderTargets = this.renderTargets; var targets = GetFastValue(config, 'renderTarget', false); // If boolean, set to number = 1 if (typeof(targets) === 'boolean' && targets) { targets = 1; } var width = renderer.width; var height = renderer.height; if (typeof(targets) === 'number') { // Create this many default RTs for (i = 0; i < targets; i++) { renderTargets.push(new RenderTarget(renderer, width, height, 1, 0, true)); } } else if (Array.isArray(targets)) { for (i = 0; i < targets.length; i++) { var scale = GetFastValue(targets[i], 'scale', 1); var minFilter = GetFastValue(targets[i], 'minFilter', 0); var autoClear = GetFastValue(targets[i], 'autoClear', 1); var autoResize = GetFastValue(targets[i], 'autoResize', false); var targetWidth = GetFastValue(targets[i], 'width', null); var targetHeight = GetFastValue(targets[i], 'height', targetWidth); if (targetWidth) { renderTargets.push(new RenderTarget(renderer, targetWidth, targetHeight, 1, minFilter, autoClear, autoResize)); } else { renderTargets.push(new RenderTarget(renderer, width, height, scale, minFilter, autoClear, autoResize)); } } } if (renderTargets.length) { // Default to the first one in the array this.currentRenderTarget = renderTargets[0]; } // Create the Shaders this.setShadersFromConfig(config); // Which shader has the largest vertex size? var shaders = this.shaders; var vertexSize = 0; for (i = 0; i < shaders.length; i++) { if (shaders[i].vertexSize > vertexSize) { vertexSize = shaders[i].vertexSize; } } var batchSize = GetFastValue(config, 'batchSize', renderer.config.batchSize); // * 6 because there are 6 vertices in a quad and 'batchSize' represents the quantity of quads in the batch this.vertexCapacity = batchSize * 6; var data = new ArrayBuffer(this.vertexCapacity * vertexSize); this.vertexData = data; this.bytes = new Uint8Array(data); this.vertexViewF32 = new Float32Array(data); this.vertexViewU32 = new Uint32Array(data); var configVerts = GetFastValue(config, 'vertices', null); if (configVerts) { this.vertexViewF32.set(configVerts); this.vertexBuffer = renderer.createVertexBuffer(data, gl.STATIC_DRAW); } else { this.vertexBuffer = renderer.createVertexBuffer(data.byteLength, gl.DYNAMIC_DRAW); } // Set-up shaders this.setVertexBuffer(); for (i = shaders.length - 1; i >= 0; i--) { shaders[i].rebind(); } this.hasBooted = true; renderer.on(RendererEvents.RESIZE, this.resize, this); renderer.on(RendererEvents.PRE_RENDER, this.onPreRender, this); renderer.on(RendererEvents.RENDER, this.onRender, this); renderer.on(RendererEvents.POST_RENDER, this.onPostRender, this); this.emit(Events.BOOT, this); this.onBoot(); }, /** * This method is called once when this pipeline has finished being set-up * at the end of the boot process. By the time this method is called, all * of the shaders are ready and configured. * * @method Phaser.Renderer.WebGL.WebGLPipeline#onBoot * @since 3.50.0 */ onBoot: function () { }, /** * This method is called once when this pipeline has finished being set-up * at the end of the boot process. By the time this method is called, all * of the shaders are ready and configured. It's also called if the renderer * changes size. * * @method Phaser.Renderer.WebGL.WebGLPipeline#onResize * @since 3.50.0 * * @param {number} width - The new width of this WebGL Pipeline. * @param {number} height - The new height of this WebGL Pipeline. */ onResize: function () { }, /** * Sets the currently active shader within this pipeline. * * @method Phaser.Renderer.WebGL.WebGLPipeline#setShader * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.WebGLShader} shader - The shader to set as being current. * @param {boolean} [setAttributes=false] - Should the vertex attribute pointers be set? * @param {Phaser.Renderer.WebGL.Wrappers.WebGLBufferWrapper} [vertexBuffer] - The vertex buffer to be set before the shader is bound. Defaults to the one owned by this pipeline. * * @return {this} This WebGLPipeline instance. */ setShader: function (shader, setAttributes, vertexBuffer) { var renderer = this.renderer; if (shader !== this.currentShader || renderer.currentProgram !== this.currentShader.program) { this.flush(); var wasBound = this.setVertexBuffer(vertexBuffer); if (wasBound && !setAttributes) { setAttributes = true; } shader.bind(setAttributes, false); this.currentShader = shader; } return this; }, /** * Searches all shaders in this pipeline for one matching the given name, then returns it. * * @method Phaser.Renderer.WebGL.WebGLPipeline#getShaderByName * @since 3.50.0 * * @param {string} name - The index of the shader to set. * * @return {Phaser.Renderer.WebGL.WebGLShader} The WebGLShader instance, if found. */ getShaderByName: function (name) { var shaders = this.shaders; for (var i = 0; i < shaders.length; i++) { if (shaders[i].name === name) { return shaders[i]; } } }, /** * Destroys all shaders currently set in the `WebGLPipeline.shaders` array and then parses the given * `config` object, extracting the shaders from it, creating `WebGLShader` instances and finally * setting them into the `shaders` array of this pipeline. * * This is a destructive process. Be very careful when you call it, should you need to. * * @method Phaser.Renderer.WebGL.WebGLPipeline#setShadersFromConfig * @since 3.50.0 * * @param {Phaser.Types.Renderer.WebGL.WebGLPipelineConfig} config - The configuration object for this WebGL Pipeline. * * @return {this} This WebGLPipeline instance. */ setShadersFromConfig: function (config) { var i; var shaders = this.shaders; var renderer = this.renderer; for (i = 0; i < shaders.length; i++) { shaders[i].destroy(); } var vName = 'vertShader'; var fName = 'fragShader'; var aName = 'attributes'; var defaultVertShader = GetFastValue(config, vName, null); var defaultFragShader = Utils.parseFragmentShaderMaxTextures(GetFastValue(config, fName, null), renderer.maxTextures); var defaultAttribs = GetFastValue(config, aName, null); var configShaders = GetFastValue(config, 'shaders', []); var len = configShaders.length; if (len === 0) { if (defaultVertShader && defaultFragShader) { this.shaders = [ new WebGLShader(this, 'default', defaultVertShader, defaultFragShader, DeepCopy(defaultAttribs)) ]; } } else { var newShaders = []; for (i = 0; i < len; i++) { var shaderEntry = configShaders[i]; var name; var vertShader; var fragShader; var attributes; if (typeof shaderEntry === 'string') { name = 'default'; vertShader = defaultVertShader; fragShader = Utils.parseFragmentShaderMaxTextures(shaderEntry, renderer.maxTextures); attributes = defaultAttribs; } else { name = GetFastValue(shaderEntry, 'name', 'default'); vertShader = GetFastValue(shaderEntry, vName, defaultVertShader); fragShader = Utils.parseFragmentShaderMaxTextures(GetFastValue(shaderEntry, fName, defaultFragShader), renderer.maxTextures); attributes = GetFastValue(shaderEntry, aName, defaultAttribs); } if (name === 'default') { var lines = fragShader.split('\n'); var test = lines[0].trim(); if (test.indexOf('#define SHADER_NAME') > -1) { name = test.substring(20); } } if (vertShader && fragShader) { newShaders.push(new WebGLShader(this, name, vertShader, fragShader, DeepCopy(attributes))); } } this.shaders = newShaders; } if (this.shaders.length === 0) { console.warn('Pipeline: ' + this.name + ' - Invalid shader config'); } else { this.currentShader = this.shaders[0]; } return this; }, /** * Creates a new WebGL Pipeline Batch Entry, sets the texture unit as zero * and pushes the entry into the batch. * * @method Phaser.Renderer.WebGL.WebGLPipeline#createBatch * @since 3.60.0 * * @param {Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper} texture - The texture assigned to this batch entry. * * @return {number} The texture unit that was bound. */ createBatch: function (texture) { this.currentBatch = { start: this.vertexCount, count: 0, texture: [ texture ], unit: 0, maxUnit: 0 }; this.currentUnit = 0; this.currentTexture = texture; this.batch.push(this.currentBatch); return 0; }, /** * Adds the given texture to the current WebGL Pipeline Batch Entry and * increases the batch entry unit and maxUnit values by 1. * * @method Phaser.Renderer.WebGL.WebGLPipeline#addTextureToBatch * @since 3.60.0 * * @param {Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper} texture - The texture assigned to this batch entry. */ addTextureToBatch: function (texture) { var batch = this.currentBatch; if (batch) { batch.texture.push(texture); batch.unit++; batch.maxUnit++; } }, /** * Takes the given WebGLTextureWrapper and determines what to do with it. * * If there is no current batch (i.e. after a flush) it will create a new * batch from it. * * If the texture is already bound, it will return the current texture unit. * * If the texture already exists in the current batch, the unit gets reset * to match it. * * If the texture cannot be found in the current batch, and it supports * multiple textures, it's added into the batch and the unit indexes are * advanced. * * @method Phaser.Renderer.WebGL.WebGLPipeline#pushBatch * @since 3.60.0 * * @param {Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper} texture - The texture assigned to this batch entry. * * @return {number} The texture unit that was bound. */ pushBatch: function (texture) { // No current batch? Create one and return if (!this.currentBatch || (this.forceZero && texture !== this.currentTexture)) { return this.createBatch(texture); } // Otherwise, check if the texture is in the current batch if (texture === this.currentTexture) { return this.currentUnit; } else { var current = this.currentBatch; var idx = current.texture.indexOf(texture); if (idx === -1) { // This is a brand new texture, not in the current batch // Have we exceed our limit? if (current.texture.length === this.renderer.maxTextures) { return this.createBatch(texture); } else { // We're good, push it in current.unit++; current.maxUnit++; current.texture.push(texture); this.currentUnit = current.unit; this.currentTexture = texture; return current.unit; } } else { this.currentUnit = idx; this.currentTexture = texture; return idx; } } }, /** * Custom pipelines can use this method in order to perform any required pre-batch tasks * for the given Game Object. It must return the texture unit the Game Object was assigned. * * @method Phaser.Renderer.WebGL.WebGLPipeline#setGameObject * @since 3.50.0 * * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object being rendered or added to the batch. * @param {Phaser.Textures.Frame} [frame] - Optional frame to use. Can override that of the Game Object. * * @return {number} The texture unit the Game Object has been assigned. */ setGameObject: function (gameObject, frame) { if (frame === undefined) { frame = gameObject.frame; } return this.pushBatch(frame.source.glTexture); }, /** * Check if the current batch of vertices is full. * * You can optionally provide an `amount` parameter. If given, it will check if the batch * needs to flush _if_ the `amount` is added to it. This allows you to test if you should * flush before populating the batch. * * @method Phaser.Renderer.WebGL.WebGLPipeline#shouldFlush * @since 3.0.0 * * @param {number} [amount=0] - Will the batch need to flush if this many vertices are added to it? * * @return {boolean} `true` if the current batch should be flushed, otherwise `false`. */ shouldFlush: function (amount) { if (amount === undefined) { amount = 0; } return (this.vertexCount + amount > this.vertexCapacity); }, /** * Returns the number of vertices that can be added to the current batch before * it will trigger a flush to happen. * * @method Phaser.Renderer.WebGL.WebGLPipeline#vertexAvailable * @since 3.60.0 * * @return {number} The number of vertices that can still be added to the current batch before it will flush. */ vertexAvailable: function () { return this.vertexCapacity - this.vertexCount; }, /** * Resizes the properties used to describe the viewport. * * This method is called automatically by the renderer during its resize handler. * * @method Phaser.Renderer.WebGL.WebGLPipeline#resize * @fires Phaser.Renderer.WebGL.Pipelines.Events#RESIZE * @since 3.0.0 * * @param {number} width - The new width of this WebGL Pipeline. * @param {number} height - The new height of this WebGL Pipeline. * * @return {this} This WebGLPipeline instance. */ resize: function (width, height) { if (width !== this.width || height !== this.height) { this.flush(); } this.width = width; this.height = height; var targets = this.renderTargets; for (var i = 0; i < targets.length; i++) { targets[i].resize(width, height); } this.setProjectionMatrix(width, height); if (this.resizeUniform) { this.set2f(this.resizeUniform, width, height); } this.emit(Events.RESIZE, width, height, this); this.onResize(width, height); return this; }, /** * Adjusts this pipelines ortho Projection Matrix to use the given dimensions * and resets the `uProjectionMatrix` uniform on all bound shaders. * * This method is called automatically by the renderer during its resize handler. * * @method Phaser.Renderer.WebGL.WebGLPipeline#setProjectionMatrix * @since 3.50.0 * * @param {number} width - The new width of this WebGL Pipeline. * @param {number} height - The new height of this WebGL Pipeline. * * @return {this} This WebGLPipeline instance. */ setProjectionMatrix: function (width, height) { var projectionMatrix = this.projectionMatrix; // Because not all pipelines have them if (!projectionMatrix) { return this; } this.projectionWidth = width; this.projectionHeight = height; projectionMatrix.ortho(0, width, height, 0, -1000, 1000); var shaders = this.shaders; var name = 'uProjectionMatrix'; for (var i = 0; i < shaders.length; i++) { var shader = shaders[i]; if (shader.hasUniform(name)) { shader.resetUniform(name); shader.setMatrix4fv(name, false, projectionMatrix.val, shader); } } return this; }, /** * Adjusts this pipelines ortho Projection Matrix to flip the y * and bottom values. Call with 'false' as the parameter to flip * them back again. * * @method Phaser.Renderer.WebGL.WebGLPipeline#flipProjectionMatrix * @since 3.60.0 * * @param {boolean} [flipY=true] - Flip the y and bottom values? */ flipProjectionMatrix: function (flipY) { if (flipY === undefined) { flipY = true; } var projectionMatrix = this.projectionMatrix; // Because not all pipelines have them if (!projectionMatrix) { return this; } var width = this.projectionWidth; var height = this.projectionHeight; if (flipY) { projectionMatrix.ortho(0, width, 0, height, -1000, 1000); } else { projectionMatrix.ortho(0, width, height, 0, -1000, 1000); } this.setMatrix4fv('uProjectionMatrix', false, projectionMatrix.val); }, /** * Adjusts this pipelines ortho Projection Matrix to match that of the global * WebGL Renderer Projection Matrix. * * This method is called automatically by the Pipeline Manager when this * pipeline is set. * * @method Phaser.Renderer.WebGL.WebGLPipeline#updateProjectionMatrix * @since 3.50.0 */ updateProjectionMatrix: function () { if (this.projectionMatrix) { var globalWidth = this.renderer.projectionWidth; var globalHeight = this.renderer.projectionHeight; if (this.projectionWidth !== globalWidth || this.projectionHeight !== globalHeight) { this.setProjectionMatrix(globalWidth, globalHeight); } } }, /** * This method is called every time the Pipeline Manager makes this pipeline the currently active one. * * It binds the resources and shader needed for this pipeline, including setting the vertex buffer * and attribute pointers. * * @method Phaser.Renderer.WebGL.WebGLPipeline#bind * @fires Phaser.Renderer.WebGL.Pipelines.Events#BIND * @since 3.0.0 * * @param {Phaser.Renderer.WebGL.WebGLShader} [currentShader] - The shader to set as being current. * * @return {this} This WebGLPipeline instance. */ bind: function (currentShader) { if (currentShader === undefined) { currentShader = this.currentShader; } if (this.glReset) { return this.rebind(currentShader); } var wasBound = false; var gl = this.gl; if (gl.getParameter(gl.ARRAY_BUFFER_BINDING) !== this.vertexBuffer) { gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer.webGLBuffer); this.activeBuffer = this.vertexBuffer; wasBound = true; } currentShader.bind(wasBound); this.currentShader = currentShader; this.activeTextures.length = 0; this.emit(Events.BIND, this, currentShader); this.onActive(currentShader); return this; }, /** * This method is called every time the Pipeline Manager rebinds this pipeline. * * It resets all shaders this pipeline uses, setting their attributes again. * * @method Phaser.Renderer.WebGL.WebGLPipeline#rebind * @fires Phaser.Renderer.WebGL.Pipelines.Events#REBIND * @since 3.0.0 * * @param {Phaser.Renderer.WebGL.WebGLShader} [currentShader] - The shader to set as being current. * * @return {this} This WebGLPipeline instance. */ rebind: function (currentShader) { this.activeBuffer = null; this.setVertexBuffer(); var shaders = this.shaders; // Loop in reverse, so the first shader in the array is left as being bound for (var i = shaders.length - 1; i >= 0; i--) { var shader = shaders[i].rebind(); if (!currentShader || shader === currentShader) { this.currentShader = shader; } } this.activeTextures.length = 0; this.emit(Events.REBIND, this.currentShader); this.onActive(this.currentShader); this.onRebind(); this.glReset = false; return this; }, /** * This method is called if the WebGL context is lost and restored. * It ensures that uniforms are synced back to the GPU * for all shaders in this pipeline. * * @method Phaser.Renderer.WebGL.WebGLPipeline#restoreContext * @since 3.80.0 */ restoreContext: function () { var shaders = this.shaders; var hasVertexBuffer = !!this.vertexBuffer; // Deactivate all invalidated state. this.activeBuffer = null; this.activeTextures.length = 0; this.batch.length = 0; this.currentBatch = null; this.currentTexture = null; this.currentUnit = 0; if (hasVertexBuffer) { this.setVertexBuffer(); } for (var i = 0; i < shaders.length; i++) { var shader = shaders[i]; shader.syncUniforms(); if (hasVertexBuffer) { shader.rebind(); } } }, /** * Binds the vertex buffer to be the active ARRAY_BUFFER on the WebGL context. * * It first checks to see if it's already set as the active buffer and only * binds itself if not. * * @method Phaser.Renderer.WebGL.WebGLPipeline#setVertexBuffer * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.Wrappers.WebGLBufferWrapper} [buffer] - The Vertex Buffer to be bound. Defaults to the one owned by this pipeline. * * @return {boolean} `true` if the vertex buffer was bound, or `false` if it was already bound. */ setVertexBuffer: function (buffer) { if (buffer === undefined) { buffer = this.vertexBuffer; } if (buffer !== this.activeBuffer) { var gl = this.gl; this.gl.bindBuffer(gl.ARRAY_BUFFER, buffer.webGLBuffer); this.activeBuffer = buffer; return true; } return false; }, /** * This method is called as a result of the `WebGLPipeline.batchQuad` method, right before a quad * belonging to a Game Object is about to be added to the batch. When this is called, the * renderer has just performed a flush. It will bind the current render target, if any are set * and finally call the `onPreBatch` hook. * * It is also called as part of the `PipelineManager.preBatch` method when processing Post FX Pipelines. * * @method Phaser.Renderer.WebGL.WebGLPipeline#preBatch * @since 3.50.0 * * @param {(Phaser.GameObjects.GameObject|Phaser.Cameras.Scene2D.Camera)} [gameObject] - The Game Object or Camera that invoked this pipeline, if any. * * @return {this} This WebGLPipeline instance. */ preBatch: function (gameObject) { if (this.currentRenderTarget) { this.currentRenderTarget.bind(); } this.onPreBatch(gameObject); return this; }, /** * This method is called as a result of the `WebGLPipeline.batchQuad` method, right after a quad * belonging to a Game Object has been added to the batch. When this is called, the * renderer has just performed a flush. * * It calls the `onDraw` hook followed by the `onPostBatch` hook, which can be used to perform * additional Post FX Pipeline processing. * * It is also called as part of the `PipelineManager.postBatch` method when processing Post FX Pipelines. * * @method Phaser.Renderer.WebGL.WebGLPipeline#postBatch * @since 3.50.0 * * @param {(Phaser.GameObjects.GameObject|Phaser.Cameras.Scene2D.Camera)} [gameObject] - The Game Object or Camera that invoked this pipeline, if any. * * @return {this} This WebGLPipeline instance. */ postBatch: function (gameObject) { this.onDraw(this.currentRenderTarget); this.onPostBatch(gameObject); return this; }, /** * This method is only used by Sprite FX and Post FX Pipelines and those that extend from them. * * This method is called every time the `postBatch` method is called and is passed a * reference to the current render target. * * At the very least a Post FX Pipeline should call `this.bindAndDraw(renderTarget)`, * however, you can do as much additional processing as you like in this method if * you override it from within your own pipelines. * * @method Phaser.Renderer.WebGL.WebGLPipeline#onDraw * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.RenderTarget} renderTarget - The Render Target. * @param {Phaser.Renderer.WebGL.RenderTarget} [swapTarget] - A Swap Render Target, useful for double-buffer effects. Only set by SpriteFX Pipelines. */ onDraw: function () { }, /** * This method is called every time the Pipeline Manager deactivates this pipeline, swapping from * it to another one. This happens after a call to `flush` and before the new pipeline is bound. * * @method Phaser.Renderer.WebGL.WebGLPipeline#unbind * @since 3.50.0 */ unbind: function () { if (this.currentRenderTarget) { this.currentRenderTarget.unbind(); } }, /** * Uploads the vertex data and emits a draw call for the current batch of vertices. * * @method Phaser.Renderer.WebGL.WebGLPipeline#flush * @fires Phaser.Renderer.WebGL.Pipelines.Events#BEFORE_FLUSH * @fires Phaser.Renderer.WebGL.Pipelines.Events#AFTER_FLUSH * @since 3.0.0 * * @param {boolean} [isPostFlush=false] - Was this flush invoked as part of a post-process, or not? * * @return {this} This WebGLPipeline instance. */ flush: function (isPostFlush) { if (isPostFlush === undefined) { isPostFlush = false; } if (this.vertexCount > 0) { this.emit(Events.BEFORE_FLUSH, this, isPostFlush); this.onBeforeFlush(isPostFlush); var gl = this.gl; var vertexCount = this.vertexCount; var vertexSize = this.currentShader.vertexSize; var topology = this.topology; if (this.active) { this.setVertexBuffer(); if (vertexCount === this.vertexCapacity) { gl.bufferData(gl.ARRAY_BUFFER, this.vertexData, gl.DYNAMIC_DRAW); } else { gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.bytes.subarray(0, vertexCount * vertexSize)); } var i; var entry; var texture; var batch = this.batch; var activeTextures = this.activeTextures; if (this.forceZero) { // Single Texture Pipeline if (!activeTextures[0]) { gl.activeTexture(gl.TEXTURE0); } for (i = 0; i < batch.length; i++) { entry = batch[i]; texture = entry.texture[0]; if (activeTextures[0] !== texture) { gl.bindTexture(gl.TEXTURE_2D, texture.webGLTexture); activeTextures[0] = texture; } gl.drawArrays(topology, entry.start, entry.count); } } else { for (i = 0; i < batch.length; i++) { entry = batch[i]; for (var t = 0; t <= entry.maxUnit; t++) { texture = entry.texture[t]; if (activeTextures[t] !== texture) { gl.activeTexture(gl.TEXTURE0 + t); gl.bindTexture(gl.TEXTURE_2D, texture.webGLTexture); activeTextures[t] = texture; } } gl.drawArrays(topology, entry.start, entry.count); } } } this.vertexCount = 0; this.batch.length = 0; this.currentBatch = null; this.currentTexture = null; this.currentUnit = 0; this.emit(Events.AFTER_FLUSH, this, isPostFlush); this.onAfterFlush(isPostFlush); } return this; }, /** * By default this is an empty method hook that you can override and use in your own custom pipelines. * * This method is called every time the Pipeline Manager makes this the active pipeline. It is called * at the end of the `WebGLPipeline.bind` method, after the current shader has been set. The current * shader is passed to this hook. * * For example, if a display list has 3 Sprites in it that all use the same pipeline, this hook will * only be called for the first one, as the 2nd and 3rd Sprites do not cause the pipeline to be changed. * * If you need to listen for that event instead, use the `onBind` hook. * * @method Phaser.Renderer.WebGL.WebGLPipeline#onActive * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.WebGLShader} currentShader - The shader that was set as current. */ onActive: function () { }, /** * By default this is an empty method hook that you can override and use in your own custom pipelines. * * This method is called every time a **Game Object** asks the Pipeline Manager to use this pipeline, * even if the pipeline is already active. * * Unlike the `onActive` method, which is only called when the Pipeline Manager makes this pipeline * active, this hook is called for every Game Object that requests use of this pipeline, allowing you to * perform per-object set-up, such as loading shader uniform data. * * @method Phaser.Renderer.WebGL.WebGLPipeline#onBind * @since 3.50.0 * * @param {Phaser.GameObjects.GameObject} [gameObject] - The Game Object that invoked this pipeline, if any. */ onBind: function () { }, /** * By default this is an empty method hook that you can override and use in your own custom pipelines. * * This method is called when the Pipeline Manager needs to rebind this pipeline. This happens after a * pipeline has been cleared, usually when passing control over to a 3rd party WebGL library, like Spine, * and then returing to Phaser again. * * @method Phaser.Renderer.WebGL.WebGLPipeline#onRebind * @since 3.50.0 */ onRebind: function () { }, /** * By default this is an empty method hook that you can override and use in your own custom pipelines. * * This method is called every time the `batchQuad` or `batchTri` methods are called. If this was * as a result of a Game Object, then the Game Object reference is passed to this hook too. * * This hook is called _after_ the quad (or tri) has been added to the batch, so you can safely * call 'flush' from within this. * * Note that Game Objects may c