diff --git a/.eslintrc.json b/.eslintrc.json index b8d7d05..39fca30 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -162,7 +162,7 @@ "keyword-spacing": [1, { "before": true, "after": true }], "linebreak-style": [1, "unix"], "lines-around-comment": 0, - "max-depth": [1, 5], + "max-depth": [1, 6], "max-len": [1, { "code": 125, "tabWidth": 4 }], "max-lines": 0, "max-nested-callbacks": [1, { "max": 5 }], diff --git a/.eslintrc.json b/.eslintrc.json index b8d7d05..39fca30 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -162,7 +162,7 @@ "keyword-spacing": [1, { "before": true, "after": true }], "linebreak-style": [1, "unix"], "lines-around-comment": 0, - "max-depth": [1, 5], + "max-depth": [1, 6], "max-len": [1, { "code": 125, "tabWidth": 4 }], "max-lines": 0, "max-nested-callbacks": [1, { "max": 5 }], diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index 83fce7d..f0c85f2 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -61,11 +61,16 @@ * Updates and/or Creates a WebGL texture for the renderer's context. * * @param {PIXI.BaseTexture|PIXI.Texture} texture - the texture to update + * @param {Number} location - the location the texture will be bound to. * @return {GLTexture} The gl texture. */ - updateTexture(texture) + updateTexture(texture, location) { - texture = texture.baseTexture || texture; + // assume it good! + // texture = texture.baseTexture || texture; + location = location || 0; + + const gl = this.gl; const isRenderTexture = !!texture._glRenderTargets; @@ -74,6 +79,8 @@ return null; } + gl.activeTexture(gl.TEXTURE0 + location); + let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; if (!glTexture) @@ -94,7 +101,8 @@ } else { - glTexture = new GLTexture(this.gl); + glTexture = new GLTexture(this.gl, null, null, null, null); + glTexture.bind(location); glTexture.premultiplyAlpha = true; glTexture.upload(texture.source); } @@ -150,6 +158,8 @@ glTexture.upload(texture.source); } + this.renderer.boundTextures[location] = texture; + return glTexture; } @@ -170,6 +180,8 @@ if (texture._glTextures[this.renderer.CONTEXT_UID]) { + this.renderer.unbindTexture(texture); + texture._glTextures[this.renderer.CONTEXT_UID].destroy(); texture.off('update', this.updateTexture, this); texture.off('dispose', this.destroyTexture, this); diff --git a/.eslintrc.json b/.eslintrc.json index b8d7d05..39fca30 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -162,7 +162,7 @@ "keyword-spacing": [1, { "before": true, "after": true }], "linebreak-style": [1, "unix"], "lines-around-comment": 0, - "max-depth": [1, 5], + "max-depth": [1, 6], "max-len": [1, { "code": 125, "tabWidth": 4 }], "max-lines": 0, "max-nested-callbacks": [1, { "max": 5 }], diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index 83fce7d..f0c85f2 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -61,11 +61,16 @@ * Updates and/or Creates a WebGL texture for the renderer's context. * * @param {PIXI.BaseTexture|PIXI.Texture} texture - the texture to update + * @param {Number} location - the location the texture will be bound to. * @return {GLTexture} The gl texture. */ - updateTexture(texture) + updateTexture(texture, location) { - texture = texture.baseTexture || texture; + // assume it good! + // texture = texture.baseTexture || texture; + location = location || 0; + + const gl = this.gl; const isRenderTexture = !!texture._glRenderTargets; @@ -74,6 +79,8 @@ return null; } + gl.activeTexture(gl.TEXTURE0 + location); + let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; if (!glTexture) @@ -94,7 +101,8 @@ } else { - glTexture = new GLTexture(this.gl); + glTexture = new GLTexture(this.gl, null, null, null, null); + glTexture.bind(location); glTexture.premultiplyAlpha = true; glTexture.upload(texture.source); } @@ -150,6 +158,8 @@ glTexture.upload(texture.source); } + this.renderer.boundTextures[location] = texture; + return glTexture; } @@ -170,6 +180,8 @@ if (texture._glTextures[this.renderer.CONTEXT_UID]) { + this.renderer.unbindTexture(texture); + texture._glTextures[this.renderer.CONTEXT_UID].destroy(); texture.off('update', this.updateTexture, this); texture.off('dispose', this.destroyTexture, this); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index e921d6d..04ffda5 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -5,6 +5,7 @@ import RenderTarget from './utils/RenderTarget'; import ObjectRenderer from './utils/ObjectRenderer'; import TextureManager from './TextureManager'; +import BaseTexture from '../../textures/BaseTexture'; import TextureGarbageCollector from './TextureGarbageCollector'; import WebGLState from './WebGLState'; import mapWebGLDrawModesToPixi from './utils/mapWebGLDrawModesToPixi'; @@ -52,6 +53,7 @@ constructor(width, height, options = {}) { super('WebGL', width, height, options); + /** * The type of this renderer as a standardised const * @@ -137,8 +139,13 @@ this.renderingToScreen = true; - this._initContext(); + /** + * Holds the current state of textures bound to the GPU. + * @type {Array} + */ + this.boundTextures = null; + this._initContext(); /** * Manages the filters. * @@ -161,7 +168,6 @@ * @member {PIXI.RenderTarget} */ this._activeRenderTarget = null; - this._activeTextureLocation = 999; this._activeTexture = null; this.setBlendMode(0); @@ -182,6 +188,11 @@ gl.getExtension('WEBGL_lose_context').restoreContext(); } + const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); + + this.boundTextures = new Array(maxTextures); + this.emptyTextures = new Array(maxTextures); + // create a texture manager... this.textureManager = new TextureManager(this); this.textureGC = new TextureGarbageCollector(this); @@ -193,6 +204,24 @@ this.bindRenderTarget(this.rootRenderTarget); + // now lets fill up the textures with empty ones! + const emptyGLTexture = new glCore.GLTexture.fromData(gl, null, 1, 1); + + const tempObj = { _glTextures: {} }; + + tempObj._glTextures[this.CONTEXT_UID] = {}; + + for (let i = 0; i < maxTextures; i++) + { + const empty = new BaseTexture(); + + empty._glTextures[this.CONTEXT_UID] = emptyGLTexture; + + this.boundTextures[i] = tempObj; + this.emptyTextures[i] = empty; + this.bindTexture(null, i); + } + this.emit('context', gl); // setup the width/height properties and gl viewport @@ -354,20 +383,14 @@ if (renderTexture) { const baseTexture = renderTexture.baseTexture; - const gl = this.gl; if (!baseTexture._glRenderTargets[this.CONTEXT_UID]) { - this.textureManager.updateTexture(baseTexture); - gl.bindTexture(gl.TEXTURE_2D, null); + // bind the current texture + this.textureManager.updateTexture(baseTexture, 0); } - else - { - // the texture needs to be unbound if its being rendererd too.. - this._activeTextureLocation = baseTexture._id; - gl.activeTexture(gl.TEXTURE0 + baseTexture._id); - gl.bindTexture(gl.TEXTURE_2D, null); - } + + this.unbindTexture(baseTexture); renderTarget = baseTexture._glRenderTargets[this.CONTEXT_UID]; renderTarget.setFrame(renderTexture.frame); @@ -435,33 +458,65 @@ * @param {number} location - the texture location * @return {PIXI.WebGLRenderer} Returns itself. */ - bindTexture(texture, location = 0) + bindTexture(texture, location) { + location = location || 0; + + texture = texture || this.emptyTextures[location]; + texture = texture.baseTexture || texture; const gl = this.gl; + const glTexture = texture._glTextures[this.CONTEXT_UID]; - // TODO test perf of cache? + texture.touched = this.textureGC.count; - if (this._activeTextureLocation !== location)// + if (this.boundTextures[location] === texture) { - this._activeTextureLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); + return this; } - // TODO - can we cache this texture too? - this._activeTexture = texture; + // TODO - what if we bind a texture that is already bound? + // Should be ok for now.. - if (!texture._glTextures[this.CONTEXT_UID]) + if (!glTexture) { // this will also bind the texture.. - this.textureManager.updateTexture(texture); + this.textureManager.updateTexture(texture, location); } else { - texture.touched = this.textureGC.count; // bind the current texture - texture._glTextures[this.CONTEXT_UID].bind(); + this.boundTextures[location] = texture; + + gl.activeTexture(gl.TEXTURE0 + location); + gl.bindTexture(gl.TEXTURE_2D, glTexture.texture); + } + + return this; + } + + /** + * unbinds the texture ... + * + * @param {PIXI.Texture} texture - the texture to unbind + * @return {PIXI.WebGLRenderer} Returns itself. + */ + unbindTexture(texture) + { + const gl = this.gl; + + texture = texture.baseTexture || texture; + + for (let i = 0; i < this.boundTextures.length; i++) + { + if (this.boundTextures[i] === texture) + { + this.boundTextures[i] = this.emptyTextures[i]; + + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[i]._glTextures[this.CONTEXT_UID].texture); + } } return this; @@ -488,7 +543,6 @@ this._activeShader = null; this._activeRenderTarget = this.rootRenderTarget; - this._activeTextureLocation = 999; this._activeTexture = null; // bind the main frame buffer (the screen); diff --git a/.eslintrc.json b/.eslintrc.json index b8d7d05..39fca30 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -162,7 +162,7 @@ "keyword-spacing": [1, { "before": true, "after": true }], "linebreak-style": [1, "unix"], "lines-around-comment": 0, - "max-depth": [1, 5], + "max-depth": [1, 6], "max-len": [1, { "code": 125, "tabWidth": 4 }], "max-lines": 0, "max-nested-callbacks": [1, { "max": 5 }], diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index 83fce7d..f0c85f2 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -61,11 +61,16 @@ * Updates and/or Creates a WebGL texture for the renderer's context. * * @param {PIXI.BaseTexture|PIXI.Texture} texture - the texture to update + * @param {Number} location - the location the texture will be bound to. * @return {GLTexture} The gl texture. */ - updateTexture(texture) + updateTexture(texture, location) { - texture = texture.baseTexture || texture; + // assume it good! + // texture = texture.baseTexture || texture; + location = location || 0; + + const gl = this.gl; const isRenderTexture = !!texture._glRenderTargets; @@ -74,6 +79,8 @@ return null; } + gl.activeTexture(gl.TEXTURE0 + location); + let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; if (!glTexture) @@ -94,7 +101,8 @@ } else { - glTexture = new GLTexture(this.gl); + glTexture = new GLTexture(this.gl, null, null, null, null); + glTexture.bind(location); glTexture.premultiplyAlpha = true; glTexture.upload(texture.source); } @@ -150,6 +158,8 @@ glTexture.upload(texture.source); } + this.renderer.boundTextures[location] = texture; + return glTexture; } @@ -170,6 +180,8 @@ if (texture._glTextures[this.renderer.CONTEXT_UID]) { + this.renderer.unbindTexture(texture); + texture._glTextures[this.renderer.CONTEXT_UID].destroy(); texture.off('update', this.updateTexture, this); texture.off('dispose', this.destroyTexture, this); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index e921d6d..04ffda5 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -5,6 +5,7 @@ import RenderTarget from './utils/RenderTarget'; import ObjectRenderer from './utils/ObjectRenderer'; import TextureManager from './TextureManager'; +import BaseTexture from '../../textures/BaseTexture'; import TextureGarbageCollector from './TextureGarbageCollector'; import WebGLState from './WebGLState'; import mapWebGLDrawModesToPixi from './utils/mapWebGLDrawModesToPixi'; @@ -52,6 +53,7 @@ constructor(width, height, options = {}) { super('WebGL', width, height, options); + /** * The type of this renderer as a standardised const * @@ -137,8 +139,13 @@ this.renderingToScreen = true; - this._initContext(); + /** + * Holds the current state of textures bound to the GPU. + * @type {Array} + */ + this.boundTextures = null; + this._initContext(); /** * Manages the filters. * @@ -161,7 +168,6 @@ * @member {PIXI.RenderTarget} */ this._activeRenderTarget = null; - this._activeTextureLocation = 999; this._activeTexture = null; this.setBlendMode(0); @@ -182,6 +188,11 @@ gl.getExtension('WEBGL_lose_context').restoreContext(); } + const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); + + this.boundTextures = new Array(maxTextures); + this.emptyTextures = new Array(maxTextures); + // create a texture manager... this.textureManager = new TextureManager(this); this.textureGC = new TextureGarbageCollector(this); @@ -193,6 +204,24 @@ this.bindRenderTarget(this.rootRenderTarget); + // now lets fill up the textures with empty ones! + const emptyGLTexture = new glCore.GLTexture.fromData(gl, null, 1, 1); + + const tempObj = { _glTextures: {} }; + + tempObj._glTextures[this.CONTEXT_UID] = {}; + + for (let i = 0; i < maxTextures; i++) + { + const empty = new BaseTexture(); + + empty._glTextures[this.CONTEXT_UID] = emptyGLTexture; + + this.boundTextures[i] = tempObj; + this.emptyTextures[i] = empty; + this.bindTexture(null, i); + } + this.emit('context', gl); // setup the width/height properties and gl viewport @@ -354,20 +383,14 @@ if (renderTexture) { const baseTexture = renderTexture.baseTexture; - const gl = this.gl; if (!baseTexture._glRenderTargets[this.CONTEXT_UID]) { - this.textureManager.updateTexture(baseTexture); - gl.bindTexture(gl.TEXTURE_2D, null); + // bind the current texture + this.textureManager.updateTexture(baseTexture, 0); } - else - { - // the texture needs to be unbound if its being rendererd too.. - this._activeTextureLocation = baseTexture._id; - gl.activeTexture(gl.TEXTURE0 + baseTexture._id); - gl.bindTexture(gl.TEXTURE_2D, null); - } + + this.unbindTexture(baseTexture); renderTarget = baseTexture._glRenderTargets[this.CONTEXT_UID]; renderTarget.setFrame(renderTexture.frame); @@ -435,33 +458,65 @@ * @param {number} location - the texture location * @return {PIXI.WebGLRenderer} Returns itself. */ - bindTexture(texture, location = 0) + bindTexture(texture, location) { + location = location || 0; + + texture = texture || this.emptyTextures[location]; + texture = texture.baseTexture || texture; const gl = this.gl; + const glTexture = texture._glTextures[this.CONTEXT_UID]; - // TODO test perf of cache? + texture.touched = this.textureGC.count; - if (this._activeTextureLocation !== location)// + if (this.boundTextures[location] === texture) { - this._activeTextureLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); + return this; } - // TODO - can we cache this texture too? - this._activeTexture = texture; + // TODO - what if we bind a texture that is already bound? + // Should be ok for now.. - if (!texture._glTextures[this.CONTEXT_UID]) + if (!glTexture) { // this will also bind the texture.. - this.textureManager.updateTexture(texture); + this.textureManager.updateTexture(texture, location); } else { - texture.touched = this.textureGC.count; // bind the current texture - texture._glTextures[this.CONTEXT_UID].bind(); + this.boundTextures[location] = texture; + + gl.activeTexture(gl.TEXTURE0 + location); + gl.bindTexture(gl.TEXTURE_2D, glTexture.texture); + } + + return this; + } + + /** + * unbinds the texture ... + * + * @param {PIXI.Texture} texture - the texture to unbind + * @return {PIXI.WebGLRenderer} Returns itself. + */ + unbindTexture(texture) + { + const gl = this.gl; + + texture = texture.baseTexture || texture; + + for (let i = 0; i < this.boundTextures.length; i++) + { + if (this.boundTextures[i] === texture) + { + this.boundTextures[i] = this.emptyTextures[i]; + + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[i]._glTextures[this.CONTEXT_UID].texture); + } } return this; @@ -488,7 +543,6 @@ this._activeShader = null; this._activeRenderTarget = this.rootRenderTarget; - this._activeTextureLocation = 999; this._activeTexture = null; // bind the main frame buffer (the screen); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index f896e3f..802ceb2 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -207,6 +207,8 @@ applyFilter(filter, input, output, clear) { const renderer = this.renderer; + const gl = renderer.gl; + let shader = filter.glShaders[renderer.CONTEXT_UID]; // cacheing.. @@ -236,8 +238,6 @@ if (clear) { - const gl = renderer.gl; - gl.disable(gl.SCISSOR_TEST); renderer.clear();// [1, 1, 1, 1]); gl.enable(gl.SCISSOR_TEST); @@ -254,14 +254,18 @@ // this syncs the pixi filters uniforms with glsl uniforms this.syncUniforms(shader, filter); - // bind the input texture.. - input.texture.bind(0); - // when you manually bind a texture, please switch active texture location to it - renderer._activeTextureLocation = 0; - renderer.state.setBlendMode(filter.blendMode); + // temporary bypass cache.. + const tex = this.renderer.boundTextures[0]; + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + this.quad.draw(); + + // restore cache. + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); } /** @@ -321,14 +325,13 @@ } else { + // TODO // this is helpful as renderTargets can also be set. // Although thinking about it, we could probably // make the filter texture cache return a RenderTexture // rather than a renderTarget const gl = this.renderer.gl; - this.renderer._activeTextureLocation = gl.TEXTURE0 + textureCount; - gl.activeTexture(gl.TEXTURE0 + textureCount); uniforms[i].texture.bind(); } @@ -502,7 +505,22 @@ this.pool[key] = []; } - const renderTarget = this.pool[key].pop() || new RenderTarget(gl, minWidth, minHeight, null, 1); + let renderTarget = this.pool[key].pop(); + + // creating render target will cause texture to be bound! + if (!renderTarget) + { + // temporary bypass cache.. + const tex = this.renderer.boundTextures[0]; + + gl.activeTexture(gl.TEXTURE0); + + // internally - this will cause a texture to be bound.. + renderTarget = new RenderTarget(gl, minWidth, minHeight, null, 1); + + // set the current one back + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } // manually tweak the resolution... // this will not modify the size of the frame buffer, just its resolution. diff --git a/.eslintrc.json b/.eslintrc.json index b8d7d05..39fca30 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -162,7 +162,7 @@ "keyword-spacing": [1, { "before": true, "after": true }], "linebreak-style": [1, "unix"], "lines-around-comment": 0, - "max-depth": [1, 5], + "max-depth": [1, 6], "max-len": [1, { "code": 125, "tabWidth": 4 }], "max-lines": 0, "max-nested-callbacks": [1, { "max": 5 }], diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index 83fce7d..f0c85f2 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -61,11 +61,16 @@ * Updates and/or Creates a WebGL texture for the renderer's context. * * @param {PIXI.BaseTexture|PIXI.Texture} texture - the texture to update + * @param {Number} location - the location the texture will be bound to. * @return {GLTexture} The gl texture. */ - updateTexture(texture) + updateTexture(texture, location) { - texture = texture.baseTexture || texture; + // assume it good! + // texture = texture.baseTexture || texture; + location = location || 0; + + const gl = this.gl; const isRenderTexture = !!texture._glRenderTargets; @@ -74,6 +79,8 @@ return null; } + gl.activeTexture(gl.TEXTURE0 + location); + let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; if (!glTexture) @@ -94,7 +101,8 @@ } else { - glTexture = new GLTexture(this.gl); + glTexture = new GLTexture(this.gl, null, null, null, null); + glTexture.bind(location); glTexture.premultiplyAlpha = true; glTexture.upload(texture.source); } @@ -150,6 +158,8 @@ glTexture.upload(texture.source); } + this.renderer.boundTextures[location] = texture; + return glTexture; } @@ -170,6 +180,8 @@ if (texture._glTextures[this.renderer.CONTEXT_UID]) { + this.renderer.unbindTexture(texture); + texture._glTextures[this.renderer.CONTEXT_UID].destroy(); texture.off('update', this.updateTexture, this); texture.off('dispose', this.destroyTexture, this); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index e921d6d..04ffda5 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -5,6 +5,7 @@ import RenderTarget from './utils/RenderTarget'; import ObjectRenderer from './utils/ObjectRenderer'; import TextureManager from './TextureManager'; +import BaseTexture from '../../textures/BaseTexture'; import TextureGarbageCollector from './TextureGarbageCollector'; import WebGLState from './WebGLState'; import mapWebGLDrawModesToPixi from './utils/mapWebGLDrawModesToPixi'; @@ -52,6 +53,7 @@ constructor(width, height, options = {}) { super('WebGL', width, height, options); + /** * The type of this renderer as a standardised const * @@ -137,8 +139,13 @@ this.renderingToScreen = true; - this._initContext(); + /** + * Holds the current state of textures bound to the GPU. + * @type {Array} + */ + this.boundTextures = null; + this._initContext(); /** * Manages the filters. * @@ -161,7 +168,6 @@ * @member {PIXI.RenderTarget} */ this._activeRenderTarget = null; - this._activeTextureLocation = 999; this._activeTexture = null; this.setBlendMode(0); @@ -182,6 +188,11 @@ gl.getExtension('WEBGL_lose_context').restoreContext(); } + const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); + + this.boundTextures = new Array(maxTextures); + this.emptyTextures = new Array(maxTextures); + // create a texture manager... this.textureManager = new TextureManager(this); this.textureGC = new TextureGarbageCollector(this); @@ -193,6 +204,24 @@ this.bindRenderTarget(this.rootRenderTarget); + // now lets fill up the textures with empty ones! + const emptyGLTexture = new glCore.GLTexture.fromData(gl, null, 1, 1); + + const tempObj = { _glTextures: {} }; + + tempObj._glTextures[this.CONTEXT_UID] = {}; + + for (let i = 0; i < maxTextures; i++) + { + const empty = new BaseTexture(); + + empty._glTextures[this.CONTEXT_UID] = emptyGLTexture; + + this.boundTextures[i] = tempObj; + this.emptyTextures[i] = empty; + this.bindTexture(null, i); + } + this.emit('context', gl); // setup the width/height properties and gl viewport @@ -354,20 +383,14 @@ if (renderTexture) { const baseTexture = renderTexture.baseTexture; - const gl = this.gl; if (!baseTexture._glRenderTargets[this.CONTEXT_UID]) { - this.textureManager.updateTexture(baseTexture); - gl.bindTexture(gl.TEXTURE_2D, null); + // bind the current texture + this.textureManager.updateTexture(baseTexture, 0); } - else - { - // the texture needs to be unbound if its being rendererd too.. - this._activeTextureLocation = baseTexture._id; - gl.activeTexture(gl.TEXTURE0 + baseTexture._id); - gl.bindTexture(gl.TEXTURE_2D, null); - } + + this.unbindTexture(baseTexture); renderTarget = baseTexture._glRenderTargets[this.CONTEXT_UID]; renderTarget.setFrame(renderTexture.frame); @@ -435,33 +458,65 @@ * @param {number} location - the texture location * @return {PIXI.WebGLRenderer} Returns itself. */ - bindTexture(texture, location = 0) + bindTexture(texture, location) { + location = location || 0; + + texture = texture || this.emptyTextures[location]; + texture = texture.baseTexture || texture; const gl = this.gl; + const glTexture = texture._glTextures[this.CONTEXT_UID]; - // TODO test perf of cache? + texture.touched = this.textureGC.count; - if (this._activeTextureLocation !== location)// + if (this.boundTextures[location] === texture) { - this._activeTextureLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); + return this; } - // TODO - can we cache this texture too? - this._activeTexture = texture; + // TODO - what if we bind a texture that is already bound? + // Should be ok for now.. - if (!texture._glTextures[this.CONTEXT_UID]) + if (!glTexture) { // this will also bind the texture.. - this.textureManager.updateTexture(texture); + this.textureManager.updateTexture(texture, location); } else { - texture.touched = this.textureGC.count; // bind the current texture - texture._glTextures[this.CONTEXT_UID].bind(); + this.boundTextures[location] = texture; + + gl.activeTexture(gl.TEXTURE0 + location); + gl.bindTexture(gl.TEXTURE_2D, glTexture.texture); + } + + return this; + } + + /** + * unbinds the texture ... + * + * @param {PIXI.Texture} texture - the texture to unbind + * @return {PIXI.WebGLRenderer} Returns itself. + */ + unbindTexture(texture) + { + const gl = this.gl; + + texture = texture.baseTexture || texture; + + for (let i = 0; i < this.boundTextures.length; i++) + { + if (this.boundTextures[i] === texture) + { + this.boundTextures[i] = this.emptyTextures[i]; + + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[i]._glTextures[this.CONTEXT_UID].texture); + } } return this; @@ -488,7 +543,6 @@ this._activeShader = null; this._activeRenderTarget = this.rootRenderTarget; - this._activeTextureLocation = 999; this._activeTexture = null; // bind the main frame buffer (the screen); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index f896e3f..802ceb2 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -207,6 +207,8 @@ applyFilter(filter, input, output, clear) { const renderer = this.renderer; + const gl = renderer.gl; + let shader = filter.glShaders[renderer.CONTEXT_UID]; // cacheing.. @@ -236,8 +238,6 @@ if (clear) { - const gl = renderer.gl; - gl.disable(gl.SCISSOR_TEST); renderer.clear();// [1, 1, 1, 1]); gl.enable(gl.SCISSOR_TEST); @@ -254,14 +254,18 @@ // this syncs the pixi filters uniforms with glsl uniforms this.syncUniforms(shader, filter); - // bind the input texture.. - input.texture.bind(0); - // when you manually bind a texture, please switch active texture location to it - renderer._activeTextureLocation = 0; - renderer.state.setBlendMode(filter.blendMode); + // temporary bypass cache.. + const tex = this.renderer.boundTextures[0]; + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + this.quad.draw(); + + // restore cache. + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); } /** @@ -321,14 +325,13 @@ } else { + // TODO // this is helpful as renderTargets can also be set. // Although thinking about it, we could probably // make the filter texture cache return a RenderTexture // rather than a renderTarget const gl = this.renderer.gl; - this.renderer._activeTextureLocation = gl.TEXTURE0 + textureCount; - gl.activeTexture(gl.TEXTURE0 + textureCount); uniforms[i].texture.bind(); } @@ -502,7 +505,22 @@ this.pool[key] = []; } - const renderTarget = this.pool[key].pop() || new RenderTarget(gl, minWidth, minHeight, null, 1); + let renderTarget = this.pool[key].pop(); + + // creating render target will cause texture to be bound! + if (!renderTarget) + { + // temporary bypass cache.. + const tex = this.renderer.boundTextures[0]; + + gl.activeTexture(gl.TEXTURE0); + + // internally - this will cause a texture to be bound.. + renderTarget = new RenderTarget(gl, minWidth, minHeight, null, 1); + + // set the current one back + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } // manually tweak the resolution... // this will not modify the size of the frame buffer, just its resolution. diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 37422fd..d7fe485 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -9,6 +9,7 @@ import bitTwiddle from 'bit-twiddle'; let TICK = 0; +let TEXTURE_TICK = 0; /** * Renderer dedicated to drawing and batching sprites. @@ -71,7 +72,7 @@ * These shaders will also be generated on the fly as required. * @member {PIXI.Shader[]} */ - this.shaders = null; + this.shader = null; this.currentIndex = 0; TICK = 0; @@ -108,17 +109,11 @@ // step 2: check the maximum number of if statements the shader can have too.. this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl); - this.shaders = new Array(this.MAX_TEXTURES); - this.shaders[0] = generateMultiTextureShader(gl, 1); - this.shaders[1] = generateMultiTextureShader(gl, 2); + const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES); // create a couple of buffers this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW); - // we use the second shader as the first one depending on your browser may omit aTextureId - // as it is not used by the shader so is optimized out. - const shader = this.shaders[1]; - for (let i = 0; i < this.vaoMax; i++) { this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW); @@ -138,6 +133,8 @@ this.vao = this.vaos[0]; this.currentBlendMode = 99999; + + this.boundTextures = new Array(this.MAX_TEXTURES); } /** @@ -188,6 +185,7 @@ } const gl = this.renderer.gl; + const MAX_TEXTURES = this.MAX_TEXTURES; const np2 = bitTwiddle.nextPow2(this.currentIndex); const log2 = bitTwiddle.log2(np2); @@ -199,6 +197,10 @@ const float32View = buffer.float32View; const uint32View = buffer.uint32View; + const boundTextures = this.boundTextures; + + const touch = this.renderer.textureGC.count; + let index = 0; let nextTexture; let currentTexture; @@ -206,11 +208,8 @@ let textureCount = 0; let currentGroup = groups[0]; let vertexData; - let tint; let uvs; - let textureId; let blendMode = sprites[0].blendMode; - let shader; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -220,7 +219,14 @@ let i; - for (i = 0; i < this.currentIndex; i++) + // copy textures.. + for (i = 0; i < MAX_TEXTURES; ++i) + { + boundTextures[i] = this.renderer.boundTextures[i]; + boundTextures[i]._virtalBoundId = i; + } + + for (i = 0; i < this.currentIndex; ++i) { // upload the sprite elemetns... // they have all ready been calculated so we just need to push them into the buffer. @@ -230,11 +236,12 @@ if (blendMode !== sprite.blendMode) { + // finish a group.. blendMode = sprite.blendMode; // force the batch to break! currentTexture = null; - textureCount = this.MAX_TEXTURES; + textureCount = MAX_TEXTURES; TICK++; } @@ -244,34 +251,56 @@ if (nextTexture._enabled !== TICK) { - if (textureCount === this.MAX_TEXTURES) + if (textureCount === MAX_TEXTURES) { TICK++; - textureCount = 0; - currentGroup.size = i - currentGroup.start; + textureCount = 0; + currentGroup = groups[groupCount++]; - currentGroup.textureCount = 0; currentGroup.blend = blendMode; + currentGroup.textureCount = 0; currentGroup.start = i; } - nextTexture._enabled = TICK; - nextTexture._id = textureCount; + nextTexture.touched = touch; - currentGroup.textures[currentGroup.textureCount++] = nextTexture; - textureCount++; + if (nextTexture._virtalBoundId === -1) + { + for (let j = 0; j < MAX_TEXTURES; ++j) + { + const tIndex = (j + TEXTURE_TICK) % MAX_TEXTURES; + + const t = boundTextures[tIndex]; + + if (t._enabled !== TICK) + { + TEXTURE_TICK++; + + t._virtalBoundId = -1; + + nextTexture._virtalBoundId = tIndex; + + boundTextures[tIndex] = nextTexture; + break; + } + } + } + + nextTexture._enabled = TICK; + + currentGroup.textureCount++; + currentGroup.ids[textureCount] = nextTexture._virtalBoundId; + currentGroup.textures[textureCount++] = nextTexture; } } vertexData = sprite.vertexData; // TODO this sum does not need to be set each frame.. - tint = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); uvs = sprite._texture._uvs.uvsUint32; - textureId = nextTexture._id; if (this.renderer.roundPixels) { @@ -317,52 +346,54 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = tint; - float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = textureId; + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + + float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; index += 20; } currentGroup.size = i - currentGroup.start; - this.vertexCount++; - + // this is still needed for IOS performance.. + // it realy doe not like uploading to the same bufffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; - shader = this.shaders[1]; this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW); + // build the vao object that will render.. this.vaos[this.vertexCount] = this.renderer.createVao() .addIndex(this.indexBuffer) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4); + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0) + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4) + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4) + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4); } - this.vertexBuffers[this.vertexCount].upload(buffer.vertices, 0); - this.vao = this.vaos[this.vertexCount].bind(); + this.vaos[this.vertexCount].bind(); + this.vertexBuffers[this.vertexCount].upload(buffer.vertices, 0, false); - // / render the groups.. - for (i = 0; i < groupCount; i++) + this.vertexCount++; + + for (i = 0; i < MAX_TEXTURES; ++i) + { + this.renderer.boundTextures[i]._virtalBoundId = -1; + } + + // render the groups.. + for (i = 0; i < groupCount; ++i) { const group = groups[i]; const groupTextureCount = group.textureCount; - shader = this.shaders[groupTextureCount - 1]; - - if (!shader) - { - shader = this.shaders[groupTextureCount - 1] = generateMultiTextureShader(gl, groupTextureCount); - // console.log("SHADER generated for " + textureCount + " textures") - } - - this.renderer.bindShader(shader); - for (let j = 0; j < groupTextureCount; j++) { - this.renderer.bindTexture(group.textures[j], j); + // reset virtual ids.. + this.renderer.bindTexture(group.textures[j], group.ids[j]); + + // reset the virtualId.. + group.textures[j]._virtalBoundId = -1; } // set the blend mode.. @@ -377,12 +408,13 @@ /** * Starts a new sprite batch. - * */ start() { // this.renderer.bindShader(this.shader); // TICK %= 1000; + this.vao = this.vaos[0].bind(); + this.renderer.bindShader(this.shader); } /** @@ -428,7 +460,7 @@ this.sprites = null; - for (let i = 0; i < this.buffers.length; i++) + for (let i = 0; i < this.buffers.length; ++i) { this.buffers[i].destroy(); } diff --git a/.eslintrc.json b/.eslintrc.json index b8d7d05..39fca30 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -162,7 +162,7 @@ "keyword-spacing": [1, { "before": true, "after": true }], "linebreak-style": [1, "unix"], "lines-around-comment": 0, - "max-depth": [1, 5], + "max-depth": [1, 6], "max-len": [1, { "code": 125, "tabWidth": 4 }], "max-lines": 0, "max-nested-callbacks": [1, { "max": 5 }], diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index 83fce7d..f0c85f2 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -61,11 +61,16 @@ * Updates and/or Creates a WebGL texture for the renderer's context. * * @param {PIXI.BaseTexture|PIXI.Texture} texture - the texture to update + * @param {Number} location - the location the texture will be bound to. * @return {GLTexture} The gl texture. */ - updateTexture(texture) + updateTexture(texture, location) { - texture = texture.baseTexture || texture; + // assume it good! + // texture = texture.baseTexture || texture; + location = location || 0; + + const gl = this.gl; const isRenderTexture = !!texture._glRenderTargets; @@ -74,6 +79,8 @@ return null; } + gl.activeTexture(gl.TEXTURE0 + location); + let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; if (!glTexture) @@ -94,7 +101,8 @@ } else { - glTexture = new GLTexture(this.gl); + glTexture = new GLTexture(this.gl, null, null, null, null); + glTexture.bind(location); glTexture.premultiplyAlpha = true; glTexture.upload(texture.source); } @@ -150,6 +158,8 @@ glTexture.upload(texture.source); } + this.renderer.boundTextures[location] = texture; + return glTexture; } @@ -170,6 +180,8 @@ if (texture._glTextures[this.renderer.CONTEXT_UID]) { + this.renderer.unbindTexture(texture); + texture._glTextures[this.renderer.CONTEXT_UID].destroy(); texture.off('update', this.updateTexture, this); texture.off('dispose', this.destroyTexture, this); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index e921d6d..04ffda5 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -5,6 +5,7 @@ import RenderTarget from './utils/RenderTarget'; import ObjectRenderer from './utils/ObjectRenderer'; import TextureManager from './TextureManager'; +import BaseTexture from '../../textures/BaseTexture'; import TextureGarbageCollector from './TextureGarbageCollector'; import WebGLState from './WebGLState'; import mapWebGLDrawModesToPixi from './utils/mapWebGLDrawModesToPixi'; @@ -52,6 +53,7 @@ constructor(width, height, options = {}) { super('WebGL', width, height, options); + /** * The type of this renderer as a standardised const * @@ -137,8 +139,13 @@ this.renderingToScreen = true; - this._initContext(); + /** + * Holds the current state of textures bound to the GPU. + * @type {Array} + */ + this.boundTextures = null; + this._initContext(); /** * Manages the filters. * @@ -161,7 +168,6 @@ * @member {PIXI.RenderTarget} */ this._activeRenderTarget = null; - this._activeTextureLocation = 999; this._activeTexture = null; this.setBlendMode(0); @@ -182,6 +188,11 @@ gl.getExtension('WEBGL_lose_context').restoreContext(); } + const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); + + this.boundTextures = new Array(maxTextures); + this.emptyTextures = new Array(maxTextures); + // create a texture manager... this.textureManager = new TextureManager(this); this.textureGC = new TextureGarbageCollector(this); @@ -193,6 +204,24 @@ this.bindRenderTarget(this.rootRenderTarget); + // now lets fill up the textures with empty ones! + const emptyGLTexture = new glCore.GLTexture.fromData(gl, null, 1, 1); + + const tempObj = { _glTextures: {} }; + + tempObj._glTextures[this.CONTEXT_UID] = {}; + + for (let i = 0; i < maxTextures; i++) + { + const empty = new BaseTexture(); + + empty._glTextures[this.CONTEXT_UID] = emptyGLTexture; + + this.boundTextures[i] = tempObj; + this.emptyTextures[i] = empty; + this.bindTexture(null, i); + } + this.emit('context', gl); // setup the width/height properties and gl viewport @@ -354,20 +383,14 @@ if (renderTexture) { const baseTexture = renderTexture.baseTexture; - const gl = this.gl; if (!baseTexture._glRenderTargets[this.CONTEXT_UID]) { - this.textureManager.updateTexture(baseTexture); - gl.bindTexture(gl.TEXTURE_2D, null); + // bind the current texture + this.textureManager.updateTexture(baseTexture, 0); } - else - { - // the texture needs to be unbound if its being rendererd too.. - this._activeTextureLocation = baseTexture._id; - gl.activeTexture(gl.TEXTURE0 + baseTexture._id); - gl.bindTexture(gl.TEXTURE_2D, null); - } + + this.unbindTexture(baseTexture); renderTarget = baseTexture._glRenderTargets[this.CONTEXT_UID]; renderTarget.setFrame(renderTexture.frame); @@ -435,33 +458,65 @@ * @param {number} location - the texture location * @return {PIXI.WebGLRenderer} Returns itself. */ - bindTexture(texture, location = 0) + bindTexture(texture, location) { + location = location || 0; + + texture = texture || this.emptyTextures[location]; + texture = texture.baseTexture || texture; const gl = this.gl; + const glTexture = texture._glTextures[this.CONTEXT_UID]; - // TODO test perf of cache? + texture.touched = this.textureGC.count; - if (this._activeTextureLocation !== location)// + if (this.boundTextures[location] === texture) { - this._activeTextureLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); + return this; } - // TODO - can we cache this texture too? - this._activeTexture = texture; + // TODO - what if we bind a texture that is already bound? + // Should be ok for now.. - if (!texture._glTextures[this.CONTEXT_UID]) + if (!glTexture) { // this will also bind the texture.. - this.textureManager.updateTexture(texture); + this.textureManager.updateTexture(texture, location); } else { - texture.touched = this.textureGC.count; // bind the current texture - texture._glTextures[this.CONTEXT_UID].bind(); + this.boundTextures[location] = texture; + + gl.activeTexture(gl.TEXTURE0 + location); + gl.bindTexture(gl.TEXTURE_2D, glTexture.texture); + } + + return this; + } + + /** + * unbinds the texture ... + * + * @param {PIXI.Texture} texture - the texture to unbind + * @return {PIXI.WebGLRenderer} Returns itself. + */ + unbindTexture(texture) + { + const gl = this.gl; + + texture = texture.baseTexture || texture; + + for (let i = 0; i < this.boundTextures.length; i++) + { + if (this.boundTextures[i] === texture) + { + this.boundTextures[i] = this.emptyTextures[i]; + + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[i]._glTextures[this.CONTEXT_UID].texture); + } } return this; @@ -488,7 +543,6 @@ this._activeShader = null; this._activeRenderTarget = this.rootRenderTarget; - this._activeTextureLocation = 999; this._activeTexture = null; // bind the main frame buffer (the screen); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index f896e3f..802ceb2 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -207,6 +207,8 @@ applyFilter(filter, input, output, clear) { const renderer = this.renderer; + const gl = renderer.gl; + let shader = filter.glShaders[renderer.CONTEXT_UID]; // cacheing.. @@ -236,8 +238,6 @@ if (clear) { - const gl = renderer.gl; - gl.disable(gl.SCISSOR_TEST); renderer.clear();// [1, 1, 1, 1]); gl.enable(gl.SCISSOR_TEST); @@ -254,14 +254,18 @@ // this syncs the pixi filters uniforms with glsl uniforms this.syncUniforms(shader, filter); - // bind the input texture.. - input.texture.bind(0); - // when you manually bind a texture, please switch active texture location to it - renderer._activeTextureLocation = 0; - renderer.state.setBlendMode(filter.blendMode); + // temporary bypass cache.. + const tex = this.renderer.boundTextures[0]; + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + this.quad.draw(); + + // restore cache. + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); } /** @@ -321,14 +325,13 @@ } else { + // TODO // this is helpful as renderTargets can also be set. // Although thinking about it, we could probably // make the filter texture cache return a RenderTexture // rather than a renderTarget const gl = this.renderer.gl; - this.renderer._activeTextureLocation = gl.TEXTURE0 + textureCount; - gl.activeTexture(gl.TEXTURE0 + textureCount); uniforms[i].texture.bind(); } @@ -502,7 +505,22 @@ this.pool[key] = []; } - const renderTarget = this.pool[key].pop() || new RenderTarget(gl, minWidth, minHeight, null, 1); + let renderTarget = this.pool[key].pop(); + + // creating render target will cause texture to be bound! + if (!renderTarget) + { + // temporary bypass cache.. + const tex = this.renderer.boundTextures[0]; + + gl.activeTexture(gl.TEXTURE0); + + // internally - this will cause a texture to be bound.. + renderTarget = new RenderTarget(gl, minWidth, minHeight, null, 1); + + // set the current one back + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } // manually tweak the resolution... // this will not modify the size of the frame buffer, just its resolution. diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 37422fd..d7fe485 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -9,6 +9,7 @@ import bitTwiddle from 'bit-twiddle'; let TICK = 0; +let TEXTURE_TICK = 0; /** * Renderer dedicated to drawing and batching sprites. @@ -71,7 +72,7 @@ * These shaders will also be generated on the fly as required. * @member {PIXI.Shader[]} */ - this.shaders = null; + this.shader = null; this.currentIndex = 0; TICK = 0; @@ -108,17 +109,11 @@ // step 2: check the maximum number of if statements the shader can have too.. this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl); - this.shaders = new Array(this.MAX_TEXTURES); - this.shaders[0] = generateMultiTextureShader(gl, 1); - this.shaders[1] = generateMultiTextureShader(gl, 2); + const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES); // create a couple of buffers this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW); - // we use the second shader as the first one depending on your browser may omit aTextureId - // as it is not used by the shader so is optimized out. - const shader = this.shaders[1]; - for (let i = 0; i < this.vaoMax; i++) { this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW); @@ -138,6 +133,8 @@ this.vao = this.vaos[0]; this.currentBlendMode = 99999; + + this.boundTextures = new Array(this.MAX_TEXTURES); } /** @@ -188,6 +185,7 @@ } const gl = this.renderer.gl; + const MAX_TEXTURES = this.MAX_TEXTURES; const np2 = bitTwiddle.nextPow2(this.currentIndex); const log2 = bitTwiddle.log2(np2); @@ -199,6 +197,10 @@ const float32View = buffer.float32View; const uint32View = buffer.uint32View; + const boundTextures = this.boundTextures; + + const touch = this.renderer.textureGC.count; + let index = 0; let nextTexture; let currentTexture; @@ -206,11 +208,8 @@ let textureCount = 0; let currentGroup = groups[0]; let vertexData; - let tint; let uvs; - let textureId; let blendMode = sprites[0].blendMode; - let shader; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -220,7 +219,14 @@ let i; - for (i = 0; i < this.currentIndex; i++) + // copy textures.. + for (i = 0; i < MAX_TEXTURES; ++i) + { + boundTextures[i] = this.renderer.boundTextures[i]; + boundTextures[i]._virtalBoundId = i; + } + + for (i = 0; i < this.currentIndex; ++i) { // upload the sprite elemetns... // they have all ready been calculated so we just need to push them into the buffer. @@ -230,11 +236,12 @@ if (blendMode !== sprite.blendMode) { + // finish a group.. blendMode = sprite.blendMode; // force the batch to break! currentTexture = null; - textureCount = this.MAX_TEXTURES; + textureCount = MAX_TEXTURES; TICK++; } @@ -244,34 +251,56 @@ if (nextTexture._enabled !== TICK) { - if (textureCount === this.MAX_TEXTURES) + if (textureCount === MAX_TEXTURES) { TICK++; - textureCount = 0; - currentGroup.size = i - currentGroup.start; + textureCount = 0; + currentGroup = groups[groupCount++]; - currentGroup.textureCount = 0; currentGroup.blend = blendMode; + currentGroup.textureCount = 0; currentGroup.start = i; } - nextTexture._enabled = TICK; - nextTexture._id = textureCount; + nextTexture.touched = touch; - currentGroup.textures[currentGroup.textureCount++] = nextTexture; - textureCount++; + if (nextTexture._virtalBoundId === -1) + { + for (let j = 0; j < MAX_TEXTURES; ++j) + { + const tIndex = (j + TEXTURE_TICK) % MAX_TEXTURES; + + const t = boundTextures[tIndex]; + + if (t._enabled !== TICK) + { + TEXTURE_TICK++; + + t._virtalBoundId = -1; + + nextTexture._virtalBoundId = tIndex; + + boundTextures[tIndex] = nextTexture; + break; + } + } + } + + nextTexture._enabled = TICK; + + currentGroup.textureCount++; + currentGroup.ids[textureCount] = nextTexture._virtalBoundId; + currentGroup.textures[textureCount++] = nextTexture; } } vertexData = sprite.vertexData; // TODO this sum does not need to be set each frame.. - tint = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); uvs = sprite._texture._uvs.uvsUint32; - textureId = nextTexture._id; if (this.renderer.roundPixels) { @@ -317,52 +346,54 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = tint; - float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = textureId; + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + + float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; index += 20; } currentGroup.size = i - currentGroup.start; - this.vertexCount++; - + // this is still needed for IOS performance.. + // it realy doe not like uploading to the same bufffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; - shader = this.shaders[1]; this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW); + // build the vao object that will render.. this.vaos[this.vertexCount] = this.renderer.createVao() .addIndex(this.indexBuffer) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4); + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0) + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4) + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4) + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4); } - this.vertexBuffers[this.vertexCount].upload(buffer.vertices, 0); - this.vao = this.vaos[this.vertexCount].bind(); + this.vaos[this.vertexCount].bind(); + this.vertexBuffers[this.vertexCount].upload(buffer.vertices, 0, false); - // / render the groups.. - for (i = 0; i < groupCount; i++) + this.vertexCount++; + + for (i = 0; i < MAX_TEXTURES; ++i) + { + this.renderer.boundTextures[i]._virtalBoundId = -1; + } + + // render the groups.. + for (i = 0; i < groupCount; ++i) { const group = groups[i]; const groupTextureCount = group.textureCount; - shader = this.shaders[groupTextureCount - 1]; - - if (!shader) - { - shader = this.shaders[groupTextureCount - 1] = generateMultiTextureShader(gl, groupTextureCount); - // console.log("SHADER generated for " + textureCount + " textures") - } - - this.renderer.bindShader(shader); - for (let j = 0; j < groupTextureCount; j++) { - this.renderer.bindTexture(group.textures[j], j); + // reset virtual ids.. + this.renderer.bindTexture(group.textures[j], group.ids[j]); + + // reset the virtualId.. + group.textures[j]._virtalBoundId = -1; } // set the blend mode.. @@ -377,12 +408,13 @@ /** * Starts a new sprite batch. - * */ start() { // this.renderer.bindShader(this.shader); // TICK %= 1000; + this.vao = this.vaos[0].bind(); + this.renderer.bindShader(this.shader); } /** @@ -428,7 +460,7 @@ this.sprites = null; - for (let i = 0; i < this.buffers.length; i++) + for (let i = 0; i < this.buffers.length; ++i) { this.buffers[i].destroy(); } diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 8c5c2a7..ae9b845 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -192,8 +192,9 @@ * @member {object} */ this._glTextures = {}; + this._enabled = 0; - this._id = 0; + this._virtalBoundId = -1; // if no source passed don't try to load if (source) diff --git a/.eslintrc.json b/.eslintrc.json index b8d7d05..39fca30 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -162,7 +162,7 @@ "keyword-spacing": [1, { "before": true, "after": true }], "linebreak-style": [1, "unix"], "lines-around-comment": 0, - "max-depth": [1, 5], + "max-depth": [1, 6], "max-len": [1, { "code": 125, "tabWidth": 4 }], "max-lines": 0, "max-nested-callbacks": [1, { "max": 5 }], diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index 83fce7d..f0c85f2 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -61,11 +61,16 @@ * Updates and/or Creates a WebGL texture for the renderer's context. * * @param {PIXI.BaseTexture|PIXI.Texture} texture - the texture to update + * @param {Number} location - the location the texture will be bound to. * @return {GLTexture} The gl texture. */ - updateTexture(texture) + updateTexture(texture, location) { - texture = texture.baseTexture || texture; + // assume it good! + // texture = texture.baseTexture || texture; + location = location || 0; + + const gl = this.gl; const isRenderTexture = !!texture._glRenderTargets; @@ -74,6 +79,8 @@ return null; } + gl.activeTexture(gl.TEXTURE0 + location); + let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; if (!glTexture) @@ -94,7 +101,8 @@ } else { - glTexture = new GLTexture(this.gl); + glTexture = new GLTexture(this.gl, null, null, null, null); + glTexture.bind(location); glTexture.premultiplyAlpha = true; glTexture.upload(texture.source); } @@ -150,6 +158,8 @@ glTexture.upload(texture.source); } + this.renderer.boundTextures[location] = texture; + return glTexture; } @@ -170,6 +180,8 @@ if (texture._glTextures[this.renderer.CONTEXT_UID]) { + this.renderer.unbindTexture(texture); + texture._glTextures[this.renderer.CONTEXT_UID].destroy(); texture.off('update', this.updateTexture, this); texture.off('dispose', this.destroyTexture, this); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index e921d6d..04ffda5 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -5,6 +5,7 @@ import RenderTarget from './utils/RenderTarget'; import ObjectRenderer from './utils/ObjectRenderer'; import TextureManager from './TextureManager'; +import BaseTexture from '../../textures/BaseTexture'; import TextureGarbageCollector from './TextureGarbageCollector'; import WebGLState from './WebGLState'; import mapWebGLDrawModesToPixi from './utils/mapWebGLDrawModesToPixi'; @@ -52,6 +53,7 @@ constructor(width, height, options = {}) { super('WebGL', width, height, options); + /** * The type of this renderer as a standardised const * @@ -137,8 +139,13 @@ this.renderingToScreen = true; - this._initContext(); + /** + * Holds the current state of textures bound to the GPU. + * @type {Array} + */ + this.boundTextures = null; + this._initContext(); /** * Manages the filters. * @@ -161,7 +168,6 @@ * @member {PIXI.RenderTarget} */ this._activeRenderTarget = null; - this._activeTextureLocation = 999; this._activeTexture = null; this.setBlendMode(0); @@ -182,6 +188,11 @@ gl.getExtension('WEBGL_lose_context').restoreContext(); } + const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); + + this.boundTextures = new Array(maxTextures); + this.emptyTextures = new Array(maxTextures); + // create a texture manager... this.textureManager = new TextureManager(this); this.textureGC = new TextureGarbageCollector(this); @@ -193,6 +204,24 @@ this.bindRenderTarget(this.rootRenderTarget); + // now lets fill up the textures with empty ones! + const emptyGLTexture = new glCore.GLTexture.fromData(gl, null, 1, 1); + + const tempObj = { _glTextures: {} }; + + tempObj._glTextures[this.CONTEXT_UID] = {}; + + for (let i = 0; i < maxTextures; i++) + { + const empty = new BaseTexture(); + + empty._glTextures[this.CONTEXT_UID] = emptyGLTexture; + + this.boundTextures[i] = tempObj; + this.emptyTextures[i] = empty; + this.bindTexture(null, i); + } + this.emit('context', gl); // setup the width/height properties and gl viewport @@ -354,20 +383,14 @@ if (renderTexture) { const baseTexture = renderTexture.baseTexture; - const gl = this.gl; if (!baseTexture._glRenderTargets[this.CONTEXT_UID]) { - this.textureManager.updateTexture(baseTexture); - gl.bindTexture(gl.TEXTURE_2D, null); + // bind the current texture + this.textureManager.updateTexture(baseTexture, 0); } - else - { - // the texture needs to be unbound if its being rendererd too.. - this._activeTextureLocation = baseTexture._id; - gl.activeTexture(gl.TEXTURE0 + baseTexture._id); - gl.bindTexture(gl.TEXTURE_2D, null); - } + + this.unbindTexture(baseTexture); renderTarget = baseTexture._glRenderTargets[this.CONTEXT_UID]; renderTarget.setFrame(renderTexture.frame); @@ -435,33 +458,65 @@ * @param {number} location - the texture location * @return {PIXI.WebGLRenderer} Returns itself. */ - bindTexture(texture, location = 0) + bindTexture(texture, location) { + location = location || 0; + + texture = texture || this.emptyTextures[location]; + texture = texture.baseTexture || texture; const gl = this.gl; + const glTexture = texture._glTextures[this.CONTEXT_UID]; - // TODO test perf of cache? + texture.touched = this.textureGC.count; - if (this._activeTextureLocation !== location)// + if (this.boundTextures[location] === texture) { - this._activeTextureLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); + return this; } - // TODO - can we cache this texture too? - this._activeTexture = texture; + // TODO - what if we bind a texture that is already bound? + // Should be ok for now.. - if (!texture._glTextures[this.CONTEXT_UID]) + if (!glTexture) { // this will also bind the texture.. - this.textureManager.updateTexture(texture); + this.textureManager.updateTexture(texture, location); } else { - texture.touched = this.textureGC.count; // bind the current texture - texture._glTextures[this.CONTEXT_UID].bind(); + this.boundTextures[location] = texture; + + gl.activeTexture(gl.TEXTURE0 + location); + gl.bindTexture(gl.TEXTURE_2D, glTexture.texture); + } + + return this; + } + + /** + * unbinds the texture ... + * + * @param {PIXI.Texture} texture - the texture to unbind + * @return {PIXI.WebGLRenderer} Returns itself. + */ + unbindTexture(texture) + { + const gl = this.gl; + + texture = texture.baseTexture || texture; + + for (let i = 0; i < this.boundTextures.length; i++) + { + if (this.boundTextures[i] === texture) + { + this.boundTextures[i] = this.emptyTextures[i]; + + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[i]._glTextures[this.CONTEXT_UID].texture); + } } return this; @@ -488,7 +543,6 @@ this._activeShader = null; this._activeRenderTarget = this.rootRenderTarget; - this._activeTextureLocation = 999; this._activeTexture = null; // bind the main frame buffer (the screen); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index f896e3f..802ceb2 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -207,6 +207,8 @@ applyFilter(filter, input, output, clear) { const renderer = this.renderer; + const gl = renderer.gl; + let shader = filter.glShaders[renderer.CONTEXT_UID]; // cacheing.. @@ -236,8 +238,6 @@ if (clear) { - const gl = renderer.gl; - gl.disable(gl.SCISSOR_TEST); renderer.clear();// [1, 1, 1, 1]); gl.enable(gl.SCISSOR_TEST); @@ -254,14 +254,18 @@ // this syncs the pixi filters uniforms with glsl uniforms this.syncUniforms(shader, filter); - // bind the input texture.. - input.texture.bind(0); - // when you manually bind a texture, please switch active texture location to it - renderer._activeTextureLocation = 0; - renderer.state.setBlendMode(filter.blendMode); + // temporary bypass cache.. + const tex = this.renderer.boundTextures[0]; + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + this.quad.draw(); + + // restore cache. + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); } /** @@ -321,14 +325,13 @@ } else { + // TODO // this is helpful as renderTargets can also be set. // Although thinking about it, we could probably // make the filter texture cache return a RenderTexture // rather than a renderTarget const gl = this.renderer.gl; - this.renderer._activeTextureLocation = gl.TEXTURE0 + textureCount; - gl.activeTexture(gl.TEXTURE0 + textureCount); uniforms[i].texture.bind(); } @@ -502,7 +505,22 @@ this.pool[key] = []; } - const renderTarget = this.pool[key].pop() || new RenderTarget(gl, minWidth, minHeight, null, 1); + let renderTarget = this.pool[key].pop(); + + // creating render target will cause texture to be bound! + if (!renderTarget) + { + // temporary bypass cache.. + const tex = this.renderer.boundTextures[0]; + + gl.activeTexture(gl.TEXTURE0); + + // internally - this will cause a texture to be bound.. + renderTarget = new RenderTarget(gl, minWidth, minHeight, null, 1); + + // set the current one back + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } // manually tweak the resolution... // this will not modify the size of the frame buffer, just its resolution. diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 37422fd..d7fe485 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -9,6 +9,7 @@ import bitTwiddle from 'bit-twiddle'; let TICK = 0; +let TEXTURE_TICK = 0; /** * Renderer dedicated to drawing and batching sprites. @@ -71,7 +72,7 @@ * These shaders will also be generated on the fly as required. * @member {PIXI.Shader[]} */ - this.shaders = null; + this.shader = null; this.currentIndex = 0; TICK = 0; @@ -108,17 +109,11 @@ // step 2: check the maximum number of if statements the shader can have too.. this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl); - this.shaders = new Array(this.MAX_TEXTURES); - this.shaders[0] = generateMultiTextureShader(gl, 1); - this.shaders[1] = generateMultiTextureShader(gl, 2); + const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES); // create a couple of buffers this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW); - // we use the second shader as the first one depending on your browser may omit aTextureId - // as it is not used by the shader so is optimized out. - const shader = this.shaders[1]; - for (let i = 0; i < this.vaoMax; i++) { this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW); @@ -138,6 +133,8 @@ this.vao = this.vaos[0]; this.currentBlendMode = 99999; + + this.boundTextures = new Array(this.MAX_TEXTURES); } /** @@ -188,6 +185,7 @@ } const gl = this.renderer.gl; + const MAX_TEXTURES = this.MAX_TEXTURES; const np2 = bitTwiddle.nextPow2(this.currentIndex); const log2 = bitTwiddle.log2(np2); @@ -199,6 +197,10 @@ const float32View = buffer.float32View; const uint32View = buffer.uint32View; + const boundTextures = this.boundTextures; + + const touch = this.renderer.textureGC.count; + let index = 0; let nextTexture; let currentTexture; @@ -206,11 +208,8 @@ let textureCount = 0; let currentGroup = groups[0]; let vertexData; - let tint; let uvs; - let textureId; let blendMode = sprites[0].blendMode; - let shader; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -220,7 +219,14 @@ let i; - for (i = 0; i < this.currentIndex; i++) + // copy textures.. + for (i = 0; i < MAX_TEXTURES; ++i) + { + boundTextures[i] = this.renderer.boundTextures[i]; + boundTextures[i]._virtalBoundId = i; + } + + for (i = 0; i < this.currentIndex; ++i) { // upload the sprite elemetns... // they have all ready been calculated so we just need to push them into the buffer. @@ -230,11 +236,12 @@ if (blendMode !== sprite.blendMode) { + // finish a group.. blendMode = sprite.blendMode; // force the batch to break! currentTexture = null; - textureCount = this.MAX_TEXTURES; + textureCount = MAX_TEXTURES; TICK++; } @@ -244,34 +251,56 @@ if (nextTexture._enabled !== TICK) { - if (textureCount === this.MAX_TEXTURES) + if (textureCount === MAX_TEXTURES) { TICK++; - textureCount = 0; - currentGroup.size = i - currentGroup.start; + textureCount = 0; + currentGroup = groups[groupCount++]; - currentGroup.textureCount = 0; currentGroup.blend = blendMode; + currentGroup.textureCount = 0; currentGroup.start = i; } - nextTexture._enabled = TICK; - nextTexture._id = textureCount; + nextTexture.touched = touch; - currentGroup.textures[currentGroup.textureCount++] = nextTexture; - textureCount++; + if (nextTexture._virtalBoundId === -1) + { + for (let j = 0; j < MAX_TEXTURES; ++j) + { + const tIndex = (j + TEXTURE_TICK) % MAX_TEXTURES; + + const t = boundTextures[tIndex]; + + if (t._enabled !== TICK) + { + TEXTURE_TICK++; + + t._virtalBoundId = -1; + + nextTexture._virtalBoundId = tIndex; + + boundTextures[tIndex] = nextTexture; + break; + } + } + } + + nextTexture._enabled = TICK; + + currentGroup.textureCount++; + currentGroup.ids[textureCount] = nextTexture._virtalBoundId; + currentGroup.textures[textureCount++] = nextTexture; } } vertexData = sprite.vertexData; // TODO this sum does not need to be set each frame.. - tint = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); uvs = sprite._texture._uvs.uvsUint32; - textureId = nextTexture._id; if (this.renderer.roundPixels) { @@ -317,52 +346,54 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = tint; - float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = textureId; + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + + float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; index += 20; } currentGroup.size = i - currentGroup.start; - this.vertexCount++; - + // this is still needed for IOS performance.. + // it realy doe not like uploading to the same bufffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; - shader = this.shaders[1]; this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW); + // build the vao object that will render.. this.vaos[this.vertexCount] = this.renderer.createVao() .addIndex(this.indexBuffer) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4); + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0) + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4) + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4) + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4); } - this.vertexBuffers[this.vertexCount].upload(buffer.vertices, 0); - this.vao = this.vaos[this.vertexCount].bind(); + this.vaos[this.vertexCount].bind(); + this.vertexBuffers[this.vertexCount].upload(buffer.vertices, 0, false); - // / render the groups.. - for (i = 0; i < groupCount; i++) + this.vertexCount++; + + for (i = 0; i < MAX_TEXTURES; ++i) + { + this.renderer.boundTextures[i]._virtalBoundId = -1; + } + + // render the groups.. + for (i = 0; i < groupCount; ++i) { const group = groups[i]; const groupTextureCount = group.textureCount; - shader = this.shaders[groupTextureCount - 1]; - - if (!shader) - { - shader = this.shaders[groupTextureCount - 1] = generateMultiTextureShader(gl, groupTextureCount); - // console.log("SHADER generated for " + textureCount + " textures") - } - - this.renderer.bindShader(shader); - for (let j = 0; j < groupTextureCount; j++) { - this.renderer.bindTexture(group.textures[j], j); + // reset virtual ids.. + this.renderer.bindTexture(group.textures[j], group.ids[j]); + + // reset the virtualId.. + group.textures[j]._virtalBoundId = -1; } // set the blend mode.. @@ -377,12 +408,13 @@ /** * Starts a new sprite batch. - * */ start() { // this.renderer.bindShader(this.shader); // TICK %= 1000; + this.vao = this.vaos[0].bind(); + this.renderer.bindShader(this.shader); } /** @@ -428,7 +460,7 @@ this.sprites = null; - for (let i = 0; i < this.buffers.length; i++) + for (let i = 0; i < this.buffers.length; ++i) { this.buffers[i].destroy(); } diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 8c5c2a7..ae9b845 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -192,8 +192,9 @@ * @member {object} */ this._glTextures = {}; + this._enabled = 0; - this._id = 0; + this._virtalBoundId = -1; // if no source passed don't try to load if (source) diff --git a/src/core/utils/maxRecommendedTextures.js b/src/core/utils/maxRecommendedTextures.js index 1075b95..fb8ba65 100644 --- a/src/core/utils/maxRecommendedTextures.js +++ b/src/core/utils/maxRecommendedTextures.js @@ -5,7 +5,7 @@ if (Device.tablet || Device.phone) { // check if the res is iphone 6 or higher.. - return 2; + return 4; } // desktop should be ok diff --git a/.eslintrc.json b/.eslintrc.json index b8d7d05..39fca30 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -162,7 +162,7 @@ "keyword-spacing": [1, { "before": true, "after": true }], "linebreak-style": [1, "unix"], "lines-around-comment": 0, - "max-depth": [1, 5], + "max-depth": [1, 6], "max-len": [1, { "code": 125, "tabWidth": 4 }], "max-lines": 0, "max-nested-callbacks": [1, { "max": 5 }], diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index 83fce7d..f0c85f2 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -61,11 +61,16 @@ * Updates and/or Creates a WebGL texture for the renderer's context. * * @param {PIXI.BaseTexture|PIXI.Texture} texture - the texture to update + * @param {Number} location - the location the texture will be bound to. * @return {GLTexture} The gl texture. */ - updateTexture(texture) + updateTexture(texture, location) { - texture = texture.baseTexture || texture; + // assume it good! + // texture = texture.baseTexture || texture; + location = location || 0; + + const gl = this.gl; const isRenderTexture = !!texture._glRenderTargets; @@ -74,6 +79,8 @@ return null; } + gl.activeTexture(gl.TEXTURE0 + location); + let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; if (!glTexture) @@ -94,7 +101,8 @@ } else { - glTexture = new GLTexture(this.gl); + glTexture = new GLTexture(this.gl, null, null, null, null); + glTexture.bind(location); glTexture.premultiplyAlpha = true; glTexture.upload(texture.source); } @@ -150,6 +158,8 @@ glTexture.upload(texture.source); } + this.renderer.boundTextures[location] = texture; + return glTexture; } @@ -170,6 +180,8 @@ if (texture._glTextures[this.renderer.CONTEXT_UID]) { + this.renderer.unbindTexture(texture); + texture._glTextures[this.renderer.CONTEXT_UID].destroy(); texture.off('update', this.updateTexture, this); texture.off('dispose', this.destroyTexture, this); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index e921d6d..04ffda5 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -5,6 +5,7 @@ import RenderTarget from './utils/RenderTarget'; import ObjectRenderer from './utils/ObjectRenderer'; import TextureManager from './TextureManager'; +import BaseTexture from '../../textures/BaseTexture'; import TextureGarbageCollector from './TextureGarbageCollector'; import WebGLState from './WebGLState'; import mapWebGLDrawModesToPixi from './utils/mapWebGLDrawModesToPixi'; @@ -52,6 +53,7 @@ constructor(width, height, options = {}) { super('WebGL', width, height, options); + /** * The type of this renderer as a standardised const * @@ -137,8 +139,13 @@ this.renderingToScreen = true; - this._initContext(); + /** + * Holds the current state of textures bound to the GPU. + * @type {Array} + */ + this.boundTextures = null; + this._initContext(); /** * Manages the filters. * @@ -161,7 +168,6 @@ * @member {PIXI.RenderTarget} */ this._activeRenderTarget = null; - this._activeTextureLocation = 999; this._activeTexture = null; this.setBlendMode(0); @@ -182,6 +188,11 @@ gl.getExtension('WEBGL_lose_context').restoreContext(); } + const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); + + this.boundTextures = new Array(maxTextures); + this.emptyTextures = new Array(maxTextures); + // create a texture manager... this.textureManager = new TextureManager(this); this.textureGC = new TextureGarbageCollector(this); @@ -193,6 +204,24 @@ this.bindRenderTarget(this.rootRenderTarget); + // now lets fill up the textures with empty ones! + const emptyGLTexture = new glCore.GLTexture.fromData(gl, null, 1, 1); + + const tempObj = { _glTextures: {} }; + + tempObj._glTextures[this.CONTEXT_UID] = {}; + + for (let i = 0; i < maxTextures; i++) + { + const empty = new BaseTexture(); + + empty._glTextures[this.CONTEXT_UID] = emptyGLTexture; + + this.boundTextures[i] = tempObj; + this.emptyTextures[i] = empty; + this.bindTexture(null, i); + } + this.emit('context', gl); // setup the width/height properties and gl viewport @@ -354,20 +383,14 @@ if (renderTexture) { const baseTexture = renderTexture.baseTexture; - const gl = this.gl; if (!baseTexture._glRenderTargets[this.CONTEXT_UID]) { - this.textureManager.updateTexture(baseTexture); - gl.bindTexture(gl.TEXTURE_2D, null); + // bind the current texture + this.textureManager.updateTexture(baseTexture, 0); } - else - { - // the texture needs to be unbound if its being rendererd too.. - this._activeTextureLocation = baseTexture._id; - gl.activeTexture(gl.TEXTURE0 + baseTexture._id); - gl.bindTexture(gl.TEXTURE_2D, null); - } + + this.unbindTexture(baseTexture); renderTarget = baseTexture._glRenderTargets[this.CONTEXT_UID]; renderTarget.setFrame(renderTexture.frame); @@ -435,33 +458,65 @@ * @param {number} location - the texture location * @return {PIXI.WebGLRenderer} Returns itself. */ - bindTexture(texture, location = 0) + bindTexture(texture, location) { + location = location || 0; + + texture = texture || this.emptyTextures[location]; + texture = texture.baseTexture || texture; const gl = this.gl; + const glTexture = texture._glTextures[this.CONTEXT_UID]; - // TODO test perf of cache? + texture.touched = this.textureGC.count; - if (this._activeTextureLocation !== location)// + if (this.boundTextures[location] === texture) { - this._activeTextureLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); + return this; } - // TODO - can we cache this texture too? - this._activeTexture = texture; + // TODO - what if we bind a texture that is already bound? + // Should be ok for now.. - if (!texture._glTextures[this.CONTEXT_UID]) + if (!glTexture) { // this will also bind the texture.. - this.textureManager.updateTexture(texture); + this.textureManager.updateTexture(texture, location); } else { - texture.touched = this.textureGC.count; // bind the current texture - texture._glTextures[this.CONTEXT_UID].bind(); + this.boundTextures[location] = texture; + + gl.activeTexture(gl.TEXTURE0 + location); + gl.bindTexture(gl.TEXTURE_2D, glTexture.texture); + } + + return this; + } + + /** + * unbinds the texture ... + * + * @param {PIXI.Texture} texture - the texture to unbind + * @return {PIXI.WebGLRenderer} Returns itself. + */ + unbindTexture(texture) + { + const gl = this.gl; + + texture = texture.baseTexture || texture; + + for (let i = 0; i < this.boundTextures.length; i++) + { + if (this.boundTextures[i] === texture) + { + this.boundTextures[i] = this.emptyTextures[i]; + + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[i]._glTextures[this.CONTEXT_UID].texture); + } } return this; @@ -488,7 +543,6 @@ this._activeShader = null; this._activeRenderTarget = this.rootRenderTarget; - this._activeTextureLocation = 999; this._activeTexture = null; // bind the main frame buffer (the screen); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index f896e3f..802ceb2 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -207,6 +207,8 @@ applyFilter(filter, input, output, clear) { const renderer = this.renderer; + const gl = renderer.gl; + let shader = filter.glShaders[renderer.CONTEXT_UID]; // cacheing.. @@ -236,8 +238,6 @@ if (clear) { - const gl = renderer.gl; - gl.disable(gl.SCISSOR_TEST); renderer.clear();// [1, 1, 1, 1]); gl.enable(gl.SCISSOR_TEST); @@ -254,14 +254,18 @@ // this syncs the pixi filters uniforms with glsl uniforms this.syncUniforms(shader, filter); - // bind the input texture.. - input.texture.bind(0); - // when you manually bind a texture, please switch active texture location to it - renderer._activeTextureLocation = 0; - renderer.state.setBlendMode(filter.blendMode); + // temporary bypass cache.. + const tex = this.renderer.boundTextures[0]; + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + this.quad.draw(); + + // restore cache. + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); } /** @@ -321,14 +325,13 @@ } else { + // TODO // this is helpful as renderTargets can also be set. // Although thinking about it, we could probably // make the filter texture cache return a RenderTexture // rather than a renderTarget const gl = this.renderer.gl; - this.renderer._activeTextureLocation = gl.TEXTURE0 + textureCount; - gl.activeTexture(gl.TEXTURE0 + textureCount); uniforms[i].texture.bind(); } @@ -502,7 +505,22 @@ this.pool[key] = []; } - const renderTarget = this.pool[key].pop() || new RenderTarget(gl, minWidth, minHeight, null, 1); + let renderTarget = this.pool[key].pop(); + + // creating render target will cause texture to be bound! + if (!renderTarget) + { + // temporary bypass cache.. + const tex = this.renderer.boundTextures[0]; + + gl.activeTexture(gl.TEXTURE0); + + // internally - this will cause a texture to be bound.. + renderTarget = new RenderTarget(gl, minWidth, minHeight, null, 1); + + // set the current one back + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } // manually tweak the resolution... // this will not modify the size of the frame buffer, just its resolution. diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 37422fd..d7fe485 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -9,6 +9,7 @@ import bitTwiddle from 'bit-twiddle'; let TICK = 0; +let TEXTURE_TICK = 0; /** * Renderer dedicated to drawing and batching sprites. @@ -71,7 +72,7 @@ * These shaders will also be generated on the fly as required. * @member {PIXI.Shader[]} */ - this.shaders = null; + this.shader = null; this.currentIndex = 0; TICK = 0; @@ -108,17 +109,11 @@ // step 2: check the maximum number of if statements the shader can have too.. this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl); - this.shaders = new Array(this.MAX_TEXTURES); - this.shaders[0] = generateMultiTextureShader(gl, 1); - this.shaders[1] = generateMultiTextureShader(gl, 2); + const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES); // create a couple of buffers this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW); - // we use the second shader as the first one depending on your browser may omit aTextureId - // as it is not used by the shader so is optimized out. - const shader = this.shaders[1]; - for (let i = 0; i < this.vaoMax; i++) { this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW); @@ -138,6 +133,8 @@ this.vao = this.vaos[0]; this.currentBlendMode = 99999; + + this.boundTextures = new Array(this.MAX_TEXTURES); } /** @@ -188,6 +185,7 @@ } const gl = this.renderer.gl; + const MAX_TEXTURES = this.MAX_TEXTURES; const np2 = bitTwiddle.nextPow2(this.currentIndex); const log2 = bitTwiddle.log2(np2); @@ -199,6 +197,10 @@ const float32View = buffer.float32View; const uint32View = buffer.uint32View; + const boundTextures = this.boundTextures; + + const touch = this.renderer.textureGC.count; + let index = 0; let nextTexture; let currentTexture; @@ -206,11 +208,8 @@ let textureCount = 0; let currentGroup = groups[0]; let vertexData; - let tint; let uvs; - let textureId; let blendMode = sprites[0].blendMode; - let shader; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -220,7 +219,14 @@ let i; - for (i = 0; i < this.currentIndex; i++) + // copy textures.. + for (i = 0; i < MAX_TEXTURES; ++i) + { + boundTextures[i] = this.renderer.boundTextures[i]; + boundTextures[i]._virtalBoundId = i; + } + + for (i = 0; i < this.currentIndex; ++i) { // upload the sprite elemetns... // they have all ready been calculated so we just need to push them into the buffer. @@ -230,11 +236,12 @@ if (blendMode !== sprite.blendMode) { + // finish a group.. blendMode = sprite.blendMode; // force the batch to break! currentTexture = null; - textureCount = this.MAX_TEXTURES; + textureCount = MAX_TEXTURES; TICK++; } @@ -244,34 +251,56 @@ if (nextTexture._enabled !== TICK) { - if (textureCount === this.MAX_TEXTURES) + if (textureCount === MAX_TEXTURES) { TICK++; - textureCount = 0; - currentGroup.size = i - currentGroup.start; + textureCount = 0; + currentGroup = groups[groupCount++]; - currentGroup.textureCount = 0; currentGroup.blend = blendMode; + currentGroup.textureCount = 0; currentGroup.start = i; } - nextTexture._enabled = TICK; - nextTexture._id = textureCount; + nextTexture.touched = touch; - currentGroup.textures[currentGroup.textureCount++] = nextTexture; - textureCount++; + if (nextTexture._virtalBoundId === -1) + { + for (let j = 0; j < MAX_TEXTURES; ++j) + { + const tIndex = (j + TEXTURE_TICK) % MAX_TEXTURES; + + const t = boundTextures[tIndex]; + + if (t._enabled !== TICK) + { + TEXTURE_TICK++; + + t._virtalBoundId = -1; + + nextTexture._virtalBoundId = tIndex; + + boundTextures[tIndex] = nextTexture; + break; + } + } + } + + nextTexture._enabled = TICK; + + currentGroup.textureCount++; + currentGroup.ids[textureCount] = nextTexture._virtalBoundId; + currentGroup.textures[textureCount++] = nextTexture; } } vertexData = sprite.vertexData; // TODO this sum does not need to be set each frame.. - tint = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); uvs = sprite._texture._uvs.uvsUint32; - textureId = nextTexture._id; if (this.renderer.roundPixels) { @@ -317,52 +346,54 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = tint; - float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = textureId; + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + + float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; index += 20; } currentGroup.size = i - currentGroup.start; - this.vertexCount++; - + // this is still needed for IOS performance.. + // it realy doe not like uploading to the same bufffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; - shader = this.shaders[1]; this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW); + // build the vao object that will render.. this.vaos[this.vertexCount] = this.renderer.createVao() .addIndex(this.indexBuffer) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4) - .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4); + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0) + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4) + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4) + .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4); } - this.vertexBuffers[this.vertexCount].upload(buffer.vertices, 0); - this.vao = this.vaos[this.vertexCount].bind(); + this.vaos[this.vertexCount].bind(); + this.vertexBuffers[this.vertexCount].upload(buffer.vertices, 0, false); - // / render the groups.. - for (i = 0; i < groupCount; i++) + this.vertexCount++; + + for (i = 0; i < MAX_TEXTURES; ++i) + { + this.renderer.boundTextures[i]._virtalBoundId = -1; + } + + // render the groups.. + for (i = 0; i < groupCount; ++i) { const group = groups[i]; const groupTextureCount = group.textureCount; - shader = this.shaders[groupTextureCount - 1]; - - if (!shader) - { - shader = this.shaders[groupTextureCount - 1] = generateMultiTextureShader(gl, groupTextureCount); - // console.log("SHADER generated for " + textureCount + " textures") - } - - this.renderer.bindShader(shader); - for (let j = 0; j < groupTextureCount; j++) { - this.renderer.bindTexture(group.textures[j], j); + // reset virtual ids.. + this.renderer.bindTexture(group.textures[j], group.ids[j]); + + // reset the virtualId.. + group.textures[j]._virtalBoundId = -1; } // set the blend mode.. @@ -377,12 +408,13 @@ /** * Starts a new sprite batch. - * */ start() { // this.renderer.bindShader(this.shader); // TICK %= 1000; + this.vao = this.vaos[0].bind(); + this.renderer.bindShader(this.shader); } /** @@ -428,7 +460,7 @@ this.sprites = null; - for (let i = 0; i < this.buffers.length; i++) + for (let i = 0; i < this.buffers.length; ++i) { this.buffers[i].destroy(); } diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 8c5c2a7..ae9b845 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -192,8 +192,9 @@ * @member {object} */ this._glTextures = {}; + this._enabled = 0; - this._id = 0; + this._virtalBoundId = -1; // if no source passed don't try to load if (source) diff --git a/src/core/utils/maxRecommendedTextures.js b/src/core/utils/maxRecommendedTextures.js index 1075b95..fb8ba65 100644 --- a/src/core/utils/maxRecommendedTextures.js +++ b/src/core/utils/maxRecommendedTextures.js @@ -5,7 +5,7 @@ if (Device.tablet || Device.phone) { // check if the res is iphone 6 or higher.. - return 2; + return 4; } // desktop should be ok diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index b74d861..2f14e89 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -41,7 +41,7 @@ glslify('./tilingSprite.vert'), glslify('./tilingSprite_simple.frag')); - this.quad = new core.Quad(gl); + this.quad = new core.Quad(gl, this.renderer.state.attribState); this.quad.initVao(this.shader); }