From 20485e1aa25450714228474ac2ca7c9cb52adfb8 Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Tue, 4 Oct 2022 17:08:48 +0100 Subject: [PATCH] Added MobilePipeline --- src/gameobjects/components/Pipeline.js | 3 +- src/renderer/webgl/PipelineManager.js | 19 ++- .../webgl/pipelines/MobilePipeline.js | 123 ++++++++++++++++++ src/renderer/webgl/pipelines/const.js | 12 +- src/renderer/webgl/pipelines/index.js | 1 + src/renderer/webgl/shaders/Mobile-frag.js | 40 ++++++ src/renderer/webgl/shaders/Mobile-vert.js | 31 +++++ src/renderer/webgl/shaders/src/Mobile.frag | 37 ++++++ src/renderer/webgl/shaders/src/Mobile.vert | 28 ++++ 9 files changed, 291 insertions(+), 3 deletions(-) create mode 100644 src/renderer/webgl/pipelines/MobilePipeline.js create mode 100644 src/renderer/webgl/shaders/Mobile-frag.js create mode 100644 src/renderer/webgl/shaders/Mobile-vert.js create mode 100644 src/renderer/webgl/shaders/src/Mobile.frag create mode 100644 src/renderer/webgl/shaders/src/Mobile.vert diff --git a/src/gameobjects/components/Pipeline.js b/src/gameobjects/components/Pipeline.js index e26e5e66d..36c667cb0 100644 --- a/src/gameobjects/components/Pipeline.js +++ b/src/gameobjects/components/Pipeline.js @@ -92,7 +92,8 @@ var Pipeline = { */ initPipeline: function (pipeline) { - if (pipeline === undefined) { pipeline = PIPELINE_CONST.MULTI_PIPELINE; } + // if (pipeline === undefined) { pipeline = PIPELINE_CONST.MULTI_PIPELINE; } + if (pipeline === undefined) { pipeline = PIPELINE_CONST.MOBILE_PIPELINE; } var renderer = this.scene.sys.renderer; diff --git a/src/renderer/webgl/PipelineManager.js b/src/renderer/webgl/PipelineManager.js index e83e90689..30a9db68c 100644 --- a/src/renderer/webgl/PipelineManager.js +++ b/src/renderer/webgl/PipelineManager.js @@ -14,6 +14,7 @@ var SnapCeil = require('../../math/snap/SnapCeil'); // Default Phaser 3 Pipelines var BitmapMaskPipeline = require('./pipelines/BitmapMaskPipeline'); 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'); @@ -37,6 +38,7 @@ var UtilityPipeline = require('./pipelines/UtilityPipeline'); * 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. * * You can add your own custom pipeline via the `PipelineManager.add` method. Pipelines are * identified by unique string-based keys. @@ -88,7 +90,8 @@ var PipelineManager = new Class({ [ CONST.SINGLE_PIPELINE, SinglePipeline ], [ CONST.ROPE_PIPELINE, RopePipeline ], [ CONST.LIGHT_PIPELINE, LightPipeline ], - [ CONST.POINTLIGHT_PIPELINE, PointLightPipeline ] + [ CONST.POINTLIGHT_PIPELINE, PointLightPipeline ], + [ CONST.MOBILE_PIPELINE, MobilePipeline ] ]); /** @@ -169,6 +172,19 @@ var PipelineManager = new Class({ */ this.UTILITY_PIPELINE = null; + /** + * A constant-style reference to the Mobile Pipeline Instance. + * + * This is the default Phaser 3 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 reference to the Full Frame 1 Render Target that belongs to the * Utility Pipeline. This property is set during the `boot` method. @@ -348,6 +364,7 @@ var PipelineManager = new Class({ // 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); // And now the ones in the config, if any diff --git a/src/renderer/webgl/pipelines/MobilePipeline.js b/src/renderer/webgl/pipelines/MobilePipeline.js new file mode 100644 index 000000000..012e955ab --- /dev/null +++ b/src/renderer/webgl/pipelines/MobilePipeline.js @@ -0,0 +1,123 @@ +/** + * @author Richard Davey + * @author Felipe Alfonso <@bitnenfer> + * @copyright 2022 Photon Storm Ltd. + * @license {@link https://opensource.org/licenses/MIT|MIT License} + */ + +var Class = require('../../../utils/Class'); +var GetFastValue = require('../../../utils/object/GetFastValue'); +var MultiPipeline = require('./MultiPipeline'); +var ShaderSourceFS = require('../shaders/Mobile-frag.js'); +var ShaderSourceVS = require('../shaders/Mobile-vert.js'); +var WEBGL_CONST = require('../const'); +var WebGLPipeline = require('../WebGLPipeline'); + +/** + * @classdesc + * The Multi Pipeline is the core 2D texture rendering pipeline used by Phaser in WebGL. + * Virtually all Game Objects use this pipeline by default, including Sprites, Graphics + * and Tilemaps. It handles the batching of quads and tris, as well as methods for + * drawing and batching geometry data. + * + * Prior to Phaser v3.50 this pipeline was called the `TextureTintPipeline`. + * + * In previous versions of Phaser only one single texture unit was supported at any one time. + * The Multi Pipeline is an evolution of the old Texture Tint Pipeline, updated to support + * multi-textures for increased performance. + * + * The fragment shader it uses can be found in `shaders/src/Multi.frag`. + * The vertex shader it uses can be found in `shaders/src/Multi.vert`. + * + * The default shader attributes for this pipeline are: + * + * `inPosition` (vec2, offset 0) + * `inTexCoord` (vec2, offset 8) + * `inTexId` (float, offset 16) + * `inTintEffect` (float, offset 20) + * `inTint` (vec4, offset 24, normalized) + * + * The default shader uniforms for this pipeline are: + * + * `uProjectionMatrix` (mat4) + * `uMainSampler` (sampler2D array) + * + * If you wish to create a custom pipeline extending from this one, you should use the string + * declaration `%count%` in your fragment shader source, which is used to set the number of + * `sampler2Ds` available. Also add `%getSampler%` so Phaser can inject the getSampler glsl function. + * This function can be used to get the pixel vec4 from the texture: + * + * `vec4 texture = getSampler(int(outTexId), outTexCoord);` + * + * This pipeline will automatically inject the getSampler function for you, should the value exist + * in your shader source. If you wish to handle this yourself, you can also use the + * function `Utils.parseFragmentShaderMaxTextures`. + * + * If you wish to create a pipeline that works from a single texture, or that doesn't have + * internal texture iteration, please see the `SinglePipeline` instead. + * + * @class MultiPipeline + * @extends Phaser.Renderer.WebGL.WebGLPipeline + * @memberof Phaser.Renderer.WebGL.Pipelines + * @constructor + * @since 3.50.0 + * + * @param {Phaser.Types.Renderer.WebGL.WebGLPipelineConfig} config - The configuration options for this pipeline. + */ +var MobilePipeline = new Class({ + + Extends: MultiPipeline, + + initialize: + + function MobilePipeline (config) + { + config.fragShader = GetFastValue(config, 'fragShader', ShaderSourceFS); + config.vertShader = GetFastValue(config, 'vertShader', ShaderSourceVS); + config.attributes = GetFastValue(config, 'attributes', [ + { + name: 'inPosition', + size: 2 + }, + { + name: 'inTexCoord', + size: 2 + }, + { + name: 'inTexId' + }, + { + name: 'inTintEffect' + }, + { + name: 'inTint', + size: 4, + type: WEBGL_CONST.UNSIGNED_BYTE, + normalized: true + } + ]); + config.forceZero = true; + + MultiPipeline.call(this, config); + }, + + /** + * Called every time the pipeline is bound by the renderer. + * Sets the shader program, vertex buffer and other resources. + * Should only be called when changing pipeline. + * + * @method Phaser.Renderer.WebGL.Pipelines.MultiPipeline#bind + * @since 3.50.0 + * + * @return {this} This WebGLPipeline instance. + */ + boot: function () + { + WebGLPipeline.prototype.boot.call(this); + + this.set1i('uMainSampler', 0); + } + +}); + +module.exports = MobilePipeline; diff --git a/src/renderer/webgl/pipelines/const.js b/src/renderer/webgl/pipelines/const.js index 85e0047c3..213de9f78 100644 --- a/src/renderer/webgl/pipelines/const.js +++ b/src/renderer/webgl/pipelines/const.js @@ -94,7 +94,17 @@ var PIPELINE_CONST = { * @const * @since 3.50.0 */ - UTILITY_PIPELINE: 'UtilityPipeline' + UTILITY_PIPELINE: 'UtilityPipeline', + + /** + * The Mobile Texture Pipeline. + * + * @name Phaser.Renderer.WebGL.Pipelines.MOBILE_PIPELINE + * @type {string} + * @const + * @since 3.60.0 + */ + MOBILE_PIPELINE: 'MobilePipeline' }; diff --git a/src/renderer/webgl/pipelines/index.js b/src/renderer/webgl/pipelines/index.js index c363d9edc..feeda2759 100644 --- a/src/renderer/webgl/pipelines/index.js +++ b/src/renderer/webgl/pipelines/index.js @@ -16,6 +16,7 @@ var Pipelines = { BitmapMaskPipeline: require('./BitmapMaskPipeline'), Events: require('./events'), LightPipeline: require('./LightPipeline'), + MobilePipeline: require('./MobilePipeline'), MultiPipeline: require('./MultiPipeline'), PointLightPipeline: require('./PointLightPipeline'), PostFXPipeline: require('./PostFXPipeline'), diff --git a/src/renderer/webgl/shaders/Mobile-frag.js b/src/renderer/webgl/shaders/Mobile-frag.js new file mode 100644 index 000000000..dd7b3f61f --- /dev/null +++ b/src/renderer/webgl/shaders/Mobile-frag.js @@ -0,0 +1,40 @@ +module.exports = [ + '#define SHADER_NAME PHASER_MOBILE_FS', + '', + '#ifdef GL_FRAGMENT_PRECISION_HIGH', + 'precision highp float;', + '#else', + 'precision mediump float;', + '#endif', + '', + 'uniform sampler2D uMainSampler;', + '', + 'varying vec2 outTexCoord;', + 'varying float outTexId;', + 'varying float outTintEffect;', + 'varying vec4 outTint;', + '', + 'void main ()', + '{', + ' vec4 texel = vec4(outTint.bgr * outTint.a, outTint.a);', + '', + ' vec4 texture = texture2D(uMainSampler, outTexCoord);', + '', + ' // Multiply texture tint', + ' vec4 color = texture * texel;', + '', + ' if (outTintEffect == 1.0)', + ' {', + ' // Solid color + texture alpha', + ' color.rgb = mix(texture.rgb, outTint.bgr * outTint.a, texture.a);', + ' }', + ' else if (outTintEffect == 2.0)', + ' {', + ' // Solid color, no texture', + ' color = texel;', + ' }', + '', + ' gl_FragColor = color;', + '}', + '' +].join('\n'); diff --git a/src/renderer/webgl/shaders/Mobile-vert.js b/src/renderer/webgl/shaders/Mobile-vert.js new file mode 100644 index 000000000..1ace7aa73 --- /dev/null +++ b/src/renderer/webgl/shaders/Mobile-vert.js @@ -0,0 +1,31 @@ +module.exports = [ + '#define SHADER_NAME PHASER_MOBILE_VS', + '', + '#ifdef GL_FRAGMENT_PRECISION_HIGH', + 'precision highp float;', + '#else', + 'precision mediump float;', + '#endif', + '', + 'uniform mat4 uProjectionMatrix;', + '', + 'attribute vec2 inPosition;', + 'attribute vec2 inTexCoord;', + 'attribute float inTexId;', + 'attribute float inTintEffect;', + 'attribute vec4 inTint;', + '', + 'varying vec2 outTexCoord;', + 'varying float outTintEffect;', + 'varying vec4 outTint;', + '', + 'void main ()', + '{', + ' gl_Position = uProjectionMatrix * vec4(inPosition, 1.0, 1.0);', + '', + ' outTexCoord = inTexCoord;', + ' outTint = inTint;', + ' outTintEffect = inTintEffect;', + '}', + '' +].join('\n'); diff --git a/src/renderer/webgl/shaders/src/Mobile.frag b/src/renderer/webgl/shaders/src/Mobile.frag new file mode 100644 index 000000000..de817e6cc --- /dev/null +++ b/src/renderer/webgl/shaders/src/Mobile.frag @@ -0,0 +1,37 @@ +#define SHADER_NAME PHASER_MOBILE_FS + +#ifdef GL_FRAGMENT_PRECISION_HIGH +precision highp float; +#else +precision mediump float; +#endif + +uniform sampler2D uMainSampler; + +varying vec2 outTexCoord; +varying float outTexId; +varying float outTintEffect; +varying vec4 outTint; + +void main () +{ + vec4 texel = vec4(outTint.bgr * outTint.a, outTint.a); + + vec4 texture = texture2D(uMainSampler, outTexCoord); + + // Multiply texture tint + vec4 color = texture * texel; + + if (outTintEffect == 1.0) + { + // Solid color + texture alpha + color.rgb = mix(texture.rgb, outTint.bgr * outTint.a, texture.a); + } + else if (outTintEffect == 2.0) + { + // Solid color, no texture + color = texel; + } + + gl_FragColor = color; +} diff --git a/src/renderer/webgl/shaders/src/Mobile.vert b/src/renderer/webgl/shaders/src/Mobile.vert new file mode 100644 index 000000000..2ca97de47 --- /dev/null +++ b/src/renderer/webgl/shaders/src/Mobile.vert @@ -0,0 +1,28 @@ +#define SHADER_NAME PHASER_MOBILE_VS + +#ifdef GL_FRAGMENT_PRECISION_HIGH +precision highp float; +#else +precision mediump float; +#endif + +uniform mat4 uProjectionMatrix; + +attribute vec2 inPosition; +attribute vec2 inTexCoord; +attribute float inTexId; +attribute float inTintEffect; +attribute vec4 inTint; + +varying vec2 outTexCoord; +varying float outTintEffect; +varying vec4 outTint; + +void main () +{ + gl_Position = uProjectionMatrix * vec4(inPosition, 1.0, 1.0); + + outTexCoord = inTexCoord; + outTint = inTint; + outTintEffect = inTintEffect; +}