/** * @author Richard Davey * @copyright 2013-2023 Photon Storm Ltd. * @license {@link https://opensource.org/licenses/MIT|MIT License} */ var Class = require('../../utils/Class'); var CONST = require('./pipelines/const'); var CustomMap = require('../../structs/Map'); var Device = require('../../device/'); var GetFastValue = require('../../utils/object/GetFastValue'); var RenderTarget = require('./RenderTarget'); var SnapCeil = require('../../math/snap/SnapCeil'); // Default Phaser 3 Pipelines var BitmapMaskPipeline = require('./pipelines/BitmapMaskPipeline'); var FXPipeline = require('./pipelines/FXPipeline'); var LightPipeline = require('./pipelines/LightPipeline'); var MobilePipeline = require('./pipelines/MobilePipeline'); var MultiPipeline = require('./pipelines/MultiPipeline'); var PointLightPipeline = require('./pipelines/PointLightPipeline'); var RopePipeline = require('./pipelines/RopePipeline'); var SinglePipeline = require('./pipelines/SinglePipeline'); var UtilityPipeline = require('./pipelines/UtilityPipeline'); // FX Pipelines var GlowFXPipeline = require('./pipelines/fx/GlowFXPipeline'); var ShadowFXPipeline = require('./pipelines/fx/ShadowFXPipeline'); var PixelateFXPipeline = require('./pipelines/fx/PixelateFXPipeline'); var VignetteFXPipeline = require('./pipelines/fx/VignetteFXPipeline'); var ShineFXPipeline = require('./pipelines/fx/ShineFXPipeline'); /** * @classdesc * The Pipeline Manager is responsible for the creation, activation, running and destruction * of WebGL Pipelines and Post FX Pipelines in Phaser 3. * * The `WebGLRenderer` owns a single instance of the Pipeline Manager, which you can access * via the `WebGLRenderer.pipelines` property. * * By default, there are 9 pipelines installed into the Pipeline Manager when Phaser boots: * * 1. The Multi Pipeline. Responsible for all multi-texture rendering, i.e. Sprites and Tilemaps. * 2. The Rope Pipeline. Responsible for rendering the Rope Game Object. * 3. The Light Pipeline. Responsible for rendering the Light Game Object. * 4. The Point Light Pipeline. Responsible for rendering the Point Light Game Object. * 5. The Single Pipeline. Responsible for rendering Game Objects that explicitly require one bound texture. * 6. The Bitmap Mask Pipeline. Responsible for Bitmap Mask rendering. * 7. The Utility Pipeline. Responsible for providing lots of handy texture manipulation functions. * 8. The Mobile Pipeline. Responsible for rendering on mobile with single-bound textures. * 9. The FX Pipeline. Responsible for rendering Game Objects with special FX applied to them. * * You can add your own custom pipeline via the `PipelineManager.add` method. Pipelines are * identified by unique string-based keys. * * @class PipelineManager * @memberof Phaser.Renderer.WebGL * @constructor * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.WebGLRenderer} renderer - A reference to the WebGL Renderer that owns this Pipeline Manager. */ var PipelineManager = new Class({ initialize: function PipelineManager (renderer) { /** * A reference to the Game instance. * * @name Phaser.Renderer.WebGL.PipelineManager#game * @type {Phaser.Game} * @since 3.50.0 */ this.game = renderer.game; /** * A reference to the WebGL Renderer instance. * * @name Phaser.Renderer.WebGL.PipelineManager#renderer * @type {Phaser.Renderer.WebGL.WebGLRenderer} * @since 3.50.0 */ this.renderer = renderer; /** * This map stores all pipeline classes available in this manager. * * The Utility Class must always come first. * * @name Phaser.Renderer.WebGL.PipelineManager#classes * @type {Phaser.Structs.Map.} * @since 3.50.0 */ this.classes = new CustomMap([ [ CONST.UTILITY_PIPELINE, UtilityPipeline ], [ CONST.MULTI_PIPELINE, MultiPipeline ], [ CONST.BITMAPMASK_PIPELINE, BitmapMaskPipeline ], [ CONST.SINGLE_PIPELINE, SinglePipeline ], [ CONST.ROPE_PIPELINE, RopePipeline ], [ CONST.LIGHT_PIPELINE, LightPipeline ], [ CONST.POINTLIGHT_PIPELINE, PointLightPipeline ], [ CONST.MOBILE_PIPELINE, MobilePipeline ], [ CONST.FX_PIPELINE, FXPipeline ] ]); /** * This map stores all Post FX Pipeline classes available in this manager. * * @name Phaser.Renderer.WebGL.PipelineManager#postPipelineClasses * @type {Phaser.Structs.Map.} * @since 3.50.0 */ this.postPipelineClasses = new CustomMap([ [ 'GlowFX', GlowFXPipeline ], [ 'ShadowFX', ShadowFXPipeline ], [ 'PixelateFX', PixelateFXPipeline ], [ 'VignetteFX', VignetteFXPipeline ], [ 'ShineFX', ShineFXPipeline ] ]); /** * This map stores all pipeline instances in this manager. * * This is populated with the default pipelines in the `boot` method. * * @name Phaser.Renderer.WebGL.PipelineManager#pipelines * @type {Phaser.Structs.Map.} * @since 3.50.0 */ this.pipelines = new CustomMap(); /** * The default Game Object pipeline. * * @name Phaser.Renderer.WebGL.PipelineManager#default * @type {Phaser.Renderer.WebGL.WebGLPipeline} * @default null * @since 3.60.0 */ this.default = null; /** * Current pipeline in use by the WebGLRenderer. * * @name Phaser.Renderer.WebGL.PipelineManager#current * @type {Phaser.Renderer.WebGL.WebGLPipeline} * @default null * @since 3.50.0 */ this.current = null; /** * The previous WebGLPipeline that was in use. * * This is set when `clearPipeline` is called and restored in `rebindPipeline` if none is given. * * @name Phaser.Renderer.WebGL.PipelineManager#previous * @type {Phaser.Renderer.WebGL.WebGLPipeline} * @default null * @since 3.50.0 */ this.previous = null; /** * A constant-style reference to the Multi Pipeline Instance. * * This is the default Phaser 3 pipeline and is used by the WebGL Renderer to manage * camera effects and more. This property is set during the `boot` method. * * @name Phaser.Renderer.WebGL.PipelineManager#MULTI_PIPELINE * @type {Phaser.Renderer.WebGL.Pipelines.MultiPipeline} * @default null * @since 3.50.0 */ this.MULTI_PIPELINE = null; /** * A constant-style reference to the Bitmap Mask Pipeline Instance. * * This is the default Phaser 3 mask pipeline and is used Game Objects using * a Bitmap Mask. This property is set during the `boot` method. * * @name Phaser.Renderer.WebGL.PipelineManager#BITMAPMASK_PIPELINE * @type {Phaser.Renderer.WebGL.Pipelines.BitmapMaskPipeline} * @default null * @since 3.50.0 */ this.BITMAPMASK_PIPELINE = null; /** * A constant-style reference to the Utility Pipeline Instance. * * @name Phaser.Renderer.WebGL.PipelineManager#UTILITY_PIPELINE * @type {Phaser.Renderer.WebGL.Pipelines.UtilityPipeline} * @default null * @since 3.50.0 */ this.UTILITY_PIPELINE = null; /** * A constant-style reference to the Mobile Pipeline Instance. * * This is the default Phaser 3 mobile pipeline and is used by the WebGL Renderer to manage * camera effects and more on mobile devices. This property is set during the `boot` method. * * @name Phaser.Renderer.WebGL.PipelineManager#MOBILE_PIPELINE * @type {Phaser.Renderer.WebGL.Pipelines.MobilePipeline} * @default null * @since 3.60.0 */ this.MOBILE_PIPELINE = null; /** * A constant-style reference to the FX Pipeline Instance. * * This is the default Phaser 3 FX pipeline and is used by the WebGL Renderer to manage * Game Objects with special effects enabled. This property is set during the `boot` method. * * @name Phaser.Renderer.WebGL.PipelineManager#FX_PIPELINE * @type {Phaser.Renderer.WebGL.Pipelines.FXPipeline} * @default null * @since 3.60.0 */ this.FX_PIPELINE = null; /** * A reference to the Full Frame 1 Render Target that belongs to the * Utility Pipeline. This property is set during the `boot` method. * * This Render Target is the full size of the renderer. * * You can use this directly in Post FX Pipelines for multi-target effects. * However, be aware that these targets are shared between all post fx pipelines. * * @name Phaser.Renderer.WebGL.PipelineManager#fullFrame1 * @type {Phaser.Renderer.WebGL.RenderTarget} * @default null * @since 3.50.0 */ this.fullFrame1; /** * A reference to the Full Frame 2 Render Target that belongs to the * Utility Pipeline. This property is set during the `boot` method. * * This Render Target is the full size of the renderer. * * You can use this directly in Post FX Pipelines for multi-target effects. * However, be aware that these targets are shared between all post fx pipelines. * * @name Phaser.Renderer.WebGL.PipelineManager#fullFrame2 * @type {Phaser.Renderer.WebGL.RenderTarget} * @default null * @since 3.50.0 */ this.fullFrame2; /** * A reference to the Half Frame 1 Render Target that belongs to the * Utility Pipeline. This property is set during the `boot` method. * * This Render Target is half the size of the renderer. * * You can use this directly in Post FX Pipelines for multi-target effects. * However, be aware that these targets are shared between all post fx pipelines. * * @name Phaser.Renderer.WebGL.PipelineManager#halfFrame1 * @type {Phaser.Renderer.WebGL.RenderTarget} * @default null * @since 3.50.0 */ this.halfFrame1; /** * A reference to the Half Frame 2 Render Target that belongs to the * Utility Pipeline. This property is set during the `boot` method. * * This Render Target is half the size of the renderer. * * You can use this directly in Post FX Pipelines for multi-target effects. * However, be aware that these targets are shared between all post fx pipelines. * * @name Phaser.Renderer.WebGL.PipelineManager#halfFrame2 * @type {Phaser.Renderer.WebGL.RenderTarget} * @default null * @since 3.50.0 */ this.halfFrame2; /** * An array of RenderTarget instances that belong to this pipeline. * * @name Phaser.Renderer.WebGL.PipelineManager#renderTargets * @type {Phaser.Renderer.WebGL.RenderTarget[]} * @since 3.60.0 */ this.renderTargets = []; /** * The largest render target dimension before we just use a full-screen target. * * @name Phaser.Renderer.WebGL.PipelineManager#maxDimension * @type {number} * @since 3.60.0 */ this.maxDimension = 0; /** * The amount in which each target frame will increase. * * Defaults to 32px but can be overridden in the config. * * @name Phaser.Renderer.WebGL.PipelineManager#frameInc * @type {number} * @since 3.60.0 */ this.frameInc = 32; /** * The Render Target index. Used internally by the methods * in this class. Do not modify directly. * * @name Phaser.Renderer.WebGL.PipelineManager#targetIndex * @type {number} * @since 3.60.0 */ this.targetIndex = 0; }, /** * Internal boot handler, called by the WebGLRenderer durings its boot process. * * Adds all of the default pipelines, based on the game config, and then calls * the `boot` method on each one of them. * * Finally, the default pipeline is set. * * @method Phaser.Renderer.WebGL.PipelineManager#boot * @since 3.50.0 * * @param {Phaser.Types.Core.PipelineConfig} pipelineConfig - The pipeline configuration object as set in the Game Config. * @param {string} defaultPipeline - The name of the default Game Object pipeline, as set in the Game Config * @param {boolean} autoMobilePipeline - Automatically set the default pipeline to mobile if non-desktop detected? */ boot: function (pipelineConfig, defaultPipeline, autoMobilePipeline) { // Create the default RenderTextures var renderer = this.renderer; var targets = this.renderTargets; this.frameInc = Math.floor(GetFastValue(pipelineConfig, 'frameInc', 32)); var renderWidth = renderer.width; var renderHeight = renderer.height; var minDimension = Math.min(renderWidth, renderHeight); var qty = Math.ceil(minDimension / this.frameInc); for (var i = 1; i < qty; i++) { var targetWidth = i * this.frameInc; targets.push(new RenderTarget(renderer, targetWidth, targetWidth)); // Duplicate RT for swap frame targets.push(new RenderTarget(renderer, targetWidth, targetWidth)); // Duplicate RT for alt swap frame targets.push(new RenderTarget(renderer, targetWidth, targetWidth)); } // Full-screen RTs targets.push(new RenderTarget(renderer, renderWidth, renderHeight, 1, 0, true, true)); targets.push(new RenderTarget(renderer, renderWidth, renderHeight, 1, 0, true, true)); targets.push(new RenderTarget(renderer, renderWidth, renderHeight, 1, 0, true, true)); this.maxDimension = (qty - 1) * this.frameInc; // Install each of the default pipelines var instance; var pipelineName; var _this = this; var game = this.game; this.classes.each(function (pipelineName, pipeline) { instance = _this.add(pipelineName, new pipeline({ game: game })); if (pipelineName === CONST.UTILITY_PIPELINE) { _this.UTILITY_PIPELINE = instance; // FBO references _this.fullFrame1 = instance.fullFrame1; _this.fullFrame2 = instance.fullFrame2; _this.halfFrame1 = instance.halfFrame1; _this.halfFrame2 = instance.halfFrame2; } }); // Our const-like references this.MULTI_PIPELINE = this.get(CONST.MULTI_PIPELINE); this.BITMAPMASK_PIPELINE = this.get(CONST.BITMAPMASK_PIPELINE); this.MOBILE_PIPELINE = this.get(CONST.MOBILE_PIPELINE); this.FX_PIPELINE = this.get(CONST.FX_PIPELINE); // And now the ones in the config, if any if (pipelineConfig) { for (pipelineName in pipelineConfig) { var pipelineClass = pipelineConfig[pipelineName]; instance = new pipelineClass(game); instance.name = pipelineName; if (instance.isPostFX) { this.postPipelineClasses.set(pipelineName, pipelineClass); } else if (!this.has(pipelineName)) { this.classes.set(pipelineName, pipelineClass); this.add(pipelineName, instance); } } } // Finally, set the Default Game Object pipeline this.default = this.get(defaultPipeline); if (autoMobilePipeline && !Device.os.desktop) { this.default = this.MOBILE_PIPELINE; } }, /** * Sets the default pipeline being used by Game Objects. * * If no instance, or matching name, exists in this manager, it returns `undefined`. * * You can use this to override the default pipeline, for example by forcing * the Mobile or Multi Tint Pipelines, which is especially useful for development * testing. * * Make sure you call this method _before_ creating any Game Objects, as it will * only impact Game Objects created after you call it. * * @method Phaser.Renderer.WebGL.PipelineManager#setDefaultPipeline * @since 3.60.0 * * @param {(string|Phaser.Renderer.WebGL.WebGLPipeline)} pipeline - Either the string-based name of the pipeline to get, or a pipeline instance to look-up. * * @return {Phaser.Renderer.WebGL.WebGLPipeline} The pipeline instance that was set as default, or `undefined` if not found. */ setDefaultPipeline: function (pipeline) { var instance = this.get(pipeline); if (instance) { this.default = instance; } return instance; }, /** * Adds a pipeline instance to this Pipeline Manager. * * The name of the instance must be unique within this manager. * * Make sure to pass an instance to this method, not a base class. * * For example, you should pass it like this: * * ```javascript * this.add('yourName', new CustomPipeline(game));` * ``` * * and **not** like this: * * ```javascript * this.add('yourName', CustomPipeline);` * ``` * * To add a **Post Pipeline**, see `addPostPipeline` instead. * * @method Phaser.Renderer.WebGL.PipelineManager#add * @since 3.50.0 * * @param {string} name - A unique string-based key for the pipeline within the manager. * @param {Phaser.Renderer.WebGL.WebGLPipeline} pipeline - A pipeline _instance_ which must extend `WebGLPipeline`. * * @return {Phaser.Renderer.WebGL.WebGLPipeline} The pipeline instance that was passed. */ add: function (name, pipeline) { if (pipeline.isPostFX) { console.warn(name + ' is a Post Pipeline. Use `addPostPipeline` instead'); return; } var pipelines = this.pipelines; var renderer = this.renderer; if (!pipelines.has(name)) { pipeline.name = name; pipeline.manager = this; pipelines.set(name, pipeline); } else { console.warn('Pipeline exists: ' + name); } if (!pipeline.hasBooted) { pipeline.boot(); } if (renderer.width !== 0 && renderer.height !== 0 && !pipeline.isPreFX) { pipeline.resize(renderer.width, renderer.height); } return pipeline; }, /** * Adds a Post Pipeline to this Pipeline Manager. * * Make sure to pass a base class to this method, not an instance. * * For example, you should pass it like this: * * ```javascript * this.addPostPipeline('yourName', CustomPipeline);` * ``` * * and **not** like this: * * ```javascript * this.addPostPipeline('yourName', new CustomPipeline());` * ``` * * To add a regular pipeline, see the `add` method instead. * * @method Phaser.Renderer.WebGL.PipelineManager#addPostPipeline * @since 3.50.0 * * @param {string} name - A unique string-based key for the pipeline within the manager. * @param {function} pipeline - A pipeline class which must extend `PostFXPipeline`. * * @return {this} This Pipeline Manager. */ addPostPipeline: function (name, pipeline) { if (!this.postPipelineClasses.has(name)) { this.postPipelineClasses.set(name, pipeline); } }, /** * Flushes the current pipeline, if one is bound. * * @method Phaser.Renderer.WebGL.PipelineManager#flush * @since 3.50.0 */ flush: function () { if (this.current) { this.current.flush(); } }, /** * Checks if a pipeline is present in this Pipeline Manager. * * @method Phaser.Renderer.WebGL.PipelineManager#has * @since 3.50.0 * * @param {(string|Phaser.Renderer.WebGL.WebGLPipeline)} pipeline - Either the string-based name of the pipeline to get, or a pipeline instance to look-up. * * @return {boolean} `true` if the given pipeline is loaded, otherwise `false`. */ has: function (pipeline) { var pipelines = this.pipelines; if (typeof pipeline === 'string') { return pipelines.has(pipeline); } else if (pipelines.contains(pipeline)) { return true; } return false; }, /** * Returns the pipeline instance based on the given name, or instance. * * If no instance, or matching name, exists in this manager, it returns `undefined`. * * @method Phaser.Renderer.WebGL.PipelineManager#get * @since 3.50.0 * * @param {(string|Phaser.Renderer.WebGL.WebGLPipeline)} pipeline - Either the string-based name of the pipeline to get, or a pipeline instance to look-up. * * @return {Phaser.Renderer.WebGL.WebGLPipeline} The pipeline instance, or `undefined` if not found. */ get: function (pipeline) { var pipelines = this.pipelines; if (typeof pipeline === 'string') { return pipelines.get(pipeline); } else if (pipelines.contains(pipeline)) { return pipeline; } }, /** * Returns a _new instance_ of the post pipeline based on the given name, or class. * * If no instance, or matching name, exists in this manager, it returns `undefined`. * * @method Phaser.Renderer.WebGL.PipelineManager#getPostPipeline * @since 3.50.0 * * @param {(string|function|Phaser.Renderer.WebGL.Pipelines.PostFXPipeline)} pipeline - Either the string-based name of the pipeline to get, or a pipeline instance, or class to look-up. * @param {Phaser.GameObjects.GameObject} [gameObject] - If this post pipeline is being installed into a Game Object or Camera, this is a reference to it. * * @return {Phaser.Renderer.WebGL.Pipelines.PostFXPipeline} The pipeline instance, or `undefined` if not found. */ getPostPipeline: function (pipeline, gameObject) { var pipelineClasses = this.postPipelineClasses; var instance; var pipelineName = ''; if (typeof pipeline === 'string') { instance = pipelineClasses.get(pipeline); pipelineName = pipeline; } else if (typeof pipeline === 'function') { // A class if (pipelineClasses.contains(pipeline)) { instance = pipeline; } pipelineName = pipeline.name; } else if (typeof pipeline === 'object') { // Instance instance = pipelineClasses.get(pipeline.name); pipelineName = pipeline.name; } if (instance) { var newPipeline = new instance(this.game); newPipeline.name = pipelineName; if (gameObject) { newPipeline.gameObject = gameObject; } return newPipeline; } }, /** * Removes a pipeline instance based on the given name. * * If no pipeline matches the name, this method does nothing. * * Note that the pipeline will not be flushed or destroyed, it's simply removed from * this manager. * * @method Phaser.Renderer.WebGL.PipelineManager#remove * @since 3.50.0 * * @param {string} name - The name of the pipeline to be removed. * @param {boolean} [removeClass=true] - Remove the pipeline class as well as the instance? * @param {boolean} [removePostPipelineClass=true] - Remove the post pipeline class as well as the instance? */ remove: function (name, removeClass, removePostPipelineClass) { if (removeClass === undefined) { removeClass = true; } if (removePostPipelineClass === undefined) { removePostPipelineClass = true; } this.pipelines.delete(name); if (removeClass) { this.classes.delete(name); } if (removePostPipelineClass) { this.postPipelineClasses.delete(name); } }, /** * Sets the current pipeline to be used by the `WebGLRenderer`. * * This method accepts a pipeline instance as its parameter, not the name. * * If the pipeline isn't already the current one it will call `WebGLPipeline.bind` and then `onBind`. * * You cannot set Post FX Pipelines using this method. To use a Post FX Pipeline, you should * apply it to either a Camera, Container or other supporting Game Object. * * @method Phaser.Renderer.WebGL.PipelineManager#set * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.WebGLPipeline} pipeline - The pipeline instance to be set as current. * @param {Phaser.GameObjects.GameObject} [gameObject] - The Game Object that invoked this pipeline, if any. * @param {Phaser.Renderer.WebGL.WebGLShader} [currentShader] - The shader to set as being current. * * @return {Phaser.Renderer.WebGL.WebGLPipeline} The pipeline that was set, or undefined if it couldn't be set. */ set: function (pipeline, gameObject, currentShader) { if (pipeline.isPostFX) { return; } if (!this.isCurrent(pipeline, currentShader)) { this.flush(); if (this.current) { this.current.unbind(); } this.current = pipeline; pipeline.bind(currentShader); } pipeline.updateProjectionMatrix(); pipeline.onBind(gameObject); return pipeline; }, /** * This method is called by the `WebGLPipeline.batchQuad` method, right before a quad * belonging to a Game Object is about to be added to the batch. It causes a batch * flush, then calls the `preBatch` method on the post-fx pipelines belonging to the * Game Object. * * @method Phaser.Renderer.WebGL.PipelineManager#preBatch * @since 3.50.0 * * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object about to be batched. */ preBatch: function (gameObject) { if (gameObject.hasPostPipeline) { this.flush(); var pipelines = gameObject.postPipelines; // Iterate in reverse because we need them stacked in the order they're in the array for (var i = pipelines.length - 1; i >= 0; i--) { var pipeline = pipelines[i]; if (pipeline.active) { pipeline.preBatch(gameObject); } } } }, /** * This method is called by the `WebGLPipeline.batchQuad` method, right after a quad * belonging to a Game Object has been added to the batch. It causes a batch * flush, then calls the `postBatch` method on the post-fx pipelines belonging to the * Game Object. * * @method Phaser.Renderer.WebGL.PipelineManager#postBatch * @since 3.50.0 * * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object that was just added to the batch. */ postBatch: function (gameObject) { if (gameObject.hasPostPipeline) { this.flush(); var pipelines = gameObject.postPipelines; for (var i = 0; i < pipelines.length; i++) { var pipeline = pipelines[i]; if (pipeline.active) { pipeline.postBatch(gameObject); } } } }, /** * Called at the start of the `WebGLRenderer.preRenderCamera` method. * * If the Camera has post pipelines set, it will flush the batch and then call the * `preBatch` method on the post-fx pipelines belonging to the Camera. * * @method Phaser.Renderer.WebGL.PipelineManager#preBatchCamera * @since 3.50.0 * * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera about to be rendered. */ preBatchCamera: function (camera) { if (camera.hasPostPipeline) { this.flush(); var pipelines = camera.postPipelines; // Iterate in reverse because we need them stacked in the order they're in the array for (var i = pipelines.length - 1; i >= 0; i--) { var pipeline = pipelines[i]; if (pipeline.active) { pipeline.preBatch(camera); } } } }, /** * Called at the end of the `WebGLRenderer.postRenderCamera` method. * * If the Camera has post pipelines set, it will flush the batch and then call the * `postBatch` method on the post-fx pipelines belonging to the Camera. * * @method Phaser.Renderer.WebGL.PipelineManager#postBatchCamera * @since 3.50.0 * * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera that was just rendered. */ postBatchCamera: function (camera) { if (camera.hasPostPipeline) { this.flush(); var pipelines = camera.postPipelines; for (var i = 0; i < pipelines.length; i++) { var pipeline = pipelines[i]; if (pipeline.active) { pipeline.postBatch(camera); } } } }, /** * Checks to see if the given pipeline is already the active pipeline, both within this * Pipeline Manager and also has the same shader set in the Renderer. * * @method Phaser.Renderer.WebGL.PipelineManager#isCurrent * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.WebGLPipeline} pipeline - The pipeline instance to be checked. * @param {Phaser.Renderer.WebGL.WebGLShader} [currentShader] - The shader to set as being current. * * @return {boolean} `true` if the given pipeline is already the current pipeline, otherwise `false`. */ isCurrent: function (pipeline, currentShader) { var renderer = this.renderer; var current = this.current; if (current && !currentShader) { currentShader = current.currentShader; } return !(current !== pipeline || currentShader.program !== renderer.currentProgram); }, /** * Copy the `source` Render Target to the `target` Render Target. * * You can optionally set the brightness factor of the copy. * * The difference between this method and `drawFrame` is that this method * uses a faster copy shader, where only the brightness can be modified. * If you need color level manipulation, see `drawFrame` instead. * * The copy itself is handled by the Utility Pipeline. * * @method Phaser.Renderer.WebGL.PipelineManager#copyFrame * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.RenderTarget} source - The source Render Target. * @param {Phaser.Renderer.WebGL.RenderTarget} [target] - The target Render Target. * @param {number} [brightness=1] - The brightness value applied to the frame copy. * @param {boolean} [clear=true] - Clear the target before copying? * @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target? * * @return {this} This Pipeline Manager instance. */ copyFrame: function (source, target, brightness, clear, clearAlpha) { this.setUtility(this.UTILITY_PIPELINE.copyShader).copyFrame(source, target, brightness, clear, clearAlpha); return this; }, /** * Pops the framebuffer from the renderers FBO stack and sets that as the active target, * then draws the `source` Render Target to it. It then resets the renderer textures. * * This should be done when you need to draw the _final_ results of a pipeline to the game * canvas, or the next framebuffer in line on the FBO stack. You should only call this once * in the `onDraw` handler and it should be the final thing called. Be careful not to call * this if you need to actually use the pipeline shader, instead of the copy shader. In * those cases, use the `bindAndDraw` method. * * @method Phaser.Renderer.WebGL.PipelineManager#copyToGame * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.RenderTarget} source - The Render Target to draw from. */ copyToGame: function (source) { this.setUtility(this.UTILITY_PIPELINE.copyShader).copyToGame(source); return this; }, /** * Copy the `source` Render Target to the `target` Render Target, using the * given Color Matrix. * * The difference between this method and `copyFrame` is that this method * uses a color matrix shader, where you have full control over the luminance * values used during the copy. If you don't need this, you can use the faster * `copyFrame` method instead. * * The copy itself is handled by the Utility Pipeline. * * @method Phaser.Renderer.WebGL.PipelineManager#drawFrame * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.RenderTarget} source - The source Render Target. * @param {Phaser.Renderer.WebGL.RenderTarget} [target] - The target Render Target. * @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target? * @param {Phaser.Display.ColorMatrix} [colorMatrix] - The Color Matrix to use when performing the draw. * * @return {this} This Pipeline Manager instance. */ drawFrame: function (source, target, clearAlpha, colorMatrix) { this.setUtility(this.UTILITY_PIPELINE.colorMatrixShader).drawFrame(source, target, clearAlpha, colorMatrix); return this; }, /** * Draws the `source1` and `source2` Render Targets to the `target` Render Target * using a linear blend effect, which is controlled by the `strength` parameter. * * The draw itself is handled by the Utility Pipeline. * * @method Phaser.Renderer.WebGL.PipelineManager#blendFrames * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.RenderTarget} source1 - The first source Render Target. * @param {Phaser.Renderer.WebGL.RenderTarget} source2 - The second source Render Target. * @param {Phaser.Renderer.WebGL.RenderTarget} [target] - The target Render Target. * @param {number} [strength=1] - The strength of the blend. * @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target? * * @return {this} This Pipeline Manager instance. */ blendFrames: function (source1, source2, target, strength, clearAlpha) { this.setUtility(this.UTILITY_PIPELINE.linearShader).blendFrames(source1, source2, target, strength, clearAlpha); return this; }, /** * Draws the `source1` and `source2` Render Targets to the `target` Render Target * using an additive blend effect, which is controlled by the `strength` parameter. * * The draw itself is handled by the Utility Pipeline. * * @method Phaser.Renderer.WebGL.PipelineManager#blendFramesAdditive * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.RenderTarget} source1 - The first source Render Target. * @param {Phaser.Renderer.WebGL.RenderTarget} source2 - The second source Render Target. * @param {Phaser.Renderer.WebGL.RenderTarget} [target] - The target Render Target. * @param {number} [strength=1] - The strength of the blend. * @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target? * * @return {this} This Pipeline Manager instance. */ blendFramesAdditive: function (source1, source2, target, strength, clearAlpha) { this.setUtility(this.UTILITY_PIPELINE.addShader).blendFramesAdditive(source1, source2, target, strength, clearAlpha); return this; }, /** * Clears the given Render Target. * * @method Phaser.Renderer.WebGL.PipelineManager#clearFrame * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.RenderTarget} target - The Render Target to clear. * @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target? * * @return {this} This Pipeline Manager instance. */ clearFrame: function (target, clearAlpha) { this.UTILITY_PIPELINE.clearFrame(target, clearAlpha); return this; }, /** * Copy the `source` Render Target to the `target` Render Target. * * The difference with this copy is that no resizing takes place. If the `source` * Render Target is larger than the `target` then only a portion the same size as * the `target` dimensions is copied across. * * You can optionally set the brightness factor of the copy. * * @method Phaser.Renderer.WebGL.PipelineManager#blitFrame * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.RenderTarget} source - The source Render Target. * @param {Phaser.Renderer.WebGL.RenderTarget} target - The target Render Target. * @param {number} [brightness=1] - The brightness value applied to the frame copy. * @param {boolean} [clear=true] - Clear the target before copying? * @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target? * @param {boolean} [eraseMode=false] - Erase source from target using ERASE Blend Mode? * * @return {this} This Pipeline Manager instance. */ blitFrame: function (source, target, brightness, clear, clearAlpha, eraseMode) { this.setUtility(this.UTILITY_PIPELINE.copyShader).blitFrame(source, target, brightness, clear, clearAlpha, eraseMode); return this; }, /** * Binds the `source` Render Target and then copies a section of it to the `target` Render Target. * * This method is extremely fast because it uses `gl.copyTexSubImage2D` and doesn't * require the use of any shaders. Remember the coordinates are given in standard WebGL format, * where x and y specify the lower-left corner of the section, not the top-left. Also, the * copy entirely replaces the contents of the target texture, no 'merging' or 'blending' takes * place. * * @method Phaser.Renderer.WebGL.PipelineManager#copyFrameRect * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.RenderTarget} source - The source Render Target. * @param {Phaser.Renderer.WebGL.RenderTarget} target - The target Render Target. * @param {number} x - The x coordinate of the lower left corner where to start copying. * @param {number} y - The y coordinate of the lower left corner where to start copying. * @param {number} width - The width of the texture. * @param {number} height - The height of the texture. * @param {boolean} [clear=true] - Clear the target before copying? * @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target? * * @return {this} This Pipeline Manager instance. */ copyFrameRect: function (source, target, x, y, width, height, clear, clearAlpha) { this.UTILITY_PIPELINE.copyFrameRect(source, target, x, y, width, height, clear, clearAlpha); return this; }, /** * Returns `true` if the current pipeline is forced to use texture unit zero. * * @method Phaser.Renderer.WebGL.PipelineManager#forceZero * @since 3.50.0 * * @return {boolean} `true` if the current pipeline is forced to use texture unit zero. */ forceZero: function () { return (this.current && this.current.forceZero); }, /** * Sets the Multi Pipeline to be the currently bound pipeline. * * This is the default Phaser 3 rendering pipeline. * * @method Phaser.Renderer.WebGL.PipelineManager#setMulti * @since 3.50.0 * * @return {Phaser.Renderer.WebGL.Pipelines.MultiPipeline} The Multi Pipeline instance. */ setMulti: function () { return this.set(this.MULTI_PIPELINE); }, /** * Sets the Utility Pipeline to be the currently bound pipeline. * * @method Phaser.Renderer.WebGL.PipelineManager#setUtility * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.WebGLShader} [currentShader] - The shader to set as being current. * * @return {Phaser.Renderer.WebGL.Pipelines.UtilityPipeline} The Utility Pipeline instance. */ setUtility: function (currentShader) { return this.UTILITY_PIPELINE.bind(currentShader); }, /** * Sets the FX Pipeline to be the currently bound pipeline. * * @method Phaser.Renderer.WebGL.PipelineManager#setFX * @since 3.60.0 * * @return {Phaser.Renderer.WebGL.Pipelines.FXPipeline} The FX Pipeline instance. */ setFX: function () { return this.set(this.FX_PIPELINE); }, /** * Use this to reset the gl context to the state that Phaser requires to continue rendering. * * Calling this will: * * * Disable `DEPTH_TEST`, `CULL_FACE` and `STENCIL_TEST`. * * Clear the depth buffer and stencil buffers. * * Reset the viewport size. * * Reset the blend mode. * * Bind a blank texture as the active texture on texture unit zero. * * Rebinds the given pipeline instance. * * You should call this if you have previously called `clear`, and then wish to return * rendering control to Phaser again. * * @method Phaser.Renderer.WebGL.PipelineManager#rebind * @since 3.50.0 * * @param {Phaser.Renderer.WebGL.WebGLPipeline} [pipeline] - The pipeline instance to be rebound. If not given, the previous pipeline will be bound. */ rebind: function (pipeline) { if (pipeline === undefined && this.previous) { pipeline = this.previous; } var renderer = this.renderer; var gl = renderer.gl; gl.disable(gl.DEPTH_TEST); gl.disable(gl.CULL_FACE); if (renderer.hasActiveStencilMask()) { gl.clear(gl.DEPTH_BUFFER_BIT); } else { // If there wasn't a stencil mask set before this call, we can disable it safely gl.disable(gl.STENCIL_TEST); gl.clear(gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); } gl.viewport(0, 0, renderer.width, renderer.height); renderer.currentProgram = null; renderer.setBlendMode(0, true); var vao = renderer.vaoExtension; if (vao) { vao.bindVertexArrayOES(null); } var entries = this.pipelines.entries; for (var key in entries) { entries[key].glReset = true; } if (pipeline) { this.current = pipeline; pipeline.rebind(); } }, /** * Flushes the current pipeline being used and then clears it, along with the * the current shader program and vertex buffer from the `WebGLRenderer`. * * Then resets the blend mode to NORMAL. * * Call this before jumping to your own gl context handler, and then call `rebind` when * you wish to return control to Phaser again. * * @method Phaser.Renderer.WebGL.PipelineManager#clear * @since 3.50.0 */ clear: function () { var renderer = this.renderer; this.flush(); if (this.current) { this.current.unbind(); this.previous = this.current; this.current = null; } else { this.previous = null; } renderer.currentProgram = null; renderer.setBlendMode(0, true); var vao = renderer.vaoExtension; if (vao) { vao.bindVertexArrayOES(null); } }, /** * Gets a Render Target the right size to render the Sprite on. * * If the Sprite exceeds the size of the renderer, the Render Target will only ever be the maximum * size of the renderer. * * @method Phaser.Renderer.WebGL.PipelineManager#getRenderTarget * @since 3.60.0 * * @param {number} size - The maximum dimension required. * * @return {Phaser.Renderer.WebGL.RenderTarget} A Render Target large enough to fit the sprite. */ getRenderTarget: function (size) { var targets = this.renderTargets; // 2 for just swap // 3 for swap + alt swap var offset = 3; if (size > this.maxDimension) { this.targetIndex = targets.length - offset; return targets[this.targetIndex]; } else { var index = (SnapCeil(size, this.frameInc, 0, true) - 1) * offset; this.targetIndex = index; return targets[index]; } }, /** * Gets a matching Render Target, the same size as the one the Sprite was drawn to, * useful for double-buffer style effects such as blurs. * * @method Phaser.Renderer.WebGL.PipelineManager#getSwapRenderTarget * @since 3.60.0 * * @return {Phaser.Renderer.WebGL.RenderTarget} The Render Target swap frame. */ getSwapRenderTarget: function () { return this.renderTargets[this.targetIndex + 1]; }, /** * Gets a matching Render Target, the same size as the one the Sprite was drawn to, * useful for double-buffer style effects such as blurs. * * @method Phaser.Renderer.WebGL.PipelineManager#getAltSwapRenderTarget * @since 3.60.0 * * @return {Phaser.Renderer.WebGL.RenderTarget} The Render Target swap frame. */ getAltSwapRenderTarget: function () { return this.renderTargets[this.targetIndex + 2]; }, /** * Destroy the Pipeline Manager, cleaning up all related resources and references. * * @method Phaser.Renderer.WebGL.PipelineManager#destroy * @since 3.50.0 */ destroy: function () { this.flush(); this.classes.clear(); this.postPipelineClasses.clear(); this.pipelines.clear(); this.renderer = null; this.game = null; this.classes = null; this.postPipelineClasses = null; this.pipelines = null; this.default = null; this.current = null; this.previous = null; } }); module.exports = PipelineManager;