UNPKG

phaser

Version:

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

542 lines (480 loc) 17.6 kB
/** * @author Benjamin D. Richards <benjamindrichards@gmail.com> * @copyright 2013-2026 Phaser Studio Inc. * @license {@link https://opensource.org/licenses/MIT|MIT License} */ var Class = require('../../../utils/Class'); var IsSizePowerOfTwo = require('../../../math/pow2/IsSizePowerOfTwo'); /** * @classdesc * Wrapper for a WebGL texture, containing all the information that was used * to create it. * * A WebGLTexture should never be exposed outside the WebGLRenderer, * so the WebGLRenderer can handle context loss and other events * without other systems having to be aware of it. * Always use WebGLTextureWrapper instead. * * @class WebGLTextureWrapper * @memberof Phaser.Renderer.WebGL.Wrappers * @constructor * @since 3.80.0 * * @param {Phaser.Renderer.WebGL.WebGLRenderer} renderer - The WebGLRenderer instance that owns this wrapper. * @param {number} mipLevel - Mip level of the texture. * @param {number} minFilter - Filtering of the texture. * @param {number} magFilter - Filtering of the texture. * @param {number} wrapT - Wrapping mode of the texture. * @param {number} wrapS - Wrapping mode of the texture. * @param {number} format - Which format does the texture use. * @param {?object} pixels - The pixel data used to populate the texture. Can be a Canvas, Video, ImageData, Uint8Array, or compressed texture object. * @param {number} width - Width of the texture in pixels. * @param {number} height - Height of the texture in pixels. * @param {boolean} [pma=true] - Does the texture have premultiplied alpha? * @param {boolean} [forceSize=false] - If `true` it will use the width and height passed to this method, regardless of the pixels dimension. * @param {boolean} [flipY=true] - Sets the `UNPACK_FLIP_Y_WEBGL` flag the WebGL Texture uses during upload. */ var WebGLTextureWrapper = new Class({ initialize: function WebGLTextureWrapper (renderer, mipLevel, minFilter, magFilter, wrapT, wrapS, format, pixels, width, height, pma, forceSize, flipY) { if (flipY === undefined) { flipY = true; } /** * The WebGLRenderer this WebGLTexture belongs to. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#renderer * @type {Phaser.Renderer.WebGL.WebGLRenderer} * @since 4.0.0 */ this.renderer = renderer; /** * The WebGLTexture that this wrapper is wrapping. * * This property could change at any time. * Therefore, you should never store a reference to this value. * It should only be passed directly to the WebGL API for drawing. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#webGLTexture * @type {?WebGLTexture} * @default null * @since 3.80.0 */ this.webGLTexture = null; /** * Whether this is used as a RenderTexture. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#isRenderTexture * @type {boolean} * @default false * @since 3.80.0 */ this.isRenderTexture = false; /** * Mip level of the texture. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#mipLevel * @type {number} * @since 3.80.0 */ this.mipLevel = mipLevel; /** * The minification filter for the texture, as a WebGL filter constant * (e.g. `gl.LINEAR` or `gl.NEAREST`). Applied when the texture is * scaled down. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#minFilter * @type {number} * @since 3.80.0 */ this.minFilter = minFilter; /** * The magnification filter for the texture, as a WebGL filter constant * (e.g. `gl.LINEAR` or `gl.NEAREST`). Applied when the texture is * scaled up. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#magFilter * @type {number} * @since 3.80.0 */ this.magFilter = magFilter; /** * The wrapping mode for the T (vertical) axis of the texture, * as a WebGL wrap constant (e.g. `gl.REPEAT` or `gl.CLAMP_TO_EDGE`). * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#wrapT * @type {number} * @since 3.80.0 */ this.wrapT = wrapT; /** * The wrapping mode for the S (horizontal) axis of the texture, * as a WebGL wrap constant (e.g. `gl.REPEAT` or `gl.CLAMP_TO_EDGE`). * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#wrapS * @type {number} * @since 3.80.0 */ this.wrapS = wrapS; /** * The WebGL pixel format of the texture (e.g. `gl.RGBA` or `gl.RGB`). * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#format * @type {number} * @since 3.80.0 */ this.format = format; /** * Pixel data. This is the source data used to create the WebGLTexture. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#pixels * @type {?object} * @since 3.80.0 */ this.pixels = pixels; /** * Width of the texture in pixels. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#width * @type {number} * @since 3.80.0 */ this.width = width; /** * Height of the texture in pixels. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#height * @type {number} * @since 3.80.0 */ this.height = height; /** * Does the texture have premultiplied alpha? * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#pma * @type {boolean} * @since 3.80.0 */ this.pma = (pma === undefined || pma === null) ? true : pma; /** * Whether to use the width and height properties, regardless of pixel dimensions. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#forceSize * @type {boolean} * @since 3.80.0 */ this.forceSize = !!forceSize; /** * Sets the `UNPACK_FLIP_Y_WEBGL` flag the WebGL Texture uses during upload. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#flipY * @type {boolean} * @since 3.80.0 */ this.flipY = !!flipY; /** * Metadata for the SpectorJS tool, set if debug is enabled. * You should set this via the `spectorMetadata` property, * which will update the `__SPECTOR_Metadata` property on the WebGLTexture. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#__SPECTOR_Metadata * @type {object} * @private * @since 3.80.0 */ // eslint-disable-next-line camelcase this.__SPECTOR_Metadata = {}; /** * The texture unit this texture will be bound to in the current * rendering batch. * * This should be set by the batcher. It is a quick way to tell whether * this texture has been included in the batch. If it is -1, it has not. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#batchUnit * @type {number} * @since 4.0.0 * @default -1 */ this.batchUnit = -1; /** * Whether the mipmaps should be regenerated. * This is only relevant to dynamic textures and framebuffers with * rapidly changing content. * The process which changes the content should set this flag. * When the texture is next bound, the WebGLTextureUnitsWrapper * should trigger the `generateMipmap` method. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#needsMipmapRegeneration * @type {boolean} * @since 4.1.0 * @default false */ this.needsMipmapRegeneration = false; this.createResource(); }, /** * Creates a WebGLTexture from the given parameters. * * This is called automatically by the constructor. It may also be * called again if the WebGLTexture needs re-creating. * * @method Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#createResource * @since 3.80.0 */ createResource: function () { var gl = this.renderer.gl; if (gl.isContextLost()) { // GL state can't be updated right now. // `createResource` will run when the context is restored. return; } if (this.pixels instanceof WebGLTextureWrapper) { // Use the source texture directly. this.webGLTexture = this.pixels.webGLTexture; return; } var texture = gl.createTexture(); // Set Spector metadata. // eslint-disable-next-line camelcase texture.__SPECTOR_Metadata = this.__SPECTOR_Metadata; // Assign the texture to our wrapper. this.webGLTexture = texture; this._processTexture(); }, /** * Resizes the WebGLTexture to the new dimensions. * This will destroy the contents of the texture. * * Wrap mode will be updated: REPEAT if the new size is power-of-two, * CLAMP_TO_EDGE if not. * * Texture minification filter will be updated: * - Uses mipmap settings from game config if: * - Size is power-of-two * - There is a mipmap setting * - This is not a render texture, OR mipmap regeneration is enabled * - Uses regular texture filter otherwise. * * @method Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#resize * @since 4.0.0 * @param {number} width - The new width of the WebGLTexture. * @param {number} height - The new height of the WebGLTexture. */ resize: function (width, height) { if (this.width === width && this.height === height) { return; } this.width = width; this.height = height; var renderer = this.renderer; var gl = renderer.gl; var isPOT = IsSizePowerOfTwo(width, height); // Repeat modes if (isPOT) { this.wrapS = gl.REPEAT; this.wrapT = gl.REPEAT; } else { this.wrapS = gl.CLAMP_TO_EDGE; this.wrapT = gl.CLAMP_TO_EDGE; } // Mipmap modes if (isPOT && renderer.mipmapFilter && (!this.isRenderTexture || renderer.config.mipmapRegeneration)) { this.minFilter = renderer.mipmapFilter; } else if (renderer.config.antialias) { this.minFilter = gl.LINEAR; } else { this.minFilter = gl.NEAREST; } this._processTexture(); }, /** * Updates the WebGLTexture from an updated source. * * This should only be used when the source is a Canvas or Video element. * * Because textures usually change into something complex and unique, * this method forces all properties to update without checking. * * @method Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#update * @since 3.80.0 * * @param {?object} source - The source to update the WebGLTexture with. * @param {number} width - The new width of the WebGLTexture. * @param {number} height - The new height of the WebGLTexture. * @param {boolean} flipY - Should the WebGLTexture set `UNPACK_FLIP_Y_WEBGL`? * @param {number} wrapS - The new wrapping mode for the WebGLTexture. * @param {number} wrapT - The new wrapping mode for the WebGLTexture. * @param {number} minFilter - The new minification filter for the WebGLTexture. * @param {number} magFilter - The new magnification filter for the WebGLTexture. * @param {number} format - The new format for the WebGLTexture. */ update: function (source, width, height, flipY, wrapS, wrapT, minFilter, magFilter, format) { if (width === 0 || height === 0) { return; } // Assume that the source might change. this.pixels = source; this.width = width; this.height = height; this.flipY = flipY; this.wrapS = wrapS; this.wrapT = wrapT; this.minFilter = minFilter; this.magFilter = magFilter; this.format = format; this._processTexture(); }, /** * Set all parameters of this WebGLTexture per the stored values. * * @function Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#_processTexture * @protected * @since 4.0.0 * @ignore */ _processTexture: function () { var gl = this.renderer.gl; this.renderer.glTextureUnits.bind(this, 0); this.renderer.glWrapper.updateTexturing({ texturing: { flipY: this.flipY, premultiplyAlpha: this.pma } }); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.minFilter); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.magFilter); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, this.wrapS); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, this.wrapT); var pixels = this.pixels; var mipLevel = this.mipLevel; var width = this.width; var height = this.height; var format = this.format; if (pixels === null || pixels === undefined) { gl.texImage2D(gl.TEXTURE_2D, mipLevel, format, width, height, 0, format, gl.UNSIGNED_BYTE, null); this.generateMipmap(); } else if (pixels.compressed) { width = pixels.width; height = pixels.height; for (var i = 0; i < pixels.mipmaps.length; i++) { gl.compressedTexImage2D(gl.TEXTURE_2D, i, pixels.internalFormat, pixels.mipmaps[i].width, pixels.mipmaps[i].height, 0, pixels.mipmaps[i].data); } } else if (pixels instanceof Uint8Array) { gl.texImage2D(gl.TEXTURE_2D, mipLevel, format, width, height, 0, format, gl.UNSIGNED_BYTE, pixels); this.generateMipmap(); } else { if (!this.forceSize) { width = pixels.width; height = pixels.height; } gl.texImage2D(gl.TEXTURE_2D, mipLevel, format, format, gl.UNSIGNED_BYTE, pixels); this.generateMipmap(); } }, /** * Return whether the texture is set to use a mipmap minification filter. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#isMipmap * @since 4.1.0 * @returns {boolean} Whether the texture is set to use a mipmap minification filter. */ isMipmap: function () { var minFilter = this.minFilter; var gl = this.renderer.gl; return minFilter === gl.NEAREST_MIPMAP_NEAREST || minFilter === gl.LINEAR_MIPMAP_NEAREST || minFilter === gl.LINEAR_MIPMAP_LINEAR || minFilter === gl.NEAREST_MIPMAP_LINEAR; }, /** * Generate mipmap levels for the texture. * This method is called internally. * * Mipmaps are only generated if this texture is mipmap-enabled * and has a size which is a power of two. * Otherwise this function returns without side effects. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#generateMipmap * @since 4.1.0 */ generateMipmap: function () { this.needsMipmapRegeneration = false; var gl = this.renderer.gl; if (!(this.isMipmap() && IsSizePowerOfTwo(this.width, this.height))) { return; } gl.generateMipmap(gl.TEXTURE_2D); }, /** * The `__SPECTOR_Metadata` property of the `WebGLTexture`, * used to add extra data to the debug SpectorJS integration. * * @name Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#spectorMetadata * @type {object} * @since 3.80.0 */ spectorMetadata: { get: function () { return this.__SPECTOR_Metadata; }, set: function (value) { // eslint-disable-next-line camelcase this.__SPECTOR_Metadata = value; // eslint-disable-next-line camelcase this.webGLTexture.__SPECTOR_Metadata = value; } }, /** * Deletes the WebGLTexture from the GPU, if it has not been already. * * @method Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper#destroy * @since 3.80.0 */ destroy: function () { if (this.webGLTexture === null) { return; } if (!(this.pixels instanceof WebGLTextureWrapper)) { // Do not delete a texture that belongs to another wrapper. this.renderer.gl.deleteTexture(this.webGLTexture); } this.pixels = null; this.webGLTexture = null; this.renderer = null; } }); module.exports = WebGLTextureWrapper;