UNPKG

9.7 kBJavaScriptView Raw
1import { SAMPLER_TYPES, TYPES, MIPMAP_MODES, WRAP_MODES, SCALE_MODES } from "@pixi/constants";
2import { ExtensionType, extensions } from "@pixi/extensions";
3import { removeItems } from "@pixi/utils";
4import { BaseTexture } from "./BaseTexture.mjs";
5import { GLTexture } from "./GLTexture.mjs";
6import { mapInternalFormatToSamplerType } from "./utils/mapInternalFormatToSamplerType.mjs";
7import { mapTypeAndFormatToInternalFormat } from "./utils/mapTypeAndFormatToInternalFormat.mjs";
8class TextureSystem {
9 /**
10 * @param renderer - The renderer this system works for.
11 */
12 constructor(renderer) {
13 this.renderer = renderer, this.boundTextures = [], this.currentLocation = -1, this.managedTextures = [], this._unknownBoundTextures = !1, this.unknownTexture = new BaseTexture(), this.hasIntegerTextures = !1;
14 }
15 /** Sets up the renderer context and necessary buffers. */
16 contextChange() {
17 const gl = this.gl = this.renderer.gl;
18 this.CONTEXT_UID = this.renderer.CONTEXT_UID, this.webGLVersion = this.renderer.context.webGLVersion, this.internalFormats = mapTypeAndFormatToInternalFormat(gl), this.samplerTypes = mapInternalFormatToSamplerType(gl);
19 const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
20 this.boundTextures.length = maxTextures;
21 for (let i = 0; i < maxTextures; i++)
22 this.boundTextures[i] = null;
23 this.emptyTextures = {};
24 const emptyTexture2D = new GLTexture(gl.createTexture());
25 gl.bindTexture(gl.TEXTURE_2D, emptyTexture2D.texture), gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4)), this.emptyTextures[gl.TEXTURE_2D] = emptyTexture2D, this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(gl.createTexture()), gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture);
26 for (let i = 0; i < 6; i++)
27 gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
28 gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR), gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
29 for (let i = 0; i < this.boundTextures.length; i++)
30 this.bind(null, i);
31 }
32 /**
33 * Bind a texture to a specific location
34 *
35 * If you want to unbind something, please use `unbind(texture)` instead of `bind(null, textureLocation)`
36 * @param texture - Texture to bind
37 * @param [location=0] - Location to bind at
38 */
39 bind(texture, location = 0) {
40 const { gl } = this;
41 if (texture = texture?.castToBaseTexture(), texture?.valid && !texture.parentTextureArray) {
42 texture.touched = this.renderer.textureGC.count;
43 const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture);
44 this.boundTextures[location] !== texture && (this.currentLocation !== location && (this.currentLocation = location, gl.activeTexture(gl.TEXTURE0 + location)), gl.bindTexture(texture.target, glTexture.texture)), glTexture.dirtyId !== texture.dirtyId ? (this.currentLocation !== location && (this.currentLocation = location, gl.activeTexture(gl.TEXTURE0 + location)), this.updateTexture(texture)) : glTexture.dirtyStyleId !== texture.dirtyStyleId && this.updateTextureStyle(texture), this.boundTextures[location] = texture;
45 } else
46 this.currentLocation !== location && (this.currentLocation = location, gl.activeTexture(gl.TEXTURE0 + location)), gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture), this.boundTextures[location] = null;
47 }
48 /** Resets texture location and bound textures Actual `bind(null, i)` calls will be performed at next `unbind()` call */
49 reset() {
50 this._unknownBoundTextures = !0, this.hasIntegerTextures = !1, this.currentLocation = -1;
51 for (let i = 0; i < this.boundTextures.length; i++)
52 this.boundTextures[i] = this.unknownTexture;
53 }
54 /**
55 * Unbind a texture.
56 * @param texture - Texture to bind
57 */
58 unbind(texture) {
59 const { gl, boundTextures } = this;
60 if (this._unknownBoundTextures) {
61 this._unknownBoundTextures = !1;
62 for (let i = 0; i < boundTextures.length; i++)
63 boundTextures[i] === this.unknownTexture && this.bind(null, i);
64 }
65 for (let i = 0; i < boundTextures.length; i++)
66 boundTextures[i] === texture && (this.currentLocation !== i && (gl.activeTexture(gl.TEXTURE0 + i), this.currentLocation = i), gl.bindTexture(texture.target, this.emptyTextures[texture.target].texture), boundTextures[i] = null);
67 }
68 /**
69 * Ensures that current boundTextures all have FLOAT sampler type,
70 * see {@link PIXI.SAMPLER_TYPES} for explanation.
71 * @param maxTextures - number of locations to check
72 */
73 ensureSamplerType(maxTextures) {
74 const { boundTextures, hasIntegerTextures, CONTEXT_UID } = this;
75 if (hasIntegerTextures)
76 for (let i = maxTextures - 1; i >= 0; --i) {
77 const tex = boundTextures[i];
78 tex && tex._glTextures[CONTEXT_UID].samplerType !== SAMPLER_TYPES.FLOAT && this.renderer.texture.unbind(tex);
79 }
80 }
81 /**
82 * Initialize a texture
83 * @private
84 * @param texture - Texture to initialize
85 */
86 initTexture(texture) {
87 const glTexture = new GLTexture(this.gl.createTexture());
88 return glTexture.dirtyId = -1, texture._glTextures[this.CONTEXT_UID] = glTexture, this.managedTextures.push(texture), texture.on("dispose", this.destroyTexture, this), glTexture;
89 }
90 initTextureType(texture, glTexture) {
91 glTexture.internalFormat = this.internalFormats[texture.type]?.[texture.format] ?? texture.format, glTexture.samplerType = this.samplerTypes[glTexture.internalFormat] ?? SAMPLER_TYPES.FLOAT, this.webGLVersion === 2 && texture.type === TYPES.HALF_FLOAT ? glTexture.type = this.gl.HALF_FLOAT : glTexture.type = texture.type;
92 }
93 /**
94 * Update a texture
95 * @private
96 * @param {PIXI.BaseTexture} texture - Texture to initialize
97 */
98 updateTexture(texture) {
99 const glTexture = texture._glTextures[this.CONTEXT_UID];
100 if (!glTexture)
101 return;
102 const renderer = this.renderer;
103 if (this.initTextureType(texture, glTexture), texture.resource?.upload(renderer, texture, glTexture))
104 glTexture.samplerType !== SAMPLER_TYPES.FLOAT && (this.hasIntegerTextures = !0);
105 else {
106 const width = texture.realWidth, height = texture.realHeight, gl = renderer.gl;
107 (glTexture.width !== width || glTexture.height !== height || glTexture.dirtyId < 0) && (glTexture.width = width, glTexture.height = height, gl.texImage2D(
108 texture.target,
109 0,
110 glTexture.internalFormat,
111 width,
112 height,
113 0,
114 texture.format,
115 glTexture.type,
116 null
117 ));
118 }
119 texture.dirtyStyleId !== glTexture.dirtyStyleId && this.updateTextureStyle(texture), glTexture.dirtyId = texture.dirtyId;
120 }
121 /**
122 * Deletes the texture from WebGL
123 * @private
124 * @param texture - the texture to destroy
125 * @param [skipRemove=false] - Whether to skip removing the texture from the TextureManager.
126 */
127 destroyTexture(texture, skipRemove) {
128 const { gl } = this;
129 if (texture = texture.castToBaseTexture(), texture._glTextures[this.CONTEXT_UID] && (this.unbind(texture), gl.deleteTexture(texture._glTextures[this.CONTEXT_UID].texture), texture.off("dispose", this.destroyTexture, this), delete texture._glTextures[this.CONTEXT_UID], !skipRemove)) {
130 const i = this.managedTextures.indexOf(texture);
131 i !== -1 && removeItems(this.managedTextures, i, 1);
132 }
133 }
134 /**
135 * Update texture style such as mipmap flag
136 * @private
137 * @param {PIXI.BaseTexture} texture - Texture to update
138 */
139 updateTextureStyle(texture) {
140 const glTexture = texture._glTextures[this.CONTEXT_UID];
141 glTexture && ((texture.mipmap === MIPMAP_MODES.POW2 || this.webGLVersion !== 2) && !texture.isPowerOfTwo ? glTexture.mipmap = !1 : glTexture.mipmap = texture.mipmap >= 1, this.webGLVersion !== 2 && !texture.isPowerOfTwo ? glTexture.wrapMode = WRAP_MODES.CLAMP : glTexture.wrapMode = texture.wrapMode, texture.resource?.style(this.renderer, texture, glTexture) || this.setStyle(texture, glTexture), glTexture.dirtyStyleId = texture.dirtyStyleId);
142 }
143 /**
144 * Set style for texture
145 * @private
146 * @param texture - Texture to update
147 * @param glTexture
148 */
149 setStyle(texture, glTexture) {
150 const gl = this.gl;
151 if (glTexture.mipmap && texture.mipmap !== MIPMAP_MODES.ON_MANUAL && gl.generateMipmap(texture.target), gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, glTexture.wrapMode), gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, glTexture.wrapMode), glTexture.mipmap) {
152 gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode === SCALE_MODES.LINEAR ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST);
153 const anisotropicExt = this.renderer.context.extensions.anisotropicFiltering;
154 if (anisotropicExt && texture.anisotropicLevel > 0 && texture.scaleMode === SCALE_MODES.LINEAR) {
155 const level = Math.min(texture.anisotropicLevel, gl.getParameter(anisotropicExt.MAX_TEXTURE_MAX_ANISOTROPY_EXT));
156 gl.texParameterf(texture.target, anisotropicExt.TEXTURE_MAX_ANISOTROPY_EXT, level);
157 }
158 } else
159 gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode === SCALE_MODES.LINEAR ? gl.LINEAR : gl.NEAREST);
160 gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode === SCALE_MODES.LINEAR ? gl.LINEAR : gl.NEAREST);
161 }
162 destroy() {
163 this.renderer = null;
164 }
165}
166TextureSystem.extension = {
167 type: ExtensionType.RendererSystem,
168 name: "texture"
169};
170extensions.add(TextureSystem);
171export {
172 TextureSystem
173};
174//# sourceMappingURL=TextureSystem.mjs.map